• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ScriptContext(const v8::Handle<v8::Context> & v8_context,blink::WebFrame * web_frame,const Extension * extension,Feature::Context context_type)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 
~ScriptContext()47 ScriptContext::~ScriptContext() {
48   VLOG(1) << "Destroyed context for extension\n"
49           << "  extension id: " << GetExtensionID();
50   Invalidate();
51 }
52 
Invalidate()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 
GetExtensionID() const62 const std::string& ScriptContext::GetExtensionID() const {
63   return extension_.get() ? extension_->id() : base::EmptyString();
64 }
65 
GetRenderView() const66 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 
CallFunction(v8::Handle<v8::Function> function,int argc,v8::Handle<v8::Value> argv[]) const73 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 
GetAvailability(const std::string & api_name)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 
DispatchEvent(const char * event_name,v8::Handle<v8::Array> args) const109 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 
DispatchOnUnloadEvent()120 void ScriptContext::DispatchOnUnloadEvent() {
121   module_system_->CallModuleMethod("unload_event", "dispatch");
122 }
123 
GetContextTypeDescription()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 
GetURL() const143 GURL ScriptContext::GetURL() const {
144   return web_frame() ? GetDataSourceURLForFrame(web_frame()) : GURL();
145 }
146 
IsAnyFeatureAvailableToContext(const Feature & api)147 bool ScriptContext::IsAnyFeatureAvailableToContext(const Feature& api) {
148   return ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
149       api, extension(), context_type(), GetDataSourceURLForFrame(web_frame()));
150 }
151 
152 // static
GetDataSourceURLForFrame(const blink::WebFrame * frame)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
GetEffectiveDocumentURL(const blink::WebFrame * frame,const GURL & document_url,bool match_about_blank)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 
GetContext()198 ScriptContext* ScriptContext::GetContext() { return this; }
199 
OnResponseReceived(const std::string & name,int request_id,bool success,const base::ListValue & response,const std::string & error)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