Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/banners/app_banner_manager.h" | 5 #include "chrome/browser/banners/app_banner_manager.h" |
| 6 | 6 |
| 7 #include "chrome/browser/banners/app_banner_data_fetcher.h" | 7 #include "base/bind.h" |
| 8 #include "chrome/browser/banners/app_banner_debug_log.h" | 8 #include "base/callback.h" |
| 9 #include "base/command_line.h" | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "base/time/time.h" | |
| 12 #include "chrome/browser/banners/app_banner_metrics.h" | |
| 9 #include "chrome/browser/banners/app_banner_settings_helper.h" | 13 #include "chrome/browser/banners/app_banner_settings_helper.h" |
| 14 #include "chrome/browser/browser_process.h" | |
| 10 #include "chrome/browser/engagement/site_engagement_service.h" | 15 #include "chrome/browser/engagement/site_engagement_service.h" |
| 16 #include "chrome/browser/installable/installable_logging.h" | |
| 17 #include "chrome/browser/installable/installable_manager.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
| 19 #include "chrome/common/chrome_switches.h" | |
| 20 #include "chrome/common/render_messages.h" | |
| 21 #include "components/rappor/rappor_utils.h" | |
| 12 #include "content/public/browser/navigation_handle.h" | 22 #include "content/public/browser/navigation_handle.h" |
| 13 #include "content/public/browser/render_frame_host.h" | 23 #include "content/public/browser/render_frame_host.h" |
| 14 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
| 15 #include "content/public/common/frame_navigate_params.h" | |
| 16 #include "content/public/common/origin_util.h" | 25 #include "content/public/common/origin_util.h" |
| 26 #include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerProm ptReply.h" | |
| 27 #include "third_party/skia/include/core/SkBitmap.h" | |
| 28 #include "ui/display/display.h" | |
| 29 #include "ui/display/screen.h" | |
| 17 | 30 |
| 18 namespace { | 31 namespace { |
| 32 | |
| 19 bool gDisableSecureCheckForTesting = false; | 33 bool gDisableSecureCheckForTesting = false; |
| 34 int gCurrentRequestID = -1; | |
| 35 base::LazyInstance<base::TimeDelta> gTimeDeltaForTesting = | |
| 36 LAZY_INSTANCE_INITIALIZER; | |
| 37 | |
| 38 // Returns |size_in_px| in dp, i.e. divided by the current device scale factor. | |
| 39 int ConvertIconSizeFromPxToDp(int size_in_px) { | |
| 40 return size_in_px / | |
| 41 display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor(); | |
| 42 } | |
| 43 | |
| 44 // Returns an InstallableParams object that requests all checks necessary for | |
| 45 // a web app banner. | |
| 46 InstallableParams GetWebAppParams(int ideal_icon_size_in_dp, | |
| 47 int minimum_icon_size_in_dp) { | |
| 48 InstallableParams params; | |
| 49 params.ideal_icon_size_in_dp = ideal_icon_size_in_dp; | |
| 50 params.minimum_icon_size_in_dp = minimum_icon_size_in_dp; | |
| 51 params.check_installable = true; | |
| 52 params.fetch_valid_icon = true; | |
| 53 | |
| 54 return params; | |
| 55 } | |
| 56 | |
| 20 } // anonymous namespace | 57 } // anonymous namespace |
| 21 | 58 |
| 22 namespace banners { | 59 namespace banners { |
| 23 | 60 |
| 61 // static | |
| 24 void AppBannerManager::DisableSecureSchemeCheckForTesting() { | 62 void AppBannerManager::DisableSecureSchemeCheckForTesting() { |
| 25 gDisableSecureCheckForTesting = true; | 63 gDisableSecureCheckForTesting = true; |
| 26 } | 64 } |
| 27 | 65 |
| 66 // static | |
| 67 base::Time AppBannerManager::GetCurrentTime() { | |
| 68 return base::Time::Now() + gTimeDeltaForTesting.Get(); | |
| 69 } | |
| 70 | |
| 71 // static | |
| 72 void AppBannerManager::SetTimeDeltaForTesting(int days) { | |
| 73 gTimeDeltaForTesting.Get() = base::TimeDelta::FromDays(days); | |
| 74 } | |
| 75 | |
| 76 // static | |
| 28 void AppBannerManager::SetEngagementWeights(double direct_engagement, | 77 void AppBannerManager::SetEngagementWeights(double direct_engagement, |
| 29 double indirect_engagement) { | 78 double indirect_engagement) { |
| 30 AppBannerSettingsHelper::SetEngagementWeights(direct_engagement, | 79 AppBannerSettingsHelper::SetEngagementWeights(direct_engagement, |
| 31 indirect_engagement); | 80 indirect_engagement); |
| 32 } | 81 } |
| 33 | 82 |
| 83 // static | |
| 34 bool AppBannerManager::URLsAreForTheSamePage(const GURL& first, | 84 bool AppBannerManager::URLsAreForTheSamePage(const GURL& first, |
| 35 const GURL& second) { | 85 const GURL& second) { |
| 36 return first.GetWithEmptyPath() == second.GetWithEmptyPath() && | 86 return first.GetWithEmptyPath() == second.GetWithEmptyPath() && |
| 37 first.path() == second.path() && first.query() == second.query(); | 87 first.path() == second.path() && first.query() == second.query(); |
| 38 } | 88 } |
| 39 | 89 |
| 40 AppBannerManager::AppBannerManager(content::WebContents* web_contents) | |
| 41 : content::WebContentsObserver(web_contents), | |
| 42 SiteEngagementObserver(nullptr), | |
| 43 data_fetcher_(nullptr), | |
| 44 banner_request_queued_(false), | |
| 45 load_finished_(false), | |
| 46 weak_factory_(this) { | |
| 47 AppBannerSettingsHelper::UpdateFromFieldTrial(); | |
| 48 } | |
| 49 | |
| 50 AppBannerManager::~AppBannerManager() { | |
| 51 CancelActiveFetcher(); | |
| 52 } | |
| 53 | |
| 54 void AppBannerManager::ReplaceWebContents(content::WebContents* web_contents) { | |
| 55 content::WebContentsObserver::Observe(web_contents); | |
| 56 if (data_fetcher_.get()) | |
| 57 data_fetcher_.get()->ReplaceWebContents(web_contents); | |
| 58 } | |
| 59 | |
| 60 bool AppBannerManager::IsFetcherActive() { | |
| 61 return data_fetcher_ && data_fetcher_->is_active(); | |
| 62 } | |
| 63 | |
| 64 void AppBannerManager::RequestAppBanner(const GURL& validated_url, | 90 void AppBannerManager::RequestAppBanner(const GURL& validated_url, |
| 65 bool is_debug_mode) { | 91 bool is_debug_mode) { |
| 66 content::WebContents* contents = web_contents(); | 92 content::WebContents* contents = web_contents(); |
| 67 if (contents->GetMainFrame()->GetParent()) { | 93 if (contents->GetMainFrame()->GetParent()) { |
| 68 OutputDeveloperNotShownMessage(contents, kNotLoadedInMainFrame, | 94 ReportError(contents, NOT_IN_MAIN_FRAME); |
| 69 is_debug_mode); | 95 return; |
| 70 return; | 96 } |
| 71 } | 97 |
| 72 | 98 // Don't start a redundant banner request. |
| 73 if (data_fetcher_.get() && data_fetcher_->is_active() && | 99 if (is_active_ && |
| 74 URLsAreForTheSamePage(data_fetcher_->validated_url(), validated_url) && | 100 URLsAreForTheSamePage(validated_url, contents->GetLastCommittedURL())) { |
| 75 !is_debug_mode) { | |
| 76 return; | 101 return; |
| 77 } | 102 } |
| 78 | 103 |
| 79 // A secure origin is required to show banners, so exit early if we see the | 104 // A secure origin is required to show banners, so exit early if we see the |
| 80 // URL is invalid. | 105 // URL is invalid. |
| 81 if (!content::IsOriginSecure(validated_url) && | 106 if (!content::IsOriginSecure(validated_url) && |
| 82 !gDisableSecureCheckForTesting) { | 107 !gDisableSecureCheckForTesting) { |
| 83 OutputDeveloperNotShownMessage(contents, kNotServedFromSecureOrigin, | 108 ReportError(contents, NOT_FROM_SECURE_ORIGIN); |
| 84 is_debug_mode); | 109 return; |
| 85 return; | 110 } |
| 86 } | 111 |
| 87 | 112 is_debug_mode_ = is_debug_mode; |
| 88 // Kick off the data retrieval pipeline. | 113 is_active_ = true; |
| 89 data_fetcher_ = | 114 |
| 90 CreateAppBannerDataFetcher(weak_factory_.GetWeakPtr(), is_debug_mode); | 115 // We start by requesting the manifest from the InstallableManager. The |
| 91 data_fetcher_->Start(validated_url, last_transition_type_); | 116 // default-constructed params will have all other fields as false. |
| 92 } | 117 InstallableParams params; |
| 93 | 118 manager_->GetData( |
| 94 void AppBannerManager::DidStartNavigation( | 119 params, base::Bind(&AppBannerManager::OnDidGetManifest, GetWeakPtr())); |
| 95 content::NavigationHandle* navigation_handle) { | 120 } |
| 96 if (!navigation_handle->IsInMainFrame()) | 121 |
| 122 base::Closure AppBannerManager::FetchWebappSplashScreenImageCallback( | |
| 123 const std::string& webapp_id) { | |
| 124 return base::Closure(); | |
| 125 } | |
| 126 | |
| 127 AppBannerManager::AppBannerManager(content::WebContents* web_contents) | |
| 128 : content::WebContentsObserver(web_contents), | |
| 129 SiteEngagementObserver(nullptr), | |
| 130 manager_(nullptr), | |
| 131 event_request_id_(-1), | |
| 132 is_active_(false), | |
| 133 banner_request_queued_(false), | |
| 134 load_finished_(false), | |
| 135 was_canceled_by_page_(false), | |
| 136 page_requested_prompt_(false), | |
| 137 is_debug_mode_(false), | |
| 138 weak_factory_(this) { | |
| 139 // Ensure the InstallableManager exists since we have a hard dependency on it. | |
| 140 InstallableManager::CreateForWebContents(web_contents); | |
| 141 manager_ = InstallableManager::FromWebContents(web_contents); | |
| 142 DCHECK(manager_); | |
| 143 | |
| 144 AppBannerSettingsHelper::UpdateFromFieldTrial(); | |
| 145 } | |
| 146 | |
| 147 AppBannerManager::~AppBannerManager() { } | |
| 148 | |
| 149 std::string AppBannerManager::GetAppIdentifier() { | |
| 150 DCHECK(!manifest_.IsEmpty()); | |
| 151 return manifest_.start_url.spec(); | |
| 152 } | |
| 153 | |
| 154 std::string AppBannerManager::GetBannerType() { | |
| 155 return "web"; | |
| 156 } | |
| 157 | |
| 158 std::string AppBannerManager::GetErrorParam(InstallableErrorCode code) { | |
| 159 if (code == NO_ACCEPTABLE_ICON || code == MANIFEST_MISSING_SUITABLE_ICON) { | |
| 160 return base::IntToString(InstallableManager::GetMinimumIconSizeInPx()); | |
| 161 } | |
| 162 | |
| 163 return std::string(); | |
| 164 } | |
| 165 | |
| 166 int AppBannerManager::GetIdealIconSizeInDp() { | |
| 167 return ConvertIconSizeFromPxToDp( | |
| 168 InstallableManager::GetMinimumIconSizeInPx()); | |
| 169 } | |
| 170 | |
| 171 int AppBannerManager::GetMinimumIconSizeInDp() { | |
| 172 return ConvertIconSizeFromPxToDp( | |
| 173 InstallableManager::GetMinimumIconSizeInPx()); | |
| 174 } | |
| 175 | |
| 176 base::WeakPtr<AppBannerManager> AppBannerManager::GetWeakPtr() { | |
| 177 return weak_factory_.GetWeakPtr(); | |
| 178 } | |
| 179 | |
| 180 bool AppBannerManager::IsDebugMode() const { | |
| 181 return is_debug_mode_ || | |
| 182 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 183 switches::kBypassAppBannerEngagementChecks); | |
| 184 } | |
| 185 | |
| 186 bool AppBannerManager::IsWebAppInstalled( | |
| 187 content::BrowserContext* browser_context, | |
| 188 const GURL& start_url) { | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 void AppBannerManager::OnDidGetManifest(const InstallableData& data) { | |
| 193 if (data.error_code != NO_ERROR_DETECTED) { | |
| 194 ReportError(web_contents(), data.error_code); | |
| 195 Stop(); | |
| 196 } | |
| 197 | |
| 198 if (!is_active_) | |
| 199 return; | |
| 200 | |
| 201 DCHECK(!data.manifest_url.is_empty()); | |
| 202 DCHECK(!data.manifest.IsEmpty()); | |
| 203 | |
| 204 manifest_url_ = data.manifest_url; | |
| 205 manifest_ = data.manifest; | |
| 206 app_title_ = (manifest_.name.is_null()) ? manifest_.short_name.string() | |
| 207 : manifest_.name.string(); | |
| 208 | |
| 209 ContinueInstallableCheck(); | |
| 210 } | |
| 211 | |
| 212 void AppBannerManager::ContinueInstallableCheck() { | |
| 213 if (IsWebAppInstalled(web_contents()->GetBrowserContext(), | |
| 214 manifest_.start_url) && | |
| 215 !IsDebugMode()) { | |
| 216 Stop(); | |
| 217 } | |
| 218 | |
| 219 if (!is_active_) | |
| 220 return; | |
| 221 | |
| 222 // Fetch and verify the other required information. | |
| 223 manager_->GetData( | |
| 224 GetWebAppParams(GetIdealIconSizeInDp(), GetMinimumIconSizeInDp()), | |
| 225 base::Bind(&AppBannerManager::OnDidFinishInstallableCheck, GetWeakPtr())); | |
| 226 } | |
| 227 | |
| 228 void AppBannerManager::OnDidFinishInstallableCheck( | |
| 229 const InstallableData& data) { | |
| 230 if (data.is_installable) | |
| 231 banners::TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_REQUESTED); | |
|
gone
2016/08/03 19:33:08
Do you need the banners:: namespace here? Seems f
dominickn
2016/08/04 02:51:07
Done.
| |
| 232 | |
| 233 if (data.error_code != NO_ERROR_DETECTED) { | |
| 234 if (data.error_code == NO_MATCHING_SERVICE_WORKER) | |
| 235 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER); | |
| 236 | |
| 237 ReportError(web_contents(), data.error_code); | |
| 238 Stop(); | |
| 239 } | |
| 240 | |
| 241 if (!is_active_) | |
| 242 return; | |
| 243 | |
| 244 DCHECK(data.is_installable); | |
| 245 DCHECK(!data.icon_url.is_empty()); | |
| 246 DCHECK(data.icon); | |
| 247 | |
| 248 icon_url_ = data.icon_url; | |
| 249 icon_.reset(new SkBitmap(*data.icon)); | |
| 250 | |
| 251 SendBannerPromptRequest(); | |
| 252 } | |
| 253 | |
| 254 void AppBannerManager::RecordDidShowBanner(const std::string& event_name) { | |
| 255 content::WebContents* contents = web_contents(); | |
| 256 DCHECK(contents); | |
| 257 | |
| 258 AppBannerSettingsHelper::RecordBannerEvent( | |
| 259 contents, validated_url_, GetAppIdentifier(), | |
| 260 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_SHOW, | |
| 261 GetCurrentTime()); | |
| 262 rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(), | |
| 263 event_name, | |
| 264 contents->GetLastCommittedURL()); | |
| 265 } | |
| 266 | |
| 267 void AppBannerManager::ReportError(content::WebContents* web_contents, | |
| 268 InstallableErrorCode code) { | |
| 269 if (IsDebugMode()) | |
| 270 LogErrorToConsole(web_contents, code, GetErrorParam(code)); | |
| 271 } | |
| 272 | |
| 273 void AppBannerManager::Stop() { | |
| 274 if (was_canceled_by_page_ && !page_requested_prompt_) { | |
| 275 TrackBeforeInstallEvent( | |
| 276 BEFORE_INSTALL_EVENT_PROMPT_NOT_CALLED_AFTER_PREVENT_DEFAULT); | |
| 277 } | |
| 278 | |
| 279 is_active_ = false; | |
| 280 was_canceled_by_page_ = false; | |
| 281 page_requested_prompt_ = false; | |
| 282 referrer_.erase(); | |
| 283 } | |
| 284 | |
| 285 void AppBannerManager::SendBannerPromptRequest() { | |
| 286 RecordCouldShowBanner(); | |
| 287 | |
| 288 // Given all of the other checks that have been made, the only possible reason | |
| 289 // for stopping now is that the banner has been added to the homescreen. | |
|
gone
2016/08/03 19:33:08
The comment is a little weird. The banner was add
dominickn
2016/08/04 02:51:07
Done.
| |
| 290 if (!IsDebugMode() && !CheckIfShouldShowBanner()) | |
| 291 Stop(); | |
| 292 | |
| 293 if (!is_active_) | |
| 294 return; | |
| 295 | |
| 296 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_CREATED); | |
| 297 event_request_id_ = ++gCurrentRequestID; | |
| 298 content::RenderFrameHost* frame = web_contents()->GetMainFrame(); | |
| 299 frame->Send(new ChromeViewMsg_AppBannerPromptRequest( | |
| 300 frame->GetRoutingID(), event_request_id_, GetBannerType())); | |
| 301 } | |
| 302 | |
| 303 void AppBannerManager::DidStartNavigation(content::NavigationHandle* handle) { | |
| 304 if (!handle->IsInMainFrame()) | |
| 97 return; | 305 return; |
| 98 | 306 |
| 99 load_finished_ = false; | 307 load_finished_ = false; |
| 100 if (AppBannerSettingsHelper::ShouldUseSiteEngagementScore() && | 308 if (AppBannerSettingsHelper::ShouldUseSiteEngagementScore() && |
| 101 GetSiteEngagementService() == nullptr) { | 309 GetSiteEngagementService() == nullptr) { |
| 102 // Ensure that we are observing the site engagement service on navigation | 310 // Ensure that we are observing the site engagement service on navigation |
| 103 // start. This may be the first navigation, or we may have stopped | 311 // start. This may be the first navigation, or we may have stopped |
| 104 // observing if the banner flow was triggered on the previous page. | 312 // observing if the banner flow was triggered on the previous page. |
| 105 SiteEngagementObserver::Observe(SiteEngagementService::Get( | 313 SiteEngagementObserver::Observe(SiteEngagementService::Get( |
| 106 Profile::FromBrowserContext(web_contents()->GetBrowserContext()))); | 314 Profile::FromBrowserContext(web_contents()->GetBrowserContext()))); |
| 107 } | 315 } |
| 108 } | 316 } |
| 109 | 317 |
| 110 void AppBannerManager::DidFinishNavigation( | 318 void AppBannerManager::DidFinishNavigation(content::NavigationHandle* handle) { |
| 111 content::NavigationHandle* navigation_handle) { | 319 if (handle->IsInMainFrame() && handle->HasCommitted()) { |
| 112 if (navigation_handle->HasCommitted()) | 320 last_transition_type_ = handle->GetPageTransition(); |
| 113 last_transition_type_ = navigation_handle->GetPageTransition(); | 321 active_media_players_.clear(); |
| 322 if (is_active_) | |
| 323 Stop(); | |
| 324 } | |
| 114 } | 325 } |
| 115 | 326 |
| 116 void AppBannerManager::DidFinishLoad( | 327 void AppBannerManager::DidFinishLoad( |
| 117 content::RenderFrameHost* render_frame_host, | 328 content::RenderFrameHost* render_frame_host, |
| 118 const GURL& validated_url) { | 329 const GURL& validated_url) { |
| 119 // Don't start the banner flow unless the main frame has finished loading. | 330 // Don't start the banner flow unless the main frame has finished loading. |
| 120 if (render_frame_host->GetParent()) | 331 if (render_frame_host->GetParent()) |
| 121 return; | 332 return; |
| 122 | 333 |
| 123 load_finished_ = true; | 334 load_finished_ = true; |
| 335 validated_url_ = validated_url; | |
| 124 if (!AppBannerSettingsHelper::ShouldUseSiteEngagementScore() || | 336 if (!AppBannerSettingsHelper::ShouldUseSiteEngagementScore() || |
| 125 banner_request_queued_) { | 337 banner_request_queued_) { |
| 126 banner_request_queued_ = false; | 338 banner_request_queued_ = false; |
| 127 | 339 |
| 128 // The third argument is the is_debug_mode boolean value, which is true only | |
| 129 // when it is triggered by the developer's action in DevTools. | |
| 130 RequestAppBanner(validated_url, false /* is_debug_mode */); | 340 RequestAppBanner(validated_url, false /* is_debug_mode */); |
| 131 } | 341 } |
| 132 } | 342 } |
| 133 | 343 |
| 134 void AppBannerManager::MediaStartedPlaying(const MediaPlayerId& id) { | 344 void AppBannerManager::MediaStartedPlaying(const MediaPlayerId& id) { |
| 135 active_media_players_.push_back(id); | 345 active_media_players_.push_back(id); |
| 136 } | 346 } |
| 137 | 347 |
| 138 void AppBannerManager::MediaStoppedPlaying(const MediaPlayerId& id) { | 348 void AppBannerManager::MediaStoppedPlaying(const MediaPlayerId& id) { |
| 139 active_media_players_.erase(std::remove(active_media_players_.begin(), | 349 active_media_players_.erase(std::remove(active_media_players_.begin(), |
| 140 active_media_players_.end(), id), | 350 active_media_players_.end(), id), |
| 141 active_media_players_.end()); | 351 active_media_players_.end()); |
| 142 } | 352 } |
| 143 | 353 |
| 144 bool AppBannerManager::HandleNonWebApp(const std::string& platform, | 354 void AppBannerManager::WebContentsDestroyed() { |
| 145 const GURL& url, | 355 Stop(); |
| 146 const std::string& id, | |
| 147 bool is_debug_mode) { | |
| 148 return false; | |
| 149 } | 356 } |
| 150 | 357 |
| 151 void AppBannerManager::OnEngagementIncreased(content::WebContents* contents, | 358 void AppBannerManager::OnEngagementIncreased(content::WebContents* contents, |
| 152 const GURL& url, | 359 const GURL& url, |
| 153 double score) { | 360 double score) { |
| 154 // Only trigger a banner using site engagement if: | 361 // Only trigger a banner using site engagement if: |
| 155 // 1. engagement increased for the web contents which we are attached to; and | 362 // 1. engagement increased for the web contents which we are attached to; and |
| 156 // 2. there are no currently active media players; and | 363 // 2. there are no currently active media players; and |
| 157 // 3. we have accumulated sufficient engagement. | 364 // 3. we have accumulated sufficient engagement. |
| 158 if (web_contents() == contents && active_media_players_.empty() && | 365 if (web_contents() == contents && active_media_players_.empty() && |
| 159 AppBannerSettingsHelper::HasSufficientEngagement(score)) { | 366 AppBannerSettingsHelper::HasSufficientEngagement(score)) { |
| 160 // Stop observing so we don't double-trigger the banner. | 367 // Stop observing so we don't double-trigger the banner. |
| 161 SiteEngagementObserver::Observe(nullptr); | 368 SiteEngagementObserver::Observe(nullptr); |
| 162 | 369 |
| 163 if (!load_finished_) { | 370 if (!load_finished_) { |
| 164 // Wait until the main frame finishes loading before requesting a banner. | 371 // Wait until the main frame finishes loading before requesting a banner. |
| 165 banner_request_queued_ = true; | 372 banner_request_queued_ = true; |
| 166 } else { | 373 } else { |
| 167 // Requesting a banner performs some simple tests, creates a data fetcher, | 374 // Requesting a banner performs some simple tests, creates a data fetcher, |
| 168 // and starts some asynchronous checks to test installability. It should | 375 // and starts some asynchronous checks to test installability. It should |
| 169 // be safe to start this in response to user input. | 376 // be safe to start this in response to user input. |
| 170 RequestAppBanner(url, false /* is_debug_mode */); | 377 RequestAppBanner(url, false /* is_debug_mode */); |
| 171 } | 378 } |
| 172 } | 379 } |
| 173 } | 380 } |
| 174 | 381 |
| 175 void AppBannerManager::CancelActiveFetcher() { | 382 void AppBannerManager::RecordCouldShowBanner() { |
| 176 if (data_fetcher_) { | 383 content::WebContents* contents = web_contents(); |
| 177 data_fetcher_->Cancel(); | 384 DCHECK(contents); |
| 178 data_fetcher_ = nullptr; | 385 |
| 386 AppBannerSettingsHelper::RecordBannerCouldShowEvent( | |
| 387 contents, validated_url_, GetAppIdentifier(), | |
| 388 GetCurrentTime(), last_transition_type_); | |
| 389 } | |
| 390 | |
| 391 bool AppBannerManager::CheckIfShouldShowBanner() { | |
| 392 content::WebContents* contents = web_contents(); | |
| 393 DCHECK(contents); | |
| 394 | |
| 395 return AppBannerSettingsHelper::ShouldShowBanner( | |
| 396 contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); | |
| 397 } | |
| 398 | |
| 399 bool AppBannerManager::OnMessageReceived( | |
| 400 const IPC::Message& message, | |
| 401 content::RenderFrameHost* render_frame_host) { | |
| 402 bool handled = true; | |
| 403 | |
| 404 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(AppBannerManager, message, render_frame_host) | |
| 405 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AppBannerPromptReply, | |
| 406 OnBannerPromptReply) | |
| 407 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestShowAppBanner, | |
| 408 OnRequestShowAppBanner) | |
| 409 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 410 IPC_END_MESSAGE_MAP() | |
| 411 | |
| 412 return handled; | |
| 413 } | |
| 414 | |
| 415 void AppBannerManager::OnBannerPromptReply( | |
| 416 content::RenderFrameHost* render_frame_host, | |
| 417 int request_id, | |
| 418 blink::WebAppBannerPromptReply reply, | |
| 419 std::string referrer) { | |
| 420 content::WebContents* contents = web_contents(); | |
| 421 if (request_id != event_request_id_) | |
| 422 return; | |
| 423 | |
| 424 // The renderer might have requested the prompt to be canceled. | |
| 425 // They may request that it is redisplayed later, so don't Stop() here. | |
| 426 // However, log that the cancelation was requested, so Stop() can be | |
| 427 // called if a redisplay isn't asked for. | |
| 428 // | |
| 429 // We use the additional page_requested_prompt_ variable because the redisplay | |
| 430 // request may be received *before* the Cancel prompt reply (e.g. if redisplay | |
| 431 // is requested in the beforeinstallprompt event handler). | |
| 432 referrer_ = referrer; | |
| 433 if (reply == blink::WebAppBannerPromptReply::Cancel && | |
| 434 !page_requested_prompt_) { | |
| 435 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED); | |
| 436 was_canceled_by_page_ = true; | |
| 437 ReportError(contents, RENDERER_CANCELLED); | |
| 438 return; | |
| 439 } | |
| 440 | |
| 441 // If we haven't yet returned, but either of |was_canceled_by_page_| or | |
| 442 // |page_requested_prompt_| is true, the page has requested a delayed showing | |
| 443 // of the prompt. Otherwise, the prompt was never canceled by the page. | |
| 444 if (was_canceled_by_page_ || page_requested_prompt_) { | |
| 445 TrackBeforeInstallEvent( | |
| 446 BEFORE_INSTALL_EVENT_PROMPT_CALLED_AFTER_PREVENT_DEFAULT); | |
| 447 was_canceled_by_page_ = false; | |
| 448 } else { | |
| 449 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_NO_ACTION); | |
| 450 } | |
| 451 | |
| 452 AppBannerSettingsHelper::RecordMinutesFromFirstVisitToShow( | |
| 453 contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); | |
| 454 | |
| 455 DCHECK(!manifest_url_.is_empty()); | |
| 456 DCHECK(!manifest_.IsEmpty()); | |
| 457 DCHECK(!icon_url_.is_empty()); | |
| 458 DCHECK(icon_.get()); | |
| 459 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_COMPLETE); | |
| 460 ShowBanner(); | |
| 461 is_active_ = false; | |
| 462 } | |
| 463 | |
| 464 void AppBannerManager::OnRequestShowAppBanner( | |
| 465 content::RenderFrameHost* render_frame_host, | |
| 466 int request_id) { | |
| 467 if (was_canceled_by_page_) { | |
| 468 // Simulate a non-canceled OnBannerPromptReply to show the delayed banner. | |
| 469 // Don't reset |was_canceled_by_page_| yet for metrics purposes. | |
| 470 OnBannerPromptReply(render_frame_host, request_id, | |
| 471 blink::WebAppBannerPromptReply::None, referrer_); | |
| 472 } else { | |
| 473 // Log that the prompt request was made for when we get the prompt reply. | |
| 474 page_requested_prompt_ = true; | |
| 179 } | 475 } |
| 180 } | 476 } |
| 181 | 477 |
| 182 } // namespace banners | 478 } // namespace banners |
| OLD | NEW |