diff options
71 files changed, 776 insertions, 223 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 5c0a8992..b034680 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -3322,6 +3322,15 @@ each locale. --> <message name="IDS_TASK_MANAGER_EXTENSION_PREFIX" desc="The prefix for a Task Manager extension row (always visible if the extension has a view)"> Extension: <ph name="EXTENSION_NAME">$1<ex>Sample Extension</ex></ph> </message> + <message name="IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito extension row (may not be visible if incognito is not open)"> + Incognito Extension: <ph name="EXTENSION_NAME">$1<ex>Sample Extension</ex></ph> + </message> + <message name="IDS_TASK_MANAGER_APP_PREFIX" desc="The prefix for a Task Manager app row (always visible if the app has a view)"> + App: <ph name="APP_NAME">$1<ex>Sample App</ex></ph> + </message> + <message name="IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito app row (may not be visible if the app is not open in incognito)"> + Incognito App: <ph name="APP_NAME">$1<ex>Sample App</ex></ph> + </message> <message name="IDS_TASK_MANAGER_TAB_PREFIX" desc="The prefix for a Task Manager Tab row"> Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph> </message> @@ -3615,6 +3624,9 @@ each locale. --> <message name="IDS_EXTENSIONS_IN_DEVELOPMENT" desc="Text that signifies that the extension is currently in development."> (Unpacked) </message> + <message name="IDS_EXTENSIONS_VIEW_INCOGNITO" desc="Text that signifies that the extension view is in an incognito process."> + (Incognito) + </message> <message name="IDS_EXTENSIONS_ID" desc="The ID label in front if the extension ID."> ID: </message> 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. diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 1014714..4699d3a 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -51,13 +51,13 @@ "message": { "type": "string", "description": "Description of the error that has taken place." } } }, - "inIncognitoTab": { + "inIncognitoContext": { "type": "object", "optional": true, "unprivileged": true, - "description": "True for content scripts running inside incognito tabs. Will be undefined for non-content scripts.", + "description": "True for content scripts running inside incognito tabs, and for extension pages running inside an incognito process. The latter only applies to extensions with 'split' incognito_behavior.", "properties": { - "message": { "type": "boolean", "description": "True if the script is in an incongito tab." } + "message": { "type": "boolean", "description": "True if the page or script is in an incongito process." } } } }, diff --git a/chrome/common/extensions/docs/experimental.sidebar.html b/chrome/common/extensions/docs/experimental.sidebar.html index 9e19c34..e4983a3 100644 --- a/chrome/common/extensions/docs/experimental.sidebar.html +++ b/chrome/common/extensions/docs/experimental.sidebar.html @@ -937,7 +937,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>'hidden' indicates sidebar is not defined for the specified tab (show was never called or hide() was called). Nothing is displayed for this sidebar.'shown' means sidebar is defined for the specified tab; mini tab is displayed for this sidebar. Sidebar UI is either collapsed or displaying a content of some other extension's sidebar.'active' indicates that sidebar is defined for the specified tab; sidebar UI is expanded and displaying this sidebar's content.</dd> + <dd>'hidden' indicates sidebar is not defined for the specified tab (show was never called or hide() was called). Nothing is displayed for this sidebar.<br>'shown' means sidebar is defined for the specified tab; mini tab is displayed for this sidebar. Sidebar UI is either collapsed or displaying a content of some other extension's sidebar.<br>'active' indicates that sidebar is defined for the specified tab; sidebar UI is expanded and displaying this sidebar's content.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. diff --git a/chrome/common/extensions/docs/extension.html b/chrome/common/extensions/docs/extension.html index fe903e3..74ceb1e 100644 --- a/chrome/common/extensions/docs/extension.html +++ b/chrome/common/extensions/docs/extension.html @@ -249,7 +249,7 @@ <li> <a href="#property-lastError">lastError</a> </li><li> - <a href="#property-inIncognitoTab">inIncognitoTab</a> + <a href="#property-inIncognitoContext">inIncognitoContext</a> </li> </ol> </li> @@ -490,15 +490,15 @@ For details, see </div> </div><div> - <a name="property-inIncognitoTab"></a> - <h4>inIncognitoTab</h4> + <a name="property-inIncognitoContext"></a> + <h4>inIncognitoContext</h4> <div class="summary"> <!-- Note: intentionally longer 80 columns --> - <span>chrome.extension.</span><span>inIncognitoTab</span> + <span>chrome.extension.</span><span>inIncognitoContext</span> </div> <div> <dt> - <var>inIncognitoTab</var> + <var>inIncognitoContext</var> <em> <!-- TYPE --> @@ -526,7 +526,7 @@ For details, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>True for content scripts running inside incognito tabs. Will be undefined for non-content scripts.</dd> + <dd>True for content scripts running inside incognito tabs, and for extension pages running inside an incognito process. The latter only applies to extensions with 'split' incognito_behavior.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -571,7 +571,7 @@ For details, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>True if the script is in an incongito tab.</dd> + <dd>True if the page or script is in an incongito process.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. diff --git a/chrome/common/extensions/docs/manifest.html b/chrome/common/extensions/docs/manifest.html index a8dcda8..1c4737a 100644 --- a/chrome/common/extensions/docs/manifest.html +++ b/chrome/common/extensions/docs/manifest.html @@ -258,6 +258,8 @@ <a href="#permissions">permissions</a> </li><li> <a href="#version">version</a> + </li><li> + <a href="#incognito">incognito</a> </li> </ol> </li> @@ -352,6 +354,7 @@ are <b>name</b> and <b>version</b>. "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" + "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -675,6 +678,36 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> +<h3 id="incognito">incognito</h3> + +<p> +Either <em>split</em> or <em>spanning</em>, to specify how this extension will +behave if allowed to run in incognito. +</p> + +<p> +<em>spanning</em> is the default for extensions, and means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +<em>split</em> is the default for apps, and it means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (e.g. the incognito process will only see incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. +</p> + <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> </div> diff --git a/chrome/common/extensions/docs/static/manifest.html b/chrome/common/extensions/docs/static/manifest.html index a4d1867..5e1758c 100644 --- a/chrome/common/extensions/docs/static/manifest.html +++ b/chrome/common/extensions/docs/static/manifest.html @@ -43,6 +43,7 @@ are <b>name</b> and <b>version</b>. "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" + "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -368,4 +369,34 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> +<h3 id="incognito">incognito</h3> + +<p> +Either <em>split</em> or <em>spanning</em>, to specify how this extension will +behave if allowed to run in incognito. +</p> + +<p> +<em>spanning</em> is the default for extensions, and means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +<em>split</em> is the default for apps, and it means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (e.g. the incognito process will only see incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. +</p> + <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 9c4366e..61c47cb 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -774,6 +774,7 @@ Extension::Extension(const FilePath& path) launch_container_(LAUNCH_TAB), launch_width_(0), launch_height_(0), + incognito_split_mode_(true), background_page_ready_(false), being_upgraded_(false) { DCHECK(path.IsAbsolute()); @@ -1552,6 +1553,25 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, devtools_url_ = GetResourceURL(devtools_str); } + // Initialize incognito behavior. Apps default to split mode, extensions + // default to spanning. + incognito_split_mode_ = is_app_; + if (source.HasKey(keys::kIncognito)) { + std::string value; + if (!source.GetString(keys::kIncognito, &value)) { + *error = errors::kInvalidIncognitoBehavior; + return false; + } + if (value == values::kIncognitoSpanning) { + incognito_split_mode_ = false; + } else if (value == values::kIncognitoSplit) { + incognito_split_mode_ = true; + } else { + *error = errors::kInvalidIncognitoBehavior; + return false; + } + } + // Although |source| is passed in as a const, it's still possible to modify // it. This is dangerous since the utility process re-uses |source| after // it calls InitFromValue, passing it up to the browser process which calls diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 66e12f2..bb4d4d5e 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -366,6 +366,7 @@ class Extension { LaunchContainer launch_container() const { return launch_container_; } int launch_width() const { return launch_width_; } int launch_height() const { return launch_height_; } + bool incognito_split_mode() const { return incognito_split_mode_; } // Gets the fully resolved absolute launch URL. GURL GetFullLaunchURL() const; @@ -582,6 +583,10 @@ class Extension { // The omnibox keyword for this extension, or empty if there is none. std::string omnibox_keyword_; + // If true, a separate process will be used for the extension in incognito + // mode. + bool incognito_split_mode_; + // Runtime data: // True if the background page is ready. diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 2e9085b..a6b294b 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -20,6 +20,7 @@ const char* kDescription = "description"; const char* kDevToolsPage = "devtools_page"; const char* kExcludeGlobs = "exclude_globs"; const char* kIcons = "icons"; +const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; const char* kJs = "js"; const char* kLaunch = "app.launch"; @@ -64,6 +65,8 @@ const char* kWebURLs = "app.urls"; } // namespace extension_manifest_keys namespace extension_manifest_values { +const char* kIncognitoSplit = "split"; +const char* kIncognitoSpanning = "spanning"; const char* kRunAtDocumentStart = "document_start"; const char* kRunAtDocumentEnd = "document_end"; const char* kRunAtDocumentIdle = "document_idle"; @@ -125,6 +128,8 @@ const char* kInvalidIconPath = "Invalid value for 'icons[\"*\"]'."; const char* kInvalidIcons = "Invalid value for 'icons'."; +const char* kInvalidIncognitoBehavior = + "Invalid value for 'incognito'."; const char* kInvalidJs = "Invalid value for 'content_scripts[*].js[*]'."; const char* kInvalidJsList = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index 54713f7..b1975d1 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -25,6 +25,7 @@ namespace extension_manifest_keys { extern const char* kDevToolsPage; extern const char* kExcludeGlobs; extern const char* kIcons; + extern const char* kIncognito; extern const char* kIncludeGlobs; extern const char* kJs; extern const char* kLaunch; @@ -71,6 +72,8 @@ namespace extension_manifest_keys { // Some values expected in manifests. namespace extension_manifest_values { + extern const char* kIncognitoSplit; + extern const char* kIncognitoSpanning; extern const char* kLaunchContainerPanel; extern const char* kLaunchContainerTab; extern const char* kLaunchContainerWindow; @@ -107,6 +110,7 @@ namespace extension_manifest_errors { extern const char* kInvalidGlobList; extern const char* kInvalidIconPath; extern const char* kInvalidIcons; + extern const char* kInvalidIncognitoBehavior; extern const char* kInvalidJs; extern const char* kInvalidJsList; extern const char* kInvalidKey; diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index be0e220..1becc31 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -750,10 +750,11 @@ IPC_BEGIN_MESSAGES(View) std::vector<URLPattern> /* URLPatterns the extension can access */) // Tell the renderer process that the given extension is enabled or disabled - // for incognito mode. - IPC_MESSAGE_CONTROL2(ViewMsg_Extension_ExtensionSetIncognitoEnabled, + // for incognito mode, and what kind of incognito behavior it has. + IPC_MESSAGE_CONTROL3(ViewMsg_Extension_ExtensionSetIncognitoEnabled, std::string /* extension_id */, - bool /* enabled */) + bool /* enabled */, + bool /* incognito_split_mode */) // Tell the renderer process all known page action ids for a particular // extension. diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 1de179e..54edbb4 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -144,9 +144,9 @@ static bool HasSufficientPermissions(ContextInfo* context, const GURL& event_url) { v8::Context::Scope context_scope(context->context); - bool incognito_permissions_ok = (!requires_incognito_access || - ExtensionProcessBindings::HasIncognitoEnabled(context->extension_id)); - if (!incognito_permissions_ok) + bool cross_profile_ok = (!requires_incognito_access || + ExtensionProcessBindings::AllowCrossProfile(context->extension_id)); + if (!cross_profile_ok) return false; // During unit tests, we might be invoked without a v8 context. In these diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 38d2919..340502f 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -63,8 +63,9 @@ typedef std::vector<std::string> PermissionsList; // A map of extension ID to permissions map. typedef std::map<std::string, PermissionsList> ExtensionPermissionsList; -// A map of extension ID to whether this extension was enabled in incognito. -typedef std::map<std::string, bool> IncognitoEnabledMap; +// A map of extension ID to whether this extension can access data from other +// profiles. +typedef std::map<std::string, bool> CrossProfileAccessMap; const char kExtensionName[] = "chrome/ExtensionProcessBindings"; const char* kExtensionDeps[] = { @@ -79,7 +80,7 @@ struct SingletonData { std::set<std::string> function_names_; PageActionIdMap page_action_ids_; ExtensionPermissionsList permissions_; - IncognitoEnabledMap incognito_enabled_map_; + CrossProfileAccessMap cross_profile_access_map_; }; static std::set<std::string>* GetFunctionNameSet() { @@ -94,8 +95,8 @@ static PermissionsList* GetPermissionsList(const std::string& extension_id) { return &Singleton<SingletonData>()->permissions_[extension_id]; } -static IncognitoEnabledMap* GetIncognitoEnabledMap() { - return &Singleton<SingletonData>()->incognito_enabled_map_; +static CrossProfileAccessMap* GetCrossProfileAccessMap() { + return &Singleton<SingletonData>()->cross_profile_access_map_; } static void GetActiveExtensionIDs(std::set<std::string>* extension_ids) { @@ -249,6 +250,8 @@ class ExtensionImpl : public ExtensionBase { return v8::FunctionTemplate::New(SetIconCommon); } else if (name->Equals(v8::String::New("IsExtensionProcess"))) { return v8::FunctionTemplate::New(IsExtensionProcess); + } else if (name->Equals(v8::String::New("IsIncognitoProcess"))) { + return v8::FunctionTemplate::New(IsIncognitoProcess); } return ExtensionBase::GetNativeFunction(name); @@ -547,6 +550,13 @@ class ExtensionImpl : public ExtensionBase { retval = EventBindings::GetRenderThread()->IsExtensionProcess(); return v8::Boolean::New(retval); } + + static v8::Handle<v8::Value> IsIncognitoProcess(const v8::Arguments& args) { + bool retval = false; + if (EventBindings::GetRenderThread()) + retval = EventBindings::GetRenderThread()->IsIncognitoProcess(); + return v8::Boolean::New(retval); + } }; } // namespace @@ -567,14 +577,18 @@ void ExtensionProcessBindings::SetFunctionNames( } void ExtensionProcessBindings::SetIncognitoEnabled( - const std::string& extension_id, bool enabled) { - (*GetIncognitoEnabledMap())[extension_id] = enabled; + const std::string& extension_id, bool enabled, bool incognito_split_mode) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + (*GetCrossProfileAccessMap())[extension_id] = + enabled && !incognito_split_mode; } // static -bool ExtensionProcessBindings::HasIncognitoEnabled( +bool ExtensionProcessBindings::AllowCrossProfile( const std::string& extension_id) { - return (!extension_id.empty() && (*GetIncognitoEnabledMap())[extension_id]); + return (!extension_id.empty() && (*GetCrossProfileAccessMap())[extension_id]); } // static diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h index d64b4255..8202c46 100644 --- a/chrome/renderer/extensions/extension_process_bindings.h +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -49,10 +49,12 @@ class ExtensionProcessBindings { // Sets whether incognito is enabled for a particular extension. static void SetIncognitoEnabled(const std::string& extension_id, - bool enabled); + bool enabled, + bool incognito_split_mode); - // Checks whether incognito is enabled for a particular extension. - static bool HasIncognitoEnabled(const std::string& extension_id); + // Checks whether the given extension can see events/data from another + // profile (normal to incognito or vice versa). + static bool AllowCrossProfile(const std::string& extension_id); // Check if the extension in the currently running context has permission to // access the given extension function. Must be called with a valid V8 diff --git a/chrome/renderer/mock_render_thread.h b/chrome/renderer/mock_render_thread.h index 54c0c44..5305c86 100644 --- a/chrome/renderer/mock_render_thread.h +++ b/chrome/renderer/mock_render_thread.h @@ -54,6 +54,7 @@ class MockRenderThread : public RenderThreadBase { virtual void WidgetRestored() { } virtual bool IsExtensionProcess() const { return is_extension_process_; } + virtual bool IsIncognitoProcess() const { return false; } void SetExtensionProcess(bool value) { is_extension_process_ = value; } ////////////////////////////////////////////////////////////////////////// diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index a3f0120..a969916 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -548,8 +548,9 @@ void RenderThread::OnExtensionSetHostPermissions( } void RenderThread::OnExtensionSetIncognitoEnabled( - const std::string& extension_id, bool enabled) { - ExtensionProcessBindings::SetIncognitoEnabled(extension_id, enabled); + const std::string& extension_id, bool enabled, bool incognito_split_mode) { + ExtensionProcessBindings::SetIncognitoEnabled(extension_id, enabled, + incognito_split_mode); } void RenderThread::OnDOMStorageEvent( diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index b90f883..8795fb7 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -109,6 +109,9 @@ class RenderThreadBase { // True if this process should be treated as an extension process. virtual bool IsExtensionProcess() const = 0; + + // True if this process is running in an incognito profile. + virtual bool IsIncognitoProcess() const = 0; }; // The RenderThread class represents a background thread where RenderView @@ -149,6 +152,8 @@ class RenderThread : public RenderThreadBase, virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter); virtual void WidgetHidden(); virtual void WidgetRestored(); + virtual bool IsExtensionProcess() const { return is_extension_process_; } + virtual bool IsIncognitoProcess() const { return is_incognito_process_; } // These methods modify how the next message is sent. Normally, when sending // a synchronous message that runs a nested message loop, we need to suspend @@ -180,10 +185,6 @@ class RenderThread : public RenderThreadBase, bool plugin_refresh_allowed() const { return plugin_refresh_allowed_; } - virtual bool IsExtensionProcess() const { return is_extension_process_; } - - bool is_incognito_process() const { return is_incognito_process_; } - // Do DNS prefetch resolution of a hostname. void Resolve(const char* name, size_t length); @@ -255,7 +256,8 @@ class RenderThread : public RenderThreadBase, const std::vector<URLPattern>& permissions); void OnExtensionSetIncognitoEnabled( const std::string& extension_id, - bool enabled); + bool enabled, + bool incognito_split_mode); void OnSetNextPageID(int32 next_page_id); void OnSetIsIncognitoProcess(bool is_incognito_process); void OnSetCSSColors(const std::vector<CSSColors::CSSColorMapping>& colors); diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index f5856a7..0c40e08 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -19,6 +19,7 @@ var chrome = chrome || {}; native function GetPopupView(); native function SetIconCommon(); native function IsExtensionProcess(); + native function IsIncognitoProcess(); var chromeHidden = GetChromeHidden(); @@ -302,7 +303,7 @@ var chrome = chrome || {}; if (!extensionId) { return; } - chrome.initExtension(extensionId, false); + chrome.initExtension(extensionId, false, IsIncognitoProcess()); // |apiFunctions| is a hash of name -> object that stores the // name & definition of the apiFunction. Custom handling of api functions diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js index 3d6228e..5a2c731 100644 --- a/chrome/renderer/resources/renderer_extension_bindings.js +++ b/chrome/renderer/resources/renderer_extension_bindings.js @@ -143,14 +143,15 @@ var chrome = chrome || {}; // This function is called on context initialization for both content scripts // and extension contexts. chrome.initExtension = function(extensionId, warnOnPrivilegedApiAccess, - inIncognitoTab) { + inIncognitoContext) { delete chrome.initExtension; chromeHidden.extensionId = extensionId; chrome.extension = chrome.extension || {}; chrome.self = chrome.extension; - chrome.extension.inIncognitoTab = inIncognitoTab; + chrome.extension.inIncognitoTab = inIncognitoContext; // deprecated + chrome.extension.inIncognitoContext = inIncognitoContext; // Events for when a message channel is opened to our extension. chrome.extension.onConnect = new chrome.Event(); diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc index 7a2f521..bbaf643 100644 --- a/chrome/renderer/user_script_slave.cc +++ b/chrome/renderer/user_script_slave.cc @@ -76,7 +76,7 @@ void UserScriptSlave::GetActiveExtensions(std::set<std::string>* extension_ids) bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { scripts_.clear(); - bool only_inject_incognito = RenderThread::current()->is_incognito_process(); + bool only_inject_incognito = RenderThread::current()->IsIncognitoProcess(); // Create the shared memory object (read only). shared_memory_.reset(new base::SharedMemory(shared_memory, true)); @@ -176,7 +176,7 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { void UserScriptSlave::InsertInitExtensionCode( std::vector<WebScriptSource>* sources, const std::string& extension_id) { DCHECK(sources); - bool incognito = RenderThread::current()->is_incognito_process(); + bool incognito = RenderThread::current()->IsIncognitoProcess(); sources->insert(sources->begin(), WebScriptSource(WebString::fromUTF8( StringPrintf(kInitExtension, extension_id.c_str(), incognito ? "true" : "false")))); diff --git a/chrome/test/data/extensions/api_test/incognito/apis/background.html b/chrome/test/data/extensions/api_test/incognito/apis/background.html index 606f6b5..34d2f25 100644 --- a/chrome/test/data/extensions/api_test/incognito/apis/background.html +++ b/chrome/test/data/extensions/api_test/incognito/apis/background.html @@ -76,8 +76,7 @@ chrome.test.runTests([ // Tests content script injection to verify that the script can tell its // in incongnito. function contentScriptTestIncognito() { - // The property is undefined outside of content scripts. - assertEq(undefined, chrome.extension.inIncognitoTab); + assertTrue(!chrome.extension.inIncognitoContext); var testUrl = "http://localhost:1337/files/extensions/test_file.html"; @@ -85,7 +84,7 @@ chrome.test.runTests([ chrome.tabs.create({windowId: incognitoWindow.id, url: testUrl}, pass(function(tab) { chrome.tabs.executeScript(tab.id, - {code: 'document.title = chrome.extension.inIncognitoTab'}, + {code: 'document.title = chrome.extension.inIncognitoContext'}, pass(function() { assertEq(undefined, chrome.extension.lastError); chrome.tabs.get(tab.id, pass(function(tab) { @@ -98,7 +97,7 @@ chrome.test.runTests([ chrome.tabs.create({windowId: normalWindow.id, url: testUrl}, pass(function(tab) { chrome.tabs.executeScript(tab.id, - {code: 'document.title = chrome.extension.inIncognitoTab'}, + {code: 'document.title = chrome.extension.inIncognitoContext'}, pass(function() { assertEq(undefined, chrome.extension.lastError); chrome.tabs.get(tab.id, pass(function(tab) { diff --git a/chrome/test/data/extensions/api_test/incognito/split/background.html b/chrome/test/data/extensions/api_test/incognito/split/background.html new file mode 100644 index 0000000..32fec55 --- /dev/null +++ b/chrome/test/data/extensions/api_test/incognito/split/background.html @@ -0,0 +1,89 @@ +<script> +var pass = chrome.test.callbackPass; +var assertEq = chrome.test.assertEq; +var assertTrue = chrome.test.assertTrue; + +var win, tab; +var inIncognitoContext = chrome.extension.inIncognitoContext; + +// Listen to some events to make sure we don't get events from the other +// profile. + +chrome.tabs.onUpdated.addListener(function(id, info, tab) { + if (inIncognitoContext != tab.incognito) { + chrome.test.notifyFail( + "[FAIL] Split-mode incognito test received an event for " + + (tab.incognito ? "an incognito" : "a normal") + + " tab in the wrong profile."); + } +}); + +chrome.extension.onRequest.addListener( + function(request, sender, sendResponse) { + if (inIncognitoContext != sender.tab.incognito) { + chrome.test.notifyFail( + "[FAIL] Split-mode incognito test received a message from " + + (sender.tab.incognito ? "an incognito" : "a normal") + + " tab in the wrong profile."); + } +}); + +chrome.test.runTests([ + function setupWindows() { + // The test harness should have set us up with 2 windows: 1 incognito + // and 1 regular. Since we are in split mode, we should only see the + // window for our profile. + chrome.windows.getAll({populate: true}, pass(function(windows) { + assertEq(1, windows.length); + + win = windows[0]; + tab = win.tabs[0]; + assertEq(inIncognitoContext, win.incognito); + })); + }, + + // Tests that we can update an incognito tab and get the event for it. + function tabUpdate() { + var newUrl = "about:blank"; + + // Prepare the event listeners first. + var done = chrome.test.listenForever(chrome.tabs.onUpdated, + function(id, info, tab) { + assertEq(tab.id, id); + assertEq(inIncognitoContext, tab.incognito); + assertEq(newUrl, tab.url); + if (info.status == "complete") + done(); + }); + + // Update our tab. + chrome.tabs.update(tab.id, {"url": newUrl}, pass()); + }, + + // Tests content script injection to verify that the script can tell its + // in incongnito. + function contentScriptTestIncognito() { + var testUrl = "http://localhost:1337/files/extensions/test_file.html"; + + // Test that chrome.extension.inIncognitoContext is true for incognito + // tabs. + chrome.tabs.create({windowId: win.id, url: testUrl}, + pass(function(tab) { + chrome.tabs.executeScript(tab.id, + {code: 'chrome.extension.sendRequest({' + + ' inIncognitoContext: chrome.extension.inIncognitoContext' + + '});'}, + pass(function() { + assertEq(undefined, chrome.extension.lastError); + })); + })); + + var done = chrome.test.listenForever(chrome.extension.onRequest, + function(request, sender, sendResponse) { + assertEq(inIncognitoContext, request.inIncognitoContext); + sendResponse(); + done(); + }); + } +]); +</script> diff --git a/chrome/test/data/extensions/api_test/incognito/split/manifest.json b/chrome/test/data/extensions/api_test/incognito/split/manifest.json new file mode 100644 index 0000000..b3a9206 --- /dev/null +++ b/chrome/test/data/extensions/api_test/incognito/split/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "incognito split-mode apitest", + "version": "0.1", + "description": "test that an incognito extension in split mode behaves properly", + "background_page": "background.html", + "incognito": "split", + "permissions": ["tabs", "http://*/*"] +} diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json index 3232930..f9d97ff 100644 --- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json +++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json @@ -25,12 +25,14 @@ { "path": "bar.html", "renderProcessId": 42, - "renderViewId": 88 + "renderViewId": 88, + "incognito": false }, { "path": "dog.html", "renderProcessId": 0, - "renderViewId": 0 + "renderViewId": 0, + "incognito": false } ], "hasPopupAction": false, diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json index 7c3c36f..8751022 100644 --- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json +++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json @@ -15,12 +15,14 @@ { "path": "bar.html", "renderProcessId": 42, - "renderViewId": 88 + "renderViewId": 88, + "incognito": false }, { "path": "bar.html", "renderProcessId": 0, - "renderViewId": 0 + "renderViewId": 0, + "incognito": false } ], "hasPopupAction": false, |