summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/download/download_manager.cc44
-rw-r--r--chrome/browser/extensions/convert_user_script.cc138
-rw-r--r--chrome/browser/extensions/convert_user_script.h23
-rw-r--r--chrome/browser/extensions/convert_user_script_unittest.cc88
-rw-r--r--chrome/browser/extensions/crx_installer.cc75
-rw-r--r--chrome/browser/extensions/crx_installer.h37
-rw-r--r--chrome/browser/extensions/user_script_master.cc18
-rw-r--r--chrome/browser/extensions/user_script_master.h2
-rw-r--r--chrome/browser/extensions/user_script_master_unittest.cc4
-rw-r--r--chrome/browser/renderer_host/buffered_resource_handler.cc5
-rwxr-xr-xchrome/chrome.gyp3
-rw-r--r--chrome/common/extensions/extension.cc52
-rw-r--r--chrome/common/extensions/extension.h16
-rw-r--r--chrome/common/extensions/extension_constants.cc7
-rw-r--r--chrome/common/extensions/extension_constants.h5
-rw-r--r--chrome/common/extensions/extension_error_utils.cc18
-rw-r--r--chrome/common/extensions/extension_error_utils.h16
-rw-r--r--chrome/common/extensions/user_script.cc75
-rw-r--r--chrome/common/extensions/user_script.h55
-rw-r--r--chrome/common/extensions/user_script_unittest.cc34
-rw-r--r--chrome/renderer/resources/greasemonkey_api.js9
-rw-r--r--chrome/renderer/user_script_slave.cc28
-rw-r--r--chrome/renderer/user_script_slave.h5
-rwxr-xr-xchrome/test/data/extensions/user_script_basic.user.js11
-rwxr-xr-xchrome/test/data/extensions/user_script_no_metadata.user.js1
25 files changed, 668 insertions, 101 deletions
diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc
index eef8b04..bb0a280 100644
--- a/chrome/browser/download/download_manager.cc
+++ b/chrome/browser/download/download_manager.cc
@@ -34,6 +34,7 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/user_script.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/platform_util.h"
@@ -1243,14 +1244,25 @@ void DownloadManager::OpenChromeExtension(const FilePath& full_path,
nservice->Notify(NotificationType::EXTENSION_READY_FOR_INSTALL,
Source<DownloadManager>(this),
NotificationService::NoDetails());
- CrxInstaller::Start(full_path,
- service->install_directory(),
- Extension::INTERNAL,
- "", // no expected id
- true, // please delete crx on completion
- true, // privilege increase allowed
- service,
- new ExtensionInstallUI(profile_));
+ if (UserScript::HasUserScriptFileExtension(full_path)) {
+ CrxInstaller::InstallUserScript(
+ full_path,
+ download_url,
+ service->install_directory(),
+ true, // please delete crx on completion
+ service,
+ new ExtensionInstallUI(profile_));
+ } else {
+ CrxInstaller::Start(
+ full_path,
+ service->install_directory(),
+ Extension::INTERNAL,
+ "", // no expected id
+ true, // please delete crx on completion
+ true, // privilege increase allowed
+ service,
+ new ExtensionInstallUI(profile_));
+ }
}
}
@@ -1453,11 +1465,23 @@ void DownloadManager::GenerateSafeFilename(const std::string& mime_type,
}
bool DownloadManager::IsExtensionInstall(const DownloadItem* item) {
- return item->mime_type() == Extension::kMimeType && !item->save_as();
+ if (item->save_as())
+ return false;
+
+ if (UserScript::HasUserScriptFileExtension(item->original_name()))
+ return true;
+
+ return item->mime_type() == Extension::kMimeType;
}
bool DownloadManager::IsExtensionInstall(const DownloadCreateInfo* info) {
- return info->mime_type == Extension::kMimeType && !info->save_as;
+ if (info->save_as)
+ return false;
+
+ if (UserScript::HasUserScriptFileExtension(info->path))
+ return true;
+
+ return info->mime_type == Extension::kMimeType;
}
// Operations posted to us from the history service ----------------------------
diff --git a/chrome/browser/extensions/convert_user_script.cc b/chrome/browser/extensions/convert_user_script.cc
new file mode 100644
index 0000000..70de6d9
--- /dev/null
+++ b/chrome/browser/extensions/convert_user_script.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/convert_user_script.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/scoped_temp_dir.h"
+#include "base/sha2.h"
+#include "base/string_util.h"
+#include "chrome/browser/extensions/user_script_master.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/user_script.h"
+#include "chrome/common/json_value_serializer.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/base64.h"
+
+namespace keys = extension_manifest_keys;
+
+Extension* ConvertUserScriptToExtension(const FilePath& user_script_path,
+ const GURL& original_url,
+ std::string* error){
+ std::string content;
+ if (!file_util::ReadFileToString(user_script_path, &content)) {
+ *error = "Could not read source file: " +
+ WideToASCII(user_script_path.ToWStringHack());
+ return NULL;
+ }
+
+ UserScript script;
+ if (!UserScriptMaster::ScriptReloader::ParseMetadataHeader(content,
+ &script)) {
+ *error = "Invalid script header.";
+ return NULL;
+ }
+
+ ScopedTempDir temp_dir;
+ if (!temp_dir.CreateUniqueTempDir()) {
+ *error = "Could not create temporary directory.";
+ return NULL;
+ }
+
+ // Create the manifest
+ scoped_ptr<DictionaryValue> root(new DictionaryValue);
+ std::string script_name;
+ if (!script.name().empty() && !script.name_space().empty())
+ script_name = script.name_space() + "/" + script.name();
+ else
+ script_name = original_url.spec();
+
+ // Create the public key.
+ // User scripts are not signed, but the public key for an extension doubles as
+ // its unique identity, and we need one of those. A user script's unique
+ // identity is its namespace+name, so we hash that to create a public key.
+ // There will be no corresponding private key, which means user scripts cannot
+ // be auto-updated, or claimed in the gallery.
+ char raw[base::SHA256_LENGTH] = {0};
+ std::string key;
+ base::SHA256HashString(script_name, raw, base::SHA256_LENGTH);
+ net::Base64Encode(std::string(raw, base::SHA256_LENGTH), &key);
+
+ // The script may not have a name field, but we need one for an extension. If
+ // it is missing, use the filename of the original URL.
+ if (!script.name().empty())
+ root->SetString(keys::kName, script.name());
+ else
+ root->SetString(keys::kName, original_url.ExtractFileName());
+
+ root->SetString(keys::kDescription, script.description());
+ root->SetString(keys::kVersion, "1.0");
+ root->SetString(keys::kPublicKey, key);
+ root->SetBoolean(keys::kConvertedFromUserScript, true);
+
+ ListValue* js_files = new ListValue();
+ js_files->Append(Value::CreateStringValue("script.js"));
+
+ // If the script provides its own match patterns, we use those. Otherwise, we
+ // generate some using the include globs.
+ ListValue* matches = new ListValue();
+ if (!script.url_patterns().empty()) {
+ for (size_t i = 0; i < script.url_patterns().size(); ++i) {
+ matches->Append(Value::CreateStringValue(
+ script.url_patterns()[i].GetAsString()));
+ }
+ } else {
+ // TODO(aa): Derive tighter matches where possible.
+ matches->Append(Value::CreateStringValue("http://*/*"));
+ matches->Append(Value::CreateStringValue("https://*/*"));
+ }
+
+ ListValue* includes = new ListValue();
+ for (size_t i = 0; i < script.globs().size(); ++i)
+ includes->Append(Value::CreateStringValue(script.globs().at(i)));
+
+ ListValue* excludes = new ListValue();
+ for (size_t i = 0; i < script.exclude_globs().size(); ++i)
+ excludes->Append(Value::CreateStringValue(script.exclude_globs().at(i)));
+
+ DictionaryValue* content_script = new DictionaryValue();
+ content_script->Set(keys::kMatches, matches);
+ content_script->Set(keys::kIncludeGlobs, includes);
+ content_script->Set(keys::kExcludeGlobs, excludes);
+ content_script->Set(keys::kJs, js_files);
+
+ ListValue* content_scripts = new ListValue();
+ content_scripts->Append(content_script);
+
+ root->Set(keys::kContentScripts, content_scripts);
+
+ FilePath manifest_path = temp_dir.path().AppendASCII(
+ Extension::kManifestFilename);
+ JSONFileValueSerializer serializer(manifest_path);
+ if (!serializer.Serialize(*root)) {
+ *error = "Could not write JSON.";
+ return NULL;
+ }
+
+ // Write the script file.
+ if (!file_util::CopyFile(user_script_path,
+ temp_dir.path().AppendASCII("script.js"))) {
+ *error = "Could not copy script file.";
+ return NULL;
+ }
+
+ scoped_ptr<Extension> extension(new Extension(temp_dir.path()));
+ if (!extension->InitFromValue(*root, false, error)) {
+ NOTREACHED() << "Could not init extension " << *error;
+ return NULL;
+ }
+
+ temp_dir.Take(); // The caller takes ownership of the directory.
+ return extension.release();
+}
diff --git a/chrome/browser/extensions/convert_user_script.h b/chrome/browser/extensions/convert_user_script.h
new file mode 100644
index 0000000..ebc9ee2
--- /dev/null
+++ b/chrome/browser/extensions/convert_user_script.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_CONVERT_USER_SCRIPT_H_
+#define CHROME_BROWSER_EXTENSIONS_CONVERT_USER_SCRIPT_H_
+
+#include <string>
+
+class Extension;
+class FilePath;
+class GURL;
+
+// Wraps the specified user script in an extension. The extension is created
+// unpacked in the system temp dir. Returns a valid extension that the caller
+// should take ownership on success, or NULL and |error| on failure.
+//
+// NOTE: This function does file IO and should not be called on the UI thread.
+Extension* ConvertUserScriptToExtension(const FilePath& user_script,
+ const GURL& original_url,
+ std::string* error);
+
+#endif // CHROME_BROWSER_EXTENSIONS_CONVERT_USER_SCRIPT_H_
diff --git a/chrome/browser/extensions/convert_user_script_unittest.cc b/chrome/browser/extensions/convert_user_script_unittest.cc
new file mode 100644
index 0000000..b7aea54
--- /dev/null
+++ b/chrome/browser/extensions/convert_user_script_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2009 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 <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/extensions/convert_user_script.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(ExtensionFromUserScript, Basic) {
+ FilePath test_file;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_file));
+ test_file = test_file.AppendASCII("extensions")
+ .AppendASCII("user_script_basic.user.js");
+
+ std::string error;
+ scoped_ptr<Extension> extension(ConvertUserScriptToExtension(
+ test_file, GURL("http://www.google.com/foo"), &error));
+
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("", error);
+
+ // Validate generated extension metadata.
+ EXPECT_EQ("My user script", extension->name());
+ EXPECT_EQ("1.0", extension->VersionString());
+ EXPECT_EQ("Does totally awesome stuff.", extension->description());
+ EXPECT_EQ("IhCFCg9PMQTAcJdc9ytUP99WME+4yh6aMnM1uupkovo=",
+ extension->public_key());
+
+ ASSERT_EQ(1u, extension->content_scripts().size());
+ const UserScript& script = extension->content_scripts()[0];
+ ASSERT_EQ(2u, script.globs().size());
+ EXPECT_EQ("http://www.google.com/*", script.globs().at(0));
+ EXPECT_EQ("http://www.yahoo.com/*", script.globs().at(1));
+ ASSERT_EQ(1u, script.exclude_globs().size());
+ EXPECT_EQ("*foo*", script.exclude_globs().at(0));
+ ASSERT_EQ(1u, script.url_patterns().size());
+ EXPECT_EQ("http://www.google.com/*", script.url_patterns()[0].GetAsString());
+
+ // Make sure the files actually exist on disk.
+ EXPECT_TRUE(file_util::PathExists(
+ extension->path().Append(script.js_scripts()[0].relative_path())));
+ EXPECT_TRUE(file_util::PathExists(
+ extension->path().AppendASCII(Extension::kManifestFilename)));
+}
+
+TEST(ExtensionFromUserScript, NoMetdata) {
+ FilePath test_file;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_file));
+ test_file = test_file.AppendASCII("extensions")
+ .AppendASCII("user_script_no_metadata.user.js");
+
+ std::string error;
+ scoped_ptr<Extension> extension(ConvertUserScriptToExtension(
+ test_file, GURL("http://www.google.com/foo/bar.user.js?monkey"), &error));
+
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("", error);
+
+ // Validate generated extension metadata.
+ EXPECT_EQ("bar.user.js", extension->name());
+ EXPECT_EQ("1.0", extension->VersionString());
+ EXPECT_EQ("", extension->description());
+ EXPECT_EQ("k1WxKx54hX6tfl5gQaXD/m4d9QUMwRdXWM4RW+QkWcY=",
+ extension->public_key());
+
+ ASSERT_EQ(1u, extension->content_scripts().size());
+ const UserScript& script = extension->content_scripts()[0];
+ ASSERT_EQ(1u, script.globs().size());
+ EXPECT_EQ("*", script.globs()[0]);
+ EXPECT_EQ(0u, script.exclude_globs().size());
+ ASSERT_EQ(2u, script.url_patterns().size());
+ EXPECT_EQ("http://*/*", script.url_patterns()[0].GetAsString());
+ EXPECT_EQ("https://*/*", script.url_patterns()[1].GetAsString());
+
+ // Make sure the files actually exist on disk.
+ EXPECT_TRUE(file_util::PathExists(
+ extension->path().Append(script.js_scripts()[0].relative_path())));
+ EXPECT_TRUE(file_util::PathExists(
+ extension->path().AppendASCII(Extension::kManifestFilename)));
+}
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 6c6259f..cb5b4e4 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -11,6 +11,7 @@
#include "base/task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/extensions/convert_user_script.h"
#include "chrome/browser/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_error_reporter.h"
#include "chrome/common/notification_service.h"
@@ -31,41 +32,56 @@ void CrxInstaller::Start(const FilePath& crx_path,
const FilePath& install_directory,
Extension::Location install_source,
const std::string& expected_id,
- bool delete_crx,
+ bool delete_source,
bool allow_privilege_increase,
ExtensionsService* frontend,
ExtensionInstallUI* client) {
// Note: We don't keep a reference because this object manages its own
// lifetime.
- new CrxInstaller(crx_path, install_directory, install_source, expected_id,
- delete_crx, allow_privilege_increase, frontend, client);
+ CrxInstaller* installer = new CrxInstaller(crx_path, install_directory,
+ delete_source, frontend,
+ client);
+ installer->install_source_ = install_source;
+ installer->expected_id_ = expected_id;
+ installer->allow_privilege_increase_ = allow_privilege_increase;
+
+ installer->unpacker_ = new SandboxedExtensionUnpacker(
+ installer->source_file_, g_browser_process->resource_dispatcher_host(),
+ installer);
+
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(installer->unpacker_, &SandboxedExtensionUnpacker::Start));
+}
+
+void CrxInstaller::InstallUserScript(const FilePath& source_file,
+ const GURL& original_url,
+ const FilePath& install_directory,
+ bool delete_source,
+ ExtensionsService* frontend,
+ ExtensionInstallUI* client) {
+ CrxInstaller* installer = new CrxInstaller(source_file, install_directory,
+ delete_source, frontend, client);
+ installer->original_url_ = original_url;
+
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(installer, &CrxInstaller::ConvertUserScriptOnFileThread));
}
-CrxInstaller::CrxInstaller(const FilePath& crx_path,
+CrxInstaller::CrxInstaller(const FilePath& source_file,
const FilePath& install_directory,
- Extension::Location install_source,
- const std::string& expected_id,
- bool delete_crx,
- bool allow_privilege_increase,
+ bool delete_source,
ExtensionsService* frontend,
ExtensionInstallUI* client)
- : crx_path_(crx_path),
+ : source_file_(source_file),
install_directory_(install_directory),
- install_source_(install_source),
- expected_id_(expected_id),
- delete_crx_(delete_crx),
- allow_privilege_increase_(allow_privilege_increase),
+ install_source_(Extension::INTERNAL),
+ delete_source_(delete_source),
+ allow_privilege_increase_(false),
frontend_(frontend),
client_(client) {
-
extensions_enabled_ = frontend_->extensions_enabled();
-
- unpacker_ = new SandboxedExtensionUnpacker(
- crx_path, g_browser_process->resource_dispatcher_host(), this);
-
- ChromeThread::PostTask(
- ChromeThread::FILE, FROM_HERE,
- NewRunnableMethod(unpacker_, &SandboxedExtensionUnpacker::Start));
}
CrxInstaller::~CrxInstaller() {
@@ -78,11 +94,23 @@ CrxInstaller::~CrxInstaller() {
NewRunnableFunction(&DeleteFileHelper, temp_dir_, true));
}
- if (delete_crx_) {
+ if (delete_source_) {
ChromeThread::PostTask(
ChromeThread::FILE, FROM_HERE,
- NewRunnableFunction(&DeleteFileHelper, crx_path_, false));
+ NewRunnableFunction(&DeleteFileHelper, source_file_, false));
+ }
+}
+
+void CrxInstaller::ConvertUserScriptOnFileThread() {
+ std::string error;
+ Extension* extension = ConvertUserScriptToExtension(source_file_,
+ original_url_, &error);
+ if (!extension) {
+ ReportFailureFromFileThread(error);
+ return;
}
+
+ OnUnpackSuccess(extension->path(), extension->path(), extension);
}
void CrxInstaller::OnUnpackFailure(const std::string& error_message) {
@@ -102,7 +130,6 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
// The unpack dir we don't have to delete explicity since it is a child of
// the temp dir.
unpacked_extension_root_ = extension_dir;
- DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_));
// Determine whether to allow installation. We always allow themes and
// external installs.
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index a0e50ef..bd78cf6 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -38,7 +38,7 @@ class CrxInstaller :
public SandboxedExtensionUnpackerClient,
public ExtensionInstallUI::Delegate {
public:
- // Starts the installation of the crx file in |crx_path| into
+ // Starts the installation of the crx file in |source_file| into
// |install_directory|.
//
// Other params:
@@ -52,15 +52,25 @@ class CrxInstaller :
// client: Optional. If specified, will be used to confirm installation and
// also notified of success/fail. Note that we hold a reference to
// this, so it can outlive its creator (eg the UI).
- static void Start(const FilePath& crx_path,
+ static void Start(const FilePath& source_file,
const FilePath& install_directory,
Extension::Location install_source,
const std::string& expected_id,
- bool delete_crx,
+ bool delete_source,
bool allow_privilege_increase,
ExtensionsService* frontend,
ExtensionInstallUI* client);
+ // Starts the installation of the user script file in |source_file| into
+ // |install_directory|. The script will be converted to an extension.
+ // See Start() for argument descriptions.
+ static void InstallUserScript(const FilePath& source_file,
+ const GURL& original_url,
+ const FilePath& install_directory,
+ bool delete_source,
+ ExtensionsService* frontend,
+ ExtensionInstallUI* client);
+
// Given the path to the large icon from an extension, read it if present and
// decode it into result.
static void DecodeInstallIcon(const FilePath& large_icon_path,
@@ -71,16 +81,16 @@ class CrxInstaller :
virtual void AbortInstall();
private:
- CrxInstaller(const FilePath& crx_path,
+ CrxInstaller(const FilePath& source_file,
const FilePath& install_directory,
- Extension::Location install_source,
- const std::string& expected_id,
- bool delete_crx,
- bool allow_privilege_increase,
+ bool delete_source,
ExtensionsService* frontend,
ExtensionInstallUI* client);
~CrxInstaller();
+ // Converts the source user script to an extension.
+ void ConvertUserScriptOnFileThread();
+
// SandboxedExtensionUnpackerClient
virtual void OnUnpackFailure(const std::string& error_message);
virtual void OnUnpackSuccess(const FilePath& temp_dir,
@@ -103,8 +113,11 @@ class CrxInstaller :
void ReportSuccessFromFileThread();
void ReportSuccessFromUIThread();
- // The crx file we're installing.
- FilePath crx_path_;
+ // The file we're installing.
+ FilePath source_file_;
+
+ // The URL the file was downloaded from. Only used for user scripts.
+ GURL original_url_;
// The directory extensions are installed to.
FilePath install_directory_;
@@ -123,8 +136,8 @@ class CrxInstaller :
// allowed.
bool extensions_enabled_;
- // Whether we're supposed to delete the source crx file on destruction.
- bool delete_crx_;
+ // Whether we're supposed to delete the source file on destruction.
+ bool delete_source_;
// Whether privileges should be allowed to silently increaes from any
// previously installed version of the extension.
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 69fd746..c9228ff 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -51,7 +51,11 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
static const base::StringPiece kUserScriptBegin("// ==UserScript==");
static const base::StringPiece kUserScriptEng("// ==/UserScript==");
+ static const base::StringPiece kNamespaceDeclaration("// @namespace ");
+ static const base::StringPiece kNameDeclaration("// @name ");
+ static const base::StringPiece kDescriptionDeclaration("// @description ");
static const base::StringPiece kIncludeDeclaration("// @include ");
+ static const base::StringPiece kExcludeDeclaration("// @exclude ");
static const base::StringPiece kMatchDeclaration("// @match ");
static const base::StringPiece kRunAtDeclaration("// @run-at ");
static const base::StringPiece kRunAtDocumentStartValue("document-start");
@@ -79,6 +83,16 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
script->add_glob(value);
+ } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
+ ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
+ ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
+ script->add_exclude_glob(value);
+ } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
+ script->set_name_space(value);
+ } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
+ script->set_name(value);
+ } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
+ script->set_description(value);
} else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
URLPattern pattern;
if (!pattern.Parse(value))
@@ -97,10 +111,6 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
line_start = line_end + 1;
}
- // It is probably a mistake to declare both @include and @match rules.
- if (script->globs().size() > 0 && script->url_patterns().size() > 0)
- return false;
-
// If no patterns were specified, default to @include *. This is what
// Greasemonkey does.
if (script->globs().size() == 0 && script->url_patterns().size() == 0)
diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h
index a062448..270a690 100644
--- a/chrome/browser/extensions/user_script_master.h
+++ b/chrome/browser/extensions/user_script_master.h
@@ -61,6 +61,7 @@ class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>,
FRIEND_TEST(UserScriptMasterTest, Parse5);
FRIEND_TEST(UserScriptMasterTest, Parse6);
+ public:
// We reload user scripts on the file thread to prevent blocking the UI.
// ScriptReloader lives on the file thread and does the reload
// work, and then sends a message back to its master with a new SharedMemory*.
@@ -118,6 +119,7 @@ class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>,
DISALLOW_COPY_AND_ASSIGN(ScriptReloader);
};
+ private:
// DirectoryWatcher::Delegate implementation.
virtual void OnDirectoryChanged(const FilePath& path);
diff --git a/chrome/browser/extensions/user_script_master_unittest.cc b/chrome/browser/extensions/user_script_master_unittest.cc
index 840d8a3..29faaea 100644
--- a/chrome/browser/extensions/user_script_master_unittest.cc
+++ b/chrome/browser/extensions/user_script_master_unittest.cc
@@ -213,8 +213,8 @@ TEST_F(UserScriptMasterTest, Parse6) {
"// @match \t http://mail.yahoo.com/*\n"
"// ==/UserScript==\n");
- // Not allowed to mix @include and @value.
+ // Allowed to match @include and @match.
UserScript script;
- EXPECT_FALSE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
+ EXPECT_TRUE(UserScriptMaster::ScriptReloader::ParseMetadataHeader(
text, &script));
}
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc
index 6022e1f..f3ca82f 100644
--- a/chrome/browser/renderer_host/buffered_resource_handler.cc
+++ b/chrome/browser/renderer_host/buffered_resource_handler.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
#include "chrome/browser/renderer_host/x509_user_cert_resource_handler.h"
+#include "chrome/common/extensions/user_script.h"
#include "chrome/common/url_constants.h"
#include "net/base/io_buffer.h"
#include "net/base/mime_sniffer.h"
@@ -441,6 +442,10 @@ bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) {
return true;
}
+ // Special-case user scripts to get downloaded instead of viewed.
+ if (UserScript::HasUserScriptFileExtension(request_->url()))
+ return true;
+
// MIME type checking.
if (net::IsSupportedMimeType(type))
return false;
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 8b4a857..e8c92f5 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1317,6 +1317,8 @@
'browser/download/save_types.h',
'browser/encoding_menu_controller.cc',
'browser/encoding_menu_controller.h',
+ 'browser/extensions/convert_user_script.cc',
+ 'browser/extensions/convert_user_script.h',
'browser/extensions/crashed_extension_infobar.cc',
'browser/extensions/crashed_extension_infobar.h',
'browser/extensions/crx_installer.cc',
@@ -4551,6 +4553,7 @@
'browser/download/download_request_manager_unittest.cc',
'browser/download/save_package_unittest.cc',
'browser/encoding_menu_controller_unittest.cc',
+ 'browser/extensions/convert_user_script_unittest.cc',
'browser/extensions/extension_file_util_unittest.cc',
'browser/extensions/extension_messages_unittest.cc',
'browser/extensions/extension_process_manager_unittest.cc',
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index c617232..0aad3de 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -248,6 +248,17 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
result->add_url_pattern(pattern);
}
+ // include/exclude globs (mostly for Greasemonkey compat)
+ if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
+ error, &UserScript::add_glob, result)) {
+ return false;
+ }
+
+ if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
+ error, &UserScript::add_exclude_glob, result)) {
+ return false;
+ }
+
// js and css keys
ListValue* js = NULL;
if (content_script->HasKey(keys::kJs) &&
@@ -311,6 +322,38 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
return true;
}
+bool Extension::LoadGlobsHelper(
+ const DictionaryValue* content_script,
+ int content_script_index,
+ const wchar_t* globs_property_name,
+ std::string* error,
+ void (UserScript::*add_method) (const std::string& glob),
+ UserScript *instance) {
+ if (!content_script->HasKey(globs_property_name))
+ return true; // they are optional
+
+ ListValue* list = NULL;
+ if (!content_script->GetList(globs_property_name, &list)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlobList,
+ IntToString(content_script_index), WideToASCII(globs_property_name));
+ return false;
+ }
+
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ std::string glob;
+ if (!list->GetString(i, &glob)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlob,
+ IntToString(content_script_index), WideToASCII(globs_property_name),
+ IntToString(i));
+ return false;
+ }
+
+ (instance->*add_method)(glob);
+ }
+
+ return true;
+}
+
ExtensionAction* Extension::LoadExtensionActionHelper(
const DictionaryValue* extension_action, std::string* error) {
scoped_ptr<ExtensionAction> result(new ExtensionAction());
@@ -450,7 +493,8 @@ ExtensionResource Extension::GetResource(const FilePath& extension_path,
}
Extension::Extension(const FilePath& path)
- : is_theme_(false), background_page_ready_(false) {
+ : converted_from_user_script_(false), is_theme_(false),
+ background_page_ready_(false) {
DCHECK(path.IsAbsolute());
location_ = INVALID;
@@ -668,6 +712,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id,
}
}
+ // Initialize converted_from_user_script (if present)
+ source.GetBoolean(keys::kConvertedFromUserScript,
+ &converted_from_user_script_);
+
// Initialize icons (if present).
if (source.HasKey(keys::kIcons)) {
DictionaryValue* icons_value = NULL;
@@ -943,6 +991,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id,
if (!LoadUserScriptHelper(content_script, i, error, &script))
return false; // Failed to parse script context definition
script.set_extension_id(id());
+ if (converted_from_user_script_)
+ script.set_emulate_greasemonkey(true);
content_scripts_.push_back(script);
}
}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 050515b..d240388 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -195,6 +195,9 @@ class Extension {
const std::string& name() const { return name_; }
const std::string& public_key() const { return public_key_; }
const std::string& description() const { return description_; }
+ bool converted_from_user_script() const {
+ return converted_from_user_script_;
+ }
const UserScriptList& content_scripts() const { return content_scripts_; }
ExtensionAction* page_action() const { return page_action_.get(); }
ExtensionAction* browser_action() const { return browser_action_.get(); }
@@ -290,6 +293,15 @@ class Extension {
std::string* error,
UserScript* result);
+ // Helper method that loads either the include_globs or exclude_globs list
+ // from an entry in the content_script lists of the manifest.
+ bool LoadGlobsHelper(const DictionaryValue* content_script,
+ int content_script_index,
+ const wchar_t* globs_property_name,
+ std::string* error,
+ void (UserScript::*add_method) (const std::string& glob),
+ UserScript *instance);
+
// Helper method to load an ExtensionAction from the page_action or
// browser_action entries in the manifest.
ExtensionAction* LoadExtensionActionHelper(
@@ -325,6 +337,10 @@ class Extension {
// An optional longer description of the extension.
std::string description_;
+ // True if the extension was generated from a user script. (We show slightly
+ // different UI if so).
+ bool converted_from_user_script_;
+
// Paths to the content scripts the extension contains.
UserScriptList content_scripts_;
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 59e7c14..0dbab1f 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -10,12 +10,15 @@ const wchar_t* kBackground = L"background_page";
const wchar_t* kBrowserAction = L"browser_action";
const wchar_t* kChromeURLOverrides = L"chrome_url_overrides";
const wchar_t* kContentScripts = L"content_scripts";
+const wchar_t* kConvertedFromUserScript = L"converted_from_user_script";
const wchar_t* kCss = L"css";
const wchar_t* kDefaultLocale = L"default_locale";
const wchar_t* kDescription = L"description";
const wchar_t* kIcons = L"icons";
const wchar_t* kJs = L"js";
const wchar_t* kMatches = L"matches";
+const wchar_t* kIncludeGlobs = L"include_globs";
+const wchar_t* kExcludeGlobs = L"exclude_globs";
const wchar_t* kName = L"name";
const wchar_t* kPageActionId = L"id";
const wchar_t* kPageAction = L"page_action";
@@ -76,6 +79,10 @@ const char* kInvalidCssList =
"Required value 'content_scripts[*].css is invalid.";
const char* kInvalidDescription =
"Invalid value for 'description'.";
+const char* kInvalidGlobList =
+ "Invalid value for 'content_scripts[*].*'.";
+const char* kInvalidGlob =
+ "Invalid value for 'content_scripts[*].*[*]'.";
const char* kInvalidIcons =
"Invalid value for 'icons'.";
const char* kInvalidIconPath =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index b7826a7..d44f7b9 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -11,10 +11,13 @@ namespace extension_manifest_keys {
extern const wchar_t* kBrowserAction;
extern const wchar_t* kChromeURLOverrides;
extern const wchar_t* kContentScripts;
+ extern const wchar_t* kConvertedFromUserScript;
extern const wchar_t* kCss;
extern const wchar_t* kDefaultLocale;
extern const wchar_t* kDescription;
+ extern const wchar_t* kExcludeGlobs;
extern const wchar_t* kIcons;
+ extern const wchar_t* kIncludeGlobs;
extern const wchar_t* kJs;
extern const wchar_t* kMatches;
extern const wchar_t* kName;
@@ -70,6 +73,8 @@ namespace extension_manifest_errors {
extern const char* kInvalidDescription;
extern const char* kInvalidIcons;
extern const char* kInvalidIconPath;
+ extern const char* kInvalidGlobList;
+ extern const char* kInvalidGlob;
extern const char* kInvalidJs;
extern const char* kInvalidJsList;
extern const char* kInvalidKey;
diff --git a/chrome/common/extensions/extension_error_utils.cc b/chrome/common/extensions/extension_error_utils.cc
index 724cdcd..0256c8d8 100644
--- a/chrome/common/extensions/extension_error_utils.cc
+++ b/chrome/common/extensions/extension_error_utils.cc
@@ -8,7 +8,7 @@
std::string ExtensionErrorUtils::FormatErrorMessage(
const std::string& format,
- const std::string s1) {
+ const std::string& s1) {
std::string ret_val = format;
ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
return ret_val;
@@ -16,10 +16,22 @@ std::string ExtensionErrorUtils::FormatErrorMessage(
std::string ExtensionErrorUtils::FormatErrorMessage(
const std::string& format,
- const std::string s1,
- const std::string s2) {
+ const std::string& s1,
+ const std::string& s2) {
std::string ret_val = format;
ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
return ret_val;
}
+
+std::string ExtensionErrorUtils::FormatErrorMessage(
+ const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s3);
+ return ret_val;
+}
diff --git a/chrome/common/extensions/extension_error_utils.h b/chrome/common/extensions/extension_error_utils.h
index f9f1e7c..fdd9b03 100644
--- a/chrome/common/extensions/extension_error_utils.h
+++ b/chrome/common/extensions/extension_error_utils.h
@@ -9,16 +9,18 @@
class ExtensionErrorUtils {
public:
- // Creates an error messages from a pattern. Places first instance if "*"
- // with |s1|.
+ // Creates an error messages from a pattern.
static std::string FormatErrorMessage(const std::string& format,
- const std::string s1);
+ const std::string& s1);
- // Creates an error messages from a pattern. Places first instance if "*"
- // with |s1| and second instance of "*" with |s2|.
static std::string FormatErrorMessage(const std::string& format,
- const std::string s1,
- const std::string s2);
+ const std::string& s1,
+ const std::string& s2);
+
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3);
};
#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_ERROR_UTILS_H_
diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc
index df34e52..10d0b60 100644
--- a/chrome/common/extensions/user_script.cc
+++ b/chrome/common/extensions/user_script.cc
@@ -7,21 +7,59 @@
#include "base/pickle.h"
#include "base/string_util.h"
-bool UserScript::MatchesUrl(const GURL& url) const {
- for (std::vector<std::string>::const_iterator glob = globs_.begin();
- glob != globs_.end(); ++glob) {
- if (MatchPattern(url.spec(), *glob))
+namespace {
+static bool UrlMatchesPatterns(const UserScript::PatternList* patterns,
+ const GURL& url) {
+ for (UserScript::PatternList::const_iterator pattern = patterns->begin();
+ pattern != patterns->end(); ++pattern) {
+ if (pattern->MatchesUrl(url))
return true;
}
- for (PatternList::const_iterator pattern = url_patterns_.begin();
- pattern != url_patterns_.end(); ++pattern) {
- if (pattern->MatchesUrl(url))
+ return false;
+}
+
+static bool UrlMatchesGlobs(const std::vector<std::string>* globs,
+ const GURL& url) {
+ for (std::vector<std::string>::const_iterator glob = globs->begin();
+ glob != globs->end(); ++glob) {
+ if (MatchPattern(url.spec(), *glob))
return true;
}
return false;
}
+}
+
+const char UserScript::kFileExtension[] = ".user.js";
+
+bool UserScript::HasUserScriptFileExtension(const GURL& url) {
+ return EndsWith(url.ExtractFileName(), kFileExtension, false);
+}
+
+bool UserScript::HasUserScriptFileExtension(const FilePath& path) {
+ static FilePath extension(FilePath().AppendASCII(kFileExtension));
+ return EndsWith(path.BaseName().value(), extension.value(), false);
+}
+
+bool UserScript::MatchesUrl(const GURL& url) const {
+ if (url_patterns_.size() > 0) {
+ if (!UrlMatchesPatterns(&url_patterns_, url))
+ return false;
+ }
+
+ if (globs_.size() > 0) {
+ if (!UrlMatchesGlobs(&globs_, url))
+ return false;
+ }
+
+ if (exclude_globs_.size() > 0) {
+ if (UrlMatchesGlobs(&exclude_globs_, url))
+ return false;
+ }
+
+ return true;
+}
void UserScript::File::Pickle(::Pickle* pickle) const {
pickle->WriteString(url_.spec());
@@ -43,10 +81,17 @@ void UserScript::Pickle(::Pickle* pickle) const {
// Write the extension id.
pickle->WriteString(extension_id());
+ // Write Greasemonkey emulation.
+ pickle->WriteBool(emulate_greasemonkey());
+
// Write globs.
+ std::vector<std::string>::const_iterator glob;
pickle->WriteSize(globs_.size());
- for (std::vector<std::string>::const_iterator glob = globs_.begin();
- glob != globs_.end(); ++glob) {
+ for (glob = globs_.begin(); glob != globs_.end(); ++glob) {
+ pickle->WriteString(*glob);
+ }
+ pickle->WriteSize(exclude_globs_.size());
+ for (glob = exclude_globs_.begin(); glob != exclude_globs_.end(); ++glob) {
pickle->WriteString(*glob);
}
@@ -82,10 +127,12 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) {
// Read the extension ID.
CHECK(pickle.ReadString(iter, &extension_id_));
+ // Read Greasemonkey emulation.
+ CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_));
+
// Read globs.
size_t num_globs = 0;
CHECK(pickle.ReadSize(iter, &num_globs));
-
globs_.clear();
for (size_t i = 0; i < num_globs; ++i) {
std::string glob;
@@ -93,6 +140,14 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) {
globs_.push_back(glob);
}
+ CHECK(pickle.ReadSize(iter, &num_globs));
+ exclude_globs_.clear();
+ for (size_t i = 0; i < num_globs; ++i) {
+ std::string glob;
+ CHECK(pickle.ReadString(iter, &glob));
+ exclude_globs_.push_back(glob);
+ }
+
// Read url patterns.
size_t num_patterns = 0;
CHECK(pickle.ReadSize(iter, &num_patterns));
diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h
index bdd1e54..adcfa3f 100644
--- a/chrome/common/extensions/user_script.h
+++ b/chrome/common/extensions/user_script.h
@@ -22,6 +22,13 @@ class UserScript {
public:
typedef std::vector<URLPattern> PatternList;
+ // The file extension for standalone user scripts.
+ static const char kFileExtension[];
+
+ // Check if a file or URL has the user script file extension.
+ static bool HasUserScriptFileExtension(const GURL& url);
+ static bool HasUserScriptFileExtension(const FilePath& path);
+
// Locations that user scripts can be run inside the document.
enum RunLocation {
DOCUMENT_START, // After the documentElemnet is created, but before
@@ -92,20 +99,45 @@ class UserScript {
typedef std::vector<File> FileList;
- // Constructor. Default the run location to document idle, which is similar
- // to Greasemonkey but should result in better page load times for fast-
- // loading pages.
- UserScript() : run_location_(DOCUMENT_IDLE) {}
+ // Constructor. Default the run location to document end, which is like
+ // Greasemonkey and probably more useful for typical scripts.
+ UserScript()
+ : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false) {
+ }
+
+ const std::string& name_space() const { return name_space_; }
+ void set_name_space(const std::string& name_space) {
+ name_space_ = name_space;
+ }
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ const std::string& description() const { return description_; }
+ void set_description(const std::string& description) {
+ description_ = description;
+ }
// The place in the document to run the script.
RunLocation run_location() const { return run_location_; }
void set_run_location(RunLocation location) { run_location_ = location; }
+ // Whether to emulate greasemonkey when running this script.
+ bool emulate_greasemonkey() const { return emulate_greasemonkey_; }
+ void set_emulate_greasemonkey(bool val) { emulate_greasemonkey_ = val; }
+
// The globs, if any, that determine which pages this script runs against.
// These are only used with "standalone" Greasemonkey-like user scripts.
const std::vector<std::string>& globs() const { return globs_; }
void add_glob(const std::string& glob) { globs_.push_back(glob); }
void clear_globs() { globs_.clear(); }
+ const std::vector<std::string>& exclude_globs() const {
+ return exclude_globs_;
+ }
+ void add_exclude_glob(const std::string& glob) {
+ exclude_globs_.push_back(glob);
+ }
+ void clear_exclude_globs() { exclude_globs_.clear(); }
// The URLPatterns, if any, that determine which pages this script runs
// against.
@@ -145,9 +177,20 @@ class UserScript {
// The location to run the script inside the document.
RunLocation run_location_;
+ // The namespace of the script. This is used by Greasemonkey in the same way
+ // as XML namespaces. Only used when parsing Greasemonkey-style scripts.
+ std::string name_space_;
+
+ // The script's name. Only used when parsing Greasemonkey-style scripts.
+ std::string name_;
+
+ // A longer description. Only used when parsing Greasemonkey-style scripts.
+ std::string description_;
+
// Greasemonkey-style globs that determine pages to inject the script into.
// These are only used with standalone scripts.
std::vector<std::string> globs_;
+ std::vector<std::string> exclude_globs_;
// URLPatterns that determine pages to inject the script into. These are
// only used with scripts that are part of extensions.
@@ -162,6 +205,10 @@ class UserScript {
// The ID of the extension this script is a part of, if any. Can be empty if
// the script is a "standlone" user script.
std::string extension_id_;
+
+ // Whether we should try to emulate Greasemonkey's APIs when running this
+ // script.
+ bool emulate_greasemonkey_;
};
typedef std::vector<UserScript> UserScriptList;
diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc
index 58ef77e..7b5cc6d 100644
--- a/chrome/common/extensions/user_script_unittest.cc
+++ b/chrome/common/extensions/user_script_unittest.cc
@@ -21,6 +21,10 @@ TEST(UserScriptTest, Match1) {
EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.yahoo.com/bar")));
EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.msn.com/baz")));
EXPECT_FALSE(script.MatchesUrl(GURL("http://www.hotmail.com")));
+
+ script.add_exclude_glob("*foo*");
+ EXPECT_TRUE(script.MatchesUrl(GURL("http://mail.google.com")));
+ EXPECT_FALSE(script.MatchesUrl(GURL("http://mail.google.com/foo")));
}
TEST(UserScriptTest, Match2) {
@@ -70,6 +74,36 @@ TEST(UserScriptTest, Match6) {
// NOTE: URLPattern is tested more extensively in url_pattern_unittest.cc.
}
+TEST(UserScriptTest, UrlPatternGlobInteraction) {
+ // If there are both, match intersection(union(globs), union(urlpatterns)).
+ UserScript script;
+
+ URLPattern pattern;
+ ASSERT_TRUE(pattern.Parse("http://www.google.com/*"));
+ script.add_url_pattern(pattern);
+
+ script.add_glob("*bar*");
+
+ // No match, because it doesn't match the glob.
+ EXPECT_FALSE(script.MatchesUrl(GURL("http://www.google.com/foo")));
+
+ script.add_exclude_glob("*baz*");
+
+ // No match, because it matches the exclude glob.
+ EXPECT_FALSE(script.MatchesUrl(GURL("http://www.google.com/baz")));
+
+ // Match, because it matches the glob, doesn't match the exclude glob.
+ EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/bar")));
+
+ // Try with just a single exclude glob.
+ script.clear_globs();
+ EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/foo")));
+
+ // Try with no globs or exclude globs.
+ script.clear_exclude_globs();
+ EXPECT_TRUE(script.MatchesUrl(GURL("http://www.google.com/foo")));
+}
+
TEST(UserScriptTest, Pickle) {
URLPattern pattern1;
URLPattern pattern2;
diff --git a/chrome/renderer/resources/greasemonkey_api.js b/chrome/renderer/resources/greasemonkey_api.js
index 7b829ad..52f7560 100644
--- a/chrome/renderer/resources/greasemonkey_api.js
+++ b/chrome/renderer/resources/greasemonkey_api.js
@@ -67,3 +67,12 @@ function GM_openInTab(url) {
function GM_log(message) {
window.console.log(message);
}
+
+(function() {
+ var apis = ["GM_getValue", "GM_setValue", "GM_registerMenuCommand"];
+ for (var i = 0, api; api = apis[i]; i++) {
+ window[api] = function() {
+ console.log("%s is not supported.", api);
+ }
+ }
+})();
diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc
index b71c7a8..473cda8 100644
--- a/chrome/renderer/user_script_slave.cc
+++ b/chrome/renderer/user_script_slave.cc
@@ -51,22 +51,9 @@ int UserScriptSlave::GetIsolatedWorldId(const std::string& extension_id) {
UserScriptSlave::UserScriptSlave()
: shared_memory_(NULL),
- script_deleter_(&scripts_),
- user_script_start_line_(0) {
+ script_deleter_(&scripts_) {
api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_GREASEMONKEY_API_JS);
-
- // Count the number of lines that will be injected before the user script.
- base::StringPiece::size_type pos = 0;
- while ((pos = api_js_.find('\n', pos)) != base::StringPiece::npos) {
- user_script_start_line_++;
- pos++;
- }
-
- // NOTE: There is actually one extra line in the injected script because the
- // function header includes a newline as well. But WebKit expects the
- // numbering to be one-based, not zero-based, so actually *not* accounting for
- // this extra line ends us up with the right offset.
}
bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
@@ -178,13 +165,16 @@ bool UserScriptSlave::InjectScripts(WebFrame* frame,
if (!sources.empty()) {
int isolated_world_id = 0;
- if (script->is_standalone()) {
- // For standalone scripts, we try to emulate the Greasemonkey API.
+ // 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())));
- } else {
- // Setup chrome.self to contain an Extension object with the correct
- // ID.
+ }
+
+ // Setup chrome.self to contain an Extension object with the correct
+ // ID.
+ if (!script->extension_id().empty()) {
InsertInitExtensionCode(&sources, script->extension_id());
isolated_world_id = GetIsolatedWorldId(script->extension_id());
}
diff --git a/chrome/renderer/user_script_slave.h b/chrome/renderer/user_script_slave.h
index 8232ec4..3d7c49d 100644
--- a/chrome/renderer/user_script_slave.h
+++ b/chrome/renderer/user_script_slave.h
@@ -50,11 +50,6 @@ class UserScriptSlave {
// Greasemonkey API source that is injected with the scripts.
base::StringPiece api_js_;
- // The line number of the first line of the user script among all of the
- // injected javascript. This is used to make reported errors correspond with
- // the proper line in the user script.
- int user_script_start_line_;
-
DISALLOW_COPY_AND_ASSIGN(UserScriptSlave);
};
diff --git a/chrome/test/data/extensions/user_script_basic.user.js b/chrome/test/data/extensions/user_script_basic.user.js
new file mode 100755
index 0000000..ea4f8a0
--- /dev/null
+++ b/chrome/test/data/extensions/user_script_basic.user.js
@@ -0,0 +1,11 @@
+// ==UserScript==
+// @name My user script
+// @namespace http://www.google.com
+// @description Does totally awesome stuff.
+// @include http://www.google.com/*
+// @include http://www.yahoo.com/*
+// @exclude *foo*
+// @match http://www.google.com/*
+// ==/UserScript==
+
+alert("Hello! This is my script.");
diff --git a/chrome/test/data/extensions/user_script_no_metadata.user.js b/chrome/test/data/extensions/user_script_no_metadata.user.js
new file mode 100755
index 0000000..f8fe23e
--- /dev/null
+++ b/chrome/test/data/extensions/user_script_no_metadata.user.js
@@ -0,0 +1 @@
+alert("This user script has no metadata, but it is also valid!");