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