diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-14 04:15:16 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-14 04:15:16 +0000 |
commit | 0afe827a49501c6801c3c9efbbdb1f64aa010beb (patch) | |
tree | dba6031dd45eb3f0a0435b9750976b1cd432abc1 | |
parent | 27eef9c8edf84061f4e36a3bd26ff7538092a22b (diff) | |
download | chromium_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.cc | 23 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.h | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.cc | 8 | ||||
-rw-r--r-- | chrome/common/extensions/user_script.cc | 6 | ||||
-rw-r--r-- | chrome/common/extensions/user_script.h | 21 | ||||
-rw-r--r-- | chrome/common/extensions/user_script_unittest.cc | 6 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 18 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 2 | ||||
-rw-r--r-- | chrome/renderer/user_script_slave.cc | 20 | ||||
-rw-r--r-- | chrome/renderer/user_script_slave.h | 2 | ||||
-rw-r--r-- | webkit/glue/webframeloaderclient_impl.cc | 6 | ||||
-rw-r--r-- | webkit/glue/webview_delegate.h | 6 |
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 |