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 23 using content::NavigationEntry; 24 25 // Key used in dictionaries for the app id. 26 static const char kAppID[] = "app_id"; 27 28 // Key used in dictionaries for the url. 29 static const char kURL[] = "url"; 30 31 // Returns true if |browser| has any pinned tabs. 32 static 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|. 42 static 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. 52 static 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. 81 static 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. 89 static 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 104 void PinnedTabCodec::RegisterProfilePrefs( 105 user_prefs::PrefRegistrySyncable* registry) { 106 registry->RegisterListPref(prefs::kPinnedTabs, 107 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 108 } 109 110 // static 111 void 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 128 void 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 142 StartupTabs 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 150 StartupTabs 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