diff options
Diffstat (limited to 'chrome/browser')
46 files changed, 494 insertions, 174 deletions
diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc index 23e8e3d..41994e8 100644 --- a/chrome/browser/automation/automation_provider_win.cc +++ b/chrome/browser/automation/automation_provider_win.cc @@ -487,7 +487,7 @@ bool AutomationProvider::InterceptBrowserEventMessageFromExternalHost( if (profile()->GetExtensionMessageService()) { profile()->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile()->IsOffTheRecord(), GURL()); + event_name, json_args, profile(), GURL()); } return true; diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index ebcf1da..85cf378 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -3052,13 +3052,14 @@ void Browser::Observe(NotificationType type, case NotificationType::EXTENSION_UPDATE_DISABLED: { // Show the UI if the extension was disabled for escalated permissions. Profile* profile = Source<Profile>(source).ptr(); - DCHECK_EQ(profile_, profile); - ExtensionsService* service = profile->GetExtensionsService(); - DCHECK(service); - Extension* extension = Details<Extension>(details).ptr(); - if (service->extension_prefs()->DidExtensionEscalatePermissions( - extension->id())) - ShowExtensionDisabledUI(service, profile_, extension); + if (profile_->IsSameProfile(profile)) { + ExtensionsService* service = profile->GetExtensionsService(); + DCHECK(service); + Extension* extension = Details<Extension>(details).ptr(); + if (service->extension_prefs()->DidExtensionEscalatePermissions( + extension->id())) + ShowExtensionDisabledUI(service, profile_, extension); + } break; } diff --git a/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm b/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm index 235d85b..110f719 100644 --- a/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm +++ b/chrome/browser/cocoa/extensions/extension_popup_controller_unittest.mm @@ -24,7 +24,7 @@ class ExtensionTestingProfile : public TestingProfile { DCHECK(!GetExtensionProcessManager()); DCHECK(!GetExtensionsService()); - manager_.reset(new ExtensionProcessManager(this)); + manager_.reset(ExtensionProcessManager::Create(this)); service_ = new ExtensionsService(this, CommandLine::ForCurrentProcess(), GetPrefs(), diff --git a/chrome/browser/dom_ui/dom_ui_factory.cc b/chrome/browser/dom_ui/dom_ui_factory.cc index 96f8b8a..524059b 100644 --- a/chrome/browser/dom_ui/dom_ui_factory.cc +++ b/chrome/browser/dom_ui/dom_ui_factory.cc @@ -59,8 +59,7 @@ DOMUI* NewDOMUI<ExtensionDOMUI>(TabContents* contents, const GURL& url) { // within a single process. ExtensionsService* service = contents->profile()->GetExtensionsService(); if (service && - service->ExtensionBindingsAllowed(url) && - !contents->profile()->IsOffTheRecord()) { + service->ExtensionBindingsAllowed(url)) { return new ExtensionDOMUI(contents, url); } return NULL; diff --git a/chrome/browser/extensions/extension_accessibility_api.cc b/chrome/browser/extensions/extension_accessibility_api.cc index 760869a..bf310f2 100644 --- a/chrome/browser/extensions/extension_accessibility_api.cc +++ b/chrome/browser/extensions/extension_accessibility_api.cc @@ -177,7 +177,7 @@ void ExtensionAccessibilityEventRouter::DispatchEvent( const std::string& json_args) { if (enabled_ && profile && profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile->IsOffTheRecord(), GURL()); + event_name, json_args, profile, GURL()); } } diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index ba4da59..c5e1f54 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -11,7 +11,8 @@ #include "chrome/common/notification_registrar.h" #include "chrome/test/ui_test_utils.h" -ExtensionApiTest::ResultCatcher::ResultCatcher() { +ExtensionApiTest::ResultCatcher::ResultCatcher() + : profile_restriction_(NULL) { registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED, @@ -44,6 +45,11 @@ bool ExtensionApiTest::ResultCatcher::GetNextResult() { void ExtensionApiTest::ResultCatcher::Observe( NotificationType type, const NotificationSource& source, const NotificationDetails& details) { + if (profile_restriction_ && + Source<Profile>(source).ptr() != profile_restriction_) { + return; + } + switch (type.value) { case NotificationType::EXTENSION_TEST_PASSED: std::cout << "Got EXTENSION_TEST_PASSED notification.\n"; diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h index 3e2c13c..1c20bab 100644 --- a/chrome/browser/extensions/extension_apitest.h +++ b/chrome/browser/extensions/extension_apitest.h @@ -39,6 +39,8 @@ class ExtensionApiTest : public ExtensionBrowserTest { // succeeded or failed. Returns true if the test succeeded, false otherwise. bool GetNextResult(); + void RestrictToProfile(Profile* profile) { profile_restriction_ = profile; } + const std::string& message() { return message_; } private: @@ -54,6 +56,9 @@ class ExtensionApiTest : public ExtensionBrowserTest { // If it failed, what was the error message? std::deque<std::string> messages_; std::string message_; + + // If non-NULL, we will listen to events from this profile only. + Profile* profile_restriction_; }; // Load |extension_name| and wait for pass / fail notification. diff --git a/chrome/browser/extensions/extension_bookmark_manager_api.cc b/chrome/browser/extensions/extension_bookmark_manager_api.cc index 8e6df4d..0b0dc0f 100644 --- a/chrome/browser/extensions/extension_bookmark_manager_api.cc +++ b/chrome/browser/extensions/extension_bookmark_manager_api.cc @@ -161,7 +161,7 @@ void ExtensionBookmarkManagerEventRouter::DispatchEvent(const char* event_name, std::string json_args; base::JSONWriter::Write(args, false, &json_args); profile_->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile_->IsOffTheRecord(), GURL()); + event_name, json_args, profile_, GURL()); } void ExtensionBookmarkManagerEventRouter::DispatchDragEvent( diff --git a/chrome/browser/extensions/extension_bookmarks_module.cc b/chrome/browser/extensions/extension_bookmarks_module.cc index 3f3e8c9..4044bfc 100644 --- a/chrome/browser/extensions/extension_bookmarks_module.cc +++ b/chrome/browser/extensions/extension_bookmarks_module.cc @@ -97,7 +97,7 @@ void ExtensionBookmarkEventRouter::DispatchEvent(Profile *profile, const std::string json_args) { if (profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile->IsOffTheRecord(), GURL()); + event_name, json_args, profile, GURL()); } } diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index dedcc80..80dfa69 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -68,7 +68,7 @@ static void DispatchEvent(Profile* profile, const std::string json_args) { if (profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile->IsOffTheRecord(), GURL()); + event_name, json_args, profile, GURL()); } } diff --git a/chrome/browser/extensions/extension_cookies_api.cc b/chrome/browser/extensions/extension_cookies_api.cc index f50d475..5a2d456 100644 --- a/chrome/browser/extensions/extension_cookies_api.cc +++ b/chrome/browser/extensions/extension_cookies_api.cc @@ -76,7 +76,7 @@ void ExtensionCookiesEventRouter::DispatchEvent(Profile* profile, GURL& cookie_domain) { if (profile && profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile->IsOffTheRecord(), cookie_domain); + event_name, json_args, profile, cookie_domain); } } @@ -405,7 +405,7 @@ bool RemoveCookieFunction::RunImpl() { } bool GetAllCookieStoresFunction::RunImpl() { - Profile* original_profile = profile()->GetOriginalProfile(); + Profile* original_profile = profile(); DCHECK(original_profile); scoped_ptr<ListValue> original_tab_ids(new ListValue()); Profile* incognito_profile = NULL; @@ -415,6 +415,8 @@ bool GetAllCookieStoresFunction::RunImpl() { if (incognito_profile) incognito_tab_ids.reset(new ListValue()); } + DCHECK(original_profile != incognito_profile); + // Iterate through all browser instances, and for each browser, // add its tab IDs to either the regular or incognito tab ID list depending // whether the browser is regular or incognito. diff --git a/chrome/browser/extensions/extension_cookies_helpers.cc b/chrome/browser/extensions/extension_cookies_helpers.cc index 2aedb5f..52368e9 100644 --- a/chrome/browser/extensions/extension_cookies_helpers.cc +++ b/chrome/browser/extensions/extension_cookies_helpers.cc @@ -27,9 +27,11 @@ Profile* ChooseProfileFromStoreId(const std::string& store_id, Profile* profile, bool include_incognito) { DCHECK(profile); - if (store_id == kOriginalProfileStoreId) + bool allow_original = !profile->IsOffTheRecord(); + bool allow_incognito = profile->IsOffTheRecord() || include_incognito; + if (store_id == kOriginalProfileStoreId && allow_original) return profile->GetOriginalProfile(); - if (store_id == kOffTheRecordProfileStoreId && include_incognito) + if (store_id == kOffTheRecordProfileStoreId && allow_incognito) return profile->GetOffTheRecordProfile(); return NULL; } diff --git a/chrome/browser/extensions/extension_cookies_unittest.cc b/chrome/browser/extensions/extension_cookies_unittest.cc index 0abbe7e..ec9084a 100644 --- a/chrome/browser/extensions/extension_cookies_unittest.cc +++ b/chrome/browser/extensions/extension_cookies_unittest.cc @@ -83,16 +83,16 @@ TEST_F(ExtensionCookiesTest, StoreIdProfileConversion) { EXPECT_EQ(std::string("1"), extension_cookies_helpers::GetStoreIdFromProfile(&otrProfile)); - EXPECT_EQ(&profile, + EXPECT_EQ(NULL, extension_cookies_helpers::ChooseProfileFromStoreId( "0", &otrProfile, true)); - EXPECT_EQ(&profile, + EXPECT_EQ(NULL, extension_cookies_helpers::ChooseProfileFromStoreId( "0", &otrProfile, false)); EXPECT_EQ(&otrProfile, extension_cookies_helpers::ChooseProfileFromStoreId( "1", &otrProfile, true)); - EXPECT_EQ(NULL, + EXPECT_EQ(&otrProfile, extension_cookies_helpers::ChooseProfileFromStoreId( "1", &otrProfile, false)); } diff --git a/chrome/browser/extensions/extension_devtools_bridge.cc b/chrome/browser/extensions/extension_devtools_bridge.cc index 71d795b..01a0aef 100644 --- a/chrome/browser/extensions/extension_devtools_bridge.cc +++ b/chrome/browser/extensions/extension_devtools_bridge.cc @@ -65,7 +65,7 @@ void ExtensionDevToolsBridge::InspectedTabClosing() { // event in extensions. std::string json("[{}]"); profile_->GetExtensionMessageService()->DispatchEventToRenderers( - on_tab_close_event_name_, json, profile_->IsOffTheRecord(), GURL()); + on_tab_close_event_name_, json, profile_, GURL()); // This may result in this object being destroyed. extension_devtools_manager_->BridgeClosingForTab(tab_id_); @@ -83,6 +83,6 @@ void ExtensionDevToolsBridge::OnDispatchToAPU(const std::string& data) { std::string json = StringPrintf("[%s]", data.c_str()); profile_->GetExtensionMessageService()->DispatchEventToRenderers( - on_page_event_name_, json, profile_->IsOffTheRecord(), GURL()); + on_page_event_name_, json, profile_, GURL()); } diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h index 51f25fe..b48d0a9 100644 --- a/chrome/browser/extensions/extension_function.h +++ b/chrome/browser/extensions/extension_function.h @@ -149,7 +149,10 @@ class ExtensionFunction : public base::RefCountedThreadSafe<ExtensionFunction> { // of this call. bool has_callback_; - // True if this callback should include information from incognito contexts. + // True if this callback should include information from incognito contexts + // even if our profile_ is non-incognito. Note that in the case of a "split" + // mode extension, this will always be false, and we will limit access to + // data from within the same profile_ (either incognito or not). bool include_incognito_; // True if the call was made in response of user gesture. diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index a8253cf..fd4ba5f 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -391,7 +391,7 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( render_view_host->Send(new ViewMsg_Extension_SetHostPermissions( extension->url(), extension->host_permissions())); render_view_host->Send(new ViewMsg_Extension_ExtensionSetIncognitoEnabled( - extension->id(), incognito_enabled)); + extension->id(), incognito_enabled, extension->incognito_split_mode())); NotificationService::current()->Notify( NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED, @@ -412,20 +412,16 @@ Browser* ExtensionFunctionDispatcher::GetCurrentBrowser( bool include_incognito) { Browser* browser = delegate_->GetBrowser(); - // If the delegate has an associated browser and that browser is in the right - // incognito state, we can return it. - if (browser) { - if (include_incognito || !browser->profile()->IsOffTheRecord()) - return browser; - } + // If the delegate has an associated browser, that is always the right answer. + if (browser) + return browser; - // Otherwise, try to default to a reasonable browser. + // Otherwise, try to default to a reasonable browser. If |include_incognito| + // is true, we will also search browsers in the incognito version of this + // profile. Note that the profile may already be incognito, in which case + // we will search the incognito version only, regardless of the value of + // |include_incognito|. Profile* profile = render_view_host()->process()->profile(); - - // Make sure we don't return an incognito browser without proper access. - if (!include_incognito) - profile = profile->GetOriginalProfile(); - browser = BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL, include_incognito); @@ -454,7 +450,8 @@ void ExtensionFunctionDispatcher::HandleRequest( DCHECK(service); Extension* extension = service->GetExtensionById(extension_id(), false); DCHECK(extension); - function->set_include_incognito(service->IsIncognitoEnabled(extension)); + function->set_include_incognito(service->IsIncognitoEnabled(extension) && + !extension->incognito_split_mode()); std::string permission_name = function->name(); size_t separator = permission_name.find_first_of("./"); diff --git a/chrome/browser/extensions/extension_history_api.cc b/chrome/browser/extensions/extension_history_api.cc index 3df0509..82be3dc 100644 --- a/chrome/browser/extensions/extension_history_api.cc +++ b/chrome/browser/extensions/extension_history_api.cc @@ -143,7 +143,7 @@ void ExtensionHistoryEventRouter::DispatchEvent(Profile* profile, const std::string& json_args) { if (profile && profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile->IsOffTheRecord(), GURL()); + event_name, json_args, profile, GURL()); } } diff --git a/chrome/browser/extensions/extension_idle_api.cc b/chrome/browser/extensions/extension_idle_api.cc index 104cc8b..0de5032 100644 --- a/chrome/browser/extensions/extension_idle_api.cc +++ b/chrome/browser/extensions/extension_idle_api.cc @@ -153,8 +153,5 @@ void ExtensionIdleEventRouter::OnIdleStateChange(Profile* profile, base::JSONWriter::Write(&args, false, &json_args); profile->GetExtensionMessageService()->DispatchEventToRenderers( - keys::kOnStateChanged, - json_args, - profile->IsOffTheRecord(), - GURL()); + keys::kOnStateChanged, json_args, profile, GURL()); } diff --git a/chrome/browser/extensions/extension_incognito_apitest.cc b/chrome/browser/extensions/extension_incognito_apitest.cc index dd0abd4..41ddb99 100644 --- a/chrome/browser/extensions/extension_incognito_apitest.cc +++ b/chrome/browser/extensions/extension_incognito_apitest.cc @@ -95,6 +95,31 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Incognito) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } +// Tests that the APIs in an incognito-enabled split-mode extension work +// properly. +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, IncognitoSplitMode) { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + ResultCatcher catcher_incognito; + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + // Open incognito window and navigate to test page. + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), + GURL("http://www.example.com:1337/files/extensions/test_file.html")); + + ASSERT_TRUE(LoadExtensionIncognito(test_data_dir_ + .AppendASCII("incognito").AppendASCII("split"))); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + // Tests that the APIs in an incognito-disabled extension don't see incognito // events or callbacks. // Hangy, http://crbug.com/53869. diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 4b581b2..ba5eab3 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -200,6 +200,6 @@ void ExtensionManagementEventRouter::Observe( profile->GetExtensionMessageService(); message_service->DispatchEventToRenderers(event_name, args_json, - profile->IsOffTheRecord(), + profile, GURL()); } diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc index 77c3408..e4bda71 100644 --- a/chrome/browser/extensions/extension_menu_manager.cc +++ b/chrome/browser/extensions/extension_menu_manager.cc @@ -441,8 +441,7 @@ void ExtensionMenuManager::ExecuteCommand( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); std::string event_name = "contextMenus/" + item->extension_id(); - service->DispatchEventToRenderers(event_name, json_args, - profile->IsOffTheRecord(), GURL()); + service->DispatchEventToRenderers(event_name, json_args, profile, GURL()); } void ExtensionMenuManager::Observe(NotificationType type, diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc index f074b4a..e3a61a1 100644 --- a/chrome/browser/extensions/extension_menu_manager_unittest.cc +++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc @@ -338,7 +338,7 @@ class MockExtensionMessageService : public ExtensionMessageService { MOCK_METHOD4(DispatchEventToRenderers, void(const std::string& event_name, const std::string& event_args, - bool has_incognito_data, + Profile* source_profile, const GURL& event_url)); private: @@ -413,17 +413,13 @@ TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { .Times(1) .WillOnce(Return(mock_message_service.get())); - EXPECT_CALL(profile, IsOffTheRecord()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(false)); - // Use the magic of googlemock to save a parameter to our mock's // DispatchEventToRenderers method into event_args. std::string event_args; std::string expected_event_name = "contextMenus/" + item->extension_id(); EXPECT_CALL(*mock_message_service.get(), DispatchEventToRenderers(expected_event_name, _, - profile.IsOffTheRecord(), + &profile, GURL())) .Times(1) .WillOnce(SaveArg<1>(&event_args)); diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index 9775551..2fb59c3 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -204,15 +204,15 @@ void ExtensionMessageService::OpenChannelToExtension( const std::string& source_extension_id, const std::string& target_extension_id, const std::string& channel_name) { - if (!profile_) - return; - RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); if (!source) return; + // Note: we use the source's profile here. If the source is an incognito + // process, we will use the incognito EPM to find the right extension process, + // which depends on whether the extension uses spanning or split mode. MessagePort receiver( - profile_->GetExtensionProcessManager()->GetExtensionProcess( + source->profile()->GetExtensionProcessManager()->GetExtensionProcess( target_extension_id), MSG_ROUTING_CONTROL); TabContents* source_contents = tab_util::GetTabContentsByID( @@ -321,8 +321,7 @@ int ExtensionMessageService::OpenSpecialChannelToExtension( AllocatePortIdPair(&port1_id, &port2_id); MessagePort receiver( - profile_->GetExtensionProcessManager()-> - GetExtensionProcess(extension_id), + profile_->GetExtensionProcessManager()->GetExtensionProcess(extension_id), MSG_ROUTING_CONTROL); if (!OpenChannelImpl(source, tab_json, receiver, port2_id, extension_id, extension_id, channel_name)) @@ -393,7 +392,13 @@ void ExtensionMessageService::PostMessageFromRenderer( void ExtensionMessageService::DispatchEventToRenderers( const std::string& event_name, const std::string& event_args, - bool has_incognito_data, const GURL& event_url) { + Profile* source_profile, const GURL& event_url) { + if (!profile_) + return; + + // We don't expect to get events from a completely different profile. + DCHECK(!source_profile || profile_->IsSameProfile(source_profile)); + ListenerMap::iterator it = listeners_.find(event_name); if (it == listeners_.end()) return; @@ -411,17 +416,20 @@ void ExtensionMessageService::DispatchEventToRenderers( continue; } - DispatchEvent( - renderer, event_name, event_args, has_incognito_data, event_url); + // Is this event from a different profile than the renderer (ie, an + // incognito tab event sent to a normal process, or vice versa). + bool cross_profile = + source_profile && renderer->profile() != source_profile; + DispatchEvent(renderer, event_name, event_args, cross_profile, event_url); } } void ExtensionMessageService::DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, - bool has_incognito_data, const GURL& event_url) { + Profile* source_profile, const GURL& event_url) { DispatchEventToRenderers(GetPerExtensionEventName(event_name, extension_id), - event_args, has_incognito_data, event_url); + event_args, source_profile, event_url); } void ExtensionMessageService::Observe(NotificationType type, diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index e18140b..9d8c038 100644 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -87,14 +87,14 @@ class ExtensionMessageService // event is only sent to extension with host permissions for this url. virtual void DispatchEventToRenderers( const std::string& event_name, const std::string& event_args, - bool has_incognito_data, const GURL& event_url); + Profile* source_profile, const GURL& event_url); // Same as above, except use the extension-specific naming scheme for the // event. This is used by events that are per-extension. void DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, - bool has_incognito_data, const GURL& event_url); + Profile* source_profile, const GURL& event_url); // Given an extension's ID, opens a channel between the given renderer "port" // and every listening context owned by that extension. |channel_name| is diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc index 9a393c6..533b840 100644 --- a/chrome/browser/extensions/extension_messages_apitest.cc +++ b/chrome/browser/extensions/extension_messages_apitest.cc @@ -28,19 +28,19 @@ class MessageSender : public NotificationObserver { // from the origin http://b.com/ are supposed to arrive. message_service->DispatchEventToRenderers("test.onMessage", "[{\"lastMessage\":false,\"data\":\"no restriction\"}]", - Source<Profile>(source).ptr()->IsOffTheRecord(), + Source<Profile>(source).ptr(), GURL()); message_service->DispatchEventToRenderers("test.onMessage", "[{\"lastMessage\":false,\"data\":\"http://a.com/\"}]", - Source<Profile>(source).ptr()->IsOffTheRecord(), + Source<Profile>(source).ptr(), GURL("http://a.com/")); message_service->DispatchEventToRenderers("test.onMessage", "[{\"lastMessage\":false,\"data\":\"http://b.com/\"}]", - Source<Profile>(source).ptr()->IsOffTheRecord(), + Source<Profile>(source).ptr(), GURL("http://b.com/")); message_service->DispatchEventToRenderers("test.onMessage", "[{\"lastMessage\":true,\"data\":\"last message\"}]", - Source<Profile>(source).ptr()->IsOffTheRecord(), + Source<Profile>(source).ptr(), GURL()); } diff --git a/chrome/browser/extensions/extension_omnibox_api.cc b/chrome/browser/extensions/extension_omnibox_api.cc index 89ed86c..26c91bb 100644 --- a/chrome/browser/extensions/extension_omnibox_api.cc +++ b/chrome/browser/extensions/extension_omnibox_api.cc @@ -36,8 +36,7 @@ const char kDescriptionStylesOffset[] = "offset"; void ExtensionOmniboxEventRouter::OnInputStarted( Profile* profile, const std::string& extension_id) { profile->GetExtensionMessageService()->DispatchEventToExtension( - extension_id, events::kOnInputStarted, "[]", profile->IsOffTheRecord(), - GURL()); + extension_id, events::kOnInputStarted, "[]", profile, GURL()); } // static @@ -56,8 +55,7 @@ bool ExtensionOmniboxEventRouter::OnInputChanged( base::JSONWriter::Write(&args, false, &json_args); profile->GetExtensionMessageService()->DispatchEventToExtension( - extension_id, events::kOnInputChanged, json_args, - profile->IsOffTheRecord(), GURL()); + extension_id, events::kOnInputChanged, json_args, profile, GURL()); return true; } @@ -73,8 +71,7 @@ void ExtensionOmniboxEventRouter::OnInputEntered( base::JSONWriter::Write(&args, false, &json_args); profile->GetExtensionMessageService()->DispatchEventToExtension( - extension_id, events::kOnInputEntered, json_args, - profile->IsOffTheRecord(), GURL()); + extension_id, events::kOnInputEntered, json_args, profile, GURL()); NotificationService::current()->Notify( NotificationType::EXTENSION_OMNIBOX_INPUT_ENTERED, @@ -85,8 +82,7 @@ void ExtensionOmniboxEventRouter::OnInputEntered( void ExtensionOmniboxEventRouter::OnInputCancelled( Profile* profile, const std::string& extension_id) { profile->GetExtensionMessageService()->DispatchEventToExtension( - extension_id, events::kOnInputCancelled, "[]", profile->IsOffTheRecord(), - GURL()); + extension_id, events::kOnInputCancelled, "[]", profile, GURL()); } bool OmniboxSendSuggestionsFunction::RunImpl() { diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc index e6c559b..7fc8f1b 100644 --- a/chrome/browser/extensions/extension_popup_api.cc +++ b/chrome/browser/extensions/extension_popup_api.cc @@ -498,8 +498,5 @@ void PopupEventRouter::OnPopupClosed(Profile* profile, routing_id); profile->GetExtensionMessageService()->DispatchEventToRenderers( - full_event_name, - base::JSONWriter::kEmptyArray, - profile->IsOffTheRecord(), - GURL()); + full_event_name, base::JSONWriter::kEmptyArray, profile, GURL()); } diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 49205cd..1fff690 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -21,6 +21,37 @@ #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/render_messages.h" +#include "chrome/common/url_constants.h" + +namespace { + +// Incognito profiles use this process manager. It is mostly a shim that decides +// whether to fall back on the original profile's ExtensionProcessManager based +// on whether a given extension uses "split" or "spanning" incognito behavior. +class IncognitoExtensionProcessManager : public ExtensionProcessManager { + public: + explicit IncognitoExtensionProcessManager(Profile* profile); + virtual ~IncognitoExtensionProcessManager() {} + virtual ExtensionHost* CreateView(Extension* extension, + const GURL& url, + Browser* browser, + ViewType::Type view_type); + virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); + virtual RenderProcessHost* GetExtensionProcess(const GURL& url); + + private: + // NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Returns the extension for an URL, which can either be a chrome-extension + // URL or a web app URL. + Extension* GetExtensionOrAppByURL(const GURL& url); + + ExtensionProcessManager* original_manager_; +}; static void CreateBackgroundHost( ExtensionProcessManager* manager, Extension* extension) { @@ -37,6 +68,19 @@ static void CreateBackgroundHosts( } } +} // namespace + +// +// ExtensionProcessManager +// + +// static +ExtensionProcessManager* ExtensionProcessManager::Create(Profile* profile) { + return (profile->IsOffTheRecord()) ? + new IncognitoExtensionProcessManager(profile) : + new ExtensionProcessManager(profile); +} + ExtensionProcessManager::ExtensionProcessManager(Profile* profile) : browsing_instance_(new BrowsingInstance(profile)) { registrar_.Add(this, NotificationType::EXTENSIONS_READY, @@ -85,7 +129,7 @@ ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url, // A NULL browser may only be given for pop-up views. DCHECK(browser || (!browser && view_type == ViewType::EXTENSION_POPUP)); ExtensionsService* service = - browsing_instance_->profile()->GetExtensionsService(); + browsing_instance_->profile()->GetExtensionsService(); if (service) { Extension* extension = service->GetExtensionByURL(url); if (extension) @@ -116,8 +160,12 @@ ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url, return CreateView(url, browser, ViewType::EXTENSION_INFOBAR); } -ExtensionHost* ExtensionProcessManager::CreateBackgroundHost( +void ExtensionProcessManager::CreateBackgroundHost( Extension* extension, const GURL& url) { + // Don't create multiple background hosts for an extension. + if (GetBackgroundHostForExtension(extension)) + return; + ExtensionHost* host = #if defined(OS_MACOSX) new ExtensionHostMac(extension, GetSiteInstanceForURL(url), url, @@ -129,16 +177,19 @@ ExtensionHost* ExtensionProcessManager::CreateBackgroundHost( host->CreateRenderViewSoon(NULL); // create a RenderViewHost with no view OnExtensionHostCreated(host, true); - return host; } void ExtensionProcessManager::OpenOptionsPage(Extension* extension, Browser* browser) { DCHECK(!extension->options_url().is_empty()); - // We can't open extensions URLs in incognito windows. - if (!browser || browser->profile()->IsOffTheRecord()) - browser = Browser::GetOrCreateTabbedBrowser(browsing_instance_->profile()); + // We can't open extensions URLs in incognito windows, unless the extension + // uses "split" incognito mode. + if (!browser || (browser->profile()->IsOffTheRecord() && + !extension->incognito_split_mode())) { + browser = Browser::GetOrCreateTabbedBrowser( + browsing_instance_->profile()->GetOriginalProfile()); + } browser->OpenURL(extension->options_url(), GURL(), SINGLETON_TAB, PageTransition::LINK); @@ -159,6 +210,8 @@ ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension( void ExtensionProcessManager::RegisterExtensionProcess( const std::string& extension_id, int process_id) { + // TODO(mpcomplete): This is the only place we actually read process_ids_. + // Is it necessary? ProcessIDMap::const_iterator it = process_ids_.find(extension_id); if (it != process_ids_.end() && (*it).second == process_id) return; @@ -197,7 +250,7 @@ RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( if (!browsing_instance_->HasSiteInstance(url)) return NULL; scoped_refptr<SiteInstance> site = - browsing_instance_->GetSiteInstanceForURL(url); + browsing_instance_->GetSiteInstanceForURL(url); if (site->HasProcess()) return site->GetProcess(); return NULL; @@ -205,13 +258,8 @@ RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( const std::string& extension_id) { - ProcessIDMap::const_iterator it = process_ids_.find(extension_id); - if (it == process_ids_.end()) - return NULL; - - RenderProcessHost* rph = RenderProcessHost::FromID(it->second); - DCHECK(rph) << "We should have unregistered this host."; - return rph; + return GetExtensionProcess( + Extension::GetBaseURLFromExtensionId(extension_id)); } SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { @@ -285,6 +333,8 @@ void ExtensionProcessManager::Observe(NotificationType type, void ExtensionProcessManager::OnExtensionHostCreated(ExtensionHost* host, bool is_background) { + DCHECK_EQ(browsing_instance_->profile(), host->profile()); + all_hosts_.insert(host); if (is_background) background_hosts_.insert(host); @@ -301,3 +351,93 @@ void ExtensionProcessManager::CloseBackgroundHosts() { delete *current; } } + +// +// IncognitoExtensionProcessManager +// + +IncognitoExtensionProcessManager::IncognitoExtensionProcessManager( + Profile* profile) + : ExtensionProcessManager(profile), + original_manager_(profile->GetOriginalProfile()-> + GetExtensionProcessManager()) { + DCHECK(profile->IsOffTheRecord()); + + registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY, + NotificationService::AllSources()); +} + +ExtensionHost* IncognitoExtensionProcessManager::CreateView( + Extension* extension, + const GURL& url, + Browser* browser, + ViewType::Type view_type) { + if (extension->incognito_split_mode()) { + return ExtensionProcessManager::CreateView(extension, url, + browser, view_type); + } else { + return original_manager_->CreateView(extension, url, browser, view_type); + } +} + +void IncognitoExtensionProcessManager::CreateBackgroundHost( + Extension* extension, const GURL& url) { + if (extension->incognito_split_mode()) { + ExtensionProcessManager::CreateBackgroundHost(extension, url); + } else { + // Do nothing. If an extension is spanning, then its original-profile + // background page is shared with incognito, so we don't create another. + } +} + +SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( + const GURL& url) { + Extension* extension = GetExtensionOrAppByURL(url); + if (!extension || extension->incognito_split_mode()) { + return ExtensionProcessManager::GetSiteInstanceForURL(url); + } else { + return original_manager_->GetSiteInstanceForURL(url); + } +} + +RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( + const GURL& url) { + Extension* extension = GetExtensionOrAppByURL(url); + if (!extension || extension->incognito_split_mode()) { + return ExtensionProcessManager::GetExtensionProcess(url); + } else { + return original_manager_->GetExtensionProcess(url); + } +} + +Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( + const GURL& url) { + ExtensionsService* service = + browsing_instance_->profile()->GetExtensionsService(); + return (url.SchemeIs(chrome::kExtensionScheme)) ? + service->GetExtensionByURL(url) : service->GetExtensionByWebExtent(url); +} + +void IncognitoExtensionProcessManager::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::BROWSER_WINDOW_READY: { + // We want to spawn our background hosts as soon as the user opens an + // incognito window. Watch for new browsers and create the hosts if + // it matches our profile. + Browser* browser = Source<Browser>(source).ptr(); + if (browser->profile() == browsing_instance_->profile()) { + ExtensionsService* service = + browsing_instance_->profile()->GetExtensionsService(); + if (service && service->is_ready()) + CreateBackgroundHosts(this, service->extensions()); + } + break; + } + default: + ExtensionProcessManager::Observe(type, source, details); + break; + } +} diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index 5f71806..d284275 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -24,17 +24,18 @@ class Profile; class RenderProcessHost; class SiteInstance; -// Manages dynamic state of running Chromium extensions. There is one instance -// of this class per Profile (including OTR). +// Manages dynamic state of running Chromium extensions. There is one instance +// of this class per Profile. OTR Profiles have a separate instance that keeps +// track of split-mode extensions only. class ExtensionProcessManager : public NotificationObserver { public: - explicit ExtensionProcessManager(Profile* profile); - ~ExtensionProcessManager(); + static ExtensionProcessManager* Create(Profile* profile); + virtual ~ExtensionProcessManager(); // Creates a new ExtensionHost with its associated view, grouping it in the // appropriate SiteInstance (and therefore process) based on the URL and // profile. - ExtensionHost* CreateView(Extension* extension, + virtual ExtensionHost* CreateView(Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type); @@ -51,19 +52,19 @@ class ExtensionProcessManager : public NotificationObserver { ExtensionHost* CreateInfobar(const GURL& url, Browser* browser); - // Creates a new UI-less extension instance. Like CreateView, but not - // displayed anywhere. - ExtensionHost* CreateBackgroundHost(Extension* extension, const GURL& url); - // Open the extension's options page. void OpenOptionsPage(Extension* extension, Browser* browser); + // Creates a new UI-less extension instance. Like CreateView, but not + // displayed anywhere. + virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + // Gets the ExtensionHost for the background page for an extension, or NULL if // the extension isn't running or doesn't have a background page. ExtensionHost* GetBackgroundHostForExtension(Extension* extension); // Returns the SiteInstance that the given URL belongs to. - SiteInstance* GetSiteInstanceForURL(const GURL& url); + virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); // Registers an extension process by |extension_id| and specifying which // |process_id| it belongs to. @@ -74,33 +75,33 @@ class ExtensionProcessManager : public NotificationObserver { void UnregisterExtensionProcess(int process_id); // Returns the extension process that |url| is associated with if it exists. - RenderProcessHost* GetExtensionProcess(const GURL& url); + virtual RenderProcessHost* GetExtensionProcess(const GURL& url); // Returns the process that the extension with the given ID is running in. - // NOTE: This does not currently handle app processes with no - // ExtensionFunctionDispatcher objects. RenderProcessHost* GetExtensionProcess(const std::string& extension_id); // Returns true if |host| is managed by this process manager. bool HasExtensionHost(ExtensionHost* host) const; - // NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - typedef std::set<ExtensionHost*> ExtensionHostSet; typedef ExtensionHostSet::const_iterator const_iterator; const_iterator begin() const { return all_hosts_.begin(); } const_iterator end() const { return all_hosts_.end(); } - private: + protected: + explicit ExtensionProcessManager(Profile* profile); + // Called just after |host| is created so it can be registered in our lists. void OnExtensionHostCreated(ExtensionHost* host, bool is_background); // Called on browser shutdown to close our extension hosts. void CloseBackgroundHosts(); + // NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + NotificationRegistrar registrar_; // The set of all ExtensionHosts managed by this process manager. diff --git a/chrome/browser/extensions/extension_process_manager_unittest.cc b/chrome/browser/extensions/extension_process_manager_unittest.cc index cf2d5d9..f73c0b0 100644 --- a/chrome/browser/extensions/extension_process_manager_unittest.cc +++ b/chrome/browser/extensions/extension_process_manager_unittest.cc @@ -33,11 +33,11 @@ TEST_F(ExtensionProcessManagerTest, ProcessGrouping) { // functionality. This means we can get away with a NULL UserScriptMaster. TestingProfile profile1; scoped_ptr<ExtensionProcessManager> manager1( - new ExtensionProcessManager(&profile1)); + ExtensionProcessManager::Create(&profile1)); TestingProfile profile2; scoped_ptr<ExtensionProcessManager> manager2( - new ExtensionProcessManager(&profile2)); + ExtensionProcessManager::Create(&profile2)); // Extensions with common origins ("scheme://id/") should be grouped in the // same SiteInstance. diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc index 30f7b37..f281c74 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -99,7 +99,8 @@ bool AllowExtensionResourceLoad(URLRequest* request, // This is because an extension must run in a single process, and an // incognito tab prevents that. if (context->is_off_the_record() && - info->resource_type() == ResourceType::MAIN_FRAME) { + info->resource_type() == ResourceType::MAIN_FRAME && + !context->ExtensionCanLoadInIncognito(request->url().host())) { LOG(ERROR) << "Denying load of " << request->url().spec() << " from " << "incognito tab."; return false; diff --git a/chrome/browser/extensions/extension_sidebar_api.cc b/chrome/browser/extensions/extension_sidebar_api.cc index e5f693e..d63f632 100644 --- a/chrome/browser/extensions/extension_sidebar_api.cc +++ b/chrome/browser/extensions/extension_sidebar_api.cc @@ -90,8 +90,7 @@ void ExtensionSidebarEventRouter::OnStateChanged( const std::string& extension_id(content_id); profile->GetExtensionMessageService()->DispatchEventToExtension( - extension_id, kOnStateChanged, json_args, - profile->IsOffTheRecord(), GURL()); + extension_id, kOnStateChanged, json_args, profile, GURL()); } diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc index f4075b6..6df1ac7 100644 --- a/chrome/browser/extensions/extension_ui_unittest.cc +++ b/chrome/browser/extensions/extension_ui_unittest.cc @@ -72,10 +72,10 @@ TEST(ExtensionUITest, GenerateExtensionsJSONData) { std::vector<ExtensionPage> pages; pages.push_back(ExtensionPage( GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/bar.html"), - 42, 88)); + 42, 88, false)); pages.push_back(ExtensionPage( GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/dog.html"), - 0, 0)); + 0, 0, false)); expected_output_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("ui") diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 362ef22..7ba0f42 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -204,7 +204,7 @@ ExtensionsService::ExtensionsService(Profile* profile, registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED, - Source<Profile>(profile_)); + NotificationService::AllSources()); prefs->AddPrefObserver(prefs::kExtensionInstallAllowList, this); prefs->AddPrefObserver(prefs::kExtensionInstallDenyList, this); @@ -1368,7 +1368,8 @@ void ExtensionsService::Observe(NotificationType type, } case NotificationType::EXTENSION_PROCESS_TERMINATED: { - DCHECK_EQ(profile_, Source<Profile>(source).ptr()); + if (profile_ != Source<Profile>(source).ptr()->GetOriginalProfile()) + break; ExtensionHost* host = Details<ExtensionHost>(details).ptr(); @@ -1387,7 +1388,11 @@ void ExtensionsService::Observe(NotificationType type, // Unload the entire extension. We want it to be in a consistent state: // either fully working or not loaded at all, but never half-crashed. - UnloadExtension(host->extension()->id()); + // We do it in a PostTask so that other handlers of this notification will + // still have access to the Extension and ExtensionHost. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableMethod(this, &ExtensionsService::UnloadExtension, + host->extension()->id())); break; } diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index 08f2af3..d335cde 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -15,6 +15,7 @@ #include "base/thread.h" #include "base/version.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/debugger/devtools_toggle_action.h" @@ -115,6 +116,8 @@ void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path, l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLED_EXTENSION)); localized_strings.SetString("inDevelopment", l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT)); + localized_strings.SetString("viewIncognito", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO)); localized_strings.SetString("extensionId", l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); localized_strings.SetString("extensionVersion", @@ -317,18 +320,14 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { std::vector<ExtensionResource>* extension_icons = new std::vector<ExtensionResource>(); - ExtensionProcessManager* process_manager = - extensions_service_->profile()->GetExtensionProcessManager(); const ExtensionList* extensions = extensions_service_->extensions(); for (ExtensionList::const_iterator extension = extensions->begin(); extension != extensions->end(); ++extension) { if (ShouldShowExtension(*extension)) { - RenderProcessHost* process = - process_manager->GetExtensionProcess((*extension)->url()); extensions_list->Append(CreateExtensionDetailValue( extensions_service_.get(), *extension, - GetActivePagesForExtension(process, *extension), + GetActivePagesForExtension(*extension), true)); // enabled extension_icons->push_back(PickExtensionIcon(*extension)); } @@ -337,12 +336,10 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { for (ExtensionList::const_iterator extension = extensions->begin(); extension != extensions->end(); ++extension) { if (ShouldShowExtension(*extension)) { - RenderProcessHost* process = - process_manager->GetExtensionProcess((*extension)->url()); extensions_list->Append(CreateExtensionDetailValue( extensions_service_.get(), *extension, - GetActivePagesForExtension(process, *extension), + GetActivePagesForExtension(*extension), false)); // enabled extension_icons->push_back(PickExtensionIcon(*extension)); } @@ -836,6 +833,7 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( } view_value->SetInteger("renderViewId", iter->render_view_id); view_value->SetInteger("renderProcessId", iter->render_process_id); + view_value->SetBoolean("incognito", iter->incognito); views->Append(view_value); } extension_data->Set("views", views); @@ -847,11 +845,36 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( } std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( - RenderProcessHost* process, Extension* extension) { std::vector<ExtensionPage> result; + + // Get the extension process's active views. + ExtensionProcessManager* process_manager = + extensions_service_->profile()->GetExtensionProcessManager(); + GetActivePagesForExtensionProcess( + process_manager->GetExtensionProcess(extension->url()), + extension, &result); + + // Repeat for the incognito process, if applicable. + if (extensions_service_->profile()->HasOffTheRecordProfile() && + extension->incognito_split_mode()) { + ExtensionProcessManager* process_manager = + extensions_service_->profile()->GetOffTheRecordProfile()-> + GetExtensionProcessManager(); + GetActivePagesForExtensionProcess( + process_manager->GetExtensionProcess(extension->url()), + extension, &result); + } + + return result; +} + +void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( + RenderProcessHost* process, + Extension* extension, + std::vector<ExtensionPage> *result) { if (!process) - return result; + return; RenderProcessHost::listeners_iterator iter = process->ListenersIterator(); for (; !iter.IsAtEnd(); iter.Advance()) { @@ -873,10 +896,9 @@ std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( continue; } - result.push_back(ExtensionPage(url, process->id(), host->routing_id())); + result->push_back(ExtensionPage(url, process->id(), host->routing_id(), + process->profile()->IsOffTheRecord())); } - - return result; } ExtensionsDOMHandler::~ExtensionsDOMHandler() { diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h index fb186fc..0d961a1 100644 --- a/chrome/browser/extensions/extensions_ui.h +++ b/chrome/browser/extensions/extensions_ui.h @@ -32,12 +32,14 @@ class UserScript; // Information about a page running in an extension, for example a toolstrip, // a background page, or a tab contents. struct ExtensionPage { - ExtensionPage(const GURL& url, int render_process_id, int render_view_id) + ExtensionPage(const GURL& url, int render_process_id, int render_view_id, + bool incognito) : url(url), render_process_id(render_process_id), - render_view_id(render_view_id) {} + render_view_id(render_view_id), incognito(incognito) {} GURL url; int render_process_id; int render_view_id; + bool incognito; }; class ExtensionsUIHTMLSource : public ChromeURLDataManager::DataSource { @@ -196,9 +198,11 @@ class ExtensionsDOMHandler const NotificationDetails& details); // Helper that lists the current active html pages for an extension. - std::vector<ExtensionPage> GetActivePagesForExtension( + std::vector<ExtensionPage> GetActivePagesForExtension(Extension* extension); + void GetActivePagesForExtensionProcess( RenderProcessHost* process, - Extension* extension); + Extension* extension, + std::vector<ExtensionPage> *result); // Returns the best icon to display in the UI for an extension, or an empty // ExtensionResource if no good icon exists. diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index c4104a5..f14958f 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -325,15 +325,18 @@ ChromeURLRequestContext* FactoryForOriginal::Create() { // Factory that creates the ChromeURLRequestContext for extensions. class FactoryForExtensions : public ChromeURLRequestContextFactory { public: - FactoryForExtensions(Profile* profile, const FilePath& cookie_store_path) + FactoryForExtensions(Profile* profile, const FilePath& cookie_store_path, + bool incognito) : ChromeURLRequestContextFactory(profile), - cookie_store_path_(cookie_store_path) { + cookie_store_path_(cookie_store_path), + incognito_(incognito) { } virtual ChromeURLRequestContext* Create(); private: FilePath cookie_store_path_; + bool incognito_; }; ChromeURLRequestContext* FactoryForExtensions::Create() { @@ -342,11 +345,14 @@ ChromeURLRequestContext* FactoryForExtensions::Create() { IOThread::Globals* io_thread_globals = io_thread()->globals(); - // All we care about for extensions is the cookie store. - DCHECK(!cookie_store_path_.empty()); + // All we care about for extensions is the cookie store. For incognito, we + // use a non-persistent cookie store. + scoped_refptr<SQLitePersistentCookieStore> cookie_db = NULL; + if (!incognito_) { + DCHECK(!cookie_store_path_.empty()); + cookie_db = new SQLitePersistentCookieStore(cookie_store_path_); + } - scoped_refptr<SQLitePersistentCookieStore> cookie_db = - new SQLitePersistentCookieStore(cookie_store_path_); net::CookieMonster* cookie_monster = new net::CookieMonster(cookie_db.get(), NULL); @@ -624,7 +630,7 @@ ChromeURLRequestContextGetter::CreateOriginalForExtensions( DCHECK(!profile->IsOffTheRecord()); return new ChromeURLRequestContextGetter( profile, - new FactoryForExtensions(profile, cookie_store_path)); + new FactoryForExtensions(profile, cookie_store_path, false)); } // static @@ -635,6 +641,15 @@ ChromeURLRequestContextGetter::CreateOffTheRecord(Profile* profile) { profile, new FactoryForOffTheRecord(profile)); } +// static +ChromeURLRequestContextGetter* +ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions( + Profile* profile) { + DCHECK(profile->IsOffTheRecord()); + return new ChromeURLRequestContextGetter( + profile, new FactoryForExtensions(profile, FilePath(), true)); +} + void ChromeURLRequestContextGetter::CleanupOnUIThread() { CheckCurrentlyOnMainThread(); @@ -798,6 +813,13 @@ bool ChromeURLRequestContext::ExtensionHasWebExtent(const std::string& id) { return iter != extension_info_.end() && !iter->second->extent.is_empty(); } +bool ChromeURLRequestContext::ExtensionCanLoadInIncognito( + const std::string& id) { + ExtensionInfoMap::iterator iter = extension_info_.find(id); + // Only split-mode extensions can load in incognito profiles. + return iter != extension_info_.end() && iter->second->incognito_split_mode; +} + std::string ChromeURLRequestContext::GetDefaultLocaleForExtension( const std::string& id) { ExtensionInfoMap::iterator iter = extension_info_.find(id); @@ -852,14 +874,11 @@ const std::string& ChromeURLRequestContext::GetUserAgent( void ChromeURLRequestContext::OnNewExtensions(const std::string& id, ExtensionInfo* info) { - if (!is_off_the_record_) - extension_info_[id] = linked_ptr<ExtensionInfo>(info); + extension_info_[id] = linked_ptr<ExtensionInfo>(info); } void ChromeURLRequestContext::OnUnloadedExtension(const std::string& id) { CheckCurrentlyOnIOThread(); - if (is_off_the_record_) - return; ExtensionInfoMap::iterator iter = extension_info_.find(id); if (iter != extension_info_.end()) { extension_info_.erase(iter); @@ -976,6 +995,7 @@ ChromeURLRequestContextFactory::ChromeURLRequestContextFactory(Profile* profile) (*iter)->name(), (*iter)->path(), (*iter)->default_locale(), + (*iter)->incognito_split_mode(), (*iter)->web_extent(), (*iter)->GetEffectiveHostPermissions(), (*iter)->api_permissions())); diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index 2638d46..97997e3 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -51,12 +51,17 @@ class ChromeURLRequestContext : public URLRequestContext { // could be immutable and ref-counted so that we could use them directly from // both threads. There is only a small amount of mutable state in Extension. struct ExtensionInfo { - ExtensionInfo(const std::string& name, const FilePath& path, + ExtensionInfo(const std::string& name, + const FilePath& path, const std::string& default_locale, + bool incognito_split_mode, const ExtensionExtent& extent, const ExtensionExtent& effective_host_permissions, const std::vector<std::string>& api_permissions) - : name(name), path(path), default_locale(default_locale), + : name(name), + path(path), + default_locale(default_locale), + incognito_split_mode(incognito_split_mode), extent(extent), effective_host_permissions(effective_host_permissions), api_permissions(api_permissions) { @@ -64,6 +69,7 @@ class ChromeURLRequestContext : public URLRequestContext { const std::string name; const FilePath path; const std::string default_locale; + const bool incognito_split_mode; const ExtensionExtent extent; const ExtensionExtent effective_host_permissions; std::vector<std::string> api_permissions; @@ -84,6 +90,10 @@ class ChromeURLRequestContext : public URLRequestContext { // extent. bool ExtensionHasWebExtent(const std::string& id); + // Returns true if the specified extension exists and can load in incognito + // contexts. + bool ExtensionCanLoadInIncognito(const std::string& id); + // Returns an empty string if the extension with |id| doesn't have a default // locale. std::string GetDefaultLocaleForExtension(const std::string& id); @@ -312,6 +322,11 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, // called on the UI thread. static ChromeURLRequestContextGetter* CreateOffTheRecord(Profile* profile); + // Create an instance for an OTR profile for extensions. This is expected + // to get called on UI thread. + static ChromeURLRequestContextGetter* CreateOffTheRecordForExtensions( + Profile* profile); + // Clean up UI thread resources. This is expected to get called on the UI // thread before the instance is deleted on the IO thread. void CleanupOnUIThread(); diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 2f1641e..5c3669c 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -18,6 +18,8 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/file_system/file_system_host_context.h" +#include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/find_bar_state.h" #include "chrome/browser/in_process_webkit/webkit_context.h" #include "chrome/browser/net/chrome_url_request_context.h" @@ -119,6 +121,7 @@ class OffTheRecordProfileImpl : public Profile, : profile_(real_profile), start_time_(Time::Now()) { request_context_ = ChromeURLRequestContextGetter::CreateOffTheRecord(this); + extension_process_manager_.reset(ExtensionProcessManager::Create(this)); // Register for browser close notifications so we can detect when the last // off-the-record window is closed, in which case we can clean our states @@ -134,6 +137,7 @@ class OffTheRecordProfileImpl : public Profile, Source<Profile>(this), NotificationService::NoDetails()); CleanupRequestContext(request_context_); + CleanupRequestContext(extensions_request_context_); // Clean up all DB files/directories ChromeThread::PostTask( @@ -220,7 +224,7 @@ class OffTheRecordProfileImpl : public Profile, } virtual ExtensionProcessManager* GetExtensionProcessManager() { - return GetOriginalProfile()->GetExtensionProcessManager(); + return extension_process_manager_.get(); } virtual ExtensionMessageService* GetExtensionMessageService() { @@ -357,7 +361,12 @@ class OffTheRecordProfileImpl : public Profile, } URLRequestContextGetter* GetRequestContextForExtensions() { - return GetOriginalProfile()->GetRequestContextForExtensions(); + if (!extensions_request_context_) { + extensions_request_context_ = + ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions(this); + } + + return extensions_request_context_; } virtual net::SSLConfigService* GetSSLConfigService() { @@ -543,9 +552,14 @@ class OffTheRecordProfileImpl : public Profile, // The real underlying profile. Profile* profile_; + scoped_ptr<ExtensionProcessManager> extension_process_manager_; + // The context to use for requests made from this OTR session. scoped_refptr<ChromeURLRequestContextGetter> request_context_; + // The context to use for requests made by an extension while in OTR mode. + scoped_refptr<ChromeURLRequestContextGetter> extensions_request_context_; + // The download manager that only stores downloaded items in memory. scoped_refptr<DownloadManager> download_manager_; diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc index e81cef4..6a7cb51 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -172,6 +172,7 @@ void PostExtensionLoadedToContextGetter(ChromeURLRequestContextGetter* getter, extension->name(), extension->path(), extension->default_locale(), + extension->incognito_split_mode(), extension->web_extent(), extension->GetEffectiveHostPermissions(), extension->api_permissions()))); @@ -369,7 +370,7 @@ void ProfileImpl::InitExtensions() { extension_devtools_manager_ = new ExtensionDevToolsManager(this); } - extension_process_manager_.reset(new ExtensionProcessManager(this)); + extension_process_manager_.reset(ExtensionProcessManager::Create(this)); extension_message_service_ = new ExtensionMessageService(this); ExtensionErrorReporter::Init(true); // allow noisy errors. @@ -737,6 +738,9 @@ URLRequestContextGetter* ProfileImpl::GetRequestContextForExtensions() { return extensions_request_context_; } +// TODO(mpcomplete): This is lame. 5+ copies of the extension data on the IO +// thread. We should have 1 shared data object that all the contexts get access +// to. Fix by M8. void ProfileImpl::RegisterExtensionWithRequestContexts( Extension* extension) { // Notify the default, extension and media contexts on the IO thread. @@ -751,6 +755,19 @@ void ProfileImpl::RegisterExtensionWithRequestContexts( static_cast<ChromeURLRequestContextGetter*>( GetRequestContextForMedia()), extension); + + // Ditto for OTR if it's active, except for the media context which is the + // same as the regular context. + if (off_the_record_profile_.get()) { + PostExtensionLoadedToContextGetter( + static_cast<ChromeURLRequestContextGetter*>( + off_the_record_profile_->GetRequestContext()), + extension); + PostExtensionLoadedToContextGetter( + static_cast<ChromeURLRequestContextGetter*>( + off_the_record_profile_->GetRequestContextForExtensions()), + extension); + } } void ProfileImpl::UnregisterExtensionWithRequestContexts( @@ -767,6 +784,19 @@ void ProfileImpl::UnregisterExtensionWithRequestContexts( static_cast<ChromeURLRequestContextGetter*>( GetRequestContextForMedia()), extension); + + // Ditto for OTR if it's active, except for the media context which is the + // same as the regular context. + if (off_the_record_profile_.get()) { + PostExtensionUnloadedToContextGetter( + static_cast<ChromeURLRequestContextGetter*>( + off_the_record_profile_->GetRequestContext()), + extension); + PostExtensionUnloadedToContextGetter( + static_cast<ChromeURLRequestContextGetter*>( + off_the_record_profile_->GetRequestContextForExtensions()), + extension); + } } net::SSLConfigService* ProfileImpl::GetSSLConfigService() { diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html index 5e9b020..a5671a424 100644 --- a/chrome/browser/resources/extensions_ui.html +++ b/chrome/browser/resources/extensions_ui.html @@ -378,12 +378,14 @@ var extensionDataFormat = { { 'path': 'toolstrip.html', 'renderViewId': 1, - 'renderProcessId': 1 + 'renderProcessId': 1, + 'incognito': false }, { 'path': 'background.html', 'renderViewId': 2, - 'renderProcessId': 1 + 'renderProcessId': 1, + 'incognito': false } ] }, @@ -415,7 +417,8 @@ var extensionDataFormat = { { 'path': 'foo/bar/toolstrip.html', 'renderViewId': 3, - 'renderProcessId': 1 + 'renderProcessId': 1, + 'incognito': false } ], "hasPopupAction": false @@ -856,10 +859,13 @@ document.addEventListener('DOMContentLoaded', requestExtensionsData); </span> <ul class="extension-views"> <li jsselect="views"> - <a jsvalues=".extensionView:$this" href="#" - onclick="sendInspectMessage(this.extensionView); return false;"> - <span jscontent="path"></span> - </a> + <span jsvalues=".extensionView:$this"> + <a jsvalues=".extensionView:$this" href="#" + onclick="sendInspectMessage(this.extensionView); return false;"> + <span jscontent="path"></span></a> + <span jsdisplay="incognito" + i18n-content="viewIncognito">(INCOGNITO)</span> + </span> </li> <li i18n-content="inspectPopupsInstructions" class="inspectPopupNote" jsdisplay="hasPopupAction"> diff --git a/chrome/browser/task_manager_resource_providers.cc b/chrome/browser/task_manager_resource_providers.cc index 2b41233..1207462 100644 --- a/chrome/browser/task_manager_resource_providers.cc +++ b/chrome/browser/task_manager_resource_providers.cc @@ -529,8 +529,16 @@ TaskManagerExtensionProcessResource::TaskManagerExtensionProcessResource( pid_ = base::GetProcId(process_handle_); std::wstring extension_name(UTF8ToWide(GetExtension()->name())); DCHECK(!extension_name.empty()); - title_ = l10n_util::GetStringF(IDS_TASK_MANAGER_EXTENSION_PREFIX, - extension_name); + + int message_id = + GetExtension()->is_app() ? + (extension_host_->profile()->IsOffTheRecord() ? + IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX : + IDS_TASK_MANAGER_APP_PREFIX) : + (extension_host_->profile()->IsOffTheRecord() ? + IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX : + IDS_TASK_MANAGER_EXTENSION_PREFIX); + title_ = l10n_util::GetStringF(message_id, extension_name); } TaskManagerExtensionProcessResource::~TaskManagerExtensionProcessResource() { @@ -586,13 +594,25 @@ void TaskManagerExtensionProcessResourceProvider::StartUpdating() { ProfileManager* profile_manager = g_browser_process->profile_manager(); for (ProfileManager::const_iterator it = profile_manager->begin(); it != profile_manager->end(); ++it) { + ExtensionProcessManager* process_manager = + (*it)->GetExtensionProcessManager(); + if (process_manager) { + ExtensionProcessManager::const_iterator jt; + for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) + AddToTaskManager(*jt); + } + + // If we have an incognito profile active, include the split-mode incognito + // extensions. + if (BrowserList::IsOffTheRecordSessionActive()) { ExtensionProcessManager* process_manager = - (*it)->GetExtensionProcessManager(); - if (!process_manager) - continue; + (*it)->GetOffTheRecordProfile()->GetExtensionProcessManager(); + if (process_manager) { ExtensionProcessManager::const_iterator jt; for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) AddToTaskManager(*jt); + } + } } // Register for notifications about extension process changes. diff --git a/chrome/browser/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/views/location_bar/icon_label_bubble_view.cc index 0f7059b..1e0159b 100644 --- a/chrome/browser/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/views/location_bar/icon_label_bubble_view.cc @@ -20,7 +20,8 @@ static const int kLabelPadding = 5; IconLabelBubbleView::IconLabelBubbleView(const int background_images[], int contained_image, const SkColor& color) - : background_painter_(background_images) { + : background_painter_(background_images), + item_padding_(LocationBarView::kItemPadding) { image_ = new views::ImageView(); AddChildView(image_); image_->SetImage( @@ -60,7 +61,7 @@ void IconLabelBubbleView::Layout() { height()); const int label_height = label_->GetPreferredSize().height(); label_->SetBounds(image_->x() + image_->width() + - LocationBarView::kItemPadding, (height() - label_height) / 2, + item_padding_ , (height() - label_height) / 2, width() - GetNonLabelWidth(), label_height); } @@ -74,5 +75,5 @@ gfx::Size IconLabelBubbleView::GetNonLabelSize() { int IconLabelBubbleView::GetNonLabelWidth() { return kBubbleOuterPadding + image_->GetPreferredSize().width() + - LocationBarView::kItemPadding + kBubbleOuterPadding; + item_padding_ + kBubbleOuterPadding; } diff --git a/chrome/browser/views/location_bar/icon_label_bubble_view.h b/chrome/browser/views/location_bar/icon_label_bubble_view.h index de252dd..d2e4b78 100644 --- a/chrome/browser/views/location_bar/icon_label_bubble_view.h +++ b/chrome/browser/views/location_bar/icon_label_bubble_view.h @@ -36,6 +36,7 @@ class IconLabelBubbleView : public views::View { void SetFont(const gfx::Font& font); void SetLabel(const std::wstring& label); void SetImage(const SkBitmap& bitmap); + void SetItemPadding(int padding) { item_padding_ = padding; } virtual void Paint(gfx::Canvas* canvas); virtual gfx::Size GetPreferredSize(); @@ -55,6 +56,8 @@ class IconLabelBubbleView : public views::View { views::ImageView* image_; views::Label* label_; + int item_padding_; + DISALLOW_IMPLICIT_CONSTRUCTORS(IconLabelBubbleView); }; diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc index fdfdacc..c3ef4c0 100644 --- a/chrome/browser/views/location_bar/location_bar_view.cc +++ b/chrome/browser/views/location_bar/location_bar_view.cc @@ -48,6 +48,7 @@ using views::View; const int LocationBarView::kNormalHorizontalEdgeThickness = 1; const int LocationBarView::kVerticalEdgeThickness = 2; const int LocationBarView::kItemPadding = 3; +const int LocationBarView::kExtensionItemPadding = 5; const int LocationBarView::kEdgeItemPadding = kItemPadding; const int LocationBarView::kBubblePadding = 1; const char LocationBarView::kViewClassName[] = @@ -490,9 +491,11 @@ void LocationBarView::Layout() { const SkBitmap& bitmap = profile_->GetExtensionsService()-> GetOmniboxIcon(template_url->GetExtensionId()); selected_keyword_view_->SetImage(bitmap); + selected_keyword_view_->SetItemPadding(kExtensionItemPadding); } else { selected_keyword_view_->SetImage(*ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_OMNIBOX_SEARCH)); + selected_keyword_view_->SetItemPadding(kItemPadding); } } } else if (show_keyword_hint) { diff --git a/chrome/browser/views/location_bar/location_bar_view.h b/chrome/browser/views/location_bar/location_bar_view.h index ebace6b..281fee7 100644 --- a/chrome/browser/views/location_bar/location_bar_view.h +++ b/chrome/browser/views/location_bar/location_bar_view.h @@ -231,6 +231,9 @@ class LocationBarView : public LocationBar, static const int kVerticalEdgeThickness; // Space between items in the location bar. static const int kItemPadding; + // Space between items in the location bar when an extension keyword is + // showing. + static const int kExtensionItemPadding; // Space between the edges and the items next to them. static const int kEdgeItemPadding; // Space between the edge and a bubble. |