diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-10-31 11:16:26 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-10-31 11:16:26 +0000 |
commit | 1e9bf3e0803691d0a228da41fc608347b6db4340 (patch) | |
tree | ab2e5565f71b4219b3da406e19f16fe306704ef5 /extensions | |
parent | f10b58d5bc6ae3e74076fc4ccca14cbc57ef805c (diff) | |
download | chromium_org-1e9bf3e0803691d0a228da41fc608347b6db4340.tar.gz |
Merge from Chromium at DEPS revision 232015
This commit was generated by merge_to_master.py.
Change-Id: If86767ad396b9e2e1a4c1e9df1427daea29703ef
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/DEPS | 27 | ||||
-rw-r--r-- | extensions/browser/OWNERS | 7 | ||||
-rw-r--r-- | extensions/browser/extensions_browser_client.cc | 28 | ||||
-rw-r--r-- | extensions/browser/extensions_browser_client.h | 51 | ||||
-rw-r--r-- | extensions/browser/lazy_background_task_queue.cc | 191 | ||||
-rw-r--r-- | extensions/browser/lazy_background_task_queue.h | 89 | ||||
-rw-r--r-- | extensions/browser/lazy_background_task_queue_unittest.cc | 195 | ||||
-rw-r--r-- | extensions/common/extensions_client.h | 18 | ||||
-rw-r--r-- | extensions/common/manifest_constants.cc | 9 | ||||
-rw-r--r-- | extensions/common/manifest_constants.h | 7 | ||||
-rw-r--r-- | extensions/common/permissions/api_permission.h | 2 | ||||
-rw-r--r-- | extensions/common/permissions/permission_message.h | 1 | ||||
-rw-r--r-- | extensions/common/permissions/permission_set.cc | 251 | ||||
-rw-r--r-- | extensions/common/permissions/permission_set.h | 143 | ||||
-rw-r--r-- | extensions/extensions.gyp | 9 |
15 files changed, 1024 insertions, 4 deletions
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS index 1c35d9ca69..3c1ac11cc4 100644 --- a/extensions/browser/DEPS +++ b/extensions/browser/DEPS @@ -1,3 +1,30 @@ include_rules = [ "+content/public/browser", + + # Temporarily allowed includes as part of the app shell/extensions refactor. + # + # NOTE: Please do not add includes without talking to the app shell team; + # see OWNERS for this directory. + # + # TODO(jamescook): Remove these. http://crbug.com/162530 + "+chrome/browser/chrome_notification_types.h", + "+chrome/browser/extensions/extension_host.h", + "+chrome/browser/extensions/extension_process_manager.h", + "+chrome/browser/extensions/extension_service.h", + "+chrome/browser/extensions/extension_system.h", + "+chrome/browser/extensions/process_map.h", + "+chrome/common/extensions/background_info.h", + "+chrome/common/extensions/extension.h", + "+chrome/common/extensions/extension_messages.h", ] + +specific_include_rules = { + ".*test\.cc": [ + # Temporarily allowed testing includes. See above. + # TODO(jamescook): Remove these. http://crbug.com/159366 + "+chrome/browser/extensions/extension_service_unittest.h", + "+chrome/browser/extensions/test_extension_system.h", + "+chrome/common/extensions/extension_builder.h", + "+chrome/test/base/testing_profile.h", + ] +} diff --git a/extensions/browser/OWNERS b/extensions/browser/OWNERS new file mode 100644 index 0000000000..b06f377b52 --- /dev/null +++ b/extensions/browser/OWNERS @@ -0,0 +1,7 @@ +# Please talk to the apps shell team before adding DEPS. +per-file DEPS=set noparent +per-file DEPS=benwells@chromium.org +per-file DEPS=derat@chromium.org +per-file DEPS=jamescook@chromium.org +per-file DEPS=miket@chromium.org +per-file DEPS=yoz@chromium.org diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc new file mode 100644 index 0000000000..2724d47623 --- /dev/null +++ b/extensions/browser/extensions_browser_client.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/extensions_browser_client.h" + +#include "base/basictypes.h" + +namespace extensions { + +namespace { + +ExtensionsBrowserClient* g_client = NULL; + +} // namespace + +ExtensionsBrowserClient* ExtensionsBrowserClient::Get() { + return g_client; +} + +void ExtensionsBrowserClient::Set(ExtensionsBrowserClient* client) { + // This can happen in tests. + if (g_client) + return; + g_client = client; +} + +} // namespace extensions diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h new file mode 100644 index 0000000000..4810836781 --- /dev/null +++ b/extensions/browser/extensions_browser_client.h @@ -0,0 +1,51 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSIONS_BROWSER_CLIENT_H_ +#define EXTENSIONS_BROWSER_EXTENSIONS_BROWSER_CLIENT_H_ + +namespace content { +class BrowserContext; +} + +namespace extensions { + +// Interface to allow the extensions module to make browser-process-specific +// queries of the embedder. Should be Set() once in the browser process. +// +// NOTE: Methods that do not require knowledge of browser concepts should be +// added in ExtensionsClient (extensions/common/extensions_client.h) even if +// they are only used in the browser process. +class ExtensionsBrowserClient { + public: + virtual ~ExtensionsBrowserClient() {} + + // Returns true if the embedder has started shutting down. + virtual bool IsShuttingDown() = 0; + + // Returns true if the BrowserContexts could be considered equivalent, for + // example, if one is an off-the-record context owned by the other. + virtual bool IsSameContext(content::BrowserContext* first, + content::BrowserContext* second) = 0; + + // Returns true if |context| has an off-the-record content associated with it. + virtual bool HasOffTheRecordContext(content::BrowserContext* context) = 0; + + // Returns the off-the-record context associated with |context|. If |context| + // is already off-the-record, returns |context|. + // WARNING: This may create a new off-the-record context. To avoid creating + // another context, check HasOffTheRecordContext() first. + virtual content::BrowserContext* GetOffTheRecordContext( + content::BrowserContext* context) = 0; + + // Returns the single instance of |this|. + static ExtensionsBrowserClient* Get(); + + // Initialize the single instance. + static void Set(ExtensionsBrowserClient* client); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSIONS_BROWSER_CLIENT_H_ diff --git a/extensions/browser/lazy_background_task_queue.cc b/extensions/browser/lazy_background_task_queue.cc new file mode 100644 index 0000000000..584d897e2e --- /dev/null +++ b/extensions/browser/lazy_background_task_queue.cc @@ -0,0 +1,191 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/lazy_background_task_queue.h" + +#include "base/callback.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/process_map.h" +#include "chrome/common/extensions/background_info.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_messages.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/site_instance.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/view_type.h" + +namespace extensions { + +LazyBackgroundTaskQueue::LazyBackgroundTaskQueue( + content::BrowserContext* browser_context) + : browser_context_(browser_context) { + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, + content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, + content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::Source<content::BrowserContext>(browser_context)); +} + +LazyBackgroundTaskQueue::~LazyBackgroundTaskQueue() { +} + +bool LazyBackgroundTaskQueue::ShouldEnqueueTask( + content::BrowserContext* browser_context, + const Extension* extension) { + DCHECK(extension); + if (BackgroundInfo::HasBackgroundPage(extension)) { + ExtensionProcessManager* pm = ExtensionSystem::GetForBrowserContext( + browser_context)->process_manager(); + DCHECK(pm); + ExtensionHost* background_host = + pm->GetBackgroundHostForExtension(extension->id()); + if (!background_host || !background_host->did_stop_loading()) + return true; + if (pm->IsBackgroundHostClosing(extension->id())) + pm->CancelSuspend(extension); + } + + return false; +} + +void LazyBackgroundTaskQueue::AddPendingTask( + content::BrowserContext* browser_context, + const std::string& extension_id, + const PendingTask& task) { + if (ExtensionsBrowserClient::Get()->IsShuttingDown()) { + task.Run(NULL); + return; + } + PendingTasksList* tasks_list = NULL; + PendingTasksKey key(browser_context, extension_id); + PendingTasksMap::iterator it = pending_tasks_.find(key); + if (it == pending_tasks_.end()) { + tasks_list = new PendingTasksList(); + pending_tasks_[key] = linked_ptr<PendingTasksList>(tasks_list); + + ExtensionService* extension_service = ExtensionSystem::GetForBrowserContext( + browser_context)->extension_service(); + DCHECK(extension_service); + const Extension* extension = + extension_service->extensions()->GetByID(extension_id); + if (extension && BackgroundInfo::HasLazyBackgroundPage(extension)) { + // If this is the first enqueued task, and we're not waiting for the + // background page to unload, ensure the background page is loaded. + ExtensionProcessManager* pm = ExtensionSystem::GetForBrowserContext( + browser_context)->process_manager(); + pm->IncrementLazyKeepaliveCount(extension); + // Creating the background host may fail, e.g. if |profile| is incognito + // but the extension isn't enabled in incognito mode. + if (!pm->CreateBackgroundHost( + extension, BackgroundInfo::GetBackgroundURL(extension))) { + task.Run(NULL); + return; + } + } + } else { + tasks_list = it->second.get(); + } + + tasks_list->push_back(task); +} + +void LazyBackgroundTaskQueue::ProcessPendingTasks( + ExtensionHost* host, + content::BrowserContext* browser_context, + const Extension* extension) { + if (!ExtensionsBrowserClient::Get()->IsSameContext(browser_context, + browser_context_)) + return; + + PendingTasksKey key(browser_context, extension->id()); + PendingTasksMap::iterator map_it = pending_tasks_.find(key); + if (map_it == pending_tasks_.end()) { + if (BackgroundInfo::HasLazyBackgroundPage(extension)) + CHECK(!host); // lazy page should not load without any pending tasks + return; + } + + // Swap the pending tasks to a temporary, to avoid problems if the task + // list is modified during processing. + PendingTasksList tasks; + tasks.swap(*map_it->second); + for (PendingTasksList::const_iterator it = tasks.begin(); + it != tasks.end(); ++it) { + it->Run(host); + } + + pending_tasks_.erase(key); + + // Balance the keepalive in AddPendingTask. Note we don't do this on a + // failure to load, because the keepalive count is reset in that case. + if (host && BackgroundInfo::HasLazyBackgroundPage(extension)) { + ExtensionSystem::GetForBrowserContext(browser_context)->process_manager()-> + DecrementLazyKeepaliveCount(extension); + } +} + +void LazyBackgroundTaskQueue::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: { + // If an on-demand background page finished loading, dispatch queued up + // events for it. + ExtensionHost* host = + content::Details<ExtensionHost>(details).ptr(); + if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { + CHECK(host->did_stop_loading()); + ProcessPendingTasks(host, host->browser_context(), host->extension()); + } + break; + } + case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: { + // Notify consumers about the load failure when the background host dies. + // This can happen if the extension crashes. This is not strictly + // necessary, since we also unload the extension in that case (which + // dispatches the tasks below), but is a good extra precaution. + content::BrowserContext* browser_context = + content::Source<content::BrowserContext>(source).ptr(); + ExtensionHost* host = + content::Details<ExtensionHost>(details).ptr(); + if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { + ProcessPendingTasks(NULL, browser_context, host->extension()); + } + break; + } + case chrome::NOTIFICATION_EXTENSION_UNLOADED: { + // Notify consumers that the page failed to load. + content::BrowserContext* browser_context = + content::Source<content::BrowserContext>(source).ptr(); + UnloadedExtensionInfo* unloaded = + content::Details<UnloadedExtensionInfo>(details).ptr(); + ProcessPendingTasks(NULL, browser_context, unloaded->extension); + // If this extension is also running in an off-the-record context, + // notify that task queue as well. + ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get(); + if (browser_client->HasOffTheRecordContext(browser_context)) { + ProcessPendingTasks( + NULL, + browser_client->GetOffTheRecordContext(browser_context), + unloaded->extension); + } + break; + } + default: + NOTREACHED(); + break; + } +} + +} // namespace extensions diff --git a/extensions/browser/lazy_background_task_queue.h b/extensions/browser/lazy_background_task_queue.h new file mode 100644 index 0000000000..a92ec7c383 --- /dev/null +++ b/extensions/browser/lazy_background_task_queue.h @@ -0,0 +1,89 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_LAZY_BACKGROUND_TASK_QUEUE_H_ +#define EXTENSIONS_BROWSER_LAZY_BACKGROUND_TASK_QUEUE_H_ + +#include <map> +#include <string> + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/linked_ptr.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { +class Extension; +class ExtensionHost; + +// This class maintains a queue of tasks that should execute when an +// extension's lazy background page is loaded. It is also in charge of loading +// the page when the first task is queued. +// +// It is the consumer's responsibility to use this class when appropriate, i.e. +// only with extensions that have not-yet-loaded lazy background pages. +class LazyBackgroundTaskQueue : public content::NotificationObserver { + public: + typedef base::Callback<void(ExtensionHost*)> PendingTask; + + explicit LazyBackgroundTaskQueue(content::BrowserContext* browser_context); + virtual ~LazyBackgroundTaskQueue(); + + // Returns the number of extensions having pending tasks. + size_t extensions_with_pending_tasks() { return pending_tasks_.size(); } + + // Returns true if the task should be added to the queue (that is, if the + // extension has a lazy background page that isn't ready yet). If the + // extension has a lazy background page that is being suspended this method + // cancels that suspension. + bool ShouldEnqueueTask(content::BrowserContext* context, + const Extension* extension); + + // Adds a task to the queue for a given extension. If this is the first + // task added for the extension, its lazy background page will be loaded. + // The task will be called either when the page is loaded, or when the + // page fails to load for some reason (e.g. a crash or browser + // shutdown). In the latter case, the ExtensionHost parameter is NULL. + void AddPendingTask( + content::BrowserContext* context, + const std::string& extension_id, + const PendingTask& task); + + private: + FRIEND_TEST_ALL_PREFIXES(LazyBackgroundTaskQueueTest, ProcessPendingTasks); + + // A map between a BrowserContext/extension_id pair and the queue of tasks + // pending the load of its background page. + typedef std::string ExtensionID; + typedef std::pair<content::BrowserContext*, ExtensionID> PendingTasksKey; + typedef std::vector<PendingTask> PendingTasksList; + typedef std::map<PendingTasksKey, + linked_ptr<PendingTasksList> > PendingTasksMap; + + // content::NotificationObserver interface. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // Called when a lazy background page has finished loading, or has failed to + // load (host is NULL in that case). All enqueued tasks are run in order. + void ProcessPendingTasks( + ExtensionHost* host, + content::BrowserContext* context, + const Extension* extension); + + content::BrowserContext* browser_context_; + content::NotificationRegistrar registrar_; + PendingTasksMap pending_tasks_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_LAZY_BACKGROUND_TASK_QUEUE_H_ diff --git a/extensions/browser/lazy_background_task_queue_unittest.cc b/extensions/browser/lazy_background_task_queue_unittest.cc new file mode 100644 index 0000000000..27d847152f --- /dev/null +++ b/extensions/browser/lazy_background_task_queue_unittest.cc @@ -0,0 +1,195 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/lazy_background_task_queue.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service_unittest.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_builder.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +// An ExtensionProcessManager that doesn't create background host pages. +class TestExtensionProcessManager : public ExtensionProcessManager { + public: + explicit TestExtensionProcessManager(Profile* profile) + : ExtensionProcessManager(profile), + create_count_(0) {} + virtual ~TestExtensionProcessManager() {} + + int create_count() { return create_count_; } + + // ExtensionProcessManager overrides: + virtual extensions::ExtensionHost* CreateBackgroundHost( + const extensions::Extension* extension, + const GURL& url) OVERRIDE { + // Don't actually try to create a web contents. + create_count_++; + return NULL; + } + + private: + int create_count_; + + DISALLOW_COPY_AND_ASSIGN(TestExtensionProcessManager); +}; + +// Derives from ExtensionServiceTestBase because ExtensionService is difficult +// to initialize alone. +class LazyBackgroundTaskQueueTest : public ExtensionServiceTestBase { + public: + LazyBackgroundTaskQueueTest() : task_run_count_(0) {} + virtual ~LazyBackgroundTaskQueueTest() {} + + int task_run_count() { return task_run_count_; } + + // A simple callback for AddPendingTask. + void RunPendingTask(ExtensionHost* host) { + task_run_count_++; + } + + // Creates and registers an extension without a background page. + scoped_refptr<Extension> CreateSimpleExtension() { + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(DictionaryBuilder() + .Set("name", "No background") + .Set("version", "1") + .Set("manifest_version", 2)) + .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + .Build(); + service_->AddExtension(extension); + return extension; + } + + // Creates and registers an extension with a lazy background page. + scoped_refptr<Extension> CreateLazyBackgroundExtension() { + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(DictionaryBuilder() + .Set("name", "Lazy background") + .Set("version", "1") + .Set("manifest_version", 2) + .Set("background", + DictionaryBuilder() + .Set("page", "background.html") + .SetBoolean("persistent", false))) + .SetID("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + .Build(); + service_->AddExtension(extension); + return extension; + } + + private: + // The total number of pending tasks that have been executed. + int task_run_count_; + + DISALLOW_COPY_AND_ASSIGN(LazyBackgroundTaskQueueTest); +}; + +// Tests that only extensions with background pages should have tasks queued. +TEST_F(LazyBackgroundTaskQueueTest, ShouldEnqueueTask) { + InitializeEmptyExtensionService(); + InitializeExtensionProcessManager(); + + LazyBackgroundTaskQueue queue(profile_.get()); + + // Build a simple extension with no background page. + scoped_refptr<Extension> no_background = CreateSimpleExtension(); + EXPECT_FALSE(queue.ShouldEnqueueTask(profile_.get(), no_background.get())); + + // Build another extension with a background page. + scoped_refptr<Extension> with_background = CreateLazyBackgroundExtension(); + EXPECT_TRUE(queue.ShouldEnqueueTask(profile_.get(), with_background.get())); +} + +// Tests that adding tasks actually increases the pending task count, and that +// multiple extensions can have pending tasks. +TEST_F(LazyBackgroundTaskQueueTest, AddPendingTask) { + InitializeEmptyExtensionService(); + + // Swap in our stub TestExtensionProcessManager. + TestExtensionSystem* extension_system = + static_cast<extensions::TestExtensionSystem*>( + ExtensionSystem::Get(profile_.get())); + // Owned by |extension_system|. + TestExtensionProcessManager* process_manager = + new TestExtensionProcessManager(profile_.get()); + extension_system->SetExtensionProcessManager(process_manager); + + LazyBackgroundTaskQueue queue(profile_.get()); + + // Build a simple extension with no background page. + scoped_refptr<Extension> no_background = CreateSimpleExtension(); + + // Adding a pending task increases the number of extensions with tasks, but + // doesn't run the task. + queue.AddPendingTask(profile_.get(), + no_background->id(), + base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask, + base::Unretained(this))); + EXPECT_EQ(1u, queue.extensions_with_pending_tasks()); + EXPECT_EQ(0, task_run_count()); + + // Another task on the same extension doesn't increase the number of + // extensions that have tasks and doesn't run any tasks. + queue.AddPendingTask(profile_.get(), + no_background->id(), + base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask, + base::Unretained(this))); + EXPECT_EQ(1u, queue.extensions_with_pending_tasks()); + EXPECT_EQ(0, task_run_count()); + + // Adding a task on an extension with a lazy background page tries to create + // a background host, and if that fails, runs the task immediately. + scoped_refptr<Extension> lazy_background = CreateLazyBackgroundExtension(); + queue.AddPendingTask(profile_.get(), + lazy_background->id(), + base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask, + base::Unretained(this))); + EXPECT_EQ(2u, queue.extensions_with_pending_tasks()); + // The process manager tried to create a background host. + EXPECT_EQ(1, process_manager->create_count()); + // The task ran immediately because the creation failed. + EXPECT_EQ(1, task_run_count()); +} + +// Tests that pending tasks are actually run. +TEST_F(LazyBackgroundTaskQueueTest, ProcessPendingTasks) { + InitializeEmptyExtensionService(); + + LazyBackgroundTaskQueue queue(profile_.get()); + + // ProcessPendingTasks is a no-op if there are no tasks. + scoped_refptr<Extension> extension = CreateSimpleExtension(); + queue.ProcessPendingTasks(NULL, profile_.get(), extension); + EXPECT_EQ(0, task_run_count()); + + // Schedule a task to run. + queue.AddPendingTask(profile_.get(), + extension->id(), + base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask, + base::Unretained(this))); + EXPECT_EQ(0, task_run_count()); + EXPECT_EQ(1u, queue.extensions_with_pending_tasks()); + + // Trying to run tasks for an unrelated profile should do nothing. + TestingProfile profile2; + queue.ProcessPendingTasks(NULL, &profile2, extension); + EXPECT_EQ(0, task_run_count()); + EXPECT_EQ(1u, queue.extensions_with_pending_tasks()); + + // Processing tasks when there is one pending runs the task and removes the + // extension from the list of extensions with pending tasks. + queue.ProcessPendingTasks(NULL, profile_.get(), extension); + EXPECT_EQ(1, task_run_count()); + EXPECT_EQ(0u, queue.extensions_with_pending_tasks()); +} + +} // namespace extensions diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h index ed06d32287..71a4dfded1 100644 --- a/extensions/common/extensions_client.h +++ b/extensions/common/extensions_client.h @@ -7,9 +7,12 @@ #include <set> #include <string> +#include <vector> namespace extensions { +class APIPermissionSet; +class Extension; class FeatureProvider; class PermissionMessage; class PermissionMessageProvider; @@ -20,6 +23,8 @@ class URLPatternSet; // process. This should be implemented by the client of the extensions system. class ExtensionsClient { public: + typedef std::vector<std::string> ScriptingWhitelist; + // Initializes global state. Not done in the constructor because unit tests // can create additional ExtensionsClients because the utility thread runs // in-process. @@ -45,6 +50,19 @@ class ExtensionsClient { URLPatternSet* new_hosts, std::set<PermissionMessage>* messages) const = 0; + // Replaces the scripting whitelist with |whitelist|. Used in the renderer; + // only used for testing in the browser process. + virtual void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) = 0; + + // Return the whitelist of extensions that can run content scripts on + // any origin. + virtual const ScriptingWhitelist& GetScriptingWhitelist() const = 0; + + // Get the set of chrome:// hosts that |extension| can run content scripts on. + virtual URLPatternSet GetPermittedChromeSchemeHosts( + const Extension* extension, + const APIPermissionSet& api_permissions) const = 0; + // Return the extensions client. static ExtensionsClient* Get(); diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index c03e3f4553..26df7ceaba 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -111,6 +111,8 @@ const char kRunAt[] = "run_at"; const char kSandboxedPages[] = "sandbox.pages"; const char kSandboxedPagesCSP[] = "sandbox.content_security_policy"; const char kScriptBadge[] = "script_badge"; +const char kSearchProvider[] = "chrome_settings_overrides.search_provider"; +const char kSettingsOverride[] = "chrome_settings_overrides"; const char kShiftKey[] = "shiftKey"; const char kShortcutKey[] = "shortcutKey"; const char kShortName[] = "short_name"; @@ -149,7 +151,6 @@ const char kUrlHandlers[] = "url_handlers"; const char kUrlHandlerTitle[] = "title"; const char kVersion[] = "version"; const char kWebAccessibleResources[] = "web_accessible_resources"; -const char kWebRtc[] = "webrtc"; const char kWebURLs[] = "app.urls"; const char kWebview[] = "webview"; const char kWebviewAccessibleResources[] = "accessible_resources"; @@ -337,6 +338,8 @@ const char kInvalidGlob[] = "Invalid value for 'content_scripts[*].*[*]'."; const char kInvalidGlobList[] = "Invalid value for 'content_scripts[*].*'."; +const char kInvalidHomepageOverrideURL[] = + "Invalid value for overriding homepage url: '[*]'."; const char kInvalidHomepageURL[] = "Invalid value for homepage url: '[*]'."; const char kInvalidIconPath[] = @@ -528,6 +531,8 @@ const char kInvalidSandboxedPagesCSP[] = "Invalid value for 'sandbox.content_security_policy'."; const char kInvalidScriptBadge[] = "Invalid value for 'script_badge'."; +const char kInvalidEmptySettingsOverrides[] = + "Empty dictionary for 'chrome_settings_overrides'."; const char kInvalidShortName[] = "Invalid value for 'short_name'."; const char kInvalidSignature[] = @@ -542,6 +547,8 @@ const char kInvalidSpellcheckDictionaryLocale[] = "Invalid value for spellcheck dictionary locale."; const char kInvalidSpellcheckDictionaryPath[] = "Invalid value for spellcheck dictionary path."; +const char kInvalidStartupOverrideURL[] = + "Invalid value for overriding startup URL: '[*]'."; const char kInvalidSystemIndicator[] = "Invalid value for 'system_indicator'."; const char kInvalidTheme[] = diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index bf78fa5978..846845e200 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -48,7 +48,6 @@ extern const char kFileHandlerTypes[]; extern const char kFileFilters[]; extern const char kFileBrowserHandlers[]; extern const char kGlobal[]; -extern const char kMediaGalleriesHandlers[]; extern const char kHomepageURL[]; extern const char kIcons[]; extern const char kId[]; @@ -79,6 +78,7 @@ extern const char kLaunchWidth[]; extern const char kLayouts[]; extern const char kManifestVersion[]; extern const char kMatches[]; +extern const char kMediaGalleriesHandlers[]; extern const char kMIMETypes[]; extern const char kMimeTypesHandler[]; extern const char kMinimumChromeVersion[]; @@ -120,6 +120,8 @@ extern const char kRunAt[]; extern const char kSandboxedPages[]; extern const char kSandboxedPagesCSP[]; extern const char kScriptBadge[]; +extern const char kSearchProvider[]; +extern const char kSettingsOverride[]; extern const char kShiftKey[]; extern const char kShortcutKey[]; extern const char kShortName[]; @@ -283,6 +285,7 @@ extern const char kInvalidFileHandlerType[]; extern const char kInvalidFileHandlerTypeElement[]; extern const char kInvalidGlob[]; extern const char kInvalidGlobList[]; +extern const char kInvalidHomepageOverrideURL[]; extern const char kInvalidHomepageURL[]; extern const char kInvalidIconPath[]; extern const char kInvalidIcons[]; @@ -374,6 +377,7 @@ extern const char kInvalidSandboxedPagesList[]; extern const char kInvalidSandboxedPage[]; extern const char kInvalidSandboxedPagesCSP[]; extern const char kInvalidScriptBadge[]; +extern const char kInvalidEmptySettingsOverrides[]; extern const char kInvalidShortName[]; extern const char kInvalidSignature[]; extern const char kInvalidSpellcheck[]; @@ -381,6 +385,7 @@ extern const char kInvalidSpellcheckDictionaryFormat[]; extern const char kInvalidSpellcheckDictionaryLanguage[]; extern const char kInvalidSpellcheckDictionaryLocale[]; extern const char kInvalidSpellcheckDictionaryPath[]; +extern const char kInvalidStartupOverrideURL[]; extern const char kInvalidSystemIndicator[]; extern const char kInvalidTheme[]; extern const char kInvalidThemeColors[]; diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h index 7dd42c0046..2ff17eaef4 100644 --- a/extensions/common/permissions/api_permission.h +++ b/extensions/common/permissions/api_permission.h @@ -91,7 +91,6 @@ class APIPermission { kGeolocation, kHistory, kIdentity, - kIdentityEmail, kIdentityPrivate, kIdltest, kIdle, @@ -154,6 +153,7 @@ class APIPermission { kWebRequestBlocking, kWebRequestInternal, kWebRtc, + kWebrtcAudioPrivate, kWebrtcLoggingPrivate, kWebstorePrivate, kWebView, diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h index 3b745b2fa9..19c5799e71 100644 --- a/extensions/common/permissions/permission_message.h +++ b/extensions/common/permissions/permission_message.h @@ -74,7 +74,6 @@ class PermissionMessage { kSignedInDevices, kWallpaper, kNetworkState, - kIdentityEmail, kEnumBoundary, }; COMPILE_ASSERT(PermissionMessage::kNone > PermissionMessage::kUnknown, diff --git a/extensions/common/permissions/permission_set.cc b/extensions/common/permissions/permission_set.cc new file mode 100644 index 0000000000..d3f46fc194 --- /dev/null +++ b/extensions/common/permissions/permission_set.cc @@ -0,0 +1,251 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/permissions/permission_set.h" + +#include <algorithm> +#include <iterator> +#include <string> + +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/url_pattern.h" +#include "extensions/common/url_pattern_set.h" +#include "url/gurl.h" + +using extensions::URLPatternSet; + +namespace { + +void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) { + DCHECK(out); + for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) { + URLPattern p = *i; + p.SetPath("/*"); + out->AddPattern(p); + } +} + +} // namespace + +namespace extensions { + +// +// PermissionSet +// + +PermissionSet::PermissionSet() {} + +PermissionSet::PermissionSet( + const APIPermissionSet& apis, + const URLPatternSet& explicit_hosts, + const URLPatternSet& scriptable_hosts) + : apis_(apis), + scriptable_hosts_(scriptable_hosts) { + AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); + InitImplicitPermissions(); + InitEffectiveHosts(); +} + +// static +PermissionSet* PermissionSet::CreateDifference( + const PermissionSet* set1, + const PermissionSet* set2) { + scoped_refptr<PermissionSet> empty = new PermissionSet(); + const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; + const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; + + APIPermissionSet apis; + APIPermissionSet::Difference(set1_safe->apis(), set2_safe->apis(), &apis); + + URLPatternSet explicit_hosts; + URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + + URLPatternSet scriptable_hosts; + URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + + return new PermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +// static +PermissionSet* PermissionSet::CreateIntersection( + const PermissionSet* set1, + const PermissionSet* set2) { + scoped_refptr<PermissionSet> empty = new PermissionSet(); + const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; + const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; + + APIPermissionSet apis; + APIPermissionSet::Intersection(set1_safe->apis(), set2_safe->apis(), &apis); + + URLPatternSet explicit_hosts; + URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + + URLPatternSet scriptable_hosts; + URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + + return new PermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +// static +PermissionSet* PermissionSet::CreateUnion( + const PermissionSet* set1, + const PermissionSet* set2) { + scoped_refptr<PermissionSet> empty = new PermissionSet(); + const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; + const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; + + APIPermissionSet apis; + APIPermissionSet::Union(set1_safe->apis(), set2_safe->apis(), &apis); + + URLPatternSet explicit_hosts; + URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + + URLPatternSet scriptable_hosts; + URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + + return new PermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +bool PermissionSet::operator==( + const PermissionSet& rhs) const { + return apis_ == rhs.apis_ && + scriptable_hosts_ == rhs.scriptable_hosts_ && + explicit_hosts_ == rhs.explicit_hosts_; +} + +bool PermissionSet::Contains(const PermissionSet& set) const { + return apis_.Contains(set.apis()) && + explicit_hosts().Contains(set.explicit_hosts()) && + scriptable_hosts().Contains(set.scriptable_hosts()); +} + +std::set<std::string> PermissionSet::GetAPIsAsStrings() const { + std::set<std::string> apis_str; + for (APIPermissionSet::const_iterator i = apis_.begin(); + i != apis_.end(); ++i) { + apis_str.insert(i->name()); + } + return apis_str; +} + +bool PermissionSet::IsEmpty() const { + // Not default if any host permissions are present. + if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty())) + return false; + + // Or if it has no api permissions. + return apis().empty(); +} + +bool PermissionSet::HasAPIPermission( + APIPermission::ID id) const { + return apis().find(id) != apis().end(); +} + +bool PermissionSet::HasAPIPermission(const std::string& permission_name) const { + const APIPermissionInfo* permission = + PermissionsInfo::GetInstance()->GetByName(permission_name); + CHECK(permission) << permission_name; + return (permission && apis_.count(permission->id())); +} + +bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const { + return CheckAPIPermissionWithParam(permission, NULL); +} + +bool PermissionSet::CheckAPIPermissionWithParam( + APIPermission::ID permission, + const APIPermission::CheckParam* param) const { + APIPermissionSet::const_iterator iter = apis().find(permission); + if (iter == apis().end()) + return false; + return iter->Check(param); +} + +bool PermissionSet::HasExplicitAccessToOrigin( + const GURL& origin) const { + return explicit_hosts().MatchesURL(origin); +} + +bool PermissionSet::HasScriptableAccessToURL( + const GURL& origin) const { + // We only need to check our host list to verify access. The host list should + // already reflect any special rules (such as chrome://favicon, all hosts + // access, etc.). + return scriptable_hosts().MatchesURL(origin); +} + +bool PermissionSet::HasEffectiveAccessToAllHosts() const { + // There are two ways this set can have effective access to all hosts: + // 1) it has an <all_urls> URL pattern. + // 2) it has a named permission with implied full URL access. + for (URLPatternSet::const_iterator host = effective_hosts().begin(); + host != effective_hosts().end(); ++host) { + if (host->match_all_urls() || + (host->match_subdomains() && host->host().empty())) + return true; + } + + for (APIPermissionSet::const_iterator i = apis().begin(); + i != apis().end(); ++i) { + if (i->info()->implies_full_url_access()) + return true; + } + return false; +} + +bool PermissionSet::HasEffectiveAccessToURL(const GURL& url) const { + return effective_hosts().MatchesURL(url); +} + +bool PermissionSet::HasEffectiveFullAccess() const { + for (APIPermissionSet::const_iterator i = apis().begin(); + i != apis().end(); ++i) { + if (i->info()->implies_full_access()) + return true; + } + return false; +} + +PermissionSet::~PermissionSet() {} + +void PermissionSet::InitImplicitPermissions() { + // The downloads permission implies the internal version as well. + if (apis_.find(APIPermission::kDownloads) != apis_.end()) + apis_.insert(APIPermission::kDownloadsInternal); + + // TODO(fsamuel): Is there a better way to request access to the WebRequest + // API without exposing it to the Chrome App? + if (apis_.find(APIPermission::kWebView) != apis_.end()) + apis_.insert(APIPermission::kWebRequestInternal); + + // The webRequest permission implies the internal version as well. + if (apis_.find(APIPermission::kWebRequest) != apis_.end()) + apis_.insert(APIPermission::kWebRequestInternal); + + // The fileBrowserHandler permission implies the internal version as well. + if (apis_.find(APIPermission::kFileBrowserHandler) != apis_.end()) + apis_.insert(APIPermission::kFileBrowserHandlerInternal); +} + +void PermissionSet::InitEffectiveHosts() { + effective_hosts_.ClearPatterns(); + + URLPatternSet::CreateUnion( + explicit_hosts(), scriptable_hosts(), &effective_hosts_); +} + +} // namespace extensions diff --git a/extensions/common/permissions/permission_set.h b/extensions/common/permissions/permission_set.h new file mode 100644 index 0000000000..14ffe12cda --- /dev/null +++ b/extensions/common/permissions/permission_set.h @@ -0,0 +1,143 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_PERMISSION_SET_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_PERMISSION_SET_H_ + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "base/strings/string16.h" +#include "extensions/common/manifest.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/url_pattern_set.h" + +namespace extensions { +class Extension; + +// The PermissionSet is an immutable class that encapsulates an +// extension's permissions. The class exposes set operations for combining and +// manipulating the permissions. +class PermissionSet + : public base::RefCountedThreadSafe<PermissionSet> { + public: + // Creates an empty permission set (e.g. default permissions). + PermissionSet(); + + // Creates a new permission set based on the specified data: the API + // permissions, host permissions, and scriptable hosts. The effective hosts + // of the newly created permission set will be inferred from the given + // host permissions. + PermissionSet(const APIPermissionSet& apis, + const URLPatternSet& explicit_hosts, + const URLPatternSet& scriptable_hosts); + + // Creates a new permission set equal to |set1| - |set2|, passing ownership of + // the new set to the caller. + static PermissionSet* CreateDifference( + const PermissionSet* set1, const PermissionSet* set2); + + // Creates a new permission set equal to the intersection of |set1| and + // |set2|, passing ownership of the new set to the caller. + static PermissionSet* CreateIntersection( + const PermissionSet* set1, const PermissionSet* set2); + + // Creates a new permission set equal to the union of |set1| and |set2|. + // Passes ownership of the new set to the caller. + static PermissionSet* CreateUnion( + const PermissionSet* set1, const PermissionSet* set2); + + bool operator==(const PermissionSet& rhs) const; + + // Returns true if every API or host permission available to |set| is also + // available to this. In other words, if the API permissions of |set| are a + // subset of this, and the host permissions in this encompass those in |set|. + bool Contains(const PermissionSet& set) const; + + // Gets the API permissions in this set as a set of strings. + std::set<std::string> GetAPIsAsStrings() const; + + // Returns true if this is an empty set (e.g., the default permission set). + bool IsEmpty() const; + + // Returns true if the set has the specified API permission. + bool HasAPIPermission(APIPermission::ID permission) const; + + // Returns true if the |extension| explicitly requests access to the given + // |permission_name|. Note this does not include APIs without no corresponding + // permission, like "runtime" or "browserAction". + bool HasAPIPermission(const std::string& permission_name) const; + + // Returns true if the set allows the given permission with the default + // permission detal. + bool CheckAPIPermission(APIPermission::ID permission) const; + + // Returns true if the set allows the given permission and permission param. + bool CheckAPIPermissionWithParam(APIPermission::ID permission, + const APIPermission::CheckParam* param) const; + + // Returns true if this includes permission to access |origin|. + bool HasExplicitAccessToOrigin(const GURL& origin) const; + + // Returns true if this permission set includes access to script |url|. + bool HasScriptableAccessToURL(const GURL& url) const; + + // Returns true if this permission set includes effective access to all + // origins. + bool HasEffectiveAccessToAllHosts() const; + + // Returns true if this permission set includes effective access to |url|. + bool HasEffectiveAccessToURL(const GURL& url) const; + + // Returns ture if this permission set effectively represents full access + // (e.g. native code). + bool HasEffectiveFullAccess() const; + + const APIPermissionSet& apis() const { return apis_; } + + const URLPatternSet& effective_hosts() const { return effective_hosts_; } + + const URLPatternSet& explicit_hosts() const { return explicit_hosts_; } + + const URLPatternSet& scriptable_hosts() const { return scriptable_hosts_; } + + private: + FRIEND_TEST_ALL_PREFIXES(PermissionsTest, GetWarningMessages_AudioVideo); + friend class base::RefCountedThreadSafe<PermissionSet>; + + ~PermissionSet(); + + void AddAPIPermission(APIPermission::ID id); + + // Adds permissions implied independently of other context. + void InitImplicitPermissions(); + + // Initializes the effective host permission based on the data in this set. + void InitEffectiveHosts(); + + // The api list is used when deciding if an extension can access certain + // extension APIs and features. + APIPermissionSet apis_; + + // The list of hosts that can be accessed directly from the extension. + // TODO(jstritar): Rename to "hosts_"? + URLPatternSet explicit_hosts_; + + // The list of hosts that can be scripted by content scripts. + // TODO(jstritar): Rename to "user_script_hosts_"? + URLPatternSet scriptable_hosts_; + + // The list of hosts this effectively grants access to. + URLPatternSet effective_hosts_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_PERMISSION_SET_H_ diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 0db3b4f454..e54d9651ae 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -3,6 +3,9 @@ # found in the LICENSE file. { + 'variables': { + 'chromium_code': 1, + }, 'targets': [ { 'target_name': 'extensions_common', @@ -72,6 +75,8 @@ 'common/permissions/permission_message.h', 'common/permissions/permission_message_provider.cc', 'common/permissions/permission_message_provider.h', + 'common/permissions/permission_set.cc', + 'common/permissions/permission_set.h', 'common/permissions/permissions_info.cc', 'common/permissions/permissions_info.h', 'common/permissions/permissions_provider.h', @@ -107,10 +112,14 @@ 'browser/extension_prefs_scope.h', 'browser/extension_error.cc', 'browser/extension_error.h', + 'browser/extensions_browser_client.cc', + 'browser/extensions_browser_client.h', 'browser/file_highlighter.cc', 'browser/file_highlighter.h', 'browser/file_reader.cc', 'browser/file_reader.h', + 'browser/lazy_background_task_queue.cc', + 'browser/lazy_background_task_queue.h', 'browser/pref_names.cc', 'browser/pref_names.h', 'browser/view_type_utils.cc', |