Home | History | Annotate | Download | only in renderer
      1 // Copyright 2014 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 "extensions/renderer/script_context.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/strings/string_split.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/values.h"
     12 #include "content/public/common/url_constants.h"
     13 #include "content/public/renderer/render_view.h"
     14 #include "content/public/renderer/v8_value_converter.h"
     15 #include "extensions/common/extension.h"
     16 #include "extensions/common/extension_api.h"
     17 #include "extensions/common/extension_urls.h"
     18 #include "extensions/common/features/base_feature_provider.h"
     19 #include "third_party/WebKit/public/web/WebDataSource.h"
     20 #include "third_party/WebKit/public/web/WebDocument.h"
     21 #include "third_party/WebKit/public/web/WebFrame.h"
     22 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
     23 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
     24 #include "third_party/WebKit/public/web/WebView.h"
     25 #include "v8/include/v8.h"
     26 
     27 using content::V8ValueConverter;
     28 
     29 namespace extensions {
     30 
     31 ScriptContext::ScriptContext(const v8::Handle<v8::Context>& v8_context,
     32                              blink::WebFrame* web_frame,
     33                              const Extension* extension,
     34                              Feature::Context context_type)
     35     : v8_context_(v8_context),
     36       web_frame_(web_frame),
     37       extension_(extension),
     38       context_type_(context_type),
     39       safe_builtins_(this),
     40       isolate_(v8_context->GetIsolate()) {
     41   VLOG(1) << "Created context:\n"
     42           << "  extension id: " << GetExtensionID() << "\n"
     43           << "  frame:        " << web_frame_ << "\n"
     44           << "  context type: " << GetContextTypeDescription();
     45 }
     46 
     47 ScriptContext::~ScriptContext() {
     48   VLOG(1) << "Destroyed context for extension\n"
     49           << "  extension id: " << GetExtensionID();
     50   Invalidate();
     51 }
     52 
     53 void ScriptContext::Invalidate() {
     54   if (!is_valid())
     55     return;
     56   if (module_system_)
     57     module_system_->Invalidate();
     58   web_frame_ = NULL;
     59   v8_context_.reset();
     60 }
     61 
     62 const std::string& ScriptContext::GetExtensionID() const {
     63   return extension_.get() ? extension_->id() : base::EmptyString();
     64 }
     65 
     66 content::RenderView* ScriptContext::GetRenderView() const {
     67   if (web_frame_ && web_frame_->view())
     68     return content::RenderView::FromWebView(web_frame_->view());
     69   else
     70     return NULL;
     71 }
     72 
     73 v8::Local<v8::Value> ScriptContext::CallFunction(
     74     v8::Handle<v8::Function> function,
     75     int argc,
     76     v8::Handle<v8::Value> argv[]) const {
     77   v8::EscapableHandleScope handle_scope(isolate());
     78   v8::Context::Scope scope(v8_context());
     79 
     80   blink::WebScopedMicrotaskSuppression suppression;
     81   if (!is_valid()) {
     82     return handle_scope.Escape(
     83         v8::Local<v8::Primitive>(v8::Undefined(isolate())));
     84   }
     85 
     86   v8::Handle<v8::Object> global = v8_context()->Global();
     87   if (!web_frame_)
     88     return handle_scope.Escape(function->Call(global, argc, argv));
     89   return handle_scope.Escape(
     90       v8::Local<v8::Value>(web_frame_->callFunctionEvenIfScriptDisabled(
     91           function, global, argc, argv)));
     92 }
     93 
     94 Feature::Availability ScriptContext::GetAvailability(
     95     const std::string& api_name) {
     96   // Hack: Hosted apps should have the availability of messaging APIs based on
     97   // the URL of the page (which might have access depending on some extension
     98   // with externally_connectable), not whether the app has access to messaging
     99   // (which it won't).
    100   const Extension* extension = extension_.get();
    101   if (extension && extension->is_hosted_app() &&
    102       (api_name == "runtime.connect" || api_name == "runtime.sendMessage")) {
    103     extension = NULL;
    104   }
    105   return ExtensionAPI::GetSharedInstance()->IsAvailable(
    106       api_name, extension, context_type_, GetURL());
    107 }
    108 
    109 void ScriptContext::DispatchEvent(const char* event_name,
    110                                   v8::Handle<v8::Array> args) const {
    111   v8::HandleScope handle_scope(isolate());
    112   v8::Context::Scope context_scope(v8_context());
    113 
    114   v8::Handle<v8::Value> argv[] = {
    115       v8::String::NewFromUtf8(isolate(), event_name), args};
    116   module_system_->CallModuleMethod(
    117       kEventBindings, "dispatchEvent", arraysize(argv), argv);
    118 }
    119 
    120 void ScriptContext::DispatchOnUnloadEvent() {
    121   module_system_->CallModuleMethod("unload_event", "dispatch");
    122 }
    123 
    124 std::string ScriptContext::GetContextTypeDescription() {
    125   switch (context_type_) {
    126     case Feature::UNSPECIFIED_CONTEXT:
    127       return "UNSPECIFIED";
    128     case Feature::BLESSED_EXTENSION_CONTEXT:
    129       return "BLESSED_EXTENSION";
    130     case Feature::UNBLESSED_EXTENSION_CONTEXT:
    131       return "UNBLESSED_EXTENSION";
    132     case Feature::CONTENT_SCRIPT_CONTEXT:
    133       return "CONTENT_SCRIPT";
    134     case Feature::WEB_PAGE_CONTEXT:
    135       return "WEB_PAGE";
    136     case Feature::BLESSED_WEB_PAGE_CONTEXT:
    137       return "BLESSED_WEB_PAGE";
    138   }
    139   NOTREACHED();
    140   return std::string();
    141 }
    142 
    143 GURL ScriptContext::GetURL() const {
    144   return web_frame() ? GetDataSourceURLForFrame(web_frame()) : GURL();
    145 }
    146 
    147 bool ScriptContext::IsAnyFeatureAvailableToContext(const Feature& api) {
    148   return ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
    149       api, extension(), context_type(), GetDataSourceURLForFrame(web_frame()));
    150 }
    151 
    152 // static
    153 GURL ScriptContext::GetDataSourceURLForFrame(const blink::WebFrame* frame) {
    154   // Normally we would use frame->document().url() to determine the document's
    155   // URL, but to decide whether to inject a content script, we use the URL from
    156   // the data source. This "quirk" helps prevents content scripts from
    157   // inadvertently adding DOM elements to the compose iframe in Gmail because
    158   // the compose iframe's dataSource URL is about:blank, but the document URL
    159   // changes to match the parent document after Gmail document.writes into
    160   // it to create the editor.
    161   // http://code.google.com/p/chromium/issues/detail?id=86742
    162   blink::WebDataSource* data_source = frame->provisionalDataSource()
    163                                           ? frame->provisionalDataSource()
    164                                           : frame->dataSource();
    165   CHECK(data_source);
    166   return GURL(data_source->request().url());
    167 }
    168 
    169 // static
    170 GURL ScriptContext::GetEffectiveDocumentURL(const blink::WebFrame* frame,
    171                                             const GURL& document_url,
    172                                             bool match_about_blank) {
    173   // Common scenario. If |match_about_blank| is false (as is the case in most
    174   // extensions), or if the frame is not an about:-page, just return
    175   // |document_url| (supposedly the URL of the frame).
    176   if (!match_about_blank || !document_url.SchemeIs(url::kAboutScheme))
    177     return document_url;
    178 
    179   // Non-sandboxed about:blank and about:srcdoc pages inherit their security
    180   // origin from their parent frame/window. So, traverse the frame/window
    181   // hierarchy to find the closest non-about:-page and return its URL.
    182   const blink::WebFrame* parent = frame;
    183   do {
    184     parent = parent->parent() ? parent->parent() : parent->opener();
    185   } while (parent != NULL &&
    186            GURL(parent->document().url()).SchemeIs(url::kAboutScheme));
    187 
    188   if (parent) {
    189     // Only return the parent URL if the frame can access it.
    190     const blink::WebDocument& parent_document = parent->document();
    191     if (frame->document().securityOrigin().canAccess(
    192             parent_document.securityOrigin()))
    193       return parent_document.url();
    194   }
    195   return document_url;
    196 }
    197 
    198 ScriptContext* ScriptContext::GetContext() { return this; }
    199 
    200 void ScriptContext::OnResponseReceived(const std::string& name,
    201                                        int request_id,
    202                                        bool success,
    203                                        const base::ListValue& response,
    204                                        const std::string& error) {
    205   v8::HandleScope handle_scope(isolate());
    206 
    207   scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
    208   v8::Handle<v8::Value> argv[] = {
    209       v8::Integer::New(isolate(), request_id),
    210       v8::String::NewFromUtf8(isolate(), name.c_str()),
    211       v8::Boolean::New(isolate(), success),
    212       converter->ToV8Value(&response, v8_context_.NewHandle(isolate())),
    213       v8::String::NewFromUtf8(isolate(), error.c_str())};
    214 
    215   v8::Handle<v8::Value> retval = module_system()->CallModuleMethod(
    216       "sendRequest", "handleResponse", arraysize(argv), argv);
    217 
    218   // In debug, the js will validate the callback parameters and return a
    219   // string if a validation error has occured.
    220   DCHECK(retval.IsEmpty() || retval->IsUndefined())
    221       << *v8::String::Utf8Value(retval);
    222 }
    223 
    224 }  // namespace extensions
    225