| // 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_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, | 
 |         false /* is_declarative */); | 
 |     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. | 
 |   size_t num_scripts = 0; | 
 |   Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()), pickle_size); | 
 |   PickleIterator iter(pickle); | 
 |   CHECK(pickle.ReadSizeT(&iter, &num_scripts)); | 
 |  | 
 |   scripts_.clear(); | 
 |   scripts_.reserve(num_scripts); | 
 |   for (size_t 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::GetDeclarativeScriptInjection( | 
 |     int script_id, | 
 |     blink::WebFrame* web_frame, | 
 |     int tab_id, | 
 |     UserScript::RunLocation run_location, | 
 |     const GURL& document_url, | 
 |     const Extension* extension) { | 
 |   for (ScopedVector<UserScript>::const_iterator it = scripts_.begin(); | 
 |        it != scripts_.end(); | 
 |        ++it) { | 
 |     if ((*it)->id() == script_id) { | 
 |       return GetInjectionForScript(*it, | 
 |                                    web_frame, | 
 |                                    tab_id, | 
 |                                    run_location, | 
 |                                    document_url, | 
 |                                    extension, | 
 |                                    true /* is_declarative */); | 
 |     } | 
 |   } | 
 |   return scoped_ptr<ScriptInjection>(); | 
 | } | 
 |  | 
 | // TODO(dcheng): Scripts can't be injected on a remote frame, so this function | 
 | // signature needs to be updated. | 
 | 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, | 
 |     bool is_declarative) { | 
 |   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(); | 
 |  | 
 |   scoped_ptr<ScriptInjector> injector(new UserScriptInjector(script, | 
 |                                                              this, | 
 |                                                              is_declarative)); | 
 |   if (injector->CanExecuteOnFrame( | 
 |           extension, | 
 |           web_frame, | 
 |           -1,  // Content scripts are not tab-specific. | 
 |           web_frame->top()->document().url()) == | 
 |       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( | 
 |         injector.Pass(), | 
 |         web_frame->toWebLocalFrame(), | 
 |         extension->id(), | 
 |         run_location, | 
 |         tab_id)); | 
 |   } | 
 |   return injection.Pass(); | 
 | } | 
 |  | 
 | }  // namespace extensions |