| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/renderer/user_script_set.h" |
| |
| #include "content/public/common/url_constants.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_messages.h" |
| #include "extensions/common/extension_set.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "extensions/renderer/extensions_renderer_client.h" |
| #include "extensions/renderer/script_context.h" |
| #include "extensions/renderer/script_injection.h" |
| #include "extensions/renderer/user_script_injector.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebFrame.h" |
| #include "url/gurl.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| GURL GetDocumentUrlForFrame(blink::WebFrame* frame) { |
| GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); |
| if (!data_source_url.is_empty() && frame->isViewSourceModeEnabled()) { |
| data_source_url = GURL(content::kViewSourceScheme + std::string(":") + |
| data_source_url.spec()); |
| } |
| |
| return data_source_url; |
| } |
| |
| } // namespace |
| |
| UserScriptSet::UserScriptSet(const ExtensionSet* extensions) |
| : extensions_(extensions) { |
| } |
| |
| UserScriptSet::~UserScriptSet() { |
| } |
| |
| void UserScriptSet::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void UserScriptSet::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void UserScriptSet::GetActiveExtensionIds( |
| std::set<std::string>* ids) const { |
| for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); |
| iter != scripts_.end(); |
| ++iter) { |
| DCHECK(!(*iter)->extension_id().empty()); |
| ids->insert((*iter)->extension_id()); |
| } |
| } |
| |
| void UserScriptSet::GetInjections( |
| ScopedVector<ScriptInjection>* injections, |
| blink::WebFrame* web_frame, |
| int tab_id, |
| UserScript::RunLocation run_location) { |
| GURL document_url = GetDocumentUrlForFrame(web_frame); |
| for (ScopedVector<UserScript>::const_iterator iter = scripts_.begin(); |
| iter != scripts_.end(); |
| ++iter) { |
| const Extension* extension = extensions_->GetByID((*iter)->extension_id()); |
| if (!extension) |
| continue; |
| scoped_ptr<ScriptInjection> injection = GetInjectionForScript( |
| *iter, web_frame, tab_id, run_location, document_url, extension); |
| if (injection.get()) |
| injections->push_back(injection.release()); |
| } |
| } |
| |
| bool UserScriptSet::UpdateUserScripts( |
| base::SharedMemoryHandle shared_memory, |
| const std::set<std::string>& changed_extensions) { |
| bool only_inject_incognito = |
| ExtensionsRendererClient::Get()->IsIncognitoProcess(); |
| |
| // Create the shared memory object (read only). |
| shared_memory_.reset(new base::SharedMemory(shared_memory, true)); |
| if (!shared_memory_.get()) |
| return false; |
| |
| // First get the size of the memory block. |
| if (!shared_memory_->Map(sizeof(Pickle::Header))) |
| return false; |
| Pickle::Header* pickle_header = |
| reinterpret_cast<Pickle::Header*>(shared_memory_->memory()); |
| |
| // Now map in the rest of the block. |
| int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size; |
| shared_memory_->Unmap(); |
| if (!shared_memory_->Map(pickle_size)) |
| return false; |
| |
| // Unpickle scripts. |
| uint64 num_scripts = 0; |
| Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size); |
| PickleIterator iter(pickle); |
| CHECK(pickle.ReadUInt64(&iter, &num_scripts)); |
| |
| scripts_.clear(); |
| scripts_.reserve(num_scripts); |
| for (uint64 i = 0; i < num_scripts; ++i) { |
| scoped_ptr<UserScript> script(new UserScript()); |
| script->Unpickle(pickle, &iter); |
| |
| // Note that this is a pointer into shared memory. We don't own it. It gets |
| // cleared up when the last renderer or browser process drops their |
| // reference to the shared memory. |
| for (size_t j = 0; j < script->js_scripts().size(); ++j) { |
| const char* body = NULL; |
| int body_length = 0; |
| CHECK(pickle.ReadData(&iter, &body, &body_length)); |
| script->js_scripts()[j].set_external_content( |
| base::StringPiece(body, body_length)); |
| } |
| for (size_t j = 0; j < script->css_scripts().size(); ++j) { |
| const char* body = NULL; |
| int body_length = 0; |
| CHECK(pickle.ReadData(&iter, &body, &body_length)); |
| script->css_scripts()[j].set_external_content( |
| base::StringPiece(body, body_length)); |
| } |
| |
| if (only_inject_incognito && !script->is_incognito_enabled()) |
| continue; // This script shouldn't run in an incognito tab. |
| |
| scripts_.push_back(script.release()); |
| } |
| |
| FOR_EACH_OBSERVER(Observer, |
| observers_, |
| OnUserScriptsUpdated(changed_extensions, scripts_.get())); |
| return true; |
| } |
| |
| scoped_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript( |
| UserScript* script, |
| blink::WebFrame* web_frame, |
| int tab_id, |
| UserScript::RunLocation run_location, |
| const GURL& document_url, |
| const Extension* extension) { |
| scoped_ptr<ScriptInjection> injection; |
| if (web_frame->parent() && !script->match_all_frames()) |
| return injection.Pass(); // Only match subframes if the script declared it. |
| |
| GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( |
| web_frame, document_url, script->match_about_blank()); |
| |
| if (!script->MatchesURL(effective_document_url)) |
| return injection.Pass(); |
| |
| if (extension->permissions_data()->GetContentScriptAccess( |
| extension, |
| effective_document_url, |
| web_frame->top()->document().url(), |
| -1, // Content scripts are not tab-specific. |
| -1, // We don't have a process id in this context. |
| NULL /* ignore error */) == PermissionsData::ACCESS_DENIED) { |
| return injection.Pass(); |
| } |
| |
| bool inject_css = !script->css_scripts().empty() && |
| run_location == UserScript::DOCUMENT_START; |
| bool inject_js = |
| !script->js_scripts().empty() && script->run_location() == run_location; |
| if (inject_css || inject_js) { |
| injection.reset(new ScriptInjection( |
| scoped_ptr<ScriptInjector>(new UserScriptInjector(script, this)), |
| web_frame, |
| extension->id(), |
| run_location, |
| tab_id)); |
| } |
| return injection.Pass(); |
| } |
| |
| } // namespace extensions |