Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / banners / app_banner_data_fetcher.cc
bloba32ba763ea3ed522bd0b827f218c4918e9d17be8
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
3 // found in the LICENSE file.
5 #include "chrome/browser/banners/app_banner_data_fetcher.h"
7 #include "base/bind.h"
8 #include "base/strings/string_util.h"
9 #include "chrome/browser/banners/app_banner_metrics.h"
10 #include "chrome/browser/banners/app_banner_settings_helper.h"
11 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/infobars/infobar_service.h"
14 #include "chrome/browser/manifest/manifest_icon_selector.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/render_messages.h"
17 #include "components/infobars/core/infobar.h"
18 #include "components/rappor/rappor_utils.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/navigation_details.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/service_worker_context.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "net/base/load_flags.h"
26 #include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerPromptReply.h"
27 #include "ui/gfx/screen.h"
29 namespace {
31 base::TimeDelta gTimeDeltaForTesting;
32 int gCurrentRequestID = -1;
34 // The requirement for now is an image/png that is at least 144x144.
35 const int kIconMinimumSize = 144;
36 bool DoesManifestContainRequiredIcon(const content::Manifest& manifest) {
37 for (const auto& icon : manifest.icons) {
38 if (!EqualsASCII(icon.type.string(), "image/png"))
39 continue;
41 for (const auto& size : icon.sizes) {
42 if (size.IsEmpty()) // "any"
43 return true;
44 if (size.width() >= kIconMinimumSize && size.height() >= kIconMinimumSize)
45 return true;
49 return false;
52 } // anonymous namespace
54 namespace banners {
56 // static
57 base::Time AppBannerDataFetcher::GetCurrentTime() {
58 return base::Time::Now() + gTimeDeltaForTesting;
61 // static
62 void AppBannerDataFetcher::SetTimeDeltaForTesting(int days) {
63 gTimeDeltaForTesting = base::TimeDelta::FromDays(days);
66 AppBannerDataFetcher::AppBannerDataFetcher(
67 content::WebContents* web_contents,
68 base::WeakPtr<Delegate> delegate,
69 int ideal_icon_size)
70 : WebContentsObserver(web_contents),
71 ideal_icon_size_(ideal_icon_size),
72 weak_delegate_(delegate),
73 is_active_(false) {
76 void AppBannerDataFetcher::Start(const GURL& validated_url) {
77 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
79 content::WebContents* web_contents = GetWebContents();
80 DCHECK(web_contents);
82 is_active_ = true;
83 validated_url_ = validated_url;
84 web_contents->GetManifest(
85 base::Bind(&AppBannerDataFetcher::OnDidGetManifest, this));
88 void AppBannerDataFetcher::Cancel() {
89 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
90 if (is_active_) {
91 FOR_EACH_OBSERVER(Observer, observer_list_,
92 OnDecidedWhetherToShow(this, false));
93 is_active_ = false;
97 void AppBannerDataFetcher::ReplaceWebContents(
98 content::WebContents* web_contents) {
99 Observe(web_contents);
102 void AppBannerDataFetcher::AddObserverForTesting(Observer* observer) {
103 observer_list_.AddObserver(observer);
106 void AppBannerDataFetcher::RemoveObserverForTesting(Observer* observer) {
107 observer_list_.RemoveObserver(observer);
110 void AppBannerDataFetcher::WebContentsDestroyed() {
111 Cancel();
112 Observe(nullptr);
115 void AppBannerDataFetcher::DidNavigateMainFrame(
116 const content::LoadCommittedDetails& details,
117 const content::FrameNavigateParams& params) {
118 if (!details.is_in_page)
119 Cancel();
122 bool AppBannerDataFetcher::OnMessageReceived(
123 const IPC::Message& message, content::RenderFrameHost* render_frame_host) {
124 bool handled = true;
126 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(AppBannerDataFetcher, message,
127 render_frame_host)
128 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AppBannerPromptReply,
129 OnBannerPromptReply)
130 IPC_MESSAGE_UNHANDLED(handled = false)
131 IPC_END_MESSAGE_MAP()
133 return handled;
136 void AppBannerDataFetcher::OnBannerPromptReply(
137 content::RenderFrameHost* render_frame_host,
138 int request_id,
139 blink::WebAppBannerPromptReply reply) {
140 content::WebContents* web_contents = GetWebContents();
141 if (!is_active_ || !web_contents || request_id != gCurrentRequestID) {
142 Cancel();
143 return;
146 // The renderer might have requested the prompt to be canceled.
147 if (reply == blink::WebAppBannerPromptReply::Cancel) {
148 // TODO(mlamouri,benwells): we should probably record that to behave
149 // differently with regard to showing the banner.
150 Cancel();
151 return;
154 // Definitely going to show the banner now.
155 FOR_EACH_OBSERVER(Observer, observer_list_,
156 OnDecidedWhetherToShow(this, true));
158 infobars::InfoBar* infobar = CreateBanner(app_icon_.get(), app_title_);
159 if (infobar) {
160 InfoBarService::FromWebContents(web_contents)->AddInfoBar(
161 make_scoped_ptr(infobar));
163 is_active_ = false;
166 AppBannerDataFetcher::~AppBannerDataFetcher() {
167 FOR_EACH_OBSERVER(Observer, observer_list_, OnFetcherDestroyed(this));
170 std::string AppBannerDataFetcher::GetBannerType() {
171 return "web";
174 content::WebContents* AppBannerDataFetcher::GetWebContents() {
175 if (!web_contents() || web_contents()->IsBeingDestroyed())
176 return nullptr;
177 return web_contents();
180 std::string AppBannerDataFetcher::GetAppIdentifier() {
181 DCHECK(!web_app_data_.IsEmpty());
182 return web_app_data_.start_url.spec();
185 bool AppBannerDataFetcher::FetchIcon(const GURL& image_url) {
186 content::WebContents* web_contents = GetWebContents();
187 DCHECK(web_contents);
189 // Begin asynchronously fetching the app icon. AddRef() is done before the
190 // fetch to ensure that the AppBannerDataFetcher isn't deleted before the
191 // BitmapFetcher has called OnFetchComplete() (where the references are
192 // decremented).
193 AddRef();
194 Profile* profile =
195 Profile::FromBrowserContext(web_contents->GetBrowserContext());
196 bitmap_fetcher_.reset(new chrome::BitmapFetcher(image_url, this));
197 bitmap_fetcher_->Start(
198 profile->GetRequestContext(),
199 std::string(),
200 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
201 net::LOAD_NORMAL);
202 return true;
205 infobars::InfoBar* AppBannerDataFetcher::CreateBanner(
206 const SkBitmap* icon,
207 const base::string16& title) {
208 content::WebContents* web_contents = GetWebContents();
209 DCHECK(web_contents && !web_app_data_.IsEmpty());
211 // TODO(dfalcantara): Desktop doesn't display app banners, yet. Just pretend
212 // that a banner was shown for testing purposes.
213 RecordDidShowBanner("AppBanner.WebApp.Shown");
214 return nullptr;
217 void AppBannerDataFetcher::RecordDidShowBanner(const std::string& event_name) {
218 content::WebContents* web_contents = GetWebContents();
219 DCHECK(web_contents);
221 AppBannerSettingsHelper::RecordBannerEvent(
222 web_contents, validated_url_, GetAppIdentifier(),
223 AppBannerSettingsHelper::APP_BANNER_EVENT_DID_SHOW,
224 GetCurrentTime());
225 rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(),
226 event_name,
227 web_contents->GetURL());
228 banners::TrackDisplayEvent(DISPLAY_EVENT_CREATED);
231 void AppBannerDataFetcher::OnDidGetManifest(
232 const content::Manifest& manifest) {
233 content::WebContents* web_contents = GetWebContents();
234 if (!is_active_ || !web_contents) {
235 Cancel();
236 return;
239 if (!IsManifestValid(manifest)) {
240 if (!weak_delegate_.get()->OnInvalidManifest(this))
241 Cancel();
242 return;
245 banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED);
247 web_app_data_ = manifest;
248 app_title_ = web_app_data_.name.string();
250 // Check to see if there is a single service worker controlling this page
251 // and the manifest's start url.
252 Profile* profile =
253 Profile::FromBrowserContext(web_contents->GetBrowserContext());
254 content::StoragePartition* storage_partition =
255 content::BrowserContext::GetStoragePartition(
256 profile, web_contents->GetSiteInstance());
257 DCHECK(storage_partition);
259 storage_partition->GetServiceWorkerContext()->CheckHasServiceWorker(
260 validated_url_, manifest.start_url,
261 base::Bind(&AppBannerDataFetcher::OnDidCheckHasServiceWorker,
262 this));
265 void AppBannerDataFetcher::OnDidCheckHasServiceWorker(
266 bool has_service_worker) {
267 content::WebContents* web_contents = GetWebContents();
268 if (!is_active_ || !web_contents) {
269 Cancel();
270 return;
273 if (has_service_worker) {
274 // Create an infobar to promote the manifest's app.
275 GURL icon_url =
276 ManifestIconSelector::FindBestMatchingIcon(
277 web_app_data_.icons,
278 ideal_icon_size_,
279 gfx::Screen::GetScreenFor(web_contents->GetNativeView()));
280 if (!icon_url.is_empty()) {
281 FetchIcon(icon_url);
282 return;
284 } else {
285 TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
288 Cancel();
291 void AppBannerDataFetcher::OnFetchComplete(const GURL& url,
292 const SkBitmap* icon) {
293 if (is_active_)
294 ShowBanner(icon);
296 Release();
299 void AppBannerDataFetcher::ShowBanner(const SkBitmap* icon) {
300 content::WebContents* web_contents = GetWebContents();
301 if (!is_active_ || !web_contents || !icon) {
302 Cancel();
303 return;
306 RecordCouldShowBanner();
307 if (!CheckIfShouldShowBanner()) {
308 Cancel();
309 return;
312 app_icon_.reset(new SkBitmap(*icon));
313 web_contents->GetMainFrame()->Send(
314 new ChromeViewMsg_AppBannerPromptRequest(
315 web_contents->GetMainFrame()->GetRoutingID(),
316 ++gCurrentRequestID,
317 GetBannerType()));
320 void AppBannerDataFetcher::RecordCouldShowBanner() {
321 content::WebContents* web_contents = GetWebContents();
322 DCHECK(web_contents);
324 AppBannerSettingsHelper::RecordBannerEvent(
325 web_contents, validated_url_, GetAppIdentifier(),
326 AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW,
327 GetCurrentTime());
330 bool AppBannerDataFetcher::CheckIfShouldShowBanner() {
331 content::WebContents* web_contents = GetWebContents();
332 DCHECK(web_contents);
334 return AppBannerSettingsHelper::ShouldShowBanner(
335 web_contents, validated_url_, GetAppIdentifier(), GetCurrentTime());
338 // static
339 bool AppBannerDataFetcher::IsManifestValid(
340 const content::Manifest& manifest) {
341 if (manifest.IsEmpty())
342 return false;
343 if (!manifest.start_url.is_valid())
344 return false;
345 if (manifest.name.is_null() && manifest.short_name.is_null())
346 return false;
347 if (!DoesManifestContainRequiredIcon(manifest))
348 return false;
349 return true;
352 } // namespace banners