summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-01 17:29:13 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-01 17:29:13 +0000
commite6893672ec403d9dbf7fc5aab64ba6109ffd625a (patch)
tree25d46598e07af97d4f284663a3e3d53412e4eb2b /extensions
parent3dfb202d7b2cbdb92b1fe9d6274211d823efebe8 (diff)
downloadchromium_src-e6893672ec403d9dbf7fc5aab64ba6109ffd625a.zip
chromium_src-e6893672ec403d9dbf7fc5aab64ba6109ffd625a.tar.gz
chromium_src-e6893672ec403d9dbf7fc5aab64ba6109ffd625a.tar.bz2
Pull chrome stuff out of (and move) extensions::Dispatcher.
This moves Dispatcher into //extensions along with several of its remaining dependencies. This also introduces a DispatcherDelegate interface along with implementations for chrome and app_shell. BUG=359836 TBR=sky for CCRC and ChromeRenderViewTest Review URL: https://codereview.chromium.org/260083006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@267564 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r--extensions/DEPS9
-rw-r--r--extensions/common/api/extensions_manifest_types.json26
-rw-r--r--extensions/common/manifest_handlers/externally_connectable.cc217
-rw-r--r--extensions/common/manifest_handlers/externally_connectable.h97
-rw-r--r--extensions/common/manifest_handlers/externally_connectable_unittest.cc315
-rw-r--r--extensions/extensions.gyp35
-rw-r--r--extensions/renderer/api_activity_logger.cc80
-rw-r--r--extensions/renderer/api_activity_logger.h51
-rw-r--r--extensions/renderer/api_definitions_natives.cc2
-rw-r--r--extensions/renderer/app_runtime_custom_bindings.cc66
-rw-r--r--extensions/renderer/app_runtime_custom_bindings.h23
-rw-r--r--extensions/renderer/console.cc2
-rw-r--r--extensions/renderer/default_dispatcher_delegate.cc27
-rw-r--r--extensions/renderer/default_dispatcher_delegate.h27
-rw-r--r--extensions/renderer/dispatcher.cc1216
-rw-r--r--extensions/renderer/dispatcher.h293
-rw-r--r--extensions/renderer/dispatcher_delegate.h96
-rw-r--r--extensions/renderer/event_bindings.cc2
-rw-r--r--extensions/renderer/extensions_renderer_client.h10
-rw-r--r--extensions/renderer/lazy_background_page_native_handler.cc62
-rw-r--r--extensions/renderer/lazy_background_page_native_handler.h31
-rw-r--r--extensions/renderer/messaging_bindings.cc441
-rw-r--r--extensions/renderer/messaging_bindings.h71
-rw-r--r--extensions/renderer/print_native_handler.cc32
-rw-r--r--extensions/renderer/print_native_handler.h21
-rw-r--r--extensions/renderer/process_info_native_handler.cc86
-rw-r--r--extensions/renderer/process_info_native_handler.h40
-rw-r--r--extensions/renderer/request_sender.cc2
-rw-r--r--extensions/renderer/resource_bundle_source_map.cc47
-rw-r--r--extensions/renderer/resource_bundle_source_map.h45
-rw-r--r--extensions/renderer/runtime_custom_bindings.cc177
-rw-r--r--extensions/renderer/runtime_custom_bindings.h34
-rw-r--r--extensions/renderer/script_context.cc2
-rw-r--r--extensions/renderer/script_context.h2
-rw-r--r--extensions/renderer/static_v8_external_ascii_string_resource.cc25
-rw-r--r--extensions/renderer/static_v8_external_ascii_string_resource.h32
-rw-r--r--extensions/renderer/test_extensions_renderer_client.cc9
-rw-r--r--extensions/renderer/test_extensions_renderer_client.h3
-rw-r--r--extensions/renderer/test_features_native_handler.cc36
-rw-r--r--extensions/renderer/test_features_native_handler.h22
-rw-r--r--extensions/renderer/user_gestures_native_handler.cc52
-rw-r--r--extensions/renderer/user_gestures_native_handler.h24
-rw-r--r--extensions/renderer/user_script_slave.cc314
-rw-r--r--extensions/renderer/user_script_slave.h84
-rw-r--r--extensions/renderer/v8_context_native_handler.cc55
-rw-r--r--extensions/renderer/v8_context_native_handler.h28
46 files changed, 4340 insertions, 31 deletions
diff --git a/extensions/DEPS b/extensions/DEPS
index 5d3a4d5..4dabc14 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+components/url_matcher",
"+content/public/common",
"+crypto",
+ "+grit/extensions_resources.h",
"+testing",
"+ui",
@@ -12,10 +13,12 @@ include_rules = [
#
# TODO(jamescook): Remove these. http://crbug.com/162530
"!chrome/browser/chrome_notification_types.h",
- "!chrome/renderer/extensions/dispatcher.h",
"!chrome/renderer/extensions/extension_helper.h",
"!grit/common_resources.h",
- "!grit/extensions_api_resources.h",
+ '!grit/extensions_api_resources.h',
+ # This is needed for renderer JS sources which should eventually move to
+ # the extensions_resources target.
+ "!grit/renderer_resources.h",
]
specific_include_rules = {
@@ -28,12 +31,12 @@ specific_include_rules = {
"+chrome/browser/extensions/extension_service_unittest.h",
"+chrome/browser/extensions/test_extension_system.h",
"+chrome/common/chrome_paths.h",
+ "+chrome/common/extensions/features/feature_channel.h",
"+chrome/common/extensions/manifest_tests/extension_manifest_test.h",
"+chrome/test/base/testing_profile.h",
],
"(simple|complex)_feature_unittest\.cc|base_feature_provider_unittest\.cc": [
"+chrome/common/extensions/features/chrome_channel_feature_filter.h",
- "+chrome/common/extensions/features/feature_channel.h",
],
"permissions_data_unittest\.cc": [
"+chrome/common/chrome_version_info.h",
diff --git a/extensions/common/api/extensions_manifest_types.json b/extensions/common/api/extensions_manifest_types.json
index 24c2df5..00262692 100644
--- a/extensions/common/api/extensions_manifest_types.json
+++ b/extensions/common/api/extensions_manifest_types.json
@@ -13,6 +13,32 @@
},
"types": [
{
+ "id": "ExternallyConnectable",
+ "type": "object",
+ // Note: description commented out because externally_connectable.html
+ // already describes it, and the repetition looks odd.
+ // "description": "The <code>externally_connectable</code> manifest property declares which extensions, apps, and web pages can connect to your extension via $(ref:runtime.connect) and $(ref:runtime.sendMessage).",
+ "properties": {
+ "ids": {
+ "description": "<p>The IDs of extensions or apps that are allowed to connect. If left empty or unspecified, no extensions or apps can connect.</p><p>The wildcard <code>\"*\"</code> will allow all extensions and apps to connect.</p>",
+ "optional": true,
+ "type": "array",
+ "items": {"type": "string"}
+ },
+ "matches": {
+ "description": "<p>The URL patterns for <em>web pages</em> that are allowed to connect. <em>This does not affect content scripts.</em> If left empty or unspecified, no web pages can connect.</p><p>Patterns cannot include wildcard domains nor subdomains of <a href=\"http://publicsuffix.org/list/\">(effective) top level domains</a>; <code>*://google.com/*</code> and <code>http://*.chromium.org/*</code> are valid, while <code>&lt;all_urls&gt;</code>, <code>http://*/*</code>, <code>*://*.com/*</code>, and even <code>http://*.appspot.com/*</code> are not.</p>",
+ "optional": true,
+ "type": "array",
+ "items": {"type": "string"}
+ },
+ "accepts_tls_channel_id": {
+ "description": "If <code>true</code>, messages sent via $(ref:runtime.connect) or $(ref:runtime.sendMessage) will set $(ref:runtime.MessageSender.tlsChannelId) if those methods request it to be. If <code>false</code>, $(ref:runtime.MessageSender.tlsChannelId) will never be set under any circumstance.",
+ "optional": true,
+ "type": "boolean"
+ }
+ }
+ },
+ {
"id": "SocketHostPatterns",
"description": "<p>A single string or a list of strings representing host:port patterns.</p>",
"choices": [
diff --git a/extensions/common/manifest_handlers/externally_connectable.cc b/extensions/common/manifest_handlers/externally_connectable.cc
new file mode 100644
index 0000000..1261ad8
--- /dev/null
+++ b/extensions/common/manifest_handlers/externally_connectable.cc
@@ -0,0 +1,217 @@
+// Copyright 2014 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/manifest_handlers/externally_connectable.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "extensions/common/api/extensions_manifest_types.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/api_permission_set.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/url_pattern.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/gurl.h"
+
+namespace rcd = net::registry_controlled_domains;
+
+namespace extensions {
+
+namespace externally_connectable_errors {
+const char kErrorInvalidMatchPattern[] = "Invalid match pattern '*'";
+const char kErrorInvalidId[] = "Invalid ID '*'";
+const char kErrorNothingSpecified[] =
+ "'externally_connectable' specifies neither 'matches' nor 'ids'; "
+ "nothing will be able to connect";
+const char kErrorTopLevelDomainsNotAllowed[] =
+ "\"*\" is an effective top level domain for which wildcard subdomains such "
+ "as \"*\" are not allowed";
+const char kErrorWildcardHostsNotAllowed[] =
+ "Wildcard domain patterns such as \"*\" are not allowed";
+} // namespace externally_connectable_errors
+
+namespace keys = extensions::manifest_keys;
+namespace errors = externally_connectable_errors;
+using core_api::extensions_manifest_types::ExternallyConnectable;
+
+namespace {
+
+const char kAllIds[] = "*";
+
+template <typename T>
+std::vector<T> Sorted(const std::vector<T>& in) {
+ std::vector<T> out = in;
+ std::sort(out.begin(), out.end());
+ return out;
+}
+
+} // namespace
+
+ExternallyConnectableHandler::ExternallyConnectableHandler() {
+}
+
+ExternallyConnectableHandler::~ExternallyConnectableHandler() {
+}
+
+bool ExternallyConnectableHandler::Parse(Extension* extension,
+ base::string16* error) {
+ const base::Value* externally_connectable = NULL;
+ CHECK(extension->manifest()->Get(keys::kExternallyConnectable,
+ &externally_connectable));
+ std::vector<InstallWarning> install_warnings;
+ scoped_ptr<ExternallyConnectableInfo> info =
+ ExternallyConnectableInfo::FromValue(
+ *externally_connectable, &install_warnings, error);
+ if (!info)
+ return false;
+ if (!info->matches.is_empty()) {
+ PermissionsData::GetInitialAPIPermissions(extension)
+ ->insert(APIPermission::kWebConnectable);
+ }
+ extension->AddInstallWarnings(install_warnings);
+ extension->SetManifestData(keys::kExternallyConnectable, info.release());
+ return true;
+}
+
+const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
+ return SingleKey(keys::kExternallyConnectable);
+}
+
+// static
+ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
+ const Extension* extension) {
+ return static_cast<ExternallyConnectableInfo*>(
+ extension->GetManifestData(keys::kExternallyConnectable));
+}
+
+// static
+scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
+ const base::Value& value,
+ std::vector<InstallWarning>* install_warnings,
+ base::string16* error) {
+ scoped_ptr<ExternallyConnectable> externally_connectable =
+ ExternallyConnectable::FromValue(value, error);
+ if (!externally_connectable)
+ return scoped_ptr<ExternallyConnectableInfo>();
+
+ URLPatternSet matches;
+
+ if (externally_connectable->matches) {
+ for (std::vector<std::string>::iterator it =
+ externally_connectable->matches->begin();
+ it != externally_connectable->matches->end();
+ ++it) {
+ // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
+ // extension communication path, not the other way.
+ URLPattern pattern(URLPattern::SCHEME_ALL);
+ if (pattern.Parse(*it) != URLPattern::PARSE_SUCCESS) {
+ *error = ErrorUtils::FormatErrorMessageUTF16(
+ errors::kErrorInvalidMatchPattern, *it);
+ return scoped_ptr<ExternallyConnectableInfo>();
+ }
+
+ // Wildcard hosts are not allowed.
+ if (pattern.host().empty()) {
+ // Warning not error for forwards compatibility.
+ install_warnings->push_back(
+ InstallWarning(ErrorUtils::FormatErrorMessage(
+ errors::kErrorWildcardHostsNotAllowed, *it),
+ keys::kExternallyConnectable,
+ *it));
+ continue;
+ }
+
+ // Wildcards on subdomains of a TLD are not allowed.
+ size_t registry_length = rcd::GetRegistryLength(
+ pattern.host(),
+ // This means that things that look like TLDs - the foobar in
+ // http://google.foobar - count as TLDs.
+ rcd::INCLUDE_UNKNOWN_REGISTRIES,
+ // This means that effective TLDs like appspot.com count as TLDs;
+ // codereview.appspot.com and evil.appspot.com are different.
+ rcd::INCLUDE_PRIVATE_REGISTRIES);
+
+ if (registry_length == std::string::npos) {
+ // The URL parsing combined with host().empty() should have caught this.
+ NOTREACHED() << *it;
+ *error = ErrorUtils::FormatErrorMessageUTF16(
+ errors::kErrorInvalidMatchPattern, *it);
+ return scoped_ptr<ExternallyConnectableInfo>();
+ }
+
+ // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
+ // are not allowed. However just "appspot.com" is ok.
+ if (registry_length == 0 && pattern.match_subdomains()) {
+ // Warning not error for forwards compatibility.
+ install_warnings->push_back(
+ InstallWarning(ErrorUtils::FormatErrorMessage(
+ errors::kErrorTopLevelDomainsNotAllowed,
+ pattern.host().c_str(),
+ *it),
+ keys::kExternallyConnectable,
+ *it));
+ continue;
+ }
+
+ matches.AddPattern(pattern);
+ }
+ }
+
+ std::vector<std::string> ids;
+ bool all_ids = false;
+
+ if (externally_connectable->ids) {
+ for (std::vector<std::string>::iterator it =
+ externally_connectable->ids->begin();
+ it != externally_connectable->ids->end();
+ ++it) {
+ if (*it == kAllIds) {
+ all_ids = true;
+ } else if (Extension::IdIsValid(*it)) {
+ ids.push_back(*it);
+ } else {
+ *error =
+ ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId, *it);
+ return scoped_ptr<ExternallyConnectableInfo>();
+ }
+ }
+ }
+
+ if (!externally_connectable->matches && !externally_connectable->ids) {
+ install_warnings->push_back(InstallWarning(errors::kErrorNothingSpecified,
+ keys::kExternallyConnectable));
+ }
+
+ bool accepts_tls_channel_id =
+ externally_connectable->accepts_tls_channel_id.get() &&
+ *externally_connectable->accepts_tls_channel_id;
+ return make_scoped_ptr(new ExternallyConnectableInfo(
+ matches, ids, all_ids, accepts_tls_channel_id));
+}
+
+ExternallyConnectableInfo::~ExternallyConnectableInfo() {
+}
+
+ExternallyConnectableInfo::ExternallyConnectableInfo(
+ const URLPatternSet& matches,
+ const std::vector<std::string>& ids,
+ bool all_ids,
+ bool accepts_tls_channel_id)
+ : matches(matches),
+ ids(Sorted(ids)),
+ all_ids(all_ids),
+ accepts_tls_channel_id(accepts_tls_channel_id) {
+}
+
+bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
+ if (all_ids)
+ return true;
+ DCHECK(base::STLIsSorted(ids));
+ return std::binary_search(ids.begin(), ids.end(), id);
+}
+
+} // namespace extensions
diff --git a/extensions/common/manifest_handlers/externally_connectable.h b/extensions/common/manifest_handlers/externally_connectable.h
new file mode 100644
index 0000000..960904e
--- /dev/null
+++ b/extensions/common/manifest_handlers/externally_connectable.h
@@ -0,0 +1,97 @@
+// Copyright 2014 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_MANIFEST_HANDLERS_EXTERNALLY_CONNECTABLE_H_
+#define EXTENSIONS_COMMON_MANIFEST_HANDLERS_EXTERNALLY_CONNECTABLE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/install_warning.h"
+#include "extensions/common/manifest_handler.h"
+#include "extensions/common/url_pattern_set.h"
+
+class GURL;
+
+namespace base {
+class Value;
+}
+
+namespace extensions {
+
+// Error constants used when parsing the externally_connectable manifest entry.
+namespace externally_connectable_errors {
+extern const char kErrorInvalid[];
+extern const char kErrorInvalidMatchPattern[];
+extern const char kErrorInvalidId[];
+extern const char kErrorNothingSpecified[];
+extern const char kErrorTopLevelDomainsNotAllowed[];
+extern const char kErrorWildcardHostsNotAllowed[];
+} // namespace externally_connectable_errors
+
+// Parses the externally_connectable manifest entry.
+class ExternallyConnectableHandler : public ManifestHandler {
+ public:
+ ExternallyConnectableHandler();
+ virtual ~ExternallyConnectableHandler();
+
+ virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE;
+
+ private:
+ virtual const std::vector<std::string> Keys() const OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternallyConnectableHandler);
+};
+
+// The parsed form of the externally_connectable manifest entry.
+struct ExternallyConnectableInfo : public Extension::ManifestData {
+ public:
+ // Gets the ExternallyConnectableInfo for |extension|, or NULL if none was
+ // specified.
+ static ExternallyConnectableInfo* Get(const Extension* extension);
+
+ // Tries to construct the info based on |value|, as it would have appeared in
+ // the manifest. Sets |error| and returns an empty scoped_ptr on failure.
+ static scoped_ptr<ExternallyConnectableInfo> FromValue(
+ const base::Value& value,
+ std::vector<InstallWarning>* install_warnings,
+ base::string16* error);
+
+ virtual ~ExternallyConnectableInfo();
+
+ // The URL patterns that are allowed to connect/sendMessage.
+ const URLPatternSet matches;
+
+ // The extension IDs that are allowed to connect/sendMessage. Sorted.
+ const std::vector<std::string> ids;
+
+ // True if any extension is allowed to connect. This would have corresponded
+ // to an ID of "*" in |ids|.
+ const bool all_ids;
+
+ // True if extension accepts the TLS channel ID, when requested by the
+ // connecting origin.
+ const bool accepts_tls_channel_id;
+
+ // Returns true if |ids| contains |id| or if |all_ids| is true.
+ //
+ // More convenient for callers than checking each individually, and it makes
+ // use of the sortedness of |ids|.
+ bool IdCanConnect(const std::string& id);
+
+ // Public only for testing. Use FromValue in production.
+ ExternallyConnectableInfo(const URLPatternSet& matches,
+ const std::vector<std::string>& ids,
+ bool all_ids,
+ bool accepts_tls_channel_id);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExternallyConnectableInfo);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_MANIFEST_HANDLERS_EXTERNALLY_CONNECTABLE_H_
diff --git a/extensions/common/manifest_handlers/externally_connectable_unittest.cc b/extensions/common/manifest_handlers/externally_connectable_unittest.cc
new file mode 100644
index 0000000..41b2d32
--- /dev/null
+++ b/extensions/common/manifest_handlers/externally_connectable_unittest.cc
@@ -0,0 +1,315 @@
+// Copyright 2014 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 <algorithm>
+
+#include "chrome/common/extensions/features/feature_channel.h"
+#include "chrome/common/extensions/manifest_tests/extension_manifest_test.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/externally_connectable.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace extensions {
+
+namespace errors = externally_connectable_errors;
+
+class ExternallyConnectableTest : public ExtensionManifestTest {
+ public:
+ ExternallyConnectableTest() : channel_(chrome::VersionInfo::CHANNEL_DEV) {}
+
+ protected:
+ ExternallyConnectableInfo* GetExternallyConnectableInfo(
+ scoped_refptr<Extension> extension) {
+ return static_cast<ExternallyConnectableInfo*>(
+ extension->GetManifestData(manifest_keys::kExternallyConnectable));
+ }
+
+ private:
+ ScopedCurrentChannel channel_;
+};
+
+TEST_F(ExternallyConnectableTest, IDsAndMatches) {
+ scoped_refptr<Extension> extension =
+ LoadAndExpectSuccess("externally_connectable_ids_and_matches.json");
+ ASSERT_TRUE(extension.get());
+
+ EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kWebConnectable));
+
+ ExternallyConnectableInfo* info =
+ ExternallyConnectableInfo::Get(extension.get());
+ ASSERT_TRUE(info);
+
+ EXPECT_THAT(info->ids,
+ ElementsAre("abcdefghijklmnopabcdefghijklmnop",
+ "ponmlkjihgfedcbaponmlkjihgfedcba"));
+
+ EXPECT_FALSE(info->all_ids);
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com/")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com/")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com/")));
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org/")));
+ EXPECT_TRUE(
+ info->matches.MatchesURL(GURL("http://build.chromium.org/index.html")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org/")));
+ EXPECT_FALSE(
+ info->matches.MatchesURL(GURL("http://foo.chromium.org/index.html")));
+
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com/")));
+
+ // TLD-style patterns should match just the TLD.
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://appspot.com/foo.html")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://go/here")));
+
+ // TLD-style patterns should *not* match any subdomains of the TLD.
+ EXPECT_FALSE(
+ info->matches.MatchesURL(GURL("http://codereview.appspot.com/foo.html")));
+ EXPECT_FALSE(
+ info->matches.MatchesURL(GURL("http://chromium.com/index.html")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://here.go/somewhere")));
+
+ // Paths that don't have any wildcards should match the exact domain, but
+ // ignore the trailing slash. This is kind of a corner case, so let's test it.
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://no.wildcard.path")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://no.wildcard.path/")));
+ EXPECT_FALSE(
+ info->matches.MatchesURL(GURL("http://no.wildcard.path/index.html")));
+}
+
+TEST_F(ExternallyConnectableTest, IDs) {
+ scoped_refptr<Extension> extension =
+ LoadAndExpectSuccess("externally_connectable_ids.json");
+ ASSERT_TRUE(extension.get());
+
+ EXPECT_FALSE(extension->HasAPIPermission(APIPermission::kWebConnectable));
+
+ ExternallyConnectableInfo* info =
+ ExternallyConnectableInfo::Get(extension.get());
+ ASSERT_TRUE(info);
+
+ EXPECT_THAT(info->ids,
+ ElementsAre("abcdefghijklmnopabcdefghijklmnop",
+ "ponmlkjihgfedcbaponmlkjihgfedcba"));
+
+ EXPECT_FALSE(info->all_ids);
+
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
+}
+
+TEST_F(ExternallyConnectableTest, Matches) {
+ scoped_refptr<Extension> extension =
+ LoadAndExpectSuccess("externally_connectable_matches.json");
+ ASSERT_TRUE(extension.get());
+
+ EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kWebConnectable));
+
+ ExternallyConnectableInfo* info =
+ ExternallyConnectableInfo::Get(extension.get());
+ ASSERT_TRUE(info);
+
+ EXPECT_THAT(info->ids, ElementsAre());
+
+ EXPECT_FALSE(info->all_ids);
+
+ EXPECT_FALSE(info->accepts_tls_channel_id);
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com/")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com/")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com/")));
+
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org/")));
+ EXPECT_TRUE(
+ info->matches.MatchesURL(GURL("http://build.chromium.org/index.html")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org/")));
+ EXPECT_FALSE(
+ info->matches.MatchesURL(GURL("http://foo.chromium.org/index.html")));
+
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com/")));
+}
+
+TEST_F(ExternallyConnectableTest, MatchesWithTlsChannelId) {
+ scoped_refptr<Extension> extension = LoadAndExpectSuccess(
+ "externally_connectable_matches_tls_channel_id.json");
+ ASSERT_TRUE(extension.get());
+
+ EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kWebConnectable));
+
+ ExternallyConnectableInfo* info =
+ ExternallyConnectableInfo::Get(extension.get());
+ ASSERT_TRUE(info);
+
+ EXPECT_THAT(info->ids, ElementsAre());
+
+ EXPECT_FALSE(info->all_ids);
+
+ EXPECT_TRUE(info->accepts_tls_channel_id);
+
+ // The matches portion of the manifest is identical to those in
+ // externally_connectable_matches, so only a subset of the Matches tests is
+ // repeated here.
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
+}
+
+TEST_F(ExternallyConnectableTest, AllIDs) {
+ scoped_refptr<Extension> extension =
+ LoadAndExpectSuccess("externally_connectable_all_ids.json");
+ ASSERT_TRUE(extension.get());
+
+ EXPECT_FALSE(extension->HasAPIPermission(APIPermission::kWebConnectable));
+
+ ExternallyConnectableInfo* info =
+ ExternallyConnectableInfo::Get(extension.get());
+ ASSERT_TRUE(info);
+
+ EXPECT_THAT(info->ids,
+ ElementsAre("abcdefghijklmnopabcdefghijklmnop",
+ "ponmlkjihgfedcbaponmlkjihgfedcba"));
+
+ EXPECT_TRUE(info->all_ids);
+
+ EXPECT_FALSE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
+}
+
+TEST_F(ExternallyConnectableTest, IdCanConnect) {
+ // Not in order to test that ExternallyConnectableInfo sorts it.
+ std::string matches_ids_array[] = {"g", "h", "c", "i", "a", "z", "b"};
+ std::vector<std::string> matches_ids(
+ matches_ids_array, matches_ids_array + arraysize(matches_ids_array));
+
+ std::string nomatches_ids_array[] = {"2", "3", "1"};
+
+ // all_ids = false.
+ {
+ ExternallyConnectableInfo info(URLPatternSet(), matches_ids, false, false);
+ for (size_t i = 0; i < matches_ids.size(); ++i)
+ EXPECT_TRUE(info.IdCanConnect(matches_ids[i]));
+ for (size_t i = 0; i < arraysize(nomatches_ids_array); ++i)
+ EXPECT_FALSE(info.IdCanConnect(nomatches_ids_array[i]));
+ }
+
+ // all_ids = true.
+ {
+ ExternallyConnectableInfo info(URLPatternSet(), matches_ids, true, false);
+ for (size_t i = 0; i < matches_ids.size(); ++i)
+ EXPECT_TRUE(info.IdCanConnect(matches_ids[i]));
+ for (size_t i = 0; i < arraysize(nomatches_ids_array); ++i)
+ EXPECT_TRUE(info.IdCanConnect(nomatches_ids_array[i]));
+ }
+}
+
+TEST_F(ExternallyConnectableTest, ErrorWrongFormat) {
+ LoadAndExpectError("externally_connectable_error_wrong_format.json",
+ "expected dictionary, got string");
+}
+
+TEST_F(ExternallyConnectableTest, ErrorBadID) {
+ LoadAndExpectError(
+ "externally_connectable_bad_id.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorInvalidId, "badid"));
+}
+
+TEST_F(ExternallyConnectableTest, ErrorBadMatches) {
+ LoadAndExpectError("externally_connectable_error_bad_matches.json",
+ ErrorUtils::FormatErrorMessage(
+ errors::kErrorInvalidMatchPattern, "www.yahoo.com"));
+}
+
+TEST_F(ExternallyConnectableTest, WarningNoAllURLs) {
+ scoped_refptr<Extension> extension = LoadAndExpectWarning(
+ "externally_connectable_error_all_urls.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorWildcardHostsNotAllowed,
+ "<all_urls>"));
+ ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
+ EXPECT_FALSE(info->matches.ContainsPattern(
+ URLPattern(URLPattern::SCHEME_ALL, "<all_urls>")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+}
+
+TEST_F(ExternallyConnectableTest, WarningWildcardHost) {
+ scoped_refptr<Extension> extension = LoadAndExpectWarning(
+ "externally_connectable_error_wildcard_host.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorWildcardHostsNotAllowed,
+ "http://*/*"));
+ ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
+ EXPECT_FALSE(info->matches.ContainsPattern(
+ URLPattern(URLPattern::SCHEME_ALL, "http://*/*")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+}
+
+TEST_F(ExternallyConnectableTest, WarningNoTLD) {
+ scoped_refptr<Extension> extension = LoadAndExpectWarning(
+ "externally_connectable_error_tld.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
+ "co.uk",
+ "http://*.co.uk/*"));
+ ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
+ EXPECT_FALSE(info->matches.ContainsPattern(
+ URLPattern(URLPattern::SCHEME_ALL, "http://*.co.uk/*")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+}
+
+TEST_F(ExternallyConnectableTest, WarningNoEffectiveTLD) {
+ scoped_refptr<Extension> extension = LoadAndExpectWarning(
+ "externally_connectable_error_effective_tld.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
+ "appspot.com",
+ "http://*.appspot.com/*"));
+ ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
+ EXPECT_FALSE(info->matches.ContainsPattern(
+ URLPattern(URLPattern::SCHEME_ALL, "http://*.appspot.com/*")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+}
+
+TEST_F(ExternallyConnectableTest, WarningUnknownTLD) {
+ scoped_refptr<Extension> extension = LoadAndExpectWarning(
+ "externally_connectable_error_unknown_tld.json",
+ ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
+ "notatld",
+ "http://*.notatld/*"));
+ ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
+ EXPECT_FALSE(info->matches.ContainsPattern(
+ URLPattern(URLPattern::SCHEME_ALL, "http://*.notatld/*")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
+ EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
+}
+
+TEST_F(ExternallyConnectableTest, WarningNothingSpecified) {
+ LoadAndExpectWarning("externally_connectable_nothing_specified.json",
+ errors::kErrorNothingSpecified);
+}
+
+} // namespace extensions
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 1664e87..faa7790 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -124,6 +124,8 @@
'common/manifest_handlers/background_info.h',
'common/manifest_handlers/csp_info.cc',
'common/manifest_handlers/csp_info.h',
+ 'common/manifest_handlers/externally_connectable.cc',
+ 'common/manifest_handlers/externally_connectable.h',
'common/manifest_handlers/icons_handler.cc',
'common/manifest_handlers/icons_handler.h',
'common/manifest_handlers/incognito_info.cc',
@@ -417,8 +419,12 @@
'sources': [
'renderer/activity_log_converter_strategy.cc',
'renderer/activity_log_converter_strategy.h',
+ 'renderer/api_activity_logger.cc',
+ 'renderer/api_activity_logger.h',
'renderer/api_definitions_natives.cc',
'renderer/api_definitions_natives.h',
+ 'renderer/app_runtime_custom_bindings.cc',
+ 'renderer/app_runtime_custom_bindings.h',
'renderer/binding_generating_native_handler.cc',
'renderer/binding_generating_native_handler.h',
'renderer/blob_native_handler.cc',
@@ -431,6 +437,11 @@
'renderer/context_menus_custom_bindings.h',
'renderer/css_native_handler.cc',
'renderer/css_native_handler.h',
+ 'renderer/default_dispatcher_delegate.cc',
+ 'renderer/default_dispatcher_delegate.h',
+ 'renderer/dispatcher.cc',
+ 'renderer/dispatcher.h',
+ 'renderer/dispatcher_delegate.h',
'renderer/document_custom_bindings.cc',
'renderer/document_custom_bindings.h',
'renderer/dom_activity_logger.cc',
@@ -446,18 +457,30 @@
'renderer/i18n_custom_bindings.h',
'renderer/id_generator_custom_bindings.cc',
'renderer/id_generator_custom_bindings.h',
+ 'renderer/lazy_background_page_native_handler.cc',
+ 'renderer/lazy_background_page_native_handler.h',
'renderer/logging_native_handler.cc',
'renderer/logging_native_handler.h',
+ 'renderer/messaging_bindings.cc',
+ 'renderer/messaging_bindings.h',
'renderer/module_system.cc',
'renderer/module_system.h',
'renderer/native_handler.cc',
'renderer/native_handler.h',
'renderer/object_backed_native_handler.cc',
'renderer/object_backed_native_handler.h',
+ 'renderer/print_native_handler.cc',
+ 'renderer/print_native_handler.h',
+ 'renderer/process_info_native_handler.cc',
+ 'renderer/process_info_native_handler.h',
'renderer/render_view_observer_natives.cc',
'renderer/render_view_observer_natives.h',
'renderer/request_sender.cc',
'renderer/request_sender.h',
+ 'renderer/resource_bundle_source_map.cc',
+ 'renderer/resource_bundle_source_map.h',
+ 'renderer/runtime_custom_bindings.cc',
+ 'renderer/runtime_custom_bindings.h',
'renderer/safe_builtins.cc',
'renderer/safe_builtins.h',
'renderer/send_request_natives.cc',
@@ -469,12 +492,24 @@
'renderer/script_context.h',
'renderer/script_context_set.cc',
'renderer/script_context_set.h',
+ 'renderer/static_v8_external_ascii_string_resource.cc',
+ 'renderer/static_v8_external_ascii_string_resource.h',
+ 'renderer/test_features_native_handler.cc',
+ 'renderer/test_features_native_handler.h',
+ 'renderer/user_gestures_native_handler.cc',
+ 'renderer/user_gestures_native_handler.h',
+ 'renderer/user_script_slave.cc',
+ 'renderer/user_script_slave.h',
'renderer/utils_native_handler.cc',
'renderer/utils_native_handler.h',
+ 'renderer/v8_context_native_handler.cc',
+ 'renderer/v8_context_native_handler.h',
'renderer/v8_schema_registry.cc',
'renderer/v8_schema_registry.h',
],
'dependencies': [
+ 'extensions_resources.gyp:extensions_resources',
+ '../chrome/chrome_resources.gyp:chrome_resources',
'../third_party/WebKit/public/blink.gyp:blink',
],
# Disable c4267 warnings until we fix size_t to int truncations.
diff --git a/extensions/renderer/api_activity_logger.cc b/extensions/renderer/api_activity_logger.cc
new file mode 100644
index 0000000..c1c5aef
--- /dev/null
+++ b/extensions/renderer/api_activity_logger.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 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/bind.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/renderer/activity_log_converter_strategy.h"
+#include "extensions/renderer/api_activity_logger.h"
+#include "extensions/renderer/script_context.h"
+
+using content::V8ValueConverter;
+
+namespace extensions {
+
+APIActivityLogger::APIActivityLogger(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction("LogEvent", base::Bind(&APIActivityLogger::LogEvent));
+ RouteFunction("LogAPICall", base::Bind(&APIActivityLogger::LogAPICall));
+}
+
+// static
+void APIActivityLogger::LogAPICall(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ LogInternal(APICALL, args);
+}
+
+// static
+void APIActivityLogger::LogEvent(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ LogInternal(EVENT, args);
+}
+
+// static
+void APIActivityLogger::LogInternal(
+ const CallType call_type,
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DCHECK_GT(args.Length(), 2);
+ DCHECK(args[0]->IsString());
+ DCHECK(args[1]->IsString());
+ DCHECK(args[2]->IsArray());
+
+ std::string ext_id = *v8::String::Utf8Value(args[0]);
+ ExtensionHostMsg_APIActionOrEvent_Params params;
+ params.api_call = *v8::String::Utf8Value(args[1]);
+ if (args.Length() == 4) // Extras are optional.
+ params.extra = *v8::String::Utf8Value(args[3]);
+ else
+ params.extra = "";
+
+ // Get the array of api call arguments.
+ v8::Local<v8::Array> arg_array = v8::Local<v8::Array>::Cast(args[2]);
+ if (arg_array->Length() > 0) {
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+ ActivityLogConverterStrategy strategy;
+ converter->SetFunctionAllowed(true);
+ converter->SetStrategy(&strategy);
+ scoped_ptr<base::ListValue> arg_list(new base::ListValue());
+ for (size_t i = 0; i < arg_array->Length(); ++i) {
+ arg_list->Set(
+ i,
+ converter->FromV8Value(arg_array->Get(i),
+ args.GetIsolate()->GetCurrentContext()));
+ }
+ params.arguments.Swap(arg_list.get());
+ }
+
+ if (call_type == APICALL) {
+ content::RenderThread::Get()->Send(
+ new ExtensionHostMsg_AddAPIActionToActivityLog(ext_id, params));
+ } else if (call_type == EVENT) {
+ content::RenderThread::Get()->Send(
+ new ExtensionHostMsg_AddEventToActivityLog(ext_id, params));
+ }
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api_activity_logger.h b/extensions/renderer/api_activity_logger.h
new file mode 100644
index 0000000..13f23f9
--- /dev/null
+++ b/extensions/renderer/api_activity_logger.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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_RENDERER_API_ACTIVITY_LOGGER_H_
+#define EXTENSIONS_RENDERER_API_ACTIVITY_LOGGER_H_
+
+#include <string>
+
+#include "extensions/common/features/feature.h"
+#include "extensions/renderer/object_backed_native_handler.h"
+#include "v8/include/v8.h"
+
+namespace extensions {
+
+// Used to log extension API calls and events that are implemented with custom
+// bindings.The actions are sent via IPC to extensions::ActivityLog for
+// recording and display.
+class APIActivityLogger : public ObjectBackedNativeHandler {
+ public:
+ explicit APIActivityLogger(ScriptContext* context);
+
+ private:
+ // Used to distinguish API calls & events from each other in LogInternal.
+ enum CallType { APICALL, EVENT };
+
+ // This is ultimately invoked in bindings.js with JavaScript arguments.
+ // arg0 - extension ID as a string
+ // arg1 - API call name as a string
+ // arg2 - arguments to the API call
+ // arg3 - any extra logging info as a string (optional)
+ static void LogAPICall(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // This is ultimately invoked in bindings.js with JavaScript arguments.
+ // arg0 - extension ID as a string
+ // arg1 - Event name as a string
+ // arg2 - Event arguments
+ // arg3 - any extra logging info as a string (optional)
+ static void LogEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // LogAPICall and LogEvent are really the same underneath except for
+ // how they are ultimately dispatched to the log.
+ static void LogInternal(const CallType call_type,
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ DISALLOW_COPY_AND_ASSIGN(APIActivityLogger);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_ACTIVITY_LOGGER_H_
diff --git a/extensions/renderer/api_definitions_natives.cc b/extensions/renderer/api_definitions_natives.cc
index a7970b2..c0dc699 100644
--- a/extensions/renderer/api_definitions_natives.cc
+++ b/extensions/renderer/api_definitions_natives.cc
@@ -4,9 +4,9 @@
#include "extensions/renderer/api_definitions_natives.h"
-#include "chrome/renderer/extensions/dispatcher.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
+#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/script_context.h"
namespace extensions {
diff --git a/extensions/renderer/app_runtime_custom_bindings.cc b/extensions/renderer/app_runtime_custom_bindings.cc
new file mode 100644
index 0000000..3a1f169
--- /dev/null
+++ b/extensions/renderer/app_runtime_custom_bindings.cc
@@ -0,0 +1,66 @@
+// Copyright 2014 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/renderer/app_runtime_custom_bindings.h"
+
+#include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "third_party/WebKit/public/platform/WebCString.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/web/WebBlob.h"
+#include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
+
+using blink::WebBlob;
+using blink::WebSerializedScriptValue;
+using blink::WebString;
+
+namespace {
+
+void DeserializeString(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DCHECK(args.Length() == 1);
+ DCHECK(args[0]->IsString());
+
+ std::string data_v8(*v8::String::Utf8Value(args[0]));
+ WebString data_webstring = WebString::fromUTF8(data_v8);
+ WebSerializedScriptValue serialized =
+ WebSerializedScriptValue::fromString(data_webstring);
+ args.GetReturnValue().Set(serialized.deserialize());
+}
+
+void SerializeToString(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DCHECK(args.Length() == 1);
+ WebSerializedScriptValue data = WebSerializedScriptValue::serialize(args[0]);
+ WebString data_webstring = data.toString();
+
+ std::string v = std::string(data_webstring.utf8());
+ args.GetReturnValue().Set(
+ v8::String::NewFromUtf8(args.GetIsolate(), v.c_str()));
+}
+
+void CreateBlob(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DCHECK(args.Length() == 2);
+ DCHECK(args[0]->IsString());
+ DCHECK(args[1]->IsNumber());
+
+ std::string blob_file_path(*v8::String::Utf8Value(args[0]));
+ std::string blob_length_string(*v8::String::Utf8Value(args[1]));
+ int64 blob_length = 0;
+ DCHECK(base::StringToInt64(blob_length_string, &blob_length));
+ blink::WebBlob web_blob =
+ WebBlob::createFromFile(WebString::fromUTF8(blob_file_path), blob_length);
+ args.GetReturnValue().Set(web_blob.toV8Value());
+}
+
+} // namespace
+
+namespace extensions {
+
+AppRuntimeCustomBindings::AppRuntimeCustomBindings(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction("DeserializeString", base::Bind(&DeserializeString));
+ RouteFunction("SerializeToString", base::Bind(&SerializeToString));
+ RouteFunction("CreateBlob", base::Bind(&CreateBlob));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/app_runtime_custom_bindings.h b/extensions/renderer/app_runtime_custom_bindings.h
new file mode 100644
index 0000000..f4ff906
--- /dev/null
+++ b/extensions/renderer/app_runtime_custom_bindings.h
@@ -0,0 +1,23 @@
+// Copyright 2014 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_RENDERER_APP_RUNTIME_CUSTOM_BINDINGS_H_
+#define EXTENSIONS_RENDERER_APP_RUNTIME_CUSTOM_BINDINGS_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+// The native component of custom bindings for the chrome.app.runtime API.
+class AppRuntimeCustomBindings : public ObjectBackedNativeHandler {
+ public:
+ explicit AppRuntimeCustomBindings(ScriptContext* context);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AppRuntimeCustomBindings);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_APP_RUNTIME_CUSTOM_BINDINGS_H_
diff --git a/extensions/renderer/console.cc b/extensions/renderer/console.cc
index 303de44..f7ae995 100644
--- a/extensions/renderer/console.cc
+++ b/extensions/renderer/console.cc
@@ -10,10 +10,10 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "chrome/renderer/extensions/dispatcher.h"
#include "chrome/renderer/extensions/extension_helper.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/render_view_visitor.h"
+#include "extensions/renderer/dispatcher.h"
#include "third_party/WebKit/public/web/WebConsoleMessage.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
diff --git a/extensions/renderer/default_dispatcher_delegate.cc b/extensions/renderer/default_dispatcher_delegate.cc
new file mode 100644
index 0000000..326ee70
--- /dev/null
+++ b/extensions/renderer/default_dispatcher_delegate.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 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/renderer/default_dispatcher_delegate.h"
+
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+DefaultDispatcherDelegate::DefaultDispatcherDelegate() {
+}
+
+DefaultDispatcherDelegate::~DefaultDispatcherDelegate() {
+}
+
+// DispatcherDelegate implementation.
+scoped_ptr<ScriptContext> DefaultDispatcherDelegate::CreateScriptContext(
+ const v8::Handle<v8::Context>& v8_context,
+ blink::WebFrame* frame,
+ const Extension* extension,
+ Feature::Context context_type) {
+ return make_scoped_ptr(
+ new ScriptContext(v8_context, frame, extension, context_type));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/default_dispatcher_delegate.h b/extensions/renderer/default_dispatcher_delegate.h
new file mode 100644
index 0000000..1ee92ab
--- /dev/null
+++ b/extensions/renderer/default_dispatcher_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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_RENDERER_DEFAULT_DISPATCHER_DELEGATE_H
+#define EXTENSIONS_RENDERER_DEFAULT_DISPATCHER_DELEGATE_H
+
+#include "extensions/renderer/dispatcher_delegate.h"
+
+namespace extensions {
+
+class DefaultDispatcherDelegate : public DispatcherDelegate {
+ public:
+ DefaultDispatcherDelegate();
+ virtual ~DefaultDispatcherDelegate();
+
+ // DispatcherDelegate implementation.
+ virtual scoped_ptr<ScriptContext> CreateScriptContext(
+ const v8::Handle<v8::Context>& v8_context,
+ blink::WebFrame* frame,
+ const Extension* extension,
+ Feature::Context context_type) OVERRIDE;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_DEFAULT_DISPATCHER_DELEGATE_H
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
new file mode 100644
index 0000000..93fa434
--- /dev/null
+++ b/extensions/renderer/dispatcher.cc
@@ -0,0 +1,1216 @@
+// Copyright 2014 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/renderer/dispatcher.h"
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "chrome/renderer/extensions/extension_helper.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "extensions/common/api/messaging/message.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_api.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/extension_urls.h"
+#include "extensions/common/features/feature.h"
+#include "extensions/common/features/feature_provider.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/background_info.h"
+#include "extensions/common/manifest_handlers/externally_connectable.h"
+#include "extensions/common/manifest_handlers/sandboxed_page_info.h"
+#include "extensions/common/message_bundle.h"
+#include "extensions/common/permissions/permission_set.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/switches.h"
+#include "extensions/common/view_type.h"
+#include "extensions/renderer/api_activity_logger.h"
+#include "extensions/renderer/api_definitions_natives.h"
+#include "extensions/renderer/app_runtime_custom_bindings.h"
+#include "extensions/renderer/binding_generating_native_handler.h"
+#include "extensions/renderer/blob_native_handler.h"
+#include "extensions/renderer/content_watcher.h"
+#include "extensions/renderer/context_menus_custom_bindings.h"
+#include "extensions/renderer/css_native_handler.h"
+#include "extensions/renderer/dispatcher_delegate.h"
+#include "extensions/renderer/document_custom_bindings.h"
+#include "extensions/renderer/dom_activity_logger.h"
+#include "extensions/renderer/event_bindings.h"
+#include "extensions/renderer/extension_groups.h"
+#include "extensions/renderer/extensions_renderer_client.h"
+#include "extensions/renderer/file_system_natives.h"
+#include "extensions/renderer/i18n_custom_bindings.h"
+#include "extensions/renderer/id_generator_custom_bindings.h"
+#include "extensions/renderer/lazy_background_page_native_handler.h"
+#include "extensions/renderer/logging_native_handler.h"
+#include "extensions/renderer/messaging_bindings.h"
+#include "extensions/renderer/module_system.h"
+#include "extensions/renderer/print_native_handler.h"
+#include "extensions/renderer/process_info_native_handler.h"
+#include "extensions/renderer/render_view_observer_natives.h"
+#include "extensions/renderer/request_sender.h"
+#include "extensions/renderer/runtime_custom_bindings.h"
+#include "extensions/renderer/safe_builtins.h"
+#include "extensions/renderer/script_context.h"
+#include "extensions/renderer/script_context_set.h"
+#include "extensions/renderer/send_request_natives.h"
+#include "extensions/renderer/set_icon_natives.h"
+#include "extensions/renderer/test_features_native_handler.h"
+#include "extensions/renderer/user_gestures_native_handler.h"
+#include "extensions/renderer/user_script_slave.h"
+#include "extensions/renderer/utils_native_handler.h"
+#include "extensions/renderer/v8_context_native_handler.h"
+#include "grit/common_resources.h"
+#include "grit/renderer_resources.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/web/WebCustomElement.h"
+#include "third_party/WebKit/public/web/WebDataSource.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
+#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
+#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "v8/include/v8.h"
+
+using base::UserMetricsAction;
+using blink::WebDataSource;
+using blink::WebDocument;
+using blink::WebFrame;
+using blink::WebScopedUserGesture;
+using blink::WebSecurityPolicy;
+using blink::WebString;
+using blink::WebVector;
+using blink::WebView;
+using content::RenderThread;
+using content::RenderView;
+
+namespace extensions {
+
+namespace {
+
+static const int64 kInitialExtensionIdleHandlerDelayMs = 5 * 1000;
+static const int64 kMaxExtensionIdleHandlerDelayMs = 5 * 60 * 1000;
+static const char kEventDispatchFunction[] = "dispatchEvent";
+static const char kOnSuspendEvent[] = "runtime.onSuspend";
+static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled";
+
+// Returns the global value for "chrome" from |context|. If one doesn't exist
+// creates a new object for it.
+//
+// Note that this isn't necessarily an object, since webpages can write, for
+// example, "window.chrome = true".
+v8::Handle<v8::Value> GetOrCreateChrome(ScriptContext* context) {
+ v8::Handle<v8::String> chrome_string(
+ v8::String::NewFromUtf8(context->isolate(), "chrome"));
+ v8::Handle<v8::Object> global(context->v8_context()->Global());
+ v8::Handle<v8::Value> chrome(global->Get(chrome_string));
+ if (chrome->IsUndefined()) {
+ chrome = v8::Object::New(context->isolate());
+ global->Set(chrome_string, chrome);
+ }
+ return chrome;
+}
+
+// Returns |value| cast to an object if possible, else an empty handle.
+v8::Handle<v8::Object> AsObjectOrEmpty(v8::Handle<v8::Value> value) {
+ return value->IsObject() ? value.As<v8::Object>() : v8::Handle<v8::Object>();
+}
+
+// Calls a method |method_name| in a module |module_name| belonging to the
+// module system from |context|. Intended as a callback target from
+// ScriptContextSet::ForEach.
+void CallModuleMethod(const std::string& module_name,
+ const std::string& method_name,
+ const base::ListValue* args,
+ ScriptContext* context) {
+ v8::HandleScope handle_scope(context->isolate());
+ v8::Context::Scope context_scope(context->v8_context());
+
+ scoped_ptr<content::V8ValueConverter> converter(
+ content::V8ValueConverter::create());
+
+ std::vector<v8::Handle<v8::Value> > arguments;
+ for (base::ListValue::const_iterator it = args->begin(); it != args->end();
+ ++it) {
+ arguments.push_back(converter->ToV8Value(*it, context->v8_context()));
+ }
+
+ context->module_system()->CallModuleMethod(
+ module_name, method_name, &arguments);
+}
+
+// This handles the "chrome." root API object in script contexts.
+class ChromeNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ explicit ChromeNativeHandler(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction(
+ "GetChrome",
+ base::Bind(&ChromeNativeHandler::GetChrome, base::Unretained(this)));
+ }
+
+ void GetChrome(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(GetOrCreateChrome(context()));
+ }
+};
+
+} // namespace
+
+Dispatcher::Dispatcher(DispatcherDelegate* delegate)
+ : delegate_(delegate),
+ content_watcher_(new ContentWatcher()),
+ source_map_(&ResourceBundle::GetSharedInstance()),
+ v8_schema_registry_(new V8SchemaRegistry),
+ is_webkit_initialized_(false) {
+ const CommandLine& command_line = *(CommandLine::ForCurrentProcess());
+ is_extension_process_ =
+ command_line.HasSwitch(extensions::switches::kExtensionProcess) ||
+ command_line.HasSwitch(::switches::kSingleProcess);
+
+ if (is_extension_process_) {
+ RenderThread::Get()->SetIdleNotificationDelayInMs(
+ kInitialExtensionIdleHandlerDelayMs);
+ }
+
+ RenderThread::Get()->RegisterExtension(SafeBuiltins::CreateV8Extension());
+
+ user_script_slave_.reset(new UserScriptSlave(&extensions_));
+ request_sender_.reset(new RequestSender(this));
+ PopulateSourceMap();
+}
+
+Dispatcher::~Dispatcher() {
+}
+
+bool Dispatcher::IsExtensionActive(const std::string& extension_id) const {
+ bool is_active =
+ active_extension_ids_.find(extension_id) != active_extension_ids_.end();
+ if (is_active)
+ CHECK(extensions_.Contains(extension_id));
+ return is_active;
+}
+
+std::string Dispatcher::GetExtensionID(const WebFrame* frame, int world_id) {
+ if (world_id != 0) {
+ // Isolated worlds (content script).
+ return user_script_slave_->GetExtensionIdForIsolatedWorld(world_id);
+ }
+
+ // TODO(kalman): Delete this check.
+ if (frame->document().securityOrigin().isUnique())
+ return std::string();
+
+ // Extension pages (chrome-extension:// URLs).
+ GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
+ return extensions_.GetExtensionOrAppIDByURL(frame_url);
+}
+
+void Dispatcher::DidCreateScriptContext(
+ WebFrame* frame,
+ const v8::Handle<v8::Context>& v8_context,
+ int extension_group,
+ int world_id) {
+#if !defined(ENABLE_EXTENSIONS)
+ return;
+#endif
+
+ std::string extension_id = GetExtensionID(frame, world_id);
+
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (!extension && !extension_id.empty()) {
+ // There are conditions where despite a context being associated with an
+ // extension, no extension actually gets found. Ignore "invalid" because
+ // CSP blocks extension page loading by switching the extension ID to
+ // "invalid". This isn't interesting.
+ if (extension_id != "invalid") {
+ LOG(ERROR) << "Extension \"" << extension_id << "\" not found";
+ RenderThread::Get()->RecordAction(
+ UserMetricsAction("ExtensionNotFound_ED"));
+ }
+
+ extension_id = "";
+ }
+
+ Feature::Context context_type =
+ ClassifyJavaScriptContext(extension,
+ extension_group,
+ ScriptContext::GetDataSourceURLForFrame(frame),
+ frame->document().securityOrigin());
+
+ ScriptContext* context =
+ delegate_->CreateScriptContext(v8_context, frame, extension, context_type)
+ .release();
+ script_context_set_.Add(context);
+
+ if (extension) {
+ InitOriginPermissions(extension, context_type);
+ }
+
+ {
+ scoped_ptr<ModuleSystem> module_system(
+ new ModuleSystem(context, &source_map_));
+ context->set_module_system(module_system.Pass());
+ }
+ ModuleSystem* module_system = context->module_system();
+
+ // Enable natives in startup.
+ ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system);
+
+ RegisterNativeHandlers(module_system, context);
+
+ // chrome.Event is part of the public API (although undocumented). Make it
+ // lazily evalulate to Event from event_bindings.js. For extensions only
+ // though, not all webpages!
+ if (context->extension()) {
+ v8::Handle<v8::Object> chrome = AsObjectOrEmpty(GetOrCreateChrome(context));
+ if (!chrome.IsEmpty())
+ module_system->SetLazyField(chrome, "Event", kEventBindings, "Event");
+ }
+
+ UpdateBindingsForContext(context);
+
+ bool is_within_platform_app = IsWithinPlatformApp();
+ // Inject custom JS into the platform app context.
+ if (is_within_platform_app) {
+ module_system->Require("platformApp");
+ }
+
+ delegate_->RequireAdditionalModules(
+ module_system, extension, context_type, is_within_platform_app);
+
+ VLOG(1) << "Num tracked contexts: " << script_context_set_.size();
+}
+
+void Dispatcher::WillReleaseScriptContext(
+ WebFrame* frame,
+ const v8::Handle<v8::Context>& v8_context,
+ int world_id) {
+ ScriptContext* context = script_context_set_.GetByV8Context(v8_context);
+ if (!context)
+ return;
+
+ context->DispatchOnUnloadEvent();
+ // TODO(kalman): add an invalidation observer interface to ScriptContext.
+ request_sender_->InvalidateSource(context);
+
+ script_context_set_.Remove(context);
+ VLOG(1) << "Num tracked contexts: " << script_context_set_.size();
+}
+
+void Dispatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
+ if (IsWithinPlatformApp()) {
+ // WebKit doesn't let us define an additional user agent stylesheet, so we
+ // insert the default platform app stylesheet into all documents that are
+ // loaded in each app.
+ std::string stylesheet = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_PLATFORM_APP_CSS)
+ .as_string();
+ ReplaceFirstSubstringAfterOffset(
+ &stylesheet, 0, "$FONTFAMILY", system_font_family_);
+ ReplaceFirstSubstringAfterOffset(
+ &stylesheet, 0, "$FONTSIZE", system_font_size_);
+ frame->document().insertStyleSheet(WebString::fromUTF8(stylesheet));
+ }
+
+ content_watcher_->DidCreateDocumentElement(frame);
+}
+
+void Dispatcher::DidMatchCSS(
+ blink::WebFrame* frame,
+ const blink::WebVector<blink::WebString>& newly_matching_selectors,
+ const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
+ content_watcher_->DidMatchCSS(
+ frame, newly_matching_selectors, stopped_matching_selectors);
+}
+
+void Dispatcher::OnExtensionResponse(int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error) {
+ request_sender_->HandleResponse(request_id, success, response, error);
+}
+
+bool Dispatcher::CheckContextAccessToExtensionAPI(
+ const std::string& function_name,
+ ScriptContext* context) const {
+ if (!context) {
+ DLOG(ERROR) << "Not in a v8::Context";
+ return false;
+ }
+
+ if (!context->extension()) {
+ context->isolate()->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(context->isolate(), "Not in an extension.")));
+ return false;
+ }
+
+ // Theoretically we could end up with bindings being injected into sandboxed
+ // frames, for example content scripts. Don't let them execute API functions.
+ blink::WebFrame* frame = context->web_frame();
+ if (IsSandboxedPage(ScriptContext::GetDataSourceURLForFrame(frame))) {
+ static const char kMessage[] =
+ "%s cannot be used within a sandboxed frame.";
+ std::string error_msg = base::StringPrintf(kMessage, function_name.c_str());
+ context->isolate()->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(context->isolate(), error_msg.c_str())));
+ return false;
+ }
+
+ Feature::Availability availability = context->GetAvailability(function_name);
+ if (!availability.is_available()) {
+ context->isolate()->ThrowException(
+ v8::Exception::Error(v8::String::NewFromUtf8(
+ context->isolate(), availability.message().c_str())));
+ }
+
+ return availability.is_available();
+}
+
+void Dispatcher::DispatchEvent(const std::string& extension_id,
+ const std::string& event_name) const {
+ base::ListValue args;
+ args.Set(0, new base::StringValue(event_name));
+ args.Set(1, new base::ListValue());
+
+ // Needed for Windows compilation, since kEventBindings is declared extern.
+ const char* local_event_bindings = kEventBindings;
+ script_context_set_.ForEach(extension_id,
+ NULL, // all render views
+ base::Bind(&CallModuleMethod,
+ local_event_bindings,
+ kEventDispatchFunction,
+ &args));
+}
+
+void Dispatcher::InvokeModuleSystemMethod(content::RenderView* render_view,
+ const std::string& extension_id,
+ const std::string& module_name,
+ const std::string& function_name,
+ const base::ListValue& args,
+ bool user_gesture) {
+ scoped_ptr<WebScopedUserGesture> web_user_gesture;
+ if (user_gesture)
+ web_user_gesture.reset(new WebScopedUserGesture);
+
+ script_context_set_.ForEach(
+ extension_id,
+ render_view,
+ base::Bind(&CallModuleMethod, module_name, function_name, &args));
+
+ // Reset the idle handler each time there's any activity like event or message
+ // dispatch, for which Invoke is the chokepoint.
+ if (is_extension_process_) {
+ RenderThread::Get()->ScheduleIdleHandler(
+ kInitialExtensionIdleHandlerDelayMs);
+ }
+
+ // Tell the browser process when an event has been dispatched with a lazy
+ // background page active.
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (extension && BackgroundInfo::HasLazyBackgroundPage(extension) &&
+ module_name == kEventBindings &&
+ function_name == kEventDispatchFunction) {
+ RenderView* background_view =
+ ExtensionHelper::GetBackgroundPage(extension_id);
+ if (background_view) {
+ background_view->Send(
+ new ExtensionHostMsg_EventAck(background_view->GetRoutingID()));
+ }
+ }
+}
+
+void Dispatcher::ClearPortData(int port_id) {
+ // Only the target port side has entries in |port_to_tab_id_map_|. If
+ // |port_id| is a source port, std::map::erase() will just silently fail
+ // here as a no-op.
+ port_to_tab_id_map_.erase(port_id);
+}
+
+bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(Dispatcher, message)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_CancelSuspend, OnCancelSuspend)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ClearTabSpecificPermissions,
+ OnClearTabSpecificPermissions)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, OnDispatchOnConnect)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect, OnDispatchOnDisconnect)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_Loaded, OnLoaded)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnMessageInvoke)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_SetChannel, OnSetChannel)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_SetFunctionNames, OnSetFunctionNames)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingWhitelist,
+ OnSetScriptingWhitelist)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions,
+ OnUpdateTabSpecificPermissions)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UsingWebRequestAPI, OnUsingWebRequestAPI)
+ IPC_MESSAGE_FORWARD(ExtensionMsg_WatchPages,
+ content_watcher_.get(),
+ ContentWatcher::OnWatchPages)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void Dispatcher::WebKitInitialized() {
+ // For extensions, we want to ensure we call the IdleHandler every so often,
+ // even if the extension keeps up activity.
+ if (is_extension_process_) {
+ forced_idle_timer_.reset(new base::RepeatingTimer<content::RenderThread>);
+ forced_idle_timer_->Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kMaxExtensionIdleHandlerDelayMs),
+ RenderThread::Get(),
+ &RenderThread::IdleHandler);
+ }
+
+ // Initialize host permissions for any extensions that were activated before
+ // WebKit was initialized.
+ for (std::set<std::string>::iterator iter = active_extension_ids_.begin();
+ iter != active_extension_ids_.end();
+ ++iter) {
+ const Extension* extension = extensions_.GetByID(*iter);
+ CHECK(extension);
+ }
+
+ EnableCustomElementWhiteList();
+
+ is_webkit_initialized_ = true;
+}
+
+void Dispatcher::IdleNotification() {
+ if (is_extension_process_) {
+ // Dampen the forced delay as well if the extension stays idle for long
+ // periods of time.
+ int64 forced_delay_ms =
+ std::max(RenderThread::Get()->GetIdleNotificationDelayInMs(),
+ kMaxExtensionIdleHandlerDelayMs);
+ forced_idle_timer_->Stop();
+ forced_idle_timer_->Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(forced_delay_ms),
+ RenderThread::Get(),
+ &RenderThread::IdleHandler);
+ }
+}
+
+void Dispatcher::OnRenderProcessShutdown() {
+ v8_schema_registry_.reset();
+ forced_idle_timer_.reset();
+}
+
+void Dispatcher::OnActivateExtension(const std::string& extension_id) {
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (!extension) {
+ // Extension was activated but was never loaded. This probably means that
+ // the renderer failed to load it (or the browser failed to tell us when it
+ // did). Failures shouldn't happen, but instead of crashing there (which
+ // executes on all renderers) be conservative and only crash in the renderer
+ // of the extension which failed to load; this one.
+ std::string& error = extension_load_errors_[extension_id];
+ char minidump[256];
+ base::debug::Alias(&minidump);
+ base::snprintf(minidump,
+ arraysize(minidump),
+ "e::dispatcher:%s:%s",
+ extension_id.c_str(),
+ error.c_str());
+ CHECK(extension) << extension_id << " was never loaded: " << error;
+ }
+
+ active_extension_ids_.insert(extension_id);
+
+ // This is called when starting a new extension page, so start the idle
+ // handler ticking.
+ RenderThread::Get()->ScheduleIdleHandler(kInitialExtensionIdleHandlerDelayMs);
+
+ if (is_webkit_initialized_) {
+ extensions::DOMActivityLogger::AttachToWorld(
+ extensions::DOMActivityLogger::kMainWorldId, extension_id);
+ }
+
+ UpdateActiveExtensions();
+}
+
+void Dispatcher::OnCancelSuspend(const std::string& extension_id) {
+ DispatchEvent(extension_id, kOnSuspendCanceledEvent);
+}
+
+void Dispatcher::OnClearTabSpecificPermissions(
+ int tab_id,
+ const std::vector<std::string>& extension_ids) {
+ delegate_->ClearTabSpecificPermissions(this, tab_id, extension_ids);
+}
+
+void Dispatcher::OnDeliverMessage(int target_port_id, const Message& message) {
+ scoped_ptr<RequestSender::ScopedTabID> scoped_tab_id;
+ std::map<int, int>::const_iterator it =
+ port_to_tab_id_map_.find(target_port_id);
+ if (it != port_to_tab_id_map_.end()) {
+ scoped_tab_id.reset(
+ new RequestSender::ScopedTabID(request_sender(), it->second));
+ }
+
+ MessagingBindings::DeliverMessage(script_context_set_.GetAll(),
+ target_port_id,
+ message,
+ NULL); // All render views.
+}
+
+void Dispatcher::OnDispatchOnConnect(
+ int target_port_id,
+ const std::string& channel_name,
+ const base::DictionaryValue& source_tab,
+ const ExtensionMsg_ExternalConnectionInfo& info,
+ const std::string& tls_channel_id) {
+ DCHECK(!ContainsKey(port_to_tab_id_map_, target_port_id));
+ DCHECK_EQ(1, target_port_id % 2); // target renderer ports have odd IDs.
+ int sender_tab_id = -1;
+ source_tab.GetInteger("id", &sender_tab_id);
+ port_to_tab_id_map_[target_port_id] = sender_tab_id;
+
+ MessagingBindings::DispatchOnConnect(script_context_set_.GetAll(),
+ target_port_id,
+ channel_name,
+ source_tab,
+ info.source_id,
+ info.target_id,
+ info.source_url,
+ tls_channel_id,
+ NULL); // All render views.
+}
+
+void Dispatcher::OnDispatchOnDisconnect(int port_id,
+ const std::string& error_message) {
+ MessagingBindings::DispatchOnDisconnect(script_context_set_.GetAll(),
+ port_id,
+ error_message,
+ NULL); // All render views.
+}
+
+void Dispatcher::OnLoaded(
+ const std::vector<ExtensionMsg_Loaded_Params>& loaded_extensions) {
+ std::vector<ExtensionMsg_Loaded_Params>::const_iterator i;
+ for (i = loaded_extensions.begin(); i != loaded_extensions.end(); ++i) {
+ std::string error;
+ scoped_refptr<const Extension> extension = i->ConvertToExtension(&error);
+ if (!extension.get()) {
+ extension_load_errors_[i->id] = error;
+ continue;
+ }
+ OnLoadedInternal(extension);
+ }
+ // Update the available bindings for all contexts. These may have changed if
+ // an externally_connectable extension was loaded that can connect to an
+ // open webpage.
+ UpdateBindings("");
+}
+
+void Dispatcher::OnLoadedInternal(scoped_refptr<const Extension> extension) {
+ extensions_.Insert(extension);
+}
+
+void Dispatcher::OnMessageInvoke(const std::string& extension_id,
+ const std::string& module_name,
+ const std::string& function_name,
+ const base::ListValue& args,
+ bool user_gesture) {
+ InvokeModuleSystemMethod(
+ NULL, extension_id, module_name, function_name, args, user_gesture);
+}
+
+void Dispatcher::OnSetChannel(int channel) {
+ delegate_->SetChannel(channel);
+}
+
+void Dispatcher::OnSetFunctionNames(const std::vector<std::string>& names) {
+ function_names_.clear();
+ for (size_t i = 0; i < names.size(); ++i)
+ function_names_.insert(names[i]);
+}
+
+void Dispatcher::OnSetScriptingWhitelist(
+ const ExtensionsClient::ScriptingWhitelist& extension_ids) {
+ ExtensionsClient::Get()->SetScriptingWhitelist(extension_ids);
+}
+
+void Dispatcher::OnSetSystemFont(const std::string& font_family,
+ const std::string& font_size) {
+ system_font_family_ = font_family;
+ system_font_size_ = font_size;
+}
+
+void Dispatcher::OnShouldSuspend(const std::string& extension_id,
+ int sequence_id) {
+ RenderThread::Get()->Send(
+ new ExtensionHostMsg_ShouldSuspendAck(extension_id, sequence_id));
+}
+
+void Dispatcher::OnSuspend(const std::string& extension_id) {
+ // Dispatch the suspend event. This doesn't go through the standard event
+ // dispatch machinery because it requires special handling. We need to let
+ // the browser know when we are starting and stopping the event dispatch, so
+ // that it still considers the extension idle despite any activity the suspend
+ // event creates.
+ DispatchEvent(extension_id, kOnSuspendEvent);
+ RenderThread::Get()->Send(new ExtensionHostMsg_SuspendAck(extension_id));
+}
+
+void Dispatcher::OnUnloaded(const std::string& id) {
+ extensions_.Remove(id);
+ active_extension_ids_.erase(id);
+
+ // If the extension is later reloaded with a different set of permissions,
+ // we'd like it to get a new isolated world ID, so that it can pick up the
+ // changed origin whitelist.
+ user_script_slave_->RemoveIsolatedWorld(id);
+
+ // Invalidate all of the contexts that were removed.
+ // TODO(kalman): add an invalidation observer interface to ScriptContext.
+ ScriptContextSet::ContextSet removed_contexts =
+ script_context_set_.OnExtensionUnloaded(id);
+ for (ScriptContextSet::ContextSet::iterator it = removed_contexts.begin();
+ it != removed_contexts.end();
+ ++it) {
+ request_sender_->InvalidateSource(*it);
+ }
+
+ // Update the available bindings for the remaining contexts. These may have
+ // changed if an externally_connectable extension is unloaded and a webpage
+ // is no longer accessible.
+ UpdateBindings("");
+
+ // Invalidates the messages map for the extension in case the extension is
+ // reloaded with a new messages map.
+ EraseL10nMessagesMap(id);
+
+ // We don't do anything with existing platform-app stylesheets. They will
+ // stay resident, but the URL pattern corresponding to the unloaded
+ // extension's URL just won't match anything anymore.
+}
+
+void Dispatcher::OnUpdatePermissions(
+ const ExtensionMsg_UpdatePermissions_Params& params) {
+ int reason_id = params.reason_id;
+ const std::string& extension_id = params.extension_id;
+ const APIPermissionSet& apis = params.apis;
+ const ManifestPermissionSet& manifest_permissions =
+ params.manifest_permissions;
+ const URLPatternSet& explicit_hosts = params.explicit_hosts;
+ const URLPatternSet& scriptable_hosts = params.scriptable_hosts;
+
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (!extension)
+ return;
+
+ scoped_refptr<const PermissionSet> delta = new PermissionSet(
+ apis, manifest_permissions, explicit_hosts, scriptable_hosts);
+ scoped_refptr<const PermissionSet> old_active =
+ extension->GetActivePermissions();
+ UpdatedExtensionPermissionsInfo::Reason reason =
+ static_cast<UpdatedExtensionPermissionsInfo::Reason>(reason_id);
+
+ const PermissionSet* new_active = NULL;
+ switch (reason) {
+ case UpdatedExtensionPermissionsInfo::ADDED:
+ new_active = PermissionSet::CreateUnion(old_active.get(), delta.get());
+ break;
+ case UpdatedExtensionPermissionsInfo::REMOVED:
+ new_active =
+ PermissionSet::CreateDifference(old_active.get(), delta.get());
+ break;
+ }
+
+ PermissionsData::SetActivePermissions(extension, new_active);
+ UpdateOriginPermissions(reason, extension, explicit_hosts);
+ UpdateBindings(extension->id());
+}
+
+void Dispatcher::OnUpdateTabSpecificPermissions(
+ int page_id,
+ int tab_id,
+ const std::string& extension_id,
+ const URLPatternSet& origin_set) {
+ delegate_->UpdateTabSpecificPermissions(
+ this, page_id, tab_id, extension_id, origin_set);
+}
+
+void Dispatcher::OnUpdateUserScripts(base::SharedMemoryHandle scripts) {
+ DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle";
+ user_script_slave_->UpdateScripts(scripts);
+ UpdateActiveExtensions();
+}
+
+void Dispatcher::OnUsingWebRequestAPI(bool adblock,
+ bool adblock_plus,
+ bool other_webrequest) {
+ delegate_->HandleWebRequestAPIUsage(adblock, adblock_plus, other_webrequest);
+}
+
+void Dispatcher::UpdateActiveExtensions() {
+ std::set<std::string> active_extensions = active_extension_ids_;
+ user_script_slave_->GetActiveExtensions(&active_extensions);
+ delegate_->OnActiveExtensionsUpdated(active_extensions);
+}
+
+void Dispatcher::InitOriginPermissions(const Extension* extension,
+ Feature::Context context_type) {
+ delegate_->InitOriginPermissions(extension, context_type);
+ UpdateOriginPermissions(
+ UpdatedExtensionPermissionsInfo::ADDED,
+ extension,
+ PermissionsData::GetEffectiveHostPermissions(extension));
+}
+
+void Dispatcher::UpdateOriginPermissions(
+ UpdatedExtensionPermissionsInfo::Reason reason,
+ const Extension* extension,
+ const URLPatternSet& origins) {
+ for (URLPatternSet::const_iterator i = origins.begin(); i != origins.end();
+ ++i) {
+ const char* schemes[] = {
+ content::kHttpScheme, content::kHttpsScheme, content::kFileScheme,
+ content::kChromeUIScheme, content::kFtpScheme,
+ };
+ for (size_t j = 0; j < arraysize(schemes); ++j) {
+ if (i->MatchesScheme(schemes[j])) {
+ ((reason == UpdatedExtensionPermissionsInfo::REMOVED)
+ ? WebSecurityPolicy::removeOriginAccessWhitelistEntry
+ : WebSecurityPolicy::addOriginAccessWhitelistEntry)(
+ extension->url(),
+ WebString::fromUTF8(schemes[j]),
+ WebString::fromUTF8(i->host()),
+ i->match_subdomains());
+ }
+ }
+ }
+}
+
+void Dispatcher::EnableCustomElementWhiteList() {
+ blink::WebCustomElement::addEmbedderCustomElementName("webview");
+ // TODO(fsamuel): Add <adview> to the whitelist once it has been converted
+ // into a custom element.
+ blink::WebCustomElement::addEmbedderCustomElementName("browser-plugin");
+}
+
+void Dispatcher::UpdateBindings(const std::string& extension_id) {
+ script_context_set().ForEach(extension_id,
+ NULL, // all render views
+ base::Bind(&Dispatcher::UpdateBindingsForContext,
+ base::Unretained(this)));
+}
+
+void Dispatcher::UpdateBindingsForContext(ScriptContext* context) {
+ v8::HandleScope handle_scope(context->isolate());
+ v8::Context::Scope context_scope(context->v8_context());
+
+ // TODO(kalman): Make the bindings registration have zero overhead then run
+ // the same code regardless of context type.
+ switch (context->context_type()) {
+ case Feature::UNSPECIFIED_CONTEXT:
+ case Feature::WEB_PAGE_CONTEXT:
+ case Feature::BLESSED_WEB_PAGE_CONTEXT: {
+ // Web page context; it's too expensive to run the full bindings code.
+ // Hard-code that the app and webstore APIs are available...
+ RegisterBinding("app", context);
+ RegisterBinding("webstore", context);
+
+ // ... and that the runtime API might be available if any extension can
+ // connect to it.
+ bool runtime_is_available = false;
+ for (ExtensionSet::const_iterator it = extensions_.begin();
+ it != extensions_.end();
+ ++it) {
+ ExternallyConnectableInfo* info =
+ static_cast<ExternallyConnectableInfo*>(
+ (*it)->GetManifestData(manifest_keys::kExternallyConnectable));
+ if (info && info->matches.MatchesURL(context->GetURL())) {
+ runtime_is_available = true;
+ break;
+ }
+ }
+ if (runtime_is_available)
+ RegisterBinding("runtime", context);
+ break;
+ }
+
+ case Feature::BLESSED_EXTENSION_CONTEXT:
+ case Feature::UNBLESSED_EXTENSION_CONTEXT:
+ case Feature::CONTENT_SCRIPT_CONTEXT: {
+ // Extension context; iterate through all the APIs and bind the available
+ // ones.
+ FeatureProvider* api_feature_provider = FeatureProvider::GetAPIFeatures();
+ const std::vector<std::string>& apis =
+ api_feature_provider->GetAllFeatureNames();
+ for (std::vector<std::string>::const_iterator it = apis.begin();
+ it != apis.end();
+ ++it) {
+ const std::string& api_name = *it;
+ Feature* feature = api_feature_provider->GetFeature(api_name);
+ DCHECK(feature);
+
+ // Internal APIs are included via require(api_name) from internal code
+ // rather than chrome[api_name].
+ if (feature->IsInternal())
+ continue;
+
+ // If this API has a parent feature (and isn't marked 'noparent'),
+ // then this must be a function or event, so we should not register.
+ if (api_feature_provider->GetParent(feature) != NULL)
+ continue;
+
+ if (context->IsAnyFeatureAvailableToContext(*feature))
+ RegisterBinding(api_name, context);
+ }
+ if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType)) {
+ RegisterBinding("test", context);
+ }
+ break;
+ }
+ }
+}
+
+void Dispatcher::RegisterBinding(const std::string& api_name,
+ ScriptContext* context) {
+ std::string bind_name;
+ v8::Handle<v8::Object> bind_object =
+ GetOrCreateBindObjectIfAvailable(api_name, &bind_name, context);
+
+ // Empty if the bind object failed to be created, probably because the
+ // extension overrode chrome with a non-object, e.g. window.chrome = true.
+ if (bind_object.IsEmpty())
+ return;
+
+ v8::Local<v8::String> v8_api_name =
+ v8::String::NewFromUtf8(context->isolate(), api_name.c_str());
+ if (bind_object->HasRealNamedProperty(v8_api_name)) {
+ // The bind object may already have the property if the API has been
+ // registered before (or if the extension has put something there already,
+ // but, whatevs).
+ //
+ // In the former case, we need to re-register the bindings for the APIs
+ // which the extension now has permissions for (if any), but not touch any
+ // others so that we don't destroy state such as event listeners.
+ //
+ // TODO(kalman): Only register available APIs to make this all moot.
+ if (bind_object->HasRealNamedCallbackProperty(v8_api_name))
+ return; // lazy binding still there, nothing to do
+ if (bind_object->Get(v8_api_name)->IsObject())
+ return; // binding has already been fully installed
+ }
+
+ ModuleSystem* module_system = context->module_system();
+ if (!source_map_.Contains(api_name)) {
+ module_system->RegisterNativeHandler(
+ api_name,
+ scoped_ptr<NativeHandler>(new BindingGeneratingNativeHandler(
+ module_system, api_name, "binding")));
+ module_system->SetNativeLazyField(
+ bind_object, bind_name, api_name, "binding");
+ } else {
+ module_system->SetLazyField(bind_object, bind_name, api_name, "binding");
+ }
+}
+
+// NOTE: please use the naming convention "foo_natives" for these.
+void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system,
+ ScriptContext* context) {
+ module_system->RegisterNativeHandler(
+ "chrome", scoped_ptr<NativeHandler>(new ChromeNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "lazy_background_page",
+ scoped_ptr<NativeHandler>(new LazyBackgroundPageNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "logging", scoped_ptr<NativeHandler>(new LoggingNativeHandler(context)));
+ module_system->RegisterNativeHandler("schema_registry",
+ v8_schema_registry_->AsNativeHandler());
+ module_system->RegisterNativeHandler(
+ "print", scoped_ptr<NativeHandler>(new PrintNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "test_features",
+ scoped_ptr<NativeHandler>(new TestFeaturesNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "user_gestures",
+ scoped_ptr<NativeHandler>(new UserGesturesNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "utils", scoped_ptr<NativeHandler>(new UtilsNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "v8_context",
+ scoped_ptr<NativeHandler>(new V8ContextNativeHandler(context, this)));
+
+ const Extension* extension = context->extension();
+ int manifest_version = extension ? extension->manifest_version() : 1;
+ bool send_request_disabled =
+ (extension && Manifest::IsUnpackedLocation(extension->location()) &&
+ BackgroundInfo::HasLazyBackgroundPage(extension));
+ module_system->RegisterNativeHandler(
+ "process",
+ scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler(
+ context,
+ context->GetExtensionID(),
+ context->GetContextTypeDescription(),
+ ExtensionsRendererClient::Get()->IsIncognitoProcess(),
+ manifest_version,
+ send_request_disabled)));
+
+ module_system->RegisterNativeHandler(
+ "event_natives",
+ scoped_ptr<NativeHandler>(new EventBindings(this, context)));
+ module_system->RegisterNativeHandler(
+ "messaging_natives",
+ scoped_ptr<NativeHandler>(MessagingBindings::Get(this, context)));
+ module_system->RegisterNativeHandler(
+ "apiDefinitions",
+ scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this, context)));
+ module_system->RegisterNativeHandler(
+ "sendRequest",
+ scoped_ptr<NativeHandler>(
+ new SendRequestNatives(request_sender_.get(), context)));
+ module_system->RegisterNativeHandler(
+ "setIcon",
+ scoped_ptr<NativeHandler>(
+ new SetIconNatives(request_sender_.get(), context)));
+ module_system->RegisterNativeHandler(
+ "activityLogger",
+ scoped_ptr<NativeHandler>(new APIActivityLogger(context)));
+ module_system->RegisterNativeHandler(
+ "renderViewObserverNatives",
+ scoped_ptr<NativeHandler>(new RenderViewObserverNatives(context)));
+
+ // Natives used by multiple APIs.
+ module_system->RegisterNativeHandler(
+ "file_system_natives",
+ scoped_ptr<NativeHandler>(new FileSystemNatives(context)));
+
+ // Custom bindings.
+ module_system->RegisterNativeHandler(
+ "app_runtime",
+ scoped_ptr<NativeHandler>(new AppRuntimeCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "blob_natives",
+ scoped_ptr<NativeHandler>(new BlobNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "context_menus",
+ scoped_ptr<NativeHandler>(new ContextMenusCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "css_natives", scoped_ptr<NativeHandler>(new CssNativeHandler(context)));
+ module_system->RegisterNativeHandler(
+ "document_natives",
+ scoped_ptr<NativeHandler>(new DocumentCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "i18n", scoped_ptr<NativeHandler>(new I18NCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "id_generator",
+ scoped_ptr<NativeHandler>(new IdGeneratorCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "runtime", scoped_ptr<NativeHandler>(new RuntimeCustomBindings(context)));
+
+ delegate_->RegisterNativeHandlers(this, module_system, context);
+}
+
+void Dispatcher::PopulateSourceMap() {
+ // Libraries.
+ source_map_.RegisterSource("entryIdManager", IDR_ENTRY_ID_MANAGER);
+ source_map_.RegisterSource(kEventBindings, IDR_EVENT_BINDINGS_JS);
+ source_map_.RegisterSource("imageUtil", IDR_IMAGE_UTIL_JS);
+ source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS);
+ source_map_.RegisterSource("lastError", IDR_LAST_ERROR_JS);
+ source_map_.RegisterSource("messaging", IDR_MESSAGING_JS);
+ source_map_.RegisterSource("messaging_utils", IDR_MESSAGING_UTILS_JS);
+ source_map_.RegisterSource(kSchemaUtils, IDR_SCHEMA_UTILS_JS);
+ source_map_.RegisterSource("sendRequest", IDR_SEND_REQUEST_JS);
+ source_map_.RegisterSource("setIcon", IDR_SET_ICON_JS);
+ source_map_.RegisterSource("test", IDR_TEST_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("unload_event", IDR_UNLOAD_EVENT_JS);
+ source_map_.RegisterSource("utils", IDR_UTILS_JS);
+
+ // Custom bindings.
+ source_map_.RegisterSource("app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("contextMenus",
+ IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("i18n", IDR_I18N_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("permissions", IDR_PERMISSIONS_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("runtime", IDR_RUNTIME_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("binding", IDR_BINDING_JS);
+
+ // Custom types sources.
+ source_map_.RegisterSource("StorageArea", IDR_STORAGE_AREA_JS);
+
+ // Platform app sources that are not API-specific..
+ source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS);
+
+ delegate_->PopulateSourceMap(&source_map_);
+}
+
+bool Dispatcher::IsWithinPlatformApp() {
+ for (std::set<std::string>::iterator iter = active_extension_ids_.begin();
+ iter != active_extension_ids_.end();
+ ++iter) {
+ const Extension* extension = extensions_.GetByID(*iter);
+ if (extension && extension->is_platform_app())
+ return true;
+ }
+ return false;
+}
+
+// TODO(kalman): This is checking for the wrong thing, it should be checking if
+// the frame's security origin is unique. The extension sandbox directive is
+// checked for in extensions/common/manifest_handlers/csp_info.cc.
+bool Dispatcher::IsSandboxedPage(const GURL& url) const {
+ if (url.SchemeIs(kExtensionScheme)) {
+ const Extension* extension = extensions_.GetByID(url.host());
+ if (extension) {
+ return SandboxedPageInfo::IsSandboxedPage(extension, url.path());
+ }
+ }
+ return false;
+}
+
+Feature::Context Dispatcher::ClassifyJavaScriptContext(
+ const Extension* extension,
+ int extension_group,
+ const GURL& url,
+ const blink::WebSecurityOrigin& origin) {
+ DCHECK_GE(extension_group, 0);
+ if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
+ return extension ? // TODO(kalman): when does this happen?
+ Feature::CONTENT_SCRIPT_CONTEXT
+ : Feature::UNSPECIFIED_CONTEXT;
+ }
+
+ // We have an explicit check for sandboxed pages before checking whether the
+ // extension is active in this process because:
+ // 1. Sandboxed pages run in the same process as regular extension pages, so
+ // the extension is considered active.
+ // 2. ScriptContext creation (which triggers bindings injection) happens
+ // before the SecurityContext is updated with the sandbox flags (after
+ // reading the CSP header), so the caller can't check if the context's
+ // security origin is unique yet.
+ if (IsSandboxedPage(url))
+ return Feature::WEB_PAGE_CONTEXT;
+
+ if (extension && IsExtensionActive(extension->id())) {
+ // |extension| is active in this process, but it could be either a true
+ // extension process or within the extent of a hosted app. In the latter
+ // case this would usually be considered a (blessed) web page context,
+ // unless the extension in question is a component extension, in which case
+ // we cheat and call it blessed.
+ return (extension->is_hosted_app() &&
+ extension->location() != Manifest::COMPONENT)
+ ? Feature::BLESSED_WEB_PAGE_CONTEXT
+ : Feature::BLESSED_EXTENSION_CONTEXT;
+ }
+
+ // TODO(kalman): This isUnique() check is wrong, it should be performed as
+ // part of IsSandboxedPage().
+ if (!origin.isUnique() && extensions_.ExtensionBindingsAllowed(url)) {
+ if (!extension) // TODO(kalman): when does this happen?
+ return Feature::UNSPECIFIED_CONTEXT;
+ return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
+ : Feature::UNBLESSED_EXTENSION_CONTEXT;
+ }
+
+ if (url.is_valid())
+ return Feature::WEB_PAGE_CONTEXT;
+
+ return Feature::UNSPECIFIED_CONTEXT;
+}
+
+v8::Handle<v8::Object> Dispatcher::GetOrCreateObject(
+ const v8::Handle<v8::Object>& object,
+ const std::string& field,
+ v8::Isolate* isolate) {
+ v8::Handle<v8::String> key = v8::String::NewFromUtf8(isolate, field.c_str());
+ // If the object has a callback property, it is assumed it is an unavailable
+ // API, so it is safe to delete. This is checked before GetOrCreateObject is
+ // called.
+ if (object->HasRealNamedCallbackProperty(key)) {
+ object->Delete(key);
+ } else if (object->HasRealNamedProperty(key)) {
+ v8::Handle<v8::Value> value = object->Get(key);
+ CHECK(value->IsObject());
+ return v8::Handle<v8::Object>::Cast(value);
+ }
+
+ v8::Handle<v8::Object> new_object = v8::Object::New(isolate);
+ object->Set(key, new_object);
+ return new_object;
+}
+
+v8::Handle<v8::Object> Dispatcher::GetOrCreateBindObjectIfAvailable(
+ const std::string& api_name,
+ std::string* bind_name,
+ ScriptContext* context) {
+ std::vector<std::string> split;
+ base::SplitString(api_name, '.', &split);
+
+ v8::Handle<v8::Object> bind_object;
+
+ // Check if this API has an ancestor. If the API's ancestor is available and
+ // the API is not available, don't install the bindings for this API. If
+ // the API is available and its ancestor is not, delete the ancestor and
+ // install the bindings for the API. This is to prevent loading the ancestor
+ // API schema if it will not be needed.
+ //
+ // For example:
+ // If app is available and app.window is not, just install app.
+ // If app.window is available and app is not, delete app and install
+ // app.window on a new object so app does not have to be loaded.
+ FeatureProvider* api_feature_provider = FeatureProvider::GetAPIFeatures();
+ std::string ancestor_name;
+ bool only_ancestor_available = false;
+
+ for (size_t i = 0; i < split.size() - 1; ++i) {
+ ancestor_name += (i ? "." : "") + split[i];
+ if (api_feature_provider->GetFeature(ancestor_name) &&
+ context->GetAvailability(ancestor_name).is_available() &&
+ !context->GetAvailability(api_name).is_available()) {
+ only_ancestor_available = true;
+ break;
+ }
+
+ if (bind_object.IsEmpty()) {
+ bind_object = AsObjectOrEmpty(GetOrCreateChrome(context));
+ if (bind_object.IsEmpty())
+ return v8::Handle<v8::Object>();
+ }
+ bind_object = GetOrCreateObject(bind_object, split[i], context->isolate());
+ }
+
+ if (only_ancestor_available)
+ return v8::Handle<v8::Object>();
+
+ if (bind_name)
+ *bind_name = split.back();
+
+ return bind_object.IsEmpty() ? AsObjectOrEmpty(GetOrCreateChrome(context))
+ : bind_object;
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
new file mode 100644
index 0000000..e4d7aed
--- /dev/null
+++ b/extensions/renderer/dispatcher.h
@@ -0,0 +1,293 @@
+// Copyright 2014 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_RENDERER_DISPATCHER_H_
+#define EXTENSIONS_RENDERER_DISPATCHER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/shared_memory.h"
+#include "base/timer/timer.h"
+#include "content/public/renderer/render_process_observer.h"
+#include "extensions/common/event_filter.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/extensions_client.h"
+#include "extensions/common/features/feature.h"
+#include "extensions/renderer/resource_bundle_source_map.h"
+#include "extensions/renderer/script_context.h"
+#include "extensions/renderer/script_context_set.h"
+#include "extensions/renderer/v8_schema_registry.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "v8/include/v8.h"
+
+class ChromeRenderViewTest;
+class GURL;
+class ModuleSystem;
+class URLPattern;
+struct ExtensionMsg_ExternalConnectionInfo;
+struct ExtensionMsg_Loaded_Params;
+struct ExtensionMsg_UpdatePermissions_Params;
+
+namespace blink {
+class WebFrame;
+class WebSecurityOrigin;
+}
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace content {
+class RenderThread;
+}
+
+namespace extensions {
+class ContentWatcher;
+class DispatcherDelegate;
+class Extension;
+class FilteredEventRouter;
+class ManifestPermissionSet;
+class RequestSender;
+class ScriptContext;
+class UserScriptSlave;
+struct Message;
+
+// Dispatches extension control messages sent to the renderer and stores
+// renderer extension related state.
+class Dispatcher : public content::RenderProcessObserver {
+ public:
+ explicit Dispatcher(DispatcherDelegate* delegate);
+ virtual ~Dispatcher();
+
+ const std::set<std::string>& function_names() const {
+ return function_names_;
+ }
+
+ bool is_extension_process() const { return is_extension_process_; }
+
+ const ExtensionSet* extensions() const { return &extensions_; }
+
+ const ScriptContextSet& script_context_set() const {
+ return script_context_set_;
+ }
+
+ V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); }
+
+ ContentWatcher* content_watcher() { return content_watcher_.get(); }
+
+ UserScriptSlave* user_script_slave() { return user_script_slave_.get(); }
+
+ RequestSender* request_sender() { return request_sender_.get(); }
+
+ bool IsExtensionActive(const std::string& extension_id) const;
+
+ // Finds the extension ID for the JavaScript context associated with the
+ // specified |frame| and isolated world. If |world_id| is zero, finds the
+ // extension ID associated with the main world's JavaScript context. If the
+ // JavaScript context isn't from an extension, returns empty string.
+ std::string GetExtensionID(const blink::WebFrame* frame, int world_id);
+
+ void DidCreateScriptContext(blink::WebFrame* frame,
+ const v8::Handle<v8::Context>& context,
+ int extension_group,
+ int world_id);
+
+ void WillReleaseScriptContext(blink::WebFrame* frame,
+ const v8::Handle<v8::Context>& context,
+ int world_id);
+
+ void DidCreateDocumentElement(blink::WebFrame* frame);
+
+ void DidMatchCSS(
+ blink::WebFrame* frame,
+ const blink::WebVector<blink::WebString>& newly_matching_selectors,
+ const blink::WebVector<blink::WebString>& stopped_matching_selectors);
+
+ void OnExtensionResponse(int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error);
+
+ // Checks that the current context contains an extension that has permission
+ // to execute the specified function. If it does not, a v8 exception is thrown
+ // and the method returns false. Otherwise returns true.
+ bool CheckContextAccessToExtensionAPI(const std::string& function_name,
+ ScriptContext* context) const;
+
+ // Dispatches the event named |event_name| to all render views.
+ void DispatchEvent(const std::string& extension_id,
+ const std::string& event_name) const;
+
+ // Shared implementation of the various MessageInvoke IPCs.
+ void InvokeModuleSystemMethod(content::RenderView* render_view,
+ const std::string& extension_id,
+ const std::string& module_name,
+ const std::string& function_name,
+ const base::ListValue& args,
+ bool user_gesture);
+
+ void ClearPortData(int port_id);
+
+ private:
+ friend class ::ChromeRenderViewTest;
+ FRIEND_TEST_ALL_PREFIXES(RendererPermissionsPolicyDelegateTest,
+ CannotScriptWebstore);
+
+ // RenderProcessObserver implementation:
+ virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void WebKitInitialized() OVERRIDE;
+ virtual void IdleNotification() OVERRIDE;
+ virtual void OnRenderProcessShutdown() OVERRIDE;
+
+ void OnActivateExtension(const std::string& extension_id);
+ void OnCancelSuspend(const std::string& extension_id);
+ void OnClearTabSpecificPermissions(
+ int tab_id,
+ const std::vector<std::string>& extension_ids);
+ void OnDeliverMessage(int target_port_id, const Message& message);
+ void OnDispatchOnConnect(int target_port_id,
+ const std::string& channel_name,
+ const base::DictionaryValue& source_tab,
+ const ExtensionMsg_ExternalConnectionInfo& info,
+ const std::string& tls_channel_id);
+ void OnDispatchOnDisconnect(int port_id, const std::string& error_message);
+ void OnLoaded(
+ const std::vector<ExtensionMsg_Loaded_Params>& loaded_extensions);
+ void OnLoadedInternal(scoped_refptr<const Extension> extension);
+ void OnMessageInvoke(const std::string& extension_id,
+ const std::string& module_name,
+ const std::string& function_name,
+ const base::ListValue& args,
+ bool user_gesture);
+ void OnSetChannel(int channel);
+ void OnSetFunctionNames(const std::vector<std::string>& names);
+ void OnSetScriptingWhitelist(
+ const ExtensionsClient::ScriptingWhitelist& extension_ids);
+ void OnSetSystemFont(const std::string& font_family,
+ const std::string& font_size);
+ void OnShouldSuspend(const std::string& extension_id, int sequence_id);
+ void OnSuspend(const std::string& extension_id);
+ void OnUnloaded(const std::string& id);
+ void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params);
+ void OnUpdateTabSpecificPermissions(int page_id,
+ int tab_id,
+ const std::string& extension_id,
+ const URLPatternSet& origin_set);
+ void OnUpdateUserScripts(base::SharedMemoryHandle scripts);
+ void OnUsingWebRequestAPI(bool adblock,
+ bool adblock_plus,
+ bool other_webrequest);
+
+ void UpdateActiveExtensions();
+
+ // Sets up the host permissions for |extension|.
+ void InitOriginPermissions(const Extension* extension,
+ Feature::Context context_type);
+ void UpdateOriginPermissions(UpdatedExtensionPermissionsInfo::Reason reason,
+ const Extension* extension,
+ const URLPatternSet& origins);
+
+ // Enable custom element whitelist in Apps.
+ void EnableCustomElementWhiteList();
+
+ // Adds or removes bindings for every context belonging to |extension_id|, or
+ // or all contexts if |extension_id| is empty.
+ void UpdateBindings(const std::string& extension_id);
+
+ void UpdateBindingsForContext(ScriptContext* context);
+
+ void RegisterBinding(const std::string& api_name, ScriptContext* context);
+
+ void RegisterNativeHandlers(ModuleSystem* module_system,
+ ScriptContext* context);
+
+ // Inserts static source code into |source_map_|.
+ void PopulateSourceMap();
+
+ // Returns whether the current renderer hosts a platform app.
+ bool IsWithinPlatformApp();
+
+ bool IsSandboxedPage(const GURL& url) const;
+
+ // Returns the Feature::Context type of context for a JavaScript context.
+ Feature::Context ClassifyJavaScriptContext(
+ const Extension* extension,
+ int extension_group,
+ const GURL& url,
+ const blink::WebSecurityOrigin& origin);
+
+ // Gets |field| from |object| or creates it as an empty object if it doesn't
+ // exist.
+ v8::Handle<v8::Object> GetOrCreateObject(const v8::Handle<v8::Object>& object,
+ const std::string& field,
+ v8::Isolate* isolate);
+
+ v8::Handle<v8::Object> GetOrCreateBindObjectIfAvailable(
+ const std::string& api_name,
+ std::string* bind_name,
+ ScriptContext* context);
+
+ // The delegate for this dispatcher. Not owned, but must extend beyond the
+ // Dispatcher's own lifetime.
+ DispatcherDelegate* delegate_;
+
+ // True if this renderer is running extensions.
+ bool is_extension_process_;
+
+ // Contains all loaded extensions. This is essentially the renderer
+ // counterpart to ExtensionService in the browser. It contains information
+ // about all extensions currently loaded by the browser.
+ ExtensionSet extensions_;
+
+ // The IDs of extensions that failed to load, mapped to the error message
+ // generated on failure.
+ std::map<std::string, std::string> extension_load_errors_;
+
+ // All the bindings contexts that are currently loaded for this renderer.
+ // There is zero or one for each v8 context.
+ ScriptContextSet script_context_set_;
+
+ scoped_ptr<ContentWatcher> content_watcher_;
+
+ scoped_ptr<UserScriptSlave> user_script_slave_;
+
+ // Same as above, but on a longer timer and will run even if the process is
+ // not idle, to ensure that IdleHandle gets called eventually.
+ scoped_ptr<base::RepeatingTimer<content::RenderThread> > forced_idle_timer_;
+
+ // All declared function names.
+ std::set<std::string> function_names_;
+
+ // The extensions and apps that are active in this process.
+ std::set<std::string> active_extension_ids_;
+
+ ResourceBundleSourceMap source_map_;
+
+ // Cache for the v8 representation of extension API schemas.
+ scoped_ptr<V8SchemaRegistry> v8_schema_registry_;
+
+ // Sends API requests to the extension host.
+ scoped_ptr<RequestSender> request_sender_;
+
+ // The platforms system font family and size;
+ std::string system_font_family_;
+ std::string system_font_size_;
+
+ // Mapping of port IDs to tabs. If there is no tab, the value would be -1.
+ std::map<int, int> port_to_tab_id_map_;
+
+ // True once WebKit has been initialized (and it is therefore safe to poke).
+ bool is_webkit_initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(Dispatcher);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_DISPATCHER_H_
diff --git a/extensions/renderer/dispatcher_delegate.h b/extensions/renderer/dispatcher_delegate.h
new file mode 100644
index 0000000..13d557f
--- /dev/null
+++ b/extensions/renderer/dispatcher_delegate.h
@@ -0,0 +1,96 @@
+// Copyright 2014 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_RENDERER_DISPATCHER_DELEGATE_H
+#define EXTENSIONS_RENDERER_DISPATCHER_DELEGATE_H
+
+#include <set>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "extensions/common/features/feature.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+class WebFrame;
+}
+
+namespace extensions {
+class Dispatcher;
+class Extension;
+class ModuleSystem;
+class ResourceBundleSourceMap;
+class ScriptContext;
+class URLPatternSet;
+
+// Base class and default implementation for an extensions::Dispacher delegate.
+// DispatcherDelegate can be used to override and extend the behavior of the
+// extensions system's renderer side.
+class DispatcherDelegate {
+ public:
+ virtual ~DispatcherDelegate() {}
+
+ // Creates a new ScriptContext for a given v8 context.
+ virtual scoped_ptr<ScriptContext> CreateScriptContext(
+ const v8::Handle<v8::Context>& v8_context,
+ blink::WebFrame* frame,
+ const Extension* extension,
+ Feature::Context context_type) = 0;
+
+ // Initializes origin permissions for a newly created extension context.
+ virtual void InitOriginPermissions(const Extension* extension,
+ Feature::Context context_type) {}
+
+ // Includes additional native handlers in a given ModuleSystem.
+ virtual void RegisterNativeHandlers(Dispatcher* dispatcher,
+ ModuleSystem* module_system,
+ ScriptContext* context) {}
+
+ // Includes additional source resources into the resource map.
+ virtual void PopulateSourceMap(ResourceBundleSourceMap* source_map) {}
+
+ // Requires additional modules within an extension context's module system.
+ virtual void RequireAdditionalModules(ModuleSystem* module_system,
+ const Extension* extension,
+ Feature::Context context_type,
+ bool is_within_platform_app) {}
+
+ // Allows the delegate to respond to an updated set of active extensions in
+ // the Dispatcher.
+ virtual void OnActiveExtensionsUpdated(
+ const std::set<std::string>& extension_ids) {}
+
+ // Sets the current Chrome channel.
+ // TODO(rockot): This doesn't belong in a generic extensions system interface.
+ // See http://crbug.com/368431.
+ virtual void SetChannel(int channel) {}
+
+ // Clears extension permissions specific to a given tab.
+ // TODO(rockot): This doesn't belong in a generic extensions system interface.
+ // See http://crbug.com/368431.
+ virtual void ClearTabSpecificPermissions(
+ const extensions::Dispatcher* dispatcher,
+ int tab_id,
+ const std::vector<std::string>& extension_ids) {}
+
+ // Updates extension permissions specific to a given tab.
+ // TODO(rockot): This doesn't belong in a generic extensions system interface.
+ // See http://crbug.com/368431.
+ virtual void UpdateTabSpecificPermissions(
+ const extensions::Dispatcher* dispatcher,
+ int page_id,
+ int tab_id,
+ const std::string& extension_id,
+ const extensions::URLPatternSet& origin_set) {}
+
+ // Allows the delegate to respond to reports from the browser about WebRequest
+ // API usage from within this process.
+ virtual void HandleWebRequestAPIUsage(bool adblock,
+ bool adblock_plus,
+ bool other_webrequest) {}
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_DISPATCHER_DELEGATE_H
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index 9446595..0ebd08e 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -13,7 +13,6 @@
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
-#include "chrome/renderer/extensions/dispatcher.h"
#include "chrome/renderer/extensions/extension_helper.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
@@ -23,6 +22,7 @@
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/value_counter.h"
+#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/object_backed_native_handler.h"
#include "url/gurl.h"
#include "v8/include/v8.h"
diff --git a/extensions/renderer/extensions_renderer_client.h b/extensions/renderer/extensions_renderer_client.h
index 6b06b6c..ae4a31b 100644
--- a/extensions/renderer/extensions_renderer_client.h
+++ b/extensions/renderer/extensions_renderer_client.h
@@ -9,9 +9,6 @@ class ResourceBundleSourceMap;
namespace extensions {
-class ModuleSystem;
-class ScriptContext;
-
// Interface to allow the extensions module to make render-process-specific
// queries of the embedder. Should be Set() once in the render process.
//
@@ -30,13 +27,6 @@ class ExtensionsRendererClient {
// (third_party/WebKit/public/web/WebFrame.h) for additional context.
virtual int GetLowestIsolatedWorldId() const = 0;
- // Registers additional native C++ code handlers for JS API functions.
- virtual void RegisterNativeHandlers(ModuleSystem* module_system,
- ScriptContext* context) = 0;
-
- // Registers additional JS source code resources for API implementations.
- virtual void PopulateSourceMap(ResourceBundleSourceMap* source_map) = 0;
-
// Returns the single instance of |this|.
static ExtensionsRendererClient* Get();
diff --git a/extensions/renderer/lazy_background_page_native_handler.cc b/extensions/renderer/lazy_background_page_native_handler.cc
new file mode 100644
index 0000000..b7c72cd
--- /dev/null
+++ b/extensions/renderer/lazy_background_page_native_handler.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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/renderer/lazy_background_page_native_handler.h"
+
+#include "base/bind.h"
+#include "chrome/renderer/extensions/extension_helper.h"
+#include "content/public/renderer/render_view.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/manifest_handlers/background_info.h"
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+LazyBackgroundPageNativeHandler::LazyBackgroundPageNativeHandler(
+ ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction(
+ "IncrementKeepaliveCount",
+ base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount,
+ base::Unretained(this)));
+ RouteFunction(
+ "DecrementKeepaliveCount",
+ base::Bind(&LazyBackgroundPageNativeHandler::DecrementKeepaliveCount,
+ base::Unretained(this)));
+}
+
+void LazyBackgroundPageNativeHandler::IncrementKeepaliveCount(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (!context())
+ return;
+ content::RenderView* render_view = context()->GetRenderView();
+ if (IsContextLazyBackgroundPage(render_view, context()->extension())) {
+ render_view->Send(new ExtensionHostMsg_IncrementLazyKeepaliveCount(
+ render_view->GetRoutingID()));
+ }
+}
+
+void LazyBackgroundPageNativeHandler::DecrementKeepaliveCount(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (!context())
+ return;
+ content::RenderView* render_view = context()->GetRenderView();
+ if (IsContextLazyBackgroundPage(render_view, context()->extension())) {
+ render_view->Send(new ExtensionHostMsg_DecrementLazyKeepaliveCount(
+ render_view->GetRoutingID()));
+ }
+}
+
+bool LazyBackgroundPageNativeHandler::IsContextLazyBackgroundPage(
+ content::RenderView* render_view,
+ const Extension* extension) {
+ if (!render_view)
+ return false;
+
+ ExtensionHelper* helper = ExtensionHelper::Get(render_view);
+ return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) &&
+ helper->view_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/lazy_background_page_native_handler.h b/extensions/renderer/lazy_background_page_native_handler.h
new file mode 100644
index 0000000..5ee43b2
--- /dev/null
+++ b/extensions/renderer/lazy_background_page_native_handler.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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_RENDERER_LAZY_BACKGROUND_PAGE_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_LAZY_BACKGROUND_PAGE_NATIVE_HANDLER_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace content {
+class RenderView;
+}
+
+namespace extensions {
+
+class Extension;
+
+class LazyBackgroundPageNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ explicit LazyBackgroundPageNativeHandler(ScriptContext* context);
+ void IncrementKeepaliveCount(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void DecrementKeepaliveCount(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ bool IsContextLazyBackgroundPage(content::RenderView* render_view,
+ const Extension* extension);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_LAZY_BACKGROUND_PAGE_NATIVE_HANDLER_H_
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
new file mode 100644
index 0000000..281f276
--- /dev/null
+++ b/extensions/renderer/messaging_bindings.cc
@@ -0,0 +1,441 @@
+// Copyright 2014 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/renderer/messaging_bindings.h"
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop/message_loop.h"
+#include "base/values.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "extensions/common/api/messaging/message.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/manifest_handlers/externally_connectable.h"
+#include "extensions/renderer/dispatcher.h"
+#include "extensions/renderer/event_bindings.h"
+#include "extensions/renderer/object_backed_native_handler.h"
+#include "extensions/renderer/scoped_persistent.h"
+#include "extensions/renderer/script_context.h"
+#include "extensions/renderer/script_context_set.h"
+#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
+#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
+#include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h"
+#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
+#include "third_party/WebKit/public/web/WebUserGestureToken.h"
+#include "v8/include/v8.h"
+
+// Message passing API example (in a content script):
+// var extension =
+// new chrome.Extension('00123456789abcdef0123456789abcdef0123456');
+// var port = runtime.connect();
+// port.postMessage('Can you hear me now?');
+// port.onmessage.addListener(function(msg, port) {
+// alert('response=' + msg);
+// port.postMessage('I got your reponse');
+// });
+
+using content::RenderThread;
+using content::V8ValueConverter;
+
+namespace extensions {
+
+namespace {
+
+struct ExtensionData {
+ struct PortData {
+ int ref_count; // how many contexts have a handle to this port
+ PortData() : ref_count(0) {}
+ };
+ std::map<int, PortData> ports; // port ID -> data
+};
+
+base::LazyInstance<ExtensionData> g_extension_data = LAZY_INSTANCE_INITIALIZER;
+
+bool HasPortData(int port_id) {
+ return g_extension_data.Get().ports.find(port_id) !=
+ g_extension_data.Get().ports.end();
+}
+
+ExtensionData::PortData& GetPortData(int port_id) {
+ return g_extension_data.Get().ports[port_id];
+}
+
+void ClearPortData(int port_id) {
+ g_extension_data.Get().ports.erase(port_id);
+}
+
+const char kPortClosedError[] = "Attempting to use a disconnected port object";
+const char kReceivingEndDoesntExistError[] =
+ "Could not establish connection. Receiving end does not exist.";
+
+class ExtensionImpl : public ObjectBackedNativeHandler {
+ public:
+ ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context)
+ : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) {
+ RouteFunction(
+ "CloseChannel",
+ base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this)));
+ RouteFunction(
+ "PortAddRef",
+ base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this)));
+ RouteFunction(
+ "PortRelease",
+ base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this)));
+ RouteFunction(
+ "PostMessage",
+ base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this)));
+ // TODO(fsamuel, kalman): Move BindToGC out of messaging natives.
+ RouteFunction("BindToGC",
+ base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this)));
+ }
+
+ virtual ~ExtensionImpl() {}
+
+ private:
+ void ClearPortDataAndNotifyDispatcher(int port_id) {
+ ClearPortData(port_id);
+ dispatcher_->ClearPortData(port_id);
+ }
+
+ bool ShouldForwardUserGesture() {
+ return blink::WebUserGestureIndicator::isProcessingUserGesture() &&
+ !blink::WebUserGestureIndicator::currentUserGestureToken()
+ .wasForwarded();
+ }
+
+ // Sends a message along the given channel.
+ void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ content::RenderView* renderview = context()->GetRenderView();
+ if (!renderview)
+ return;
+
+ // Arguments are (int32 port_id, string message).
+ CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString());
+
+ int port_id = args[0]->Int32Value();
+ if (!HasPortData(port_id)) {
+ args.GetIsolate()->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(args.GetIsolate(), kPortClosedError)));
+ return;
+ }
+
+ renderview->Send(new ExtensionHostMsg_PostMessage(
+ renderview->GetRoutingID(),
+ port_id,
+ Message(*v8::String::Utf8Value(args[1]), ShouldForwardUserGesture())));
+ }
+
+ // Forcefully disconnects a port.
+ void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Arguments are (int32 port_id, boolean notify_browser).
+ CHECK_EQ(2, args.Length());
+ CHECK(args[0]->IsInt32());
+ CHECK(args[1]->IsBoolean());
+
+ int port_id = args[0]->Int32Value();
+ if (!HasPortData(port_id))
+ return;
+
+ // Send via the RenderThread because the RenderView might be closing.
+ bool notify_browser = args[1]->BooleanValue();
+ if (notify_browser) {
+ content::RenderThread::Get()->Send(
+ new ExtensionHostMsg_CloseChannel(port_id, std::string()));
+ }
+
+ ClearPortDataAndNotifyDispatcher(port_id);
+ }
+
+ // A new port has been created for a context. This occurs both when script
+ // opens a connection, and when a connection is opened to this script.
+ void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Arguments are (int32 port_id).
+ CHECK_EQ(1, args.Length());
+ CHECK(args[0]->IsInt32());
+
+ int port_id = args[0]->Int32Value();
+ ++GetPortData(port_id).ref_count;
+ }
+
+ // The frame a port lived in has been destroyed. When there are no more
+ // frames with a reference to a given port, we will disconnect it and notify
+ // the other end of the channel.
+ void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Arguments are (int32 port_id).
+ CHECK_EQ(1, args.Length());
+ CHECK(args[0]->IsInt32());
+
+ int port_id = args[0]->Int32Value();
+ if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) {
+ // Send via the RenderThread because the RenderView might be closing.
+ content::RenderThread::Get()->Send(
+ new ExtensionHostMsg_CloseChannel(port_id, std::string()));
+ ClearPortDataAndNotifyDispatcher(port_id);
+ }
+ }
+
+ // Holds a |callback| to run sometime after |object| is GC'ed. |callback| will
+ // not be executed re-entrantly to avoid running JS in an unexpected state.
+ class GCCallback {
+ public:
+ static void Bind(v8::Handle<v8::Object> object,
+ v8::Handle<v8::Function> callback,
+ v8::Isolate* isolate) {
+ GCCallback* cb = new GCCallback(object, callback, isolate);
+ cb->object_.SetWeak(cb, NearDeathCallback);
+ }
+
+ private:
+ static void NearDeathCallback(
+ const v8::WeakCallbackData<v8::Object, GCCallback>& data) {
+ // v8 says we need to explicitly reset weak handles from their callbacks.
+ // It's not implicit as one might expect.
+ data.GetParameter()->object_.reset();
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&GCCallback::RunCallback,
+ base::Owned(data.GetParameter())));
+ }
+
+ GCCallback(v8::Handle<v8::Object> object,
+ v8::Handle<v8::Function> callback,
+ v8::Isolate* isolate)
+ : object_(object), callback_(callback), isolate_(isolate) {}
+
+ void RunCallback() {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Handle<v8::Function> callback = callback_.NewHandle(isolate_);
+ v8::Handle<v8::Context> context = callback->CreationContext();
+ if (context.IsEmpty())
+ return;
+ v8::Context::Scope context_scope(context);
+ blink::WebScopedMicrotaskSuppression suppression;
+ callback->Call(context->Global(), 0, NULL);
+ }
+
+ ScopedPersistent<v8::Object> object_;
+ ScopedPersistent<v8::Function> callback_;
+ v8::Isolate* isolate_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCCallback);
+ };
+
+ // void BindToGC(object, callback)
+ //
+ // Binds |callback| to be invoked *sometime after* |object| is garbage
+ // collected. We don't call the method re-entrantly so as to avoid executing
+ // JS in some bizarro undefined mid-GC state.
+ void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK(args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction());
+ GCCallback::Bind(args[0].As<v8::Object>(),
+ args[1].As<v8::Function>(),
+ args.GetIsolate());
+ }
+
+ // Dispatcher handle. Not owned.
+ Dispatcher* dispatcher_;
+};
+
+} // namespace
+
+ObjectBackedNativeHandler* MessagingBindings::Get(Dispatcher* dispatcher,
+ ScriptContext* context) {
+ return new ExtensionImpl(dispatcher, context);
+}
+
+// static
+void MessagingBindings::DispatchOnConnect(
+ const ScriptContextSet::ContextSet& contexts,
+ int target_port_id,
+ const std::string& channel_name,
+ const base::DictionaryValue& source_tab,
+ const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const GURL& source_url,
+ const std::string& tls_channel_id,
+ content::RenderView* restrict_to_render_view) {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::HandleScope handle_scope(isolate);
+
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+
+ bool port_created = false;
+ std::string source_url_spec = source_url.spec();
+
+ // TODO(kalman): pass in the full ScriptContextSet; call ForEach.
+ for (ScriptContextSet::ContextSet::const_iterator it = contexts.begin();
+ it != contexts.end();
+ ++it) {
+ if (restrict_to_render_view &&
+ restrict_to_render_view != (*it)->GetRenderView()) {
+ continue;
+ }
+
+ // TODO(kalman): remove when ContextSet::ForEach is available.
+ if ((*it)->v8_context().IsEmpty())
+ continue;
+
+ v8::Handle<v8::Value> tab = v8::Null(isolate);
+ v8::Handle<v8::Value> tls_channel_id_value = v8::Undefined(isolate);
+ const Extension* extension = (*it)->extension();
+ if (extension) {
+ if (!source_tab.empty() && !extension->is_platform_app())
+ tab = converter->ToV8Value(&source_tab, (*it)->v8_context());
+
+ ExternallyConnectableInfo* externally_connectable =
+ ExternallyConnectableInfo::Get(extension);
+ if (externally_connectable &&
+ externally_connectable->accepts_tls_channel_id) {
+ tls_channel_id_value =
+ v8::String::NewFromUtf8(isolate,
+ tls_channel_id.c_str(),
+ v8::String::kNormalString,
+ tls_channel_id.size());
+ }
+ }
+
+ v8::Handle<v8::Value> arguments[] = {
+ // portId
+ v8::Integer::New(isolate, target_port_id),
+ // channelName
+ v8::String::NewFromUtf8(isolate,
+ channel_name.c_str(),
+ v8::String::kNormalString,
+ channel_name.size()),
+ // sourceTab
+ tab,
+ // sourceExtensionId
+ v8::String::NewFromUtf8(isolate,
+ source_extension_id.c_str(),
+ v8::String::kNormalString,
+ source_extension_id.size()),
+ // targetExtensionId
+ v8::String::NewFromUtf8(isolate,
+ target_extension_id.c_str(),
+ v8::String::kNormalString,
+ target_extension_id.size()),
+ // sourceUrl
+ v8::String::NewFromUtf8(isolate,
+ source_url_spec.c_str(),
+ v8::String::kNormalString,
+ source_url_spec.size()),
+ // tlsChannelId
+ tls_channel_id_value,
+ };
+
+ v8::Handle<v8::Value> retval = (*it)->module_system()->CallModuleMethod(
+ "messaging", "dispatchOnConnect", arraysize(arguments), arguments);
+
+ if (retval.IsEmpty()) {
+ LOG(ERROR) << "Empty return value from dispatchOnConnect.";
+ continue;
+ }
+
+ CHECK(retval->IsBoolean());
+ port_created |= retval->BooleanValue();
+ }
+
+ // If we didn't create a port, notify the other end of the channel (treat it
+ // as a disconnect).
+ if (!port_created) {
+ content::RenderThread::Get()->Send(new ExtensionHostMsg_CloseChannel(
+ target_port_id, kReceivingEndDoesntExistError));
+ }
+}
+
+// static
+void MessagingBindings::DeliverMessage(
+ const ScriptContextSet::ContextSet& contexts,
+ int target_port_id,
+ const Message& message,
+ content::RenderView* restrict_to_render_view) {
+ scoped_ptr<blink::WebScopedUserGesture> web_user_gesture;
+ scoped_ptr<blink::WebScopedWindowFocusAllowedIndicator> allow_window_focus;
+ if (message.user_gesture) {
+ web_user_gesture.reset(new blink::WebScopedUserGesture);
+ blink::WebUserGestureIndicator::currentUserGestureToken().setForwarded();
+ allow_window_focus.reset(new blink::WebScopedWindowFocusAllowedIndicator);
+ }
+
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::HandleScope handle_scope(isolate);
+
+ // TODO(kalman): pass in the full ScriptContextSet; call ForEach.
+ for (ScriptContextSet::ContextSet::const_iterator it = contexts.begin();
+ it != contexts.end();
+ ++it) {
+ if (restrict_to_render_view &&
+ restrict_to_render_view != (*it)->GetRenderView()) {
+ continue;
+ }
+
+ // TODO(kalman): remove when ContextSet::ForEach is available.
+ if ((*it)->v8_context().IsEmpty())
+ continue;
+
+ // Check to see whether the context has this port before bothering to create
+ // the message.
+ v8::Handle<v8::Value> port_id_handle =
+ v8::Integer::New(isolate, target_port_id);
+ v8::Handle<v8::Value> has_port = (*it)->module_system()->CallModuleMethod(
+ "messaging", "hasPort", 1, &port_id_handle);
+
+ CHECK(!has_port.IsEmpty());
+ if (!has_port->BooleanValue())
+ continue;
+
+ std::vector<v8::Handle<v8::Value> > arguments;
+ arguments.push_back(v8::String::NewFromUtf8(isolate,
+ message.data.c_str(),
+ v8::String::kNormalString,
+ message.data.size()));
+ arguments.push_back(port_id_handle);
+ (*it)->module_system()->CallModuleMethod(
+ "messaging", "dispatchOnMessage", &arguments);
+ }
+}
+
+// static
+void MessagingBindings::DispatchOnDisconnect(
+ const ScriptContextSet::ContextSet& contexts,
+ int port_id,
+ const std::string& error_message,
+ content::RenderView* restrict_to_render_view) {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::HandleScope handle_scope(isolate);
+
+ // TODO(kalman): pass in the full ScriptContextSet; call ForEach.
+ for (ScriptContextSet::ContextSet::const_iterator it = contexts.begin();
+ it != contexts.end();
+ ++it) {
+ if (restrict_to_render_view &&
+ restrict_to_render_view != (*it)->GetRenderView()) {
+ continue;
+ }
+
+ // TODO(kalman): remove when ContextSet::ForEach is available.
+ if ((*it)->v8_context().IsEmpty())
+ continue;
+
+ std::vector<v8::Handle<v8::Value> > arguments;
+ arguments.push_back(v8::Integer::New(isolate, port_id));
+ if (!error_message.empty()) {
+ arguments.push_back(
+ v8::String::NewFromUtf8(isolate, error_message.c_str()));
+ } else {
+ arguments.push_back(v8::Null(isolate));
+ }
+ (*it)->module_system()->CallModuleMethod(
+ "messaging", "dispatchOnDisconnect", &arguments);
+ }
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/messaging_bindings.h b/extensions/renderer/messaging_bindings.h
new file mode 100644
index 0000000..721b434
--- /dev/null
+++ b/extensions/renderer/messaging_bindings.h
@@ -0,0 +1,71 @@
+// Copyright 2014 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_RENDERER_MESSAGING_BINDINGS_H_
+#define EXTENSIONS_RENDERER_MESSAGING_BINDINGS_H_
+
+#include <string>
+
+#include "extensions/renderer/script_context_set.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace content {
+class RenderView;
+}
+
+namespace v8 {
+class Extension;
+}
+
+namespace extensions {
+class Dispatcher;
+struct Message;
+class ObjectBackedNativeHandler;
+
+// Manually implements JavaScript bindings for extension messaging.
+//
+// TODO(aa): This should all get re-implemented using SchemaGeneratedBindings.
+// If anything needs to be manual for some reason, it should be implemented in
+// its own class.
+class MessagingBindings {
+ public:
+ // Creates an instance of the extension.
+ static ObjectBackedNativeHandler* Get(Dispatcher* dispatcher,
+ ScriptContext* context);
+
+ // Dispatches the onConnect content script messaging event to some contexts
+ // in |contexts|. If |restrict_to_render_view| is specified, only contexts in
+ // that render view will receive the message.
+ static void DispatchOnConnect(const ScriptContextSet::ContextSet& contexts,
+ int target_port_id,
+ const std::string& channel_name,
+ const base::DictionaryValue& source_tab,
+ const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const GURL& source_url,
+ const std::string& tls_channel_id,
+ content::RenderView* restrict_to_render_view);
+
+ // Delivers a message sent using content script messaging to some of the
+ // contexts in |bindings_context_set|. If |restrict_to_render_view| is
+ // specified, only contexts in that render view will receive the message.
+ static void DeliverMessage(const ScriptContextSet::ContextSet& context_set,
+ int target_port_id,
+ const Message& message,
+ content::RenderView* restrict_to_render_view);
+
+ // Dispatches the onDisconnect event in response to the channel being closed.
+ static void DispatchOnDisconnect(
+ const ScriptContextSet::ContextSet& context_set,
+ int port_id,
+ const std::string& error_message,
+ content::RenderView* restrict_to_render_view);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_MESSAGING_BINDINGS_H_
diff --git a/extensions/renderer/print_native_handler.cc b/extensions/renderer/print_native_handler.cc
new file mode 100644
index 0000000..1e83b0e
--- /dev/null
+++ b/extensions/renderer/print_native_handler.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 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/renderer/print_native_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+
+namespace extensions {
+
+PrintNativeHandler::PrintNativeHandler(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction("Print",
+ base::Bind(&PrintNativeHandler::Print, base::Unretained(this)));
+}
+
+void PrintNativeHandler::Print(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() < 1)
+ return;
+
+ std::vector<std::string> components;
+ for (int i = 0; i < args.Length(); ++i)
+ components.push_back(*v8::String::Utf8Value(args[i]->ToString()));
+
+ LOG(ERROR) << JoinString(components, ',');
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/print_native_handler.h b/extensions/renderer/print_native_handler.h
new file mode 100644
index 0000000..b2e5d37
--- /dev/null
+++ b/extensions/renderer/print_native_handler.h
@@ -0,0 +1,21 @@
+// Copyright 2014 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_RENDERER_PRINT_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_PRINT_NATIVE_HANDLER_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+class PrintNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ explicit PrintNativeHandler(ScriptContext* context);
+
+ void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_PRINT_NATIVE_HANDLER_H_
diff --git a/extensions/renderer/process_info_native_handler.cc b/extensions/renderer/process_info_native_handler.cc
new file mode 100644
index 0000000..3c7ba78
--- /dev/null
+++ b/extensions/renderer/process_info_native_handler.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 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/renderer/process_info_native_handler.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+ProcessInfoNativeHandler::ProcessInfoNativeHandler(
+ ScriptContext* context,
+ const std::string& extension_id,
+ const std::string& context_type,
+ bool is_incognito_context,
+ int manifest_version,
+ bool send_request_disabled)
+ : ObjectBackedNativeHandler(context),
+ extension_id_(extension_id),
+ context_type_(context_type),
+ is_incognito_context_(is_incognito_context),
+ manifest_version_(manifest_version),
+ send_request_disabled_(send_request_disabled) {
+ RouteFunction("GetExtensionId",
+ base::Bind(&ProcessInfoNativeHandler::GetExtensionId,
+ base::Unretained(this)));
+ RouteFunction("GetContextType",
+ base::Bind(&ProcessInfoNativeHandler::GetContextType,
+ base::Unretained(this)));
+ RouteFunction("InIncognitoContext",
+ base::Bind(&ProcessInfoNativeHandler::InIncognitoContext,
+ base::Unretained(this)));
+ RouteFunction("GetManifestVersion",
+ base::Bind(&ProcessInfoNativeHandler::GetManifestVersion,
+ base::Unretained(this)));
+ RouteFunction("IsSendRequestDisabled",
+ base::Bind(&ProcessInfoNativeHandler::IsSendRequestDisabled,
+ base::Unretained(this)));
+ RouteFunction(
+ "HasSwitch",
+ base::Bind(&ProcessInfoNativeHandler::HasSwitch, base::Unretained(this)));
+}
+
+void ProcessInfoNativeHandler::GetExtensionId(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(
+ v8::String::NewFromUtf8(args.GetIsolate(), extension_id_.c_str()));
+}
+
+void ProcessInfoNativeHandler::GetContextType(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(
+ v8::String::NewFromUtf8(args.GetIsolate(), context_type_.c_str()));
+}
+
+void ProcessInfoNativeHandler::InIncognitoContext(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(is_incognito_context_);
+}
+
+void ProcessInfoNativeHandler::GetManifestVersion(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(static_cast<int32_t>(manifest_version_));
+}
+
+void ProcessInfoNativeHandler::IsSendRequestDisabled(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (send_request_disabled_) {
+ args.GetReturnValue().Set(v8::String::NewFromUtf8(
+ args.GetIsolate(),
+ "sendRequest and onRequest are obsolete."
+ " Please use sendMessage and onMessage instead."));
+ }
+}
+
+void ProcessInfoNativeHandler::HasSwitch(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK(args.Length() == 1 && args[0]->IsString());
+ bool has_switch = CommandLine::ForCurrentProcess()->HasSwitch(
+ *v8::String::Utf8Value(args[0]));
+ args.GetReturnValue().Set(v8::Boolean::New(args.GetIsolate(), has_switch));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/process_info_native_handler.h b/extensions/renderer/process_info_native_handler.h
new file mode 100644
index 0000000..1f25269
--- /dev/null
+++ b/extensions/renderer/process_info_native_handler.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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_RENDERER_PROCESS_INFO_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_PROCESS_INFO_NATIVE_HANDLER_H_
+
+#include <string>
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+class ProcessInfoNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ ProcessInfoNativeHandler(ScriptContext* context,
+ const std::string& extension_id,
+ const std::string& context_type,
+ bool is_incognito_context,
+ int manifest_version,
+ bool send_request_disabled);
+
+ private:
+ void GetExtensionId(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void GetContextType(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void InIncognitoContext(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void GetManifestVersion(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void IsSendRequestDisabled(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void HasSwitch(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ std::string extension_id_;
+ std::string context_type_;
+ bool is_incognito_context_;
+ int manifest_version_;
+ bool send_request_disabled_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_PROCESS_INFO_NATIVE_HANDLER_H_
diff --git a/extensions/renderer/request_sender.cc b/extensions/renderer/request_sender.cc
index 09ffa8f..60c77c6 100644
--- a/extensions/renderer/request_sender.cc
+++ b/extensions/renderer/request_sender.cc
@@ -5,9 +5,9 @@
#include "extensions/renderer/request_sender.h"
#include "base/values.h"
-#include "chrome/renderer/extensions/dispatcher.h"
#include "content/public/renderer/render_view.h"
#include "extensions/common/extension_messages.h"
+#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
diff --git a/extensions/renderer/resource_bundle_source_map.cc b/extensions/renderer/resource_bundle_source_map.cc
new file mode 100644
index 0000000..954a6d7
--- /dev/null
+++ b/extensions/renderer/resource_bundle_source_map.cc
@@ -0,0 +1,47 @@
+// Copyright 2014 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/renderer/resource_bundle_source_map.h"
+
+#include "ui/base/resource/resource_bundle.h"
+
+namespace extensions {
+
+ResourceBundleSourceMap::ResourceBundleSourceMap(
+ const ui::ResourceBundle* resource_bundle)
+ : resource_bundle_(resource_bundle) {
+}
+
+ResourceBundleSourceMap::~ResourceBundleSourceMap() {
+}
+
+void ResourceBundleSourceMap::RegisterSource(const std::string& name,
+ int resource_id) {
+ resource_id_map_[name] = resource_id;
+}
+
+v8::Handle<v8::Value> ResourceBundleSourceMap::GetSource(
+ v8::Isolate* isolate,
+ const std::string& name) {
+ if (!Contains(name))
+ return v8::Undefined(isolate);
+ int resource_id = resource_id_map_[name];
+ return ConvertString(isolate,
+ resource_bundle_->GetRawDataResource(resource_id));
+}
+
+bool ResourceBundleSourceMap::Contains(const std::string& name) {
+ return !!resource_id_map_.count(name);
+}
+
+v8::Handle<v8::String> ResourceBundleSourceMap::ConvertString(
+ v8::Isolate* isolate,
+ const base::StringPiece& string) {
+ // v8 takes ownership of the StaticV8ExternalAsciiStringResource (see
+ // v8::String::NewExternal()).
+ return v8::String::NewExternal(
+ isolate, new StaticV8ExternalAsciiStringResource(string));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/resource_bundle_source_map.h b/extensions/renderer/resource_bundle_source_map.h
new file mode 100644
index 0000000..89e8b1d
--- /dev/null
+++ b/extensions/renderer/resource_bundle_source_map.h
@@ -0,0 +1,45 @@
+// Copyright 2014 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_RENDERER_RESOURCE_BUNDLE_SOURCE_MAP_H_
+#define EXTENSIONS_RENDERER_RESOURCE_BUNDLE_SOURCE_MAP_H_
+
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/linked_ptr.h"
+#include "base/strings/string_piece.h"
+#include "extensions/renderer/module_system.h"
+#include "extensions/renderer/static_v8_external_ascii_string_resource.h"
+#include "v8/include/v8.h"
+
+namespace ui {
+class ResourceBundle;
+}
+
+namespace extensions {
+
+class ResourceBundleSourceMap : public extensions::ModuleSystem::SourceMap {
+ public:
+ explicit ResourceBundleSourceMap(const ui::ResourceBundle* resource_bundle);
+ virtual ~ResourceBundleSourceMap();
+
+ virtual v8::Handle<v8::Value> GetSource(v8::Isolate* isolate,
+ const std::string& name) OVERRIDE;
+ virtual bool Contains(const std::string& name) OVERRIDE;
+
+ void RegisterSource(const std::string& name, int resource_id);
+
+ private:
+ v8::Handle<v8::String> ConvertString(v8::Isolate* isolate,
+ const base::StringPiece& string);
+
+ const ui::ResourceBundle* resource_bundle_;
+ std::map<std::string, int> resource_id_map_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_RESOURCE_BUNDLE_SOURCE_MAP_H_
diff --git a/extensions/renderer/runtime_custom_bindings.cc b/extensions/renderer/runtime_custom_bindings.cc
new file mode 100644
index 0000000..7ed2ad2
--- /dev/null
+++ b/extensions/renderer/runtime_custom_bindings.cc
@@ -0,0 +1,177 @@
+// Copyright 2014 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/renderer/runtime_custom_bindings.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/renderer/extensions/extension_helper.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/features/feature.h"
+#include "extensions/common/features/feature_provider.h"
+#include "extensions/common/manifest.h"
+#include "extensions/renderer/api_activity_logger.h"
+#include "extensions/renderer/script_context.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebView.h"
+
+using content::V8ValueConverter;
+
+namespace extensions {
+
+RuntimeCustomBindings::RuntimeCustomBindings(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction(
+ "GetManifest",
+ base::Bind(&RuntimeCustomBindings::GetManifest, base::Unretained(this)));
+ RouteFunction("OpenChannelToExtension",
+ base::Bind(&RuntimeCustomBindings::OpenChannelToExtension,
+ base::Unretained(this)));
+ RouteFunction("OpenChannelToNativeApp",
+ base::Bind(&RuntimeCustomBindings::OpenChannelToNativeApp,
+ base::Unretained(this)));
+ RouteFunction("GetExtensionViews",
+ base::Bind(&RuntimeCustomBindings::GetExtensionViews,
+ base::Unretained(this)));
+}
+
+RuntimeCustomBindings::~RuntimeCustomBindings() {
+}
+
+void RuntimeCustomBindings::OpenChannelToExtension(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Get the current RenderView so that we can send a routed IPC message from
+ // the correct source.
+ content::RenderView* renderview = context()->GetRenderView();
+ if (!renderview)
+ return;
+
+ // The Javascript code should validate/fill the arguments.
+ CHECK_EQ(args.Length(), 3);
+ CHECK(args[0]->IsString() && args[1]->IsString() && args[2]->IsBoolean());
+
+ ExtensionMsg_ExternalConnectionInfo info;
+
+ // For messaging APIs, hosted apps should be considered a web page so hide
+ // its extension ID.
+ const Extension* extension = context()->extension();
+ if (extension && !extension->is_hosted_app())
+ info.source_id = extension->id();
+
+ info.target_id = *v8::String::Utf8Value(args[0]->ToString());
+ info.source_url = context()->GetURL();
+ std::string channel_name = *v8::String::Utf8Value(args[1]->ToString());
+ bool include_tls_channel_id =
+ args.Length() > 2 ? args[2]->BooleanValue() : false;
+ int port_id = -1;
+ renderview->Send(
+ new ExtensionHostMsg_OpenChannelToExtension(renderview->GetRoutingID(),
+ info,
+ channel_name,
+ include_tls_channel_id,
+ &port_id));
+ args.GetReturnValue().Set(static_cast<int32_t>(port_id));
+}
+
+void RuntimeCustomBindings::OpenChannelToNativeApp(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Verify that the extension has permission to use native messaging.
+ Feature::Availability availability =
+ FeatureProvider::GetPermissionFeatures()
+ ->GetFeature("nativeMessaging")
+ ->IsAvailableToContext(context()->extension(),
+ context()->context_type(),
+ context()->GetURL());
+ if (!availability.is_available())
+ return;
+
+ // Get the current RenderView so that we can send a routed IPC message from
+ // the correct source.
+ content::RenderView* renderview = context()->GetRenderView();
+ if (!renderview)
+ return;
+
+ // The Javascript code should validate/fill the arguments.
+ CHECK(args.Length() >= 2 && args[0]->IsString() && args[1]->IsString());
+
+ std::string extension_id = *v8::String::Utf8Value(args[0]->ToString());
+ std::string native_app_name = *v8::String::Utf8Value(args[1]->ToString());
+
+ int port_id = -1;
+ renderview->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
+ renderview->GetRoutingID(), extension_id, native_app_name, &port_id));
+ args.GetReturnValue().Set(static_cast<int32_t>(port_id));
+}
+
+void RuntimeCustomBindings::GetManifest(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK(context()->extension());
+
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+ args.GetReturnValue().Set(converter->ToV8Value(
+ context()->extension()->manifest()->value(), context()->v8_context()));
+}
+
+void RuntimeCustomBindings::GetExtensionViews(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2)
+ return;
+
+ if (!args[0]->IsInt32() || !args[1]->IsString())
+ return;
+
+ // |browser_window_id| == extension_misc::kUnknownWindowId means getting
+ // all views for the current extension.
+ int browser_window_id = args[0]->Int32Value();
+
+ std::string view_type_string = *v8::String::Utf8Value(args[1]->ToString());
+ StringToUpperASCII(&view_type_string);
+ // |view_type| == VIEW_TYPE_INVALID means getting any type of
+ // views.
+ ViewType view_type = VIEW_TYPE_INVALID;
+ if (view_type_string == kViewTypeBackgroundPage) {
+ view_type = VIEW_TYPE_EXTENSION_BACKGROUND_PAGE;
+ } else if (view_type_string == kViewTypeInfobar) {
+ view_type = VIEW_TYPE_EXTENSION_INFOBAR;
+ } else if (view_type_string == kViewTypeTabContents) {
+ view_type = VIEW_TYPE_TAB_CONTENTS;
+ } else if (view_type_string == kViewTypePopup) {
+ view_type = VIEW_TYPE_EXTENSION_POPUP;
+ } else if (view_type_string == kViewTypeExtensionDialog) {
+ view_type = VIEW_TYPE_EXTENSION_DIALOG;
+ } else if (view_type_string == kViewTypeAppWindow) {
+ view_type = VIEW_TYPE_APP_WINDOW;
+ } else if (view_type_string == kViewTypePanel) {
+ view_type = VIEW_TYPE_PANEL;
+ } else if (view_type_string != kViewTypeAll) {
+ return;
+ }
+
+ std::string extension_id = context()->GetExtensionID();
+ if (extension_id.empty())
+ return;
+
+ std::vector<content::RenderView*> views = ExtensionHelper::GetExtensionViews(
+ extension_id, browser_window_id, view_type);
+ v8::Local<v8::Array> v8_views = v8::Array::New(args.GetIsolate());
+ int v8_index = 0;
+ for (size_t i = 0; i < views.size(); ++i) {
+ v8::Local<v8::Context> context =
+ views[i]->GetWebView()->mainFrame()->mainWorldScriptContext();
+ if (!context.IsEmpty()) {
+ v8::Local<v8::Value> window = context->Global();
+ DCHECK(!window.IsEmpty());
+ v8_views->Set(v8::Integer::New(args.GetIsolate(), v8_index++), window);
+ }
+ }
+
+ args.GetReturnValue().Set(v8_views);
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/runtime_custom_bindings.h b/extensions/renderer/runtime_custom_bindings.h
new file mode 100644
index 0000000..70f4d03
--- /dev/null
+++ b/extensions/renderer/runtime_custom_bindings.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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_RENDERER_RUNTIME_CUSTOM_BINDINGS_H_
+#define EXTENSIONS_RENDERER_RUNTIME_CUSTOM_BINDINGS_H_
+
+#include "base/compiler_specific.h"
+#include "extensions/renderer/object_backed_native_handler.h"
+#include "v8/include/v8.h"
+
+namespace extensions {
+
+// The native component of custom bindings for the chrome.runtime API.
+class RuntimeCustomBindings : public ObjectBackedNativeHandler {
+ public:
+ explicit RuntimeCustomBindings(ScriptContext* context);
+
+ virtual ~RuntimeCustomBindings();
+
+ // Creates a new messaging channel to the given extension.
+ void OpenChannelToExtension(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Creates a new messaging channels for the specified native application.
+ void OpenChannelToNativeApp(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ void GetManifest(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void GetExtensionViews(const v8::FunctionCallbackInfo<v8::Value>& args);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_RUNTIME_CUSTOM_BINDINGS_H_
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 77f72aa..33a9413 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -24,7 +24,7 @@ using content::V8ValueConverter;
namespace extensions {
-ScriptContext::ScriptContext(v8::Handle<v8::Context> v8_context,
+ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
blink::WebFrame* web_frame,
const Extension* extension,
Feature::Context context_type)
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index c2cbb04..5cc137e 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -30,7 +30,7 @@ class Extension;
// Extensions wrapper for a v8 context.
class ScriptContext : public RequestSender::Source {
public:
- ScriptContext(v8::Handle<v8::Context> context,
+ ScriptContext(const v8::Handle<v8::Context>& context,
blink::WebFrame* frame,
const Extension* extension,
Feature::Context context_type);
diff --git a/extensions/renderer/static_v8_external_ascii_string_resource.cc b/extensions/renderer/static_v8_external_ascii_string_resource.cc
new file mode 100644
index 0000000..5d64d2e
--- /dev/null
+++ b/extensions/renderer/static_v8_external_ascii_string_resource.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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/renderer/static_v8_external_ascii_string_resource.h"
+
+namespace extensions {
+
+StaticV8ExternalAsciiStringResource::StaticV8ExternalAsciiStringResource(
+ const base::StringPiece& buffer)
+ : buffer_(buffer) {
+}
+
+StaticV8ExternalAsciiStringResource::~StaticV8ExternalAsciiStringResource() {
+}
+
+const char* StaticV8ExternalAsciiStringResource::data() const {
+ return buffer_.data();
+}
+
+size_t StaticV8ExternalAsciiStringResource::length() const {
+ return buffer_.length();
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/static_v8_external_ascii_string_resource.h b/extensions/renderer/static_v8_external_ascii_string_resource.h
new file mode 100644
index 0000000..da02650
--- /dev/null
+++ b/extensions/renderer/static_v8_external_ascii_string_resource.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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_RENDERER_STATIC_V8_EXTERNAL_ASCII_STRING_RESOURCE_H_
+#define EXTENSIONS_RENDERER_STATIC_V8_EXTERNAL_ASCII_STRING_RESOURCE_H_
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+#include "v8/include/v8.h"
+
+namespace extensions {
+
+// A very simple implementation of v8::ExternalAsciiStringResource that just
+// wraps a buffer. The buffer must outlive the v8 runtime instance this resource
+// is used in.
+class StaticV8ExternalAsciiStringResource
+ : public v8::String::ExternalAsciiStringResource {
+ public:
+ explicit StaticV8ExternalAsciiStringResource(const base::StringPiece& buffer);
+ virtual ~StaticV8ExternalAsciiStringResource();
+
+ virtual const char* data() const OVERRIDE;
+ virtual size_t length() const OVERRIDE;
+
+ private:
+ base::StringPiece buffer_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_STATIC_V8_EXTERNAL_ASCII_STRING_RESOURCE_H_
diff --git a/extensions/renderer/test_extensions_renderer_client.cc b/extensions/renderer/test_extensions_renderer_client.cc
index 4d1c021..5299878 100644
--- a/extensions/renderer/test_extensions_renderer_client.cc
+++ b/extensions/renderer/test_extensions_renderer_client.cc
@@ -19,13 +19,4 @@ int TestExtensionsRendererClient::GetLowestIsolatedWorldId() const {
return 1;
}
-void TestExtensionsRendererClient::RegisterNativeHandlers(
- extensions::ModuleSystem* module_system,
- extensions::ScriptContext* context) {
-}
-
-void TestExtensionsRendererClient::PopulateSourceMap(
- ResourceBundleSourceMap* source_map) {
-}
-
} // namespace extensions
diff --git a/extensions/renderer/test_extensions_renderer_client.h b/extensions/renderer/test_extensions_renderer_client.h
index a71d1bb..72550a9 100644
--- a/extensions/renderer/test_extensions_renderer_client.h
+++ b/extensions/renderer/test_extensions_renderer_client.h
@@ -18,9 +18,6 @@ class TestExtensionsRendererClient : public ExtensionsRendererClient {
// ExtensionsRendererClient implementation.
virtual bool IsIncognitoProcess() const OVERRIDE;
virtual int GetLowestIsolatedWorldId() const OVERRIDE;
- virtual void RegisterNativeHandlers(ModuleSystem* module_system,
- ScriptContext* context) OVERRIDE;
- virtual void PopulateSourceMap(ResourceBundleSourceMap* source_map) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(TestExtensionsRendererClient);
diff --git a/extensions/renderer/test_features_native_handler.cc b/extensions/renderer/test_features_native_handler.cc
new file mode 100644
index 0000000..6e0423d
--- /dev/null
+++ b/extensions/renderer/test_features_native_handler.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 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/renderer/test_features_native_handler.h"
+
+#include "base/bind.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "extensions/common/features/json_feature_provider_source.h"
+#include "extensions/renderer/script_context.h"
+#include "grit/common_resources.h"
+#include "grit/extensions_resources.h"
+
+namespace extensions {
+
+TestFeaturesNativeHandler::TestFeaturesNativeHandler(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction("GetAPIFeatures",
+ base::Bind(&TestFeaturesNativeHandler::GetAPIFeatures,
+ base::Unretained(this)));
+}
+
+void TestFeaturesNativeHandler::GetAPIFeatures(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ JSONFeatureProviderSource source("api");
+ // TODO(rockot): Only inlcude extensions features here. Chrome should add
+ // its own native handler for Chrome features.
+ source.LoadJSON(IDR_CHROME_EXTENSION_API_FEATURES);
+ source.LoadJSON(IDR_EXTENSION_API_FEATURES);
+ scoped_ptr<content::V8ValueConverter> converter(
+ content::V8ValueConverter::create());
+ args.GetReturnValue().Set(
+ converter->ToV8Value(&source.dictionary(), context()->v8_context()));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/test_features_native_handler.h b/extensions/renderer/test_features_native_handler.h
new file mode 100644
index 0000000..2422178
--- /dev/null
+++ b/extensions/renderer/test_features_native_handler.h
@@ -0,0 +1,22 @@
+// Copyright 2014 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_RENDERER_TEST_FEATURES_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_TEST_FEATURES_NATIVE_HANDLER_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+class TestFeaturesNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ explicit TestFeaturesNativeHandler(ScriptContext* context);
+
+ private:
+ void GetAPIFeatures(const v8::FunctionCallbackInfo<v8::Value>& args);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_TEST_FEATURES_NATIVE_HANDLER_H_
diff --git a/extensions/renderer/user_gestures_native_handler.cc b/extensions/renderer/user_gestures_native_handler.cc
new file mode 100644
index 0000000..9875ac0
--- /dev/null
+++ b/extensions/renderer/user_gestures_native_handler.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 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/renderer/user_gestures_native_handler.h"
+
+#include "base/bind.h"
+#include "extensions/renderer/script_context.h"
+#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
+#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
+
+namespace extensions {
+
+UserGesturesNativeHandler::UserGesturesNativeHandler(ScriptContext* context)
+ : ObjectBackedNativeHandler(context) {
+ RouteFunction("IsProcessingUserGesture",
+ base::Bind(&UserGesturesNativeHandler::IsProcessingUserGesture,
+ base::Unretained(this)));
+ RouteFunction("RunWithUserGesture",
+ base::Bind(&UserGesturesNativeHandler::RunWithUserGesture,
+ base::Unretained(this)));
+ RouteFunction("RunWithoutUserGesture",
+ base::Bind(&UserGesturesNativeHandler::RunWithoutUserGesture,
+ base::Unretained(this)));
+}
+
+void UserGesturesNativeHandler::IsProcessingUserGesture(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(v8::Boolean::New(
+ args.GetIsolate(),
+ blink::WebUserGestureIndicator::isProcessingUserGesture()));
+}
+
+void UserGesturesNativeHandler::RunWithUserGesture(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ blink::WebScopedUserGesture user_gesture;
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsFunction());
+ v8::Handle<v8::Value> no_args;
+ context()->CallFunction(v8::Handle<v8::Function>::Cast(args[0]), 0, &no_args);
+}
+
+void UserGesturesNativeHandler::RunWithoutUserGesture(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ blink::WebUserGestureIndicator::consumeUserGesture();
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsFunction());
+ v8::Handle<v8::Value> no_args;
+ context()->CallFunction(v8::Handle<v8::Function>::Cast(args[0]), 0, &no_args);
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/user_gestures_native_handler.h b/extensions/renderer/user_gestures_native_handler.h
new file mode 100644
index 0000000..fbe15fb
--- /dev/null
+++ b/extensions/renderer/user_gestures_native_handler.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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_RENDERER_USER_GESTURES_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_USER_GESTURES_NATIVE_HANDLER_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+class UserGesturesNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ explicit UserGesturesNativeHandler(ScriptContext* context);
+
+ private:
+ void IsProcessingUserGesture(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void RunWithUserGesture(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void RunWithoutUserGesture(const v8::FunctionCallbackInfo<v8::Value>& args);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_USER_GESTURES_NATIVE_HANDLER_H_
diff --git a/extensions/renderer/user_script_slave.cc b/extensions/renderer/user_script_slave.cc
new file mode 100644
index 0000000..64cd3f6
--- /dev/null
+++ b/extensions/renderer/user_script_slave.cc
@@ -0,0 +1,314 @@
+// Copyright 2014 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/renderer/user_script_slave.h"
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "base/metrics/histogram.h"
+#include "base/pickle.h"
+#include "base/strings/stringprintf.h"
+#include "base/timer/elapsed_timer.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/manifest_handlers/csp_info.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/renderer/dom_activity_logger.h"
+#include "extensions/renderer/extension_groups.h"
+#include "extensions/renderer/extensions_renderer_client.h"
+#include "extensions/renderer/script_context.h"
+#include "grit/renderer_resources.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "url/gurl.h"
+
+using blink::WebFrame;
+using blink::WebSecurityOrigin;
+using blink::WebSecurityPolicy;
+using blink::WebString;
+using blink::WebVector;
+using blink::WebView;
+using content::RenderThread;
+
+namespace extensions {
+
+// These two strings are injected before and after the Greasemonkey API and
+// user script to wrap it in an anonymous scope.
+static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
+static const char kUserScriptTail[] = "\n})(window);";
+
+int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension,
+ WebFrame* frame) {
+ static int g_next_isolated_world_id =
+ ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
+
+ IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id());
+ if (iter != isolated_world_ids_.end()) {
+ // We need to set the isolated world origin and CSP even if it's not a new
+ // world since these are stored per frame, and we might not have used this
+ // isolated world in this frame before.
+ frame->setIsolatedWorldSecurityOrigin(
+ iter->second, WebSecurityOrigin::create(extension->url()));
+ frame->setIsolatedWorldContentSecurityPolicy(
+ iter->second,
+ WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
+ return iter->second;
+ }
+
+ int new_id = g_next_isolated_world_id;
+ ++g_next_isolated_world_id;
+
+ // This map will tend to pile up over time, but realistically, you're never
+ // going to have enough extensions for it to matter.
+ isolated_world_ids_[extension->id()] = new_id;
+ frame->setIsolatedWorldSecurityOrigin(
+ new_id, WebSecurityOrigin::create(extension->url()));
+ frame->setIsolatedWorldContentSecurityPolicy(
+ new_id,
+ WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
+ return new_id;
+}
+
+std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
+ int isolated_world_id) {
+ for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
+ iter != isolated_world_ids_.end();
+ ++iter) {
+ if (iter->second == isolated_world_id)
+ return iter->first;
+ }
+ return std::string();
+}
+
+void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
+ isolated_world_ids_.erase(extension_id);
+}
+
+UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
+ : script_deleter_(&scripts_), extensions_(extensions) {
+ api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_GREASEMONKEY_API_JS);
+}
+
+UserScriptSlave::~UserScriptSlave() {
+}
+
+void UserScriptSlave::GetActiveExtensions(
+ std::set<std::string>* extension_ids) {
+ for (size_t i = 0; i < scripts_.size(); ++i) {
+ DCHECK(!scripts_[i]->extension_id().empty());
+ extension_ids->insert(scripts_[i]->extension_id());
+ }
+}
+
+bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
+ scripts_.clear();
+
+ bool only_inject_incognito =
+ ExtensionsRendererClient::Get()->IsIncognitoProcess();
+
+ // Create the shared memory object (read only).
+ shared_memory_.reset(new base::SharedMemory(shared_memory, true));
+ if (!shared_memory_.get())
+ return false;
+
+ // First get the size of the memory block.
+ if (!shared_memory_->Map(sizeof(Pickle::Header)))
+ return false;
+ Pickle::Header* pickle_header =
+ reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
+
+ // Now map in the rest of the block.
+ int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
+ shared_memory_->Unmap();
+ if (!shared_memory_->Map(pickle_size))
+ return false;
+
+ // Unpickle scripts.
+ uint64 num_scripts = 0;
+ Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size);
+ PickleIterator iter(pickle);
+ CHECK(pickle.ReadUInt64(&iter, &num_scripts));
+
+ scripts_.reserve(num_scripts);
+ for (uint64 i = 0; i < num_scripts; ++i) {
+ scripts_.push_back(new UserScript());
+ UserScript* script = scripts_.back();
+ script->Unpickle(pickle, &iter);
+
+ // Note that this is a pointer into shared memory. We don't own it. It gets
+ // cleared up when the last renderer or browser process drops their
+ // reference to the shared memory.
+ for (size_t j = 0; j < script->js_scripts().size(); ++j) {
+ const char* body = NULL;
+ int body_length = 0;
+ CHECK(pickle.ReadData(&iter, &body, &body_length));
+ script->js_scripts()[j].set_external_content(
+ base::StringPiece(body, body_length));
+ }
+ for (size_t j = 0; j < script->css_scripts().size(); ++j) {
+ const char* body = NULL;
+ int body_length = 0;
+ CHECK(pickle.ReadData(&iter, &body, &body_length));
+ script->css_scripts()[j].set_external_content(
+ base::StringPiece(body, body_length));
+ }
+
+ if (only_inject_incognito && !script->is_incognito_enabled()) {
+ // This script shouldn't run in an incognito tab.
+ delete script;
+ scripts_.pop_back();
+ }
+ }
+
+ return true;
+}
+
+void UserScriptSlave::InjectScripts(WebFrame* frame,
+ UserScript::RunLocation location) {
+ GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame);
+ if (data_source_url.is_empty())
+ return;
+
+ if (frame->isViewSourceModeEnabled())
+ data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
+ data_source_url.spec());
+
+ base::ElapsedTimer timer;
+ int num_css = 0;
+ int num_scripts = 0;
+
+ ExecutingScriptsMap extensions_executing_scripts;
+
+ for (size_t i = 0; i < scripts_.size(); ++i) {
+ std::vector<WebScriptSource> sources;
+ UserScript* script = scripts_[i];
+
+ if (frame->parent() && !script->match_all_frames())
+ continue; // Only match subframes if the script declared it wanted to.
+
+ const Extension* extension = extensions_->GetByID(script->extension_id());
+
+ // Since extension info is sent separately from user script info, they can
+ // be out of sync. We just ignore this situation.
+ if (!extension)
+ continue;
+
+ // Content scripts are not tab-specific.
+ const int kNoTabId = -1;
+ // We don't have a process id in this context.
+ const int kNoProcessId = -1;
+ if (!PermissionsData::CanExecuteScriptOnPage(extension,
+ data_source_url,
+ frame->top()->document().url(),
+ kNoTabId,
+ script,
+ kNoProcessId,
+ NULL)) {
+ continue;
+ }
+
+ if (location == UserScript::DOCUMENT_START) {
+ num_css += script->css_scripts().size();
+ for (UserScript::FileList::const_iterator iter =
+ script->css_scripts().begin();
+ iter != script->css_scripts().end();
+ ++iter) {
+ frame->document().insertStyleSheet(
+ WebString::fromUTF8(iter->GetContent().as_string()));
+ }
+ }
+
+ if (script->run_location() == location) {
+ num_scripts += script->js_scripts().size();
+ for (size_t j = 0; j < script->js_scripts().size(); ++j) {
+ UserScript::File& file = script->js_scripts()[j];
+ std::string content = file.GetContent().as_string();
+
+ // We add this dumb function wrapper for standalone user script to
+ // emulate what Greasemonkey does.
+ // TODO(aa): I think that maybe "is_standalone" scripts don't exist
+ // anymore. Investigate.
+ if (script->is_standalone() || script->emulate_greasemonkey()) {
+ content.insert(0, kUserScriptHead);
+ content += kUserScriptTail;
+ }
+ sources.push_back(
+ WebScriptSource(WebString::fromUTF8(content), file.url()));
+ }
+ }
+
+ if (!sources.empty()) {
+ // Emulate Greasemonkey API for scripts that were converted to extensions
+ // and "standalone" user scripts.
+ if (script->is_standalone() || script->emulate_greasemonkey()) {
+ sources.insert(
+ sources.begin(),
+ WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
+ }
+
+ int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
+
+ base::ElapsedTimer exec_timer;
+ DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id());
+ frame->executeScriptInIsolatedWorld(isolated_world_id,
+ &sources.front(),
+ sources.size(),
+ EXTENSION_GROUP_CONTENT_SCRIPTS);
+ UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
+
+ for (std::vector<WebScriptSource>::const_iterator iter = sources.begin();
+ iter != sources.end();
+ ++iter) {
+ extensions_executing_scripts[extension->id()].insert(
+ GURL(iter->url).path());
+ }
+ }
+ }
+
+ // Notify the browser if any extensions are now executing scripts.
+ if (!extensions_executing_scripts.empty()) {
+ blink::WebFrame* top_frame = frame->top();
+ content::RenderView* render_view =
+ content::RenderView::FromWebView(top_frame->view());
+ render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
+ render_view->GetRoutingID(),
+ extensions_executing_scripts,
+ render_view->GetPageId(),
+ ScriptContext::GetDataSourceURLForFrame(top_frame)));
+ }
+
+ // Log debug info.
+ if (location == UserScript::DOCUMENT_START) {
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts);
+ if (num_css || num_scripts)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed());
+ } else if (location == UserScript::DOCUMENT_END) {
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts);
+ if (num_scripts)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed());
+ } else if (location == UserScript::DOCUMENT_IDLE) {
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts);
+ if (num_scripts)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed());
+ } else {
+ NOTREACHED();
+ }
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/user_script_slave.h b/extensions/renderer/user_script_slave.h
new file mode 100644
index 0000000..fc3da79
--- /dev/null
+++ b/extensions/renderer/user_script_slave.h
@@ -0,0 +1,84 @@
+// Copyright 2014 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_RENDERER_USER_SCRIPT_SLAVE_H_
+#define EXTENSIONS_RENDERER_USER_SCRIPT_SLAVE_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "extensions/common/user_script.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+
+class GURL;
+
+namespace blink {
+class WebFrame;
+}
+
+using blink::WebScriptSource;
+
+namespace extensions {
+class Extension;
+class ExtensionSet;
+
+// Manages installed UserScripts for a render process.
+class UserScriptSlave {
+ public:
+ explicit UserScriptSlave(const ExtensionSet* extensions);
+ ~UserScriptSlave();
+
+ // Returns the unique set of extension IDs this UserScriptSlave knows about.
+ void GetActiveExtensions(std::set<std::string>* extension_ids);
+
+ // Update the parsed scripts from shared memory.
+ bool UpdateScripts(base::SharedMemoryHandle shared_memory);
+
+ // Inject the appropriate scripts into a frame based on its URL.
+ // TODO(aa): Extract a UserScriptFrame interface out of this to improve
+ // testability.
+ void InjectScripts(blink::WebFrame* frame, UserScript::RunLocation location);
+
+ // Gets the isolated world ID to use for the given |extension| in the given
+ // |frame|. If no isolated world has been created for that extension,
+ // one will be created and initialized.
+ int GetIsolatedWorldIdForExtension(const Extension* extension,
+ blink::WebFrame* frame);
+
+ // Gets the id of the extension running in a given isolated world. If no such
+ // isolated world exists, or no extension is running in it, returns empty
+ // string.
+ std::string GetExtensionIdForIsolatedWorld(int isolated_world_id);
+
+ void RemoveIsolatedWorld(const std::string& extension_id);
+
+ private:
+ // Shared memory containing raw script data.
+ scoped_ptr<base::SharedMemory> shared_memory_;
+
+ // Parsed script data.
+ std::vector<UserScript*> scripts_;
+ STLElementDeleter<std::vector<UserScript*> > script_deleter_;
+
+ // Greasemonkey API source that is injected with the scripts.
+ base::StringPiece api_js_;
+
+ // Extension metadata.
+ const ExtensionSet* extensions_;
+
+ typedef std::map<std::string, int> IsolatedWorldMap;
+ IsolatedWorldMap isolated_world_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserScriptSlave);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_USER_SCRIPT_SLAVE_H_
diff --git a/extensions/renderer/v8_context_native_handler.cc b/extensions/renderer/v8_context_native_handler.cc
new file mode 100644
index 0000000..56cd973
--- /dev/null
+++ b/extensions/renderer/v8_context_native_handler.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 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/renderer/v8_context_native_handler.h"
+
+#include "base/bind.h"
+#include "extensions/common/features/feature.h"
+#include "extensions/renderer/dispatcher.h"
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+V8ContextNativeHandler::V8ContextNativeHandler(ScriptContext* context,
+ Dispatcher* dispatcher)
+ : ObjectBackedNativeHandler(context),
+ context_(context),
+ dispatcher_(dispatcher) {
+ RouteFunction("GetAvailability",
+ base::Bind(&V8ContextNativeHandler::GetAvailability,
+ base::Unretained(this)));
+ RouteFunction("GetModuleSystem",
+ base::Bind(&V8ContextNativeHandler::GetModuleSystem,
+ base::Unretained(this)));
+}
+
+void V8ContextNativeHandler::GetAvailability(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK_EQ(args.Length(), 1);
+ v8::Isolate* isolate = args.GetIsolate();
+ std::string api_name = *v8::String::Utf8Value(args[0]->ToString());
+ Feature::Availability availability = context_->GetAvailability(api_name);
+
+ v8::Handle<v8::Object> ret = v8::Object::New(isolate);
+ ret->Set(v8::String::NewFromUtf8(isolate, "is_available"),
+ v8::Boolean::New(isolate, availability.is_available()));
+ ret->Set(v8::String::NewFromUtf8(isolate, "message"),
+ v8::String::NewFromUtf8(isolate, availability.message().c_str()));
+ ret->Set(v8::String::NewFromUtf8(isolate, "result"),
+ v8::Integer::New(isolate, availability.result()));
+ args.GetReturnValue().Set(ret);
+}
+
+void V8ContextNativeHandler::GetModuleSystem(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsObject());
+ v8::Handle<v8::Context> v8_context =
+ v8::Handle<v8::Object>::Cast(args[0])->CreationContext();
+ ScriptContext* context =
+ dispatcher_->script_context_set().GetByV8Context(v8_context);
+ args.GetReturnValue().Set(context->module_system()->NewInstance());
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/v8_context_native_handler.h b/extensions/renderer/v8_context_native_handler.h
new file mode 100644
index 0000000..a3a6163
--- /dev/null
+++ b/extensions/renderer/v8_context_native_handler.h
@@ -0,0 +1,28 @@
+// Copyright 2014 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_RENDERER_V8_CONTEXT_NATIVE_HANDLER_H_
+#define EXTENSIONS_RENDERER_V8_CONTEXT_NATIVE_HANDLER_H_
+
+#include "extensions/renderer/object_backed_native_handler.h"
+
+namespace extensions {
+
+class Dispatcher;
+
+class V8ContextNativeHandler : public ObjectBackedNativeHandler {
+ public:
+ V8ContextNativeHandler(ScriptContext* context, Dispatcher* dispatcher);
+
+ private:
+ void GetAvailability(const v8::FunctionCallbackInfo<v8::Value>& args);
+ void GetModuleSystem(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ ScriptContext* context_;
+ Dispatcher* dispatcher_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_V8_CONTEXT_NATIVE_HANDLER_H_