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