Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(902)

Side by Side Diff: chrome/browser/ui/startup/default_browser_prompt.cc

Issue 1338423004: Use the DefaultBrowserWorker to check/set Chrome as the default browser. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@defbrow
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/startup/default_browser_prompt.h" 5 #include "chrome/browser/ui/startup/default_browser_prompt.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/memory/weak_ptr.h" 10 #include "base/memory/weak_ptr.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/prefs/pref_registry_simple.h" 12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/pref_service.h" 13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h" 14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h" 15 #include "base/thread_task_runner_handle.h"
16 #include "base/version.h" 16 #include "base/version.h"
17 #include "chrome/browser/browser_process.h" 17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/first_run/first_run.h"
19 #include "chrome/browser/infobars/infobar_service.h" 18 #include "chrome/browser/infobars/infobar_service.h"
20 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_manager.h" 20 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/shell_integration.h" 21 #include "chrome/browser/shell_integration.h"
23 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_finder.h" 23 #include "chrome/browser/ui/browser_finder.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h" 24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/common/pref_names.h" 25 #include "chrome/common/pref_names.h"
27 #include "chrome/grit/chromium_strings.h" 26 #include "chrome/grit/chromium_strings.h"
28 #include "chrome/grit/generated_resources.h" 27 #include "chrome/grit/generated_resources.h"
29 #include "chrome/installer/util/master_preferences.h"
30 #include "chrome/installer/util/master_preferences_constants.h"
31 #include "components/infobars/core/confirm_infobar_delegate.h" 28 #include "components/infobars/core/confirm_infobar_delegate.h"
32 #include "components/infobars/core/infobar.h" 29 #include "components/infobars/core/infobar.h"
33 #include "components/version_info/version_info.h" 30 #include "components/version_info/version_info.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/navigation_details.h" 31 #include "content/public/browser/navigation_details.h"
36 #include "content/public/browser/web_contents.h" 32 #include "content/public/browser/web_contents.h"
37 #include "grit/theme_resources.h" 33 #include "grit/theme_resources.h"
38 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/l10n/l10n_util.h"
39 35
40 36
41 namespace { 37 namespace {
42 38
43 // Calls the appropriate function for setting Chrome as the default browser. 39 // A ShellIntegration::DefaultWebClientObserver that records user metrics for
44 // This requires IO access (registry) and may result in interaction with a 40 // the result of making Chrome the default browser.
45 // modal system UI. 41 class SetDefaultBrowserObserver
46 void SetChromeAsDefaultBrowser(bool interactive_flow, PrefService* prefs) { 42 : public ShellIntegration::DefaultWebClientObserver {
47 if (interactive_flow) { 43 public:
44 SetDefaultBrowserObserver();
45 ~SetDefaultBrowserObserver() override;
46
47 void SetDefaultWebClientUIState(
Peter Kasting 2015/09/22 21:17:25 Nit: I would make these private if they aren't cal
grt (UTC plus 2) 2015/09/23 13:57:49 Done here and in CheckDefaultBrowserObserver.
48 ShellIntegration::DefaultWebClientUIState state) override;
49 void OnSetAsDefaultConcluded(bool succeeded) override;
50 bool IsOwnedByWorker() override;
51 bool IsInteractiveSetDefaultPermitted() override;
52
53 private:
54 // True if an interactive flow will be used (i.e., Windows 8+).
55 bool interactive_;
56
57 // The result of the call to ShellIntegration::SetAsDefaultBrowser() or
58 // ShellIntegration::SetAsDefaultBrowserInteractive().
59 bool interaction_succeeded_ = false;
60
61 DISALLOW_COPY_AND_ASSIGN(SetDefaultBrowserObserver);
62 };
63
64 SetDefaultBrowserObserver::SetDefaultBrowserObserver()
65 : interactive_(ShellIntegration::CanSetAsDefaultBrowser() ==
66 ShellIntegration::SET_DEFAULT_INTERACTIVE) {
67 // Log that an attempt is about to be made to set one way or the other.
68 if (interactive_)
48 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUI", true); 69 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUI", true);
49 if (!ShellIntegration::SetAsDefaultBrowserInteractive()) { 70 else
50 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUIFailed", true);
51 } else if (ShellIntegration::GetDefaultBrowser() ==
52 ShellIntegration::NOT_DEFAULT) {
53 // If the interaction succeeded but we are still not the default browser
54 // it likely means the user simply selected another browser from the
55 // panel. We will respect this choice and write it down as 'no, thanks'.
56 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true);
57 }
58 } else {
59 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefault", true); 71 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefault", true);
60 ShellIntegration::SetAsDefaultBrowser(); 72 }
73
74 SetDefaultBrowserObserver::~SetDefaultBrowserObserver() {}
75
76 void SetDefaultBrowserObserver::SetDefaultWebClientUIState(
77 ShellIntegration::DefaultWebClientUIState state) {
78 if (interactive_ && interaction_succeeded_ &&
79 state == ShellIntegration::STATE_NOT_DEFAULT) {
80 // The interactive flow succeeded, yet Chrome is not the default browser.
81 // This likely means that the user selected another browser from the panel.
82 // Consider this the same as canceling the infobar.
83 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true);
61 } 84 }
62 } 85 }
63 86
87 void SetDefaultBrowserObserver::OnSetAsDefaultConcluded(bool succeeded) {
88 interaction_succeeded_ = succeeded;
89 if (interactive_ && !succeeded) {
90 // Log that the interactive flow failed.
91 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUIFailed", true);
92 }
93 }
94
95 bool SetDefaultBrowserObserver::IsOwnedByWorker() {
96 // Instruct the DefaultBrowserWorker to delete this instance when it is done.
97 return true;
98 }
99
100 bool SetDefaultBrowserObserver::IsInteractiveSetDefaultPermitted() {
101 return true;
102 }
103
64 // The delegate for the infobar shown when Chrome is not the default browser. 104 // The delegate for the infobar shown when Chrome is not the default browser.
65 class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate { 105 class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate {
66 public: 106 public:
67 // Creates a default browser infobar and delegate and adds the infobar to 107 // Creates a default browser infobar and delegate and adds the infobar to
68 // |infobar_service|. 108 // |infobar_service|.
69 static void Create(InfoBarService* infobar_service, 109 static void Create(InfoBarService* infobar_service, PrefService* prefs);
70 PrefService* prefs,
71 bool interactive_flow_required);
72 110
73 private: 111 private:
74 DefaultBrowserInfoBarDelegate(PrefService* prefs, 112 explicit DefaultBrowserInfoBarDelegate(PrefService* prefs);
75 bool interactive_flow_required);
76 ~DefaultBrowserInfoBarDelegate() override; 113 ~DefaultBrowserInfoBarDelegate() override;
77 114
78 void AllowExpiry() { should_expire_ = true; } 115 void AllowExpiry() { should_expire_ = true; }
79 116
80 // ConfirmInfoBarDelegate: 117 // ConfirmInfoBarDelegate:
81 int GetIconId() const override; 118 int GetIconId() const override;
82 bool ShouldExpire(const NavigationDetails& details) const override; 119 bool ShouldExpire(const NavigationDetails& details) const override;
83 base::string16 GetMessageText() const override; 120 base::string16 GetMessageText() const override;
84 base::string16 GetButtonLabel(InfoBarButton button) const override; 121 base::string16 GetButtonLabel(InfoBarButton button) const override;
85 bool OKButtonTriggersUACPrompt() const override; 122 bool OKButtonTriggersUACPrompt() const override;
86 bool Accept() override; 123 bool Accept() override;
87 bool Cancel() override; 124 bool Cancel() override;
88 125
89 // The prefs to use. 126 // The prefs to use.
90 PrefService* prefs_; 127 PrefService* prefs_;
91 128
92 // Whether the user clicked one of the buttons. 129 // Whether the user clicked one of the buttons.
93 bool action_taken_; 130 bool action_taken_;
94 131
95 // Whether the info-bar should be dismissed on the next navigation. 132 // Whether the info-bar should be dismissed on the next navigation.
96 bool should_expire_; 133 bool should_expire_;
97 134
98 // Whether changing the default application will require entering the
99 // modal-UI flow.
100 const bool interactive_flow_required_;
101
102 // Used to delay the expiration of the info-bar. 135 // Used to delay the expiration of the info-bar.
103 base::WeakPtrFactory<DefaultBrowserInfoBarDelegate> weak_factory_; 136 base::WeakPtrFactory<DefaultBrowserInfoBarDelegate> weak_factory_;
104 137
105 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate); 138 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate);
106 }; 139 };
107 140
108 // static 141 // static
109 void DefaultBrowserInfoBarDelegate::Create(InfoBarService* infobar_service, 142 void DefaultBrowserInfoBarDelegate::Create(InfoBarService* infobar_service,
110 PrefService* prefs, 143 PrefService* prefs) {
111 bool interactive_flow_required) { 144 infobar_service->AddInfoBar(
112 infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar( 145 infobar_service->CreateConfirmInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
113 scoped_ptr<ConfirmInfoBarDelegate>(new DefaultBrowserInfoBarDelegate( 146 new DefaultBrowserInfoBarDelegate(prefs))));
114 prefs, interactive_flow_required))));
115 } 147 }
116 148
117 DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate( 149 DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate(PrefService* prefs)
118 PrefService* prefs,
119 bool interactive_flow_required)
120 : ConfirmInfoBarDelegate(), 150 : ConfirmInfoBarDelegate(),
121 prefs_(prefs), 151 prefs_(prefs),
122 action_taken_(false), 152 action_taken_(false),
123 should_expire_(false), 153 should_expire_(false),
124 interactive_flow_required_(interactive_flow_required),
125 weak_factory_(this) { 154 weak_factory_(this) {
126 // We want the info-bar to stick-around for few seconds and then be hidden 155 // We want the info-bar to stick-around for few seconds and then be hidden
127 // on the next navigation after that. 156 // on the next navigation after that.
128 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 157 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
129 FROM_HERE, base::Bind(&DefaultBrowserInfoBarDelegate::AllowExpiry, 158 FROM_HERE, base::Bind(&DefaultBrowserInfoBarDelegate::AllowExpiry,
130 weak_factory_.GetWeakPtr()), 159 weak_factory_.GetWeakPtr()),
131 base::TimeDelta::FromSeconds(8)); 160 base::TimeDelta::FromSeconds(8));
132 } 161 }
133 162
134 DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() { 163 DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() {
(...skipping 23 matching lines...) Expand all
158 187
159 // Setting an app as the default browser doesn't require elevation directly, but 188 // Setting an app as the default browser doesn't require elevation directly, but
160 // it does require registering it as the protocol handler for "http", so if 189 // it does require registering it as the protocol handler for "http", so if
161 // protocol registration in general requires elevation, this does as well. 190 // protocol registration in general requires elevation, this does as well.
162 bool DefaultBrowserInfoBarDelegate::OKButtonTriggersUACPrompt() const { 191 bool DefaultBrowserInfoBarDelegate::OKButtonTriggersUACPrompt() const {
163 return ShellIntegration::IsElevationNeededForSettingDefaultProtocolClient(); 192 return ShellIntegration::IsElevationNeededForSettingDefaultProtocolClient();
164 } 193 }
165 194
166 bool DefaultBrowserInfoBarDelegate::Accept() { 195 bool DefaultBrowserInfoBarDelegate::Accept() {
167 action_taken_ = true; 196 action_taken_ = true;
168 content::BrowserThread::PostTask( 197 scoped_refptr<ShellIntegration::DefaultBrowserWorker>(
169 content::BrowserThread::FILE, FROM_HERE, 198 new ShellIntegration::DefaultBrowserWorker(new SetDefaultBrowserObserver))
170 base::Bind(&SetChromeAsDefaultBrowser, interactive_flow_required_, 199 ->StartSetAsDefault();
171 prefs_));
172
173 return true; 200 return true;
174 } 201 }
175 202
176 bool DefaultBrowserInfoBarDelegate::Cancel() { 203 bool DefaultBrowserInfoBarDelegate::Cancel() {
177 action_taken_ = true; 204 action_taken_ = true;
178 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true); 205 UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true);
179 // User clicked "Don't ask me again", remember that. 206 // User clicked "Don't ask me again", remember that.
180 prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false); 207 prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false);
181 return true; 208 return true;
182 } 209 }
183 210
184 void NotifyNotDefaultBrowserCallback(chrome::HostDesktopType desktop_type) { 211 // A ShellIntegration::DefaultWebClientObserver that handles the check to
185 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type); 212 // determine whether or not to show the default browser prompt. If Chrome is the
213 // default browser, then the kCheckDefaultBrowser pref is reset. Otherwise, the
214 // prompt is shown.
215 class CheckDefaultBrowserObserver
216 : public ShellIntegration::DefaultWebClientObserver {
217 public:
218 CheckDefaultBrowserObserver(const base::FilePath& profile_path,
219 bool show_prompt,
220 chrome::HostDesktopType desktop_type);
221 ~CheckDefaultBrowserObserver() override;
Peter Kasting 2015/09/22 21:17:25 Nit: Blank line below this for consistency with th
grt (UTC plus 2) 2015/09/23 13:57:49 Done.
222 void SetDefaultWebClientUIState(
223 ShellIntegration::DefaultWebClientUIState state) override;
224 bool IsOwnedByWorker() override;
225
226 private:
227 void ResetCheckDefaultBrowserPref();
228 void ShowPrompt();
229
230 // The path to the profile for which the prompt is to be shown.
231 base::FilePath profile_path_;
232
233 // True if the prompt is to be shown if Chrome is not the default browser.
234 bool show_prompt_;
235 chrome::HostDesktopType desktop_type_;
236
237 DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserObserver);
238 };
239
240 CheckDefaultBrowserObserver::CheckDefaultBrowserObserver(
241 const base::FilePath& profile_path,
242 bool show_prompt,
243 chrome::HostDesktopType desktop_type)
244 : profile_path_(profile_path),
245 show_prompt_(show_prompt),
246 desktop_type_(desktop_type) {}
247
248 CheckDefaultBrowserObserver::~CheckDefaultBrowserObserver() {}
249
250 void CheckDefaultBrowserObserver::SetDefaultWebClientUIState(
251 ShellIntegration::DefaultWebClientUIState state) {
252 if (state == ShellIntegration::STATE_IS_DEFAULT) {
253 // Notify the user in the future if Chrome ceases to be the user's chosen
254 // default browser.
255 ResetCheckDefaultBrowserPref();
256 } else if (show_prompt_ && state == ShellIntegration::STATE_NOT_DEFAULT &&
257 ShellIntegration::CanSetAsDefaultBrowser() !=
258 ShellIntegration::SET_DEFAULT_NOT_ALLOWED) {
259 ShowPrompt();
260 }
261 }
262
263 bool CheckDefaultBrowserObserver::IsOwnedByWorker() {
264 // Instruct the DefaultBrowserWorker to delete this instance when it is done.
265 return true;
266 }
267
268 void CheckDefaultBrowserObserver::ResetCheckDefaultBrowserPref() {
269 Profile* profile =
270 g_browser_process->profile_manager()->GetProfileByPath(profile_path_);
271 if (profile)
272 profile->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, true);
273 }
274
275 void CheckDefaultBrowserObserver::ShowPrompt() {
276 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type_);
186 if (!browser) 277 if (!browser)
187 return; // Reached during ui tests. 278 return; // Reached during ui tests.
188 279
189 // In ChromeBot tests, there might be a race. This line appears to get 280 // In ChromeBot tests, there might be a race. This line appears to get
190 // called during shutdown and |tab| can be NULL. 281 // called during shutdown and |tab| can be NULL.
191 content::WebContents* web_contents = 282 content::WebContents* web_contents =
192 browser->tab_strip_model()->GetActiveWebContents(); 283 browser->tab_strip_model()->GetActiveWebContents();
193 if (!web_contents) 284 if (!web_contents)
194 return; 285 return;
195 286
196 DefaultBrowserInfoBarDelegate::Create( 287 DefaultBrowserInfoBarDelegate::Create(
197 InfoBarService::FromWebContents(web_contents), 288 InfoBarService::FromWebContents(web_contents),
198 Profile::FromBrowserContext( 289 Profile::FromBrowserContext(web_contents->GetBrowserContext())
199 web_contents->GetBrowserContext())->GetPrefs(), 290 ->GetPrefs());
200 (ShellIntegration::CanSetAsDefaultBrowser() ==
201 ShellIntegration::SET_DEFAULT_INTERACTIVE));
202 }
203
204 void ResetCheckDefaultBrowserPrefOnUIThread(
205 const base::FilePath& profile_path) {
206 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
207 Profile* profile =
208 g_browser_process->profile_manager()->GetProfileByPath(profile_path);
209 if (profile)
210 profile->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, true);
211 }
212
213 void CheckDefaultBrowserOnFileThread(const base::FilePath& profile_path,
214 bool show_prompt,
215 chrome::HostDesktopType desktop_type) {
216 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
217 ShellIntegration::DefaultWebClientState state =
218 ShellIntegration::GetDefaultBrowser();
219 if (state == ShellIntegration::IS_DEFAULT) {
220 // Notify the user in the future if Chrome ceases to be the user's chosen
221 // default browser.
222 content::BrowserThread::PostTask(
223 content::BrowserThread::UI, FROM_HERE,
224 base::Bind(&ResetCheckDefaultBrowserPrefOnUIThread, profile_path));
225 } else if (show_prompt && state == ShellIntegration::NOT_DEFAULT) {
226 ShellIntegration::DefaultWebClientSetPermission default_change_mode =
227 ShellIntegration::CanSetAsDefaultBrowser();
228
229 if (default_change_mode != ShellIntegration::SET_DEFAULT_NOT_ALLOWED) {
230 content::BrowserThread::PostTask(
231 content::BrowserThread::UI, FROM_HERE,
232 base::Bind(&NotifyNotDefaultBrowserCallback, desktop_type));
233 }
234 }
235 } 291 }
236 292
237 } // namespace 293 } // namespace
238 294
239 namespace chrome { 295 namespace chrome {
240 296
241 void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) { 297 void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) {
242 registry->RegisterStringPref( 298 registry->RegisterStringPref(
243 prefs::kBrowserSuppressDefaultBrowserPrompt, std::string()); 299 prefs::kBrowserSuppressDefaultBrowserPrompt, std::string());
244 } 300 }
(...skipping 19 matching lines...) Expand all
264 g_browser_process->local_state()->GetString( 320 g_browser_process->local_state()->GetString(
265 prefs::kBrowserSuppressDefaultBrowserPrompt); 321 prefs::kBrowserSuppressDefaultBrowserPrompt);
266 const Version disable_version(disable_version_string); 322 const Version disable_version(disable_version_string);
267 DCHECK(disable_version_string.empty() || disable_version.IsValid()); 323 DCHECK(disable_version_string.empty() || disable_version.IsValid());
268 if (disable_version.IsValid()) { 324 if (disable_version.IsValid()) {
269 if (disable_version.Equals(Version(version_info::GetVersionNumber()))) 325 if (disable_version.Equals(Version(version_info::GetVersionNumber())))
270 show_prompt = false; 326 show_prompt = false;
271 } 327 }
272 } 328 }
273 329
274 content::BrowserThread::PostTask( 330 scoped_refptr<ShellIntegration::DefaultBrowserWorker>(
275 content::BrowserThread::FILE, FROM_HERE, 331 new ShellIntegration::DefaultBrowserWorker(
276 base::Bind(&CheckDefaultBrowserOnFileThread, profile->GetPath(), 332 new CheckDefaultBrowserObserver(profile->GetPath(), show_prompt,
277 show_prompt, desktop_type)); 333 desktop_type)))
334 ->StartCheckIsDefault();
278 } 335 }
279 336
280 #if !defined(OS_WIN) 337 #if !defined(OS_WIN)
281 bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) { 338 bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) {
282 return false; 339 return false;
283 } 340 }
284 #endif 341 #endif
285 342
286 } // namespace chrome 343 } // namespace chrome
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698