// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/renderer/extensions/bindings_utils.h" #include "base/lazy_instance.h" #include "base/stringprintf.h" #include "base/string_split.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "content/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" using WebKit::WebFrame; using WebKit::WebView; namespace bindings_utils { const char* kChromeHidden = "chromeHidden"; const char* kValidateCallbacks = "validateCallbacks"; struct SingletonData { ContextList contexts; PendingRequestMap pending_requests; }; static base::LazyInstance g_singleton_data( base::LINKER_INITIALIZED); typedef std::map StringMap; static base::LazyInstance g_string_map(base::LINKER_INITIALIZED); const char* GetStringResource(int resource_id) { StringMap* strings = g_string_map.Pointer(); StringMap::iterator it = strings->find(resource_id); if (it == strings->end()) { it = strings->insert(std::make_pair( resource_id, ResourceBundle::GetSharedInstance().GetRawDataResource( resource_id).as_string())).first; } return it->second.c_str(); } // ExtensionBase const Extension* ExtensionBase::GetExtensionForCurrentContext() const { RenderView* renderview = bindings_utils::GetRenderViewForCurrentContext(); if (!renderview) return NULL; // this can happen as a tab is closing. GURL url = renderview->webview()->mainFrame()->url(); const ExtensionSet* extensions = extension_dispatcher_->extensions(); if (!extensions->ExtensionBindingsAllowed(url)) return NULL; return extensions->GetByURL(url); } bool ExtensionBase::CheckPermissionForCurrentContext( const std::string& function_name) const { const ::Extension* extension = GetExtensionForCurrentContext(); if (extension && extension_dispatcher_->IsExtensionActive(extension->id()) && extension->HasApiPermission(function_name)) return true; static const char kMessage[] = "You do not have permission to use '%s'. Be sure to declare" " in your manifest what permissions you need."; std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); v8::ThrowException(v8::Exception::Error(v8::String::New(error_msg.c_str()))); return false; } v8::Handle ExtensionBase::GetNativeFunction(v8::Handle name) { if (name->Equals(v8::String::New("GetChromeHidden"))) { return v8::FunctionTemplate::New(GetChromeHidden); } return v8::Handle(); } v8::Handle ExtensionBase::GetChromeHidden( const v8::Arguments& args) { v8::Local context = v8::Context::GetCurrent(); v8::Local global = context->Global(); v8::Local hidden = global->GetHiddenValue( v8::String::New(kChromeHidden)); if (hidden.IsEmpty() || hidden->IsUndefined()) { hidden = v8::Object::New(); global->SetHiddenValue(v8::String::New(kChromeHidden), hidden); #ifndef NDEBUG // Tell extension_process_bindings.js to validate callbacks and events // against their schema definitions in api/extension_api.json. v8::Local::Cast(hidden) ->Set(v8::String::New(kValidateCallbacks), v8::True()); #endif } DCHECK(hidden->IsObject()); return hidden; } ContextInfo::ContextInfo(v8::Persistent context, const std::string& extension_id, WebKit::WebFrame* parent_frame, RenderView* render_view) : context(context), extension_id(extension_id), parent_frame(parent_frame), render_view(render_view), num_connected_events(0) { } ContextInfo::~ContextInfo() {} ContextList& GetContexts() { return g_singleton_data.Get().contexts; } ContextList GetContextsForExtension(const std::string& extension_id) { ContextList& all_contexts = GetContexts(); ContextList contexts; for (ContextList::iterator it = all_contexts.begin(); it != all_contexts.end(); ++it) { if ((*it)->extension_id == extension_id) contexts.push_back(*it); } return contexts; } ContextInfo* GetInfoForCurrentContext() { // This can happen in testing scenarios and v8::Context::GetCurrent() crashes // if there is no JavaScript currently running. if (!v8::Context::InContext()) return NULL; v8::Local context = v8::Context::GetCurrent(); ContextList::iterator context_iter = FindContext(context); if (context_iter == GetContexts().end()) return NULL; else return context_iter->get(); } PendingRequest::PendingRequest(v8::Persistent context, const std::string& name) : context(context), name(name) { } PendingRequest::~PendingRequest() {} ContextList::iterator FindContext(v8::Handle context) { ContextList& all_contexts = GetContexts(); ContextList::iterator it = all_contexts.begin(); for (; it != all_contexts.end(); ++it) { if ((*it)->context == context) break; } return it; } PendingRequestMap& GetPendingRequestMap() { return g_singleton_data.Get().pending_requests; } RenderView* GetRenderViewForCurrentContext() { WebFrame* webframe = WebFrame::frameForCurrentContext(); DCHECK(webframe) << "RetrieveCurrentFrame called when not in a V8 context."; if (!webframe) return NULL; WebView* webview = webframe->view(); if (!webview) return NULL; // can happen during closing RenderView* renderview = RenderView::FromWebView(webview); DCHECK(renderview) << "Encountered a WebView without a WebViewDelegate"; return renderview; } v8::Handle CallFunctionInContext(v8::Handle context, const std::string& function_name, int argc, v8::Handle* argv) { v8::Context::Scope context_scope(context); // Look up the function name, which may be a sub-property like // "Port.dispatchOnMessage" in the hidden global variable. v8::Local value = context->Global()->GetHiddenValue(v8::String::New(kChromeHidden)); std::vector components; base::SplitStringDontTrim(function_name, '.', &components); for (size_t i = 0; i < components.size(); ++i) { if (!value.IsEmpty() && value->IsObject()) value = value->ToObject()->Get(v8::String::New(components[i].c_str())); } if (value.IsEmpty() || !value->IsFunction()) { NOTREACHED(); return v8::Undefined(); } v8::Local function = v8::Local::Cast(value); if (!function.IsEmpty()) return function->Call(v8::Object::New(), argc, argv); return v8::Undefined(); } } // namespace bindings_utils