| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/extension_dispatcher.h" | 5 #include "chrome/renderer/extensions/extension_dispatcher.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/string_piece.h" | 10 #include "base/string_piece.h" |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount, | 134 base::Bind(&LazyBackgroundPageNativeHandler::IncrementKeepaliveCount, |
| 135 base::Unretained(this))); | 135 base::Unretained(this))); |
| 136 RouteFunction("DecrementKeepaliveCount", | 136 RouteFunction("DecrementKeepaliveCount", |
| 137 base::Bind(&LazyBackgroundPageNativeHandler::DecrementKeepaliveCount, | 137 base::Bind(&LazyBackgroundPageNativeHandler::DecrementKeepaliveCount, |
| 138 base::Unretained(this))); | 138 base::Unretained(this))); |
| 139 } | 139 } |
| 140 | 140 |
| 141 v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) { | 141 v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) { |
| 142 ChromeV8Context* context = | 142 ChromeV8Context* context = |
| 143 extension_dispatcher()->v8_context_set().GetCurrent(); | 143 extension_dispatcher()->v8_context_set().GetCurrent(); |
| 144 if (IsCurrentContextLazyBackgroundPage(context->extension_id())) { | 144 if (IsCurrentContextLazyBackgroundPage(context->extension())) { |
| 145 content::RenderThread::Get()->Send( | 145 content::RenderThread::Get()->Send( |
| 146 new ExtensionHostMsg_IncrementLazyKeepaliveCount( | 146 new ExtensionHostMsg_IncrementLazyKeepaliveCount( |
| 147 context->extension_id())); | 147 context->extension()->id())); |
| 148 } | 148 } |
| 149 return v8::Undefined(); | 149 return v8::Undefined(); |
| 150 } | 150 } |
| 151 | 151 |
| 152 v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) { | 152 v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) { |
| 153 ChromeV8Context* context = | 153 ChromeV8Context* context = |
| 154 extension_dispatcher()->v8_context_set().GetCurrent(); | 154 extension_dispatcher()->v8_context_set().GetCurrent(); |
| 155 if (IsCurrentContextLazyBackgroundPage(context->extension_id())) { | 155 if (IsCurrentContextLazyBackgroundPage(context->extension())) { |
| 156 content::RenderThread::Get()->Send( | 156 content::RenderThread::Get()->Send( |
| 157 new ExtensionHostMsg_DecrementLazyKeepaliveCount( | 157 new ExtensionHostMsg_DecrementLazyKeepaliveCount( |
| 158 context->extension_id())); | 158 context->extension()->id())); |
| 159 } | 159 } |
| 160 return v8::Undefined(); | 160 return v8::Undefined(); |
| 161 } | 161 } |
| 162 | 162 |
| 163 private: | 163 private: |
| 164 bool IsCurrentContextLazyBackgroundPage(const std::string& extension_id) { | 164 bool IsCurrentContextLazyBackgroundPage(const Extension* extension) { |
| 165 content::RenderView* render_view = GetCurrentRenderView(); | 165 content::RenderView* render_view = GetCurrentRenderView(); |
| 166 if (!render_view) | 166 if (!render_view) |
| 167 return false; | 167 return false; |
| 168 | 168 |
| 169 ExtensionHelper* helper = ExtensionHelper::Get(render_view); | 169 ExtensionHelper* helper = ExtensionHelper::Get(render_view); |
| 170 const ::Extension* extension = | |
| 171 extension_dispatcher()->extensions()->GetByID(extension_id); | |
| 172 return (extension && extension->has_lazy_background_page() && | 170 return (extension && extension->has_lazy_background_page() && |
| 173 helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | 171 helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); |
| 174 } | 172 } |
| 175 }; | 173 }; |
| 176 | 174 |
| 177 void InstallAppBindings(ModuleSystem* module_system, | 175 void InstallAppBindings(ModuleSystem* module_system, |
| 178 v8::Handle<v8::Object> chrome, | 176 v8::Handle<v8::Object> chrome, |
| 179 v8::Handle<v8::Object> chrome_hidden) { | 177 v8::Handle<v8::Object> chrome_hidden) { |
| 180 module_system->SetLazyField(chrome, "app", "app", "chromeApp"); | 178 module_system->SetLazyField(chrome, "app", "app", "chromeApp"); |
| 181 module_system->SetLazyField(chrome, "appNotifications", "app", | 179 module_system->SetLazyField(chrome, "appNotifications", "app", |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 562 void ExtensionDispatcher::DidCreateScriptContext( | 560 void ExtensionDispatcher::DidCreateScriptContext( |
| 563 WebFrame* frame, v8::Handle<v8::Context> v8_context, int extension_group, | 561 WebFrame* frame, v8::Handle<v8::Context> v8_context, int extension_group, |
| 564 int world_id) { | 562 int world_id) { |
| 565 // TODO(koz): If the caller didn't pass extension_group, use the last value. | 563 // TODO(koz): If the caller didn't pass extension_group, use the last value. |
| 566 if (extension_group == -1) | 564 if (extension_group == -1) |
| 567 extension_group = g_hack_extension_group; | 565 extension_group = g_hack_extension_group; |
| 568 | 566 |
| 569 std::string extension_id = GetExtensionID(frame, world_id); | 567 std::string extension_id = GetExtensionID(frame, world_id); |
| 570 | 568 |
| 571 const Extension* extension = extensions_.GetByID(extension_id); | 569 const Extension* extension = extensions_.GetByID(extension_id); |
| 572 if (!extension && !extension_id.empty() && !IsTestExtensionId(extension_id)) { | 570 if (!extension && !extension_id.empty()) { |
| 573 // There are conditions where despite a context being associated with an | 571 // There are conditions where despite a context being associated with an |
| 574 // extension, no extension actually gets found. Ignore "invalid" because | 572 // extension, no extension actually gets found. Ignore "invalid" because |
| 575 // CSP blocks extension page loading by switching the extension ID to | 573 // CSP blocks extension page loading by switching the extension ID to |
| 576 // "invalid". This isn't interesting. | 574 // "invalid". This isn't interesting. |
| 577 if (extension_id != "invalid") { | 575 if (extension_id != "invalid") { |
| 578 LOG(ERROR) << "Extension \"" << extension_id << "\" not found"; | 576 LOG(ERROR) << "Extension \"" << extension_id << "\" not found"; |
| 579 RenderThread::Get()->RecordUserMetrics("ExtensionNotFound_ED"); | 577 RenderThread::Get()->RecordUserMetrics("ExtensionNotFound_ED"); |
| 580 } | 578 } |
| 579 |
| 580 extension_id = ""; |
| 581 } | 581 } |
| 582 | 582 |
| 583 ExtensionURLInfo url_info(frame->document().securityOrigin(), | 583 ExtensionURLInfo url_info(frame->document().securityOrigin(), |
| 584 UserScriptSlave::GetDataSourceURLForFrame(frame)); | 584 UserScriptSlave::GetDataSourceURLForFrame(frame)); |
| 585 | 585 |
| 586 Feature::Context context_type = | 586 Feature::Context context_type = |
| 587 ClassifyJavaScriptContext(extension_id, extension_group, url_info); | 587 ClassifyJavaScriptContext(extension_id, extension_group, url_info); |
| 588 | 588 |
| 589 ChromeV8Context* context = | 589 ChromeV8Context* context = |
| 590 new ChromeV8Context(v8_context, frame, extension_id, context_type); | 590 new ChromeV8Context(v8_context, frame, extension, context_type); |
| 591 v8_context_set_.Add(context); | 591 v8_context_set_.Add(context); |
| 592 | 592 |
| 593 scoped_ptr<ModuleSystem> module_system(new ModuleSystem(&source_map_)); | 593 scoped_ptr<ModuleSystem> module_system(new ModuleSystem(&source_map_)); |
| 594 // Enable natives in startup. | 594 // Enable natives in startup. |
| 595 ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system.get()); | 595 ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system.get()); |
| 596 | 596 |
| 597 RegisterNativeHandlers(module_system.get(), context); | 597 RegisterNativeHandlers(module_system.get(), context); |
| 598 | 598 |
| 599 module_system->RegisterNativeHandler("chrome_hidden", | 599 module_system->RegisterNativeHandler("chrome_hidden", |
| 600 scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler())); | 600 scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler())); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 630 case Feature::UNBLESSED_EXTENSION_CONTEXT: | 630 case Feature::UNBLESSED_EXTENSION_CONTEXT: |
| 631 case Feature::CONTENT_SCRIPT_CONTEXT: { | 631 case Feature::CONTENT_SCRIPT_CONTEXT: { |
| 632 module_system->Require("miscellaneous_bindings"); | 632 module_system->Require("miscellaneous_bindings"); |
| 633 module_system->Require("schema_generated_bindings"); | 633 module_system->Require("schema_generated_bindings"); |
| 634 module_system->Require("apitest"); | 634 module_system->Require("apitest"); |
| 635 | 635 |
| 636 // TODO(kalman): move this code back out of the switch and execute it | 636 // TODO(kalman): move this code back out of the switch and execute it |
| 637 // regardless of |context_type|. ExtensionAPI knows how to return the | 637 // regardless of |context_type|. ExtensionAPI knows how to return the |
| 638 // correct APIs, however, until it doesn't have a 2MB overhead we can't | 638 // correct APIs, however, until it doesn't have a 2MB overhead we can't |
| 639 // load it in every process. | 639 // load it in every process. |
| 640 scoped_ptr<std::set<std::string> > apis = | 640 const std::set<std::string>& apis = context->GetAvailableExtensionAPIs(); |
| 641 ExtensionAPI::GetSharedInstance()->GetAPIsForContext( | 641 for (std::set<std::string>::const_iterator i = apis.begin(); |
| 642 context_type, extension, url_info.url()); | 642 i != apis.end(); ++i) { |
| 643 for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); | |
| 644 ++i) { | |
| 645 InstallBindings(module_system.get(), v8_context, *i); | 643 InstallBindings(module_system.get(), v8_context, *i); |
| 646 } | 644 } |
| 647 | 645 |
| 648 break; | 646 break; |
| 649 } | 647 } |
| 650 } | 648 } |
| 651 | 649 |
| 652 // TODO(kalman): this is probably the most unfortunate thing I've ever had to | |
| 653 // write. We really need to factor things differently to delete the concept | |
| 654 // of a test extension ID. | |
| 655 if (IsTestExtensionId(extension_id)) { | |
| 656 module_system->Require("miscellaneous_bindings"); | |
| 657 module_system->Require("schema_generated_bindings"); | |
| 658 InstallBindings(module_system.get(), v8_context, "extension"); | |
| 659 } | |
| 660 | |
| 661 // Inject custom JS into the platform app context to block certain features | 650 // Inject custom JS into the platform app context to block certain features |
| 662 // of the document and window. | 651 // of the document and window. |
| 663 if (extension && extension->is_platform_app()) | 652 if (extension && extension->is_platform_app()) |
| 664 module_system->Require("platformApp"); | 653 module_system->Require("platformApp"); |
| 665 | 654 |
| 666 context->set_module_system(module_system.Pass()); | 655 context->set_module_system(module_system.Pass()); |
| 667 | 656 |
| 668 context->DispatchOnLoadEvent( | 657 context->DispatchOnLoadEvent( |
| 669 is_extension_process_, | 658 is_extension_process_, |
| 670 ChromeRenderProcessObserver::is_incognito_process(), | 659 ChromeRenderProcessObserver::is_incognito_process(), |
| 671 manifest_version); | 660 manifest_version); |
| 672 | 661 |
| 673 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); | 662 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); |
| 674 } | 663 } |
| 675 | 664 |
| 676 std::string ExtensionDispatcher::GetExtensionID(WebFrame* frame, int world_id) { | 665 std::string ExtensionDispatcher::GetExtensionID(WebFrame* frame, int world_id) { |
| 677 if (!test_extension_id_.empty()) { | 666 if (world_id != 0) { |
| 678 return test_extension_id_; | |
| 679 } else if (world_id != 0) { | |
| 680 // Isolated worlds (content script). | 667 // Isolated worlds (content script). |
| 681 return user_script_slave_->GetExtensionIdForIsolatedWorld(world_id); | 668 return user_script_slave_->GetExtensionIdForIsolatedWorld(world_id); |
| 682 } else { | 669 } else { |
| 683 // Extension pages (chrome-extension:// URLs). | 670 // Extension pages (chrome-extension:// URLs). |
| 684 GURL frame_url = UserScriptSlave::GetDataSourceURLForFrame(frame); | 671 GURL frame_url = UserScriptSlave::GetDataSourceURLForFrame(frame); |
| 685 return extensions_.GetExtensionOrAppIDByURL( | 672 return extensions_.GetExtensionOrAppIDByURL( |
| 686 ExtensionURLInfo(frame->document().securityOrigin(), frame_url)); | 673 ExtensionURLInfo(frame->document().securityOrigin(), frame_url)); |
| 687 } | 674 } |
| 688 } | 675 } |
| 689 | 676 |
| 690 void ExtensionDispatcher::WillReleaseScriptContext( | 677 void ExtensionDispatcher::WillReleaseScriptContext( |
| 691 WebFrame* frame, v8::Handle<v8::Context> v8_context, int world_id) { | 678 WebFrame* frame, v8::Handle<v8::Context> v8_context, int world_id) { |
| 692 ChromeV8Context* context = v8_context_set_.GetByV8Context(v8_context); | 679 ChromeV8Context* context = v8_context_set_.GetByV8Context(v8_context); |
| 693 if (!context) | 680 if (!context) |
| 694 return; | 681 return; |
| 695 | 682 |
| 696 context->DispatchOnUnloadEvent(); | 683 context->DispatchOnUnloadEvent(); |
| 697 | 684 |
| 698 v8_context_set_.Remove(context); | 685 v8_context_set_.Remove(context); |
| 699 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); | 686 VLOG(1) << "Num tracked contexts: " << v8_context_set_.size(); |
| 700 } | 687 } |
| 701 | 688 |
| 702 void ExtensionDispatcher::SetTestExtensionId(const std::string& id) { | |
| 703 CHECK(!id.empty()); | |
| 704 test_extension_id_ = id; | |
| 705 } | |
| 706 | |
| 707 bool ExtensionDispatcher::IsTestExtensionId(const std::string& id) { | |
| 708 return !test_extension_id_.empty() && id == test_extension_id_; | |
| 709 } | |
| 710 | |
| 711 void ExtensionDispatcher::OnActivateExtension( | 689 void ExtensionDispatcher::OnActivateExtension( |
| 712 const std::string& extension_id) { | 690 const std::string& extension_id) { |
| 713 active_extension_ids_.insert(extension_id); | 691 active_extension_ids_.insert(extension_id); |
| 714 | 692 |
| 715 // This is called when starting a new extension page, so start the idle | 693 // This is called when starting a new extension page, so start the idle |
| 716 // handler ticking. | 694 // handler ticking. |
| 717 RenderThread::Get()->ScheduleIdleHandler(kInitialExtensionIdleHandlerDelayMs); | 695 RenderThread::Get()->ScheduleIdleHandler(kInitialExtensionIdleHandlerDelayMs); |
| 718 | 696 |
| 719 UpdateActiveExtensions(); | 697 UpdateActiveExtensions(); |
| 720 | 698 |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 } | 856 } |
| 879 | 857 |
| 880 bool ExtensionDispatcher::CheckCurrentContextAccessToExtensionAPI( | 858 bool ExtensionDispatcher::CheckCurrentContextAccessToExtensionAPI( |
| 881 const std::string& function_name) const { | 859 const std::string& function_name) const { |
| 882 ChromeV8Context* context = v8_context_set().GetCurrent(); | 860 ChromeV8Context* context = v8_context_set().GetCurrent(); |
| 883 if (!context) { | 861 if (!context) { |
| 884 DLOG(ERROR) << "Not in a v8::Context"; | 862 DLOG(ERROR) << "Not in a v8::Context"; |
| 885 return false; | 863 return false; |
| 886 } | 864 } |
| 887 | 865 |
| 888 const ::Extension* extension = NULL; | 866 if (!context->extension() || |
| 889 if (!context->extension_id().empty()) { | 867 !context->extension()->HasAPIPermission(function_name)) { |
| 890 extension = extensions()->GetByID(context->extension_id()); | |
| 891 } | |
| 892 | |
| 893 if (!extension || !extension->HasAPIPermission(function_name)) { | |
| 894 static const char kMessage[] = | 868 static const char kMessage[] = |
| 895 "You do not have permission to use '%s'. Be sure to declare" | 869 "You do not have permission to use '%s'. Be sure to declare" |
| 896 " in your manifest what permissions you need."; | 870 " in your manifest what permissions you need."; |
| 897 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); | 871 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); |
| 898 v8::ThrowException( | 872 v8::ThrowException( |
| 899 v8::Exception::Error(v8::String::New(error_msg.c_str()))); | 873 v8::Exception::Error(v8::String::New(error_msg.c_str()))); |
| 900 return false; | 874 return false; |
| 901 } | 875 } |
| 902 | 876 |
| 903 if (!IsExtensionActive(extension->id()) && | 877 if (!IsExtensionActive(context->extension()->id()) && |
| 904 ExtensionAPI::GetSharedInstance()->IsPrivileged(function_name)) { | 878 ExtensionAPI::GetSharedInstance()->IsPrivileged(function_name)) { |
| 905 static const char kMessage[] = | 879 static const char kMessage[] = |
| 906 "%s can only be used in an extension process."; | 880 "%s can only be used in an extension process."; |
| 907 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); | 881 std::string error_msg = base::StringPrintf(kMessage, function_name.c_str()); |
| 908 v8::ThrowException( | 882 v8::ThrowException( |
| 909 v8::Exception::Error(v8::String::New(error_msg.c_str()))); | 883 v8::Exception::Error(v8::String::New(error_msg.c_str()))); |
| 910 return false; | 884 return false; |
| 911 } | 885 } |
| 912 | 886 |
| 913 return true; | 887 return true; |
| 914 } | 888 } |
| OLD | NEW |