diff --git a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc index 6fa6931c7c2681..bc0662e525468c 100644 --- a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc +++ b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc @@ -50,7 +50,8 @@ infobars::InfoBar* AppBannerDataFetcherAndroid::CreateBanner( infobars::InfoBar* infobar = nullptr; if (native_app_data_.is_null()) { scoped_ptr delegate( - new AppBannerInfoBarDelegate(title, + new AppBannerInfoBarDelegate(event_request_id(), + title, new SkBitmap(*icon), web_app_data())); @@ -59,7 +60,8 @@ infobars::InfoBar* AppBannerDataFetcherAndroid::CreateBanner( RecordDidShowBanner("AppBanner.WebApp.Shown"); } else { scoped_ptr delegate( - new AppBannerInfoBarDelegate(title, + new AppBannerInfoBarDelegate(event_request_id(), + title, new SkBitmap(*icon), native_app_data_, native_app_package_)); diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate.cc b/chrome/browser/android/banners/app_banner_infobar_delegate.cc index 77b9b5ad1f69d7..86534b2b4dfa86 100644 --- a/chrome/browser/android/banners/app_banner_infobar_delegate.cc +++ b/chrome/browser/android/banners/app_banner_infobar_delegate.cc @@ -18,9 +18,12 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/ui/android/infobars/app_banner_infobar.h" +#include "chrome/common/render_messages.h" #include "chrome/grit/generated_resources.h" #include "components/rappor/rappor_utils.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" #include "content/public/common/manifest.h" #include "jni/AppBannerInfoBarDelegate_jni.h" #include "ui/gfx/android/java_bitmap.h" @@ -33,23 +36,27 @@ using base::android::ConvertUTF16ToJavaString; namespace banners { AppBannerInfoBarDelegate::AppBannerInfoBarDelegate( + int event_request_id, const base::string16& app_title, SkBitmap* app_icon, const content::Manifest& web_app_data) : app_title_(app_title), app_icon_(app_icon), + event_request_id_(event_request_id), web_app_data_(web_app_data) { DCHECK(!web_app_data.IsEmpty()); CreateJavaDelegate(); } AppBannerInfoBarDelegate::AppBannerInfoBarDelegate( + int event_request_id, const base::string16& app_title, SkBitmap* app_icon, const base::android::ScopedJavaGlobalRef& native_app_data, const std::string& native_app_package) : app_title_(app_title), app_icon_(app_icon), + event_request_id_(event_request_id), native_app_data_(native_app_data), native_app_package_(native_app_package) { DCHECK(!native_app_data_.is_null()); @@ -126,6 +133,16 @@ void AppBannerInfoBarDelegate::CreateJavaDelegate() { reinterpret_cast(this))); } +void AppBannerInfoBarDelegate::SendBannerAccepted( + content::WebContents* web_contents, + const std::string& platform) { + web_contents->GetMainFrame()->Send( + new ChromeViewMsg_AppBannerAccepted( + web_contents->GetMainFrame()->GetRoutingID(), + event_request_id_, + platform)); +} + gfx::Image AppBannerInfoBarDelegate::GetIcon() const { return gfx::Image::CreateFrom1xBitmap(*app_icon_.get()); } @@ -138,6 +155,11 @@ void AppBannerInfoBarDelegate::InfoBarDismissed() { TrackDismissEvent(DISMISS_EVENT_CLOSE_BUTTON); + web_contents->GetMainFrame()->Send( + new ChromeViewMsg_AppBannerDismissed( + web_contents->GetMainFrame()->GetRoutingID(), + event_request_id_)); + if (!native_app_data_.is_null()) { AppBannerSettingsHelper::RecordBannerEvent( web_contents, web_contents->GetURL(), @@ -197,6 +219,7 @@ bool AppBannerInfoBarDelegate::Accept() { } else { TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_TRIGGERED); } + SendBannerAccepted(web_contents, "play"); return was_opened; } else if (!web_app_data_.IsEmpty()) { AppBannerSettingsHelper::RecordBannerEvent( @@ -215,6 +238,7 @@ bool AppBannerInfoBarDelegate::Accept() { *app_icon_.get())); TrackInstallEvent(INSTALL_EVENT_WEB_APP_INSTALLED); + SendBannerAccepted(web_contents, "web"); rappor::SampleDomainAndRegistryFromGURL(g_browser_process->rappor_service(), "AppBanner.WebApp.Installed", web_contents->GetURL()); diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate.h b/chrome/browser/android/banners/app_banner_infobar_delegate.h index ef46a366ac78b0..c91177c1526a12 100644 --- a/chrome/browser/android/banners/app_banner_infobar_delegate.h +++ b/chrome/browser/android/banners/app_banner_infobar_delegate.h @@ -12,6 +12,10 @@ #include "ui/gfx/image/image.h" #include "url/gurl.h" +namespace content { +class WebContents; +} + namespace infobars { class InfoBarManager; } // namespace infobars @@ -25,12 +29,14 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate { public: // Delegate for promoting a web app. AppBannerInfoBarDelegate( + int event_request_id, const base::string16& app_title, SkBitmap* app_icon, const content::Manifest& web_app_data); // Delegate for promoting an Android app. AppBannerInfoBarDelegate( + int event_request_id, const base::string16& app_title, SkBitmap* app_icon, const base::android::ScopedJavaGlobalRef& native_app_data, @@ -54,6 +60,8 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate { private: void CreateJavaDelegate(); + void SendBannerAccepted(content::WebContents* web_contents, + const std::string& platform); // ConfirmInfoBarDelegate: gfx::Image GetIcon() const override; @@ -68,6 +76,7 @@ class AppBannerInfoBarDelegate : public ConfirmInfoBarDelegate { base::string16 app_title_; scoped_ptr app_icon_; + int event_request_id_; content::Manifest web_app_data_; base::android::ScopedJavaGlobalRef native_app_data_; diff --git a/chrome/browser/banners/app_banner_data_fetcher.cc b/chrome/browser/banners/app_banner_data_fetcher.cc index e46110efd2e89e..0fadc96e188798 100644 --- a/chrome/browser/banners/app_banner_data_fetcher.cc +++ b/chrome/browser/banners/app_banner_data_fetcher.cc @@ -71,7 +71,8 @@ AppBannerDataFetcher::AppBannerDataFetcher( : WebContentsObserver(web_contents), ideal_icon_size_(ideal_icon_size), weak_delegate_(delegate), - is_active_(false) { + is_active_(false), + event_request_id_(-1) { } void AppBannerDataFetcher::Start(const GURL& validated_url) { @@ -139,15 +140,13 @@ void AppBannerDataFetcher::OnBannerPromptReply( int request_id, blink::WebAppBannerPromptReply reply) { content::WebContents* web_contents = GetWebContents(); - if (!is_active_ || !web_contents || request_id != gCurrentRequestID) { + if (!is_active_ || !web_contents || request_id != event_request_id_) { Cancel(); return; } // The renderer might have requested the prompt to be canceled. if (reply == blink::WebAppBannerPromptReply::Cancel) { - // TODO(mlamouri,benwells): we should probably record that to behave - // differently with regard to showing the banner. Cancel(); return; } @@ -320,10 +319,11 @@ void AppBannerDataFetcher::ShowBanner(const SkBitmap* icon) { } app_icon_.reset(new SkBitmap(*icon)); + event_request_id_ = ++gCurrentRequestID; web_contents->GetMainFrame()->Send( new ChromeViewMsg_AppBannerPromptRequest( web_contents->GetMainFrame()->GetRoutingID(), - ++gCurrentRequestID, + event_request_id_, GetBannerType())); } diff --git a/chrome/browser/banners/app_banner_data_fetcher.h b/chrome/browser/banners/app_banner_data_fetcher.h index ce6d8e3013fe17..6327453104c4ed 100644 --- a/chrome/browser/banners/app_banner_data_fetcher.h +++ b/chrome/browser/banners/app_banner_data_fetcher.h @@ -108,6 +108,7 @@ class AppBannerDataFetcher virtual std::string GetAppIdentifier(); const content::Manifest& web_app_data() { return web_app_data_; } void set_app_title(const base::string16& title) { app_title_ = title; } + int event_request_id() { return event_request_id_; } // Fetches the icon at the given URL asynchronously, returning |false| if a // load could not be started. @@ -145,6 +146,7 @@ class AppBannerDataFetcher const base::WeakPtr weak_delegate_; ObserverList observer_list_; bool is_active_; + int event_request_id_; scoped_ptr bitmap_fetcher_; scoped_ptr app_icon_; diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 1c81c6eb57aa6a..ac9a678f362d22 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -5,6 +5,8 @@ { 'variables': { 'chrome_renderer_sources': [ + 'renderer/banners/app_banner_client.cc', + 'renderer/banners/app_banner_client.h', 'renderer/benchmarking_extension.cc', 'renderer/benchmarking_extension.h', 'renderer/chrome_content_renderer_client.cc', diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index c9218057d0217d..9fbca7a054ed97 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -453,6 +453,15 @@ IPC_MESSAGE_ROUTED2(ChromeViewMsg_AppBannerPromptRequest, int /* request_id */, std::string /* platform */) +// Tells the renderer that a banner has been accepted. +IPC_MESSAGE_ROUTED2(ChromeViewMsg_AppBannerAccepted, + int32_t /* request_id */, + std::string /* platform */) + +// Tells the renderer that a banner has been dismissed. +IPC_MESSAGE_ROUTED1(ChromeViewMsg_AppBannerDismissed, + int32_t /* request_id */) + // Notification that the page has an OpenSearch description document // associated with it. IPC_MESSAGE_ROUTED3(ChromeViewHostMsg_PageHasOSDD, diff --git a/chrome/renderer/banners/app_banner_client.cc b/chrome/renderer/banners/app_banner_client.cc new file mode 100644 index 00000000000000..19afeafa5f1a7b --- /dev/null +++ b/chrome/renderer/banners/app_banner_client.cc @@ -0,0 +1,62 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/banners/app_banner_client.h" + +#include "chrome/common/render_messages.h" +#include "ipc/ipc_message.h" +#include "third_party/WebKit/public/platform/WebString.h" + +using blink::WebString; + +AppBannerClient::AppBannerClient(content::RenderFrame* render_frame) + : content::RenderFrameObserver(render_frame) { +} + +AppBannerClient::~AppBannerClient() { +} + +bool AppBannerClient::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AppBannerClient, message) + IPC_MESSAGE_HANDLER(ChromeViewMsg_AppBannerAccepted, OnBannerAccepted); + IPC_MESSAGE_HANDLER(ChromeViewMsg_AppBannerDismissed, OnBannerDismissed); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void AppBannerClient::registerBannerCallbacks( + int request_id, + blink::WebAppBannerCallbacks* callbacks) { + banner_callbacks_.AddWithID(callbacks, request_id); +} + +void AppBannerClient::ResolveEvent( + int request_id, + const std::string& platform, + const blink::WebAppBannerPromptResult::Outcome& outcome) { + blink::WebAppBannerCallbacks* callbacks = + banner_callbacks_.Lookup(request_id); + if (!callbacks) + return; + + scoped_ptr result( + new blink::WebAppBannerPromptResult( + blink::WebString::fromUTF8(platform), + outcome)); + callbacks->onSuccess(result.release()); + banner_callbacks_.Remove(request_id); +} + +void AppBannerClient::OnBannerAccepted(int request_id, + const std::string& platform) { + ResolveEvent(request_id, platform, + blink::WebAppBannerPromptResult::Outcome::Accepted); +} + +void AppBannerClient::OnBannerDismissed(int request_id) { + ResolveEvent(request_id, "", + blink::WebAppBannerPromptResult::Outcome::Dismissed); +} diff --git a/chrome/renderer/banners/app_banner_client.h b/chrome/renderer/banners/app_banner_client.h new file mode 100644 index 00000000000000..bc0f15570eae93 --- /dev/null +++ b/chrome/renderer/banners/app_banner_client.h @@ -0,0 +1,46 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_BANNERS_APP_BANNER_CLIENT_H_ +#define CHROME_RENDERER_BANNERS_APP_BANNER_CLIENT_H_ + +#include +#include + +#include "base/id_map.h" +#include "content/public/renderer/render_frame_observer.h" +#include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerClient.h" +#include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerPromptResult.h" + +namespace IPC { +class Message; +} // namespace IPC + +class AppBannerClient : public content::RenderFrameObserver, + public blink::WebAppBannerClient { + public: + explicit AppBannerClient(content::RenderFrame* render_frame); + virtual ~AppBannerClient(); + + private: + // content::RenderFrame::Observer implementation. + bool OnMessageReceived(const IPC::Message& message) override; + + // WebAppBannerClient implementation. + void registerBannerCallbacks(int request_id, + blink::WebAppBannerCallbacks*) override; + + void ResolveEvent(int request_id, + const std::string& platform, + const blink::WebAppBannerPromptResult::Outcome& outcome); + void OnBannerAccepted(int request_id, const std::string& platform); + void OnBannerDismissed(int request_id); + + IDMap + banner_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(AppBannerClient); +}; + +#endif // CHROME_RENDERER_BANNERS_APP_BANNER_CLIENT_H_ diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index cba0636ae5f617..4d6ee1519eeb72 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -27,6 +27,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/locale_settings.h" #include "chrome/grit/renderer_resources.h" +#include "chrome/renderer/banners/app_banner_client.h" #include "chrome/renderer/benchmarking_extension.h" #include "chrome/renderer/chrome_render_frame_observer.h" #include "chrome/renderer/chrome_render_process_observer.h" @@ -1647,3 +1648,10 @@ void ChromeContentRendererClient::RecordRapporURL(const std::string& metric, const GURL& url) { RenderThread::Get()->Send(new ChromeViewHostMsg_RecordRapporURL(metric, url)); } + +scoped_ptr +ChromeContentRendererClient::CreateAppBannerClient( + content::RenderFrame* render_frame) { + return scoped_ptr( + new AppBannerClient(render_frame)); +} diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h index 36468cb66de0a4..c343781709630e 100644 --- a/chrome/renderer/chrome_content_renderer_client.h +++ b/chrome/renderer/chrome_content_renderer_client.h @@ -150,6 +150,8 @@ class ChromeContentRendererClient : public content::ContentRendererClient { void RecordRappor(const std::string& metric, const std::string& sample) override; void RecordRapporURL(const std::string& metric, const GURL& url) override; + scoped_ptr CreateAppBannerClient( + content::RenderFrame* render_frame) override; #if defined(ENABLE_EXTENSIONS) // Takes ownership. diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc index c65fc501aceacb..bf53999771bcc9 100644 --- a/chrome/renderer/chrome_render_frame_observer.cc +++ b/chrome/renderer/chrome_render_frame_observer.cc @@ -196,11 +196,14 @@ void ChromeRenderFrameObserver::DidFinishDocumentLoad() { void ChromeRenderFrameObserver::OnAppBannerPromptRequest( int request_id, const std::string& platform) { + // App banner prompt requests are handled in the general chrome render frame + // observer, not the AppBannerClient, as the AppBannerClient is created lazily + // by blink and may not exist when the request is sent. blink::WebAppBannerPromptReply reply = blink::WebAppBannerPromptReply::None; blink::WebString web_platform(base::UTF8ToUTF16(platform)); blink::WebVector web_platforms(&web_platform, 1); render_frame()->GetWebFrame()->willShowInstallBannerPrompt( - web_platforms, &reply); + request_id, web_platforms, &reply); Send(new ChromeViewHostMsg_AppBannerPromptReply( routing_id(), request_id, reply)); diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc index 6e66fe349dede9..df391b644c1579 100644 --- a/content/public/renderer/content_renderer_client.cc +++ b/content/public/renderer/content_renderer_client.cc @@ -5,6 +5,7 @@ #include "content/public/renderer/content_renderer_client.h" #include "media/base/renderer_factory.h" +#include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerClient.h" #include "third_party/WebKit/public/web/WebPluginPlaceholder.h" namespace content { @@ -220,4 +221,9 @@ std::string ContentRendererClient::GetUserAgentOverrideForURL(const GURL& url) { return std::string(); } +scoped_ptr +ContentRendererClient::CreateAppBannerClient(RenderFrame* render_frame) { + return nullptr; +} + } // namespace content diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h index e4e8f0371a8456..9e1e7d73e617ec 100644 --- a/content/public/renderer/content_renderer_client.h +++ b/content/public/renderer/content_renderer_client.h @@ -29,6 +29,7 @@ class SingleThreadTaskRunner; } namespace blink { +class WebAppBannerClient; class WebAudioDevice; class WebClipboard; class WebFrame; @@ -300,6 +301,10 @@ class CONTENT_EXPORT ContentRendererClient { // Records a domain and registry of a url to a Rappor privacy-preserving // metric. See: https://www.chromium.org/developers/design-documents/rappor virtual void RecordRapporURL(const std::string& metric, const GURL& url) {} + + // Allows an embedder to provide a blink::WebAppBannerClient. + virtual scoped_ptr CreateAppBannerClient( + RenderFrame* render_frame); }; } // namespace content diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 043b33cd4501fb..207b26f2218856 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -3769,6 +3769,15 @@ blink::WebPermissionClient* RenderFrameImpl::permissionClient() { return permission_client_.get(); } +blink::WebAppBannerClient* RenderFrameImpl::appBannerClient() { + if (!app_banner_client_) { + app_banner_client_ = + GetContentClient()->renderer()->CreateAppBannerClient(this); + } + + return app_banner_client_.get(); +} + void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) { Send(new FrameHostMsg_MediaPlayingNotification( routing_id_, reinterpret_cast(player), player->hasVideo(), diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index d64a5079724c07..35ff78529613bb 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h @@ -25,6 +25,7 @@ #include "content/renderer/renderer_webcookiejar_impl.h" #include "ipc/ipc_message.h" #include "media/blink/webmediaplayer_delegate.h" +#include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerClient.h" #include "third_party/WebKit/public/web/WebAXObject.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebFrameClient.h" @@ -533,6 +534,7 @@ class CONTENT_EXPORT RenderFrameImpl virtual bool enterFullscreen(); virtual bool exitFullscreen(); virtual blink::WebPermissionClient* permissionClient(); + virtual blink::WebAppBannerClient* appBannerClient(); // WebMediaPlayerDelegate implementation: void DidPlay(blink::WebMediaPlayer* player) override; @@ -970,6 +972,8 @@ class CONTENT_EXPORT RenderFrameImpl scoped_ptr permission_client_; + scoped_ptr app_banner_client_; + #if defined(OS_MACOSX) || defined(OS_ANDROID) // The external popup for the currently showing select popup. scoped_ptr external_popup_menu_; diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 3cf58f9210f09b..d4966bc41962b7 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -123,6 +123,7 @@ enum IPCMessageStart { CastChannelMsgStart, DataReductionProxyStart, ContentSettingsMsgStart, + ChromeAppBannerMsgStart, LastIPCMsgStart // Must come last. };