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_data_fetcher.h" | 5 #include "chrome/browser/banners/app_banner_data_fetcher.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | |
| 8 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 9 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 10 #include "chrome/browser/banners/app_banner_metrics.h" | 11 #include "chrome/browser/banners/app_banner_metrics.h" |
| 11 #include "chrome/browser/banners/app_banner_settings_helper.h" | 12 #include "chrome/browser/banners/app_banner_settings_helper.h" |
| 12 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h" | 13 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h" |
| 13 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/infobars/infobar_service.h" | 15 #include "chrome/browser/infobars/infobar_service.h" |
| 15 #include "chrome/browser/manifest/manifest_icon_selector.h" | 16 #include "chrome/browser/manifest/manifest_icon_selector.h" |
| 16 #include "chrome/browser/profiles/profile.h" | 17 #include "chrome/browser/profiles/profile.h" |
| 18 #include "chrome/common/chrome_switches.h" | |
| 17 #include "chrome/common/render_messages.h" | 19 #include "chrome/common/render_messages.h" |
| 18 #include "components/infobars/core/infobar.h" | 20 #include "components/infobars/core/infobar.h" |
| 19 #include "components/rappor/rappor_utils.h" | 21 #include "components/rappor/rappor_utils.h" |
| 20 #include "content/public/browser/browser_context.h" | 22 #include "content/public/browser/browser_context.h" |
| 21 #include "content/public/browser/browser_thread.h" | 23 #include "content/public/browser/browser_thread.h" |
| 22 #include "content/public/browser/navigation_details.h" | 24 #include "content/public/browser/navigation_details.h" |
| 23 #include "content/public/browser/render_frame_host.h" | 25 #include "content/public/browser/render_frame_host.h" |
| 24 #include "content/public/browser/service_worker_context.h" | 26 #include "content/public/browser/service_worker_context.h" |
| 25 #include "content/public/browser/storage_partition.h" | 27 #include "content/public/browser/storage_partition.h" |
| 26 #include "net/base/load_flags.h" | 28 #include "net/base/load_flags.h" |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 64 gTimeDeltaForTesting = base::TimeDelta::FromDays(days); | 66 gTimeDeltaForTesting = base::TimeDelta::FromDays(days); |
| 65 } | 67 } |
| 66 | 68 |
| 67 AppBannerDataFetcher::AppBannerDataFetcher( | 69 AppBannerDataFetcher::AppBannerDataFetcher( |
| 68 content::WebContents* web_contents, | 70 content::WebContents* web_contents, |
| 69 base::WeakPtr<Delegate> delegate, | 71 base::WeakPtr<Delegate> delegate, |
| 70 int ideal_icon_size) | 72 int ideal_icon_size) |
| 71 : WebContentsObserver(web_contents), | 73 : WebContentsObserver(web_contents), |
| 72 ideal_icon_size_(ideal_icon_size), | 74 ideal_icon_size_(ideal_icon_size), |
| 73 weak_delegate_(delegate), | 75 weak_delegate_(delegate), |
| 74 is_active_(false) { | 76 is_active_(false), |
| 77 log_err_(false) { | |
| 75 } | 78 } |
| 76 | 79 |
| 77 void AppBannerDataFetcher::Start(const GURL& validated_url) { | 80 void AppBannerDataFetcher::Start(const GURL& validated_url) { |
| 78 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 81 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 79 | 82 |
| 80 content::WebContents* web_contents = GetWebContents(); | 83 content::WebContents* web_contents = GetWebContents(); |
| 81 DCHECK(web_contents); | 84 DCHECK(web_contents); |
| 82 | 85 |
| 83 is_active_ = true; | 86 is_active_ = true; |
| 87 log_err_ = base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 88 switches::kBypassAppBannerEngagementChecks); | |
|
benwells
2015/05/07 01:57:20
Nit: I think this indenting isn't quite right.
Th
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 84 validated_url_ = validated_url; | 89 validated_url_ = validated_url; |
| 85 web_contents->GetManifest( | 90 web_contents->GetManifest( |
| 86 base::Bind(&AppBannerDataFetcher::OnDidGetManifest, this)); | 91 base::Bind(&AppBannerDataFetcher::OnDidGetManifest, this)); |
| 87 } | 92 } |
| 88 | 93 |
| 89 void AppBannerDataFetcher::Cancel() { | 94 void AppBannerDataFetcher::Cancel() { |
| 90 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 95 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 91 if (is_active_) { | 96 if (is_active_) { |
| 92 FOR_EACH_OBSERVER(Observer, observer_list_, | 97 FOR_EACH_OBSERVER(Observer, observer_list_, |
| 93 OnDecidedWhetherToShow(this, false)); | 98 OnDecidedWhetherToShow(this, false)); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 | 138 |
| 134 return handled; | 139 return handled; |
| 135 } | 140 } |
| 136 | 141 |
| 137 void AppBannerDataFetcher::OnBannerPromptReply( | 142 void AppBannerDataFetcher::OnBannerPromptReply( |
| 138 content::RenderFrameHost* render_frame_host, | 143 content::RenderFrameHost* render_frame_host, |
| 139 int request_id, | 144 int request_id, |
| 140 blink::WebAppBannerPromptReply reply) { | 145 blink::WebAppBannerPromptReply reply) { |
| 141 content::WebContents* web_contents = GetWebContents(); | 146 content::WebContents* web_contents = GetWebContents(); |
| 142 if (!is_active_ || !web_contents || request_id != gCurrentRequestID) { | 147 if (!is_active_ || !web_contents || request_id != gCurrentRequestID) { |
| 148 if (!is_active_) | |
|
benwells
2015/05/07 01:57:20
Nit: you need braces around all these statements a
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 149 SendDebugMessage( | |
| 150 "Banner not shown: display pipeline halted before " | |
|
benwells
2015/05/07 01:57:20
I think 'Banner not shown' is probably a touch too
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 151 "prompt reply checking."); | |
|
benwells
2015/05/07 01:57:20
For this error I think something like 'user naviga
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 152 else if (!web_contents) | |
| 153 SendDebugMessage( | |
| 154 "Banner not shown: no web content available for banner display."); | |
|
benwells
2015/05/07 01:57:19
Something like 'web contents closed.' would be goo
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 155 else if (request_id != gCurrentRequestID) | |
| 156 SendDebugMessage("Banner not shown: incorrect request id."); | |
|
benwells
2015/05/07 01:57:19
We shouldn't show the error in this case. This mea
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 143 Cancel(); | 157 Cancel(); |
| 144 return; | 158 return; |
| 145 } | 159 } |
| 146 | 160 |
| 147 // The renderer might have requested the prompt to be canceled. | 161 // The renderer might have requested the prompt to be canceled. |
| 148 if (reply == blink::WebAppBannerPromptReply::Cancel) { | 162 if (reply == blink::WebAppBannerPromptReply::Cancel) { |
| 149 // TODO(mlamouri,benwells): we should probably record that to behave | 163 // TODO(mlamouri,benwells): we should probably record that to behave |
| 150 // differently with regard to showing the banner. | 164 // differently with regard to showing the banner. |
| 165 SendDebugMessage("Banner not shown: renderer has requested the banner " | |
| 166 "prompt be cancelled."); | |
|
benwells
2015/05/07 01:57:20
Nit: indenting here is also off.
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 151 Cancel(); | 167 Cancel(); |
| 152 return; | 168 return; |
| 153 } | 169 } |
| 154 | 170 |
| 155 // Definitely going to show the banner now. | 171 // Definitely going to show the banner now. |
| 156 FOR_EACH_OBSERVER(Observer, observer_list_, | 172 FOR_EACH_OBSERVER(Observer, observer_list_, |
| 157 OnDecidedWhetherToShow(this, true)); | 173 OnDecidedWhetherToShow(this, true)); |
| 158 | 174 |
| 159 infobars::InfoBar* infobar = CreateBanner(app_icon_.get(), app_title_); | 175 infobars::InfoBar* infobar = CreateBanner(app_icon_.get(), app_title_); |
| 160 if (infobar) { | 176 if (infobar) { |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 226 rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(), | 242 rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(), |
| 227 event_name, | 243 event_name, |
| 228 web_contents->GetURL()); | 244 web_contents->GetURL()); |
| 229 banners::TrackDisplayEvent(DISPLAY_EVENT_CREATED); | 245 banners::TrackDisplayEvent(DISPLAY_EVENT_CREATED); |
| 230 } | 246 } |
| 231 | 247 |
| 232 void AppBannerDataFetcher::OnDidGetManifest( | 248 void AppBannerDataFetcher::OnDidGetManifest( |
| 233 const content::Manifest& manifest) { | 249 const content::Manifest& manifest) { |
| 234 content::WebContents* web_contents = GetWebContents(); | 250 content::WebContents* web_contents = GetWebContents(); |
| 235 if (!is_active_ || !web_contents || manifest.IsEmpty()) { | 251 if (!is_active_ || !web_contents || manifest.IsEmpty()) { |
| 252 if (!is_active_) | |
| 253 SendDebugMessage("Banner not shown: display pipeline halted before " | |
| 254 "manifest checking."); | |
| 255 else if (!web_contents) | |
| 256 SendDebugMessage( | |
| 257 "Banner not shown: no web content available for banner display."); | |
| 258 else if (manifest.IsEmpty()) | |
| 259 SendDebugMessage("Banner not shown: manifest is empty."); | |
| 236 Cancel(); | 260 Cancel(); |
| 237 return; | 261 return; |
| 238 } | 262 } |
| 239 | 263 |
| 240 if (manifest.prefer_related_applications && | 264 if (manifest.prefer_related_applications && |
| 241 manifest.related_applications.size()) { | 265 manifest.related_applications.size()) { |
| 242 for (const auto& application : manifest.related_applications) { | 266 for (const auto& application : manifest.related_applications) { |
| 243 std::string platform = base::UTF16ToUTF8(application.platform.string()); | 267 std::string platform = base::UTF16ToUTF8(application.platform.string()); |
| 244 std::string id = base::UTF16ToUTF8(application.id.string()); | 268 std::string id = base::UTF16ToUTF8(application.id.string()); |
| 245 if (weak_delegate_->HandleNonWebApp(platform, application.url, id)) | 269 if (weak_delegate_->HandleNonWebApp(platform, application.url, id)) |
| 246 return; | 270 return; |
| 247 } | 271 } |
| 248 } | 272 } |
| 249 | 273 |
| 250 if (!IsManifestValidForWebApp(manifest)) { | 274 if (!IsManifestValidForWebApp(manifest)) { |
| 275 SendDebugMessage("Banner not shown: invalid manifest."); | |
|
benwells
2015/05/07 01:57:20
Can we move this output into IsManifestValidForWeb
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 251 Cancel(); | 276 Cancel(); |
| 252 return; | 277 return; |
| 253 } | 278 } |
| 254 | 279 |
| 255 banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED); | 280 banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED); |
| 256 | 281 |
| 257 web_app_data_ = manifest; | 282 web_app_data_ = manifest; |
| 258 app_title_ = web_app_data_.name.string(); | 283 app_title_ = web_app_data_.name.string(); |
| 259 | 284 |
| 260 // Check to see if there is a single service worker controlling this page | 285 // Check to see if there is a single service worker controlling this page |
| 261 // and the manifest's start url. | 286 // and the manifest's start url. |
| 262 Profile* profile = | 287 Profile* profile = |
| 263 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 288 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 264 content::StoragePartition* storage_partition = | 289 content::StoragePartition* storage_partition = |
| 265 content::BrowserContext::GetStoragePartition( | 290 content::BrowserContext::GetStoragePartition( |
| 266 profile, web_contents->GetSiteInstance()); | 291 profile, web_contents->GetSiteInstance()); |
| 267 DCHECK(storage_partition); | 292 DCHECK(storage_partition); |
| 268 | 293 |
| 269 storage_partition->GetServiceWorkerContext()->CheckHasServiceWorker( | 294 storage_partition->GetServiceWorkerContext()->CheckHasServiceWorker( |
| 270 validated_url_, manifest.start_url, | 295 validated_url_, manifest.start_url, |
| 271 base::Bind(&AppBannerDataFetcher::OnDidCheckHasServiceWorker, | 296 base::Bind(&AppBannerDataFetcher::OnDidCheckHasServiceWorker, |
| 272 this)); | 297 this)); |
| 273 } | 298 } |
| 274 | 299 |
| 275 void AppBannerDataFetcher::OnDidCheckHasServiceWorker( | 300 void AppBannerDataFetcher::OnDidCheckHasServiceWorker( |
| 276 bool has_service_worker) { | 301 bool has_service_worker) { |
| 277 content::WebContents* web_contents = GetWebContents(); | 302 content::WebContents* web_contents = GetWebContents(); |
| 278 if (!is_active_ || !web_contents) { | 303 if (!is_active_ || !web_contents) { |
| 304 if (!is_active_) | |
| 305 SendDebugMessage("Banner not shown: display pipeline halted before " | |
| 306 "manifest checking."); | |
|
benwells
2015/05/07 01:57:20
This code block is repeated a few times. Would be
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 307 else if (!web_contents) | |
| 308 SendDebugMessage( | |
| 309 "Banner not shown: no web content available for banner display."); | |
| 279 Cancel(); | 310 Cancel(); |
| 280 return; | 311 return; |
| 281 } | 312 } |
| 282 | 313 |
| 283 if (has_service_worker) { | 314 if (has_service_worker) { |
| 284 // Create an infobar to promote the manifest's app. | 315 // Create an infobar to promote the manifest's app. |
| 285 GURL icon_url = | 316 GURL icon_url = |
| 286 ManifestIconSelector::FindBestMatchingIcon( | 317 ManifestIconSelector::FindBestMatchingIcon( |
| 287 web_app_data_.icons, | 318 web_app_data_.icons, |
| 288 ideal_icon_size_, | 319 ideal_icon_size_, |
| 289 gfx::Screen::GetScreenFor(web_contents->GetNativeView())); | 320 gfx::Screen::GetScreenFor(web_contents->GetNativeView())); |
| 290 if (!icon_url.is_empty()) { | 321 if (!icon_url.is_empty()) { |
| 291 FetchIcon(icon_url); | 322 FetchIcon(icon_url); |
| 292 return; | 323 return; |
| 293 } | 324 } |
| 325 SendDebugMessage( | |
| 326 "Banner not shownL could not find icon specified in manifest"); | |
| 294 } else { | 327 } else { |
| 295 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER); | 328 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER); |
| 296 } | 329 } |
| 297 | 330 |
| 331 SendDebugMessage("Banner not shown: no service worker detected."); | |
|
benwells
2015/05/07 01:57:20
A common cause of this will be that the site is no
dominickn (DO NOT USE)
2015/05/07 05:37:24
Done.
| |
| 298 Cancel(); | 332 Cancel(); |
| 299 } | 333 } |
| 300 | 334 |
| 301 void AppBannerDataFetcher::OnFetchComplete(const GURL& url, | 335 void AppBannerDataFetcher::OnFetchComplete(const GURL& url, |
| 302 const SkBitmap* icon) { | 336 const SkBitmap* icon) { |
| 303 if (is_active_) | 337 if (is_active_) |
| 304 ShowBanner(icon); | 338 ShowBanner(icon); |
| 305 | 339 |
| 306 Release(); | 340 Release(); |
| 307 } | 341 } |
| 308 | 342 |
| 309 void AppBannerDataFetcher::ShowBanner(const SkBitmap* icon) { | 343 void AppBannerDataFetcher::ShowBanner(const SkBitmap* icon) { |
| 310 content::WebContents* web_contents = GetWebContents(); | 344 content::WebContents* web_contents = GetWebContents(); |
| 311 if (!is_active_ || !web_contents || !icon) { | 345 if (!is_active_ || !web_contents || !icon) { |
| 346 if (!is_active_) | |
| 347 SendDebugMessage( | |
| 348 "Banner not shown: display pipeline halted before banner display."); | |
| 349 else if (!web_contents) | |
| 350 SendDebugMessage( | |
| 351 "Banner not shown: no web content available for banner display."); | |
| 352 else if (!icon) | |
| 353 SendDebugMessage("Banner not shown: no icon available to display."); | |
| 312 Cancel(); | 354 Cancel(); |
| 313 return; | 355 return; |
| 314 } | 356 } |
| 315 | 357 |
| 316 RecordCouldShowBanner(); | 358 RecordCouldShowBanner(); |
| 317 if (!CheckIfShouldShowBanner()) { | 359 if (!CheckIfShouldShowBanner()) { |
| 360 SendDebugMessage("Banner not shown: engagement checks failed."); | |
| 318 Cancel(); | 361 Cancel(); |
| 319 return; | 362 return; |
| 320 } | 363 } |
| 321 | 364 |
| 365 SendDebugMessage("Showing banner."); | |
| 322 app_icon_.reset(new SkBitmap(*icon)); | 366 app_icon_.reset(new SkBitmap(*icon)); |
| 323 web_contents->GetMainFrame()->Send( | 367 web_contents->GetMainFrame()->Send( |
| 324 new ChromeViewMsg_AppBannerPromptRequest( | 368 new ChromeViewMsg_AppBannerPromptRequest( |
| 325 web_contents->GetMainFrame()->GetRoutingID(), | 369 web_contents->GetMainFrame()->GetRoutingID(), |
| 326 ++gCurrentRequestID, | 370 ++gCurrentRequestID, |
| 327 GetBannerType())); | 371 GetBannerType())); |
| 328 } | 372 } |
| 329 | 373 |
| 330 void AppBannerDataFetcher::RecordCouldShowBanner() { | 374 void AppBannerDataFetcher::RecordCouldShowBanner() { |
| 331 content::WebContents* web_contents = GetWebContents(); | 375 content::WebContents* web_contents = GetWebContents(); |
| 332 DCHECK(web_contents); | 376 DCHECK(web_contents); |
| 333 | 377 |
| 334 AppBannerSettingsHelper::RecordBannerEvent( | 378 AppBannerSettingsHelper::RecordBannerEvent( |
| 335 web_contents, validated_url_, GetAppIdentifier(), | 379 web_contents, validated_url_, GetAppIdentifier(), |
| 336 AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW, | 380 AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW, |
| 337 GetCurrentTime()); | 381 GetCurrentTime()); |
| 338 } | 382 } |
| 339 | 383 |
| 340 bool AppBannerDataFetcher::CheckIfShouldShowBanner() { | 384 bool AppBannerDataFetcher::CheckIfShouldShowBanner() { |
| 341 content::WebContents* web_contents = GetWebContents(); | 385 content::WebContents* web_contents = GetWebContents(); |
| 342 DCHECK(web_contents); | 386 DCHECK(web_contents); |
| 343 | 387 |
| 344 return AppBannerSettingsHelper::ShouldShowBanner( | 388 return AppBannerSettingsHelper::ShouldShowBanner( |
| 345 web_contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); | 389 web_contents, validated_url_, GetAppIdentifier(), GetCurrentTime()); |
| 346 } | 390 } |
| 347 | 391 |
| 392 void AppBannerDataFetcher::SendDebugMessage(const std::string& message) { | |
| 393 if (log_err_) { | |
| 394 content::WebContents* web_contents = GetWebContents(); | |
| 395 if (web_contents) { | |
| 396 web_contents->GetMainFrame()->Send( | |
| 397 new ChromeViewMsg_AppBannerDebugMessageRequest( | |
| 398 web_contents->GetMainFrame()->GetRoutingID(), message)); | |
| 399 } | |
| 400 } | |
| 401 } | |
| 402 | |
| 348 // static | 403 // static |
| 349 bool AppBannerDataFetcher::IsManifestValidForWebApp( | 404 bool AppBannerDataFetcher::IsManifestValidForWebApp( |
| 350 const content::Manifest& manifest) { | 405 const content::Manifest& manifest) { |
| 351 if (manifest.IsEmpty()) | 406 if (manifest.IsEmpty()) |
| 352 return false; | 407 return false; |
| 353 if (!manifest.start_url.is_valid()) | 408 if (!manifest.start_url.is_valid()) |
| 354 return false; | 409 return false; |
| 355 if (manifest.name.is_null() && manifest.short_name.is_null()) | 410 if (manifest.name.is_null() && manifest.short_name.is_null()) |
| 356 return false; | 411 return false; |
| 357 if (!DoesManifestContainRequiredIcon(manifest)) | 412 if (!DoesManifestContainRequiredIcon(manifest)) |
| 358 return false; | 413 return false; |
| 359 return true; | 414 return true; |
| 360 } | 415 } |
| 361 | 416 |
| 362 } // namespace banners | 417 } // namespace banners |
| OLD | NEW |