diff options
Diffstat (limited to 'chrome')
23 files changed, 786 insertions, 63 deletions
diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc new file mode 100644 index 0000000..685b12b --- /dev/null +++ b/chrome/browser/background_contents_service.cc @@ -0,0 +1,256 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/background_contents_service.h" + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/background_contents.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" + +// Keys for the information we store about individual BackgroundContents in +// prefs. There is one top-level DictionaryValue (stored at +// prefs::kRegisteredBackgroundContents). Information about each +// BackgroundContents is stored under that top-level DictionaryValue, keyed +// by the parent application ID for easy lookup. +// +// kRegisteredBackgroundContents: +// DictionaryValue { +// <appid_1>: { "url": <url1>, "name": <frame_name> }, +// <appid_2>: { "url": <url2>, "name": <frame_name> }, +// ... etc ... +// } +const wchar_t kUrlKey[] = L"url"; +const wchar_t kFrameNameKey[] = L"name"; + +BackgroundContentsService::BackgroundContentsService( + Profile* profile, const CommandLine* command_line) + : prefs_(NULL) { + // Don't load/store preferences if the proper switch is not enabled, or if + // the parent profile is off the record. + if (!profile->IsOffTheRecord() && + command_line->HasSwitch(switches::kRestoreBackgroundContents)) + prefs_ = profile->GetPrefs(); + + // Listen for events to tell us when to load/unload persisted background + // contents. + StartObserving(profile); +} + +BackgroundContentsService::~BackgroundContentsService() { + // BackgroundContents should be shutdown before we go away, as otherwise + // our browser process refcount will be off. + DCHECK(contents_map_.empty()); +} + +void BackgroundContentsService::StartObserving(Profile* profile) { + // On startup, load our background pages after extension-apps have loaded. + registrar_.Add(this, NotificationType::EXTENSIONS_READY, + Source<Profile>(profile)); + + // Track the lifecycle of all BackgroundContents in the system to allow us + // to store an up-to-date list of the urls. Start tracking contents when they + // have been opened, and stop tracking them when they are closed by script. + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_OPENED, + Source<Profile>(profile)); + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_CLOSED, + Source<Profile>(profile)); + + // Stop tracking BackgroundContents when they have been deleted (happens + // during shutdown or if the render process dies). + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_DELETED, + Source<Profile>(profile)); + // Track when the BackgroundContents navigates to a new URL so we can update + // our persisted information as appropriate. + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED, + Source<Profile>(profile)); + + // Listen for extensions to be unloaded so we can shutdown associated + // BackgroundContents. + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + Source<Profile>(profile)); +} + +void BackgroundContentsService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSIONS_READY: + LoadBackgroundContentsFromPrefs(); + break; + case NotificationType::BACKGROUND_CONTENTS_DELETED: + BackgroundContentsShutdown(Details<BackgroundContents>(details).ptr()); + break; + case NotificationType::BACKGROUND_CONTENTS_OPENED: + BackgroundContentsOpened( + Details<BackgroundContentsOpenedDetails>(details).ptr()); + break; + case NotificationType::BACKGROUND_CONTENTS_CLOSED: + DCHECK(IsTracked(Details<BackgroundContents>(details).ptr())); + UnregisterBackgroundContents(Details<BackgroundContents>(details).ptr()); + break; + case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: + DCHECK(IsTracked(Details<BackgroundContents>(details).ptr())); + RegisterBackgroundContents(Details<BackgroundContents>(details).ptr()); + break; + case NotificationType::EXTENSION_UNLOADED: + ShutdownAssociatedBackgroundContents( + ASCIIToUTF16(Details<Extension>(details)->id())); + break; + default: + NOTREACHED(); + break; + } +} + +// Loads all background contents whose urls have been stored in prefs. +void BackgroundContentsService::LoadBackgroundContentsFromPrefs() { + if (!prefs_) + return; + DLOG(INFO) << "Starting to load background contents"; + const DictionaryValue* contents = + prefs_->GetDictionary(prefs::kRegisteredBackgroundContents); + if (!contents) + return; + DLOG(INFO) << "Loading " << contents->size() << " background contents"; + for (DictionaryValue::key_iterator it = contents->begin_keys(); + it != contents->end_keys(); ++it) { + DictionaryValue* dict; + contents->GetDictionaryWithoutPathExpansion(*it, &dict); + string16 frame_name; + std::string url; + dict->GetString(kUrlKey, &url); + dict->GetStringAsUTF16(kFrameNameKey, &frame_name); + CreateBackgroundContents(GURL(url), + frame_name, + WideToUTF16(*it)); + } +} + +void BackgroundContentsService::CreateBackgroundContents( + const GURL& url, + const string16& frame_name, + const string16& application_id) { + // We are depending on the fact that we will initialize before any user + // actions or session restore can take place, so no BackgroundContents should + // be running yet for the passed application_id. + DCHECK(!GetAppBackgroundContents(application_id)); + DCHECK(url.is_valid()); + // TODO(atwilson): Fire up renderer and load BackgroundContents for this url, + // and set its initial url in the contents_map. + DLOG(INFO) << "Loading background content url: " << url; +} + +void BackgroundContentsService::RegisterBackgroundContents( + BackgroundContents* background_contents) { + DCHECK(IsTracked(background_contents)); + if (!prefs_) + return; + + // We store the first URL we receive for a given application. If there's + // already an entry for this application, no need to do anything. + DictionaryValue* pref = prefs_->GetMutableDictionary( + prefs::kRegisteredBackgroundContents); + const string16& appid = GetParentApplicationId(background_contents); + DictionaryValue* current; + if (pref->GetDictionaryWithoutPathExpansion(UTF16ToWide(appid), ¤t)) + return; + + // No entry for this application yet, so add one. + DictionaryValue* dict = new DictionaryValue(); + dict->SetString(kUrlKey, background_contents->GetURL().spec()); + dict->SetStringFromUTF16(kFrameNameKey, contents_map_[appid].frame_name); + pref->SetWithoutPathExpansion(UTF16ToWide(appid), dict); + prefs_->ScheduleSavePersistentPrefs(); +} + +void BackgroundContentsService::UnregisterBackgroundContents( + BackgroundContents* background_contents) { + if (!prefs_) + return; + DCHECK(IsTracked(background_contents)); + const string16 appid = GetParentApplicationId(background_contents); + DictionaryValue* pref = prefs_->GetMutableDictionary( + prefs::kRegisteredBackgroundContents); + pref->RemoveWithoutPathExpansion(UTF16ToWide(appid), NULL); + prefs_->ScheduleSavePersistentPrefs(); +} + +void BackgroundContentsService::ShutdownAssociatedBackgroundContents( + const string16& appid) { + BackgroundContents* contents = GetAppBackgroundContents(appid); + if (contents) { + UnregisterBackgroundContents(contents); + // Background contents destructor shuts down the renderer. + delete contents; + } +} + +void BackgroundContentsService::BackgroundContentsOpened( + BackgroundContentsOpenedDetails* details) { + // If this is the first BackgroundContents loaded, kick ourselves into + // persistent mode. + // TODO(atwilson): Enable this when we support running with no active windows + // on all platforms (http://crbug.com/45275). + // if (contents_map_.empty()) + // g_browser_process->AddRefModule(); + + // Add the passed object to our list. Should not already be tracked. + DCHECK(!IsTracked(details->contents)); + DCHECK(!details->application_id.empty()); + contents_map_[details->application_id].contents = details->contents; + contents_map_[details->application_id].frame_name = details->frame_name; +} + +// Used by test code and debug checks to verify whether a given +// BackgroundContents is being tracked by this instance. +bool BackgroundContentsService::IsTracked( + BackgroundContents* background_contents) const { + return !GetParentApplicationId(background_contents).empty(); +} + +void BackgroundContentsService::BackgroundContentsShutdown( + BackgroundContents* background_contents) { + // Remove the passed object from our list. + DCHECK(IsTracked(background_contents)); + string16 appid = GetParentApplicationId(background_contents); + contents_map_.erase(appid); + // If we have no more BackgroundContents active, then stop keeping the browser + // process alive. + // TODO(atwilson): Enable this when we support running with no active windows + // on all platforms (http://crbug.com/45275). + // if (contents_map_.empty()) + // g_browser_process->ReleaseModule(); +} + +BackgroundContents* BackgroundContentsService::GetAppBackgroundContents( + const string16& application_id) { + BackgroundContentsMap::const_iterator it = contents_map_.find(application_id); + return (it != contents_map_.end()) ? it->second.contents : NULL; +} + +const string16& BackgroundContentsService::GetParentApplicationId( + BackgroundContents* contents) const { + for (BackgroundContentsMap::const_iterator it = contents_map_.begin(); + it != contents_map_.end(); ++it) { + if (contents == it->second.contents) + return it->first; + } + return EmptyString16(); +} + +// static +void BackgroundContentsService::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterDictionaryPref(prefs::kRegisteredBackgroundContents); +} diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h new file mode 100644 index 0000000..d9e9e67 --- /dev/null +++ b/chrome/browser/background_contents_service.h @@ -0,0 +1,113 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_BACKGROUND_CONTENTS_SERVICE_H_ +#define CHROME_BROWSER_BACKGROUND_CONTENTS_SERVICE_H_ + +#include <map> + +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +class BackgroundContents; +class CommandLine; +class PrefService; +class Profile; +struct BackgroundContentsOpenedDetails; + +// BackgroundContentsService is owned by the profile, and is responsible for +// managing the lifetime of BackgroundContents (tracking the set of background +// urls, loading them at startup, and keeping the browser process alive as long +// as there are BackgroundContents loaded). +// +// It is also responsible for tracking the association between +// BackgroundContents and their parent app, and shutting them down when the +// parent app is unloaded. +class BackgroundContentsService : private NotificationObserver { + public: + BackgroundContentsService(Profile* profile, const CommandLine* command_line); + virtual ~BackgroundContentsService(); + + // Returns the BackgroundContents associated with the passed application id, + // or NULL if none. + BackgroundContents* GetAppBackgroundContents(const string16& appid); + + static void RegisterUserPrefs(PrefService* prefs); + + private: + friend class BackgroundContentsServiceTest; + FRIEND_TEST(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy); + FRIEND_TEST(BackgroundContentsServiceTest, TestApplicationIDLinkage); + + // Registers for various notifications. + void StartObserving(Profile* profile); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Loads all registered BackgroundContents at startup. + void LoadBackgroundContentsFromPrefs(); + + // Creates a single BackgroundContents associated with the specified |appid|. + // The BackgroundContents frame will be given the name specified by + // |frame_name| and navigated to the passed URL. + void CreateBackgroundContents(const GURL& url, + const string16& frame_name, + const string16& appid); + + // Invoked when a new BackgroundContents is opened. + void BackgroundContentsOpened(BackgroundContentsOpenedDetails* details); + + // Invoked when a BackgroundContents object is destroyed. + void BackgroundContentsShutdown(BackgroundContents* contents); + + // Registers the |contents->GetURL()| to be run at startup. Only happens for + // the first navigation after window.open() (future calls to + // RegisterBackgroundContents() for the same BackgroundContents will do + // nothing). + void RegisterBackgroundContents(BackgroundContents* contents); + + // Stops loading the passed BackgroundContents on startup. + void UnregisterBackgroundContents(BackgroundContents* contents); + + // Unregisters and deletes the BackgroundContents associated with the + // passed extension. + void ShutdownAssociatedBackgroundContents(const string16& appid); + + // Returns true if this BackgroundContents is in the contents_list_. + bool IsTracked(BackgroundContents* contents) const; + + // Gets the parent application id for the passed BackgroundContents. Returns + // an empty string if no parent application found (e.g. passed + // BackgroundContents has already shut down). + const string16& GetParentApplicationId(BackgroundContents* contents) const; + + // PrefService used to store list of background pages (or NULL if this is + // running under an off-the-record profile). + PrefService* prefs_; + NotificationRegistrar registrar_; + + // Information we track about each BackgroundContents. + struct BackgroundContentsInfo { + // The BackgroundContents whose information we are tracking. + BackgroundContents* contents; + // The name of the top level frame for this BackgroundContents. + string16 frame_name; + }; + + // Map associating currently loaded BackgroundContents with their parent + // applications. + // Key: application id + // Value: BackgroundContentsInfo for the BC associated with that application + typedef std::map<string16, BackgroundContentsInfo> BackgroundContentsMap; + BackgroundContentsMap contents_map_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundContentsService); +}; + +#endif // CHROME_BROWSER_BACKGROUND_CONTENTS_SERVICE_H_ diff --git a/chrome/browser/background_contents_service_unittest.cc b/chrome/browser/background_contents_service_unittest.cc new file mode 100644 index 0000000..8e4394e --- /dev/null +++ b/chrome/browser/background_contents_service_unittest.cc @@ -0,0 +1,238 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/scoped_ptr.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/background_contents_service.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/tab_contents/background_contents.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_browser_process.h" +#include "chrome/test/testing_profile.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +class BackgroundContentsServiceTest : public testing::Test { + public: + BackgroundContentsServiceTest() {} + ~BackgroundContentsServiceTest() {} + void SetUp() { + command_line_.reset(new CommandLine(CommandLine::ARGUMENTS_ONLY)); + command_line_->AppendSwitch(switches::kRestoreBackgroundContents); + } + + DictionaryValue* GetPrefs(Profile* profile) { + return profile->GetPrefs()->GetMutableDictionary( + prefs::kRegisteredBackgroundContents); + } + + // Returns the stored pref URL for the passed app id. + std::string GetPrefURLForApp(Profile* profile, const string16& appid) { + DictionaryValue* pref = GetPrefs(profile); + EXPECT_TRUE(pref->HasKey(UTF16ToWide(appid))); + DictionaryValue* value; + pref->GetDictionaryWithoutPathExpansion(UTF16ToWide(appid), &value); + std::string url; + value->GetString(L"url", &url); + return url; + } + + scoped_ptr<CommandLine> command_line_; +}; + +class MockBackgroundContents : public BackgroundContents { + public: + explicit MockBackgroundContents(Profile* profile) + : appid_(ASCIIToUTF16("app_id")), + profile_(profile) { + } + MockBackgroundContents(Profile* profile, const std::string& id) + : appid_(ASCIIToUTF16(id)), + profile_(profile) { + } + + void SendOpenedNotification() { + string16 frame_name = ASCIIToUTF16("background"); + BackgroundContentsOpenedDetails details = { + this, frame_name, appid_ }; + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_OPENED, + Source<Profile>(profile_), + Details<BackgroundContentsOpenedDetails>(&details)); + } + + virtual const void Navigate(GURL url) { + url_ = url; + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_NAVIGATED, + Source<Profile>(profile_), + Details<BackgroundContents>(this)); + } + virtual const GURL& GetURL() const { return url_; } + + void MockClose(Profile* profile) { + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_CLOSED, + Source<Profile>(profile), + Details<BackgroundContents>(this)); + delete this; + } + + ~MockBackgroundContents() { + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_DELETED, + Source<Profile>(profile_), + Details<BackgroundContents>(this)); + } + + const string16& appid() { return appid_; } + + private: + GURL url_; + + // The ID of our parent application + string16 appid_; + + // Parent profile + Profile* profile_; +}; + +TEST_F(BackgroundContentsServiceTest, Create) { + // Check for creation and leaks. + TestingProfile profile; + BackgroundContentsService service(&profile, command_line_.get()); +} + +TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) { + TestingProfile profile; + BackgroundContentsService service(&profile, command_line_.get()); + // TODO(atwilson): Enable the refcount part of the test once we fix the + // APP_TERMINATING notification to be sent at the proper time. + // (http://crbug.com/45275). + // + // TestingBrowserProcess* process = + // static_cast<TestingBrowserProcess*>(g_browser_process); + // unsigned int starting_ref_count = process->module_ref_count(); + MockBackgroundContents* contents = new MockBackgroundContents(&profile); + EXPECT_FALSE(service.IsTracked(contents)); + contents->SendOpenedNotification(); + // EXPECT_EQ(starting_ref_count+1, process->module_ref_count()); + EXPECT_TRUE(service.IsTracked(contents)); + delete contents; + // EXPECT_EQ(starting_ref_count, process->module_ref_count()); + EXPECT_FALSE(service.IsTracked(contents)); +} + +TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) { + TestingProfile profile; + GetPrefs(&profile)->Clear(); + BackgroundContentsService service(&profile, command_line_.get()); + GURL orig_url; + GURL url("http://a/"); + GURL url2("http://a/"); + { + scoped_ptr<MockBackgroundContents> contents( + new MockBackgroundContents(&profile)); + EXPECT_EQ(0U, GetPrefs(&profile)->size()); + contents->SendOpenedNotification(); + + contents->Navigate(url); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); + + // Navigate the contents to a new url, should not change url. + contents->Navigate(url2); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); + + } + // Contents are deleted, url should persist. + EXPECT_EQ(1U, GetPrefs(&profile)->size()); +} + +TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) { + TestingProfile profile; + GetPrefs(&profile)->Clear(); + BackgroundContentsService service(&profile, command_line_.get()); + GURL url("http://a/"); + MockBackgroundContents* contents = new MockBackgroundContents(&profile); + EXPECT_EQ(0U, GetPrefs(&profile)->size()); + contents->SendOpenedNotification(); + contents->Navigate(url); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); + + // Fake a window closed by script. + contents->MockClose(&profile); + EXPECT_EQ(0U, GetPrefs(&profile)->size()); +} + +// Test what happens if a BackgroundContents shuts down (say, due to a renderer +// crash) then is restarted. Should not persist URL twice. +TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) { + TestingProfile profile; + GetPrefs(&profile)->Clear(); + BackgroundContentsService service(&profile, command_line_.get()); + GURL url("http://a/"); + { + scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( + &profile, "appid")); + contents->SendOpenedNotification(); + contents->Navigate(url); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); + } + // Contents deleted, url should be persisted. + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + + { + // Reopen the BackgroundContents to the same URL, we should not register the + // URL again. + scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( + &profile, "appid")); + contents->SendOpenedNotification(); + contents->Navigate(url); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + } +} + +// Ensures that BackgroundContentsService properly tracks the association +// between a BackgroundContents and its parent extension, including +// unregistering the BC when the extension is uninstalled. +TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) { + TestingProfile profile; + BackgroundContentsService service(&profile, command_line_.get()); + GetPrefs(&profile)->Clear(); + + EXPECT_EQ(NULL, service.GetAppBackgroundContents(ASCIIToUTF16("appid"))); + MockBackgroundContents* contents = new MockBackgroundContents(&profile, + "appid"); + scoped_ptr<MockBackgroundContents> contents2( + new MockBackgroundContents(&profile, "appid2")); + contents->SendOpenedNotification(); + EXPECT_EQ(contents, service.GetAppBackgroundContents(contents->appid())); + contents2->SendOpenedNotification(); + EXPECT_EQ(contents2.get(), service.GetAppBackgroundContents( + contents2->appid())); + EXPECT_EQ(0U, GetPrefs(&profile)->size()); + + // Navigate the contents, then make sure the one associated with the extension + // is unregistered. + GURL url("http://a/"); + GURL url2("http://b/"); + contents->Navigate(url); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + contents2->Navigate(url2); + EXPECT_EQ(2U, GetPrefs(&profile)->size()); + service.ShutdownAssociatedBackgroundContents(ASCIIToUTF16("appid")); + EXPECT_FALSE(service.IsTracked(contents)); + EXPECT_EQ(NULL, service.GetAppBackgroundContents(ASCIIToUTF16("appid"))); + EXPECT_EQ(1U, GetPrefs(&profile)->size()); + EXPECT_EQ(url2.spec(), GetPrefURLForApp(&profile, contents2->appid())); +} diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc index a38224f..63b6d32 100644 --- a/chrome/browser/browser_init.cc +++ b/chrome/browser/browser_init.cc @@ -439,7 +439,7 @@ bool BrowserInit::LaunchBrowser( } #endif - if (command_line.HasSwitch(switches::kLongLivedExtensions)) { + if (command_line.HasSwitch(switches::kRestoreBackgroundContents)) { // Create status icons StatusTrayManager* tray = g_browser_process->status_tray_manager(); if (tray) diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index 816c50b..251a4f0 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -5,6 +5,7 @@ #include "chrome/browser/browser_prefs.h" #include "chrome/browser/autofill/autofill_manager.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_shutdown.h" @@ -126,6 +127,7 @@ void RegisterUserPrefs(PrefService* user_prefs) { #if defined(OS_CHROMEOS) chromeos::Preferences::RegisterUserPrefs(user_prefs); #endif + BackgroundContentsService::RegisterUserPrefs(user_prefs); } } // namespace browser diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index a74f5ab..486dd1e 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -576,10 +576,8 @@ void BrowserProcessImpl::RestartPersistentInstance() { } } - // TODO(atwilson): Uncomment the following two lines to add the "persistence" - // switch when the corresponding CL is committed. - // if (!new_cl->HasSwitch(switches::kLongLivedExtensions)) - // new_cl->AppendSwitch(switches::kLongLivedExtensions); + if (!new_cl->HasSwitch(switches::kRestoreBackgroundContents)) + new_cl->AppendSwitch(switches::kRestoreBackgroundContents); DLOG(WARNING) << "Shutting down current instance of the browser."; BrowserList::CloseAllBrowsersAndExit(); diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index 8c6664b..5cd3711 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -28,6 +28,7 @@ #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/background_contents.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/chrome_switches.h" @@ -698,8 +699,13 @@ void ExtensionsDOMHandler::Observe(NotificationType type, // Doing it this way gets everything but causes the page to be rendered // more than we need. It doesn't seem to result in any noticeable flicker. case NotificationType::RENDER_VIEW_HOST_DELETED: - case NotificationType::BACKGROUND_CONTENTS_DELETED: deleting_rvh_ = Details<RenderViewHost>(details).ptr(); + MaybeUpdateAfterNotification(); + break; + case NotificationType::BACKGROUND_CONTENTS_DELETED: + deleting_rvh_ = Details<BackgroundContents>(details)->render_view_host(); + MaybeUpdateAfterNotification(); + break; case NotificationType::EXTENSION_LOADED: case NotificationType::EXTENSION_PROCESS_CREATED: case NotificationType::EXTENSION_UNLOADED: @@ -709,16 +715,19 @@ void ExtensionsDOMHandler::Observe(NotificationType type, case NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED: case NotificationType::NAV_ENTRY_COMMITTED: case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: - if (!ignore_notifications_ && dom_ui_->tab_contents()) - HandleRequestExtensionsData(NULL); - deleting_rvh_ = NULL; + MaybeUpdateAfterNotification(); break; - default: NOTREACHED(); } } +void ExtensionsDOMHandler::MaybeUpdateAfterNotification() { + if (!ignore_notifications_ && dom_ui_->tab_contents()) + HandleRequestExtensionsData(NULL); + deleting_rvh_ = NULL; +} + static void CreateScriptFileDetailValue( const FilePath& extension_path, const UserScript::FileList& scripts, const wchar_t* key, DictionaryValue* script_data) { diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h index 449c8da..e85e586 100644 --- a/chrome/browser/extensions/extensions_ui.h +++ b/chrome/browser/extensions/extensions_ui.h @@ -170,6 +170,9 @@ class ExtensionsDOMHandler // Callback for "selectFilePath" message. void HandleSelectFilePathMessage(const Value* value); + // Forces a UI update if appropriate after a notification is received. + void MaybeUpdateAfterNotification(); + // SelectFileDialog::Listener virtual void FileSelected(const FilePath& path, int index, void* params); diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index bbfd60d..a2fbff3 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -16,6 +16,7 @@ #include "chrome/browser/appcache/chrome_appcache_service.h" #include "chrome/browser/autocomplete/autocomplete_classifier.h" #include "chrome/browser/autofill/personal_data_manager.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_prefs.h" @@ -309,6 +310,8 @@ class OffTheRecordProfileImpl : public Profile, // (cookies, downloads...). registrar_.Add(this, NotificationType::BROWSER_CLOSED, NotificationService::AllSources()); + background_contents_service_.reset( + new BackgroundContentsService(this, CommandLine::ForCurrentProcess())); } virtual ~OffTheRecordProfileImpl() { @@ -366,6 +369,10 @@ class OffTheRecordProfileImpl : public Profile, return GetOriginalProfile()->GetExtensionsService(); } + virtual BackgroundContentsService* GetBackgroundContentsService() { + return background_contents_service_.get(); + } + virtual UserScriptMaster* GetUserScriptMaster() { return GetOriginalProfile()->GetUserScriptMaster(); } @@ -721,6 +728,9 @@ class OffTheRecordProfileImpl : public Profile, FilePath last_selected_directory_; + // Tracks all BackgroundContents running under this profile. + scoped_ptr<BackgroundContentsService> background_contents_service_; + DISALLOW_COPY_AND_ASSIGN(OffTheRecordProfileImpl); }; @@ -808,6 +818,9 @@ ProfileImpl::ProfileImpl(const FilePath& path) pinned_tab_service_.reset(new PinnedTabService(this)); + background_contents_service_.reset( + new BackgroundContentsService(this, CommandLine::ForCurrentProcess())); + // Log the profile size after a reasonable startup delay. ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE, new ProfileSizeTask(path_), 112000); @@ -1049,6 +1062,10 @@ ExtensionsService* ProfileImpl::GetExtensionsService() { return extensions_service_.get(); } +BackgroundContentsService* ProfileImpl::GetBackgroundContentsService() { + return background_contents_service_.get(); +} + UserScriptMaster* ProfileImpl::GetUserScriptMaster() { return user_script_master_.get(); } diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index 5ec66d6..4fc6223 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -40,6 +40,7 @@ class DatabaseTracker; } class AutocompleteClassifier; +class BackgroundContentsService; class Blacklist; class BookmarkModel; class BrowserThemeProvider; @@ -403,6 +404,9 @@ class Profile { // Returns the provider of desktop notifications for this profile. virtual DesktopNotificationService* GetDesktopNotificationService() = 0; + // Returns the service that manages BackgroundContents for this profile. + virtual BackgroundContentsService* GetBackgroundContentsService() = 0; + // Marks the profile as cleanly shutdown. // // NOTE: this is invoked internally on a normal shutdown, but is public so @@ -537,6 +541,7 @@ class ProfileImpl : public Profile, virtual void ReinitializeSpellCheckHost(bool force); virtual WebKitContext* GetWebKitContext(); virtual DesktopNotificationService* GetDesktopNotificationService(); + virtual BackgroundContentsService* GetBackgroundContentsService(); virtual void MarkAsCleanShutdown(); virtual void InitExtensions(); virtual void InitWebResources(); @@ -634,6 +639,7 @@ class ProfileImpl : public Profile, scoped_ptr<BrowserThemeProvider> theme_provider_; scoped_refptr<WebKitContext> webkit_context_; scoped_ptr<DesktopNotificationService> desktop_notification_service_; + scoped_ptr<BackgroundContentsService> background_contents_service_; scoped_refptr<PersonalDataManager> personal_data_manager_; scoped_ptr<PinnedTabService> pinned_tab_service_; bool history_service_created_; diff --git a/chrome/browser/tab_contents/background_contents.cc b/chrome/browser/tab_contents/background_contents.cc index 5409d65..2e65d9d 100644 --- a/chrome/browser/tab_contents/background_contents.cc +++ b/chrome/browser/tab_contents/background_contents.cc @@ -4,6 +4,7 @@ #include "chrome/browser/tab_contents/background_contents.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browsing_instance.h" @@ -14,6 +15,7 @@ #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/renderer_preferences_util.h" #include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" #include "chrome/common/view_types.h" #include "chrome/common/render_messages.h" @@ -32,13 +34,15 @@ BackgroundContents::BackgroundContents(SiteInstance* site_instance, session_storage_namespace_id); render_view_host_->AllowScriptToClose(true); -#if defined(OS_WIN) || defined(OS_LINUX) - registrar_.Add(this, NotificationType::BROWSER_CLOSED, - NotificationService::AllSources()); -#elif defined(OS_MACOSX) - registrar_.Add(this, NotificationType::APP_TERMINATING, - NotificationService::AllSources()); -#endif + // Register for our parent profile to shutdown, so we can shut ourselves down + // as well. + registrar_.Add(this, NotificationType::PROFILE_DESTROYED, + Source<Profile>(profile)); +} + +// Exposed to allow creating mocks. +BackgroundContents::BackgroundContents() + : render_view_host_(NULL) { } void BackgroundContents::Observe(NotificationType type, @@ -47,19 +51,10 @@ void BackgroundContents::Observe(NotificationType type, // TODO(rafaelw): Implement pagegroup ref-counting so that non-persistent // background pages are closed when the last referencing frame is closed. switch (type.value) { -#if defined(OS_WIN) || defined(OS_LINUX) - case NotificationType::BROWSER_CLOSED: { - bool app_closing = *Details<bool>(details).ptr(); - if (app_closing) - delete this; - break; - } -#elif defined(OS_MACOSX) - case NotificationType::APP_TERMINATING: { + case NotificationType::PROFILE_DESTROYED: { delete this; break; } -#endif default: NOTREACHED() << "Unexpected notification sent."; break; @@ -67,10 +62,13 @@ void BackgroundContents::Observe(NotificationType type, } BackgroundContents::~BackgroundContents() { + if (!render_view_host_) // Will be null for unit tests. + return; + Profile* profile = render_view_host_->process()->profile(); NotificationService::current()->Notify( NotificationType::BACKGROUND_CONTENTS_DELETED, - Source<BackgroundContents>(this), - Details<RenderViewHost>(render_view_host_)); + Source<Profile>(profile), + Details<BackgroundContents>(this)); render_view_host_->Shutdown(); // deletes render_view_host } @@ -90,10 +88,11 @@ void BackgroundContents::DidNavigate( // extent a background page will be opened but will remain at about:blank. url_ = params.url; + Profile* profile = render_view_host->process()->profile(); NotificationService::current()->Notify( NotificationType::BACKGROUND_CONTENTS_NAVIGATED, - Source<BackgroundContents>(this), - Details<RenderViewHost>(render_view_host_)); + Source<Profile>(profile), + Details<BackgroundContents>(this)); } void BackgroundContents::RunJavaScriptMessage( @@ -126,6 +125,11 @@ void BackgroundContents::OnMessageBoxClosed(IPC::Message* reply_msg, } void BackgroundContents::Close(RenderViewHost* render_view_host) { + Profile* profile = render_view_host->process()->profile(); + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_CLOSED, + Source<Profile>(profile), + Details<BackgroundContents>(this)); delete this; } diff --git a/chrome/browser/tab_contents/background_contents.h b/chrome/browser/tab_contents/background_contents.h index af07a19..6b94c82 100644 --- a/chrome/browser/tab_contents/background_contents.h +++ b/chrome/browser/tab_contents/background_contents.h @@ -32,7 +32,7 @@ class BackgroundContents : public RenderViewHostDelegate, public: BackgroundContents(SiteInstance* site_instance, int routing_id); - ~BackgroundContents(); + virtual ~BackgroundContents(); // Provide access to the RenderViewHost for the // RenderViewHostDelegateViewHelper @@ -109,6 +109,10 @@ class BackgroundContents : public RenderViewHostDelegate, virtual TabContents* AsTabContents() { return NULL; } virtual ExtensionHost* AsExtensionHost() { return NULL; } + protected: + // Exposed for testing. + BackgroundContents(); + private: // The host for our HTML content. RenderViewHost* render_view_host_; @@ -124,4 +128,16 @@ class BackgroundContents : public RenderViewHostDelegate, DISALLOW_COPY_AND_ASSIGN(BackgroundContents); }; +// This is the data sent out as the details with BACKGROUND_CONTENTS_OPENED. +struct BackgroundContentsOpenedDetails { + // The BackgroundContents object that has just been opened. + BackgroundContents* contents; + + // The name of the parent frame for these contents. + const string16& frame_name; + + // The ID of the parent application (if any). + const string16& application_id; +}; + #endif // CHROME_BROWSER_TAB_CONTENTS_BACKGROUND_CONTENTS_H_ diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc index be662e2..6edd873 100644 --- a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc +++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/string_util.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/browser.h" #include "chrome/browser/character_encoding.h" #include "chrome/browser/extensions/extensions_service.h" @@ -21,35 +22,56 @@ #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/user_style_sheet_watcher.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" -bool RenderViewHostDelegateViewHelper::ShouldOpenBackgroundContents( - WindowContainerType window_container_type, +BackgroundContents* +RenderViewHostDelegateViewHelper::MaybeCreateBackgroundContents( + int route_id, + Profile* profile, + SiteInstance* site, GURL opener_url, - RenderProcessHost* opener_process, - Profile* profile) { + const string16& frame_name) { ExtensionsService* extensions_service = profile->GetExtensionsService(); - if (window_container_type != WINDOW_CONTAINER_TYPE_BACKGROUND || - !opener_url.is_valid() || + + if (!opener_url.is_valid() || + frame_name.empty() || !extensions_service || !extensions_service->is_ready()) - return false; + return NULL; Extension* extension = extensions_service->GetExtensionByURL(opener_url); if (!extension) extension = extensions_service->GetExtensionByWebExtent(opener_url); if (!extension || !extension->HasApiPermission(Extension::kBackgroundPermission)) - return false; + return NULL; + // Only allow a single background contents per app. + if (!profile->GetBackgroundContentsService() || + profile->GetBackgroundContentsService()->GetAppBackgroundContents( + ASCIIToUTF16(extension->id()))) + return NULL; + + // Ensure that we're trying to open this from the extension's process. ExtensionProcessManager* process_manager = profile->GetExtensionProcessManager(); - if (!opener_process || !process_manager || - opener_process != process_manager->GetExtensionProcess(opener_url)) - return false; + if (!site->GetProcess() || !process_manager || + site->GetProcess() != process_manager->GetExtensionProcess(opener_url)) + return NULL; + + // Passed all the checks, so this should be created as a BackgroundContents. + BackgroundContents* contents = new BackgroundContents(site, route_id); + string16 appid = ASCIIToUTF16(extension->id()); + BackgroundContentsOpenedDetails details = { contents, frame_name, appid }; + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_OPENED, + Source<Profile>(profile), + Details<BackgroundContentsOpenedDetails>(&details)); - return true; + return contents; } + TabContents* RenderViewHostDelegateViewHelper::CreateNewWindow( int route_id, Profile* profile, @@ -58,13 +80,17 @@ TabContents* RenderViewHostDelegateViewHelper::CreateNewWindow( RenderViewHostDelegate* opener, WindowContainerType window_container_type, const string16& frame_name) { - if (ShouldOpenBackgroundContents(window_container_type, - opener->GetURL(), - site->GetProcess(), - profile)) { - BackgroundContents* contents = new BackgroundContents(site, route_id); - pending_contents_[route_id] = contents->render_view_host(); - return NULL; + if (window_container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) { + BackgroundContents* contents = MaybeCreateBackgroundContents( + route_id, + profile, + site, + opener->GetURL(), + frame_name); + if (contents) { + pending_contents_[route_id] = contents->render_view_host(); + return NULL; + } } // Create the new web contents. This will automatically create the new diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.h b/chrome/browser/tab_contents/render_view_host_delegate_helper.h index 08ef2db..5b1d99b0 100644 --- a/chrome/browser/tab_contents/render_view_host_delegate_helper.h +++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.h @@ -16,6 +16,7 @@ #include "webkit/glue/webpreferences.h" #include "webkit/glue/window_open_disposition.h" +class BackgroundContents; class Browser; class ExtensionsService; class PrefService; @@ -68,10 +69,12 @@ class RenderViewHostDelegateViewHelper { void RenderWidgetHostDestroyed(RenderWidgetHost* host); private: - bool ShouldOpenBackgroundContents(WindowContainerType window_container_type, - GURL opener_url, - RenderProcessHost* opener_process, - Profile* profile); + BackgroundContents* MaybeCreateBackgroundContents( + int route_id, + Profile* profile, + SiteInstance* site, + GURL opener_url, + const string16& frame_name); // Tracks created RenderViewHost objects that have not been shown yet. // They are identified by the route ID passed to CreateNewWindow. diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 291f5dc..a5f6a24 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -221,6 +221,8 @@ 'browser/automation/ui_controls.h', 'browser/back_forward_menu_model.cc', 'browser/back_forward_menu_model.h', + 'browser/background_contents_service.h', + 'browser/background_contents_service.cc', 'browser/blocked_popup_container.cc', 'browser/blocked_popup_container.h', 'browser/bookmarks/bookmark_codec.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 72d24db..46a855d 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -582,6 +582,7 @@ 'browser/autofill/phone_number_unittest.cc', 'browser/automation/automation_provider_unittest.cc', 'browser/back_forward_menu_model_unittest.cc', + 'browser/background_contents_service_unittest.cc', 'browser/bookmarks/bookmark_codec_unittest.cc', 'browser/bookmarks/bookmark_context_menu_controller_unittest.cc', 'browser/bookmarks/bookmark_drag_data_unittest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index f5b6dac..da839ae 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -514,9 +514,6 @@ const char kLoadExtension[] = "load-extension"; // Load an NPAPI plugin from the specified path. const char kLoadPlugin[] = "load-plugin"; -// Long lived extensions. -const char kLongLivedExtensions[] = "long-lived-extensions"; - // Will filter log messages to show only the messages that are prefixed // with the specified value. See also kEnableLogging and kLoggingLevel. const char kLogFilterPrefix[] = "log-filter-prefix"; @@ -711,6 +708,10 @@ const char kRendererProcess[] = "renderer"; // Causes the renderer process to display a dialog on launch. const char kRendererStartupDialog[] = "renderer-startup-dialog"; +// Causes the URLs of BackgroundContents to be remembered and re-launched when +// the browser restarts. +const char kRestoreBackgroundContents[] = "restore-background-contents"; + // Indicates the last session should be restored on startup. This overrides // the preferences value and is primarily intended for testing. The value of // this switch is the number of tabs to wait until loaded before diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index a6ea1d4..3144673 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -154,7 +154,6 @@ extern const char kInternalPepper[]; extern const char kJavaScriptFlags[]; extern const char kLoadExtension[]; extern const char kLoadPlugin[]; -extern const char kLongLivedExtensions[]; extern const char kLogFilterPrefix[]; extern const char kLogPluginMessages[]; extern const char kLoggingLevel[]; @@ -203,6 +202,7 @@ extern const char kRendererCmdPrefix[]; extern const char kRendererCrashTest[]; extern const char kRendererProcess[]; extern const char kRendererStartupDialog[]; +extern const char kRestoreBackgroundContents[]; extern const char kRestoreLastSession[]; extern const char kSafePlugins[]; extern const char kSdchFilter[]; diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index f47088c..93145d1 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -482,12 +482,23 @@ class NotificationType { // BackgroundContents ------------------------------------------------------ + // A new background contents was opened by script. The source is the parent + // profile and the details are BackgroundContentsOpenedDetails. + BACKGROUND_CONTENTS_OPENED, + // The background contents navigated to a new location. The source is the - // BackgroundContents, and the details are contained RenderViewHost. + // parent Profile, and the details are the BackgroundContents that was + // navigated. BACKGROUND_CONTENTS_NAVIGATED, + // The background contents were closed by someone invoking window.close() + // or the parent application was uninstalled. + // The source is the parent profile, and the details are the + // BackgroundContents. + BACKGROUND_CONTENTS_CLOSED, + // The background contents is being deleted. The source is the - // BackgroundContents, and the details are the contained RendeViewHost. + // parent Profile, and the details are the BackgroundContents being deleted. BACKGROUND_CONTENTS_DELETED, // Child Processes --------------------------------------------------------- diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 9e13585..f3a8e2b 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -868,6 +868,11 @@ const wchar_t kLoginDatabaseMigrated[] = L"login_database.migrated"; // The root URL of the cloud print service. const wchar_t kCloudPrintServiceURL[] = L"cloud_print.service_url"; +// The list of BackgroundContents that should be loaded when the browser +// launches. +const wchar_t kRegisteredBackgroundContents[] = + L"background_contents.registered"; + // *************** SERVICE PREFS *************** // These are attached to the service process. diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 192b598..6441e9d 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -336,6 +336,8 @@ extern const wchar_t kProxyServer[]; extern const wchar_t kProxyPacUrl[]; extern const wchar_t kProxyBypassList[]; +extern const wchar_t kRegisteredBackgroundContents[]; + } // namespace prefs #endif // CHROME_COMMON_PREF_NAMES_H_ diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h index fdfe776..bf1744f 100644 --- a/chrome/test/testing_browser_process.h +++ b/chrome/test/testing_browser_process.h @@ -26,6 +26,7 @@ class TestingBrowserProcess : public BrowserProcess { public: TestingBrowserProcess() : shutdown_event_(new base::WaitableEvent(true, false)), + module_ref_count_(0), app_locale_("en") { } @@ -125,10 +126,15 @@ class TestingBrowserProcess : public BrowserProcess { } virtual unsigned int AddRefModule() { - return 1; + return ++module_ref_count_; } virtual unsigned int ReleaseModule() { - return 1; + DCHECK(module_ref_count_ > 0); + return --module_ref_count_; + } + + unsigned int module_ref_count() { + return module_ref_count_; } virtual bool IsShuttingDown() { @@ -165,6 +171,7 @@ class TestingBrowserProcess : public BrowserProcess { private: NotificationService notification_service_; scoped_ptr<base::WaitableEvent> shutdown_event_; + unsigned int module_ref_count_; scoped_ptr<Clipboard> clipboard_; std::string app_locale_; diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index 50bd1fe..adc8d95 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -265,6 +265,9 @@ class TestingProfile : public Profile { virtual DesktopNotificationService* GetDesktopNotificationService() { return NULL; } + virtual BackgroundContentsService* GetBackgroundContentsService() { + return NULL; + } virtual FilePath last_selected_directory() { return last_selected_directory_; } |