blob: e518b21fbd6b62caf4d290602283c11d831510f9 [file] [log] [blame]
[email protected]2ee1e3a2011-10-04 15:04:041// Copyright (c) 2011 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
[email protected]27131e72011-10-06 03:34:565#include "chrome/renderer/extensions/chrome_v8_context_set.h"
[email protected]2ee1e3a2011-10-04 15:04:046
7#include "base/logging.h"
8#include "base/message_loop.h"
9#include "base/tracked_objects.h"
[email protected]27131e72011-10-06 03:34:5610#include "chrome/renderer/extensions/chrome_v8_context.h"
[email protected]2ee1e3a2011-10-04 15:04:0411#include "content/common/url_constants.h"
[email protected]8d86f13d2011-10-04 17:01:1912#include "content/public/renderer/v8_value_converter.h"
[email protected]2ee1e3a2011-10-04 15:04:0413#include "content/renderer/render_thread.h"
14#include "content/renderer/render_view.h"
[email protected]2ee1e3a2011-10-04 15:04:0415#include "v8/include/v8.h"
16#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
17#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
18#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
19#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
20#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
21
[email protected]8d86f13d2011-10-04 17:01:1922using content::V8ValueConverter;
23
[email protected]2ee1e3a2011-10-04 15:04:0424namespace {
25
26// Returns true if the extension running in the given |render_view| has
27// sufficient permissions to access the data.
28//
29// TODO(aa): This looks super suspicious. Is it correct? Can we use something
30// else already in the system? Should it be moved elsewhere?
31bool HasSufficientPermissions(RenderView* render_view, const GURL& event_url) {
32 // During unit tests, we might be invoked without a v8 context. In these
33 // cases, we only allow empty event_urls and short-circuit before retrieving
34 // the render view from the current context.
35 if (!event_url.is_valid())
36 return true;
37
38 WebKit::WebDocument document =
39 render_view->webview()->mainFrame()->document();
40 return GURL(document.url()).SchemeIs(chrome::kExtensionScheme) &&
41 document.securityOrigin().canRequest(event_url);
42}
43
44}
45
46// static
[email protected]27131e72011-10-06 03:34:5647MessageLoop* ChromeV8ContextSet::delete_loop_ = NULL;
[email protected]2ee1e3a2011-10-04 15:04:0448
49// static
[email protected]27131e72011-10-06 03:34:5650void ChromeV8ContextSet::SetDeleteLoop(MessageLoop* delete_loop) {
[email protected]2ee1e3a2011-10-04 15:04:0451 delete_loop_ = delete_loop;
52}
53
[email protected]27131e72011-10-06 03:34:5654ChromeV8ContextSet::ChromeV8ContextSet() {
[email protected]2ee1e3a2011-10-04 15:04:0455}
[email protected]27131e72011-10-06 03:34:5656ChromeV8ContextSet::~ChromeV8ContextSet() {
[email protected]2ee1e3a2011-10-04 15:04:0457}
58
[email protected]27131e72011-10-06 03:34:5659int ChromeV8ContextSet::size() const {
[email protected]2ee1e3a2011-10-04 15:04:0460 return static_cast<int>(contexts_.size());
61}
62
[email protected]27131e72011-10-06 03:34:5663void ChromeV8ContextSet::Add(ChromeV8Context* context) {
[email protected]2ee1e3a2011-10-04 15:04:0464#ifndef NDEBUG
65 // It's OK to insert the same context twice, but we should only ever have one
[email protected]27131e72011-10-06 03:34:5666 // ChromeV8Context per v8::Context.
[email protected]2ee1e3a2011-10-04 15:04:0467 for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end();
68 ++iter) {
[email protected]27131e72011-10-06 03:34:5669 ChromeV8Context* candidate = *iter;
[email protected]2ee1e3a2011-10-04 15:04:0470 if (candidate != context)
71 DCHECK(candidate->v8_context() != context->v8_context());
72 }
73#endif
74 contexts_.insert(context);
75}
76
[email protected]27131e72011-10-06 03:34:5677void ChromeV8ContextSet::Remove(ChromeV8Context* context) {
[email protected]2ee1e3a2011-10-04 15:04:0478 if (contexts_.erase(context)) {
79 context->clear_web_frame();
80 MessageLoop* loop = delete_loop_ ?
81 delete_loop_ :
82 RenderThread::current()->message_loop();
83 loop->DeleteSoon(FROM_HERE, context);
84 }
85}
86
[email protected]27131e72011-10-06 03:34:5687void ChromeV8ContextSet::RemoveByV8Context(
[email protected]2ee1e3a2011-10-04 15:04:0488 v8::Handle<v8::Context> v8_context) {
[email protected]27131e72011-10-06 03:34:5689 ChromeV8Context* context = GetByV8Context(v8_context);
[email protected]2ee1e3a2011-10-04 15:04:0490 if (context)
91 Remove(context);
92}
93
[email protected]27131e72011-10-06 03:34:5694ChromeV8ContextSet::ContextSet ChromeV8ContextSet::GetAll()
[email protected]2ee1e3a2011-10-04 15:04:0495 const {
96 return contexts_;
97}
98
[email protected]27131e72011-10-06 03:34:5699ChromeV8Context* ChromeV8ContextSet::GetCurrent() const {
[email protected]2ee1e3a2011-10-04 15:04:04100 if (!v8::Context::InContext())
101 return NULL;
102 else
103 return GetByV8Context(v8::Context::GetCurrent());
104}
105
[email protected]27131e72011-10-06 03:34:56106ChromeV8Context* ChromeV8ContextSet::GetByV8Context(
[email protected]2ee1e3a2011-10-04 15:04:04107 v8::Handle<v8::Context> v8_context) const {
108 for (ContextSet::const_iterator iter = contexts_.begin();
109 iter != contexts_.end(); ++iter) {
110 if ((*iter)->v8_context() == v8_context)
111 return *iter;
112 }
113
114 return NULL;
115}
116
[email protected]27131e72011-10-06 03:34:56117void ChromeV8ContextSet::DispatchChromeHiddenMethod(
[email protected]2ee1e3a2011-10-04 15:04:04118 const std::string& extension_id,
119 const std::string& method_name,
120 const base::ListValue& arguments,
121 RenderView* render_view,
122 const GURL& event_url) const {
123 v8::HandleScope handle_scope;
124
125 // We copy the context list, because calling into javascript may modify it
126 // out from under us.
127 ContextSet contexts = GetAll();
128
[email protected]8d86f13d2011-10-04 17:01:19129 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
[email protected]2ee1e3a2011-10-04 15:04:04130 for (ContextSet::iterator it = contexts.begin(); it != contexts.end();
131 ++it) {
132 if ((*it)->v8_context().IsEmpty())
133 continue;
134
135 if (!extension_id.empty() && extension_id != (*it)->extension_id())
136 continue;
137
138 RenderView* context_render_view = (*it)->GetRenderView();
139 if (!context_render_view)
140 continue;
141
142 if (render_view && render_view != context_render_view)
143 continue;
144
145 if (!HasSufficientPermissions(context_render_view, event_url))
146 continue;
147
148 v8::Local<v8::Context> context(*((*it)->v8_context()));
149 std::vector<v8::Handle<v8::Value> > v8_arguments;
150 for (size_t i = 0; i < arguments.GetSize(); ++i) {
151 base::Value* item = NULL;
152 CHECK(arguments.Get(i, &item));
[email protected]8d86f13d2011-10-04 17:01:19153 v8_arguments.push_back(converter->ToV8Value(item, context));
[email protected]2ee1e3a2011-10-04 15:04:04154 }
155
156 v8::Handle<v8::Value> retval = (*it)->CallChromeHiddenMethod(
157 method_name, v8_arguments.size(), &v8_arguments[0]);
158 // In debug, the js will validate the event parameters and return a
159 // string if a validation error has occured.
160 // TODO(rafaelw): Consider only doing this check if function_name ==
161 // "Event.dispatchJSON".
162#ifndef NDEBUG
163 if (!retval.IsEmpty() && !retval->IsUndefined()) {
164 std::string error = *v8::String::AsciiValue(retval);
165 DCHECK(false) << error;
166 }
167#endif
168 }
169}