Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/renderer/extensions/event_bindings.h" | 5 #include "chrome/renderer/extensions/event_bindings.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 182 WebKit::WebString::fromUTF8(file_full_path.c_str()), | 182 WebKit::WebString::fromUTF8(file_full_path.c_str()), |
| 183 is_directory); | 183 is_directory); |
| 184 #else | 184 #else |
| 185 return v8::Undefined(); | 185 return v8::Undefined(); |
| 186 #endif | 186 #endif |
| 187 } | 187 } |
| 188 }; | 188 }; |
| 189 | 189 |
| 190 // Returns true if the extension running in the given |context| has sufficient | 190 // Returns true if the extension running in the given |context| has sufficient |
| 191 // permissions to access the data. | 191 // permissions to access the data. |
| 192 static bool HasSufficientPermissions(ContextInfo* context, | 192 static bool HasSufficientPermissions(RenderView* render_view, |
| 193 const GURL& event_url) { | 193 const GURL& event_url) { |
| 194 v8::Context::Scope context_scope(context->context); | |
| 195 | |
| 196 // During unit tests, we might be invoked without a v8 context. In these | 194 // During unit tests, we might be invoked without a v8 context. In these |
| 197 // cases, we only allow empty event_urls and short-circuit before retrieving | 195 // cases, we only allow empty event_urls and short-circuit before retrieving |
| 198 // the render view from the current context. | 196 // the render view from the current context. |
| 199 if (!event_url.is_valid()) | 197 if (!event_url.is_valid()) |
| 200 return true; | 198 return true; |
| 201 | 199 |
| 202 RenderView* renderview = bindings_utils::GetRenderViewForCurrentContext(); | 200 WebDocument document = render_view->webview()->mainFrame()->document(); |
| 203 if (!renderview) | |
| 204 return false; | |
| 205 | |
| 206 WebDocument document = renderview->webview()->mainFrame()->document(); | |
| 207 return GURL(document.url()).SchemeIs(chrome::kExtensionScheme) && | 201 return GURL(document.url()).SchemeIs(chrome::kExtensionScheme) && |
| 208 document.securityOrigin().canRequest(event_url); | 202 document.securityOrigin().canRequest(event_url); |
| 209 } | 203 } |
| 210 | 204 |
| 211 } // namespace | 205 } // namespace |
| 212 | 206 |
| 213 const char* EventBindings::kName = "chrome/EventBindings"; | 207 const char* EventBindings::kName = "chrome/EventBindings"; |
| 214 const char* EventBindings::kTestingExtensionId = | 208 const char* EventBindings::kTestingExtensionId = |
| 215 "oooooooooooooooooooooooooooooooo"; | 209 "oooooooooooooooooooooooooooooooo"; |
| 216 | 210 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 291 v8::Handle<v8::Context> context, | 285 v8::Handle<v8::Context> context, |
| 292 ExtensionDispatcher* extension_dispatcher, | 286 ExtensionDispatcher* extension_dispatcher, |
| 293 int isolated_world_id) { | 287 int isolated_world_id) { |
| 294 if (!bindings_registered) | 288 if (!bindings_registered) |
| 295 return; | 289 return; |
| 296 | 290 |
| 297 bool content_script = isolated_world_id != 0; | 291 bool content_script = isolated_world_id != 0; |
| 298 ContextList& contexts = GetContexts(); | 292 ContextList& contexts = GetContexts(); |
| 299 | 293 |
| 300 v8::Persistent<v8::Context> persistent_context; | 294 v8::Persistent<v8::Context> persistent_context; |
| 295 v8::Persistent<v8::Context> main_world_context; | |
| 301 std::string extension_id; | 296 std::string extension_id; |
| 302 | 297 |
| 303 if (content_script) { | 298 if (content_script) { |
| 304 // Content script contexts can get GCed before their frame goes away, so | 299 // Content script contexts can get GCed before their frame goes away, so |
| 305 // set up a GC callback. | 300 // set up a GC callback. |
| 306 persistent_context = v8::Persistent<v8::Context>::New(context); | 301 persistent_context = v8::Persistent<v8::Context>::New(context); |
| 307 persistent_context.MakeWeak(NULL, &ContextWeakReferenceCallback); | 302 persistent_context.MakeWeak(NULL, &ContextWeakReferenceCallback); |
| 303 | |
| 304 main_world_context = v8::Persistent<v8::Context>::New( | |
| 305 frame->mainWorldScriptContext()); | |
| 306 main_world_context.MakeWeak(NULL, NULL); | |
| 307 | |
| 308 extension_id = | 308 extension_id = |
| 309 extension_dispatcher->user_script_slave()-> | 309 extension_dispatcher->user_script_slave()-> |
| 310 GetExtensionIdForIsolatedWorld(isolated_world_id); | 310 GetExtensionIdForIsolatedWorld(isolated_world_id); |
| 311 } else { | 311 } else { |
| 312 // Figure out the frame's URL. If the frame is loading, use its provisional | 312 // Figure out the frame's URL. If the frame is loading, use its provisional |
| 313 // URL, since we get this notification before commit. | 313 // URL, since we get this notification before commit. |
| 314 WebDataSource* ds = frame->provisionalDataSource(); | 314 WebDataSource* ds = frame->provisionalDataSource(); |
| 315 if (!ds) | 315 if (!ds) |
| 316 ds = frame->dataSource(); | 316 ds = frame->dataSource(); |
| 317 GURL url = ds->request().url(); | 317 GURL url = ds->request().url(); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 329 | 329 |
| 330 // For tests, we want the dispatchOnLoad to actually setup our bindings, | 330 // For tests, we want the dispatchOnLoad to actually setup our bindings, |
| 331 // so we give a fake extension id; | 331 // so we give a fake extension id; |
| 332 extension_id = kTestingExtensionId; | 332 extension_id = kTestingExtensionId; |
| 333 } | 333 } |
| 334 | 334 |
| 335 persistent_context = v8::Persistent<v8::Context>::New(context); | 335 persistent_context = v8::Persistent<v8::Context>::New(context); |
| 336 } | 336 } |
| 337 | 337 |
| 338 contexts.push_back(linked_ptr<ContextInfo>( | 338 contexts.push_back(linked_ptr<ContextInfo>( |
| 339 new ContextInfo(persistent_context, extension_id, frame))); | 339 new ContextInfo(persistent_context, main_world_context, extension_id))); |
| 340 | 340 |
| 341 // Content scripts get initialized in user_script_slave.cc. | 341 // Content scripts get initialized in user_script_slave.cc. |
| 342 if (!content_script) { | 342 if (!content_script) { |
| 343 v8::HandleScope handle_scope; | 343 v8::HandleScope handle_scope; |
| 344 v8::Handle<v8::Value> argv[1]; | 344 v8::Handle<v8::Value> argv[1]; |
| 345 argv[0] = v8::String::New(extension_id.c_str()); | 345 argv[0] = v8::String::New(extension_id.c_str()); |
| 346 CallFunctionInContext(context, "dispatchOnLoad", arraysize(argv), argv); | 346 CallFunctionInContext(context, "dispatchOnLoad", arraysize(argv), argv); |
| 347 } | 347 } |
| 348 } | 348 } |
| 349 | 349 |
| 350 // static | 350 // static |
| 351 void EventBindings::HandleContextDestroyed(WebFrame* frame) { | 351 void EventBindings::HandleContextDestroyed(WebFrame* frame) { |
| 352 if (!bindings_registered) | 352 if (!bindings_registered) |
| 353 return; | 353 return; |
| 354 | 354 |
| 355 v8::HandleScope handle_scope; | 355 v8::HandleScope handle_scope; |
| 356 v8::Local<v8::Context> context = frame->mainWorldScriptContext(); | 356 v8::Local<v8::Context> context = frame->mainWorldScriptContext(); |
| 357 if (!context.IsEmpty()) { | 357 |
| 358 ContextList::iterator context_iter = bindings_utils::FindContext(context); | 358 if (context.IsEmpty()) { |
| 359 if (context_iter != GetContexts().end()) | 359 NOTREACHED(); |
|
Aaron Boodman
2011/08/24 02:58:16
According to the code upstream, this should not ha
| |
| 360 UnregisterContext(context_iter, false); | 360 return; |
| 361 } | 361 } |
| 362 | 362 |
| 363 // Unload any content script contexts for this frame. Note that the frame | 363 ContextList::iterator context_iter = bindings_utils::FindContext(context); |
| 364 // itself might not be registered, but can still be a parent frame. | 364 if (context_iter != GetContexts().end()) |
| 365 UnregisterContext(context_iter, false); | |
| 366 | |
| 367 // Unload any associated content script contexts. Note that the main world | |
| 368 // context itself might not be registered, but can still be associated with a | |
| 369 // content script that is. | |
| 365 for (ContextList::iterator it = GetContexts().begin(); | 370 for (ContextList::iterator it = GetContexts().begin(); |
| 366 it != GetContexts().end(); ) { | 371 it != GetContexts().end(); ) { |
| 367 if ((*it)->frame == frame) { | 372 if ((*it)->main_world_context == context) { |
| 368 UnregisterContext(it, false); | 373 UnregisterContext(it, false); |
| 369 // UnregisterContext will remove |it| from the list, but may also | 374 // UnregisterContext will remove |it| from the list, but may also |
| 370 // modify the rest of the list as a result of calling into javascript. | 375 // modify the rest of the list as a result of calling into javascript. |
| 371 it = GetContexts().begin(); | 376 it = GetContexts().begin(); |
| 372 } else { | 377 } else { |
| 373 ++it; | 378 ++it; |
| 374 } | 379 } |
| 375 } | 380 } |
| 376 } | 381 } |
| 377 | 382 |
| 378 // static | 383 // static |
| 379 void EventBindings::CallFunction(const std::string& extension_id, | 384 void EventBindings::CallFunction(const std::string& extension_id, |
| 380 const std::string& function_name, | 385 const std::string& function_name, |
| 381 const ListValue& arguments, | 386 const ListValue& arguments, |
| 382 RenderView* render_view, | 387 RenderView* render_view, |
| 383 const GURL& event_url) { | 388 const GURL& event_url) { |
| 384 v8::HandleScope handle_scope; | 389 v8::HandleScope handle_scope; |
| 385 | 390 |
| 386 // We copy the context list, because calling into javascript may modify it | 391 // We copy the context list, because calling into javascript may modify it |
| 387 // out from under us. We also guard against deleted contexts by checking if | 392 // out from under us. We also guard against deleted contexts by checking if |
| 388 // they have been cleared first. | 393 // they have been cleared first. |
| 389 ContextList contexts = GetContexts(); | 394 ContextList contexts = GetContexts(); |
| 390 | 395 |
| 391 V8ValueConverter converter; | 396 V8ValueConverter converter; |
| 392 for (ContextList::iterator it = contexts.begin(); | 397 for (ContextList::iterator it = contexts.begin(); |
| 393 it != contexts.end(); ++it) { | 398 it != contexts.end(); ++it) { |
| 394 if (render_view) { | 399 if ((*it)->context.IsEmpty()) |
| 395 RenderView* context_render_view = | 400 continue; |
| 396 RenderView::FromWebView((*it)->frame->view()); | |
| 397 if (render_view != context_render_view) | |
| 398 continue; | |
| 399 } | |
| 400 | 401 |
| 401 if (!extension_id.empty() && extension_id != (*it)->extension_id) | 402 if (!extension_id.empty() && extension_id != (*it)->extension_id) |
| 402 continue; | 403 continue; |
| 403 | 404 |
| 404 if ((*it)->context.IsEmpty()) | 405 WebFrame* context_frame = WebFrame::frameForContext((*it)->context); |
|
Aaron Boodman
2011/08/24 02:58:16
It will return NULL if the context is detached fro
| |
| 406 if (!context_frame || !context_frame->view()) | |
| 405 continue; | 407 continue; |
| 406 | 408 |
| 407 if (!HasSufficientPermissions(it->get(), event_url)) | 409 RenderView* context_render_view = |
| 410 RenderView::FromWebView(context_frame->view()); | |
| 411 if (!context_render_view) | |
| 412 continue; | |
| 413 | |
| 414 if (render_view && render_view != context_render_view) | |
| 415 continue; | |
| 416 | |
| 417 if (!HasSufficientPermissions(render_view, event_url)) | |
| 408 continue; | 418 continue; |
| 409 | 419 |
| 410 v8::Local<v8::Context> context(*((*it)->context)); | 420 v8::Local<v8::Context> context(*((*it)->context)); |
| 411 std::vector<v8::Handle<v8::Value> > v8_arguments; | 421 std::vector<v8::Handle<v8::Value> > v8_arguments; |
| 412 for (size_t i = 0; i < arguments.GetSize(); ++i) { | 422 for (size_t i = 0; i < arguments.GetSize(); ++i) { |
| 413 Value* item = NULL; | 423 Value* item = NULL; |
| 414 CHECK(arguments.Get(i, &item)); | 424 CHECK(arguments.Get(i, &item)); |
| 415 v8_arguments.push_back(converter.ToV8Value(item, context)); | 425 v8_arguments.push_back(converter.ToV8Value(item, context)); |
| 416 } | 426 } |
| 417 | 427 |
| 418 v8::Handle<v8::Value> retval = CallFunctionInContext( | 428 v8::Handle<v8::Value> retval = CallFunctionInContext( |
| 419 context, function_name, v8_arguments.size(), &v8_arguments[0]); | 429 context, function_name, v8_arguments.size(), &v8_arguments[0]); |
| 420 // In debug, the js will validate the event parameters and return a | 430 // In debug, the js will validate the event parameters and return a |
| 421 // string if a validation error has occured. | 431 // string if a validation error has occured. |
| 422 // TODO(rafaelw): Consider only doing this check if function_name == | 432 // TODO(rafaelw): Consider only doing this check if function_name == |
| 423 // "Event.dispatchJSON". | 433 // "Event.dispatchJSON". |
| 424 #ifndef NDEBUG | 434 #ifndef NDEBUG |
| 425 if (!retval.IsEmpty() && !retval->IsUndefined()) { | 435 if (!retval.IsEmpty() && !retval->IsUndefined()) { |
| 426 std::string error = *v8::String::AsciiValue(retval); | 436 std::string error = *v8::String::AsciiValue(retval); |
| 427 DCHECK(false) << error; | 437 DCHECK(false) << error; |
| 428 } | 438 } |
| 429 #endif | 439 #endif |
| 430 } | 440 } |
| 431 } | 441 } |
| OLD | NEW |