Chromium Code Reviews| Index: chrome_frame/turndown_prompt/turndown_prompt.cc |
| diff --git a/chrome_frame/turndown_prompt/turndown_prompt.cc b/chrome_frame/turndown_prompt/turndown_prompt.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..3e14b80c2783c304cf537d08a0e7e2a50a42ad77 |
| --- /dev/null |
| +++ b/chrome_frame/turndown_prompt/turndown_prompt.cc |
| @@ -0,0 +1,250 @@ |
| +// Copyright 2013 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_frame/turndown_prompt/turndown_prompt.h" |
| + |
| +#include <atlbase.h> |
| +#include <shlguid.h> |
| + |
| +#include "base/bind.h" |
| +#include "base/compiler_specific.h" |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/path_service.h" |
| +#include "base/win/scoped_bstr.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "base/win/win_util.h" |
| +#include "chrome/installer/util/browser_distribution.h" |
| +#include "chrome/installer/util/google_update_settings.h" |
| +#include "chrome/installer/util/install_util.h" |
| +#include "chrome_frame/infobars/infobar_manager.h" |
| +#include "chrome_frame/policy_settings.h" |
| +#include "chrome_frame/ready_mode/internal/ready_mode_web_browser_adapter.h" |
| +#include "chrome_frame/ready_mode/internal/url_launcher.h" |
| +#include "chrome_frame/turndown_prompt/reshow_state.h" |
| +#include "chrome_frame/turndown_prompt/turndown_prompt_content.h" |
| +#include "chrome_frame/utils.h" |
| +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| + |
| +namespace { |
| + |
| +// Time between showingings of the turndown prompt. |
|
robertshield
2013/06/19 23:36:33
Too many ings
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| +const int kTurndownPromptReshowDeltaMinutes = 60 * 24 * 7; |
| + |
| +void UninstallChromeFrame(); |
| + |
| +// Manages the Turndown UI in response to browsing ChromeFrame-rendered |
| +// pages. |
| +class BrowserObserver : public ReadyModeWebBrowserAdapter::Observer { |
| + public: |
| + BrowserObserver(IWebBrowser2* web_browser, |
| + ReadyModeWebBrowserAdapter* adapter); |
| + |
| + // ReadyModeWebBrowserAdapter::Observer implementation |
| + virtual void OnNavigateTo(const std::wstring& url); |
| + virtual void OnRenderInChromeFrame(const std::wstring& url); |
| + virtual void OnRenderInHost(const std::wstring& url); |
| + |
| + private: |
| + // Shows the turndown prompt if it hasn't been seen since the reshow delta. |
|
robertshield
2013/06/19 23:36:33
reshow delta -> kTurndownPromptReshowDeltaMinutes
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + void ShowPrompt(); |
| + void Hide(); |
| + InfobarManager* GetInfobarManager(); |
|
robertshield
2013/06/19 23:36:33
The pointer this returns has a non-obvious life-cy
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + |
| + GURL rendered_url_; |
| + base::win::ScopedComPtr<IWebBrowser2> web_browser_; |
| + ReadyModeWebBrowserAdapter* adapter_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BrowserObserver); |
| +}; |
| + |
| +// Implements launching of a URL in an instance of IWebBrowser2. |
| +class UrlLauncherImpl : public UrlLauncher { |
| + public: |
| + explicit UrlLauncherImpl(IWebBrowser2* web_browser); |
| + |
| + // UrlLauncher implementation |
| + void LaunchUrl(const std::wstring& url); |
| + |
| + private: |
| + base::win::ScopedComPtr<IWebBrowser2> web_browser_; |
| +}; |
| + |
| +UrlLauncherImpl::UrlLauncherImpl(IWebBrowser2* web_browser) { |
| + DCHECK(web_browser); |
| + web_browser_ = web_browser; |
| +} |
| + |
| +void UrlLauncherImpl::LaunchUrl(const std::wstring& url) { |
| + VARIANT flags = { VT_I4 }; |
| + V_I4(&flags) = navOpenInNewWindow; |
| + base::win::ScopedBstr location(url.c_str()); |
| + |
| + HRESULT hr = web_browser_->Navigate(location, &flags, NULL, NULL, NULL); |
| + DLOG_IF(ERROR, FAILED(hr)) << "Failed to invoke Navigate on IWebBrowser2. " |
| + << "Error: " << hr; |
| +} |
| + |
| +BrowserObserver::BrowserObserver(IWebBrowser2* web_browser, |
| + ReadyModeWebBrowserAdapter* adapter) |
| + : web_browser_(web_browser), |
| + adapter_(adapter) { |
| +} |
| + |
| +void BrowserObserver::OnNavigateTo(const std::wstring& url) { |
| + if (!net::registry_controlled_domains::SameDomainOrHost( |
| + GURL(url), |
| + rendered_url_, |
| + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) { |
| + rendered_url_ = GURL(); |
| + Hide(); |
| + } |
| +} |
| + |
| +void BrowserObserver::OnRenderInChromeFrame(const std::wstring& url) { |
| + ShowPrompt(); |
| + rendered_url_ = GURL(url); |
| +} |
| + |
| +void BrowserObserver::OnRenderInHost(const std::wstring& url) { |
| + Hide(); |
| + rendered_url_ = GURL(url); |
| +} |
| + |
| +void BrowserObserver::ShowPrompt() { |
| + BrowserDistribution* dist = |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_FRAME); |
| + turndown_prompt::ReshowState reshow_state( |
| + dist->GetStateKey(), |
| + base::TimeDelta::FromMinutes(kTurndownPromptReshowDeltaMinutes)); |
| + |
| + // Short-circuit if the prompt shouldn't be shown again yet. |
| + if (!reshow_state.HasReshowDeltaExpired(base::Time::Now())) |
| + return; |
| + |
| + // This pointer is self-managed and not guaranteed to survive handling of |
| + // Windows events. |
|
robertshield
2013/06/19 23:36:33
As an unfamiliar reader, I don't know how to parse
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + InfobarManager* infobar_manager = GetInfobarManager(); |
| + |
| + if (infobar_manager) { |
| + // Owned by infobar_content |
| + scoped_ptr<UrlLauncher> url_launcher(new UrlLauncherImpl(web_browser_)); |
| + |
| + // Owned by infobar_manager |
| + scoped_ptr<InfobarContent> infobar_content(new TurndownPromptContent( |
| + url_launcher.release(), |
| + base::Bind(&UninstallChromeFrame))); |
| + |
| + if (infobar_manager->Show(infobar_content.release(), TOP_INFOBAR)) { |
| + // Update state in the registry that the prompt was shown. |
| + reshow_state.MarkShown(base::Time::Now()); |
|
robertshield
2013/06/19 23:36:33
Random thought: if I open two tabs both rendering
grt (UTC plus 2)
2013/06/20 19:55:29
I think this is possible.
robertshield
2013/06/20 21:45:10
Yeah, was simply musing. I don't have an immediate
|
| + } |
| + } |
| +} |
| + |
| +void BrowserObserver::Hide() { |
| + InfobarManager* infobar_manager = GetInfobarManager(); |
| + if (infobar_manager) |
| + infobar_manager->HideAll(); |
| +} |
| + |
| +InfobarManager* BrowserObserver::GetInfobarManager() { |
| + HRESULT hr = NOERROR; |
| + |
| + base::win::ScopedComPtr<IOleWindow> ole_window; |
| + hr = DoQueryService(SID_SShellBrowser, web_browser_, ole_window.Receive()); |
| + if (FAILED(hr) || ole_window == NULL) { |
| + DLOG(ERROR) << "Failed to query SID_SShellBrowser from IWebBrowser2. " |
| + << "Error: " << hr; |
| + return NULL; |
| + } |
| + |
| + HWND web_browserhwnd = NULL; |
|
robertshield
2013/06/19 23:36:33
nit: web_browser_hwnd
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + hr = ole_window->GetWindow(&web_browserhwnd); |
| + if (FAILED(hr) || web_browserhwnd == NULL) { |
| + DLOG(ERROR) << "Failed to query HWND from IOleWindow. " |
| + << "Error: " << hr; |
| + return NULL; |
| + } |
| + |
| + return InfobarManager::Get(web_browserhwnd); |
| +} |
| + |
| +// Attempts to create a ReadyModeWebBrowserAdapter instance. |
| +bool CreateWebBrowserAdapter(ReadyModeWebBrowserAdapter** pointer) { |
|
robertshield
2013/06/19 23:36:33
nit: pointer -> adapter
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + *pointer = NULL; |
| + |
| + CComObject<ReadyModeWebBrowserAdapter>* com_object; |
| + HRESULT hr = |
| + CComObject<ReadyModeWebBrowserAdapter>::CreateInstance(&com_object); |
| + |
| + if (FAILED(hr)) { |
| + DLOG(ERROR) << "Failed to create instance of ReadyModeWebBrowserAdapter. " |
| + << "Error: " << hr; |
| + return false; |
| + } |
| + |
| + com_object->AddRef(); |
| + *pointer = com_object; |
| + return true; |
| +} |
| + |
| +// Attempts to install Turnown prompts in the provided web browser. |
| +bool InstallPrompts(IWebBrowser2* web_browser) { |
| + base::win::ScopedComPtr<ReadyModeWebBrowserAdapter, NULL> adapter; |
| + |
| + if (!CreateWebBrowserAdapter(adapter.Receive())) |
| + return false; |
| + |
| + // Pass ownership of our delegate to the BrowserObserver |
| + scoped_ptr<ReadyModeWebBrowserAdapter::Observer> browser_observer( |
| + new BrowserObserver(web_browser, adapter)); |
| + |
| + // Owns the BrowserObserver |
| + return adapter->Initialize(web_browser, browser_observer.release()); |
| +} |
| + |
| +// Uninstalls Chrome Frame in response to user action. |
| +void UninstallChromeFrame() { |
| + // TODO(grt) Find and run the uninstaller. |
| +} |
| + |
| +} // namespace |
| + |
| +namespace turndown_prompt { |
| + |
| +bool IsPromptSuppressed() { |
| + // See if updates are disabled for Chrome Frame (check the binaries if |
| + // multi-install). |
|
robertshield
2013/06/19 23:36:33
I wonder if we should check the MSI bit as well?
grt (UTC plus 2)
2013/06/20 19:55:29
Done.
|
| + BrowserDistribution* dist = |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_FRAME); |
| + |
| + base::FilePath dll_path; |
| + if (PathService::Get(base::DIR_MODULE, &dll_path)) { |
| + bool system_level = |
| + !InstallUtil::IsPerUserInstall(dll_path.value().c_str()); |
| + bool multi_install = InstallUtil::IsMultiInstall(dist, system_level); |
| + if (multi_install) { |
| + dist = |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_BINARIES); |
| + } |
| + if (GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(), NULL) == |
| + GoogleUpdateSettings::UPDATES_DISABLED) { |
| + return true; |
| + } |
| + } |
| + |
| + // See if the prompt is explicitly suppressed via GP. |
| + return PolicySettings::GetInstance()->suppress_turndown_prompt(); |
| +} |
| + |
| +void Configure(IWebBrowser2* web_browser) { |
| + if (!IsPromptSuppressed()) |
| + InstallPrompts(web_browser); |
| +} |
| + |
| +} // namespace turndown_prompt |