1 // Copyright (c) 2012 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 "chrome/renderer/extensions/chrome_v8_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/values.h" 11 #include "chrome/common/extensions/extension_set.h" 12 #include "chrome/common/extensions/features/base_feature_provider.h" 13 #include "chrome/renderer/extensions/chrome_v8_extension.h" 14 #include "chrome/renderer/extensions/module_system.h" 15 #include "chrome/renderer/extensions/user_script_slave.h" 16 #include "content/public/renderer/render_view.h" 17 #include "content/public/renderer/v8_value_converter.h" 18 #include "extensions/common/extension.h" 19 #include "extensions/common/extension_api.h" 20 #include "third_party/WebKit/public/web/WebFrame.h" 21 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h" 22 #include "third_party/WebKit/public/web/WebView.h" 23 #include "v8/include/v8.h" 24 25 using content::V8ValueConverter; 26 27 namespace extensions { 28 29 ChromeV8Context::ChromeV8Context(v8::Handle<v8::Context> v8_context, 30 blink::WebFrame* web_frame, 31 const Extension* extension, 32 Feature::Context context_type) 33 : v8_context_(v8_context), 34 web_frame_(web_frame), 35 extension_(extension), 36 context_type_(context_type), 37 safe_builtins_(this), 38 pepper_request_proxy_(this), 39 isolate_(v8_context->GetIsolate()) { 40 VLOG(1) << "Created context:\n" 41 << " extension id: " << GetExtensionID() << "\n" 42 << " frame: " << web_frame_ << "\n" 43 << " context type: " << GetContextTypeDescription(); 44 } 45 46 ChromeV8Context::~ChromeV8Context() { 47 VLOG(1) << "Destroyed context for extension\n" 48 << " extension id: " << GetExtensionID(); 49 Invalidate(); 50 } 51 52 void ChromeV8Context::Invalidate() { 53 if (!is_valid()) 54 return; 55 if (module_system_) 56 module_system_->Invalidate(); 57 web_frame_ = NULL; 58 v8_context_.reset(); 59 } 60 61 std::string ChromeV8Context::GetExtensionID() const { 62 return extension_.get() ? extension_->id() : std::string(); 63 } 64 65 content::RenderView* ChromeV8Context::GetRenderView() const { 66 if (web_frame_ && web_frame_->view()) 67 return content::RenderView::FromWebView(web_frame_->view()); 68 else 69 return NULL; 70 } 71 72 GURL ChromeV8Context::GetURL() const { 73 return web_frame_ ? 74 UserScriptSlave::GetDataSourceURLForFrame(web_frame_) : GURL(); 75 } 76 77 v8::Local<v8::Value> ChromeV8Context::CallFunction( 78 v8::Handle<v8::Function> function, 79 int argc, 80 v8::Handle<v8::Value> argv[]) const { 81 v8::EscapableHandleScope handle_scope(isolate()); 82 v8::Context::Scope scope(v8_context()); 83 84 blink::WebScopedMicrotaskSuppression suppression; 85 if (!is_valid()) { 86 return handle_scope.Escape( 87 v8::Local<v8::Primitive>(v8::Undefined(isolate()))); 88 } 89 90 v8::Handle<v8::Object> global = v8_context()->Global(); 91 if (!web_frame_) 92 return handle_scope.Escape(function->Call(global, argc, argv)); 93 return handle_scope.Escape( 94 v8::Local<v8::Value>(web_frame_->callFunctionEvenIfScriptDisabled( 95 function, global, argc, argv))); 96 } 97 98 bool ChromeV8Context::IsAnyFeatureAvailableToContext( 99 const std::string& api_name) { 100 return ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext( 101 api_name, 102 extension_.get(), 103 context_type_, 104 UserScriptSlave::GetDataSourceURLForFrame(web_frame_)); 105 } 106 107 Feature::Availability ChromeV8Context::GetAvailability( 108 const std::string& api_name) { 109 // Hack: Hosted apps should have the availability of messaging APIs based on 110 // the URL of the page (which might have access depending on some extension 111 // with externally_connectable), not whether the app has access to messaging 112 // (which it won't). 113 const Extension* extension = extension_.get(); 114 if (extension && extension->is_hosted_app() && 115 (api_name == "runtime.connect" || api_name == "runtime.sendMessage")) { 116 extension = NULL; 117 } 118 return ExtensionAPI::GetSharedInstance()->IsAvailable(api_name, 119 extension, 120 context_type_, 121 GetURL()); 122 } 123 124 void ChromeV8Context::DispatchOnUnloadEvent() { 125 module_system_->CallModuleMethod("unload_event", "dispatch"); 126 } 127 128 std::string ChromeV8Context::GetContextTypeDescription() { 129 switch (context_type_) { 130 case Feature::UNSPECIFIED_CONTEXT: return "UNSPECIFIED"; 131 case Feature::BLESSED_EXTENSION_CONTEXT: return "BLESSED_EXTENSION"; 132 case Feature::UNBLESSED_EXTENSION_CONTEXT: return "UNBLESSED_EXTENSION"; 133 case Feature::CONTENT_SCRIPT_CONTEXT: return "CONTENT_SCRIPT"; 134 case Feature::WEB_PAGE_CONTEXT: return "WEB_PAGE"; 135 case Feature::BLESSED_WEB_PAGE_CONTEXT: return "BLESSED_WEB_PAGE"; 136 } 137 NOTREACHED(); 138 return std::string(); 139 } 140 141 ChromeV8Context* ChromeV8Context::GetContext() { 142 return this; 143 } 144 145 void ChromeV8Context::OnResponseReceived(const std::string& name, 146 int request_id, 147 bool success, 148 const base::ListValue& response, 149 const std::string& error) { 150 v8::HandleScope handle_scope(isolate()); 151 152 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); 153 v8::Handle<v8::Value> argv[] = { 154 v8::Integer::New(request_id), 155 v8::String::NewFromUtf8(isolate(), name.c_str()), 156 v8::Boolean::New(isolate(), success), 157 converter->ToV8Value(&response, v8_context_.NewHandle(isolate())), 158 v8::String::NewFromUtf8(isolate(), error.c_str()) 159 }; 160 161 v8::Handle<v8::Value> retval = module_system_->CallModuleMethod( 162 "sendRequest", "handleResponse", arraysize(argv), argv); 163 164 // In debug, the js will validate the callback parameters and return a 165 // string if a validation error has occured. 166 if (DCHECK_IS_ON()) { 167 if (!retval.IsEmpty() && !retval->IsUndefined()) { 168 std::string error = *v8::String::Utf8Value(retval); 169 DCHECK(false) << error; 170 } 171 } 172 } 173 174 } // namespace extensions 175