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