// 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/event_bindings.h" #include <vector> #include "base/basictypes.h" #include "base/lazy_instance.h" #include "base/message_loop.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_set.h" #include "chrome/common/url_constants.h" #include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_context_set.h" #include "chrome/renderer/extensions/chrome_v8_extension.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/schema_generated_bindings.h" #include "chrome/renderer/extensions/user_script_slave.h" #include "content/public/renderer/render_thread.h" #include "googleurl/src/gurl.h" #include "grit/renderer_resources.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" using WebKit::WebFrame; using WebKit::WebSecurityOrigin; using WebKit::WebURL; using content::RenderThread; namespace { // A map of event names to the number of listeners for that event. We notify // the browser about event listeners when we transition between 0 and 1. typedef std::map<std::string, int> EventListenerCounts; struct SingletonData { // A map of extension IDs to listener counts for that extension. std::map<std::string, EventListenerCounts> listener_counts_; }; static base::LazyInstance<SingletonData> g_singleton_data( base::LINKER_INITIALIZED); static EventListenerCounts& GetListenerCounts(const std::string& extension_id) { return g_singleton_data.Get().listener_counts_[extension_id]; } class ExtensionImpl : public ChromeV8Extension { public: explicit ExtensionImpl(ExtensionDispatcher* dispatcher) : ChromeV8Extension("extensions/event.js", IDR_EVENT_BINDINGS_JS, dispatcher) { } ~ExtensionImpl() {} virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( v8::Handle<v8::String> name) { if (name->Equals(v8::String::New("AttachEvent"))) { return v8::FunctionTemplate::New(AttachEvent, v8::External::New(this)); } else if (name->Equals(v8::String::New("DetachEvent"))) { return v8::FunctionTemplate::New(DetachEvent, v8::External::New(this)); } return ChromeV8Extension::GetNativeFunction(name); } // Attach an event name to an object. static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { DCHECK(args.Length() == 1); // TODO(erikkay) should enforce that event name is a string in the bindings DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); const ChromeV8ContextSet& context_set = v8_extension->extension_dispatcher()->v8_context_set(); ChromeV8Context* context = context_set.GetCurrent(); CHECK(context); EventListenerCounts& listener_counts = GetListenerCounts(context->extension_id()); std::string event_name(*v8::String::AsciiValue(args[0])); if (!v8_extension->CheckCurrentContextAccessToExtensionAPI(event_name)) return v8::Undefined(); if (++listener_counts[event_name] == 1) { content::RenderThread::Get()->Send( new ExtensionHostMsg_AddListener(context->extension_id(), event_name)); } } return v8::Undefined(); } static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { DCHECK(args.Length() == 1); // TODO(erikkay) should enforce that event name is a string in the bindings DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); const ChromeV8ContextSet& context_set = v8_extension->extension_dispatcher()->v8_context_set(); ChromeV8Context* context = context_set.GetCurrent(); if (!context) return v8::Undefined(); EventListenerCounts& listener_counts = GetListenerCounts(context->extension_id()); std::string event_name(*v8::String::AsciiValue(args[0])); if (--listener_counts[event_name] == 0) { content::RenderThread::Get()->Send( new ExtensionHostMsg_RemoveListener(context->extension_id(), event_name)); } } return v8::Undefined(); } }; } // namespace v8::Extension* EventBindings::Get(ExtensionDispatcher* dispatcher) { static v8::Extension* extension = new ExtensionImpl(dispatcher); return extension; }