Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/renderer/extensions/user_script_slave.h"
      6 
      7 #include <map>
      8 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/memory/shared_memory.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/perftimer.h"
     14 #include "base/pickle.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "chrome/common/extensions/csp_handler.h"
     17 #include "chrome/common/extensions/extension.h"
     18 #include "chrome/common/extensions/extension_messages.h"
     19 #include "chrome/common/extensions/extension_set.h"
     20 #include "chrome/common/extensions/permissions/permissions_data.h"
     21 #include "chrome/common/url_constants.h"
     22 #include "chrome/renderer/chrome_render_process_observer.h"
     23 #include "chrome/renderer/extensions/dom_activity_logger.h"
     24 #include "chrome/renderer/extensions/extension_groups.h"
     25 #include "chrome/renderer/isolated_world_ids.h"
     26 #include "content/public/renderer/render_thread.h"
     27 #include "content/public/renderer/render_view.h"
     28 #include "grit/renderer_resources.h"
     29 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     30 #include "third_party/WebKit/public/platform/WebVector.h"
     31 #include "third_party/WebKit/public/web/WebDataSource.h"
     32 #include "third_party/WebKit/public/web/WebDocument.h"
     33 #include "third_party/WebKit/public/web/WebFrame.h"
     34 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
     35 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
     36 #include "third_party/WebKit/public/web/WebView.h"
     37 #include "ui/base/resource/resource_bundle.h"
     38 #include "url/gurl.h"
     39 
     40 using WebKit::WebFrame;
     41 using WebKit::WebSecurityOrigin;
     42 using WebKit::WebSecurityPolicy;
     43 using WebKit::WebString;
     44 using WebKit::WebVector;
     45 using WebKit::WebView;
     46 using content::RenderThread;
     47 
     48 namespace extensions {
     49 
     50 // These two strings are injected before and after the Greasemonkey API and
     51 // user script to wrap it in an anonymous scope.
     52 static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
     53 static const char kUserScriptTail[] = "\n})(window);";
     54 
     55 int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension,
     56                                                     WebFrame* frame) {
     57   static int g_next_isolated_world_id = chrome::ISOLATED_WORLD_ID_EXTENSIONS;
     58 
     59   IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id());
     60   if (iter != isolated_world_ids_.end()) {
     61     // We need to set the isolated world origin and CSP even if it's not a new
     62     // world since these are stored per frame, and we might not have used this
     63     // isolated world in this frame before.
     64     frame->setIsolatedWorldSecurityOrigin(
     65         iter->second,
     66         WebSecurityOrigin::create(extension->url()));
     67     frame->setIsolatedWorldContentSecurityPolicy(
     68         iter->second,
     69         WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
     70     return iter->second;
     71   }
     72 
     73   int new_id = g_next_isolated_world_id;
     74   ++g_next_isolated_world_id;
     75 
     76   // This map will tend to pile up over time, but realistically, you're never
     77   // going to have enough extensions for it to matter.
     78   isolated_world_ids_[extension->id()] = new_id;
     79   InitializeIsolatedWorld(new_id, extension);
     80   frame->setIsolatedWorldSecurityOrigin(
     81       new_id,
     82       WebSecurityOrigin::create(extension->url()));
     83   frame->setIsolatedWorldContentSecurityPolicy(
     84       new_id,
     85       WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
     86   return new_id;
     87 }
     88 
     89 std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
     90     int isolated_world_id) {
     91   for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
     92        iter != isolated_world_ids_.end(); ++iter) {
     93     if (iter->second == isolated_world_id)
     94       return iter->first;
     95   }
     96   return std::string();
     97 }
     98 
     99 // static
    100 void UserScriptSlave::InitializeIsolatedWorld(int isolated_world_id,
    101                                               const Extension* extension) {
    102   const URLPatternSet& permissions =
    103       PermissionsData::GetEffectiveHostPermissions(extension);
    104   for (URLPatternSet::const_iterator i = permissions.begin();
    105        i != permissions.end(); ++i) {
    106     const char* schemes[] = {
    107       chrome::kHttpScheme,
    108       chrome::kHttpsScheme,
    109       chrome::kFileScheme,
    110       chrome::kChromeUIScheme,
    111     };
    112     for (size_t j = 0; j < arraysize(schemes); ++j) {
    113       if (i->MatchesScheme(schemes[j])) {
    114         WebSecurityPolicy::addOriginAccessWhitelistEntry(
    115             extension->url(),
    116             WebString::fromUTF8(schemes[j]),
    117             WebString::fromUTF8(i->host()),
    118             i->match_subdomains());
    119       }
    120     }
    121   }
    122 }
    123 
    124 void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
    125   isolated_world_ids_.erase(extension_id);
    126 }
    127 
    128 UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
    129     : script_deleter_(&scripts_), extensions_(extensions) {
    130   api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
    131       IDR_GREASEMONKEY_API_JS);
    132 }
    133 
    134 UserScriptSlave::~UserScriptSlave() {}
    135 
    136 void UserScriptSlave::GetActiveExtensions(
    137     std::set<std::string>* extension_ids) {
    138   for (size_t i = 0; i < scripts_.size(); ++i) {
    139     DCHECK(!scripts_[i]->extension_id().empty());
    140     extension_ids->insert(scripts_[i]->extension_id());
    141   }
    142 }
    143 
    144 bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
    145   scripts_.clear();
    146 
    147   bool only_inject_incognito =
    148       ChromeRenderProcessObserver::is_incognito_process();
    149 
    150   // Create the shared memory object (read only).
    151   shared_memory_.reset(new base::SharedMemory(shared_memory, true));
    152   if (!shared_memory_.get())
    153     return false;
    154 
    155   // First get the size of the memory block.
    156   if (!shared_memory_->Map(sizeof(Pickle::Header)))
    157     return false;
    158   Pickle::Header* pickle_header =
    159       reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
    160 
    161   // Now map in the rest of the block.
    162   int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
    163   shared_memory_->Unmap();
    164   if (!shared_memory_->Map(pickle_size))
    165     return false;
    166 
    167   // Unpickle scripts.
    168   uint64 num_scripts = 0;
    169   Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
    170                 pickle_size);
    171   PickleIterator iter(pickle);
    172   CHECK(pickle.ReadUInt64(&iter, &num_scripts));
    173 
    174   scripts_.reserve(num_scripts);
    175   for (uint64 i = 0; i < num_scripts; ++i) {
    176     scripts_.push_back(new UserScript());
    177     UserScript* script = scripts_.back();
    178     script->Unpickle(pickle, &iter);
    179 
    180     // Note that this is a pointer into shared memory. We don't own it. It gets
    181     // cleared up when the last renderer or browser process drops their
    182     // reference to the shared memory.
    183     for (size_t j = 0; j < script->js_scripts().size(); ++j) {
    184       const char* body = NULL;
    185       int body_length = 0;
    186       CHECK(pickle.ReadData(&iter, &body, &body_length));
    187       script->js_scripts()[j].set_external_content(
    188           base::StringPiece(body, body_length));
    189     }
    190     for (size_t j = 0; j < script->css_scripts().size(); ++j) {
    191       const char* body = NULL;
    192       int body_length = 0;
    193       CHECK(pickle.ReadData(&iter, &body, &body_length));
    194       script->css_scripts()[j].set_external_content(
    195           base::StringPiece(body, body_length));
    196     }
    197 
    198     if (only_inject_incognito && !script->is_incognito_enabled()) {
    199       // This script shouldn't run in an incognito tab.
    200       delete script;
    201       scripts_.pop_back();
    202     }
    203   }
    204 
    205   // Push user styles down into WebCore
    206   RenderThread::Get()->EnsureWebKitInitialized();
    207   WebView::removeAllUserContent();
    208   for (size_t i = 0; i < scripts_.size(); ++i) {
    209     UserScript* script = scripts_[i];
    210     if (script->css_scripts().empty())
    211       continue;
    212 
    213     WebVector<WebString> patterns;
    214     std::vector<WebString> temp_patterns;
    215     const URLPatternSet& url_patterns = script->url_patterns();
    216     for (URLPatternSet::const_iterator k = url_patterns.begin();
    217          k != url_patterns.end(); ++k) {
    218       URLPatternList explicit_patterns = k->ConvertToExplicitSchemes();
    219       for (size_t m = 0; m < explicit_patterns.size(); ++m) {
    220         temp_patterns.push_back(WebString::fromUTF8(
    221             explicit_patterns[m].GetAsString()));
    222       }
    223     }
    224     patterns.assign(temp_patterns);
    225 
    226     for (size_t j = 0; j < script->css_scripts().size(); ++j) {
    227       const UserScript::File& file = scripts_[i]->css_scripts()[j];
    228       std::string content = file.GetContent().as_string();
    229 
    230       WebView::addUserStyleSheet(
    231           WebString::fromUTF8(content),
    232           patterns,
    233            script->match_all_frames() ?
    234               WebView::UserContentInjectInAllFrames :
    235               WebView::UserContentInjectInTopFrameOnly,
    236           WebView::UserStyleInjectInExistingDocuments);
    237     }
    238   }
    239 
    240   return true;
    241 }
    242 
    243 GURL UserScriptSlave::GetDataSourceURLForFrame(const WebFrame* frame) {
    244   // Normally we would use frame->document().url() to determine the document's
    245   // URL, but to decide whether to inject a content script, we use the URL from
    246   // the data source. This "quirk" helps prevents content scripts from
    247   // inadvertently adding DOM elements to the compose iframe in Gmail because
    248   // the compose iframe's dataSource URL is about:blank, but the document URL
    249   // changes to match the parent document after Gmail document.writes into
    250   // it to create the editor.
    251   // http://code.google.com/p/chromium/issues/detail?id=86742
    252   WebKit::WebDataSource* data_source = frame->provisionalDataSource() ?
    253       frame->provisionalDataSource() : frame->dataSource();
    254   CHECK(data_source);
    255   return GURL(data_source->request().url());
    256 }
    257 
    258 void UserScriptSlave::InjectScripts(WebFrame* frame,
    259                                     UserScript::RunLocation location) {
    260   GURL data_source_url = GetDataSourceURLForFrame(frame);
    261   if (data_source_url.is_empty())
    262     return;
    263 
    264   if (frame->isViewSourceModeEnabled())
    265     data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
    266                            data_source_url.spec());
    267 
    268   PerfTimer timer;
    269   int num_css = 0;
    270   int num_scripts = 0;
    271 
    272   ExecutingScriptsMap extensions_executing_scripts;
    273 
    274   for (size_t i = 0; i < scripts_.size(); ++i) {
    275     std::vector<WebScriptSource> sources;
    276     UserScript* script = scripts_[i];
    277 
    278     if (frame->parent() && !script->match_all_frames())
    279       continue;  // Only match subframes if the script declared it wanted to.
    280 
    281     const Extension* extension = extensions_->GetByID(script->extension_id());
    282 
    283     // Since extension info is sent separately from user script info, they can
    284     // be out of sync. We just ignore this situation.
    285     if (!extension)
    286       continue;
    287 
    288     // Content scripts are not tab-specific.
    289     const int kNoTabId = -1;
    290     // We don't have a process id in this context.
    291     const int kNoProcessId = -1;
    292     if (!PermissionsData::CanExecuteScriptOnPage(extension,
    293                                                  data_source_url,
    294                                                  frame->top()->document().url(),
    295                                                  kNoTabId,
    296                                                  script,
    297                                                  kNoProcessId,
    298                                                  NULL)) {
    299       continue;
    300     }
    301 
    302     // We rely on WebCore for CSS injection, but it's still useful to know how
    303     // many css files there are.
    304     if (location == UserScript::DOCUMENT_START)
    305       num_css += script->css_scripts().size();
    306 
    307     if (script->run_location() == location) {
    308       num_scripts += script->js_scripts().size();
    309       for (size_t j = 0; j < script->js_scripts().size(); ++j) {
    310         UserScript::File &file = script->js_scripts()[j];
    311         std::string content = file.GetContent().as_string();
    312 
    313         // We add this dumb function wrapper for standalone user script to
    314         // emulate what Greasemonkey does.
    315         // TODO(aa): I think that maybe "is_standalone" scripts don't exist
    316         // anymore. Investigate.
    317         if (script->is_standalone() || script->emulate_greasemonkey()) {
    318           content.insert(0, kUserScriptHead);
    319           content += kUserScriptTail;
    320         }
    321         sources.push_back(
    322             WebScriptSource(WebString::fromUTF8(content), file.url()));
    323       }
    324     }
    325 
    326     if (!sources.empty()) {
    327       // Emulate Greasemonkey API for scripts that were converted to extensions
    328       // and "standalone" user scripts.
    329       if (script->is_standalone() || script->emulate_greasemonkey()) {
    330         sources.insert(sources.begin(),
    331             WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
    332       }
    333 
    334       int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame);
    335 
    336       PerfTimer exec_timer;
    337       DOMActivityLogger::AttachToWorld(
    338           isolated_world_id,
    339           extension->id(),
    340           UserScriptSlave::GetDataSourceURLForFrame(frame),
    341           frame->document().title());
    342       frame->executeScriptInIsolatedWorld(
    343           isolated_world_id, &sources.front(), sources.size(),
    344           EXTENSION_GROUP_CONTENT_SCRIPTS);
    345       UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
    346 
    347       for (std::vector<WebScriptSource>::const_iterator iter = sources.begin();
    348            iter != sources.end(); ++iter) {
    349         extensions_executing_scripts[extension->id()].insert(
    350             GURL(iter->url).path());
    351       }
    352     }
    353   }
    354 
    355   // Notify the browser if any extensions are now executing scripts.
    356   if (!extensions_executing_scripts.empty()) {
    357     WebKit::WebFrame* top_frame = frame->top();
    358     content::RenderView* render_view =
    359         content::RenderView::FromWebView(top_frame->view());
    360     render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
    361         render_view->GetRoutingID(),
    362         extensions_executing_scripts,
    363         render_view->GetPageId(),
    364         GetDataSourceURLForFrame(top_frame)));
    365   }
    366 
    367   // Log debug info.
    368   if (location == UserScript::DOCUMENT_START) {
    369     UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css);
    370     UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts);
    371     if (num_css || num_scripts)
    372       UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed());
    373   } else if (location == UserScript::DOCUMENT_END) {
    374     UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts);
    375     if (num_scripts)
    376       UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed());
    377   } else if (location == UserScript::DOCUMENT_IDLE) {
    378     UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts);
    379     if (num_scripts)
    380       UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed());
    381   } else {
    382     NOTREACHED();
    383   }
    384 }
    385 
    386 }  // namespace extensions
    387