diff options
author | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-20 13:14:11 +0000 |
---|---|---|
committer | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-20 13:14:11 +0000 |
commit | 818915cd6f37ba0ee4e01ef37f293d5a0cce6975 (patch) | |
tree | 81d9df68c4de82b8cff3ad5f25992743039f8e9b | |
parent | 41766860ac99fc3a894728084df2f8e31054274c (diff) | |
download | chromium_src-818915cd6f37ba0ee4e01ef37f293d5a0cce6975.zip chromium_src-818915cd6f37ba0ee4e01ef37f293d5a0cce6975.tar.gz chromium_src-818915cd6f37ba0ee4e01ef37f293d5a0cce6975.tar.bz2 |
Raise an infobar and deny access to WebGL if a GPU reset was detected while a web page containing WebGL content was visible.
The same mechanism will work for Pepper 3D but has not been hooked up for that API yet.
https://bugs.webkit.org/show_bug.cgi?id=101826 added the hooks out through the Chromium WebKit API.
BUG=125125
TEST=GPUCrashTest.ContextLossRaisesInfobar
Review URL: https://chromiumcodereview.appspot.com/11378008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168794 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 751 insertions, 34 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 9ac7cb8..dbc2651 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -15208,6 +15208,23 @@ Some features may be unavailable. Please check that the profile exists and you </message> </if> + <!-- Pepper 3D and WebGL (client 3D APIs) infobar strings --> + <message name="IDS_3D_APIS_BLOCKED_TEXT" desc="Infobar message when 3D graphics APIs are blocked because the graphics processor was reset recently."> + <ph name="API_NAME">$1<ex>WebGL</ex></ph> was stopped by an error on one of your tabs. + </message> + <message name="IDS_3D_APIS_WEBGL_NAME" desc="Name of the WebGL API."> + WebGL + </message> + <message name="IDS_3D_APIS_PEPPER_3D_NAME" desc="Name of the Pepper 3D API."> + Pepper 3D + </message> + <message name="IDS_3D_APIS_BLOCKED_OK_BUTTON_LABEL" desc="Label for the 'ok' button on the infobar indicating that 3D APIs were blocked. Clicking this button dismisses the infobar with no other action."> + OK + </message> + <message name="IDS_3D_APIS_BLOCKED_TRY_AGAIN_BUTTON_LABEL" desc="Label for the 'cancel' button on the infobar indicating that 3D APIs were blocked. Clicking this button unblocks the APIs and reloads the page."> + Try again + </message> + </messages> <structures fallback_to_english="true"> diff --git a/chrome/browser/api/infobars/infobar_delegate.cc b/chrome/browser/api/infobars/infobar_delegate.cc index 18b4286..d09aa94 100644 --- a/chrome/browser/api/infobars/infobar_delegate.cc +++ b/chrome/browser/api/infobars/infobar_delegate.cc @@ -86,6 +86,10 @@ ThemeInstalledInfoBarDelegate* return NULL; } +ThreeDAPIInfoBarDelegate* InfoBarDelegate::AsThreeDAPIInfoBarDelegate() { + return NULL; +} + TranslateInfoBarDelegate* InfoBarDelegate::AsTranslateInfoBarDelegate() { return NULL; } diff --git a/chrome/browser/api/infobars/infobar_delegate.h b/chrome/browser/api/infobars/infobar_delegate.h index 27bc1c9..e3340d1 100644 --- a/chrome/browser/api/infobars/infobar_delegate.h +++ b/chrome/browser/api/infobars/infobar_delegate.h @@ -21,6 +21,7 @@ class PluginInstallerInfoBarDelegate; class RegisterProtocolHandlerInfoBarDelegate; class SavePasswordInfoBarDelegate; class ThemeInstalledInfoBarDelegate; +class ThreeDAPIInfoBarDelegate; class TranslateInfoBarDelegate; namespace gfx { @@ -105,6 +106,7 @@ class InfoBarDelegate { virtual RegisterProtocolHandlerInfoBarDelegate* AsRegisterProtocolHandlerInfoBarDelegate(); virtual ThemeInstalledInfoBarDelegate* AsThemePreviewInfobarDelegate(); + virtual ThreeDAPIInfoBarDelegate* AsThreeDAPIInfoBarDelegate(); virtual TranslateInfoBarDelegate* AsTranslateInfoBarDelegate(); protected: diff --git a/chrome/browser/three_d_api_observer.cc b/chrome/browser/three_d_api_observer.cc new file mode 100644 index 0000000..9c188d5 --- /dev/null +++ b/chrome/browser/three_d_api_observer.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2012 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/browser/three_d_api_observer.h" + +#include "chrome/browser/api/infobars/confirm_infobar_delegate.h" +#include "chrome/browser/api/infobars/infobar_service.h" +#include "content/public/browser/gpu_data_manager.h" +#include "content/public/common/three_d_api_types.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThreeDAPIObserver) + +// ThreeDAPIInfoBarDelegate --------------------------------------------- + +// TODO(kbr): write a "learn more" article about the issues associated +// with client 3D APIs and GPU resets, and override GetLinkText(), etc. + +class ThreeDAPIInfoBarDelegate : public ConfirmInfoBarDelegate { + public: + ThreeDAPIInfoBarDelegate( + InfoBarService* owner, + const GURL& url, + content::ThreeDAPIType requester); + + private: + virtual ~ThreeDAPIInfoBarDelegate(); + + // ConfirmInfoBarDelegate: + virtual bool EqualsDelegate(InfoBarDelegate* delegate) const OVERRIDE; + virtual ThreeDAPIInfoBarDelegate* AsThreeDAPIInfoBarDelegate() OVERRIDE; + virtual string16 GetMessageText() const OVERRIDE; + virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; + virtual bool Accept() OVERRIDE; + virtual bool Cancel() OVERRIDE; + + GURL url_; + content::ThreeDAPIType requester_; + + DISALLOW_COPY_AND_ASSIGN(ThreeDAPIInfoBarDelegate); +}; + +ThreeDAPIInfoBarDelegate::ThreeDAPIInfoBarDelegate( + InfoBarService* owner, + const GURL& url, + content::ThreeDAPIType requester) + : ConfirmInfoBarDelegate(owner), + url_(url), + requester_(requester) { +} + +ThreeDAPIInfoBarDelegate::~ThreeDAPIInfoBarDelegate() { +} + +bool ThreeDAPIInfoBarDelegate::EqualsDelegate(InfoBarDelegate* delegate) const { + ThreeDAPIInfoBarDelegate* three_d_delegate = + delegate->AsThreeDAPIInfoBarDelegate(); + // For the time being, if a given web page is actually using both + // WebGL and Pepper 3D and both APIs are blocked, just leave the + // first infobar up. If the user selects "try again", both APIs will + // be unblocked and the web page reload will succeed. + return (three_d_delegate != NULL); +} + +ThreeDAPIInfoBarDelegate* +ThreeDAPIInfoBarDelegate::AsThreeDAPIInfoBarDelegate() { + return this; +} + +string16 ThreeDAPIInfoBarDelegate::GetMessageText() const { + string16 api_name; + switch (requester_) { + case content::THREE_D_API_TYPE_WEBGL: + api_name = l10n_util::GetStringUTF16(IDS_3D_APIS_WEBGL_NAME); + break; + case content::THREE_D_API_TYPE_PEPPER_3D: + api_name = l10n_util::GetStringUTF16(IDS_3D_APIS_PEPPER_3D_NAME); + break; + } + + return l10n_util::GetStringFUTF16(IDS_3D_APIS_BLOCKED_TEXT, + api_name); +} + +string16 ThreeDAPIInfoBarDelegate::GetButtonLabel( + InfoBarButton button) const { + return l10n_util::GetStringUTF16((button == BUTTON_OK) ? + IDS_3D_APIS_BLOCKED_OK_BUTTON_LABEL : + IDS_3D_APIS_BLOCKED_TRY_AGAIN_BUTTON_LABEL); +} + +bool ThreeDAPIInfoBarDelegate::Accept() { + // TODO(kbr): add UMA stats. + return true; +} + +bool ThreeDAPIInfoBarDelegate::Cancel() { + // TODO(kbr): add UMA stats. + content::GpuDataManager::GetInstance()->UnblockDomainFrom3DAPIs(url_); + owner()->GetWebContents()->GetController().Reload(true); + return true; +} + + +// ThreeDAPIObserver ---------------------------------------------------- + +ThreeDAPIObserver::ThreeDAPIObserver(content::WebContents* web_contents) + : WebContentsObserver(web_contents) {} + +ThreeDAPIObserver::~ThreeDAPIObserver() {} + +void ThreeDAPIObserver::DidBlock3DAPIs(const GURL& url, + content::ThreeDAPIType requester) { + InfoBarService* service = InfoBarService::FromWebContents(web_contents()); + service->AddInfoBar(new ThreeDAPIInfoBarDelegate(service, url, requester)); +} diff --git a/chrome/browser/three_d_api_observer.h b/chrome/browser/three_d_api_observer.h new file mode 100644 index 0000000..2f4b344 --- /dev/null +++ b/chrome/browser/three_d_api_observer.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 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_BROWSER_THREE_D_API_OBSERVER_H_ +#define CHROME_BROWSER_THREE_D_API_OBSERVER_H_ + +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +class ThreeDAPIObserver + : public content::WebContentsObserver, + public content::WebContentsUserData<ThreeDAPIObserver> { + public: + virtual ~ThreeDAPIObserver(); + + private: + explicit ThreeDAPIObserver(content::WebContents* web_contents); + friend class content::WebContentsUserData<ThreeDAPIObserver>; + + virtual void DidBlock3DAPIs(const GURL& url, + content::ThreeDAPIType requester) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ThreeDAPIObserver); +}; + +#endif // CHROME_BROWSER_THREE_D_API_OBSERVER_H_ diff --git a/chrome/browser/ui/tab_contents/tab_contents.cc b/chrome/browser/ui/tab_contents/tab_contents.cc index 8092f5a..887af19 100644 --- a/chrome/browser/ui/tab_contents/tab_contents.cc +++ b/chrome/browser/ui/tab_contents/tab_contents.cc @@ -31,6 +31,7 @@ #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ssl/ssl_tab_helper.h" #include "chrome/browser/tab_contents/navigation_metrics_recorder.h" +#include "chrome/browser/three_d_api_observer.h" #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" #include "chrome/browser/translate/translate_tab_helper.h" #include "chrome/browser/ui/alternate_error_tab_observer.h" @@ -156,6 +157,7 @@ TabContents::TabContents(WebContents* contents) SSLTabHelper::CreateForWebContents(contents); TabContentsSyncedTabDelegate::CreateForWebContents(contents); TabSpecificContentSettings::CreateForWebContents(contents); + ThreeDAPIObserver::CreateForWebContents(contents); ThumbnailTabHelper::CreateForWebContents(contents); TranslateTabHelper::CreateForWebContents(contents); ZoomController::CreateForWebContents(contents); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 78044ba..06a9e118 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2053,6 +2053,8 @@ 'browser/themes/theme_service_mac.mm', 'browser/themes/theme_syncable_service.cc', 'browser/themes/theme_syncable_service.h', + 'browser/three_d_api_observer.cc', + 'browser/three_d_api_observer.h', 'browser/thumbnails/render_widget_snapshot_taker.cc', 'browser/thumbnails/render_widget_snapshot_taker.h', 'browser/thumbnails/thumbnail_service.h', diff --git a/chrome/test/gpu/gpu_crash_browsertest.cc b/chrome/test/gpu/gpu_crash_browsertest.cc index 94fa01b..569be66 100644 --- a/chrome/test/gpu/gpu_crash_browsertest.cc +++ b/chrome/test/gpu/gpu_crash_browsertest.cc @@ -5,15 +5,19 @@ #include "base/command_line.h" #include "base/path_service.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/infobars/infobar_tab_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_launcher_utils.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/common/content_paths.h" @@ -60,6 +64,9 @@ class GPUCrashTest : public InProcessBrowserTest { IN_PROC_BROWSER_TEST_F(GPUCrashTest, Kill) { content::DOMMessageQueue message_queue; + content::GpuDataManager::GetInstance()-> + DisableDomainBlockingFor3DAPIsForTesting(); + // Load page and wait for it to load. content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, @@ -77,6 +84,26 @@ IN_PROC_BROWSER_TEST_F(GPUCrashTest, Kill) { EXPECT_EQ("\"SUCCESS\"", m); } +IN_PROC_BROWSER_TEST_F(GPUCrashTest, ContextLossRaisesInfobar) { + // Load page and wait for it to load. + content::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + content::NotificationService::AllSources()); + ui_test_utils::NavigateToURL( + browser(), + content::GetFileUrlWithQuery( + gpu_test_dir_.AppendASCII("webgl.html"), "query=kill")); + observer.Wait(); + + content::WindowedNotificationObserver infobar_added( + chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, + content::NotificationService::AllSources()); + SimulateGPUCrash(browser()); + infobar_added.Wait(); + EXPECT_EQ(1u, + InfoBarTabHelper::FromWebContents( + chrome::GetActiveWebContents(browser()))->GetInfoBarCount()); +} IN_PROC_BROWSER_TEST_F(GPUCrashTest, WebkitLoseContext) { content::DOMMessageQueue message_queue; diff --git a/content/browser/DEPS b/content/browser/DEPS index b0642c6..8f04c9b 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS @@ -9,6 +9,7 @@ include_rules = [ # Other libraries. "+third_party/iaccessible2", "+third_party/isimpledom", + "+third_party/khronos", # For enum definitions only "+third_party/speex", # Allow non-browser Chrome OS code to be used. diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc index 7292c5b..e175df5 100644 --- a/content/browser/gpu/gpu_data_manager_impl.cc +++ b/content/browser/gpu/gpu_data_manager_impl.cc @@ -69,6 +69,11 @@ void DisplayReconfigCallback(CGDirectDisplayID display, } #endif // OS_MACOSX +// Block all domains' use of 3D APIs for this many milliseconds if +// approaching a threshold where system stability might be compromised. +const int64 kBlockAllDomainsMs = 10000; +const int kNumResetsWithinDuration = 1; + } // namespace anonymous // static @@ -90,7 +95,8 @@ GpuDataManagerImpl::GpuDataManagerImpl() software_rendering_(false), card_blacklisted_(false), update_histograms_(true), - window_count_(0) { + window_count_(0), + domain_blocking_enabled_(true) { CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDisableAcceleratedCompositing)) { command_line->AppendSwitch(switches::kDisableAccelerated2dCanvas); @@ -342,6 +348,30 @@ uint32 GpuDataManagerImpl::GetWindowCount() const { return window_count_; } +void GpuDataManagerImpl::UnblockDomainFrom3DAPIs(const GURL& url) { + // This method must do two things: + // + // 1. If the specific domain is blocked, then unblock it. + // + // 2. Reset our notion of how many GPU resets have occurred recently. + // This is necessary even if the specific domain was blocked. + // Otherwise, if we call Are3DAPIsBlocked with the same domain right + // after unblocking it, it will probably still be blocked because of + // the recent GPU reset caused by that domain. + // + // These policies could be refined, but at a certain point the behavior + // will become difficult to explain. + std::string domain = GetDomainFromURL(url); + + base::AutoLock auto_lock(gpu_info_lock_); + blocked_domains_.erase(domain); + timestamps_of_gpu_resets_.clear(); +} + +void GpuDataManagerImpl::DisableDomainBlockingFor3DAPIsForTesting() { + domain_blocking_enabled_ = false; +} + void GpuDataManagerImpl::AppendRendererCommandLine( CommandLine* command_line) const { DCHECK(command_line); @@ -471,6 +501,16 @@ bool GpuDataManagerImpl::IsUsingAcceleratedSurface() const { } #endif +void GpuDataManagerImpl::BlockDomainFrom3DAPIs( + const GURL& url, DomainGuilt guilt) { + BlockDomainFrom3DAPIsAtTime(url, guilt, base::Time::Now()); +} + +GpuDataManagerImpl::DomainBlockStatus +GpuDataManagerImpl::Are3DAPIsBlocked(const GURL& url) const { + return Are3DAPIsBlockedAtTime(url, base::Time::Now()); +} + void GpuDataManagerImpl::AppendPluginCommandLine( CommandLine* command_line) const { DCHECK(command_line); @@ -572,4 +612,86 @@ void GpuDataManagerImpl::BlacklistCard() { NotifyGpuInfoUpdate(); } +std::string GpuDataManagerImpl::GetDomainFromURL(const GURL& url) const { + // For the moment, we just use the host, or its IP address, as the + // entry in the set, rather than trying to figure out the top-level + // domain. This does mean that a.foo.com and b.foo.com will be + // treated independently in the blocking of a given domain, but it + // would require a third-party library to reliably figure out the + // top-level domain from a URL. + if (!url.has_host()) { + return std::string(); + } + + return url.host(); +} + +void GpuDataManagerImpl::BlockDomainFrom3DAPIsAtTime( + const GURL& url, DomainGuilt guilt, base::Time at_time) { + if (!domain_blocking_enabled_) + return; + + std::string domain = GetDomainFromURL(url); + + base::AutoLock auto_lock(gpu_info_lock_); + DomainBlockEntry& entry = blocked_domains_[domain]; + entry.last_guilt = guilt; + timestamps_of_gpu_resets_.push_back(at_time); +} + +GpuDataManagerImpl::DomainBlockStatus +GpuDataManagerImpl::Are3DAPIsBlockedAtTime( + const GURL& url, base::Time at_time) const { + if (!domain_blocking_enabled_) + return DOMAIN_BLOCK_STATUS_NOT_BLOCKED; + + // Note: adjusting the policies in this code will almost certainly + // require adjusting the associated unit tests. + std::string domain = GetDomainFromURL(url); + + base::AutoLock auto_lock(gpu_info_lock_); + { + DomainBlockMap::const_iterator iter = blocked_domains_.find(domain); + if (iter != blocked_domains_.end()) { + // Err on the side of caution, and assume that if a particular + // domain shows up in the block map, it's there for a good + // reason and don't let its presence there automatically expire. + return DOMAIN_BLOCK_STATUS_BLOCKED; + } + } + + // Look at the timestamps of the recent GPU resets to see if there are + // enough within the threshold which would cause us to blacklist all + // domains. This doesn't need to be overly precise -- if time goes + // backward due to a system clock adjustment, that's fine. + // + // TODO(kbr): make this pay attention to the TDR thresholds in the + // Windows registry, but make sure it continues to be testable. + std::list<base::Time>::iterator iter = timestamps_of_gpu_resets_.begin(); + int num_resets_within_timeframe = 0; + while (iter != timestamps_of_gpu_resets_.end()) { + base::Time time = *iter; + base::TimeDelta delta_t = at_time - time; + + // If this entry has "expired", just remove it. + if (delta_t.InMilliseconds() > kBlockAllDomainsMs) { + iter = timestamps_of_gpu_resets_.erase(iter); + continue; + } + + ++num_resets_within_timeframe; + ++iter; + } + + if (num_resets_within_timeframe >= kNumResetsWithinDuration) { + return DOMAIN_BLOCK_STATUS_ALL_DOMAINS_BLOCKED; + } + + return DOMAIN_BLOCK_STATUS_NOT_BLOCKED; +} + +int64 GpuDataManagerImpl::GetBlockAllDomainsDurationInMs() const { + return kBlockAllDomainsMs; +} + } // namespace content diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h index 6270735..f9b2c8d 100644 --- a/content/browser/gpu/gpu_data_manager_impl.h +++ b/content/browser/gpu/gpu_data_manager_impl.h @@ -5,7 +5,8 @@ #ifndef CONTENT_BROWSER_GPU_GPU_DATA_MANAGER_IMPL_H_ #define CONTENT_BROWSER_GPU_GPU_DATA_MANAGER_IMPL_H_ -#include <set> +#include <list> +#include <map> #include <string> #include "base/compiler_specific.h" @@ -15,6 +16,7 @@ #include "base/memory/singleton.h" #include "base/observer_list_threadsafe.h" #include "base/synchronization/lock.h" +#include "base/time.h" #include "base/values.h" #include "content/browser/gpu/gpu_blacklist.h" #include "content/public/browser/gpu_data_manager.h" @@ -22,12 +24,31 @@ #include "content/public/common/gpu_memory_stats.h" class CommandLine; +class GURL; namespace content { class CONTENT_EXPORT GpuDataManagerImpl : public NON_EXPORTED_BASE(GpuDataManager) { public: + // Indicates the guilt level of a domain which caused a GPU reset. + // If a domain is 100% known to be guilty of resetting the GPU, then + // it will generally not cause other domains' use of 3D APIs to be + // blocked, unless system stability would be compromised. + enum DomainGuilt { + DOMAIN_GUILT_KNOWN, + DOMAIN_GUILT_UNKNOWN + }; + + // Indicates the reason that access to a given client API (like + // WebGL or Pepper 3D) was blocked or not. This state is distinct + // from blacklisting of an entire feature. + enum DomainBlockStatus { + DOMAIN_BLOCK_STATUS_BLOCKED, + DOMAIN_BLOCK_STATUS_ALL_DOMAINS_BLOCKED, + DOMAIN_BLOCK_STATUS_NOT_BLOCKED + }; + // Getter for the singleton. This will return NULL on failure. static GpuDataManagerImpl* GetInstance(); @@ -53,6 +74,8 @@ class CONTENT_EXPORT GpuDataManagerImpl virtual void RemoveObserver(GpuDataManagerObserver* observer) OVERRIDE; virtual void SetWindowCount(uint32 count) OVERRIDE; virtual uint32 GetWindowCount() const OVERRIDE; + virtual void UnblockDomainFrom3DAPIs(const GURL& url) OVERRIDE; + virtual void DisableDomainBlockingFor3DAPIsForTesting() OVERRIDE; // This collects preliminary GPU info, load GpuBlacklist, and compute the // preliminary blacklisted features; it should only be called at browser @@ -91,7 +114,27 @@ class CONTENT_EXPORT GpuDataManagerImpl bool IsUsingAcceleratedSurface() const; #endif + // Maintenance of domains requiring explicit user permission before + // using client-facing 3D APIs (WebGL, Pepper 3D), either because + // the domain has caused the GPU to reset, or because too many GPU + // resets have been observed globally recently, and system stability + // might be compromised. + // + // The given URL may be a partial URL (including at least the host) + // or a full URL to a page. + // + // Note that the unblocking API must be part of the content API + // because it is called from Chrome side code. + void BlockDomainFrom3DAPIs(const GURL& url, DomainGuilt guilt); + DomainBlockStatus Are3DAPIsBlocked(const GURL& url) const; + private: + struct DomainBlockEntry { + DomainGuilt last_guilt; + }; + + typedef std::map<std::string, DomainBlockEntry> DomainBlockMap; + typedef ObserverListThreadSafe<GpuDataManagerObserver> GpuDataManagerObserverList; @@ -108,6 +151,16 @@ class CONTENT_EXPORT GpuDataManagerImpl NoGpuInfoUpdateWithSoftwareRendering); FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, GPUVideoMemoryUsageStatsUpdate); + FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, + BlockAllDomainsFrom3DAPIs); + FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, + UnblockGuiltyDomainFrom3DAPIs); + FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, + UnblockDomainOfUnknownGuiltFrom3DAPIs); + FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, + UnblockOtherDomainFrom3DAPIs); + FRIEND_TEST_ALL_PREFIXES(GpuDataManagerImplTest, + UnblockThisDomainFrom3DAPIs); GpuDataManagerImpl(); virtual ~GpuDataManagerImpl(); @@ -131,6 +184,17 @@ class CONTENT_EXPORT GpuDataManagerImpl // Try to switch to software rendering, if possible and necessary. void EnableSoftwareRenderingIfNecessary(); + // Helper to extract the domain from a given URL. + std::string GetDomainFromURL(const GURL& url) const; + + // Implementation functions for blocking of 3D graphics APIs, used + // for unit testing. + void BlockDomainFrom3DAPIsAtTime( + const GURL& url, DomainGuilt guilt, base::Time at_time); + DomainBlockStatus Are3DAPIsBlockedAtTime( + const GURL& url, base::Time at_time) const; + int64 GetBlockAllDomainsDurationInMs() const; + bool complete_gpu_info_already_requested_; GpuFeatureType blacklisted_features_; @@ -163,6 +227,10 @@ class CONTENT_EXPORT GpuDataManagerImpl // Number of currently open windows, to be used in gpu memory allocation. int window_count_; + DomainBlockMap blocked_domains_; + mutable std::list<base::Time> timestamps_of_gpu_resets_; + bool domain_blocking_enabled_; + DISALLOW_COPY_AND_ASSIGN(GpuDataManagerImpl); }; diff --git a/content/browser/gpu/gpu_data_manager_impl_unittest.cc b/content/browser/gpu/gpu_data_manager_impl_unittest.cc index 371f607..159cf58 100644 --- a/content/browser/gpu/gpu_data_manager_impl_unittest.cc +++ b/content/browser/gpu/gpu_data_manager_impl_unittest.cc @@ -4,9 +4,11 @@ #include "base/message_loop.h" #include "base/run_loop.h" +#include "base/time.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "content/public/common/gpu_info.h" +#include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -39,6 +41,18 @@ class TestObserver : public GpuDataManagerObserver { bool video_memory_usage_stats_updated_; }; +static base::Time GetTimeForTesting() { + return base::Time::FromDoubleT(1000); +} + +static GURL GetDomain1ForTesting() { + return GURL("http://foo.com/"); +} + +static GURL GetDomain2ForTesting() { + return GURL("http://bar.com/"); +} + } // namespace anonymous class GpuDataManagerImplTest : public testing::Test { @@ -48,12 +62,38 @@ class GpuDataManagerImplTest : public testing::Test { virtual ~GpuDataManagerImplTest() { } protected: + // scoped_ptr doesn't work with GpuDataManagerImpl because its + // destructor is private. GpuDataManagerImplTest is however a friend + // so we can make a little helper class here. + class ScopedGpuDataManagerImpl { + public: + ScopedGpuDataManagerImpl() : impl_(new GpuDataManagerImpl()) {} + ~ScopedGpuDataManagerImpl() { delete impl_; } + + GpuDataManagerImpl* get() const { return impl_; } + GpuDataManagerImpl* operator->() const { return impl_; } + // Small violation of C++ style guide to avoid polluting several + // tests with get() calls. + operator GpuDataManagerImpl*() { return impl_; } + + private: + GpuDataManagerImpl* impl_; + DISALLOW_COPY_AND_ASSIGN(ScopedGpuDataManagerImpl); + }; + void SetUp() { } void TearDown() { } + base::Time JustBeforeExpiration(GpuDataManagerImpl* manager); + base::Time JustAfterExpiration(GpuDataManagerImpl* manager); + void TestBlockingDomainFrom3DAPIs( + GpuDataManagerImpl::DomainGuilt guilt_level); + void TestUnblockingDomainFrom3DAPIs( + GpuDataManagerImpl::DomainGuilt guilt_level); + MessageLoop message_loop_; }; @@ -65,8 +105,8 @@ TEST_F(GpuDataManagerImplTest, GpuSideBlacklisting) { // disabled when GPU process launches and collects full GPU info, // it's too late to let renderer know, so we basically block all GPU // access, to be on the safe side. - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); EXPECT_TRUE(manager->GpuAccessAllowed()); @@ -108,13 +148,11 @@ TEST_F(GpuDataManagerImplTest, GpuSideBlacklisting) { EXPECT_EQ(GPU_FEATURE_TYPE_WEBGL | GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS, manager->GetBlacklistedFeatures()); - - delete manager; } TEST_F(GpuDataManagerImplTest, GpuSideExceptions) { - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); EXPECT_TRUE(manager->GpuAccessAllowed()); @@ -153,27 +191,23 @@ TEST_F(GpuDataManagerImplTest, GpuSideExceptions) { manager->UpdateGpuInfo(gpu_info); EXPECT_TRUE(manager->GpuAccessAllowed()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); - - delete manager; } TEST_F(GpuDataManagerImplTest, BlacklistCard) { - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); EXPECT_TRUE(manager->GpuAccessAllowed()); manager->BlacklistCard(); EXPECT_FALSE(manager->GpuAccessAllowed()); EXPECT_EQ(GPU_FEATURE_TYPE_ALL, manager->GetBlacklistedFeatures()); - - delete manager; } TEST_F(GpuDataManagerImplTest, SoftwareRendering) { // Blacklist, then register SwiftShader. - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); EXPECT_TRUE(manager->GpuAccessAllowed()); EXPECT_FALSE(manager->ShouldUseSoftwareRendering()); @@ -190,14 +224,12 @@ TEST_F(GpuDataManagerImplTest, SoftwareRendering) { EXPECT_TRUE(manager->GpuAccessAllowed()); EXPECT_EQ(GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS, manager->GetBlacklistedFeatures()); - - delete manager; } TEST_F(GpuDataManagerImplTest, SoftwareRendering2) { // Register SwiftShader, then blacklist. - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); EXPECT_EQ(0, manager->GetBlacklistedFeatures()); EXPECT_TRUE(manager->GpuAccessAllowed()); EXPECT_FALSE(manager->ShouldUseSoftwareRendering()); @@ -213,13 +245,11 @@ TEST_F(GpuDataManagerImplTest, SoftwareRendering2) { EXPECT_TRUE(manager->ShouldUseSoftwareRendering()); EXPECT_EQ(GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS, manager->GetBlacklistedFeatures()); - - delete manager; } TEST_F(GpuDataManagerImplTest, GpuInfoUpdate) { - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); TestObserver observer; manager->AddObserver(&observer); @@ -237,13 +267,11 @@ TEST_F(GpuDataManagerImplTest, GpuInfoUpdate) { run_loop.RunUntilIdle(); } EXPECT_TRUE(observer.gpu_info_updated()); - - delete manager; } TEST_F(GpuDataManagerImplTest, NoGpuInfoUpdateWithSoftwareRendering) { - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); manager->BlacklistCard(); const FilePath test_path(FILE_PATH_LITERAL("AnyPath")); @@ -271,13 +299,11 @@ TEST_F(GpuDataManagerImplTest, NoGpuInfoUpdateWithSoftwareRendering) { run_loop.RunUntilIdle(); } EXPECT_FALSE(observer.gpu_info_updated()); - - delete manager; } TEST_F(GpuDataManagerImplTest, GPUVideoMemoryUsageStatsUpdate) { - GpuDataManagerImpl* manager = new GpuDataManagerImpl(); - ASSERT_TRUE(manager); + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); TestObserver observer; manager->AddObserver(&observer); @@ -295,8 +321,133 @@ TEST_F(GpuDataManagerImplTest, GPUVideoMemoryUsageStatsUpdate) { run_loop.RunUntilIdle(); } EXPECT_TRUE(observer.video_memory_usage_stats_updated()); +} + +base::Time GpuDataManagerImplTest::JustBeforeExpiration( + GpuDataManagerImpl* manager) { + return GetTimeForTesting() + base::TimeDelta::FromMilliseconds( + manager->GetBlockAllDomainsDurationInMs()) - + base::TimeDelta::FromMilliseconds(3); +} + +base::Time GpuDataManagerImplTest::JustAfterExpiration( + GpuDataManagerImpl* manager) { + return GetTimeForTesting() + base::TimeDelta::FromMilliseconds( + manager->GetBlockAllDomainsDurationInMs()) + + base::TimeDelta::FromMilliseconds(3); +} + +void GpuDataManagerImplTest::TestBlockingDomainFrom3DAPIs( + GpuDataManagerImpl::DomainGuilt guilt_level) { + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); + + manager->BlockDomainFrom3DAPIsAtTime(GetDomain1ForTesting(), + guilt_level, + GetTimeForTesting()); + + // This domain should be blocked no matter what. + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + GetTimeForTesting())); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + JustBeforeExpiration(manager))); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + JustAfterExpiration(manager))); +} + +void GpuDataManagerImplTest::TestUnblockingDomainFrom3DAPIs( + GpuDataManagerImpl::DomainGuilt guilt_level) { + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); + + manager->BlockDomainFrom3DAPIsAtTime(GetDomain1ForTesting(), + guilt_level, + GetTimeForTesting()); + + // Unblocking the domain should work. + manager->UnblockDomainFrom3DAPIs(GetDomain1ForTesting()); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + GetTimeForTesting())); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + JustBeforeExpiration(manager))); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + JustAfterExpiration(manager))); +} + +TEST_F(GpuDataManagerImplTest, BlockGuiltyDomainFrom3DAPIs) { + TestBlockingDomainFrom3DAPIs(GpuDataManagerImpl::DOMAIN_GUILT_KNOWN); +} + +TEST_F(GpuDataManagerImplTest, BlockDomainOfUnknownGuiltFrom3DAPIs) { + TestBlockingDomainFrom3DAPIs(GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN); +} + +TEST_F(GpuDataManagerImplTest, BlockAllDomainsFrom3DAPIs) { + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); + + manager->BlockDomainFrom3DAPIsAtTime(GetDomain1ForTesting(), + GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN, + GetTimeForTesting()); + + // Blocking of other domains should expire. + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_ALL_DOMAINS_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain2ForTesting(), + JustBeforeExpiration(manager))); + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain2ForTesting(), + JustAfterExpiration(manager))); +} + +TEST_F(GpuDataManagerImplTest, UnblockGuiltyDomainFrom3DAPIs) { + TestUnblockingDomainFrom3DAPIs(GpuDataManagerImpl::DOMAIN_GUILT_KNOWN); +} + +TEST_F(GpuDataManagerImplTest, UnblockDomainOfUnknownGuiltFrom3DAPIs) { + TestUnblockingDomainFrom3DAPIs(GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN); +} + +TEST_F(GpuDataManagerImplTest, UnblockOtherDomainFrom3DAPIs) { + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); + + manager->BlockDomainFrom3DAPIsAtTime(GetDomain1ForTesting(), + GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN, + GetTimeForTesting()); + + manager->UnblockDomainFrom3DAPIs(GetDomain2ForTesting()); + + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain2ForTesting(), + JustBeforeExpiration(manager))); + + // The original domain should still be blocked. + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain1ForTesting(), + JustBeforeExpiration(manager))); +} + +TEST_F(GpuDataManagerImplTest, UnblockThisDomainFrom3DAPIs) { + ScopedGpuDataManagerImpl manager; + ASSERT_TRUE(manager.get()); + + manager->BlockDomainFrom3DAPIsAtTime(GetDomain1ForTesting(), + GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN, + GetTimeForTesting()); + + manager->UnblockDomainFrom3DAPIs(GetDomain1ForTesting()); - delete manager; + // This behavior is debatable. Perhaps the GPU reset caused by + // domain 1 should still cause other domains to be blocked. + EXPECT_EQ(GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED, + manager->Are3DAPIsBlockedAtTime(GetDomain2ForTesting(), + JustBeforeExpiration(manager))); } } // namespace content diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 0611555..f5d3063 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -20,6 +20,7 @@ #include "content/browser/dom_storage/dom_storage_context_impl.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/browser/download/download_stats.h" +#include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/plugin_process_host.h" #include "content/browser/plugin_service_impl.h" #include "content/browser/ppapi_plugin_process_host.h" @@ -27,6 +28,7 @@ #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_widget_helper.h" #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/common/child_process_host_impl.h" #include "content/common/child_process_messages.h" #include "content/common/desktop_notification_messages.h" @@ -65,6 +67,9 @@ #if defined(OS_MACOSX) #include "content/common/mac/font_descriptor.h" +#else +#include "third_party/khronos/GLES2/gl2.h" +#include "third_party/khronos/GLES2/gl2ext.h" #endif #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" @@ -191,6 +196,22 @@ class OpenChannelToPpapiBrokerCallback int request_id_; }; +void RaiseInfobarForBlocked3DContentOnUIThread( + int render_process_id, + int render_view_id, + const GURL& url, + content::ThreeDAPIType requester) { + RenderViewHost* rvh = RenderViewHost::FromID( + render_process_id, render_view_id); + if (!rvh) + return; + WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( + WebContents::FromRenderViewHost(rvh)); + if (!web_contents) + return; + web_contents->DidBlock3DAPIs(url, requester); +} + } // namespace class RenderMessageFilter::OpenChannelToNpapiPluginCallback @@ -394,6 +415,8 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(ViewHostMsg_GetMonitorColorProfile, OnGetMonitorColorProfile) IPC_MESSAGE_HANDLER(ViewHostMsg_MediaLogEvent, OnMediaLogEvent) + IPC_MESSAGE_HANDLER(ViewHostMsg_Are3DAPIsBlocked, OnAre3DAPIsBlocked) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidLose3DContext, OnDidLose3DContext) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP_EX() @@ -1024,4 +1047,59 @@ void RenderMessageFilter::OnUpdateIsDelayed(const IPC::Message& msg) { render_widget_helper_->DidReceiveBackingStoreMsg(msg); } +void RenderMessageFilter::OnAre3DAPIsBlocked(int render_view_id, + const GURL& top_origin_url, + ThreeDAPIType requester, + bool* blocked) { + GpuDataManagerImpl::DomainBlockStatus block_status = + GpuDataManagerImpl::GetInstance()->Are3DAPIsBlocked(top_origin_url); + *blocked = (block_status != + GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED); + if (*blocked) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&RaiseInfobarForBlocked3DContentOnUIThread, + render_process_id_, render_view_id, + top_origin_url, requester)); + } +} + +void RenderMessageFilter::OnDidLose3DContext( + const GURL& top_origin_url, + ThreeDAPIType /* unused */, + int arb_robustness_status_code) { +#if defined(OS_MACOSX) + // TODO(kbr): this file indirectly includes npapi.h, which on Mac + // OS pulls in the system OpenGL headers. For some + // not-yet-investigated reason this breaks the build with the 10.6 + // SDK but not 10.7. For now work around this in a way compatible + // with the Khronos headers. +#ifndef GL_GUILTY_CONTEXT_RESET_ARB +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#endif +#ifndef GL_INNOCENT_CONTEXT_RESET_ARB +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#endif +#ifndef GL_UNKNOWN_CONTEXT_RESET_ARB +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#endif + +#endif + GpuDataManagerImpl::DomainGuilt guilt; + switch (arb_robustness_status_code) { + case GL_GUILTY_CONTEXT_RESET_ARB: + guilt = GpuDataManagerImpl::DOMAIN_GUILT_KNOWN; + break; + case GL_UNKNOWN_CONTEXT_RESET_ARB: + guilt = GpuDataManagerImpl::DOMAIN_GUILT_UNKNOWN; + break; + default: + // Ignore lost contexts known to be innocent. + return; + } + + GpuDataManagerImpl::GetInstance()->BlockDomainFrom3DAPIs( + top_origin_url, guilt); +} + } // namespace content diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index a4f9174..9431aca 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -20,6 +20,7 @@ #include "build/build_config.h" #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" #include "content/public/browser/browser_message_filter.h" +#include "content/public/common/three_d_api_types.h" #include "media/base/channel_layout.h" #include "net/cookies/canonical_cookie.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h" @@ -239,6 +240,13 @@ class RenderMessageFilter : public BrowserMessageFilter { OpenChannelToNpapiPluginCallback* client); void OnUpdateIsDelayed(const IPC::Message& msg); + void OnAre3DAPIsBlocked(int render_view_id, + const GURL& top_origin_url, + ThreeDAPIType requester, + bool* blocked); + void OnDidLose3DContext(const GURL& top_origin_url, + ThreeDAPIType context_type, + int arb_robustness_status_code); // Cached resource request dispatcher host and plugin service, guaranteed to // be non-null if Init succeeds. We do not own the objects, they are managed diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index a1e986b9..193e130 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -2342,6 +2342,12 @@ void WebContentsImpl::OnBrowserPluginCreateGuest( params); } +void WebContentsImpl::DidBlock3DAPIs(const GURL& url, + ThreeDAPIType requester) { + FOR_EACH_OBSERVER(WebContentsObserver, observers_, + DidBlock3DAPIs(url, requester)); +} + // Notifies the RenderWidgetHost instance about the fact that the page is // loading, or done loading and calls the base implementation. void WebContentsImpl::SetIsLoading(bool is_loading, diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 85fa28e..8e45301 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -23,6 +23,7 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents.h" #include "content/public/common/renderer_preferences.h" +#include "content/public/common/three_d_api_types.h" #include "net/base/load_states.h" #include "ui/gfx/rect_f.h" #include "ui/gfx/size.h" @@ -179,6 +180,8 @@ class CONTENT_EXPORT WebContentsImpl // an embedder. BrowserPluginEmbedder* GetBrowserPluginEmbedder(); + void DidBlock3DAPIs(const GURL& url, ThreeDAPIType requester); + // WebContents ------------------------------------------------------ virtual WebContentsDelegate* GetDelegate() OVERRIDE; virtual void SetDelegate(WebContentsDelegate* delegate) OVERRIDE; diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 7a6c3b1..0ca67c8 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -21,6 +21,7 @@ #include "content/public/common/referrer.h" #include "content/public/common/renderer_preferences.h" #include "content/public/common/stop_find_action.h" +#include "content/public/common/three_d_api_types.h" #include "content/public/common/window_container_type.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message_macros.h" @@ -75,6 +76,7 @@ IPC_ENUM_TRAITS(content::PageZoom) IPC_ENUM_TRAITS(content::RendererPreferencesHintingEnum) IPC_ENUM_TRAITS(content::RendererPreferencesSubpixelRenderingEnum) IPC_ENUM_TRAITS(content::StopFindAction) +IPC_ENUM_TRAITS(content::ThreeDAPIType) IPC_ENUM_TRAITS(media::ChannelLayout) IPC_ENUM_TRAITS(media::MediaLogEvent::Type) IPC_ENUM_TRAITS(ui::TextInputType) @@ -2409,3 +2411,19 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_ShowDisambiguationPopup, gfx::Rect, /* Border of touched targets */ gfx::Size, /* Size of zoomed image */ TransportDIB::Id /* DIB of zoomed image */) + +// Sent by the renderer process to check whether client 3D APIs +// (Pepper 3D, WebGL) are explicitly blocked. +IPC_SYNC_MESSAGE_CONTROL3_1(ViewHostMsg_Are3DAPIsBlocked, + int /* render_view_id */, + GURL /* top_origin_url */, + content::ThreeDAPIType /* requester */, + bool /* blocked */) + +// Sent by the renderer process to indicate that a context was lost by +// client 3D content (Pepper 3D, WebGL) running on the page at the +// given URL. +IPC_MESSAGE_CONTROL3(ViewHostMsg_DidLose3DContext, + GURL /* top_origin_url */, + content::ThreeDAPIType /* context_type */, + int /* arb_robustness_status_code */) diff --git a/content/content_common.gypi b/content/content_common.gypi index c5f5f65..4fe8ef9 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -89,6 +89,7 @@ 'public/common/ssl_status.cc', 'public/common/ssl_status.h', 'public/common/stop_find_action.h', + 'public/common/three_d_api_types.h', 'public/common/url_constants.cc', 'public/common/url_constants.h', 'public/common/url_fetcher.h', diff --git a/content/public/browser/gpu_data_manager.h b/content/public/browser/gpu_data_manager.h index 8bcea35..e57d152 100644 --- a/content/public/browser/gpu_data_manager.h +++ b/content/public/browser/gpu_data_manager.h @@ -12,6 +12,7 @@ #include "content/public/common/gpu_switching_option.h" class FilePath; +class GURL; namespace base { class ListValue; @@ -84,6 +85,12 @@ class GpuDataManager { virtual void SetWindowCount(uint32 count) = 0; virtual uint32 GetWindowCount() const = 0; + // Allows a given domain previously blocked from accessing 3D APIs + // to access them again. + virtual void UnblockDomainFrom3DAPIs(const GURL& url) = 0; + // Disables domain blocking for 3D APIs. For use only in tests. + virtual void DisableDomainBlockingFor3DAPIsForTesting() = 0; + protected: virtual ~GpuDataManager() {} }; diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h index 59e0a1b..93565da 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h @@ -10,6 +10,7 @@ #include "content/common/content_export.h" #include "content/public/browser/navigation_controller.h" #include "content/public/common/page_transition_types.h" +#include "content/public/common/three_d_api_types.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_sender.h" #include "webkit/glue/window_open_disposition.h" @@ -149,6 +150,12 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener, const FilePath& plugin_path, const base::Callback<void(bool)>& callback); + // Indicates that client 3D APIs (Pepper 3D, WebGL) were just + // blocked on the current page, specifically because the GPU was + // reset recently. + virtual void DidBlock3DAPIs(const GURL& url, + ThreeDAPIType requester) {} + // IPC::Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; diff --git a/content/public/common/three_d_api_types.h b/content/public/common/three_d_api_types.h new file mode 100644 index 0000000..e28f5bc --- /dev/null +++ b/content/public/common/three_d_api_types.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 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 CONTENT_PUBLIC_COMMON_THREE_D_API_TYPES_H_ +#define CONTENT_PUBLIC_COMMON_THREE_D_API_TYPES_H_ + +// This file describes the kinds of 3D APIs exposed to client code. It +// is mainly used to provide more precise messages when access to +// these APIs is restricted for some reason. + +namespace content { + +enum ThreeDAPIType { + THREE_D_API_TYPE_WEBGL, + THREE_D_API_TYPE_PEPPER_3D +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_COMMON_THREE_D_API_TYPES_H_ diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 6593999..9f617a1 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -52,6 +52,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/context_menu_params.h" #include "content/public/common/file_chooser_params.h" +#include "content/public/common/three_d_api_types.h" #include "content/public/common/url_constants.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/context_menu_client.h" @@ -4252,6 +4253,28 @@ WebKit::WebString RenderViewImpl::userAgentOverride( return WebKit::WebString(); } +bool RenderViewImpl::allowWebGL(WebFrame* frame, bool default_value) { + if (!default_value) + return false; + + bool blocked = true; + Send(new ViewHostMsg_Are3DAPIsBlocked( + routing_id_, + GURL(frame->top()->document().securityOrigin().toString()), + THREE_D_API_TYPE_WEBGL, + &blocked)); + return !blocked; +} + +void RenderViewImpl::didLoseWebGLContext( + WebKit::WebFrame* frame, + int arb_robustness_status_code) { + Send(new ViewHostMsg_DidLose3DContext( + GURL(frame->top()->document().securityOrigin().toString()), + THREE_D_API_TYPE_WEBGL, + arb_robustness_status_code)); +} + // WebKit::WebPageSerializerClient implementation ------------------------------ void RenderViewImpl::didSerializeDataForFrame( diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index e46904c..b0b2f33 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -655,6 +655,10 @@ class CONTENT_EXPORT RenderViewImpl virtual WebKit::WebString userAgentOverride( WebKit::WebFrame* frame, const WebKit::WebURL& url) OVERRIDE; + virtual bool allowWebGL(WebKit::WebFrame* frame, bool default_value) OVERRIDE; + virtual void didLoseWebGLContext( + WebKit::WebFrame* frame, + int arb_robustness_status_code) OVERRIDE; // WebKit::WebPageSerializerClient implementation ---------------------------- diff --git a/content/test/data/gpu/webgl.html b/content/test/data/gpu/webgl.html index 928fae7..38e3fab 100644 --- a/content/test/data/gpu/webgl.html +++ b/content/test/data/gpu/webgl.html @@ -39,7 +39,7 @@ function testContextLost(e) { function testContextRestored() { gl = canvas.getContext("experimental-webgl"); if (!gl || gl.isContextLost()) { - document.title = "FAILED: could not get recovered context"; + // Might just be blocked because of infobar. return; } gl.clearColor(0, 0, 1, 1); |