Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Unified Diff: chrome/browser/banners/app_banner_manager.cc

Issue 2778983005: Allow the app banner installability check to run on page load. (Closed)
Patch Set: Address comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/banners/app_banner_manager.cc
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 726f25f9b08860ac1725368595806c6207059fe0..7016833f23c203ae5fbf15868cddb5de041c3798 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/banners/app_banner_metrics.h"
@@ -15,6 +16,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "components/rappor/public/rappor_utils.h"
#include "components/rappor/rappor_service_impl.h"
@@ -69,16 +71,15 @@ void AppBannerManager::RequestAppBanner(const GURL& validated_url,
// Don't start a redundant banner request. Otherwise, if one is running,
// invalidate our weak pointers so it terminates.
- if (is_active_) {
+ if (is_active()) {
if (URLsAreForTheSamePage(validated_url, contents->GetLastCommittedURL()))
return;
else
weak_factory_.InvalidateWeakPtrs();
}
- is_active_ = true;
+ UpdateState(State::ACTIVE);
is_debug_mode_ = is_debug_mode;
- was_canceled_by_page_ = false;
page_requested_prompt_ = false;
// We only need to call ReportStatus if we aren't in debug mode (this avoids
@@ -141,14 +142,14 @@ void AppBannerManager::SendBannerDismissed(int request_id) {
AppBannerManager::AppBannerManager(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
- SiteEngagementObserver(nullptr),
+ SiteEngagementObserver(SiteEngagementService::Get(
+ Profile::FromBrowserContext(web_contents->GetBrowserContext()))),
+ state_(State::INACTIVE),
manager_(nullptr),
event_request_id_(-1),
binding_(this),
- is_active_(false),
- banner_request_queued_(false),
+ has_sufficient_engagement_(false),
load_finished_(false),
- was_canceled_by_page_(false),
page_requested_prompt_(false),
is_debug_mode_(false),
need_to_log_status_(false),
@@ -211,7 +212,7 @@ void AppBannerManager::OnDidGetManifest(const InstallableData& data) {
Stop();
}
- if (!is_active_)
+ if (!is_active())
return;
DCHECK(!data.manifest_url.is_empty());
@@ -258,7 +259,7 @@ void AppBannerManager::OnDidPerformInstallableCheck(
Stop();
}
- if (!is_active_)
+ if (!is_active())
return;
DCHECK(data.is_installable);
@@ -268,6 +269,15 @@ void AppBannerManager::OnDidPerformInstallableCheck(
primary_icon_url_ = data.primary_icon_url;
primary_icon_.reset(new SkBitmap(*data.primary_icon));
+ // If we triggered the installability check on page load, then it's possible
+ // we don't have enough engagement yet. If that's the case, return here but
+ // don't call Stop(). We wait for OnEngagementIncreased to tell us that we
+ // should trigger.
+ if (!has_sufficient_engagement_) {
+ UpdateState(State::PENDING_ENGAGEMENT);
+ return;
+ }
+
SendBannerPromptRequest();
}
@@ -305,10 +315,14 @@ void AppBannerManager::ResetCurrentPageData() {
}
void AppBannerManager::Stop() {
- if (was_canceled_by_page_ && !page_requested_prompt_) {
+ if (state_ == State::PENDING_EVENT && !page_requested_prompt_) {
TrackBeforeInstallEvent(
BEFORE_INSTALL_EVENT_PROMPT_NOT_CALLED_AFTER_PREVENT_DEFAULT);
ReportStatus(web_contents(), RENDERER_CANCELLED);
+ } else if (state_ == State::PENDING_ENGAGEMENT &&
+ !has_sufficient_engagement_) {
+ TrackDisplayEvent(DISPLAY_EVENT_NOT_VISITED_ENOUGH);
+ ReportStatus(web_contents(), INSUFFICIENT_ENGAGEMENT);
}
// In every non-debug run through the banner pipeline, we should have called
@@ -316,15 +330,16 @@ void AppBannerManager::Stop() {
// we don't is if we're still active and waiting for a callback from the
// InstallableManager (e.g. the renderer crashes or the browser is shutting
// down). These situations are explicitly not logged.
- DCHECK(!need_to_log_status_ || is_active_);
+ DCHECK(!need_to_log_status_ || is_active());
weak_factory_.InvalidateWeakPtrs();
binding_.Close();
controller_.reset();
event_.reset();
- is_active_ = false;
+ UpdateState(State::COMPLETE);
need_to_log_status_ = false;
+ has_sufficient_engagement_ = false;
}
void AppBannerManager::SendBannerPromptRequest() {
@@ -342,26 +357,29 @@ void AppBannerManager::SendBannerPromptRequest() {
base::Bind(&AppBannerManager::OnBannerPromptReply, GetWeakPtr()));
}
+void AppBannerManager::UpdateState(State state) {
+ state_ = state;
+
+ // If we are PENDING_EVENT, we must have sufficient engagement.
+ DCHECK(!is_pending_event() || has_sufficient_engagement_);
+}
+
void AppBannerManager::DidStartNavigation(content::NavigationHandle* handle) {
if (!handle->IsInMainFrame() || handle->IsSameDocument())
return;
+ if (is_active_or_pending())
+ Stop();
+ UpdateState(State::INACTIVE);
load_finished_ = false;
- if (GetSiteEngagementService() == nullptr) {
- // Ensure that we are observing the site engagement service on navigation
- // start. This may be the first navigation, or we may have stopped
- // observing if the banner flow was triggered on the previous page.
- SiteEngagementObserver::Observe(SiteEngagementService::Get(
- Profile::FromBrowserContext(web_contents()->GetBrowserContext())));
- }
+ has_sufficient_engagement_ = false;
+ page_requested_prompt_ = false;
}
void AppBannerManager::DidFinishNavigation(content::NavigationHandle* handle) {
if (handle->IsInMainFrame() && handle->HasCommitted() &&
!handle->IsSameDocument()) {
ResetCurrentPageData();
- if (is_active_)
- Stop();
}
}
@@ -374,13 +392,14 @@ void AppBannerManager::DidFinishLoad(
load_finished_ = true;
validated_url_ = validated_url;
- // Start the pipeline immediately if 0 engagement is required or if we've
- // queued a banner request.
- if (banner_request_queued_ ||
- AppBannerSettingsHelper::HasSufficientEngagement(0)) {
- SiteEngagementObserver::Observe(nullptr);
- banner_request_queued_ = false;
-
+ // Start the pipeline immediately if:
+ // 1. we have sufficient engagement, or
+ // 2. 0 engagement is required, or
+ // 3. the feature to start the installability check in load is enabled
+ if (has_sufficient_engagement_ ||
+ AppBannerSettingsHelper::HasSufficientEngagement(0) ||
+ base::FeatureList::IsEnabled(
+ features::kCheckInstallabilityForBannerOnLoad)) {
RequestAppBanner(validated_url, false /* is_debug_mode */);
}
}
@@ -404,22 +423,28 @@ void AppBannerManager::WebContentsDestroyed() {
void AppBannerManager::OnEngagementIncreased(content::WebContents* contents,
const GURL& url,
double score) {
+ // In the ACTIVE state, we may have triggered the installability check, but
+ // not checked engagement yet. In the INACTIVE or PENDING_ENGAGEMENT states,
+ // we are waiting for an engagement signal to trigger the pipeline.
+ if (is_complete() || is_pending_event())
+ return;
+
// Only trigger a banner using site engagement if:
// 1. engagement increased for the web contents which we are attached to; and
// 2. there are no currently active media players; and
// 3. we have accumulated sufficient engagement.
if (web_contents() == contents && active_media_players_.empty() &&
AppBannerSettingsHelper::HasSufficientEngagement(score)) {
- // Stop observing so we don't double-trigger the banner.
- SiteEngagementObserver::Observe(nullptr);
-
- if (!load_finished_) {
- // Queue the banner request until the main frame finishes loading.
- banner_request_queued_ = true;
- } else {
- // A banner request performs some simple tests, creates a data fetcher,
- // and starts some asynchronous checks to test installability. It should
- // be safe to start this in response to user input.
+ has_sufficient_engagement_ = true;
+
+ if (is_pending_engagement()) {
+ // We have already finished the installability eligibility checks. Proceed
+ // directly to sending the banner prompt request.
+ UpdateState(State::ACTIVE);
+ SendBannerPromptRequest();
+ } else if (load_finished_) {
+ // This performs some simple tests and starts async checks to test
+ // installability. It should be safe to start in response to user input.
RequestAppBanner(url, false /* is_debug_mode */);
}
}
@@ -482,29 +507,29 @@ void AppBannerManager::OnBannerPromptReply(
controller_.reset();
content::WebContents* contents = web_contents();
- // The renderer might have requested the prompt to be canceled.
- // They may request that it is redisplayed later, so don't Stop() here.
- // However, log that the cancelation was requested, so Stop() can be
- // called if a redisplay isn't asked for.
+ // The renderer might have requested the prompt to be canceled. They may
+ // request that it is redisplayed later, so don't Stop() here. However, log
+ // that the cancelation was requested, so Stop() can be called if a redisplay
+ // isn't asked for.
//
// We use the additional page_requested_prompt_ variable because the redisplay
// request may be received *before* the Cancel prompt reply (e.g. if redisplay
// is requested in the beforeinstallprompt event handler).
referrer_ = referrer;
- if (reply == blink::mojom::AppBannerPromptReply::CANCEL &&
- !page_requested_prompt_) {
+ if (reply == blink::mojom::AppBannerPromptReply::CANCEL) {
+ UpdateState(State::PENDING_EVENT);
TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED);
- was_canceled_by_page_ = true;
- return;
+ if (!page_requested_prompt_)
+ return;
}
- // If we haven't yet returned, but either of |was_canceled_by_page_| or
+ // If we haven't yet returned, but we're in the PENDING_EVENT state or
// |page_requested_prompt_| is true, the page has requested a delayed showing
// of the prompt. Otherwise, the prompt was never canceled by the page.
- if (was_canceled_by_page_ || page_requested_prompt_) {
+ if (is_pending_event()) {
TrackBeforeInstallEvent(
BEFORE_INSTALL_EVENT_PROMPT_CALLED_AFTER_PREVENT_DEFAULT);
- was_canceled_by_page_ = false;
+ UpdateState(State::ACTIVE);
} else {
TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_NO_ACTION);
}
@@ -519,13 +544,12 @@ void AppBannerManager::OnBannerPromptReply(
TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_COMPLETE);
ShowBanner();
- is_active_ = false;
+ UpdateState(State::COMPLETE);
}
void AppBannerManager::DisplayAppBanner() {
- if (was_canceled_by_page_) {
+ if (is_pending_event()) {
// Simulate a non-canceled OnBannerPromptReply to show the delayed banner.
- // Don't reset |was_canceled_by_page_| yet for metrics purposes.
OnBannerPromptReply(blink::mojom::AppBannerPromptReply::NONE, referrer_);
} else {
// Log that the prompt request was made for when we get the prompt reply.
« no previous file with comments | « chrome/browser/banners/app_banner_manager.h ('k') | chrome/browser/banners/app_banner_manager_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698