summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/active_tab_permission_manager.cc122
-rw-r--r--chrome/browser/extensions/active_tab_permission_manager.h93
-rw-r--r--chrome/browser/extensions/active_tab_unittest.cc424
-rw-r--r--chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc2
-rw-r--r--chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc20
-rw-r--r--chrome/browser/extensions/api/tabs/tabs.cc13
-rw-r--r--chrome/browser/extensions/api/tabs/tabs.h1
-rw-r--r--chrome/browser/extensions/extension_tab_helper.cc19
-rw-r--r--chrome/browser/extensions/extension_tab_helper.h15
-rw-r--r--chrome/browser/extensions/extension_tab_util.cc7
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.cc4
-rw-r--r--chrome/browser/extensions/script_badge_controller.cc1
-rw-r--r--chrome/chrome_browser_extensions.gypi2
-rw-r--r--chrome/chrome_renderer.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/extensions/extension.cc60
-rw-r--r--chrome/common/extensions/extension.h29
-rw-r--r--chrome/common/extensions/extension_messages.cc1
-rw-r--r--chrome/common/extensions/extension_messages.h18
-rw-r--r--chrome/common/extensions/extension_unittest.cc183
-rw-r--r--chrome/common/extensions/url_pattern_set.cc40
-rw-r--r--chrome/common/extensions/url_pattern_set.h7
-rw-r--r--chrome/common/extensions/url_pattern_set_unittest.cc172
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc70
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.h9
-rw-r--r--chrome/renderer/extensions/extension_helper.cc8
-rw-r--r--chrome/renderer/extensions/extension_helper.h5
-rw-r--r--chrome/renderer/extensions/tab_finder.cc40
-rw-r--r--chrome/renderer/extensions/tab_finder.h40
-rw-r--r--chrome/renderer/extensions/user_script_scheduler.cc5
-rw-r--r--chrome/renderer/extensions/user_script_slave.cc8
-rw-r--r--chrome/test/data/extensions/script_and_capture/tab_specific.json4
32 files changed, 1341 insertions, 84 deletions
diff --git a/chrome/browser/extensions/active_tab_permission_manager.cc b/chrome/browser/extensions/active_tab_permission_manager.cc
new file mode 100644
index 0000000..29d9311
--- /dev/null
+++ b/chrome/browser/extensions/active_tab_permission_manager.cc
@@ -0,0 +1,122 @@
+// 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/extensions/active_tab_permission_manager.h"
+
+#include "chrome/browser/extensions/extension_tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/tab_contents/tab_contents.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_permission_set.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+
+using content::RenderProcessHost;
+using content::WebContentsObserver;
+
+namespace extensions {
+
+ActiveTabPermissionManager::ActiveTabPermissionManager(
+ TabContents* tab_contents)
+ : WebContentsObserver(tab_contents->web_contents()),
+ tab_contents_(tab_contents) {
+ InsertActiveURL(web_contents()->GetURL());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_UNLOADED,
+ content::Source<Profile>(tab_contents->profile()));
+}
+
+ActiveTabPermissionManager::~ActiveTabPermissionManager() {}
+
+void ActiveTabPermissionManager::GrantIfRequested(const Extension* extension) {
+ if (!extension->HasAPIPermission(ExtensionAPIPermission::kActiveTab))
+ return;
+
+ if (active_urls_.is_empty())
+ return;
+
+ // Only need to check the number of permissions here rather than the URLs
+ // themselves, because the set can only ever grow.
+ const URLPatternSet* old_permissions =
+ extension->GetTabSpecificHostPermissions(tab_id());
+ if (old_permissions && (old_permissions->size() == active_urls_.size()))
+ return;
+
+ granted_.Insert(extension);
+ extension->SetTabSpecificHostPermissions(tab_id(), active_urls_);
+ Send(new ExtensionMsg_UpdateTabSpecificPermissions(GetPageID(),
+ tab_id(),
+ extension->id(),
+ active_urls_));
+}
+
+void ActiveTabPermissionManager::DidCommitProvisionalLoadForFrame(
+ int64 frame_id,
+ bool is_main_frame,
+ const GURL& url,
+ content::PageTransition transition_type,
+ content::RenderViewHost* render_view_host) {
+ if (is_main_frame)
+ ClearActiveURLsAndNotify();
+ InsertActiveURL(url);
+}
+
+void ActiveTabPermissionManager::WebContentsDestroyed(
+ content::WebContents* web_contents) {
+ ClearActiveURLsAndNotify();
+}
+
+void ActiveTabPermissionManager::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED);
+ const Extension* extension =
+ content::Details<UnloadedExtensionInfo>(details)->extension;
+ extension->ClearTabSpecificHostPermissions(tab_id());
+ std::vector<std::string> single_id(1, extension->id());
+ Send(new ExtensionMsg_ClearTabSpecificPermissions(tab_id(), single_id));
+ granted_.Remove(extension->id());
+}
+
+void ActiveTabPermissionManager::ClearActiveURLsAndNotify() {
+ active_urls_.ClearPatterns();
+
+ if (granted_.is_empty())
+ return;
+
+ std::vector<std::string> extension_ids;
+
+ for (ExtensionSet::const_iterator it = granted_.begin();
+ it != granted_.end(); ++it) {
+ (*it)->ClearTabSpecificHostPermissions(tab_id());
+ extension_ids.push_back((*it)->id());
+ }
+
+ Send(new ExtensionMsg_ClearTabSpecificPermissions(tab_id(), extension_ids));
+ granted_.Clear();
+}
+
+void ActiveTabPermissionManager::InsertActiveURL(const GURL& url) {
+ URLPattern pattern(UserScript::kValidUserScriptSchemes);
+ if (pattern.Parse(url.spec()) == URLPattern::PARSE_SUCCESS)
+ active_urls_.AddPattern(pattern);
+}
+
+int32 ActiveTabPermissionManager::tab_id() {
+ return tab_contents_->extension_tab_helper()->tab_id();
+}
+
+int32 ActiveTabPermissionManager::GetPageID() {
+ return tab_contents_->web_contents()->GetController().GetActiveEntry()->
+ GetPageID();
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/active_tab_permission_manager.h b/chrome/browser/extensions/active_tab_permission_manager.h
new file mode 100644
index 0000000..1dffe55
--- /dev/null
+++ b/chrome/browser/extensions/active_tab_permission_manager.h
@@ -0,0 +1,93 @@
+// 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_EXTENSIONS_ACTIVE_TAB_PERMISSION_MANAGER_H_
+#define CHROME_BROWSER_EXTENSIONS_ACTIVE_TAB_PERMISSION_MANAGER_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "chrome/common/extensions/extension_set.h"
+#include "chrome/common/extensions/url_pattern_set.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class TabContents;
+
+namespace extensions {
+
+class Extension;
+
+// Responsible for granting and revoking tab-specific permissions to extensions
+// with the activeTab permission.
+class ActiveTabPermissionManager : public content::WebContentsObserver,
+ public content::NotificationObserver {
+ public:
+ explicit ActiveTabPermissionManager(TabContents* tab_contents);
+ virtual ~ActiveTabPermissionManager();
+
+ // If |extension| has the activeTab permission, grants tab-specific
+ // permissions to it until the next page navigation or refresh.
+ void GrantIfRequested(const Extension* extension);
+
+ // content::WebContentsObserver implementation.
+ //
+ // This is public to be called from ActiveTabTest as a way to fake creating
+ // frames on this tab. It's a bit fragile but there doesn't seem to be any
+ // other way to do it.
+ virtual void DidCommitProvisionalLoadForFrame(
+ int64 frame_id,
+ bool is_main_frame,
+ const GURL& url,
+ content::PageTransition transition_type,
+ content::RenderViewHost* render_view_host) OVERRIDE;
+
+ private:
+ // More content::WebContentsObserver.
+ virtual void WebContentsDestroyed(content::WebContents* web_contents)
+ OVERRIDE;
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Clears any granted tab-specific permissions for all extensions and
+ // notifies renderers.
+ void ClearActiveURLsAndNotify();
+
+ // Inserts a URL pattern giving access to the entire origin for |url|.
+ void InsertActiveURL(const GURL& url);
+
+ // Gets the tab's id.
+ int tab_id();
+
+ // Gets the current page id.
+ int32 GetPageID();
+
+ // Our owning TabContents.
+ TabContents* tab_contents_;
+
+ // The URLPatternSet for frames that are active on the current page. This is
+ // cleared on navigation.
+ //
+ // Note that concerned code probably only cares about the origins of these
+ // URLs, but let them deal with that.
+ URLPatternSet active_urls_;
+
+ // Extensions with the activeTab permission that have been granted
+ // tab-specific permissions until the next navigation/refresh.
+ ExtensionSet granted_;
+
+ // Listen to extension unloaded notifications.
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActiveTabPermissionManager);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_ACTIVE_TAB_PERMISSION_MANAGER_H_
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
new file mode 100644
index 0000000..881829ce
--- /dev/null
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -0,0 +1,424 @@
+// 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 <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/active_tab_permission_manager.h"
+#include "chrome/browser/extensions/extension_tab_helper.h"
+#include "chrome/browser/ui/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/tab_contents/test_tab_contents.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/test/test_browser_thread.h"
+
+using base::DictionaryValue;
+using base::ListValue;
+using content::BrowserThread;
+using content::NavigationController;
+
+namespace extensions {
+namespace {
+
+scoped_refptr<const Extension> CreateTestExtension(
+ const std::string& name,
+ bool has_active_tab_permission) {
+ DictionaryValue manifest;
+ manifest.SetString("name", name);
+ manifest.SetString("version", "1.0.0");
+ manifest.SetInteger("manifest_version", 2);
+
+ if (has_active_tab_permission) {
+ scoped_ptr<ListValue> permissions(new ListValue());
+ permissions->Append(Value::CreateStringValue("activeTab"));
+ manifest.Set("permissions", permissions.release());
+ }
+
+ std::string error;
+ scoped_refptr<const Extension> extension = Extension::Create(
+ FilePath(),
+ Extension::INTERNAL,
+ manifest,
+ 0, // no flags.
+ name,
+ &error);
+ CHECK_EQ("", error);
+ return extension;
+}
+
+class ActiveTabTest : public TabContentsTestHarness {
+ public:
+ ActiveTabTest()
+ : extension(CreateTestExtension("extension", true)),
+ another_extension(CreateTestExtension("another", true)),
+ extension_without_active_tab(
+ CreateTestExtension("without activeTab", false)),
+ ui_thread_(BrowserThread::UI, MessageLoop::current()) {}
+
+ protected:
+ int tab_id() {
+ return tab_contents()->extension_tab_helper()->tab_id();
+ }
+
+ ActiveTabPermissionManager* active_tab_permission_manager() {
+ return tab_contents()->extension_tab_helper()->
+ active_tab_permission_manager();
+ }
+
+ bool IsAllowed(const scoped_refptr<const Extension>& extension,
+ const GURL& url) {
+ return IsAllowed(extension, url, tab_id());
+ }
+
+ bool IsAllowed(const scoped_refptr<const Extension>& extension,
+ const GURL& url,
+ int tab_id) {
+ return (extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+ extension->CanCaptureVisiblePage(url, tab_id, NULL));
+ }
+
+ bool IsBlocked(const scoped_refptr<const Extension>& extension,
+ const GURL& url) {
+ return IsBlocked(extension, url, tab_id());
+ }
+
+ bool IsBlocked(const scoped_refptr<const Extension>& extension,
+ const GURL& url,
+ int tab_id) {
+ return (!extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+ !extension->CanCaptureVisiblePage(url, tab_id, NULL));
+ }
+
+ // Fakes loading a new frame on the page using the WebContentsObserver
+ // interface.
+ // TODO(kalman): if somebody can tell me a way to do this from the
+ // TabContentsTestHarness (or any other test harness) then pray tell.
+ void AddFrame(const GURL& url) {
+ active_tab_permission_manager()->DidCommitProvisionalLoadForFrame(
+ 0, // frame_id
+ false, // is_main_frame
+ url,
+ content::PAGE_TRANSITION_AUTO_SUBFRAME,
+ NULL); // render_view_host
+ }
+
+ // An extension with the activeTab permission.
+ scoped_refptr<const Extension> extension;
+
+ // Another extension with activeTab (for good measure).
+ scoped_refptr<const Extension> another_extension;
+
+ // An extension without the activeTab permission.
+ scoped_refptr<const Extension> extension_without_active_tab;
+
+ private:
+ content::TestBrowserThread ui_thread_;
+};
+
+TEST_F(ActiveTabTest, GrantToSinglePage) {
+ GURL google("http://www.google.com");
+ NavigateAndCommit(google);
+
+ // No access unless it's been granted.
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(
+ extension_without_active_tab);
+
+ // Granted to extension and extension_without_active_tab, but the latter
+ // doesn't have the activeTab permission so not granted.
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ // Other subdomains shouldn't be given access.
+ GURL mail_google("http://mail.google.com");
+ EXPECT_TRUE(IsBlocked(extension, mail_google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ // Reloading the page should clear the active permissions.
+ Reload();
+
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ // But they should still be able to be granted again.
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ // And grant a few more times redundantly for good measure.
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsAllowed(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ // Navigating to a new URL should clear the active permissions.
+ GURL chromium("http://www.chromium.org");
+ NavigateAndCommit(chromium);
+
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium));
+
+ // Should be able to grant to multiple extensions at the same time (if they
+ // have the activeTab permission, of course).
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(
+ extension_without_active_tab);
+
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ EXPECT_TRUE(IsAllowed(extension, chromium));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium));
+
+ // Should be able to go back to URLs that were previously cleared.
+ NavigateAndCommit(google);
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+ active_tab_permission_manager()->GrantIfRequested(
+ extension_without_active_tab);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsAllowed(another_extension, google));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium));
+ EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium));
+};
+
+TEST_F(ActiveTabTest, GrantToMultiplePages) {
+ GURL google("http://www.google.com");
+ NavigateAndCommit(google);
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ // Adding a frame after access was granted shouldn't give it access.
+ GURL chromium("http://www.chromium.org");
+ AddFrame(chromium);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+
+ // Granting access to another extension should give it access to both the
+ // main and sub-frames, but still not to the first extension.
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsAllowed(another_extension, google));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium));
+
+ // Granting access to the first extension should now give it access to the
+ // frame.
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsAllowed(extension, chromium));
+ EXPECT_TRUE(IsAllowed(another_extension, google));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium));
+
+ // Reloading should clear all access.
+ Reload();
+
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium));
+
+ // And after granting, no access to the frames that were there.
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsBlocked(another_extension, google));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium));
+
+ // Having lots of frames on the same page should behave as expected.
+ GURL chromium_index("http://www.chromium.org/index.html");
+ GURL chromium_about("http://www.chromium.org/about.html");
+ GURL chromium_blank("http://www.chromium.org/blank.html");
+ GURL gmail("http://www.gmail.com");
+ GURL mail_google("http://mail.google.com");
+ GURL plus_google("http://plus.google.com");
+ GURL codereview_appspot("http://codereview.appspot.com");
+ GURL omahaproxy_appspot("http://omahaproxy.appspot.com");
+
+ AddFrame(chromium_index);
+ AddFrame(chromium_about);
+ AddFrame(gmail);
+ AddFrame(mail_google);
+
+ EXPECT_TRUE(IsBlocked(extension, chromium_index));
+ EXPECT_TRUE(IsBlocked(extension, chromium_about));
+ EXPECT_TRUE(IsBlocked(extension, chromium_blank));
+ EXPECT_TRUE(IsBlocked(extension, gmail));
+ EXPECT_TRUE(IsBlocked(extension, mail_google));
+ EXPECT_TRUE(IsBlocked(extension, plus_google));
+ EXPECT_TRUE(IsBlocked(extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(extension, omahaproxy_appspot));
+
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_index));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_about));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_blank));
+ EXPECT_TRUE(IsBlocked(another_extension, gmail));
+ EXPECT_TRUE(IsBlocked(another_extension, mail_google));
+ EXPECT_TRUE(IsBlocked(another_extension, plus_google));
+ EXPECT_TRUE(IsBlocked(another_extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(another_extension, omahaproxy_appspot));
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ AddFrame(chromium_blank);
+ AddFrame(plus_google);
+ AddFrame(codereview_appspot);
+
+ EXPECT_TRUE(IsAllowed(extension, chromium_index));
+ EXPECT_TRUE(IsAllowed(extension, chromium_about));
+ // Even though chromium_blank hasn't been given granted, this will work
+ // because it's on the same origin as the other codereview URLs.
+ // because k$b
+ EXPECT_TRUE(IsAllowed(extension, chromium_blank));
+ EXPECT_TRUE(IsAllowed(extension, gmail));
+ EXPECT_TRUE(IsAllowed(extension, mail_google));
+ EXPECT_TRUE(IsBlocked(extension, plus_google));
+ EXPECT_TRUE(IsBlocked(extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(extension, omahaproxy_appspot));
+
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_index));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_about));
+ EXPECT_TRUE(IsBlocked(another_extension, chromium_blank));
+ EXPECT_TRUE(IsBlocked(another_extension, gmail));
+ EXPECT_TRUE(IsBlocked(another_extension, mail_google));
+ EXPECT_TRUE(IsBlocked(another_extension, plus_google));
+ EXPECT_TRUE(IsBlocked(another_extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(another_extension, omahaproxy_appspot));
+
+ active_tab_permission_manager()->GrantIfRequested(another_extension);
+
+ AddFrame(omahaproxy_appspot);
+
+ EXPECT_TRUE(IsAllowed(extension, chromium_index));
+ EXPECT_TRUE(IsAllowed(extension, chromium_about));
+ EXPECT_TRUE(IsAllowed(extension, chromium_blank));
+ EXPECT_TRUE(IsAllowed(extension, gmail));
+ EXPECT_TRUE(IsAllowed(extension, mail_google));
+ EXPECT_TRUE(IsBlocked(extension, plus_google));
+ EXPECT_TRUE(IsBlocked(extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(extension, omahaproxy_appspot));
+
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_index));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_about));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_blank));
+ EXPECT_TRUE(IsAllowed(another_extension, gmail));
+ EXPECT_TRUE(IsAllowed(another_extension, mail_google));
+ EXPECT_TRUE(IsAllowed(another_extension, plus_google));
+ EXPECT_TRUE(IsAllowed(another_extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(another_extension, omahaproxy_appspot));
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, chromium_index));
+ EXPECT_TRUE(IsAllowed(extension, chromium_about));
+ EXPECT_TRUE(IsAllowed(extension, chromium_blank));
+ EXPECT_TRUE(IsAllowed(extension, gmail));
+ EXPECT_TRUE(IsAllowed(extension, mail_google));
+ EXPECT_TRUE(IsAllowed(extension, plus_google));
+ EXPECT_TRUE(IsAllowed(extension, codereview_appspot));
+ EXPECT_TRUE(IsAllowed(extension, omahaproxy_appspot));
+
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_index));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_about));
+ EXPECT_TRUE(IsAllowed(another_extension, chromium_blank));
+ EXPECT_TRUE(IsAllowed(another_extension, gmail));
+ EXPECT_TRUE(IsAllowed(another_extension, mail_google));
+ EXPECT_TRUE(IsAllowed(another_extension, plus_google));
+ EXPECT_TRUE(IsAllowed(another_extension, codereview_appspot));
+ EXPECT_TRUE(IsBlocked(another_extension, omahaproxy_appspot));
+}
+
+TEST_F(ActiveTabTest, Uninstalling) {
+ // Some semi-arbitrary setup.
+ GURL google("http://www.google.com");
+ NavigateAndCommit(google);
+
+ GURL chromium("http://www.chromium.org");
+ AddFrame(chromium);
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ GURL gmail("http://www.gmail.com");
+ AddFrame(gmail);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsAllowed(extension, chromium));
+ EXPECT_TRUE(IsBlocked(extension, gmail));
+
+ // Uninstalling the extension should clear its tab permissions.
+ UnloadedExtensionInfo details(
+ extension,
+ extension_misc::UNLOAD_REASON_DISABLE);
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_UNLOADED,
+ content::Source<Profile>(tab_contents()->profile()),
+ content::Details<UnloadedExtensionInfo>(&details));
+
+ EXPECT_TRUE(IsBlocked(extension, google));
+ EXPECT_TRUE(IsBlocked(extension, chromium));
+ EXPECT_TRUE(IsBlocked(extension, gmail));
+
+ // Granting the extension again should give them back.
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google));
+ EXPECT_TRUE(IsAllowed(extension, chromium));
+ EXPECT_TRUE(IsAllowed(extension, gmail));
+}
+
+TEST_F(ActiveTabTest, OnlyActiveTab) {
+ GURL google("http://www.google.com");
+ NavigateAndCommit(google);
+
+ active_tab_permission_manager()->GrantIfRequested(extension);
+
+ EXPECT_TRUE(IsAllowed(extension, google, tab_id()));
+ EXPECT_TRUE(IsBlocked(extension, google, tab_id() + 1));
+}
+
+} // namespace
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc b/chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc
index a5e8c17..71b9fd9 100644
--- a/chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc
+++ b/chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc
@@ -801,7 +801,7 @@ bool UpdateOffscreenTabFunction::RunImpl() {
tab_contents_ = offscreen_tab->tab_contents();
bool is_async = false;
- if (!UpdateURLIfPresent(update_props, &is_async))
+ if (!UpdateURLIfPresent(update_props, offscreen_tab_id, &is_async))
return false;
// Update the width and height, if specified.
diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
index 9cd9c3f..cdcbd0d 100644
--- a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
@@ -90,7 +90,7 @@ bool ExecuteCodeInTabFunction::RunImpl() {
CHECK(browser);
CHECK(contents);
if (!GetExtension()->CanExecuteScriptOnPage(
- contents->web_contents()->GetURL(), NULL, &error_)) {
+ contents->web_contents()->GetURL(), execute_tab_id_, NULL, &error_)) {
return false;
}
@@ -120,11 +120,8 @@ bool ExecuteCodeInTabFunction::RunImpl() {
return false;
}
- if (!code_string.empty()) {
- if (!Execute(code_string))
- return false;
- return true;
- }
+ if (!code_string.empty())
+ return Execute(code_string);
std::string relative_path;
if (script_info->HasKey(keys::kFileKey)) {
@@ -204,7 +201,8 @@ void ExecuteCodeInTabFunction::LocalizeCSS(
void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success,
const std::string& data) {
if (success) {
- Execute(data);
+ if (!Execute(data))
+ SendResponse(false);
} else {
#if defined(OS_POSIX)
// TODO(viettrungluu): bug: there's no particular reason the path should be
@@ -227,16 +225,12 @@ bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) {
execute_tab_id_, profile(), include_incognito(), &browser, NULL,
&contents, NULL) && contents && browser;
- if (!success) {
- SendResponse(false);
+ if (!success)
return false;
- }
const extensions::Extension* extension = GetExtension();
- if (!extension) {
- SendResponse(false);
+ if (!extension)
return false;
- }
ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT;
std::string function_name = name();
diff --git a/chrome/browser/extensions/api/tabs/tabs.cc b/chrome/browser/extensions/api/tabs/tabs.cc
index 77eb4df..ec6e2de 100644
--- a/chrome/browser/extensions/api/tabs/tabs.cc
+++ b/chrome/browser/extensions/api/tabs/tabs.cc
@@ -1191,7 +1191,7 @@ bool UpdateTabFunction::RunImpl() {
error_ = keys::kNoSelectedTabError;
return false;
}
- tab_id = ExtensionTabUtil::GetTabId(contents->web_contents());
+ tab_id = contents->extension_tab_helper()->tab_id();
} else {
EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id));
}
@@ -1211,7 +1211,7 @@ bool UpdateTabFunction::RunImpl() {
// Navigate the tab to a new location if the url is different.
bool is_async = false;
- if (!UpdateURLIfPresent(update_props, &is_async))
+ if (!UpdateURLIfPresent(update_props, tab_id, &is_async))
return false;
bool active = false;
@@ -1275,6 +1275,7 @@ bool UpdateTabFunction::RunImpl() {
}
bool UpdateTabFunction::UpdateURLIfPresent(DictionaryValue* update_props,
+ int tab_id,
bool* is_async) {
if (!update_props->HasKey(keys::kUrlKey))
return true;
@@ -1301,7 +1302,7 @@ bool UpdateTabFunction::UpdateURLIfPresent(DictionaryValue* update_props,
// we need to check host permissions before allowing them.
if (url.SchemeIs(chrome::kJavaScriptScheme)) {
if (!GetExtension()->CanExecuteScriptOnPage(
- tab_contents_->web_contents()->GetURL(), NULL, &error_)) {
+ tab_contents_->web_contents()->GetURL(), tab_id, NULL, &error_)) {
return false;
}
@@ -1614,8 +1615,12 @@ bool CaptureVisibleTabFunction::RunImpl() {
// captureVisibleTab() can return an image containing sensitive information
// that the browser would otherwise protect. Ensure the extension has
// permission to do this.
- if (!GetExtension()->CanCaptureVisiblePage(web_contents->GetURL(), &error_))
+ if (!GetExtension()->CanCaptureVisiblePage(
+ web_contents->GetURL(),
+ tab_contents->extension_tab_helper()->tab_id(),
+ &error_)) {
return false;
+ }
RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
content::RenderWidgetHostView* view = render_view_host->GetView();
diff --git a/chrome/browser/extensions/api/tabs/tabs.h b/chrome/browser/extensions/api/tabs/tabs.h
index 0fe56f8..14276a0 100644
--- a/chrome/browser/extensions/api/tabs/tabs.h
+++ b/chrome/browser/extensions/api/tabs/tabs.h
@@ -121,6 +121,7 @@ class UpdateTabFunction : public AsyncExtensionFunction {
protected:
virtual ~UpdateTabFunction() {}
virtual bool UpdateURLIfPresent(base::DictionaryValue* update_props,
+ int tab_id,
bool* is_async);
virtual void PopulateResult();
diff --git a/chrome/browser/extensions/extension_tab_helper.cc b/chrome/browser/extensions/extension_tab_helper.cc
index 0a57770..2e4682f 100644
--- a/chrome/browser/extensions/extension_tab_helper.cc
+++ b/chrome/browser/extensions/extension_tab_helper.cc
@@ -30,11 +30,12 @@
#include "content/public/browser/web_contents.h"
#include "ui/gfx/image/image.h"
+using content::RenderViewHost;
using content::WebContents;
using extensions::Extension;
+using extensions::PageActionController;
using extensions::ScriptBadgeController;
using extensions::ScriptExecutorImpl;
-using extensions::PageActionController;
namespace {
@@ -48,7 +49,8 @@ ExtensionTabHelper::ExtensionTabHelper(TabContents* tab_contents)
extension_app_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(
extension_function_dispatcher_(tab_contents->profile(), this)),
- tab_contents_(tab_contents) {
+ tab_contents_(tab_contents),
+ active_tab_permission_manager_(tab_contents) {
if (extensions::switch_utils::IsActionBoxEnabled()) {
script_badge_controller_ = new ScriptBadgeController(tab_contents);
} else {
@@ -75,6 +77,14 @@ void ExtensionTabHelper::GetApplicationInfo(int32 page_id) {
Send(new ExtensionMsg_GetApplicationInfo(routing_id(), page_id));
}
+int ExtensionTabHelper::tab_id() const {
+ return tab_contents_->restore_tab_helper()->session_id().id();
+}
+
+int ExtensionTabHelper::window_id() const {
+ return tab_contents_->restore_tab_helper()->window_id().id();
+}
+
void ExtensionTabHelper::SetExtensionApp(const Extension* extension) {
DCHECK(!extension || extension->GetFullLaunchURL().is_valid());
extension_app_ = extension;
@@ -121,6 +131,11 @@ extensions::LocationBarController*
return location_bar_controller_.get();
}
+void ExtensionTabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
+ render_view_host->Send(
+ new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(), tab_id()));
+}
+
void ExtensionTabHelper::DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
diff --git a/chrome/browser/extensions/extension_tab_helper.h b/chrome/browser/extensions/extension_tab_helper.h
index 6aa2bca..ffcca1c 100644
--- a/chrome/browser/extensions/extension_tab_helper.h
+++ b/chrome/browser/extensions/extension_tab_helper.h
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "chrome/browser/extensions/active_tab_permission_manager.h"
#include "chrome/browser/extensions/app_notify_channel_setup.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
@@ -58,6 +59,12 @@ class ExtensionTabHelper
// the data is available.
void GetApplicationInfo(int32 page_id);
+ // Gets the ID of the tab.
+ int tab_id() const;
+
+ // Gets the window ID of the tab.
+ int window_id() const;
+
// App extensions ------------------------------------------------------------
// Sets the extension denoting this as an app. If |extension| is non-null this
@@ -102,12 +109,18 @@ class ExtensionTabHelper
extensions::LocationBarController* location_bar_controller();
+ extensions::ActiveTabPermissionManager* active_tab_permission_manager() {
+ return &active_tab_permission_manager_;
+ }
+
// Sets a non-extension app icon associated with WebContents and fires an
// INVALIDATE_TYPE_TITLE navigation state change to trigger repaint of title.
void SetAppIcon(const SkBitmap& app_icon);
private:
// content::WebContentsObserver overrides.
+ virtual void RenderViewCreated(
+ content::RenderViewHost* render_view_host) OVERRIDE;
virtual void DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) OVERRIDE;
@@ -196,6 +209,8 @@ class ExtensionTabHelper
scoped_ptr<extensions::LocationBarController> location_bar_controller_;
scoped_refptr<extensions::ScriptBadgeController> script_badge_controller_;
+ extensions::ActiveTabPermissionManager active_tab_permission_manager_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionTabHelper);
};
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index d2cd5b3..e8251a0 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/extension_tab_helper.h"
#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/restore_tab_helper.h"
@@ -41,20 +42,18 @@ int ExtensionTabUtil::GetWindowIdOfTabStripModel(
return -1;
}
-// TODO(sky): this function should really take a TabContents.
int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
const TabContents* tab = TabContents::FromWebContents(web_contents);
- return tab ? tab->restore_tab_helper()->session_id().id() : -1;
+ return tab ? tab->extension_tab_helper()->tab_id() : -1;
}
std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
}
-// TODO(sky): this function should really take a TabContents.
int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
const TabContents* tab = TabContents::FromWebContents(web_contents);
- return tab ? tab->restore_tab_helper()->window_id().id() : -1;
+ return tab ? tab->extension_tab_helper()->window_id() : -1;
}
DictionaryValue* ExtensionTabUtil::CreateTabValue(const WebContents* contents) {
diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc
index d8e0ed0..cc6e6474 100644
--- a/chrome/browser/extensions/extension_toolbar_model.cc
+++ b/chrome/browser/extensions/extension_toolbar_model.cc
@@ -7,6 +7,7 @@
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_helper.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
@@ -95,6 +96,9 @@ ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
if (tab_id < 0)
return ACTION_NONE;
+ tab_contents->extension_tab_helper()->active_tab_permission_manager()->
+ GrantIfRequested(extension);
+
ExtensionAction* browser_action = extension->browser_action();
if (browser_action->HasPopup(tab_id)) {
if (popup_url_out)
diff --git a/chrome/browser/extensions/script_badge_controller.cc b/chrome/browser/extensions/script_badge_controller.cc
index eb3c28d..7efd6a9 100644
--- a/chrome/browser/extensions/script_badge_controller.cc
+++ b/chrome/browser/extensions/script_badge_controller.cc
@@ -89,7 +89,6 @@ void ScriptBadgeController::ExecuteScript(
run_at,
world_type,
this_callback);
-
}
void ScriptBadgeController::OnExecuteScriptFinished(
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index a8f0c0e..9601a2d 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -53,6 +53,8 @@
'sources': [
# All .cc, .h, .m, and .mm files under browser/extensions except for
# tests and mocks.
+ 'browser/extensions/active_tab_permission_manager.cc',
+ 'browser/extensions/active_tab_permission_manager.h',
'browser/extensions/admin_policy.cc',
'browser/extensions/admin_policy.h',
'browser/extensions/api/api_function.cc',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 58a9b7b..9273650 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -111,6 +111,8 @@
'renderer/extensions/send_request_natives.h',
'renderer/extensions/set_icon_natives.cc',
'renderer/extensions/set_icon_natives.h',
+ 'renderer/extensions/tab_finder.cc',
+ 'renderer/extensions/tab_finder.h',
'renderer/extensions/tabs_custom_bindings.cc',
'renderer/extensions/tabs_custom_bindings.h',
'renderer/extensions/tts_custom_bindings.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 9191e1c..b4eaca7 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1181,6 +1181,7 @@
'browser/enumerate_modules_model_unittest_win.cc',
'browser/event_disposition.cc',
'browser/event_disposition.h',
+ 'browser/extensions/active_tab_unittest.cc',
'browser/extensions/admin_policy_unittest.cc',
'browser/extensions/api/alarms/alarms_api_unittest.cc',
'browser/extensions/api/content_settings/content_settings_store_unittest.cc',
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 63fffc7..a01096b 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -3409,6 +3409,7 @@ bool Extension::HasMultipleUISurfaces() const {
}
bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
+ int tab_id,
const UserScript* script,
std::string* error) const {
base::AutoLock auto_lock(runtime_data_lock_);
@@ -3431,6 +3432,16 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
!CanExecuteScriptEverywhere())
return false;
+ // If a tab ID is specified, try the tab-specific permissions.
+ if (tab_id >= 0) {
+ const URLPatternSet* tab_permissions =
+ runtime_data_.GetTabSpecificHostPermissions(tab_id);
+ if (tab_permissions &&
+ tab_permissions->MatchesSecurityOrigin(page_url)) {
+ return true;
+ }
+ }
+
// If a script is specified, use its matches.
if (script)
return script->MatchesURL(page_url);
@@ -3493,7 +3504,17 @@ bool Extension::CanExecuteScriptEverywhere() const {
}
bool Extension::CanCaptureVisiblePage(const GURL& page_url,
+ int tab_id,
std::string *error) const {
+ if (tab_id >= 0) {
+ const URLPatternSet* tab_permissions =
+ GetTabSpecificHostPermissions(tab_id);
+ if (tab_permissions &&
+ tab_permissions->MatchesSecurityOrigin(page_url)) {
+ return true;
+ }
+ }
+
if (HasHostPermission(page_url) || page_url.GetOrigin() == url())
return true;
@@ -3669,6 +3690,24 @@ ExtensionAction* Extension::GetScriptBadge() const {
return script_badge_.get();
}
+const URLPatternSet* Extension::GetTabSpecificHostPermissions(
+ int tab_id) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetTabSpecificHostPermissions(tab_id);
+}
+
+void Extension::SetTabSpecificHostPermissions(
+ int tab_id,
+ const URLPatternSet& permissions) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ runtime_data_.SetTabSpecificHostPermissions(tab_id, permissions);
+}
+
+void Extension::ClearTabSpecificHostPermissions(int tab_id) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ runtime_data_.ClearTabSpecificHostPermissions(tab_id);
+}
+
bool Extension::CheckPlatformAppFeatures(std::string* utf8_error) {
if (!is_platform_app())
return true;
@@ -3714,6 +3753,27 @@ void Extension::RuntimeData::SetActivePermissions(
active_permissions_ = active;
}
+const URLPatternSet*
+ Extension::RuntimeData::GetTabSpecificHostPermissions(int tab_id) const {
+ CHECK_GE(tab_id, 0);
+ TabHostPermissionsMap::const_iterator it =
+ tab_specific_host_permissions_.find(tab_id);
+ return (it != tab_specific_host_permissions_.end()) ? it->second.get() : NULL;
+}
+
+void Extension::RuntimeData::SetTabSpecificHostPermissions(
+ int tab_id,
+ const URLPatternSet& hosts) {
+ CHECK_GE(tab_id, 0);
+ tab_specific_host_permissions_[tab_id] =
+ make_linked_ptr(new URLPatternSet(hosts));
+}
+
+void Extension::RuntimeData::ClearTabSpecificHostPermissions(int tab_id) {
+ CHECK_GE(tab_id, 0);
+ tab_specific_host_permissions_.erase(tab_id);
+}
+
UnloadedExtensionInfo::UnloadedExtensionInfo(
const Extension* extension,
extension_misc::UnloadedExtensionReason reason)
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 6fac262..0239b80 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -494,6 +494,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// This method is also aware of certain special pages that extensions are
// usually not allowed to run script on.
bool CanExecuteScriptOnPage(const GURL& page_url,
+ int tab_id,
const UserScript* script,
std::string* error) const;
@@ -505,7 +506,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// page as an image. Since a page may contain sensitive information, this
// is restricted to the extension's host permissions as well as the
// extension page itself.
- bool CanCaptureVisiblePage(const GURL& page_url, std::string* error) const;
+ bool CanCaptureVisiblePage(const GURL& page_url,
+ int tab_id,
+ std::string* error) const;
// Returns true if this extension updates itself using the extension
// gallery.
@@ -534,6 +537,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// shown for this extension in the location bar.
ExtensionAction* GetScriptBadge() const;
+ // Gets the tab-specific host permissions of |tab_id|, or NULL if there
+ // aren't any.
+ //
+ // This is a weak pointer. Callers should create a copy before mutating any
+ // tab specific permissions.
+ const URLPatternSet* GetTabSpecificHostPermissions(int tab_id) const;
+
+ // Sets the tab-specific host permissions of |tab_id| to |permissions|.
+ void SetTabSpecificHostPermissions(int tab_id,
+ const URLPatternSet& permissions) const;
+
+ // Clears the tab-specific host permissions of |tab_id|.
+ void ClearTabSpecificHostPermissions(int tab_id) const;
+
// Accessors:
const FilePath& path() const { return path_; }
@@ -685,9 +702,19 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
void SetActivePermissions(const ExtensionPermissionSet* active);
scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const;
+ const URLPatternSet* GetTabSpecificHostPermissions(int tab_id) const;
+ void SetTabSpecificHostPermissions(int tab_id,
+ const URLPatternSet& permissions);
+ void ClearTabSpecificHostPermissions(int tab_id);
+
private:
friend class base::RefCountedThreadSafe<RuntimeData>;
+
scoped_refptr<const ExtensionPermissionSet> active_permissions_;
+
+ typedef std::map<int, linked_ptr<const URLPatternSet> >
+ TabHostPermissionsMap;
+ TabHostPermissionsMap tab_specific_host_permissions_;
};
// Chooses the extension ID for an extension based on a variety of criteria.
diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc
index db167d1..82930970 100644
--- a/chrome/common/extensions/extension_messages.cc
+++ b/chrome/common/extensions/extension_messages.cc
@@ -43,6 +43,7 @@ scoped_refptr<Extension>
extension->SetActivePermissions(
new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts));
+
return extension;
}
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index 42aab16..a79c9de 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -234,10 +234,14 @@ IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateUserScripts,
IPC_MESSAGE_ROUTED1(ExtensionMsg_GetApplicationInfo,
int32 /*page_id*/)
-// Tell the renderer which browser window it's being attached to.
+// Tell the render view which browser window it's being attached to.
IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId,
int /* id of browser window */)
+// Tell the render view what its tab ID is.
+IPC_MESSAGE_ROUTED1(ExtensionMsg_SetTabId,
+ int /* id of tab */)
+
// Tell the renderer to update an extension's permission set.
IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions,
int /* UpdateExtensionPermissionsInfo::REASON */,
@@ -246,6 +250,18 @@ IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions,
URLPatternSet /* explicit_hosts */,
URLPatternSet /* scriptable_hosts */)
+// Tell the renderer about new tab-specific permissions for an extension.
+IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdateTabSpecificPermissions,
+ int32 /* page_id (only relevant for the target tab) */,
+ int /* tab_id */,
+ std::string /* extension_id */,
+ URLPatternSet /* host */)
+
+// Tell the renderer to clear tab-specific permissions for some extensions.
+IPC_MESSAGE_CONTROL2(ExtensionMsg_ClearTabSpecificPermissions,
+ int /* tab_id */,
+ std::vector<std::string> /* extension_ids */)
+
// Tell the renderer which type this view is.
IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType,
chrome::ViewType /* view_type */)
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index 4c5f57a..eee2da6 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -615,62 +615,62 @@ TEST(ExtensionTest, WantsFileAccess) {
// <all_urls> permission
extension = LoadManifest("permissions", "permissions_all_urls.json");
EXPECT_TRUE(extension->wants_file_access());
- EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
extension = LoadManifest(
"permissions", "permissions_all_urls.json", Extension::ALLOW_FILE_ACCESS);
EXPECT_TRUE(extension->wants_file_access());
- EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
// file:///* permission
extension = LoadManifest("permissions", "permissions_file_scheme.json");
EXPECT_TRUE(extension->wants_file_access());
- EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
extension = LoadManifest("permissions", "permissions_file_scheme.json",
Extension::ALLOW_FILE_ACCESS);
EXPECT_TRUE(extension->wants_file_access());
- EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
// http://* permission
extension = LoadManifest("permissions", "permissions_http_scheme.json");
EXPECT_FALSE(extension->wants_file_access());
- EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
extension = LoadManifest("permissions", "permissions_http_scheme.json",
Extension::ALLOW_FILE_ACCESS);
EXPECT_FALSE(extension->wants_file_access());
- EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL));
+ EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, -1, NULL, NULL));
// <all_urls> content script match
extension = LoadManifest("permissions", "content_script_all_urls.json");
EXPECT_TRUE(extension->wants_file_access());
EXPECT_FALSE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
extension = LoadManifest("permissions", "content_script_all_urls.json",
Extension::ALLOW_FILE_ACCESS);
EXPECT_TRUE(extension->wants_file_access());
EXPECT_TRUE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
// file:///* content script match
extension = LoadManifest("permissions", "content_script_file_scheme.json");
EXPECT_TRUE(extension->wants_file_access());
EXPECT_FALSE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
extension = LoadManifest("permissions", "content_script_file_scheme.json",
Extension::ALLOW_FILE_ACCESS);
EXPECT_TRUE(extension->wants_file_access());
EXPECT_TRUE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
// http://* content script match
extension = LoadManifest("permissions", "content_script_http_scheme.json");
EXPECT_FALSE(extension->wants_file_access());
EXPECT_FALSE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
extension = LoadManifest("permissions", "content_script_http_scheme.json",
Extension::ALLOW_FILE_ACCESS);
EXPECT_FALSE(extension->wants_file_access());
EXPECT_FALSE(extension->CanExecuteScriptOnPage(
- file_url, &extension->content_scripts()[0], NULL));
+ file_url, -1, &extension->content_scripts()[0], NULL));
}
TEST(ExtensionTest, ExtraFlags) {
@@ -689,49 +689,92 @@ TEST(ExtensionTest, ExtraFlags) {
// Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage
// methods of Extension for extensions with various permissions.
class ExtensionScriptAndCaptureVisibleTest : public testing::Test {
- public:
- ExtensionScriptAndCaptureVisibleTest() {
- PathService::Get(chrome::DIR_TEST_DATA, &dirpath_);
+ protected:
+ ExtensionScriptAndCaptureVisibleTest()
+ : http_url("http://www.google.com"),
+ http_url_with_path("http://www.google.com/index.html"),
+ https_url("https://www.google.com"),
+ file_url("file:///foo/bar"),
+ favicon_url("chrome://favicon/http://www.google.com"),
+ extension_url("chrome-extension://" +
+ Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("foo")))),
+ settings_url("chrome://settings"),
+ about_url("about:flags") {
+ urls_.insert(http_url);
+ urls_.insert(http_url_with_path);
+ urls_.insert(https_url);
+ urls_.insert(file_url);
+ urls_.insert(favicon_url);
+ urls_.insert(extension_url);
+ urls_.insert(settings_url);
+ urls_.insert(about_url);
}
bool Allowed(const Extension* extension, const GURL& url) {
- return (extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
- extension->CanCaptureVisiblePage(url, NULL));
+ return Allowed(extension, url, -1);
+ }
+
+ bool Allowed(const Extension* extension, const GURL& url, int tab_id) {
+ return (extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+ extension->CanCaptureVisiblePage(url, tab_id, NULL));
}
bool CaptureOnly(const Extension* extension, const GURL& url) {
- return !extension->CanExecuteScriptOnPage(url, NULL, NULL) &&
- extension->CanCaptureVisiblePage(url, NULL);
+ return CaptureOnly(extension, url, -1);
+ }
+
+ bool CaptureOnly(const Extension* extension, const GURL& url, int tab_id) {
+ return !extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+ extension->CanCaptureVisiblePage(url, tab_id, NULL);
}
bool Blocked(const Extension* extension, const GURL& url) {
- return !(extension->CanExecuteScriptOnPage(url, NULL, NULL) ||
- extension->CanCaptureVisiblePage(url, NULL));
+ return Blocked(extension, url, -1);
}
- protected:
- FilePath dirpath_;
-};
+ bool Blocked(const Extension* extension, const GURL& url, int tab_id) {
+ return !(extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) ||
+ extension->CanCaptureVisiblePage(url, tab_id, NULL));
+ }
+
+ bool AllowedExclusivelyOnTab(
+ const Extension* extension,
+ const std::set<GURL>& allowed_urls,
+ int tab_id) {
+ bool result = true;
+ for (std::set<GURL>::iterator it = urls_.begin(); it != urls_.end(); ++it) {
+ const GURL& url = *it;
+ if (allowed_urls.count(url))
+ result &= Allowed(extension, url, tab_id);
+ else
+ result &= Blocked(extension, url, tab_id);
+ }
+ return result;
+ }
-TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) {
- scoped_refptr<Extension> extension;
// URLs that are "safe" to provide scripting and capture visible tab access
// to if the permissions allow it.
- GURL http_url("http://www.google.com");
- GURL https_url("https://www.google.com");
- GURL file_url("file:///foo/bar");
+ const GURL http_url;
+ const GURL http_url_with_path;
+ const GURL https_url;
+ const GURL file_url;
// We should allow host permission but not scripting permission for favicon
// urls.
- GURL favicon_url("chrome://favicon/http://www.google.com");
-
- std::string dummy_id =
- Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("whatever")));
+ const GURL favicon_url;
// URLs that regular extensions should never get access to.
- GURL extension_url("chrome-extension://" + dummy_id);
- GURL settings_url("chrome://settings");
- GURL about_url("about:flags");
+ const GURL extension_url;
+ const GURL settings_url;
+ const GURL about_url;
+
+ private:
+ // The set of all URLs above.
+ std::set<GURL> urls_;
+};
+
+TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) {
+ scoped_refptr<Extension> extension;
// Test <all_urls> for regular extensions.
extension = LoadManifestStrict("script_and_capture",
@@ -810,6 +853,74 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) {
EXPECT_FALSE(extension->HasHostPermission(settings_url));
}
+TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) {
+ scoped_refptr<Extension> extension =
+ LoadManifestStrict("script_and_capture", "tab_specific.json");
+
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0));
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(1));
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(2));
+
+ std::set<GURL> no_urls;
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+
+ URLPatternSet allowed_hosts;
+ allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL,
+ http_url.spec()));
+ std::set<GURL> allowed_urls;
+ allowed_urls.insert(http_url);
+ // http_url_with_path() will also be allowed, because Extension should be
+ // considering the security origin of the URL not the URL itself, and
+ // http_url is in allowed_hosts.
+ allowed_urls.insert(http_url_with_path);
+
+ extension->SetTabSpecificHostPermissions(0, allowed_hosts);
+ EXPECT_EQ(allowed_hosts, *extension->GetTabSpecificHostPermissions(0));
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, allowed_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+
+ extension->ClearTabSpecificHostPermissions(0);
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0));
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+
+ std::set<GURL> more_allowed_urls = allowed_urls;
+ more_allowed_urls.insert(https_url);
+ URLPatternSet more_allowed_hosts = allowed_hosts;
+ more_allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL,
+ https_url.spec()));
+
+ extension->SetTabSpecificHostPermissions(0, allowed_hosts);
+ EXPECT_EQ(allowed_hosts, *extension->GetTabSpecificHostPermissions(0));
+ extension->SetTabSpecificHostPermissions(1, more_allowed_hosts);
+ EXPECT_EQ(more_allowed_hosts, *extension->GetTabSpecificHostPermissions(1));
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, allowed_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, more_allowed_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+
+ extension->ClearTabSpecificHostPermissions(0);
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(0));
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, more_allowed_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+
+ extension->ClearTabSpecificHostPermissions(1);
+ EXPECT_EQ(NULL, extension->GetTabSpecificHostPermissions(1));
+
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 0));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 1));
+ EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
+}
+
TEST(ExtensionTest, GenerateId) {
std::string result;
EXPECT_TRUE(Extension::GenerateId("", &result));
diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc
index c84df14..0948f81 100644
--- a/chrome/common/extensions/url_pattern_set.cc
+++ b/chrome/common/extensions/url_pattern_set.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -8,6 +8,7 @@
#include <iterator>
#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
#include "base/values.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/url_pattern.h"
@@ -53,6 +54,39 @@ void URLPatternSet::CreateUnion(const URLPatternSet& set1,
out->patterns_, out->patterns_.begin()));
}
+// static
+void URLPatternSet::CreateUnion(const std::vector<URLPatternSet>& sets,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ if (sets.empty())
+ return;
+
+ // N-way union algorithm is basic O(nlog(n)) merge algorithm.
+ //
+ // Do the first merge step into a working set so that we don't mutate any of
+ // the input.
+ std::vector<URLPatternSet> working;
+ for (size_t i = 0; i < sets.size(); i += 2) {
+ if (i + 1 < sets.size()) {
+ URLPatternSet u;
+ URLPatternSet::CreateUnion(sets[i], sets[i + 1], &u);
+ working.push_back(u);
+ } else {
+ working.push_back(sets[i]);
+ }
+ }
+
+ for (size_t skip = 1; skip < working.size(); skip *= 2) {
+ for (size_t i = 0; i < (working.size() - skip); i += skip) {
+ URLPatternSet u;
+ URLPatternSet::CreateUnion(working[i], working[i + skip], &u);
+ working[i].patterns_.swap(u.patterns_);
+ }
+ }
+
+ out->patterns_.swap(working[0].patterns_);
+}
+
URLPatternSet::URLPatternSet() {}
URLPatternSet::URLPatternSet(const URLPatternSet& rhs)
@@ -76,6 +110,10 @@ bool URLPatternSet::is_empty() const {
return patterns_.empty();
}
+size_t URLPatternSet::size() const {
+ return patterns_.size();
+}
+
void URLPatternSet::AddPattern(const URLPattern& pattern) {
patterns_.insert(pattern);
}
diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h
index c912558..b94239f 100644
--- a/chrome/common/extensions/url_pattern_set.h
+++ b/chrome/common/extensions/url_pattern_set.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -40,6 +40,10 @@ class URLPatternSet {
const URLPatternSet& set2,
URLPatternSet* out);
+ // Clears |out| and populates it with the union of all sets in |sets|.
+ static void CreateUnion(const std::vector<URLPatternSet>& sets,
+ URLPatternSet* out);
+
URLPatternSet();
URLPatternSet(const URLPatternSet& rhs);
explicit URLPatternSet(const std::set<URLPattern>& patterns);
@@ -49,6 +53,7 @@ class URLPatternSet {
bool operator==(const URLPatternSet& rhs) const;
bool is_empty() const;
+ size_t size() const;
const std::set<URLPattern>& patterns() const { return patterns_; }
const_iterator begin() const { return patterns_.begin(); }
const_iterator end() const { return patterns_.end(); }
diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc
index ff78d90..ed45948 100644
--- a/chrome/common/extensions/url_pattern_set_unittest.cc
+++ b/chrome/common/extensions/url_pattern_set_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -9,12 +9,27 @@
namespace {
-static void AddPattern(URLPatternSet* set, const std::string& pattern) {
+void AddPattern(URLPatternSet* set, const std::string& pattern) {
int schemes = URLPattern::SCHEME_ALL;
set->AddPattern(URLPattern(schemes, pattern));
}
+URLPatternSet Patterns(const std::string& pattern) {
+ URLPatternSet set;
+ AddPattern(&set, pattern);
+ return set;
}
+
+URLPatternSet Patterns(const std::string& pattern1,
+ const std::string& pattern2) {
+ URLPatternSet set;
+ AddPattern(&set, pattern1);
+ AddPattern(&set, pattern2);
+ return set;
+}
+
+}
+
TEST(URLPatternSetTest, Empty) {
URLPatternSet set;
EXPECT_FALSE(set.MatchesURL(GURL("http://www.foo.com/bar")));
@@ -199,3 +214,156 @@ TEST(URLPatternSetTest, Duplicates) {
// The sets should still be equal after adding a duplicate.
EXPECT_EQ(set2, set1);
}
+
+TEST(URLPatternSetTest, NwayUnion) {
+ std::string google_a = "http://www.google.com/a*";
+ std::string google_b = "http://www.google.com/b*";
+ std::string google_c = "http://www.google.com/c*";
+ std::string yahoo_a = "http://www.yahoo.com/a*";
+ std::string yahoo_b = "http://www.yahoo.com/b*";
+ std::string yahoo_c = "http://www.yahoo.com/c*";
+ std::string reddit_a = "http://www.reddit.com/a*";
+ std::string reddit_b = "http://www.reddit.com/b*";
+ std::string reddit_c = "http://www.reddit.com/c*";
+
+ // Empty list.
+ {
+ std::vector<URLPatternSet> empty;
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(empty, &result);
+
+ URLPatternSet expected;
+ EXPECT_EQ(expected, result);
+ }
+
+ // Singleton list.
+ {
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected = Patterns(google_a);
+ EXPECT_EQ(expected, result);
+ }
+
+ // List with 2 elements.
+ {
+
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a, google_b));
+ test.push_back(Patterns(google_b, google_c));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected;
+ AddPattern(&expected, google_a);
+ AddPattern(&expected, google_b);
+ AddPattern(&expected, google_c);
+ EXPECT_EQ(expected, result);
+ }
+
+ // List with 3 elements.
+ {
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a, google_b));
+ test.push_back(Patterns(google_b, google_c));
+ test.push_back(Patterns(yahoo_a, yahoo_b));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected;
+ AddPattern(&expected, google_a);
+ AddPattern(&expected, google_b);
+ AddPattern(&expected, google_c);
+ AddPattern(&expected, yahoo_a);
+ AddPattern(&expected, yahoo_b);
+ EXPECT_EQ(expected, result);
+ }
+
+ // List with 7 elements.
+ {
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a));
+ test.push_back(Patterns(google_b));
+ test.push_back(Patterns(google_c));
+ test.push_back(Patterns(yahoo_a));
+ test.push_back(Patterns(yahoo_b));
+ test.push_back(Patterns(yahoo_c));
+ test.push_back(Patterns(reddit_a));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected;
+ AddPattern(&expected, google_a);
+ AddPattern(&expected, google_b);
+ AddPattern(&expected, google_c);
+ AddPattern(&expected, yahoo_a);
+ AddPattern(&expected, yahoo_b);
+ AddPattern(&expected, yahoo_c);
+ AddPattern(&expected, reddit_a);
+ EXPECT_EQ(expected, result);
+ }
+
+ // List with 8 elements.
+ {
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a));
+ test.push_back(Patterns(google_b));
+ test.push_back(Patterns(google_c));
+ test.push_back(Patterns(yahoo_a));
+ test.push_back(Patterns(yahoo_b));
+ test.push_back(Patterns(yahoo_c));
+ test.push_back(Patterns(reddit_a));
+ test.push_back(Patterns(reddit_b));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected;
+ AddPattern(&expected, google_a);
+ AddPattern(&expected, google_b);
+ AddPattern(&expected, google_c);
+ AddPattern(&expected, yahoo_a);
+ AddPattern(&expected, yahoo_b);
+ AddPattern(&expected, yahoo_c);
+ AddPattern(&expected, reddit_a);
+ AddPattern(&expected, reddit_b);
+ EXPECT_EQ(expected, result);
+ }
+
+ // List with 9 elements.
+ {
+
+ std::vector<URLPatternSet> test;
+ test.push_back(Patterns(google_a));
+ test.push_back(Patterns(google_b));
+ test.push_back(Patterns(google_c));
+ test.push_back(Patterns(yahoo_a));
+ test.push_back(Patterns(yahoo_b));
+ test.push_back(Patterns(yahoo_c));
+ test.push_back(Patterns(reddit_a));
+ test.push_back(Patterns(reddit_b));
+ test.push_back(Patterns(reddit_c));
+
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(test, &result);
+
+ URLPatternSet expected;
+ AddPattern(&expected, google_a);
+ AddPattern(&expected, google_b);
+ AddPattern(&expected, google_c);
+ AddPattern(&expected, yahoo_a);
+ AddPattern(&expected, yahoo_b);
+ AddPattern(&expected, yahoo_c);
+ AddPattern(&expected, reddit_a);
+ AddPattern(&expected, reddit_b);
+ AddPattern(&expected, reddit_c);
+ EXPECT_EQ(expected, result);
+ }
+}
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index 1104566..23af146 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -40,6 +40,7 @@
#include "chrome/renderer/extensions/page_capture_custom_bindings.h"
#include "chrome/renderer/extensions/send_request_natives.h"
#include "chrome/renderer/extensions/set_icon_natives.h"
+#include "chrome/renderer/extensions/tab_finder.h"
#include "chrome/renderer/extensions/tabs_custom_bindings.h"
#include "chrome/renderer/extensions/tts_custom_bindings.h"
#include "chrome/renderer/extensions/user_script_slave.h"
@@ -72,6 +73,7 @@ using WebKit::WebString;
using WebKit::WebVector;
using WebKit::WebView;
using content::RenderThread;
+using content::RenderView;
using extensions::ApiDefinitionsNatives;
using extensions::AppWindowCustomBindings;
using extensions::ContextMenusCustomBindings;
@@ -91,6 +93,7 @@ using extensions::PageCaptureCustomBindings;
using extensions::SendRequestNatives;
using extensions::SetIconNatives;
using extensions::TTSCustomBindings;
+using extensions::TabFinder;
using extensions::TabsCustomBindings;
using extensions::UpdatedExtensionPermissionsInfo;
using extensions::WebRequestCustomBindings;
@@ -153,7 +156,7 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension {
extension_dispatcher()->v8_context_set().GetCurrent();
if (!context)
return v8::Undefined();
- content::RenderView* render_view = context->GetRenderView();
+ RenderView* render_view = context->GetRenderView();
if (IsContextLazyBackgroundPage(render_view, context->extension())) {
render_view->Send(new ExtensionHostMsg_IncrementLazyKeepaliveCount(
render_view->GetRoutingID()));
@@ -166,7 +169,7 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension {
extension_dispatcher()->v8_context_set().GetCurrent();
if (!context)
return v8::Undefined();
- content::RenderView* render_view = context->GetRenderView();
+ RenderView* render_view = context->GetRenderView();
if (IsContextLazyBackgroundPage(render_view, context->extension())) {
render_view->Send(new ExtensionHostMsg_DecrementLazyKeepaliveCount(
render_view->GetRoutingID()));
@@ -175,7 +178,7 @@ class LazyBackgroundPageNativeHandler : public ChromeV8Extension {
}
private:
- bool IsContextLazyBackgroundPage(content::RenderView* render_view,
+ bool IsContextLazyBackgroundPage(RenderView* render_view,
const Extension* extension) {
if (!render_view)
return false;
@@ -265,6 +268,10 @@ bool ExtensionDispatcher::OnControlMessageReceived(
OnSetScriptingWhitelist)
IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions,
+ OnUpdateTabSpecificPermissions)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ClearTabSpecificPermissions,
+ OnClearTabSpecificPermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI)
IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldUnload, OnShouldUnload)
@@ -346,7 +353,7 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id,
const Extension* extension = extensions_.GetByID(extension_id);
if (extension && extension->has_lazy_background_page() &&
function_name == kEventDispatchFunction) {
- content::RenderView* background_view =
+ RenderView* background_view =
ExtensionHelper::GetBackgroundPage(extension_id);
if (background_view) {
background_view->Send(new ExtensionHostMsg_EventAck(
@@ -779,12 +786,13 @@ void ExtensionDispatcher::InitOriginPermissions(const Extension* extension) {
false);
}
- UpdateOriginPermissions(UpdatedExtensionPermissionsInfo::ADDED,
- extension,
- extension->GetActivePermissions()->explicit_hosts());
+ AddOrRemoveOriginPermissions(
+ UpdatedExtensionPermissionsInfo::ADDED,
+ extension,
+ extension->GetActivePermissions()->explicit_hosts());
}
-void ExtensionDispatcher::UpdateOriginPermissions(
+void ExtensionDispatcher::AddOrRemoveOriginPermissions(
UpdatedExtensionPermissionsInfo::Reason reason,
const Extension* extension,
const URLPatternSet& origins) {
@@ -828,15 +836,49 @@ void ExtensionDispatcher::OnUpdatePermissions(
static_cast<UpdatedExtensionPermissionsInfo::Reason>(reason_id);
const ExtensionPermissionSet* new_active = NULL;
- if (reason == UpdatedExtensionPermissionsInfo::ADDED) {
- new_active = ExtensionPermissionSet::CreateUnion(old_active, delta);
- } else {
- CHECK_EQ(UpdatedExtensionPermissionsInfo::REMOVED, reason);
- new_active = ExtensionPermissionSet::CreateDifference(old_active, delta);
+ switch (reason) {
+ case UpdatedExtensionPermissionsInfo::ADDED:
+ new_active = ExtensionPermissionSet::CreateUnion(old_active, delta);
+ break;
+ case UpdatedExtensionPermissionsInfo::REMOVED:
+ new_active = ExtensionPermissionSet::CreateDifference(old_active, delta);
+ break;
}
extension->SetActivePermissions(new_active);
- UpdateOriginPermissions(reason, extension, explicit_hosts);
+ AddOrRemoveOriginPermissions(reason, extension, explicit_hosts);
+}
+
+void ExtensionDispatcher::OnUpdateTabSpecificPermissions(
+ int page_id,
+ int tab_id,
+ const std::string& extension_id,
+ const URLPatternSet& origin_set) {
+ RenderView* view = TabFinder::Find(tab_id);
+
+ // For now, the message should only be sent to the render view that contains
+ // the target tab. This may change. Either way, if this is the target tab it
+ // gives us the chance to check against the page ID to avoid races.
+ DCHECK(view);
+ if (view && view->GetPageId() != page_id)
+ return;
+
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (!extension)
+ return;
+
+ extension->SetTabSpecificHostPermissions(tab_id, origin_set);
+}
+
+void ExtensionDispatcher::OnClearTabSpecificPermissions(
+ int tab_id,
+ const std::vector<std::string>& extension_ids) {
+ for (std::vector<std::string>::const_iterator it = extension_ids.begin();
+ it != extension_ids.end(); ++it) {
+ const Extension* extension = extensions_.GetByID(*it);
+ if (extension)
+ extension->ClearTabSpecificHostPermissions(tab_id);
+ }
}
void ExtensionDispatcher::OnUpdateUserScripts(
diff --git a/chrome/renderer/extensions/extension_dispatcher.h b/chrome/renderer/extensions/extension_dispatcher.h
index 88bb1e4..40eaac5 100644
--- a/chrome/renderer/extensions/extension_dispatcher.h
+++ b/chrome/renderer/extensions/extension_dispatcher.h
@@ -153,6 +153,13 @@ class ExtensionDispatcher : public content::RenderProcessObserver {
const ExtensionAPIPermissionSet& apis,
const URLPatternSet& explicit_hosts,
const URLPatternSet& scriptable_hosts);
+ void OnUpdateTabSpecificPermissions(int page_id,
+ int tab_id,
+ const std::string& extension_id,
+ const URLPatternSet& origin_set);
+ void OnClearTabSpecificPermissions(
+ int tab_id,
+ const std::vector<std::string>& extension_ids);
void OnUpdateUserScripts(base::SharedMemoryHandle table);
void OnUsingWebRequestAPI(
bool adblock,
@@ -170,7 +177,7 @@ class ExtensionDispatcher : public content::RenderProcessObserver {
// Sets up the host permissions for |extension|.
void InitOriginPermissions(const extensions::Extension* extension);
- void UpdateOriginPermissions(
+ void AddOrRemoveOriginPermissions(
extensions::UpdatedExtensionPermissionsInfo::Reason reason,
const extensions::Extension* extension,
const URLPatternSet& origins);
diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc
index cafb63d..a3460bf 100644
--- a/chrome/renderer/extensions/extension_helper.cc
+++ b/chrome/renderer/extensions/extension_helper.cc
@@ -144,6 +144,7 @@ ExtensionHelper::ExtensionHelper(content::RenderView* render_view,
extension_dispatcher_(extension_dispatcher),
pending_app_icon_requests_(0),
view_type_(chrome::VIEW_TYPE_INVALID),
+ tab_id_(-1),
browser_window_id_(-1) {
}
@@ -209,6 +210,7 @@ bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) {
OnExtensionDispatchOnDisconnect)
IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode)
IPC_MESSAGE_HANDLER(ExtensionMsg_GetApplicationInfo, OnGetApplicationInfo)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_SetTabId, OnSetTabId)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateBrowserWindowId,
OnUpdateBrowserWindowId)
IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType,
@@ -373,6 +375,12 @@ void ExtensionHelper::OnNotifyRendererViewType(chrome::ViewType type) {
view_type_ = type;
}
+void ExtensionHelper::OnSetTabId(int init_tab_id) {
+ CHECK_EQ(tab_id_, -1);
+ CHECK_GE(init_tab_id, 0);
+ tab_id_ = init_tab_id;
+}
+
void ExtensionHelper::OnUpdateBrowserWindowId(int window_id) {
browser_window_id_ = window_id;
}
diff --git a/chrome/renderer/extensions/extension_helper.h b/chrome/renderer/extensions/extension_helper.h
index 4e88467..99afbde 100644
--- a/chrome/renderer/extensions/extension_helper.h
+++ b/chrome/renderer/extensions/extension_helper.h
@@ -59,6 +59,7 @@ class ExtensionHelper
bool InstallWebApplicationUsingDefinitionFile(WebKit::WebFrame* frame,
string16* error);
+ int tab_id() const { return tab_id_; }
int browser_window_id() const { return browser_window_id_; }
chrome::ViewType view_type() const { return view_type_; }
@@ -92,6 +93,7 @@ class ExtensionHelper
void OnExecuteCode(const ExtensionMsg_ExecuteCode_Params& params);
void OnGetApplicationInfo(int page_id);
void OnNotifyRendererViewType(chrome::ViewType view_type);
+ void OnSetTabId(int tab_id);
void OnUpdateBrowserWindowId(int window_id);
// Callback triggered when we finish downloading the application definition
@@ -129,6 +131,9 @@ class ExtensionHelper
// Type of view attached with RenderView.
chrome::ViewType view_type_;
+ // Id of the tab which the RenderView is attached to.
+ int tab_id_;
+
// Id number of browser window which RenderView is attached to.
int browser_window_id_;
diff --git a/chrome/renderer/extensions/tab_finder.cc b/chrome/renderer/extensions/tab_finder.cc
new file mode 100644
index 0000000..b821793
--- /dev/null
+++ b/chrome/renderer/extensions/tab_finder.cc
@@ -0,0 +1,40 @@
+// 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/renderer/extensions/tab_finder.h"
+
+#include "chrome/renderer/extensions/extension_helper.h"
+#include "content/public/renderer/render_view.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+
+using content::RenderView;
+
+namespace extensions {
+
+// static
+content::RenderView* TabFinder::Find(int tab_id) {
+ TabFinder finder(tab_id);
+ RenderView::ForEach(&finder);
+ return finder.view_;
+}
+
+TabFinder::TabFinder(int tab_id) : tab_id_(tab_id), view_(NULL) {}
+
+TabFinder::~TabFinder() {}
+
+// Note: Visit returns false to terminate the iteration.
+bool TabFinder::Visit(RenderView* render_view) {
+ // Only interested in the top frame.
+ if (render_view->GetWebView()->mainFrame()->parent())
+ return true;
+
+ ExtensionHelper* helper = ExtensionHelper::Get(render_view);
+ if (helper && helper->tab_id() == tab_id_)
+ view_ = render_view;
+
+ return !view_;
+}
+
+} // namespace extensions
diff --git a/chrome/renderer/extensions/tab_finder.h b/chrome/renderer/extensions/tab_finder.h
new file mode 100644
index 0000000..04cfacb
--- /dev/null
+++ b/chrome/renderer/extensions/tab_finder.h
@@ -0,0 +1,40 @@
+// 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_RENDERER_EXTENSIONS_TAB_FINDER_H_
+#define CHROME_RENDERER_EXTENSIONS_TAB_FINDER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/public/renderer/render_view_visitor.h"
+
+namespace content {
+class RenderView;
+}
+
+namespace extensions {
+
+// Finds the top RenderView associated with a tab ID.
+class TabFinder : public content::RenderViewVisitor {
+ public:
+ // Returns the top RenderView with |tab_id|, or NULL if none is found.
+ static content::RenderView* Find(int tab_id);
+
+ private:
+ explicit TabFinder(int tab_id);
+ virtual ~TabFinder();
+
+ // content::RenderViewVisitor implementation.
+ virtual bool Visit(content::RenderView* render_view) OVERRIDE;
+
+ int tab_id_;
+ content::RenderView* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabFinder);
+};
+
+} // namespace extensions
+
+#endif // CHROME_RENDERER_EXTENSIONS_TAB_FINDER_H_
diff --git a/chrome/renderer/extensions/user_script_scheduler.cc b/chrome/renderer/extensions/user_script_scheduler.cc
index dad8cb9..dc4ebee 100644
--- a/chrome/renderer/extensions/user_script_scheduler.cc
+++ b/chrome/renderer/extensions/user_script_scheduler.cc
@@ -133,6 +133,7 @@ void UserScriptScheduler::ExecuteCodeImpl(
params.extension_id);
content::RenderView* render_view =
content::RenderView::FromWebView(frame_->view());
+ ExtensionHelper* extension_helper = ExtensionHelper::Get(render_view);
// Since extension info is sent separately from user script info, they can
// be out of sync. We just ignore this situation.
@@ -162,7 +163,9 @@ void UserScriptScheduler::ExecuteCodeImpl(
// For child frames, we just skip ones the extension doesn't have access
// to and carry on.
if (!extension->CanExecuteScriptOnPage(frame->document().url(),
- NULL, NULL)) {
+ extension_helper->tab_id(),
+ NULL,
+ NULL)) {
if (frame->parent()) {
continue;
} else {
diff --git a/chrome/renderer/extensions/user_script_slave.cc b/chrome/renderer/extensions/user_script_slave.cc
index 8862631..9ddaf4d 100644
--- a/chrome/renderer/extensions/user_script_slave.cc
+++ b/chrome/renderer/extensions/user_script_slave.cc
@@ -279,8 +279,14 @@ void UserScriptSlave::InjectScripts(WebFrame* frame,
if (!extension)
continue;
- if (!extension->CanExecuteScriptOnPage(data_source_url, script, NULL))
+ // Content scripts are not tab-specific.
+ int kNoTabId = -1;
+ if (!extension->CanExecuteScriptOnPage(data_source_url,
+ kNoTabId,
+ script,
+ NULL)) {
continue;
+ }
// We rely on WebCore for CSS injection, but it's still useful to know how
// many css files there are.
diff --git a/chrome/test/data/extensions/script_and_capture/tab_specific.json b/chrome/test/data/extensions/script_and_capture/tab_specific.json
new file mode 100644
index 0000000..52a16c3
--- /dev/null
+++ b/chrome/test/data/extensions/script_and_capture/tab_specific.json
@@ -0,0 +1,4 @@
+{
+ "name": "Extension with no host permissions.",
+ "version": "1"
+}