| 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 <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/feature_list.h" |
| 11 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 12 #include "base/time/time.h" | 13 #include "base/time/time.h" |
| 13 #include "chrome/browser/banners/app_banner_metrics.h" | 14 #include "chrome/browser/banners/app_banner_metrics.h" |
| 14 #include "chrome/browser/banners/app_banner_settings_helper.h" | 15 #include "chrome/browser/banners/app_banner_settings_helper.h" |
| 15 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
| 16 #include "chrome/browser/engagement/site_engagement_service.h" | 17 #include "chrome/browser/engagement/site_engagement_service.h" |
| 17 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
| 19 #include "chrome/common/chrome_features.h" |
| 18 #include "chrome/common/chrome_switches.h" | 20 #include "chrome/common/chrome_switches.h" |
| 19 #include "components/rappor/public/rappor_utils.h" | 21 #include "components/rappor/public/rappor_utils.h" |
| 20 #include "components/rappor/rappor_service_impl.h" | 22 #include "components/rappor/rappor_service_impl.h" |
| 21 #include "content/public/browser/navigation_handle.h" | 23 #include "content/public/browser/navigation_handle.h" |
| 22 #include "content/public/browser/render_frame_host.h" | 24 #include "content/public/browser/render_frame_host.h" |
| 23 #include "content/public/browser/web_contents.h" | 25 #include "content/public/browser/web_contents.h" |
| 24 #include "mojo/public/cpp/bindings/interface_request.h" | 26 #include "mojo/public/cpp/bindings/interface_request.h" |
| 25 #include "services/service_manager/public/cpp/interface_provider.h" | 27 #include "services/service_manager/public/cpp/interface_provider.h" |
| 26 #include "third_party/WebKit/public/platform/modules/installation/installation.m
ojom.h" | 28 #include "third_party/WebKit/public/platform/modules/installation/installation.m
ojom.h" |
| 27 #include "third_party/skia/include/core/SkBitmap.h" | 29 #include "third_party/skia/include/core/SkBitmap.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 first.path_piece() == second.path_piece() && | 64 first.path_piece() == second.path_piece() && |
| 63 first.query_piece() == second.query_piece(); | 65 first.query_piece() == second.query_piece(); |
| 64 } | 66 } |
| 65 | 67 |
| 66 void AppBannerManager::RequestAppBanner(const GURL& validated_url, | 68 void AppBannerManager::RequestAppBanner(const GURL& validated_url, |
| 67 bool is_debug_mode) { | 69 bool is_debug_mode) { |
| 68 content::WebContents* contents = web_contents(); | 70 content::WebContents* contents = web_contents(); |
| 69 | 71 |
| 70 // Don't start a redundant banner request. Otherwise, if one is running, | 72 // Don't start a redundant banner request. Otherwise, if one is running, |
| 71 // invalidate our weak pointers so it terminates. | 73 // invalidate our weak pointers so it terminates. |
| 72 if (is_active_) { | 74 if (is_active()) { |
| 73 if (URLsAreForTheSamePage(validated_url, contents->GetLastCommittedURL())) | 75 if (URLsAreForTheSamePage(validated_url, contents->GetLastCommittedURL())) |
| 74 return; | 76 return; |
| 75 else | 77 else |
| 76 weak_factory_.InvalidateWeakPtrs(); | 78 weak_factory_.InvalidateWeakPtrs(); |
| 77 } | 79 } |
| 78 | 80 |
| 79 is_active_ = true; | 81 UpdateState(State::ACTIVE); |
| 80 is_debug_mode_ = is_debug_mode; | 82 is_debug_mode_ = is_debug_mode; |
| 81 was_canceled_by_page_ = false; | |
| 82 page_requested_prompt_ = false; | 83 page_requested_prompt_ = false; |
| 83 | 84 |
| 84 // We only need to call ReportStatus if we aren't in debug mode (this avoids | 85 // We only need to call ReportStatus if we aren't in debug mode (this avoids |
| 85 // skew from testing). | 86 // skew from testing). |
| 86 DCHECK(!need_to_log_status_); | 87 DCHECK(!need_to_log_status_); |
| 87 need_to_log_status_ = !IsDebugMode(); | 88 need_to_log_status_ = !IsDebugMode(); |
| 88 | 89 |
| 89 // Exit if this is an incognito window, non-main frame, or insecure context. | 90 // Exit if this is an incognito window, non-main frame, or insecure context. |
| 90 InstallableStatusCode code = NO_ERROR_DETECTED; | 91 InstallableStatusCode code = NO_ERROR_DETECTED; |
| 91 if (Profile::FromBrowserContext(contents->GetBrowserContext()) | 92 if (Profile::FromBrowserContext(contents->GetBrowserContext()) |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 void AppBannerManager::SendBannerDismissed(int request_id) { | 135 void AppBannerManager::SendBannerDismissed(int request_id) { |
| 135 if (request_id != gCurrentRequestID) | 136 if (request_id != gCurrentRequestID) |
| 136 return; | 137 return; |
| 137 | 138 |
| 138 DCHECK(event_.is_bound()); | 139 DCHECK(event_.is_bound()); |
| 139 event_->BannerDismissed(); | 140 event_->BannerDismissed(); |
| 140 } | 141 } |
| 141 | 142 |
| 142 AppBannerManager::AppBannerManager(content::WebContents* web_contents) | 143 AppBannerManager::AppBannerManager(content::WebContents* web_contents) |
| 143 : content::WebContentsObserver(web_contents), | 144 : content::WebContentsObserver(web_contents), |
| 144 SiteEngagementObserver(nullptr), | 145 SiteEngagementObserver(SiteEngagementService::Get( |
| 146 Profile::FromBrowserContext(web_contents->GetBrowserContext()))), |
| 147 state_(State::INACTIVE), |
| 145 manager_(nullptr), | 148 manager_(nullptr), |
| 146 event_request_id_(-1), | 149 event_request_id_(-1), |
| 147 binding_(this), | 150 binding_(this), |
| 148 is_active_(false), | 151 has_sufficient_engagement_(false), |
| 149 banner_request_queued_(false), | |
| 150 load_finished_(false), | 152 load_finished_(false), |
| 151 was_canceled_by_page_(false), | |
| 152 page_requested_prompt_(false), | 153 page_requested_prompt_(false), |
| 153 is_debug_mode_(false), | 154 is_debug_mode_(false), |
| 154 need_to_log_status_(false), | 155 need_to_log_status_(false), |
| 155 weak_factory_(this) { | 156 weak_factory_(this) { |
| 156 // Ensure the InstallableManager exists since we have a hard dependency on it. | 157 // Ensure the InstallableManager exists since we have a hard dependency on it. |
| 157 InstallableManager::CreateForWebContents(web_contents); | 158 InstallableManager::CreateForWebContents(web_contents); |
| 158 manager_ = InstallableManager::FromWebContents(web_contents); | 159 manager_ = InstallableManager::FromWebContents(web_contents); |
| 159 DCHECK(manager_); | 160 DCHECK(manager_); |
| 160 | 161 |
| 161 AppBannerSettingsHelper::UpdateFromFieldTrial(); | 162 AppBannerSettingsHelper::UpdateFromFieldTrial(); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 const GURL& manifest_url) { | 205 const GURL& manifest_url) { |
| 205 return false; | 206 return false; |
| 206 } | 207 } |
| 207 | 208 |
| 208 void AppBannerManager::OnDidGetManifest(const InstallableData& data) { | 209 void AppBannerManager::OnDidGetManifest(const InstallableData& data) { |
| 209 if (data.error_code != NO_ERROR_DETECTED) { | 210 if (data.error_code != NO_ERROR_DETECTED) { |
| 210 ReportStatus(web_contents(), data.error_code); | 211 ReportStatus(web_contents(), data.error_code); |
| 211 Stop(); | 212 Stop(); |
| 212 } | 213 } |
| 213 | 214 |
| 214 if (!is_active_) | 215 if (!is_active()) |
| 215 return; | 216 return; |
| 216 | 217 |
| 217 DCHECK(!data.manifest_url.is_empty()); | 218 DCHECK(!data.manifest_url.is_empty()); |
| 218 DCHECK(!data.manifest.IsEmpty()); | 219 DCHECK(!data.manifest.IsEmpty()); |
| 219 | 220 |
| 220 manifest_url_ = data.manifest_url; | 221 manifest_url_ = data.manifest_url; |
| 221 manifest_ = data.manifest; | 222 manifest_ = data.manifest; |
| 222 app_title_ = (manifest_.name.is_null()) ? manifest_.short_name.string() | 223 app_title_ = (manifest_.name.is_null()) ? manifest_.short_name.string() |
| 223 : manifest_.name.string(); | 224 : manifest_.name.string(); |
| 224 | 225 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 251 TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_REQUESTED); | 252 TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_REQUESTED); |
| 252 | 253 |
| 253 if (data.error_code != NO_ERROR_DETECTED) { | 254 if (data.error_code != NO_ERROR_DETECTED) { |
| 254 if (data.error_code == NO_MATCHING_SERVICE_WORKER) | 255 if (data.error_code == NO_MATCHING_SERVICE_WORKER) |
| 255 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER); | 256 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER); |
| 256 | 257 |
| 257 ReportStatus(web_contents(), data.error_code); | 258 ReportStatus(web_contents(), data.error_code); |
| 258 Stop(); | 259 Stop(); |
| 259 } | 260 } |
| 260 | 261 |
| 261 if (!is_active_) | 262 if (!is_active()) |
| 262 return; | 263 return; |
| 263 | 264 |
| 264 DCHECK(data.is_installable); | 265 DCHECK(data.is_installable); |
| 265 DCHECK(!data.primary_icon_url.is_empty()); | 266 DCHECK(!data.primary_icon_url.is_empty()); |
| 266 DCHECK(data.primary_icon); | 267 DCHECK(data.primary_icon); |
| 267 | 268 |
| 268 primary_icon_url_ = data.primary_icon_url; | 269 primary_icon_url_ = data.primary_icon_url; |
| 269 primary_icon_.reset(new SkBitmap(*data.primary_icon)); | 270 primary_icon_.reset(new SkBitmap(*data.primary_icon)); |
| 270 | 271 |
| 272 // If we triggered the installability check on page load, then it's possible |
| 273 // we don't have enough engagement yet. If that's the case, return here but |
| 274 // don't call Stop(). We wait for OnEngagementIncreased to tell us that we |
| 275 // should trigger. |
| 276 if (!has_sufficient_engagement_) { |
| 277 UpdateState(State::PENDING_ENGAGEMENT); |
| 278 return; |
| 279 } |
| 280 |
| 271 SendBannerPromptRequest(); | 281 SendBannerPromptRequest(); |
| 272 } | 282 } |
| 273 | 283 |
| 274 void AppBannerManager::RecordDidShowBanner(const std::string& event_name) { | 284 void AppBannerManager::RecordDidShowBanner(const std::string& event_name) { |
| 275 content::WebContents* contents = web_contents(); | 285 content::WebContents* contents = web_contents(); |
| 276 DCHECK(contents); | 286 DCHECK(contents); |
| 277 | 287 |
| 278 AppBannerSettingsHelper::RecordBannerEvent( | 288 AppBannerSettingsHelper::RecordBannerEvent( |
| 279 contents, validated_url_, GetAppIdentifier(), | 289 contents, validated_url_, GetAppIdentifier(), |
| 280 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_SHOW, | 290 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_SHOW, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 298 | 308 |
| 299 void AppBannerManager::ResetCurrentPageData() { | 309 void AppBannerManager::ResetCurrentPageData() { |
| 300 active_media_players_.clear(); | 310 active_media_players_.clear(); |
| 301 manifest_ = content::Manifest(); | 311 manifest_ = content::Manifest(); |
| 302 manifest_url_ = GURL(); | 312 manifest_url_ = GURL(); |
| 303 validated_url_ = GURL(); | 313 validated_url_ = GURL(); |
| 304 referrer_.erase(); | 314 referrer_.erase(); |
| 305 } | 315 } |
| 306 | 316 |
| 307 void AppBannerManager::Stop() { | 317 void AppBannerManager::Stop() { |
| 308 if (was_canceled_by_page_ && !page_requested_prompt_) { | 318 if (state_ == State::PENDING_EVENT && !page_requested_prompt_) { |
| 309 TrackBeforeInstallEvent( | 319 TrackBeforeInstallEvent( |
| 310 BEFORE_INSTALL_EVENT_PROMPT_NOT_CALLED_AFTER_PREVENT_DEFAULT); | 320 BEFORE_INSTALL_EVENT_PROMPT_NOT_CALLED_AFTER_PREVENT_DEFAULT); |
| 311 ReportStatus(web_contents(), RENDERER_CANCELLED); | 321 ReportStatus(web_contents(), RENDERER_CANCELLED); |
| 322 } else if (state_ == State::PENDING_ENGAGEMENT && |
| 323 !has_sufficient_engagement_) { |
| 324 TrackDisplayEvent(DISPLAY_EVENT_NOT_VISITED_ENOUGH); |
| 325 ReportStatus(web_contents(), INSUFFICIENT_ENGAGEMENT); |
| 312 } | 326 } |
| 313 | 327 |
| 314 // In every non-debug run through the banner pipeline, we should have called | 328 // In every non-debug run through the banner pipeline, we should have called |
| 315 // ReportStatus() and set need_to_log_status_ to false. The only case where | 329 // ReportStatus() and set need_to_log_status_ to false. The only case where |
| 316 // we don't is if we're still active and waiting for a callback from the | 330 // we don't is if we're still active and waiting for a callback from the |
| 317 // InstallableManager (e.g. the renderer crashes or the browser is shutting | 331 // InstallableManager (e.g. the renderer crashes or the browser is shutting |
| 318 // down). These situations are explicitly not logged. | 332 // down). These situations are explicitly not logged. |
| 319 DCHECK(!need_to_log_status_ || is_active_); | 333 DCHECK(!need_to_log_status_ || is_active()); |
| 320 | 334 |
| 321 weak_factory_.InvalidateWeakPtrs(); | 335 weak_factory_.InvalidateWeakPtrs(); |
| 322 binding_.Close(); | 336 binding_.Close(); |
| 323 controller_.reset(); | 337 controller_.reset(); |
| 324 event_.reset(); | 338 event_.reset(); |
| 325 | 339 |
| 326 is_active_ = false; | 340 UpdateState(State::COMPLETE); |
| 327 need_to_log_status_ = false; | 341 need_to_log_status_ = false; |
| 342 has_sufficient_engagement_ = false; |
| 328 } | 343 } |
| 329 | 344 |
| 330 void AppBannerManager::SendBannerPromptRequest() { | 345 void AppBannerManager::SendBannerPromptRequest() { |
| 331 RecordCouldShowBanner(); | 346 RecordCouldShowBanner(); |
| 332 | 347 |
| 333 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_CREATED); | 348 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_CREATED); |
| 334 event_request_id_ = ++gCurrentRequestID; | 349 event_request_id_ = ++gCurrentRequestID; |
| 335 | 350 |
| 336 web_contents()->GetMainFrame()->GetRemoteInterfaces()->GetInterface( | 351 web_contents()->GetMainFrame()->GetRemoteInterfaces()->GetInterface( |
| 337 mojo::MakeRequest(&controller_)); | 352 mojo::MakeRequest(&controller_)); |
| 338 | 353 |
| 339 controller_->BannerPromptRequest( | 354 controller_->BannerPromptRequest( |
| 340 binding_.CreateInterfacePtrAndBind(), mojo::MakeRequest(&event_), | 355 binding_.CreateInterfacePtrAndBind(), mojo::MakeRequest(&event_), |
| 341 {GetBannerType()}, | 356 {GetBannerType()}, |
| 342 base::Bind(&AppBannerManager::OnBannerPromptReply, GetWeakPtr())); | 357 base::Bind(&AppBannerManager::OnBannerPromptReply, GetWeakPtr())); |
| 343 } | 358 } |
| 344 | 359 |
| 360 void AppBannerManager::UpdateState(State state) { |
| 361 state_ = state; |
| 362 |
| 363 // If we are PENDING_EVENT, we must have sufficient engagement. |
| 364 DCHECK(!is_pending_event() || has_sufficient_engagement_); |
| 365 } |
| 366 |
| 345 void AppBannerManager::DidStartNavigation(content::NavigationHandle* handle) { | 367 void AppBannerManager::DidStartNavigation(content::NavigationHandle* handle) { |
| 346 if (!handle->IsInMainFrame() || handle->IsSameDocument()) | 368 if (!handle->IsInMainFrame() || handle->IsSameDocument()) |
| 347 return; | 369 return; |
| 348 | 370 |
| 371 if (is_active_or_pending()) |
| 372 Stop(); |
| 373 UpdateState(State::INACTIVE); |
| 349 load_finished_ = false; | 374 load_finished_ = false; |
| 350 if (GetSiteEngagementService() == nullptr) { | 375 has_sufficient_engagement_ = false; |
| 351 // Ensure that we are observing the site engagement service on navigation | 376 page_requested_prompt_ = false; |
| 352 // start. This may be the first navigation, or we may have stopped | |
| 353 // observing if the banner flow was triggered on the previous page. | |
| 354 SiteEngagementObserver::Observe(SiteEngagementService::Get( | |
| 355 Profile::FromBrowserContext(web_contents()->GetBrowserContext()))); | |
| 356 } | |
| 357 } | 377 } |
| 358 | 378 |
| 359 void AppBannerManager::DidFinishNavigation(content::NavigationHandle* handle) { | 379 void AppBannerManager::DidFinishNavigation(content::NavigationHandle* handle) { |
| 360 if (handle->IsInMainFrame() && handle->HasCommitted() && | 380 if (handle->IsInMainFrame() && handle->HasCommitted() && |
| 361 !handle->IsSameDocument()) { | 381 !handle->IsSameDocument()) { |
| 362 ResetCurrentPageData(); | 382 ResetCurrentPageData(); |
| 363 if (is_active_) | |
| 364 Stop(); | |
| 365 } | 383 } |
| 366 } | 384 } |
| 367 | 385 |
| 368 void AppBannerManager::DidFinishLoad( | 386 void AppBannerManager::DidFinishLoad( |
| 369 content::RenderFrameHost* render_frame_host, | 387 content::RenderFrameHost* render_frame_host, |
| 370 const GURL& validated_url) { | 388 const GURL& validated_url) { |
| 371 // Don't start the banner flow unless the main frame has finished loading. | 389 // Don't start the banner flow unless the main frame has finished loading. |
| 372 if (render_frame_host->GetParent()) | 390 if (render_frame_host->GetParent()) |
| 373 return; | 391 return; |
| 374 | 392 |
| 375 load_finished_ = true; | 393 load_finished_ = true; |
| 376 validated_url_ = validated_url; | 394 validated_url_ = validated_url; |
| 377 // Start the pipeline immediately if 0 engagement is required or if we've | 395 // Start the pipeline immediately if: |
| 378 // queued a banner request. | 396 // 1. we have sufficient engagement, or |
| 379 if (banner_request_queued_ || | 397 // 2. 0 engagement is required, or |
| 380 AppBannerSettingsHelper::HasSufficientEngagement(0)) { | 398 // 3. the feature to start the installability check in load is enabled |
| 381 SiteEngagementObserver::Observe(nullptr); | 399 if (has_sufficient_engagement_ || |
| 382 banner_request_queued_ = false; | 400 AppBannerSettingsHelper::HasSufficientEngagement(0) || |
| 383 | 401 base::FeatureList::IsEnabled( |
| 402 features::kCheckInstallabilityForBannerOnLoad)) { |
| 384 RequestAppBanner(validated_url, false /* is_debug_mode */); | 403 RequestAppBanner(validated_url, false /* is_debug_mode */); |
| 385 } | 404 } |
| 386 } | 405 } |
| 387 | 406 |
| 388 void AppBannerManager::MediaStartedPlaying(const MediaPlayerInfo& media_info, | 407 void AppBannerManager::MediaStartedPlaying(const MediaPlayerInfo& media_info, |
| 389 const MediaPlayerId& id) { | 408 const MediaPlayerId& id) { |
| 390 active_media_players_.push_back(id); | 409 active_media_players_.push_back(id); |
| 391 } | 410 } |
| 392 | 411 |
| 393 void AppBannerManager::MediaStoppedPlaying(const MediaPlayerInfo& media_info, | 412 void AppBannerManager::MediaStoppedPlaying(const MediaPlayerInfo& media_info, |
| 394 const MediaPlayerId& id) { | 413 const MediaPlayerId& id) { |
| 395 active_media_players_.erase(std::remove(active_media_players_.begin(), | 414 active_media_players_.erase(std::remove(active_media_players_.begin(), |
| 396 active_media_players_.end(), id), | 415 active_media_players_.end(), id), |
| 397 active_media_players_.end()); | 416 active_media_players_.end()); |
| 398 } | 417 } |
| 399 | 418 |
| 400 void AppBannerManager::WebContentsDestroyed() { | 419 void AppBannerManager::WebContentsDestroyed() { |
| 401 Stop(); | 420 Stop(); |
| 402 } | 421 } |
| 403 | 422 |
| 404 void AppBannerManager::OnEngagementIncreased(content::WebContents* contents, | 423 void AppBannerManager::OnEngagementIncreased(content::WebContents* contents, |
| 405 const GURL& url, | 424 const GURL& url, |
| 406 double score) { | 425 double score) { |
| 426 // In the ACTIVE state, we may have triggered the installability check, but |
| 427 // not checked engagement yet. In the INACTIVE or PENDING_ENGAGEMENT states, |
| 428 // we are waiting for an engagement signal to trigger the pipeline. |
| 429 if (is_complete() || is_pending_event()) |
| 430 return; |
| 431 |
| 407 // Only trigger a banner using site engagement if: | 432 // Only trigger a banner using site engagement if: |
| 408 // 1. engagement increased for the web contents which we are attached to; and | 433 // 1. engagement increased for the web contents which we are attached to; and |
| 409 // 2. there are no currently active media players; and | 434 // 2. there are no currently active media players; and |
| 410 // 3. we have accumulated sufficient engagement. | 435 // 3. we have accumulated sufficient engagement. |
| 411 if (web_contents() == contents && active_media_players_.empty() && | 436 if (web_contents() == contents && active_media_players_.empty() && |
| 412 AppBannerSettingsHelper::HasSufficientEngagement(score)) { | 437 AppBannerSettingsHelper::HasSufficientEngagement(score)) { |
| 413 // Stop observing so we don't double-trigger the banner. | 438 has_sufficient_engagement_ = true; |
| 414 SiteEngagementObserver::Observe(nullptr); | |
| 415 | 439 |
| 416 if (!load_finished_) { | 440 if (is_pending_engagement()) { |
| 417 // Queue the banner request until the main frame finishes loading. | 441 // We have already finished the installability eligibility checks. Proceed |
| 418 banner_request_queued_ = true; | 442 // directly to sending the banner prompt request. |
| 419 } else { | 443 UpdateState(State::ACTIVE); |
| 420 // A banner request performs some simple tests, creates a data fetcher, | 444 SendBannerPromptRequest(); |
| 421 // and starts some asynchronous checks to test installability. It should | 445 } else if (load_finished_) { |
| 422 // be safe to start this in response to user input. | 446 // This performs some simple tests and starts async checks to test |
| 447 // installability. It should be safe to start in response to user input. |
| 423 RequestAppBanner(url, false /* is_debug_mode */); | 448 RequestAppBanner(url, false /* is_debug_mode */); |
| 424 } | 449 } |
| 425 } | 450 } |
| 426 } | 451 } |
| 427 | 452 |
| 428 void AppBannerManager::RecordCouldShowBanner() { | 453 void AppBannerManager::RecordCouldShowBanner() { |
| 429 content::WebContents* contents = web_contents(); | 454 content::WebContents* contents = web_contents(); |
| 430 DCHECK(contents); | 455 DCHECK(contents); |
| 431 | 456 |
| 432 AppBannerSettingsHelper::RecordBannerEvent( | 457 AppBannerSettingsHelper::RecordBannerEvent( |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 } | 500 } |
| 476 | 501 |
| 477 void AppBannerManager::OnBannerPromptReply( | 502 void AppBannerManager::OnBannerPromptReply( |
| 478 blink::mojom::AppBannerPromptReply reply, | 503 blink::mojom::AppBannerPromptReply reply, |
| 479 const std::string& referrer) { | 504 const std::string& referrer) { |
| 480 // We don't need the controller any more, so reset it so the Blink-side object | 505 // We don't need the controller any more, so reset it so the Blink-side object |
| 481 // is destroyed. | 506 // is destroyed. |
| 482 controller_.reset(); | 507 controller_.reset(); |
| 483 content::WebContents* contents = web_contents(); | 508 content::WebContents* contents = web_contents(); |
| 484 | 509 |
| 485 // The renderer might have requested the prompt to be canceled. | 510 // The renderer might have requested the prompt to be canceled. They may |
| 486 // They may request that it is redisplayed later, so don't Stop() here. | 511 // request that it is redisplayed later, so don't Stop() here. However, log |
| 487 // However, log that the cancelation was requested, so Stop() can be | 512 // that the cancelation was requested, so Stop() can be called if a redisplay |
| 488 // called if a redisplay isn't asked for. | 513 // isn't asked for. |
| 489 // | 514 // |
| 490 // We use the additional page_requested_prompt_ variable because the redisplay | 515 // We use the additional page_requested_prompt_ variable because the redisplay |
| 491 // request may be received *before* the Cancel prompt reply (e.g. if redisplay | 516 // request may be received *before* the Cancel prompt reply (e.g. if redisplay |
| 492 // is requested in the beforeinstallprompt event handler). | 517 // is requested in the beforeinstallprompt event handler). |
| 493 referrer_ = referrer; | 518 referrer_ = referrer; |
| 494 if (reply == blink::mojom::AppBannerPromptReply::CANCEL && | 519 if (reply == blink::mojom::AppBannerPromptReply::CANCEL) { |
| 495 !page_requested_prompt_) { | 520 UpdateState(State::PENDING_EVENT); |
| 496 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED); | 521 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED); |
| 497 was_canceled_by_page_ = true; | 522 if (!page_requested_prompt_) |
| 498 return; | 523 return; |
| 499 } | 524 } |
| 500 | 525 |
| 501 // If we haven't yet returned, but either of |was_canceled_by_page_| or | 526 // If we haven't yet returned, but we're in the PENDING_EVENT state or |
| 502 // |page_requested_prompt_| is true, the page has requested a delayed showing | 527 // |page_requested_prompt_| is true, the page has requested a delayed showing |
| 503 // of the prompt. Otherwise, the prompt was never canceled by the page. | 528 // of the prompt. Otherwise, the prompt was never canceled by the page. |
| 504 if (was_canceled_by_page_ || page_requested_prompt_) { | 529 if (is_pending_event()) { |
| 505 TrackBeforeInstallEvent( | 530 TrackBeforeInstallEvent( |
| 506 BEFORE_INSTALL_EVENT_PROMPT_CALLED_AFTER_PREVENT_DEFAULT); | 531 BEFORE_INSTALL_EVENT_PROMPT_CALLED_AFTER_PREVENT_DEFAULT); |
| 507 was_canceled_by_page_ = false; | 532 UpdateState(State::ACTIVE); |
| 508 } else { | 533 } else { |
| 509 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_NO_ACTION); | 534 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_NO_ACTION); |
| 510 } | 535 } |
| 511 | 536 |
| 512 AppBannerSettingsHelper::RecordMinutesFromFirstVisitToShow( | 537 AppBannerSettingsHelper::RecordMinutesFromFirstVisitToShow( |
| 513 contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); | 538 contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); |
| 514 | 539 |
| 515 DCHECK(!manifest_url_.is_empty()); | 540 DCHECK(!manifest_url_.is_empty()); |
| 516 DCHECK(!manifest_.IsEmpty()); | 541 DCHECK(!manifest_.IsEmpty()); |
| 517 DCHECK(!primary_icon_url_.is_empty()); | 542 DCHECK(!primary_icon_url_.is_empty()); |
| 518 DCHECK(primary_icon_.get()); | 543 DCHECK(primary_icon_.get()); |
| 519 | 544 |
| 520 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_COMPLETE); | 545 TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_COMPLETE); |
| 521 ShowBanner(); | 546 ShowBanner(); |
| 522 is_active_ = false; | 547 UpdateState(State::COMPLETE); |
| 523 } | 548 } |
| 524 | 549 |
| 525 void AppBannerManager::DisplayAppBanner() { | 550 void AppBannerManager::DisplayAppBanner() { |
| 526 if (was_canceled_by_page_) { | 551 if (is_pending_event()) { |
| 527 // Simulate a non-canceled OnBannerPromptReply to show the delayed banner. | 552 // Simulate a non-canceled OnBannerPromptReply to show the delayed banner. |
| 528 // Don't reset |was_canceled_by_page_| yet for metrics purposes. | |
| 529 OnBannerPromptReply(blink::mojom::AppBannerPromptReply::NONE, referrer_); | 553 OnBannerPromptReply(blink::mojom::AppBannerPromptReply::NONE, referrer_); |
| 530 } else { | 554 } else { |
| 531 // Log that the prompt request was made for when we get the prompt reply. | 555 // Log that the prompt request was made for when we get the prompt reply. |
| 532 page_requested_prompt_ = true; | 556 page_requested_prompt_ = true; |
| 533 } | 557 } |
| 534 } | 558 } |
| 535 | 559 |
| 536 } // namespace banners | 560 } // namespace banners |
| OLD | NEW |