summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-14 04:15:16 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-14 04:15:16 +0000
commit0afe827a49501c6801c3c9efbbdb1f64aa010beb (patch)
treedba6031dd45eb3f0a0435b9750976b1cd432abc1
parent27eef9c8edf84061f4e36a3bd26ff7538092a22b (diff)
downloadchromium_src-0afe827a49501c6801c3c9efbbdb1f64aa010beb.zip
chromium_src-0afe827a49501c6801c3c9efbbdb1f64aa010beb.tar.gz
chromium_src-0afe827a49501c6801c3c9efbbdb1f64aa010beb.tar.bz2
Add early-injection capability to user scripts.
Review URL: http://codereview.chromium.org/19624 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9822 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension.cc23
-rw-r--r--chrome/browser/extensions/extension.h6
-rw-r--r--chrome/browser/extensions/user_script_master.cc8
-rw-r--r--chrome/common/extensions/user_script.cc6
-rw-r--r--chrome/common/extensions/user_script.h21
-rw-r--r--chrome/common/extensions/user_script_unittest.cc6
-rw-r--r--chrome/renderer/render_view.cc18
-rw-r--r--chrome/renderer/render_view.h2
-rw-r--r--chrome/renderer/user_script_slave.cc20
-rw-r--r--chrome/renderer/user_script_slave.h2
-rw-r--r--webkit/glue/webframeloaderclient_impl.cc6
-rw-r--r--webkit/glue/webview_delegate.h6
12 files changed, 109 insertions, 15 deletions
diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc
index 11cab38..d1d52a51 100644
--- a/chrome/browser/extensions/extension.cc
+++ b/chrome/browser/extensions/extension.cc
@@ -22,9 +22,13 @@ const wchar_t* Extension::kIdKey = L"id";
const wchar_t* Extension::kMatchesKey = L"matches";
const wchar_t* Extension::kNameKey = L"name";
const wchar_t* Extension::kUserScriptsKey = L"user_scripts";
+const wchar_t* Extension::kRunAtKey = L"run_at";
const wchar_t* Extension::kVersionKey = L"version";
const wchar_t* Extension::kZipHashKey = L"zip_hash";
+const char* Extension::kRunAtDocumentStartValue = "document_start";
+const char* Extension::kRunAtDocumentEndValue = "document_end";
+
// Extension-related error messages. Some of these are simple patterns, where a
// '*' is replaced at runtime with a specific value. This is used instead of
// printf because we want to unit test them and scanf is hard to make
@@ -53,6 +57,8 @@ const char* Extension::kInvalidMatchesError =
"Required value 'user_scripts[*].matches' is missing or invalid.";
const char* Extension::kInvalidNameError =
"Required value 'name' is missing or invalid.";
+const char* Extension::kInvalidRunAtError =
+ "Invalid value for 'user_scripts[*].run_at'.";
const char* Extension::kInvalidUserScriptError =
"Invalid value for 'user_scripts[*]'.";
const char* Extension::kInvalidUserScriptsListError =
@@ -264,6 +270,23 @@ bool Extension::InitFromValue(const DictionaryValue& source,
}
UserScript script;
+ if (user_script->HasKey(kRunAtKey)) {
+ std::string run_location;
+ if (!user_script->GetString(kRunAtKey, &run_location)) {
+ *error = FormatErrorMessage(kInvalidRunAtError, IntToString(i));
+ return false;
+ }
+
+ if (run_location == kRunAtDocumentStartValue) {
+ script.set_run_location(UserScript::DOCUMENT_START);
+ } else if (run_location == kRunAtDocumentEndValue) {
+ script.set_run_location(UserScript::DOCUMENT_END);
+ } else {
+ *error = FormatErrorMessage(kInvalidRunAtError, IntToString(i));
+ return false;
+ }
+ }
+
for (size_t j = 0; j < matches->GetSize(); ++j) {
std::string match_str;
if (!matches->GetString(j, &match_str)) {
diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h
index 22e8fd6..5964ad4 100644
--- a/chrome/browser/extensions/extension.h
+++ b/chrome/browser/extensions/extension.h
@@ -44,9 +44,14 @@ class Extension {
static const wchar_t* kMatchesKey;
static const wchar_t* kNameKey;
static const wchar_t* kUserScriptsKey;
+ static const wchar_t* kRunAtKey;
static const wchar_t* kVersionKey;
static const wchar_t* kZipHashKey;
+ // Some values expected in manifests.
+ static const char* kRunAtDocumentStartValue;
+ static const char* kRunAtDocumentEndValue;
+
// Error messages returned from InitFromValue().
static const char* kInvalidDescriptionError;
static const char* kInvalidFileCountError;
@@ -59,6 +64,7 @@ class Extension {
static const char* kInvalidMatchError;
static const char* kInvalidMatchesError;
static const char* kInvalidNameError;
+ static const char* kInvalidRunAtError;
static const char* kInvalidUserScriptError;
static const char* kInvalidUserScriptsListError;
static const char* kInvalidVersionError;
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 7164dc1..23ef66f 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -45,6 +45,9 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
static const StringPiece kUserScriptEng("// ==/UserScript==");
static const StringPiece kIncludeDeclaration("// @include ");
static const StringPiece kMatchDeclaration("// @match ");
+ static const StringPiece kRunAtDeclaration("// @run-at ");
+ static const StringPiece kRunAtDocumentStartValue("document-start");
+ static const StringPiece kRunAtDocumentEndValue("document-end");
while (line_start < script_text.length()) {
line_end = script_text.find('\n', line_start);
@@ -76,6 +79,11 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader(
if (!pattern.Parse(value))
return false;
script->add_url_pattern(pattern);
+ } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
+ if (value == kRunAtDocumentStartValue)
+ script->set_run_location(UserScript::DOCUMENT_START);
+ else if (value != kRunAtDocumentEndValue)
+ return false;
}
// TODO(aa): Handle more types of metadata.
diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc
index c726ad3..9a0da2d 100644
--- a/chrome/common/extensions/user_script.cc
+++ b/chrome/common/extensions/user_script.cc
@@ -24,6 +24,7 @@ bool UserScript::MatchesUrl(const GURL& url) {
void UserScript::Pickle(::Pickle* pickle) {
pickle->WriteString(url_.spec());
+ pickle->WriteInt(run_location_);
// Don't write path as we don't need that in the renderer.
@@ -45,6 +46,11 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) {
CHECK(pickle.ReadString(iter, &url_spec));
url_ = GURL(url_spec);
+ int run_location = 0;
+ CHECK(pickle.ReadInt(iter, &run_location));
+ CHECK(run_location >= 0 && run_location < UserScript::RUN_LOCATION_LAST);
+ run_location_ = static_cast<UserScript::RunLocation>(run_location);
+
size_t num_globs = 0;
CHECK(pickle.ReadSize(iter, &num_globs));
diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h
index 6453c8e..671f158 100644
--- a/chrome/common/extensions/user_script.h
+++ b/chrome/common/extensions/user_script.h
@@ -17,7 +17,19 @@
// extension.
class UserScript {
public:
- UserScript(){}
+ // Locations that user scripts can be run inside the document.
+ enum RunLocation {
+ DOCUMENT_START, // After the documentElemnet is created, but before
+ // anything else happens.
+ DOCUMENT_END, // After the entire document is parsed. Same as
+ // DOMContentLoaded.
+
+ RUN_LOCATION_LAST // Leave this as the last item.
+ };
+
+ // Constructor. Default the run location to document end, which is like
+ // Greasemonkey and probably more useful for typical scripts.
+ UserScript() : run_location_(DOCUMENT_END) {}
// The URL to retrieve the content of this script at.
const GURL& url() const { return url_; }
@@ -27,6 +39,10 @@ class UserScript {
const FilePath& path() const { return path_; }
void set_path(const FilePath& path) { path_ = path; }
+ // 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; }
+
// 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_; }
@@ -60,6 +76,9 @@ class UserScript {
// The path to the content of the script.
FilePath path_;
+ // The location to run the script inside the document.
+ RunLocation run_location_;
+
// Greasemonkey-style globs that determine pages to inject the script into.
// These are only used with standalone scripts.
std::vector<std::string> globs_;
diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc
index 72204f4..2be936a 100644
--- a/chrome/common/extensions/user_script_unittest.cc
+++ b/chrome/common/extensions/user_script_unittest.cc
@@ -78,6 +78,7 @@ TEST(UserScriptTest, Pickle) {
UserScript script1;
script1.set_url(GURL("chrome-user-script:/foo.user.js"));
+ script1.set_run_location(UserScript::DOCUMENT_START);
script1.add_url_pattern(pattern1);
script1.add_url_pattern(pattern2);
@@ -99,3 +100,8 @@ TEST(UserScriptTest, Pickle) {
script2.url_patterns()[i].GetAsString());
}
}
+
+TEST(UserScriptTest, Defaults) {
+ UserScript script;
+ ASSERT_EQ(UserScript::DOCUMENT_END, script.run_location());
+}
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index bbe14c4..a8adb2f 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -1454,15 +1454,9 @@ void RenderView::DidFinishDocumentLoadForFrame(WebView* webview,
// Check whether we have new encoding name.
UpdateEncoding(frame, webview->GetMainFrameEncodingName());
- // Inject any user scripts. Do not inject into chrome UI pages, but do inject
- // into any other document.
- const GURL &gurl = frame->GetURL();
- if (g_render_thread && // Will be NULL when testing.
- (gurl.SchemeIs("file") ||
- gurl.SchemeIs("http") ||
- gurl.SchemeIs("https"))) {
- g_render_thread->user_script_slave()->InjectScripts(frame);
- }
+ if (g_render_thread) // Will be NULL during unit tests.
+ g_render_thread->user_script_slave()->InjectScripts(
+ frame, UserScript::DOCUMENT_END);
}
void RenderView::DidHandleOnloadEventsForFrame(WebView* webview,
@@ -1530,6 +1524,12 @@ void RenderView::WindowObjectCleared(WebFrame* webframe) {
#endif
}
+void RenderView::DocumentElementAvailable(WebFrame* frame) {
+ if (g_render_thread) // Will be NULL during unit tests.
+ g_render_thread->user_script_slave()->InjectScripts(
+ frame, UserScript::DOCUMENT_START);
+}
+
WindowOpenDisposition RenderView::DispositionForNavigationAction(
WebView* webview,
WebFrame* frame,
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index ee0446e..6cafaa6 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -214,6 +214,8 @@ class RenderView : public RenderWidget,
const GURL& source);
virtual void WindowObjectCleared(WebFrame* webframe);
+ virtual void DocumentElementAvailable(WebFrame* webframe);
+
virtual WindowOpenDisposition DispositionForNavigationAction(
WebView* webview,
WebFrame* frame,
diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc
index 8fc1e3a..bb4f54d 100644
--- a/chrome/renderer/user_script_slave.cc
+++ b/chrome/renderer/user_script_slave.cc
@@ -4,7 +4,9 @@
#include "chrome/renderer/user_script_slave.h"
+#include "base/histogram.h"
#include "base/logging.h"
+#include "base/perftimer.h"
#include "base/pickle.h"
#include "base/shared_memory.h"
#include "chrome/common/resource_bundle.h"
@@ -89,10 +91,15 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
return true;
}
-bool UserScriptSlave::InjectScripts(WebFrame* frame) {
+bool UserScriptSlave::InjectScripts(WebFrame* frame,
+ UserScript::RunLocation location) {
+ PerfTimer timer;
+ int num_matched = 0;
+
for (std::vector<UserScript*>::iterator script = scripts_.begin();
script != scripts_.end(); ++script) {
- if ((*script)->MatchesUrl(frame->GetURL())) {
+ if ((*script)->MatchesUrl(frame->GetURL()) &&
+ (*script)->run_location() == location) {
std::string inject(kUserScriptHead);
inject.append(api_js_.as_string());
inject.append(script_contents_[*script].as_string());
@@ -100,8 +107,17 @@ bool UserScriptSlave::InjectScripts(WebFrame* frame) {
frame->ExecuteJavaScript(inject,
GURL((*script)->url().spec()),
-user_script_start_line_);
+ ++num_matched;
}
}
+ if (location == UserScript::DOCUMENT_START) {
+ HISTOGRAM_COUNTS_100(L"UserScripts:DocStart:Count", num_matched);
+ HISTOGRAM_TIMES(L"UserScripts:DocStart:Time", timer.Elapsed());
+ } else {
+ HISTOGRAM_COUNTS_100(L"UserScripts:DocEnd:Count", num_matched);
+ HISTOGRAM_TIMES(L"UserScripts:DocEnd:Time", timer.Elapsed());
+ }
+
return true;
}
diff --git a/chrome/renderer/user_script_slave.h b/chrome/renderer/user_script_slave.h
index eeb34ae..a3363c1 100644
--- a/chrome/renderer/user_script_slave.h
+++ b/chrome/renderer/user_script_slave.h
@@ -27,7 +27,7 @@ class UserScriptSlave {
// Inject the appropriate scripts into a frame based on its URL.
// TODO(aa): Extract a UserScriptFrame interface out of this to improve
// testability.
- bool InjectScripts(WebFrame* frame);
+ bool InjectScripts(WebFrame* frame, UserScript::RunLocation location);
private:
// Shared memory containing raw script data.
diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc
index aaf3500..f8acc50 100644
--- a/webkit/glue/webframeloaderclient_impl.cc
+++ b/webkit/glue/webframeloaderclient_impl.cc
@@ -101,8 +101,10 @@ void WebFrameLoaderClient::windowObjectCleared() {
}
void WebFrameLoaderClient::documentElementAvailable() {
- // TODO(aa): Implement.
- notImplemented();
+ WebViewImpl* webview = webframe_->webview_impl();
+ WebViewDelegate* d = webview->delegate();
+ if (d)
+ d->DocumentElementAvailable(webframe_);
}
void WebFrameLoaderClient::didPerformFirstNavigation() const {
diff --git a/webkit/glue/webview_delegate.h b/webkit/glue/webview_delegate.h
index 2fe2351..892c25e 100644
--- a/webkit/glue/webview_delegate.h
+++ b/webkit/glue/webview_delegate.h
@@ -198,6 +198,12 @@ class WebViewDelegate : virtual public WebWidgetDelegate {
virtual void WindowObjectCleared(WebFrame* webframe) {
}
+ // Notifies that the documentElement for the document in a webframe has been
+ // created. This is called before anything else is parsed or executed for the
+ // document.
+ virtual void DocumentElementAvailable(WebFrame* webframe) {
+ }
+
// PolicyDelegate ----------------------------------------------------------
// This method is called to notify the delegate, and let it modify a