summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-20 13:14:11 +0000
committerkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-20 13:14:11 +0000
commit818915cd6f37ba0ee4e01ef37f293d5a0cce6975 (patch)
tree81d9df68c4de82b8cff3ad5f25992743039f8e9b
parent41766860ac99fc3a894728084df2f8e31054274c (diff)
downloadchromium_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
-rw-r--r--chrome/app/generated_resources.grd17
-rw-r--r--chrome/browser/api/infobars/infobar_delegate.cc4
-rw-r--r--chrome/browser/api/infobars/infobar_delegate.h2
-rw-r--r--chrome/browser/three_d_api_observer.cc118
-rw-r--r--chrome/browser/three_d_api_observer.h27
-rw-r--r--chrome/browser/ui/tab_contents/tab_contents.cc2
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/test/gpu/gpu_crash_browsertest.cc27
-rw-r--r--content/browser/DEPS1
-rw-r--r--content/browser/gpu/gpu_data_manager_impl.cc124
-rw-r--r--content/browser/gpu/gpu_data_manager_impl.h70
-rw-r--r--content/browser/gpu/gpu_data_manager_impl_unittest.cc213
-rw-r--r--content/browser/renderer_host/render_message_filter.cc78
-rw-r--r--content/browser/renderer_host/render_message_filter.h8
-rw-r--r--content/browser/web_contents/web_contents_impl.cc6
-rw-r--r--content/browser/web_contents/web_contents_impl.h3
-rw-r--r--content/common/view_messages.h18
-rw-r--r--content/content_common.gypi1
-rw-r--r--content/public/browser/gpu_data_manager.h7
-rw-r--r--content/public/browser/web_contents_observer.h7
-rw-r--r--content/public/common/three_d_api_types.h21
-rw-r--r--content/renderer/render_view_impl.cc23
-rw-r--r--content/renderer/render_view_impl.h4
-rw-r--r--content/test/data/gpu/webgl.html2
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);