1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/tabs/pinned_tab_codec.h" 6 7#include "base/prefs/pref_service.h" 8#include "base/prefs/scoped_user_pref_update.h" 9#include "base/values.h" 10#include "chrome/browser/extensions/tab_helper.h" 11#include "chrome/browser/profiles/profile.h" 12#include "chrome/browser/ui/browser.h" 13#include "chrome/browser/ui/browser_iterator.h" 14#include "chrome/browser/ui/browser_list.h" 15#include "chrome/browser/ui/tabs/tab_strip_model.h" 16#include "chrome/common/extensions/manifest_handlers/app_launch_info.h" 17#include "chrome/common/pref_names.h" 18#include "components/user_prefs/pref_registry_syncable.h" 19#include "content/public/browser/navigation_entry.h" 20#include "content/public/browser/web_contents.h" 21#include "extensions/common/extension.h" 22 23using content::NavigationEntry; 24 25// Key used in dictionaries for the app id. 26static const char kAppID[] = "app_id"; 27 28// Key used in dictionaries for the url. 29static const char kURL[] = "url"; 30 31// Returns true if |browser| has any pinned tabs. 32static bool HasPinnedTabs(Browser* browser) { 33 TabStripModel* tab_model = browser->tab_strip_model(); 34 for (int i = 0; i < tab_model->count(); ++i) { 35 if (tab_model->IsTabPinned(i)) 36 return true; 37 } 38 return false; 39} 40 41// Adds a DictionaryValue to |values| representing |tab|. 42static void EncodeTab(const StartupTab& tab, ListValue* values) { 43 scoped_ptr<DictionaryValue> value(new DictionaryValue); 44 value->SetString(kURL, tab.url.spec()); 45 if (tab.is_app) 46 value->SetString(kAppID, tab.app_id); 47 values->Append(value.release()); 48} 49 50// Adds a DictionaryValue to |values| representing the pinned tab at the 51// specified index. 52static void EncodePinnedTab(TabStripModel* model, 53 int index, 54 ListValue* values) { 55 scoped_ptr<DictionaryValue> value(new DictionaryValue()); 56 57 content::WebContents* web_contents = model->GetWebContentsAt(index); 58 if (model->IsAppTab(index)) { 59 const extensions::Extension* extension = 60 extensions::TabHelper::FromWebContents(web_contents)->extension_app(); 61 DCHECK(extension); 62 value->SetString(kAppID, extension->id()); 63 // For apps we use the launch url. We do this because the user is 64 // effectively restarting the app, so returning them to the app's launch 65 // page seems closest to what they expect. 66 value->SetString( 67 kURL, extensions::AppLaunchInfo::GetFullLaunchURL(extension).spec()); 68 values->Append(value.release()); 69 } else { 70 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); 71 if (!entry && web_contents->GetController().GetEntryCount()) 72 entry = web_contents->GetController().GetEntryAtIndex(0); 73 if (entry) { 74 value->SetString(kURL, entry->GetURL().spec()); 75 values->Append(value.release()); 76 } 77 } 78} 79 80// Invokes EncodePinnedTab for each pinned tab in browser. 81static void EncodePinnedTabs(Browser* browser, ListValue* values) { 82 TabStripModel* tab_model = browser->tab_strip_model(); 83 for (int i = 0; i < tab_model->count() && tab_model->IsTabPinned(i); ++i) 84 EncodePinnedTab(tab_model, i, values); 85} 86 87// Decodes the previously written values in |value| to |tab|, returning true 88// on success. 89static bool DecodeTab(const DictionaryValue& value, StartupTab* tab) { 90 tab->is_app = false; 91 92 std::string url_string; 93 if (!value.GetString(kURL, &url_string)) 94 return false; 95 tab->url = GURL(url_string); 96 97 if (value.GetString(kAppID, &(tab->app_id))) 98 tab->is_app = true; 99 100 return true; 101} 102 103// static 104void PinnedTabCodec::RegisterProfilePrefs( 105 user_prefs::PrefRegistrySyncable* registry) { 106 registry->RegisterListPref(prefs::kPinnedTabs, 107 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 108} 109 110// static 111void PinnedTabCodec::WritePinnedTabs(Profile* profile) { 112 PrefService* prefs = profile->GetPrefs(); 113 if (!prefs) 114 return; 115 116 ListValue values; 117 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 118 Browser* browser = *it; 119 if (browser->is_type_tabbed() && 120 browser->profile() == profile && HasPinnedTabs(browser)) { 121 EncodePinnedTabs(browser, &values); 122 } 123 } 124 prefs->Set(prefs::kPinnedTabs, values); 125} 126 127// static 128void PinnedTabCodec::WritePinnedTabs(Profile* profile, 129 const StartupTabs& tabs) { 130 PrefService* prefs = profile->GetPrefs(); 131 if (!prefs) 132 return; 133 134 ListPrefUpdate update(prefs, prefs::kPinnedTabs); 135 ListValue* values = update.Get(); 136 values->Clear(); 137 for (StartupTabs::const_iterator i = tabs.begin(); i != tabs.end(); ++i) 138 EncodeTab(*i, values); 139} 140 141// static 142StartupTabs PinnedTabCodec::ReadPinnedTabs(Profile* profile) { 143 PrefService* prefs = profile->GetPrefs(); 144 if (!prefs) 145 return StartupTabs(); 146 return ReadPinnedTabs(prefs->GetList(prefs::kPinnedTabs)); 147} 148 149// static 150StartupTabs PinnedTabCodec::ReadPinnedTabs(const base::Value* value) { 151 StartupTabs results; 152 153 const base::ListValue* tabs_list = NULL; 154 if (!value->GetAsList(&tabs_list)) 155 return results; 156 157 for (size_t i = 0, max = tabs_list->GetSize(); i < max; ++i) { 158 const base::DictionaryValue* tab_values = NULL; 159 if (tabs_list->GetDictionary(i, &tab_values)) { 160 StartupTab tab; 161 if (DecodeTab(*tab_values, &tab)) 162 results.push_back(tab); 163 } 164 } 165 return results; 166} 167