summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-17 01:21:47 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-17 01:21:47 +0000
commit0999c45abbe8d57f1c62c65f91459881a4af321a (patch)
tree6844ce69dfaf4cbdfe0e540e6647ef60b3c22451 /chrome/browser/extensions
parent2e6189037048f81f5025125a9884f29c1e5f544d (diff)
downloadchromium_src-0999c45abbe8d57f1c62c65f91459881a4af321a.zip
chromium_src-0999c45abbe8d57f1c62c65f91459881a4af321a.tar.gz
chromium_src-0999c45abbe8d57f1c62c65f91459881a4af321a.tar.bz2
Move parsing of metadata header into browser process. This is a prerequisite
to getting user scripts working in extensions because extensions won't express their metadata using the UserScript header, so parsing can't be done in the renderer. Review URL: http://codereview.chromium.org/18308 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8249 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/extension_protocols.cc4
-rw-r--r--chrome/browser/extensions/extension_protocols.h6
-rw-r--r--chrome/browser/extensions/user_script_master.cc121
-rw-r--r--chrome/browser/extensions/user_script_master.h67
-rw-r--r--chrome/browser/extensions/user_script_master_unittest.cc47
5 files changed, 183 insertions, 62 deletions
diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc
index 8f6957d..1643c4d6 100644
--- a/chrome/browser/extensions/extension_protocols.cc
+++ b/chrome/browser/extensions/extension_protocols.cc
@@ -10,8 +10,8 @@
#include "net/base/net_util.h"
#include "net/url_request/url_request_file_job.h"
-static const char kExtensionURLScheme[] = "chrome-extension";
-static const char kUserScriptURLScheme[] = "chrome-user-script";
+const char kExtensionURLScheme[] = "chrome-extension";
+const char kUserScriptURLScheme[] = "chrome-user-script";
FilePath GetPathForExtensionResource(const FilePath& extension_path,
const std::string& url_path) {
diff --git a/chrome/browser/extensions/extension_protocols.h b/chrome/browser/extensions/extension_protocols.h
index 80fa654..25f9fda 100644
--- a/chrome/browser/extensions/extension_protocols.h
+++ b/chrome/browser/extensions/extension_protocols.h
@@ -7,6 +7,12 @@
#include "base/file_path.h"
+// The URL scheme Chromium extensions are served from.
+extern const char kExtensionURLScheme[];
+
+// The URL scheme Chromium user scripts are served from.
+extern const char kUserScriptURLScheme[];
+
// Gets a FilePath for a resource inside an extension. |extension_path| is the
// full path to the extension directory. |resource_path| is the path to the
// resource from the extension root, including the leading '/'.
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index dcd912a..b95a1d3 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -9,69 +9,66 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
-#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/string_util.h"
+#include "chrome/browser/extensions/extension_protocols.h"
#include "chrome/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"
-// 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*.
-
-// ScriptReloader is the worker that manages running the script scan
-// on the file thread.
-// It must be created on, and its public API must only be called from,
-// the master's thread.
-class UserScriptMaster::ScriptReloader
- : public base::RefCounted<UserScriptMaster::ScriptReloader> {
- public:
- ScriptReloader(UserScriptMaster* master)
- : master_(master), master_message_loop_(MessageLoop::current()) {}
-
- // Start a scan for scripts.
- // Will always send a message to the master upon completion.
- void StartScan(MessageLoop* work_loop, const FilePath& script_dir);
-
- // The master is going away; don't call it back.
- void DisownMaster() {
- master_ = NULL;
+// static
+void UserScriptMaster::ScriptReloader::ParseMetadataHeader(
+ const StringPiece& script_text, std::vector<std::string> *includes) {
+ // http://wiki.greasespot.net/Metadata_block
+ StringPiece line;
+ size_t line_start = 0;
+ size_t line_end = 0;
+ bool in_metadata = false;
+
+ static const StringPiece kUserScriptBegin("// ==UserScript==");
+ static const StringPiece kUserScriptEng("// ==/UserScript==");
+ static const StringPiece kIncludeDeclaration("// @include ");
+
+ while (line_start < script_text.length()) {
+ line_end = script_text.find('\n', line_start);
+
+ // Handle the case where there is no trailing newline in the file.
+ if (line_end == std::string::npos) {
+ line_end = script_text.length() - 1;
+ }
+
+ line.set(script_text.data() + line_start, line_end - line_start);
+
+ if (!in_metadata) {
+ if (line.starts_with(kUserScriptBegin)) {
+ in_metadata = true;
+ }
+ } else {
+ if (line.starts_with(kUserScriptEng)) {
+ break;
+ }
+
+ if (line.starts_with(kIncludeDeclaration)) {
+ std::string pattern(line.data() + kIncludeDeclaration.length(),
+ line.length() - kIncludeDeclaration.length());
+ std::string pattern_trimmed;
+ TrimWhitespace(pattern, TRIM_ALL, &pattern_trimmed);
+ includes->push_back(pattern_trimmed);
+ }
+
+ // TODO(aa): Handle more types of metadata.
+ }
+
+ line_start = line_end + 1;
}
- private:
- // Where functions are run:
- // master file
- // StartScan -> RunScan
- // GetNewScripts()
- // NotifyMaster <- RunScan
-
- // Runs on the master thread.
- // Notify the master that new scripts are available.
- void NotifyMaster(base::SharedMemory* memory);
-
- // Runs on the File thread.
- // Scan the script directory for scripts, calling NotifyMaster when done.
- // The path is intentionally passed by value so its lifetime isn't tied
- // to the caller.
- void RunScan(const FilePath script_dir);
-
- // Runs on the File thread.
- // Scan the script directory for scripts, returning either a new SharedMemory
- // or NULL on error.
- base::SharedMemory* GetNewScripts(const FilePath& script_dir);
-
- // A pointer back to our master.
- // May be NULL if DisownMaster() is called.
- UserScriptMaster* master_;
-
- // The message loop to call our master back on.
- // Expected to always outlive us.
- MessageLoop* master_message_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(ScriptReloader);
-};
+ // If no @include patterns were specified, default to @include *.
+ // This is what Greasemonkey does.
+ if (includes->size() == 0) {
+ includes->push_back("*");
+ }
+}
void UserScriptMaster::ScriptReloader::StartScan(
MessageLoop* work_loop,
@@ -129,15 +126,26 @@ base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts(
pickle.WriteSize(scripts.size());
for (std::vector<std::wstring>::iterator path = scripts.begin();
path != scripts.end(); ++path) {
- std::string file_url = net::FilePathToFileURL(*path).spec();
+ std::string url(kUserScriptURLScheme);
+ url += ":/";
+ url += net::FilePathToFileURL(*path).ExtractFileName();
+
std::string contents;
// TODO(aa): Support unicode script files.
file_util::ReadFileToString(*path, &contents);
+ std::vector<std::string> includes;
+ ParseMetadataHeader(contents, &includes);
+
// Write scripts as 'data' so that we can read it out in the slave without
// allocating a new string.
- pickle.WriteData(file_url.c_str(), file_url.length());
+ pickle.WriteData(url.c_str(), url.length());
pickle.WriteData(contents.c_str(), contents.length());
+ pickle.WriteSize(includes.size());
+ for (std::vector<std::string>::iterator iter = includes.begin();
+ iter != includes.end(); ++iter) {
+ pickle.WriteString(*iter);
+ }
}
// Create the shared memory object.
@@ -160,7 +168,6 @@ base::SharedMemory* UserScriptMaster::ScriptReloader::GetNewScripts(
return shared_memory.release();
}
-
UserScriptMaster::UserScriptMaster(MessageLoop* worker_loop,
const FilePath& script_dir)
: user_script_dir_(new FilePath(script_dir)),
diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h
index 4798bb2..8df49be 100644
--- a/chrome/browser/extensions/user_script_master.h
+++ b/chrome/browser/extensions/user_script_master.h
@@ -7,11 +7,11 @@
#include "base/directory_watcher.h"
#include "base/file_path.h"
+#include "base/message_loop.h"
#include "base/process.h"
#include "base/scoped_ptr.h"
#include "base/shared_memory.h"
-
-class MessageLoop;
+#include "base/string_piece.h"
// Manages a segment of shared memory that contains the user scripts the user
// has installed. Lives on the UI thread.
@@ -39,7 +39,68 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>,
FilePath user_script_dir() const { return *user_script_dir_; }
private:
- class ScriptReloader;
+ FRIEND_TEST(UserScriptMasterTest, Parse1);
+ FRIEND_TEST(UserScriptMasterTest, Parse2);
+ FRIEND_TEST(UserScriptMasterTest, Parse3);
+
+ // 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*.
+ // ScriptReloader is the worker that manages running the script scan
+ // on the file thread. It must be created on, and its public API must only be
+ // called from, the master's thread.
+ class ScriptReloader
+ : public base::RefCounted<UserScriptMaster::ScriptReloader> {
+ public:
+ // Parses the includes out of |script| and returns them in |includes|.
+ static void ParseMetadataHeader(const StringPiece& script,
+ std::vector<std::string>* includes);
+
+ ScriptReloader(UserScriptMaster* master)
+ : master_(master), master_message_loop_(MessageLoop::current()) {}
+
+ // Start a scan for scripts.
+ // Will always send a message to the master upon completion.
+ void StartScan(MessageLoop* work_loop, const FilePath& script_dir);
+
+ // The master is going away; don't call it back.
+ void DisownMaster() {
+ master_ = NULL;
+ }
+
+ private:
+ // Where functions are run:
+ // master file
+ // StartScan -> RunScan
+ // GetNewScripts()
+ // ParseMetadataHeader()
+ // NotifyMaster <- RunScan
+
+ // Runs on the master thread.
+ // Notify the master that new scripts are available.
+ void NotifyMaster(base::SharedMemory* memory);
+
+ // Runs on the File thread.
+ // Scan the script directory for scripts, calling NotifyMaster when done.
+ // The path is intentionally passed by value so its lifetime isn't tied
+ // to the caller.
+ void RunScan(const FilePath script_dir);
+
+ // Runs on the File thread.
+ // Scan the script directory for scripts, returning either a
+ // new SharedMemory or NULL on error.
+ base::SharedMemory* GetNewScripts(const FilePath& script_dir);
+
+ // A pointer back to our master.
+ // May be NULL if DisownMaster() is called.
+ UserScriptMaster* master_;
+
+ // The message loop to call our master back on.
+ // Expected to always outlive us.
+ MessageLoop* master_message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScriptReloader);
+ };
// 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 8e3b95d..4c11661 100644
--- a/chrome/browser/extensions/user_script_master_unittest.cc
+++ b/chrome/browser/extensions/user_script_master_unittest.cc
@@ -116,3 +116,50 @@ TEST_F(UserScriptMasterTest, ExistingScripts) {
ASSERT_TRUE(shared_memory_ != NULL);
}
+
+TEST_F(UserScriptMasterTest, Parse1) {
+ const std::string text(
+ "// This is my awesome script\n"
+ "// It does stuff.\n"
+ "// ==UserScript== trailing garbage\n"
+ "// @name foobar script\n"
+ "// @namespace http://www.google.com/\n"
+ "// @include *mail.google.com*\n"
+ "// \n"
+ "// @othergarbage\n"
+ "// @include *mail.yahoo.com*\r\n"
+ "// @include \t *mail.msn.com*\n" // extra spaces after "@include" OK
+ "//@include not-recognized\n" // must have one space after "//"
+ "// ==/UserScript== trailing garbage\n"
+ "\n"
+ "\n"
+ "alert('hoo!');\n");
+
+ std::vector<std::string> includes;
+ UserScriptMaster::ScriptReloader::ParseMetadataHeader(text, &includes);
+ EXPECT_EQ(3U, includes.size());
+ EXPECT_EQ("*mail.google.com*", includes[0]);
+ EXPECT_EQ("*mail.yahoo.com*", includes[1]);
+ EXPECT_EQ("*mail.msn.com*", includes[2]);
+}
+
+TEST_F(UserScriptMasterTest, Parse2) {
+ const std::string text("default to @include *");
+
+ std::vector<std::string> includes;
+ UserScriptMaster::ScriptReloader::ParseMetadataHeader(text, &includes);
+ EXPECT_EQ(1U, includes.size());
+ EXPECT_EQ("*", includes[0]);
+}
+
+TEST_F(UserScriptMasterTest, Parse3) {
+ const std::string text(
+ "// ==UserScript==\n"
+ "// @include *foo*\n"
+ "// ==/UserScript=="); // no trailing newline
+
+ std::vector<std::string> includes;
+ UserScriptMaster::ScriptReloader::ParseMetadataHeader(text, &includes);
+ EXPECT_EQ(1U, includes.size());
+ EXPECT_EQ("*foo*", includes[0]);
+}