diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-18 16:46:40 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-18 16:46:40 +0000 |
commit | 911f815112d8d11778c771467fbef2c2b1f8e160 (patch) | |
tree | a6f9aa0ad2d669de7860e6f4da1b9e7f31f1354d /chrome/browser/tabs | |
parent | 4a827847f1b9b860bcd5c45725249a3de3cfc205 (diff) | |
download | chromium_src-911f815112d8d11778c771467fbef2c2b1f8e160.zip chromium_src-911f815112d8d11778c771467fbef2c2b1f8e160.tar.gz chromium_src-911f815112d8d11778c771467fbef2c2b1f8e160.tar.bz2 |
Makes pinned tab restore on startup if you haven't enabled session
restore.
BUG=23613
TEST=turn off session restore, pin a tab, restart chrome and make sure
you get back the pinned tab.
Review URL: http://codereview.chromium.org/1026005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41958 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tabs')
-rw-r--r-- | chrome/browser/tabs/pinned_tab_codec.cc | 134 | ||||
-rw-r--r-- | chrome/browser/tabs/pinned_tab_codec.h | 44 | ||||
-rw-r--r-- | chrome/browser/tabs/pinned_tab_codec_unittest.cc | 63 | ||||
-rw-r--r-- | chrome/browser/tabs/pinned_tab_service.cc | 50 | ||||
-rw-r--r-- | chrome/browser/tabs/pinned_tab_service.h | 41 |
5 files changed, 332 insertions, 0 deletions
diff --git a/chrome/browser/tabs/pinned_tab_codec.cc b/chrome/browser/tabs/pinned_tab_codec.cc new file mode 100644 index 0000000..51b8fd2 --- /dev/null +++ b/chrome/browser/tabs/pinned_tab_codec.cc @@ -0,0 +1,134 @@ +// 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/tabs/pinned_tab_codec.h" + +#include "base/values.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/common/pref_names.h" +#include "ipc/ipc_message.h" + +typedef BrowserInit::LaunchWithProfile::Tab Tab; + +// Key used in dictionaries for the app id. +static const wchar_t kAppID[] = L"app_id"; + +// Key used in dictionaries for the url. +static const wchar_t kURL[] = L"url"; + +// Returns true if |browser| has any pinned tabs. +static bool HasPinnedTabs(Browser* browser) { + TabStripModel* tab_model = browser->tabstrip_model(); + for (int i = 0; i < tab_model->count(); ++i) { + if (tab_model->IsTabPinned(i)) + return true; + } + return false; +} + +// Adds a DictionaryValue to |values| representing the pinned tab at the +// specified index. +static void EncodePinnedTab(TabStripModel* model, + int index, + ListValue* values) { + scoped_ptr<DictionaryValue> value(new DictionaryValue()); + + TabContents* tab_contents = model->GetTabContentsAt(index); + if (model->IsAppTab(index)) { + Extension* extension = tab_contents->app_extension(); + DCHECK(extension); + value->SetString(kAppID, extension->id()); + // For apps we use the launch url. We do this for two reasons: + // . the user is effectively restarting the app, so that returning them to + // the app's launch page seems closest to what they expect. + // . we do the same when restoring a phantom tab. + value->SetString(kURL, extension->app_launch_url().spec()); + values->Append(value.release()); + } else { + NavigationEntry* entry = tab_contents->controller().GetActiveEntry(); + if (!entry && tab_contents->controller().entry_count()) + entry = tab_contents->controller().GetEntryAtIndex(0); + if (entry) { + value->SetString(kURL, entry->url().spec()); + values->Append(value.release()); + } + } +} + +// Invokes EncodePinnedTab for each pinned tab in browser. +static void EncodePinnedTabs(Browser* browser, ListValue* values) { + TabStripModel* tab_model = browser->tabstrip_model(); + for (int i = 0; i < tab_model->count() && tab_model->IsTabPinned(i); ++i) + EncodePinnedTab(tab_model, i, values); +} + +// Decodes the previously written values in |value| to |tab|, returning true +// on success. +static bool DecodeTab(const DictionaryValue& value, Tab* tab) { + tab->is_app = false; + + std::string url_string; + if (!value.GetString(kURL, &url_string)) + return false; + tab->url = GURL(url_string); + + if (value.GetString(kAppID, &(tab->app_id))) + tab->is_app = true; + + return true; +} + +// static +void PinnedTabCodec::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterListPref(prefs::kPinnedTabs); +} + +// static +void PinnedTabCodec::WritePinnedTabs(Profile* profile) { + PrefService* prefs = profile->GetPrefs(); + if (!prefs) + return; + + ListValue values; + for (BrowserList::const_iterator i = BrowserList::begin(); + i != BrowserList::end(); ++i) { + Browser* browser = *i; + if (browser->type() == Browser::TYPE_NORMAL && + browser->profile() == profile && HasPinnedTabs(browser)) { + EncodePinnedTabs(browser, &values); + } + } + prefs->Set(prefs::kPinnedTabs, values); + prefs->ScheduleSavePersistentPrefs(); +} + +// static +std::vector<Tab> PinnedTabCodec::ReadPinnedTabs(Profile* profile) { + std::vector<Tab> results; + + PrefService* prefs = profile->GetPrefs(); + if (!prefs) + return results; + + const ListValue* pref_value = prefs->GetList(prefs::kPinnedTabs); + if (!pref_value) + return results; + + for (size_t i = 0, max = pref_value->GetSize(); i < max; ++i) { + DictionaryValue* values = NULL; + if (pref_value->GetDictionary(i, &values)) { + Tab tab; + if (DecodeTab(*values, &tab)) + results.push_back(tab); + } + } + return results; +} diff --git a/chrome/browser/tabs/pinned_tab_codec.h b/chrome/browser/tabs/pinned_tab_codec.h new file mode 100644 index 0000000..1b1aed3 --- /dev/null +++ b/chrome/browser/tabs/pinned_tab_codec.h @@ -0,0 +1,44 @@ +// 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_TABS_PINNED_TAB_CODEC_H_ +#define CHROME_BROWSER_TABS_PINNED_TAB_CODEC_H_ + +#include <string> +#include <vector> + +#include "chrome/browser/browser_init.h" +#include "googleurl/src/gurl.h" + +class Browser; +class PrefService; +class Profile; + +// PinnedTabCodec is used to read and write the set of pinned tabs to +// preferences. When Chrome exits the sets of pinned tabs are written to prefs. +// On startup if the user has not chosen to restore the last session the set of +// pinned tabs is opened. +// +// The entries are stored in preferences as a list of dictionaries, with each +// dictionary describing the entry. +class PinnedTabCodec { + public: + // Registers the preference used by this class. + static void RegisterUserPrefs(PrefService* prefs); + + // Resets the preferences state. + static void WritePinnedTabs(Profile* profile); + + // Reads and returns the set of pinned tabs to restore from preferences. + static std::vector<BrowserInit::LaunchWithProfile::Tab> ReadPinnedTabs( + Profile* profile); + + private: + PinnedTabCodec(); + ~PinnedTabCodec(); + + DISALLOW_COPY_AND_ASSIGN(PinnedTabCodec); +}; + +#endif // CHROME_BROWSER_TABS_PINNED_TAB_CODEC_H_ diff --git a/chrome/browser/tabs/pinned_tab_codec_unittest.cc b/chrome/browser/tabs/pinned_tab_codec_unittest.cc new file mode 100644 index 0000000..c9461eb --- /dev/null +++ b/chrome/browser/tabs/pinned_tab_codec_unittest.cc @@ -0,0 +1,63 @@ +// 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 <string> +#include <vector> + +#include "chrome/browser/tabs/pinned_tab_codec.h" +#include "chrome/test/browser_with_test_window_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +typedef BrowserInit::LaunchWithProfile::Tab Tab; + +typedef BrowserWithTestWindowTest PinnedTabCodecTest; + +namespace { + +std::string TabToString(const Tab& tab) { + return tab.url.spec() + ":" + (tab.is_app ? "app" : "") + ":" + + (tab.is_pinned ? "pinned" : "") + ":" + tab.app_id; +} + +std::string TabsToString(const std::vector<Tab>& values) { + std::string result; + for (size_t i = 0; i < values.size(); ++i) { + if (i != 0) + result += " "; + result += TabToString(values[i]); + } + return result; +} + +} // namespace + +// Make sure nothing is restored when the browser has no pinned tabs. +TEST_F(PinnedTabCodecTest, NoPinnedTabs) { + GURL url1("http://www.google.com"); + AddTab(browser(), url1); + + PinnedTabCodec::WritePinnedTabs(profile()); + + std::string result = TabsToString(PinnedTabCodec::ReadPinnedTabs(profile())); + EXPECT_EQ("", result); +} + +// Creates a browser with one pinned tab and one normal tab, does restore and +// makes sure we get back another pinned tab. +TEST_F(PinnedTabCodecTest, PinnedAndNonPinned) { + GURL url1("http://www.google.com"); + GURL url2("http://www.google.com/2"); + AddTab(browser(), url2); + + // AddTab inserts at index 0, so order after this is url1, url2. + AddTab(browser(), url1); + + browser()->tabstrip_model()->SetTabPinned(0, true); + + PinnedTabCodec::WritePinnedTabs(profile()); + + std::string result = TabsToString(PinnedTabCodec::ReadPinnedTabs(profile())); + EXPECT_EQ("http://www.google.com/::pinned:", result); +} + diff --git a/chrome/browser/tabs/pinned_tab_service.cc b/chrome/browser/tabs/pinned_tab_service.cc new file mode 100644 index 0000000..826ca65 --- /dev/null +++ b/chrome/browser/tabs/pinned_tab_service.cc @@ -0,0 +1,50 @@ +// 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/tabs/pinned_tab_service.h" + +#include "chrome/browser/browser.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tabs/pinned_tab_codec.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" + +PinnedTabService::PinnedTabService(Profile* profile) + : profile_(profile), + got_exiting_(false) { + registrar_.Add(this, NotificationType::BROWSER_CLOSING, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::APP_EXITING, + NotificationService::AllSources()); +} + +void PinnedTabService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (got_exiting_) + return; + + switch (type.value) { + case NotificationType::BROWSER_CLOSING: { + Browser* browser = Source<Browser>(source).ptr(); + if (browser->profile() == profile_ && *(Details<bool>(details)).ptr()) + GotExit(); + break; + } + + case NotificationType::APP_EXITING: { + GotExit(); + break; + } + + default: + NOTREACHED(); + } +} + +void PinnedTabService::GotExit() { + DCHECK(!got_exiting_); + got_exiting_ = true; + PinnedTabCodec::WritePinnedTabs(profile_); +} diff --git a/chrome/browser/tabs/pinned_tab_service.h b/chrome/browser/tabs/pinned_tab_service.h new file mode 100644 index 0000000..3437857 --- /dev/null +++ b/chrome/browser/tabs/pinned_tab_service.h @@ -0,0 +1,41 @@ +// 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_TABS_PINNED_TAB_SERVICE_H_ +#define CHROME_BROWSER_TABS_PINNED_TAB_SERVICE_H_ + +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" + +class Browser; +class Profile; + +// PinnedTabService is responsible for updating preferences with the set of +// pinned tabs to restore at startup. PinnedTabService listens for the +// appropriate set of notifications to know it should update preferences. +class PinnedTabService : public NotificationObserver { + public: + explicit PinnedTabService(Profile* profile); + + private: + // Invoked when we're about to exit. + void GotExit(); + + // NotificationObserver. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + Profile* profile_; + + // If true we've seen an exit event (or the last browser is closing which + // triggers an exit) and can ignore all other events. + bool got_exiting_; + + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(PinnedTabService); +}; + +#endif // CHROME_BROWSER_TABS_PINNED_TAB_SERVICE_H_ |