diff options
34 files changed, 376 insertions, 103 deletions
diff --git a/chrome/browser/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/cocoa/extensions/browser_actions_controller.mm index cc73d61..c3cd111 100644 --- a/chrome/browser/cocoa/extensions/browser_actions_controller.mm +++ b/chrome/browser/cocoa/extensions/browser_actions_controller.mm @@ -30,6 +30,7 @@ NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged"; - (void)removeActionButtonForExtension:(Extension*)extension; - (void)repositionActionButtons; - (int)currentTabId; +- (bool)shouldDisplayBrowserAction:(Extension*)extension; @end // A helper class to proxy extension notifications to the view controller's @@ -139,6 +140,9 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, if (!extension->browser_action()) return; + if (![self shouldDisplayBrowserAction:extension]) + return; + // Show the container if it's the first button. Otherwise it will be shown // already. if ([buttons_ count] == 0) @@ -266,4 +270,10 @@ class ExtensionsServiceObserverBridge : public NotificationObserver, return [buttonOrder_ objectAtIndex:(NSUInteger)index]; } +- (bool)shouldDisplayBrowserAction:(Extension*)extension { + return (!profile_->IsOffTheRecord() || + profile_->GetExtensionsService()-> + IsIncognitoEnabled(extension->id())); +} + @end diff --git a/chrome/browser/extensions/browser_action_apitest.cc b/chrome/browser/extensions/browser_action_apitest.cc index eafa655..a8cd84c 100644 --- a/chrome/browser/extensions/browser_action_apitest.cc +++ b/chrome/browser/extensions/browser_action_apitest.cc @@ -19,7 +19,9 @@ #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_action.h" +#include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" static const int kTimeoutMs = 60 * 1000; // 1 minute @@ -44,6 +46,9 @@ class BrowserActionApiTest : public ExtensionApiTest { }; IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + StartHTTPServer(); ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; Extension* extension = GetSingleLoadedExtension(); @@ -143,6 +148,9 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TabSpecificBrowserActionState) { } IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( "browser_action/popup"))); Extension* extension = GetSingleLoadedExtension(); @@ -176,6 +184,9 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { // Test that calling chrome.browserAction.setPopup() can enable and change // a popup. IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_; Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; @@ -261,3 +272,114 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { << "Browser action popup default should not be changed by setting " << "a specific tab id."; } + +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + StartHTTPServer(); + + ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; + Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + // Test that there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); + + // Open an incognito window and test that the browser action isn't there by + // default. + Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); + Browser* incognito_browser = Browser::Create(incognito_profile); + + ASSERT_EQ(0, + BrowserActionTestUtil(incognito_browser).NumberOfBrowserActions()); + + // Now enable the extension in incognito mode, and test that the browser + // action shows up. Note that we don't update the existing window at the + // moment, so we just create a new one. + browser()->profile()->GetExtensionsService()->extension_prefs()-> + SetIsIncognitoEnabled(extension->id(), true); + + incognito_browser->CloseWindow(); + incognito_browser = Browser::Create(incognito_profile); + ASSERT_EQ(1, + BrowserActionTestUtil(incognito_browser).NumberOfBrowserActions()); + + // TODO(mpcomplete): simulate a click and have it do the right thing in + // incognito. +} + +// TODO(mpcomplete): enable this when Mac gets dragging support. +#if defined(OS_MACOSX) +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_IncognitoDragging) { +#else +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoDragging) { +#endif + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + // The tooltips for each respective browser action. + const char kTooltipA[] = "Make this page red"; + const char kTooltipB[] = "grow"; + const char kTooltipC[] = "Test setPopup()"; + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( + "browser_action/basics"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( + "browser_action/popup"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( + "browser_action/add_popup"))); + + // Test that there are 3 browser actions in the toolbar. + ASSERT_EQ(3u, service->extensions()->size()); + ASSERT_EQ(3, GetBrowserActionsBar().NumberOfBrowserActions()); + + // Now enable 2 of the extensions in incognito mode, and test that the browser + // actions show up. + service->extension_prefs()->SetIsIncognitoEnabled( + service->extensions()->at(0)->id(), true); + service->extension_prefs()->SetIsIncognitoEnabled( + service->extensions()->at(2)->id(), true); + + Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); + Browser* incognito_browser = Browser::Create(incognito_profile); + BrowserActionTestUtil incognito_bar(incognito_browser); + + // Navigate just to have a tab in this window, otherwise wonky things happen. + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), + GURL(chrome::kChromeUIExtensionsURL)); + + ASSERT_EQ(2, incognito_bar.NumberOfBrowserActions()); + + // Ensure that the browser actions are in the right order (ABC). + EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(0)); + EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(1)); + EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(2)); + + EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(0)); + EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(1)); + + // Now rearrange them and ensure that they are rearranged correctly in both + // regular and incognito mode. + + // ABC -> CAB + service->toolbar_model()->MoveBrowserAction(service->extensions()->at(2), 0); + + EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(0)); + EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(1)); + EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(2)); + + EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(0)); + EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(1)); + + // CAB -> CBA + service->toolbar_model()->MoveBrowserAction(service->extensions()->at(1), 1); + + EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(0)); + EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(1)); + EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(2)); + + EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(0)); + EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(1)); +} diff --git a/chrome/browser/extensions/browser_action_test_util_views.cc b/chrome/browser/extensions/browser_action_test_util_views.cc index 9af266d..c116f22 100644 --- a/chrome/browser/extensions/browser_action_test_util_views.cc +++ b/chrome/browser/extensions/browser_action_test_util_views.cc @@ -41,7 +41,7 @@ void BrowserActionTestUtil::Press(int index) { std::string BrowserActionTestUtil::GetTooltip(int index) { std::wstring text; - GetContainer(browser_)->GetBrowserActionViewAt(0)->button()-> + GetContainer(browser_)->GetBrowserActionViewAt(index)->button()-> GetTooltipText(0, 0, &text); return WideToUTF8(text); } diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 6d92ad2..3aeda24 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -49,6 +49,10 @@ const wchar_t kExtensionToolbar[] = L"extensions.toolbar"; // server's perspective) an extension last included a "ping" parameter during // its update check. const wchar_t kLastPingDay[] = L"lastpingday"; + +// A preference that, if true, will allow this extension to run in incognito +// mode. +const wchar_t kPrefIncognitoEnabled[] = L"incognito"; } //////////////////////////////////////////////////////////////////////////////// @@ -257,6 +261,16 @@ void ExtensionPrefs::SetLastPingDay(const std::string& extension_id, prefs_->ScheduleSavePersistentPrefs(); } +bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) { + return ReadExtensionPrefBoolean(extension_id, kPrefIncognitoEnabled); +} + +void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id, + bool enabled) { + UpdateExtensionPref(extension_id, kPrefIncognitoEnabled, + Value::CreateBooleanValue(enabled)); + prefs_->SavePersistentPrefs(); +} void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) { const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref); diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index d8d3694..77f64901 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -94,6 +94,10 @@ class ExtensionPrefs { // the client's. void SetLastPingDay(const std::string& extension_id, const base::Time& time); + // Returns true if the user enabled this extension to be loaded in incognito + // mode. + bool IsIncognitoEnabled(const std::string& extension_id); + void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled); // Saves ExtensionInfo for each installed extension with the path to the // version directory and the location. Blacklisted extensions won't be saved diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index 4c9bd39..f243949 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc @@ -187,3 +187,28 @@ void ExtensionToolbarModel::UpdatePrefs() { ids.push_back((*iter)->id()); service_->extension_prefs()->SetToolbarOrder(ids); } + +int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) { + int original_index = 0, i = 0; + for (ExtensionList::iterator iter = begin(); iter != end(); + ++iter, ++original_index) { + if (service_->IsIncognitoEnabled((*iter)->id())) { + if (incognito_index == i) + break; + ++i; + } + } + return original_index; +} + +int ExtensionToolbarModel::OriginalIndexToIncognito(int original_index) { + int incognito_index = 0, i = 0; + for (ExtensionList::iterator iter = begin(); iter != end(); + ++iter, ++i) { + if (original_index == i) + break; + if (service_->IsIncognitoEnabled((*iter)->id())) + ++incognito_index; + } + return incognito_index; +} diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index 1718050..a4c7855 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h @@ -50,6 +50,11 @@ class ExtensionToolbarModel : public NotificationObserver { return toolitems_.end(); } + // Utility functions for converting between an index into the list of + // incognito-enabled browser actions, and the list of all browser actions. + int IncognitoIndexToOriginal(int incognito_index); + int OriginalIndexToIncognito(int original_index); + private: // NotificationObserver implementation. virtual void Observe(NotificationType type, diff --git a/chrome/browser/extensions/extension_toolbar_model_unittest.cc b/chrome/browser/extensions/extension_toolbar_model_unittest.cc index 79b9efb..031672e 100644 --- a/chrome/browser/extensions/extension_toolbar_model_unittest.cc +++ b/chrome/browser/extensions/extension_toolbar_model_unittest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/extension_toolbar_model.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/common/chrome_switches.h" #include "chrome/test/in_process_browser_test.h" // An InProcessBrowserTest for testing the ExtensionToolbarModel. @@ -65,6 +66,9 @@ class ExtensionToolbarModelTest : public ExtensionBrowserTest, }; IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + // Load an extension with no browser action. ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") .AppendASCII("browser_action") @@ -102,6 +106,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { } IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + // Load an extension with a browser action. FilePath extension_a_path(test_data_dir_.AppendASCII("api_test") .AppendASCII("browser_action") diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 7dddfa9..476f640 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -529,6 +529,16 @@ base::Time ExtensionsService::LastPingDay(const std::string& extension_id) { return extension_prefs_->LastPingDay(extension_id); } +bool ExtensionsService::IsIncognitoEnabled(const std::string& extension_id) { + Extension* extension = GetExtensionById(extension_id, true); + if (!extension) + return false; + + return extension_prefs_->IsIncognitoEnabled(extension_id) && + extension->HasApiPermission(Extension::kExperimentalPermission) && + extension->HasApiPermission(Extension::kIncognitoPermission); +} + void ExtensionsService::CheckForExternalUpdates() { // This installs or updates externally provided extensions. // TODO(aa): Why pass this list into the provider, why not just filter it diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 5b4d37e..078ac01 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -106,6 +106,10 @@ class ExtensionsService const base::Time& time); virtual base::Time LastPingDay(const std::string& extension_id); + // Returns true if this extension can run in an incognito window. The + // decision is based on both user consent and the extension having the right + // permission. + bool IsIncognitoEnabled(const std::string& extension_id); const FilePath& install_directory() const { return install_directory_; } diff --git a/chrome/browser/extensions/incognito_noscript_apitest.cc b/chrome/browser/extensions/incognito_noscript_apitest.cc index fa78e67..1a63d88 100755 --- a/chrome/browser/extensions/incognito_noscript_apitest.cc +++ b/chrome/browser/extensions/incognito_noscript_apitest.cc @@ -6,8 +6,11 @@ #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" #include "net/base/mock_host_resolver.h" @@ -18,28 +21,50 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, IncognitoNoScript) { // Loads a simple extension which attempts to change the title of every page // that loads to "modified". + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); FilePath extension_path = test_data_dir_.AppendASCII("api_test") .AppendASCII("incognito_no_script"); ASSERT_TRUE(LoadExtension(extension_path)); // Open incognito window and navigate to test page. - Browser::OpenURLOffTheRecord(browser()->profile(), - GURL("http://www.foo.com:1337/files/extensions/test_file.html")); - Profile* off_the_record_profile = - browser()->profile()->GetOffTheRecordProfile(); - Browser* otr_browser = Browser::Create(off_the_record_profile); - otr_browser->AddTabWithURL( - GURL("http://www.foo.com:1337/files/extensions/test_file.html"), - GURL(), - PageTransition::LINK, - true, - -1, - false, - NULL); - otr_browser->window()->Show(); - ui_test_utils::WaitForNavigationInCurrentTab(otr_browser); + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), + GURL("http://www.example.com:1337/files/extensions/test_file.html")); + Browser* otr_browser = BrowserList::FindBrowserWithType( + browser()->profile()->GetOffTheRecordProfile(), Browser::TYPE_NORMAL); string16 title; ui_test_utils::GetCurrentTabTitle(otr_browser, &title); ASSERT_EQ("Unmodified", UTF16ToASCII(title)); } + +IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, IncognitoYesScript) { + host_resolver()->AddRule("*", "127.0.0.1"); + StartHTTPServer(); + + // Loads a simple extension which attempts to change the title of every page + // that loads to "modified". + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + FilePath extension_path = test_data_dir_.AppendASCII("api_test") + .AppendASCII("incognito_no_script"); + ASSERT_TRUE(LoadExtension(extension_path)); + + // Now enable the extension in incognito mode, and ensure that page titles + // are modified. + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + service->extension_prefs()->SetIsIncognitoEnabled( + service->extensions()->at(0)->id(), true); + browser()->profile()->GetUserScriptMaster()->ReloadExtensionForTesting( + service->extensions()->at(0)); + + // 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")); + Browser* otr_browser = BrowserList::FindBrowserWithType( + browser()->profile()->GetOffTheRecordProfile(), Browser::TYPE_NORMAL); + + string16 title; + ui_test_utils::GetCurrentTabTitle(otr_browser, &title); + ASSERT_EQ("modified", UTF16ToASCII(title)); +} diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 3478b68..a7d69f6 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -16,6 +16,7 @@ #include "base/thread.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" @@ -333,17 +334,15 @@ void UserScriptMaster::Observe(NotificationType type, StartScan(); break; case NotificationType::EXTENSION_LOADED: { - // TODO(aa): Fix race here. A page could need a content script on startup, - // before the extension has loaded. We need to freeze the renderer in - // that case. - // See: http://code.google.com/p/chromium/issues/detail?id=11547. - // Add any content scripts inside the extension. Extension* extension = Details<Extension>(details).ptr(); + bool incognito_enabled = profile_->GetExtensionsService()-> + IsIncognitoEnabled(extension->id()); const UserScriptList& scripts = extension->content_scripts(); for (UserScriptList::const_iterator iter = scripts.begin(); iter != scripts.end(); ++iter) { lone_scripts_.push_back(*iter); + lone_scripts_.back().set_incognito_enabled(incognito_enabled); } if (extensions_service_ready_) StartScan(); @@ -379,3 +378,14 @@ void UserScriptMaster::StartScan() { script_reloader_->StartScan(user_script_dir_, lone_scripts_); } + +void UserScriptMaster::ReloadExtensionForTesting(Extension* extension) { + bool incognito_enabled = profile_->GetExtensionsService()-> + IsIncognitoEnabled(extension->id()); + for (UserScriptList::iterator iter = lone_scripts_.begin(); + iter != lone_scripts_.end(); ++iter) { + if (iter->extension_id() == extension->id()) + (*iter).set_incognito_enabled(incognito_enabled); + } + StartScan(); +} diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h index 0423305..19562f0 100644 --- a/chrome/browser/extensions/user_script_master.h +++ b/chrome/browser/extensions/user_script_master.h @@ -17,6 +17,7 @@ namespace base { class StringPiece; } +class Extension; class Profile; // Manages a segment of shared memory that contains the user scripts the user @@ -46,6 +47,11 @@ class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>, // Returns the path to the directory user scripts are stored in. FilePath user_script_dir() const { return user_script_dir_; } + // Note: this is only for testing. This will reload the scripts associated + // with the given extension. This is only temporary until we get better + // machinery in place for toggling incognito-enabled extensions. + void ReloadExtensionForTesting(Extension* extension); + protected: friend class base::RefCountedThreadSafe<UserScriptMaster>; diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index ef115be..c1921cd 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -332,6 +332,12 @@ void BrowserActionsToolbarGtk::CreateAllButtons() { void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension, int index) { + if (!ShouldDisplayBrowserAction(extension)) + return; + + if (profile_->IsOffTheRecord()) + index = model_->OriginalIndexToIncognito(index); + RemoveButtonForExtension(extension); linked_ptr<BrowserActionButton> button( new BrowserActionButton(this, extension)); @@ -374,6 +380,14 @@ void BrowserActionsToolbarGtk::UpdateVisibility() { gtk_widget_show(widget()); } +bool BrowserActionsToolbarGtk::ShouldDisplayBrowserAction( + Extension* extension) { + // Only display incognito-enabled extensions while in incognito mode. + return (!profile_->IsOffTheRecord() || + profile_->GetExtensionsService()-> + IsIncognitoEnabled(extension->id())); +} + void BrowserActionsToolbarGtk::HidePopup() { ExtensionPopupGtk* popup = ExtensionPopupGtk::get_current_extension_popup(); if (popup) @@ -402,10 +416,14 @@ void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension, BrowserActionButton* button = extension_button_map_[extension->id()].get(); if (!button) { - NOTREACHED(); + if (ShouldDisplayBrowserAction(extension)) + NOTREACHED(); return; } + if (profile_->IsOffTheRecord()) + index = model_->OriginalIndexToIncognito(index); + gtk_box_reorder_child(GTK_BOX(hbox_.get()), button->widget(), index); } @@ -428,6 +446,9 @@ gboolean BrowserActionsToolbarGtk::OnDragMotion(GtkWidget* widget, return FALSE; drop_index_ = x < kButtonSize ? 0 : x / (kButtonSize + kButtonPadding); + if (profile_->IsOffTheRecord()) + drop_index_ = model_->IncognitoIndexToOriginal(drop_index_); + // We will go ahead and reorder the child in order to provide visual feedback // to the user. We don't inform the model that it has moved until the drag // ends. diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h index 39daf78..1356320 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h @@ -69,6 +69,11 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { // Hide the extension popup, if any. void HidePopup(); + // Returns true if this extension should be shown in this toolbar. This can + // return false if we are in an incognito window and the extension is disabled + // for incognito. + bool ShouldDisplayBrowserAction(Extension* extension); + // ExtensionToolbarModel::Observer implementation. virtual void BrowserActionAdded(Extension* extension, int index); virtual void BrowserActionRemoved(Extension* extension); diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index e206121..3faa8d3 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -283,32 +283,6 @@ ChromeURLRequestContext* FactoryForOffTheRecord::Create() { return context; } -// Factory that creates the ChromeURLRequestContext for extensions in incognito -// mode. -class FactoryForOffTheRecordExtensions - : public ChromeURLRequestContextFactory { - public: - explicit FactoryForOffTheRecordExtensions(Profile* profile) - : ChromeURLRequestContextFactory(profile) {} - - virtual ChromeURLRequestContext* Create(); -}; - -ChromeURLRequestContext* FactoryForOffTheRecordExtensions::Create() { - ChromeURLRequestContext* context = new ChromeURLRequestContext; - ApplyProfileParametersToContext(context); - - net::CookieMonster* cookie_monster = new net::CookieMonster(NULL); - - // Enable cookies for extension URLs only. - const char* schemes[] = {chrome::kExtensionScheme}; - cookie_monster->SetCookieableSchemes(schemes, 1); - context->set_cookie_store(cookie_monster); - // No dynamic cookie policy for extensions. - - return context; -} - // Factory that creates the ChromeURLRequestContext for media. class FactoryForMedia : public ChromeURLRequestContextFactory { public: @@ -501,16 +475,6 @@ ChromeURLRequestContextGetter::CreateOffTheRecord(Profile* profile) { profile, new FactoryForOffTheRecord(profile)); } -// static -ChromeURLRequestContextGetter* -ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions( - Profile* profile) { - DCHECK(profile->IsOffTheRecord()); - return new ChromeURLRequestContextGetter( - profile, - new FactoryForOffTheRecordExtensions(profile)); -} - void ChromeURLRequestContextGetter::CleanupOnUIThread() { CheckCurrentlyOnMainThread(); diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index 99aed21..df9b64a 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -280,10 +280,6 @@ class ChromeURLRequestContextGetter : public URLRequestContextGetter, // called on the UI thread. static ChromeURLRequestContextGetter* CreateOffTheRecord(Profile* profile); - // Create an instance of request context for OTR profile for extensions. - 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 a1594b2..a83ad4a 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -196,7 +196,6 @@ class OffTheRecordProfileImpl : public Profile, public: explicit OffTheRecordProfileImpl(Profile* real_profile) : profile_(real_profile), - extensions_request_context_(NULL), start_time_(Time::Now()) { request_context_ = ChromeURLRequestContextGetter::CreateOffTheRecord(this); @@ -209,7 +208,6 @@ class OffTheRecordProfileImpl : public Profile, virtual ~OffTheRecordProfileImpl() { CleanupRequestContext(request_context_); - CleanupRequestContext(extensions_request_context_); } virtual ProfileId GetRuntimeId() { @@ -248,23 +246,25 @@ class OffTheRecordProfileImpl : public Profile, } virtual ExtensionsService* GetExtensionsService() { - return NULL; + return GetOriginalProfile()->GetExtensionsService(); } virtual UserScriptMaster* GetUserScriptMaster() { - return NULL; + return GetOriginalProfile()->GetUserScriptMaster(); } virtual ExtensionDevToolsManager* GetExtensionDevToolsManager() { + // TODO(mpcomplete): figure out whether we should return the original + // profile's version. return NULL; } virtual ExtensionProcessManager* GetExtensionProcessManager() { - return NULL; + return GetOriginalProfile()->GetExtensionProcessManager(); } virtual ExtensionMessageService* GetExtensionMessageService() { - return NULL; + return GetOriginalProfile()->GetExtensionMessageService(); } virtual SSLHostState* GetSSLHostState() { @@ -389,12 +389,7 @@ class OffTheRecordProfileImpl : public Profile, } URLRequestContextGetter* GetRequestContextForExtensions() { - if (!extensions_request_context_) { - extensions_request_context_ = - ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions(this); - } - - return extensions_request_context_; + return GetOriginalProfile()->GetRequestContextForExtensions(); } virtual net::SSLConfigService* GetSSLConfigService() { @@ -527,8 +522,6 @@ class OffTheRecordProfileImpl : public Profile, // The context to use for requests made from this OTR session. scoped_refptr<ChromeURLRequestContextGetter> request_context_; - 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/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index f567b99..1822c07 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -622,7 +622,8 @@ void BrowserRenderProcessHost::SendUserScriptsUpdate( } if (base::SharedMemory::IsHandleValid(handle_for_process)) { - Send(new ViewMsg_UserScripts_UpdatedScripts(handle_for_process)); + Send(new ViewMsg_UserScripts_UpdatedScripts(handle_for_process, + profile()->IsOffTheRecord())); } } diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index d089a40..5a1ae90 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -450,6 +450,9 @@ void BrowserActionsContainer::CreateBrowserActionViews() { DCHECK(browser_action_views_.empty()); for (ExtensionList::iterator iter = model_->begin(); iter != model_->end(); ++iter) { + if (!ShouldDisplayBrowserAction(*iter)) + continue; + BrowserActionView* view = new BrowserActionView(*iter, this); browser_action_views_.push_back(view); AddChildView(view); @@ -673,6 +676,7 @@ void BrowserActionsContainer::ViewHierarchyChanged(bool is_add, bool BrowserActionsContainer::GetDropFormats( int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) { custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat()); + return true; } @@ -771,6 +775,9 @@ int BrowserActionsContainer::OnPerformDrop( if (i > data.index()) --i; + if (profile_->IsOffTheRecord()) + i = model_->IncognitoIndexToOriginal(i); + model_->MoveBrowserAction(dragging, i); OnDragExited(); // Perform clean up after dragging. @@ -933,6 +940,12 @@ void BrowserActionsContainer::BrowserActionAdded(Extension* extension, CloseMenus(); + if (!ShouldDisplayBrowserAction(extension)) + return; + + if (profile_->IsOffTheRecord()) + index = model_->OriginalIndexToIncognito(index); + // Before we change anything, determine the number of visible browser actions. size_t visible_actions = VisibleBrowserActions(); @@ -1011,6 +1024,12 @@ void BrowserActionsContainer::BrowserActionRemoved(Extension* extension) { void BrowserActionsContainer::BrowserActionMoved(Extension* extension, int index) { + if (!ShouldDisplayBrowserAction(extension)) + return; + + if (profile_->IsOffTheRecord()) + index = model_->OriginalIndexToIncognito(index); + DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size())); DeleteBrowserActionViews(); @@ -1104,3 +1123,10 @@ void BrowserActionsContainer::NotifyMenuDeleted( DCHECK(controller == overflow_menu_); overflow_menu_ = NULL; } + +bool BrowserActionsContainer::ShouldDisplayBrowserAction(Extension* extension) { + // Only display incognito-enabled extensions while in incognito mode. + return (!profile_->IsOffTheRecord() || + profile_->GetExtensionsService()-> + IsIncognitoEnabled(extension->id())); +} diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h index a7a5b59ac..1ace12d 100644 --- a/chrome/browser/views/browser_actions_container.h +++ b/chrome/browser/views/browser_actions_container.h @@ -399,7 +399,14 @@ class BrowserActionsContainer // all the padding that we normally show if there are icons. int ContainerMinSize() const; - // The vector of browser actions (icons/image buttons for each action). + // Returns true if this extension should be shown in this toolbar. This can + // return false if we are in an incognito window and the extension is disabled + // for incognito. + bool ShouldDisplayBrowserAction(Extension* extension); + + // The vector of browser actions (icons/image buttons for each action). Note + // that not every BrowserAction in the ToolbarModel will necessarily be in + // this collection. Some extensions may be disabled in incognito windows. BrowserActionViews browser_action_views_; NotificationRegistrar registrar_; diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index eef996e..c5e733b 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -117,12 +117,14 @@ const char* Extension::kTabPermission = "tabs"; const char* Extension::kBookmarkPermission = "bookmarks"; const char* Extension::kNotificationPermission = "notifications"; const char* Extension::kExperimentalPermission = "experimental"; +const char* Extension::kIncognitoPermission = "incognito"; const char* Extension::kPermissionNames[] = { Extension::kTabPermission, Extension::kBookmarkPermission, Extension::kNotificationPermission, - Extension::kExperimentalPermission + Extension::kExperimentalPermission, + Extension::kIncognitoPermission }; const size_t Extension::kNumPermissions = arraysize(Extension::kPermissionNames); diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 25093ff..1445943 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -79,6 +79,7 @@ class Extension { static const char* kBookmarkPermission; static const char* kNotificationPermission; static const char* kExperimentalPermission; + static const char* kIncognitoPermission; static const char* kPermissionNames[]; static const size_t kNumPermissions; diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index 7a48c14..5ed2040 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -75,17 +75,12 @@ void UserScript::File::Unpickle(const ::Pickle& pickle, void** iter) { } void UserScript::Pickle(::Pickle* pickle) const { - // Write the run location. + // Write simple types. pickle->WriteInt(run_location()); - - // Write the extension id. pickle->WriteString(extension_id()); - - // Write Greasemonkey emulation. pickle->WriteBool(emulate_greasemonkey()); - - // Write match all frames pickle->WriteBool(match_all_frames()); + pickle->WriteBool(is_incognito_enabled()); // Write globs. std::vector<std::string>::const_iterator glob; @@ -127,14 +122,10 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST); run_location_ = static_cast<RunLocation>(run_location); - // Read the extension ID. CHECK(pickle.ReadString(iter, &extension_id_)); - - // Read Greasemonkey emulation. CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_)); - - // Read match all frames CHECK(pickle.ReadBool(iter, &match_all_frames_)); + CHECK(pickle.ReadBool(iter, &incognito_enabled_)); // Read globs. size_t num_globs = 0; diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h index 9fbbe89..ed84009 100644 --- a/chrome/common/extensions/user_script.h +++ b/chrome/common/extensions/user_script.h @@ -102,7 +102,7 @@ class UserScript { // Greasemonkey and probably more useful for typical scripts. UserScript() : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false), - match_all_frames_(false) { + match_all_frames_(false), incognito_enabled_(false) { } const std::string& name_space() const { return name_space_; } @@ -162,6 +162,9 @@ class UserScript { const std::string& extension_id() const { return extension_id_; } void set_extension_id(const std::string& id) { extension_id_ = id; } + bool is_incognito_enabled() const { return incognito_enabled_; } + void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; } + bool is_standalone() const { return extension_id_.empty(); } // Returns true if the script should be applied to the specified URL, false @@ -217,6 +220,9 @@ class UserScript { // Whether the user script should run in all frames, or only just the top one. // Defaults to false. bool match_all_frames_; + + // True if the script should be injected into an incognito tab. + bool incognito_enabled_; }; typedef std::vector<UserScript> UserScriptList; diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 3a58772..a37ba09 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -239,8 +239,9 @@ IPC_BEGIN_MESSAGES(View) // Notification that the user scripts have been updated. It has one // SharedMemoryHandle argument consisting of the pickled script data. This // handle is valid in the context of the renderer. - IPC_MESSAGE_CONTROL1(ViewMsg_UserScripts_UpdatedScripts, - base::SharedMemoryHandle) + IPC_MESSAGE_CONTROL2(ViewMsg_UserScripts_UpdatedScripts, + base::SharedMemoryHandle, + bool /* only_inject_incognito */) // Sent when the user wants to search for a word on the page (find in page). IPC_MESSAGE_ROUTED3(ViewMsg_Find, diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 1f61c23..7914507 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -320,9 +320,9 @@ void RenderThread::OnSetZoomLevelForCurrentHost(const std::string& host, } void RenderThread::OnUpdateUserScripts( - base::SharedMemoryHandle scripts) { + base::SharedMemoryHandle scripts, bool only_inject_incognito) { DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle"; - user_script_slave_->UpdateScripts(scripts); + user_script_slave_->UpdateScripts(scripts, only_inject_incognito); UpdateActiveExtensions(); } diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index 4f992e4..0336ba8 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -162,7 +162,8 @@ class RenderThread : public RenderThreadBase, void OnSetZoomLevelForCurrentHost(const std::string& host, int zoom_level); void OnSetContentSettingsForCurrentHost( const std::string& host, const ContentSettings& content_settings); - void OnUpdateUserScripts(base::SharedMemoryHandle table); + void OnUpdateUserScripts(base::SharedMemoryHandle table, + bool only_inject_incognito); void OnSetExtensionFunctionNames(const std::vector<std::string>& names); void OnPageActionsUpdated(const std::string& extension_id, const std::vector<std::string>& page_actions); diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc index a06d856..0c0574b 100644 --- a/chrome/renderer/user_script_slave.cc +++ b/chrome/renderer/user_script_slave.cc @@ -68,7 +68,8 @@ void UserScriptSlave::GetActiveExtensions(std::set<std::string>* extension_ids) } } -bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { +bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory, + bool only_inject_incognito) { scripts_.clear(); // Create the shared memory object (read only). @@ -101,6 +102,13 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { UserScript* script = scripts_.back(); script->Unpickle(pickle, &iter); + if (only_inject_incognito && !script->is_incognito_enabled()) { + // This script shouldn't run in an incognito tab. + delete script; + scripts_.pop_back(); + continue; + } + // Note that this is a pointer into shared memory. We don't own it. It gets // cleared up when the last renderer or browser process drops their // reference to the shared memory. diff --git a/chrome/renderer/user_script_slave.h b/chrome/renderer/user_script_slave.h index cf8fb8d..4cbbfd2 100644 --- a/chrome/renderer/user_script_slave.h +++ b/chrome/renderer/user_script_slave.h @@ -31,8 +31,11 @@ class UserScriptSlave { // Returns the unique set of extension IDs this UserScriptSlave knows about. void GetActiveExtensions(std::set<std::string>* extension_ids); - // Update the parsed scripts from shared memory. - bool UpdateScripts(base::SharedMemoryHandle shared_memory); + // Update the parsed scripts from shared memory. If |only_inject_incognito| + // is true, we will only use the scripts that have been marked as enabled for + // incognito mode. + bool UpdateScripts(base::SharedMemoryHandle shared_memory, + bool only_inject_incognito); // Inject the appropriate scripts into a frame based on its URL. // TODO(aa): Extract a UserScriptFrame interface out of this to improve diff --git a/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json b/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json index c7ddb6a..697d2ce 100644 --- a/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json +++ b/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json @@ -3,7 +3,8 @@ "version": "1.0", "background_page": "background.html", "permissions": [ - "tabs", "http://*/*" + "tabs", "http://*/*", + "experimental", "incognito" ], "browser_action": { "default_title": "Test setPopup()", diff --git a/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json b/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json index 9604839..9d9e9c1 100755 --- a/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json +++ b/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json @@ -3,7 +3,8 @@ "version": "1.0", "background_page": "background.html", "permissions": [ - "tabs", "http://*/*" + "tabs", "http://*/*", + "experimental", "incognito" ], "browser_action": { "default_title": "Make this page red", diff --git a/chrome/test/data/extensions/api_test/browser_action/popup/manifest.json b/chrome/test/data/extensions/api_test/browser_action/popup/manifest.json index f353316..fe2ad11 100755 --- a/chrome/test/data/extensions/api_test/browser_action/popup/manifest.json +++ b/chrome/test/data/extensions/api_test/browser_action/popup/manifest.json @@ -2,6 +2,9 @@ "name": "Popup tester",
"version": "0.1",
"description": "apitest for popups",
+ "permissions": [
+ "experimental", "incognito"
+ ],
"browser_action": {
"name": "grow",
"icons": ["chromium.png"],
diff --git a/chrome/test/data/extensions/api_test/incognito_no_script/manifest.json b/chrome/test/data/extensions/api_test/incognito_no_script/manifest.json index 7c6156e..e8f013c 100755 --- a/chrome/test/data/extensions/api_test/incognito_no_script/manifest.json +++ b/chrome/test/data/extensions/api_test/incognito_no_script/manifest.json @@ -2,7 +2,7 @@ "name": "incognito no script", "version": "0.1", "description": "Checks that content scripts do not inject js into incognito browsers.", - "permissions": ["http://*/*", "https://*/*"], + "permissions": ["http://*/*", "https://*/*", "experimental", "incognito"], "content_scripts": [ { "matches": ["http://*/*", "https://*/*"], |