• 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_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 
GetContextTypeDescriptionString(Feature::Context context_type)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 
ScriptContext(const v8::Handle<v8::Context> & v8_context,blink::WebFrame * web_frame,const Extension * extension,Feature::Context context_type,const Extension * effective_extension,Feature::Context effective_context_type)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 
~ScriptContext()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 
Invalidate()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 
GetExtensionID() const101 const std::string& ScriptContext::GetExtensionID() const {
102   return extension_.get() ? extension_->id() : base::EmptyString();
103 }
104 
GetRenderView() const105 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 
GetRenderFrame() const111 content::RenderFrame* ScriptContext::GetRenderFrame() const {
112   if (web_frame_)
113     return content::RenderFrame::FromWebFrame(web_frame_);
114   return NULL;
115 }
116 
CallFunction(v8::Handle<v8::Function> function,int argc,v8::Handle<v8::Value> argv[]) const117 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 
GetAvailability(const std::string & api_name)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 
DispatchEvent(const char * event_name,v8::Handle<v8::Array> args) const153 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 
DispatchOnUnloadEvent()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 
GetContextTypeDescription()170 std::string ScriptContext::GetContextTypeDescription() {
171   return GetContextTypeDescriptionString(context_type_);
172 }
173 
GetEffectiveContextTypeDescription()174 std::string ScriptContext::GetEffectiveContextTypeDescription() {
175   return GetContextTypeDescriptionString(effective_context_type_);
176 }
177 
GetURL() const178 GURL ScriptContext::GetURL() const {
179   return web_frame() ? GetDataSourceURLForFrame(web_frame()) : GURL();
180 }
181 
IsAnyFeatureAvailableToContext(const Feature & api)182 bool ScriptContext::IsAnyFeatureAvailableToContext(const Feature& api) {
183   return ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
184       api, extension(), context_type(), GetDataSourceURLForFrame(web_frame()));
185 }
186 
187 // static
GetDataSourceURLForFrame(const blink::WebFrame * frame)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
GetEffectiveDocumentURL(const blink::WebFrame * frame,const GURL & document_url,bool match_about_blank)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 
GetContext()232 ScriptContext* ScriptContext::GetContext() { return this; }
233 
OnResponseReceived(const std::string & name,int request_id,bool success,const base::ListValue & response,const std::string & error)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 
Run(const std::string & source,const std::string & resource_name)258 void ScriptContext::Run(const std::string& source,
259                         const std::string& resource_name) {
260   module_system_->RunString(source, resource_name);
261 }
262 
Call(v8::Handle<v8::Function> function,v8::Handle<v8::Value> receiver,int argc,v8::Handle<v8::Value> argv[])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 
GetContextHolder()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