summaryrefslogtreecommitdiffstats
path: root/chrome/browser/background_contents_service.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/background_contents_service.cc')
-rw-r--r--chrome/browser/background_contents_service.cc256
1 files changed, 256 insertions, 0 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), &current))
+ 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);
+}