1// Copyright 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/browser_commands.h"
6
7#include "base/command_line.h"
8#include "base/metrics/histogram.h"
9#include "base/prefs/pref_service.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/app/chrome_command_ids.h"
12#include "chrome/browser/bookmarks/bookmark_model_factory.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/browsing_data/browsing_data_helper.h"
15#include "chrome/browser/browsing_data/browsing_data_remover.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/chrome_page_zoom.h"
18#include "chrome/browser/devtools/devtools_window.h"
19#include "chrome/browser/dom_distiller/tab_utils.h"
20#include "chrome/browser/extensions/api/commands/command_service.h"
21#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
22#include "chrome/browser/extensions/tab_helper.h"
23#include "chrome/browser/favicon/favicon_tab_helper.h"
24#include "chrome/browser/lifetime/application_lifetime.h"
25#include "chrome/browser/platform_util.h"
26#include "chrome/browser/prefs/incognito_mode_prefs.h"
27#include "chrome/browser/profiles/profile.h"
28#include "chrome/browser/rlz/rlz.h"
29#include "chrome/browser/search/search.h"
30#include "chrome/browser/sessions/session_service_factory.h"
31#include "chrome/browser/sessions/tab_restore_service.h"
32#include "chrome/browser/sessions/tab_restore_service_delegate.h"
33#include "chrome/browser/sessions/tab_restore_service_factory.h"
34#include "chrome/browser/signin/signin_header_helper.h"
35#include "chrome/browser/translate/chrome_translate_client.h"
36#include "chrome/browser/ui/accelerator_utils.h"
37#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
38#include "chrome/browser/ui/browser.h"
39#include "chrome/browser/ui/browser_command_controller.h"
40#include "chrome/browser/ui/browser_dialogs.h"
41#include "chrome/browser/ui/browser_instant_controller.h"
42#include "chrome/browser/ui/browser_tab_restore_service_delegate.h"
43#include "chrome/browser/ui/browser_tabstrip.h"
44#include "chrome/browser/ui/browser_window.h"
45#include "chrome/browser/ui/chrome_pages.h"
46#include "chrome/browser/ui/find_bar/find_bar.h"
47#include "chrome/browser/ui/find_bar/find_bar_controller.h"
48#include "chrome/browser/ui/find_bar/find_tab_helper.h"
49#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
50#include "chrome/browser/ui/location_bar/location_bar.h"
51#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
52#include "chrome/browser/ui/search/search_tab_helper.h"
53#include "chrome/browser/ui/status_bubble.h"
54#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
55#include "chrome/browser/ui/tabs/tab_strip_model.h"
56#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
57#include "chrome/browser/ui/zoom/zoom_controller.h"
58#include "chrome/browser/upgrade_detector.h"
59#include "chrome/browser/web_applications/web_app.h"
60#include "chrome/common/chrome_switches.h"
61#include "chrome/common/chrome_version_info.h"
62#include "chrome/common/content_restriction.h"
63#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
64#include "chrome/common/pref_names.h"
65#include "components/bookmarks/browser/bookmark_model.h"
66#include "components/bookmarks/browser/bookmark_utils.h"
67#include "components/google/core/browser/google_util.h"
68#include "components/translate/core/browser/language_state.h"
69#include "components/web_modal/popup_manager.h"
70#include "content/public/browser/devtools_agent_host.h"
71#include "content/public/browser/navigation_controller.h"
72#include "content/public/browser/navigation_entry.h"
73#include "content/public/browser/notification_service.h"
74#include "content/public/browser/page_navigator.h"
75#include "content/public/browser/render_view_host.h"
76#include "content/public/browser/render_widget_host_view.h"
77#include "content/public/browser/user_metrics.h"
78#include "content/public/browser/web_contents.h"
79#include "content/public/common/renderer_preferences.h"
80#include "content/public/common/url_constants.h"
81#include "content/public/common/url_utils.h"
82#include "content/public/common/user_agent.h"
83#include "net/base/escape.h"
84#include "ui/events/keycodes/keyboard_codes.h"
85
86#if defined(OS_WIN)
87#include "chrome/browser/ui/metro_pin_tab_helper_win.h"
88#endif
89
90#if defined(ENABLE_EXTENSIONS)
91#include "extensions/browser/extension_registry.h"
92#include "extensions/browser/extension_system.h"
93#include "extensions/common/extension.h"
94#include "extensions/common/extension_set.h"
95#endif
96
97#if defined(ENABLE_PRINTING)
98#if defined(ENABLE_FULL_PRINTING)
99#include "chrome/browser/printing/print_preview_dialog_controller.h"
100#include "chrome/browser/printing/print_view_manager.h"
101#else
102#include "chrome/browser/printing/print_view_manager_basic.h"
103#endif  // defined(ENABLE_FULL_PRINTING)
104#endif  // defined(ENABLE_PRINTING)
105
106namespace {
107const char kOsOverrideForTabletSite[] = "Linux; Android 4.0.3";
108}
109
110using base::UserMetricsAction;
111using content::NavigationController;
112using content::NavigationEntry;
113using content::OpenURLParams;
114using content::Referrer;
115using content::SSLStatus;
116using content::WebContents;
117
118namespace chrome {
119namespace {
120
121bool CanBookmarkCurrentPageInternal(const Browser* browser,
122                                    bool check_remove_bookmark_ui) {
123  BookmarkModel* model =
124      BookmarkModelFactory::GetForProfile(browser->profile());
125  return browser_defaults::bookmarks_enabled &&
126      browser->profile()->GetPrefs()->GetBoolean(
127          bookmarks::prefs::kEditBookmarksEnabled) &&
128      model && model->loaded() && browser->is_type_tabbed() &&
129      (!check_remove_bookmark_ui ||
130           !chrome::ShouldRemoveBookmarkThisPageUI(browser->profile()));
131}
132
133bool GetBookmarkOverrideCommand(
134    Profile* profile,
135    const extensions::Extension** extension,
136    extensions::Command* command,
137    extensions::CommandService::ExtensionCommandType* command_type) {
138#if defined(ENABLE_EXTENSIONS)
139  DCHECK(extension);
140  DCHECK(command);
141  DCHECK(command_type);
142
143  ui::Accelerator bookmark_page_accelerator =
144      chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE);
145  if (bookmark_page_accelerator.key_code() == ui::VKEY_UNKNOWN)
146    return false;
147
148  extensions::CommandService* command_service =
149      extensions::CommandService::Get(profile);
150  const extensions::ExtensionSet& extension_set =
151      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
152  for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
153       i != extension_set.end();
154       ++i) {
155    extensions::Command prospective_command;
156    extensions::CommandService::ExtensionCommandType prospective_command_type;
157    if (command_service->GetBoundExtensionCommand((*i)->id(),
158                                                  bookmark_page_accelerator,
159                                                  &prospective_command,
160                                                  &prospective_command_type)) {
161      *extension = i->get();
162      *command = prospective_command;
163      *command_type = prospective_command_type;
164      return true;
165    }
166  }
167#endif
168
169  return false;
170}
171
172void BookmarkCurrentPageInternal(Browser* browser) {
173  content::RecordAction(UserMetricsAction("Star"));
174
175  BookmarkModel* model =
176      BookmarkModelFactory::GetForProfile(browser->profile());
177  if (!model || !model->loaded())
178    return;  // Ignore requests until bookmarks are loaded.
179
180  GURL url;
181  base::string16 title;
182  WebContents* web_contents =
183      browser->tab_strip_model()->GetActiveWebContents();
184  GetURLAndTitleToBookmark(web_contents, &url, &title);
185  bool is_bookmarked_by_any = model->IsBookmarked(url);
186  if (!is_bookmarked_by_any &&
187      web_contents->GetBrowserContext()->IsOffTheRecord()) {
188    // If we're incognito the favicon may not have been saved. Save it now
189    // so that bookmarks have an icon for the page.
190    FaviconTabHelper::FromWebContents(web_contents)->SaveFavicon();
191  }
192  bool was_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url);
193  bookmarks::AddIfNotBookmarked(model, url, title);
194  bool is_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url);
195  // Make sure the model actually added a bookmark before showing the star. A
196  // bookmark isn't created if the url is invalid.
197  if (browser->window()->IsActive() && is_bookmarked_by_user) {
198    // Only show the bubble if the window is active, otherwise we may get into
199    // weird situations where the bubble is deleted as soon as it is shown.
200    browser->window()->ShowBookmarkBubble(url, was_bookmarked_by_user);
201  }
202}
203
204// Based on |disposition|, creates a new tab as necessary, and returns the
205// appropriate tab to navigate.  If that tab is the current tab, reverts the
206// location bar contents, since all browser-UI-triggered navigations should
207// revert any omnibox edits in the current tab.
208WebContents* GetTabAndRevertIfNecessary(Browser* browser,
209                                        WindowOpenDisposition disposition) {
210  WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
211  switch (disposition) {
212    case NEW_FOREGROUND_TAB:
213    case NEW_BACKGROUND_TAB: {
214      WebContents* new_tab = current_tab->Clone();
215      browser->tab_strip_model()->AddWebContents(
216          new_tab, -1, ui::PAGE_TRANSITION_LINK,
217          (disposition == NEW_FOREGROUND_TAB) ?
218              TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE);
219      return new_tab;
220    }
221    case NEW_WINDOW: {
222      WebContents* new_tab = current_tab->Clone();
223      Browser* new_browser = new Browser(Browser::CreateParams(
224          browser->profile(), browser->host_desktop_type()));
225      new_browser->tab_strip_model()->AddWebContents(
226          new_tab, -1, ui::PAGE_TRANSITION_LINK,
227          TabStripModel::ADD_ACTIVE);
228      new_browser->window()->Show();
229      return new_tab;
230    }
231    default:
232      browser->window()->GetLocationBar()->Revert();
233      return current_tab;
234  }
235}
236
237void ReloadInternal(Browser* browser,
238                    WindowOpenDisposition disposition,
239                    bool ignore_cache) {
240  // As this is caused by a user action, give the focus to the page.
241  //
242  // Also notify RenderViewHostDelegate of the user gesture; this is
243  // normally done in Browser::Navigate, but a reload bypasses Navigate.
244  WebContents* new_tab = GetTabAndRevertIfNecessary(browser, disposition);
245  new_tab->UserGestureDone();
246  if (!new_tab->FocusLocationBarByDefault())
247    new_tab->Focus();
248  if (ignore_cache)
249    new_tab->GetController().ReloadIgnoringCache(true);
250  else
251    new_tab->GetController().Reload(true);
252}
253
254bool IsShowingWebContentsModalDialog(Browser* browser) {
255  WebContents* web_contents =
256      browser->tab_strip_model()->GetActiveWebContents();
257  if (!web_contents)
258    return false;
259
260  // In test code we may not have a popup manager.
261  if (!browser->popup_manager())
262    return false;
263
264  // TODO(gbillock): This is currently called in production by the CanPrint
265  // method, and may be too restrictive if we allow print preview to overlap.
266  // Re-assess how to queue print preview after we know more about popup
267  // management policy.
268  return browser->popup_manager()->IsWebModalDialogActive(web_contents);
269}
270
271bool PrintPreviewShowing(const Browser* browser) {
272#if defined(ENABLE_FULL_PRINTING)
273  WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
274  printing::PrintPreviewDialogController* controller =
275      printing::PrintPreviewDialogController::GetInstance();
276  return controller && (controller->GetPrintPreviewForContents(contents) ||
277                        controller->is_creating_print_preview_dialog());
278#else
279  return false;
280#endif
281}
282
283}  // namespace
284
285bool IsCommandEnabled(Browser* browser, int command) {
286  return browser->command_controller()->command_updater()->IsCommandEnabled(
287      command);
288}
289
290bool SupportsCommand(Browser* browser, int command) {
291  return browser->command_controller()->command_updater()->SupportsCommand(
292      command);
293}
294
295bool ExecuteCommand(Browser* browser, int command) {
296  return browser->command_controller()->command_updater()->ExecuteCommand(
297      command);
298}
299
300bool ExecuteCommandWithDisposition(Browser* browser,
301                                   int command,
302                                   WindowOpenDisposition disposition) {
303  return browser->command_controller()->command_updater()->
304      ExecuteCommandWithDisposition(command, disposition);
305}
306
307void UpdateCommandEnabled(Browser* browser, int command, bool enabled) {
308  browser->command_controller()->command_updater()->UpdateCommandEnabled(
309      command, enabled);
310}
311
312void AddCommandObserver(Browser* browser,
313                        int command,
314                        CommandObserver* observer) {
315  browser->command_controller()->command_updater()->AddCommandObserver(
316      command, observer);
317}
318
319void RemoveCommandObserver(Browser* browser,
320                           int command,
321                           CommandObserver* observer) {
322  browser->command_controller()->command_updater()->RemoveCommandObserver(
323      command, observer);
324}
325
326int GetContentRestrictions(const Browser* browser) {
327  int content_restrictions = 0;
328  WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
329  if (current_tab) {
330    CoreTabHelper* core_tab_helper =
331        CoreTabHelper::FromWebContents(current_tab);
332    content_restrictions = core_tab_helper->content_restrictions();
333    NavigationEntry* last_committed_entry =
334        current_tab->GetController().GetLastCommittedEntry();
335    if (!content::IsSavableURL(
336            last_committed_entry ? last_committed_entry->GetURL() : GURL()) ||
337        current_tab->ShowingInterstitialPage())
338      content_restrictions |= CONTENT_RESTRICTION_SAVE;
339    if (current_tab->ShowingInterstitialPage())
340      content_restrictions |= CONTENT_RESTRICTION_PRINT;
341  }
342  return content_restrictions;
343}
344
345void NewEmptyWindow(Profile* profile, HostDesktopType desktop_type) {
346  bool incognito = profile->IsOffTheRecord();
347  PrefService* prefs = profile->GetPrefs();
348  if (incognito) {
349    if (IncognitoModePrefs::GetAvailability(prefs) ==
350          IncognitoModePrefs::DISABLED) {
351      incognito = false;
352    }
353  } else if (profile->IsGuestSession() ||
354      (browser_defaults::kAlwaysOpenIncognitoWindow &&
355      IncognitoModePrefs::ShouldLaunchIncognito(
356          *CommandLine::ForCurrentProcess(), prefs))) {
357    incognito = true;
358  }
359
360  if (incognito) {
361    content::RecordAction(UserMetricsAction("NewIncognitoWindow"));
362    OpenEmptyWindow(profile->GetOffTheRecordProfile(), desktop_type);
363  } else {
364    content::RecordAction(UserMetricsAction("NewWindow"));
365    SessionService* session_service =
366        SessionServiceFactory::GetForProfileForSessionRestore(
367            profile->GetOriginalProfile());
368    if (!session_service ||
369        !session_service->RestoreIfNecessary(std::vector<GURL>())) {
370      OpenEmptyWindow(profile->GetOriginalProfile(), desktop_type);
371    }
372  }
373}
374
375Browser* OpenEmptyWindow(Profile* profile, HostDesktopType desktop_type) {
376  Browser* browser = new Browser(
377      Browser::CreateParams(Browser::TYPE_TABBED, profile, desktop_type));
378  AddTabAt(browser, GURL(), -1, true);
379  browser->window()->Show();
380  return browser;
381}
382
383void OpenWindowWithRestoredTabs(Profile* profile,
384                                HostDesktopType host_desktop_type) {
385  TabRestoreService* service = TabRestoreServiceFactory::GetForProfile(profile);
386  if (service)
387    service->RestoreMostRecentEntry(NULL, host_desktop_type);
388}
389
390void OpenURLOffTheRecord(Profile* profile,
391                         const GURL& url,
392                         chrome::HostDesktopType desktop_type) {
393  ScopedTabbedBrowserDisplayer displayer(profile->GetOffTheRecordProfile(),
394                                         desktop_type);
395  AddSelectedTabWithURL(displayer.browser(), url,
396      ui::PAGE_TRANSITION_LINK);
397}
398
399bool CanGoBack(const Browser* browser) {
400  return browser->tab_strip_model()->GetActiveWebContents()->
401      GetController().CanGoBack();
402}
403
404void GoBack(Browser* browser, WindowOpenDisposition disposition) {
405  content::RecordAction(UserMetricsAction("Back"));
406
407  if (CanGoBack(browser)) {
408    WebContents* current_tab =
409        browser->tab_strip_model()->GetActiveWebContents();
410    WebContents* new_tab = GetTabAndRevertIfNecessary(browser, disposition);
411    // If we are on an interstitial page and clone the tab, it won't be copied
412    // to the new tab, so we don't need to go back.
413    if ((new_tab == current_tab) || !current_tab->ShowingInterstitialPage())
414      new_tab->GetController().GoBack();
415  }
416}
417
418bool CanGoForward(const Browser* browser) {
419  return browser->tab_strip_model()->GetActiveWebContents()->
420      GetController().CanGoForward();
421}
422
423void GoForward(Browser* browser, WindowOpenDisposition disposition) {
424  content::RecordAction(UserMetricsAction("Forward"));
425  if (CanGoForward(browser)) {
426    GetTabAndRevertIfNecessary(browser, disposition)->
427        GetController().GoForward();
428  }
429}
430
431bool NavigateToIndexWithDisposition(Browser* browser,
432                                    int index,
433                                    WindowOpenDisposition disposition) {
434  NavigationController* controller =
435      &GetTabAndRevertIfNecessary(browser, disposition)->GetController();
436  if (index < 0 || index >= controller->GetEntryCount())
437    return false;
438  controller->GoToIndex(index);
439  return true;
440}
441
442void Reload(Browser* browser, WindowOpenDisposition disposition) {
443  content::RecordAction(UserMetricsAction("Reload"));
444  ReloadInternal(browser, disposition, false);
445}
446
447void ReloadIgnoringCache(Browser* browser, WindowOpenDisposition disposition) {
448  content::RecordAction(UserMetricsAction("ReloadIgnoringCache"));
449  ReloadInternal(browser, disposition, true);
450}
451
452bool CanReload(const Browser* browser) {
453  return !browser->is_devtools();
454}
455
456void Home(Browser* browser, WindowOpenDisposition disposition) {
457  content::RecordAction(UserMetricsAction("Home"));
458
459  std::string extra_headers;
460#if defined(ENABLE_RLZ) && !defined(OS_IOS)
461  // If the home page is a Google home page, add the RLZ header to the request.
462  PrefService* pref_service = browser->profile()->GetPrefs();
463  if (pref_service) {
464    if (google_util::IsGoogleHomePageUrl(
465        GURL(pref_service->GetString(prefs::kHomePage)))) {
466      extra_headers = RLZTracker::GetAccessPointHttpHeader(
467          RLZTracker::ChromeHomePage());
468    }
469  }
470#endif  // defined(ENABLE_RLZ) && !defined(OS_IOS)
471
472  GURL url = browser->profile()->GetHomePage();
473
474#if defined(ENABLE_EXTENSIONS)
475  // Streamlined hosted apps should return to their launch page when the home
476  // button is pressed.
477  if (browser->is_app()) {
478    const extensions::Extension* extension =
479        extensions::ExtensionRegistry::Get(browser->profile())
480            ->GetExtensionById(
481                web_app::GetExtensionIdFromApplicationName(browser->app_name()),
482                extensions::ExtensionRegistry::EVERYTHING);
483    if (!extension)
484      return;
485
486    url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
487  }
488#endif
489
490  OpenURLParams params(
491      url, Referrer(), disposition,
492      ui::PageTransitionFromInt(
493          ui::PAGE_TRANSITION_AUTO_BOOKMARK |
494          ui::PAGE_TRANSITION_HOME_PAGE),
495      false);
496  params.extra_headers = extra_headers;
497  browser->OpenURL(params);
498}
499
500void OpenCurrentURL(Browser* browser) {
501  content::RecordAction(UserMetricsAction("LoadURL"));
502  LocationBar* location_bar = browser->window()->GetLocationBar();
503  if (!location_bar)
504    return;
505
506  GURL url(location_bar->GetDestinationURL());
507
508  ui::PageTransition page_transition = location_bar->GetPageTransition();
509  ui::PageTransition page_transition_without_qualifier(
510      ui::PageTransitionStripQualifier(page_transition));
511  WindowOpenDisposition open_disposition =
512      location_bar->GetWindowOpenDisposition();
513  // A PAGE_TRANSITION_TYPED means the user has typed a URL. We do not want to
514  // open URLs with instant_controller since in some cases it disregards it
515  // and performs a search instead. For example, when using CTRL-Enter, the
516  // location_bar is aware of the URL but instant is not.
517  // Instant should also not handle PAGE_TRANSITION_RELOAD because its knowledge
518  // of the omnibox text may be stale if the user focuses in the omnibox and
519  // presses enter without typing anything.
520  if (page_transition_without_qualifier != ui::PAGE_TRANSITION_TYPED &&
521      page_transition_without_qualifier != ui::PAGE_TRANSITION_RELOAD &&
522      browser->instant_controller() &&
523      browser->instant_controller()->OpenInstant(open_disposition, url))
524    return;
525
526  NavigateParams params(browser, url, page_transition);
527  params.disposition = open_disposition;
528  // Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least
529  // inherit the opener. In some cases the tabstrip will determine the group
530  // should be inherited, in which case the group is inherited instead of the
531  // opener.
532  params.tabstrip_add_types =
533      TabStripModel::ADD_FORCE_INDEX | TabStripModel::ADD_INHERIT_OPENER;
534  Navigate(&params);
535
536#if defined(ENABLE_EXTENSIONS)
537  DCHECK(extensions::ExtensionSystem::Get(
538      browser->profile())->extension_service());
539  const extensions::Extension* extension =
540      extensions::ExtensionRegistry::Get(browser->profile())
541          ->enabled_extensions().GetAppByURL(url);
542  if (extension) {
543    CoreAppLauncherHandler::RecordAppLaunchType(
544        extension_misc::APP_LAUNCH_OMNIBOX_LOCATION,
545        extension->GetType());
546  }
547#endif
548}
549
550void Stop(Browser* browser) {
551  content::RecordAction(UserMetricsAction("Stop"));
552  browser->tab_strip_model()->GetActiveWebContents()->Stop();
553}
554
555void NewWindow(Browser* browser) {
556  NewEmptyWindow(browser->profile()->GetOriginalProfile(),
557                 browser->host_desktop_type());
558}
559
560void NewIncognitoWindow(Browser* browser) {
561  NewEmptyWindow(browser->profile()->GetOffTheRecordProfile(),
562                 browser->host_desktop_type());
563}
564
565void CloseWindow(Browser* browser) {
566  content::RecordAction(UserMetricsAction("CloseWindow"));
567  browser->window()->Close();
568}
569
570void NewTab(Browser* browser) {
571  content::RecordAction(UserMetricsAction("NewTab"));
572  // TODO(asvitkine): This is invoked programmatically from several places.
573  // Audit the code and change it so that the histogram only gets collected for
574  // user-initiated commands.
575  UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_COMMAND,
576                            TabStripModel::NEW_TAB_ENUM_COUNT);
577
578  if (browser->is_type_tabbed()) {
579    AddTabAt(browser, GURL(), -1, true);
580    browser->tab_strip_model()->GetActiveWebContents()->RestoreFocus();
581  } else {
582    ScopedTabbedBrowserDisplayer displayer(browser->profile(),
583                                           browser->host_desktop_type());
584    Browser* b = displayer.browser();
585    AddTabAt(b, GURL(), -1, true);
586    b->window()->Show();
587    // The call to AddBlankTabAt above did not set the focus to the tab as its
588    // window was not active, so we have to do it explicitly.
589    // See http://crbug.com/6380.
590    b->tab_strip_model()->GetActiveWebContents()->RestoreFocus();
591  }
592}
593
594void CloseTab(Browser* browser) {
595  content::RecordAction(UserMetricsAction("CloseTab_Accelerator"));
596  browser->tab_strip_model()->CloseSelectedTabs();
597}
598
599bool CanZoomIn(content::WebContents* contents) {
600  ZoomController* zoom_controller = ZoomController::FromWebContents(contents);
601  return zoom_controller->GetZoomPercent() !=
602      contents->GetMaximumZoomPercent() + 1;
603}
604
605bool CanZoomOut(content::WebContents* contents) {
606  ZoomController* zoom_controller = ZoomController::FromWebContents(contents);
607  return zoom_controller->GetZoomPercent() !=
608      contents->GetMinimumZoomPercent();
609}
610
611bool ActualSize(content::WebContents* contents) {
612  ZoomController* zoom_controller = ZoomController::FromWebContents(contents);
613  return zoom_controller->GetZoomPercent() != 100.0f;
614}
615
616TabStripModelDelegate::RestoreTabType GetRestoreTabType(
617    const Browser* browser) {
618  TabRestoreService* service =
619      TabRestoreServiceFactory::GetForProfile(browser->profile());
620  if (!service || service->entries().empty())
621    return TabStripModelDelegate::RESTORE_NONE;
622  if (service->entries().front()->type == TabRestoreService::WINDOW)
623    return TabStripModelDelegate::RESTORE_WINDOW;
624  return TabStripModelDelegate::RESTORE_TAB;
625}
626
627void SelectNextTab(Browser* browser) {
628  content::RecordAction(UserMetricsAction("SelectNextTab"));
629  browser->tab_strip_model()->SelectNextTab();
630}
631
632void SelectPreviousTab(Browser* browser) {
633  content::RecordAction(UserMetricsAction("SelectPrevTab"));
634  browser->tab_strip_model()->SelectPreviousTab();
635}
636
637void MoveTabNext(Browser* browser) {
638  content::RecordAction(UserMetricsAction("MoveTabNext"));
639  browser->tab_strip_model()->MoveTabNext();
640}
641
642void MoveTabPrevious(Browser* browser) {
643  content::RecordAction(UserMetricsAction("MoveTabPrevious"));
644  browser->tab_strip_model()->MoveTabPrevious();
645}
646
647void SelectNumberedTab(Browser* browser, int index) {
648  if (index < browser->tab_strip_model()->count()) {
649    content::RecordAction(UserMetricsAction("SelectNumberedTab"));
650    browser->tab_strip_model()->ActivateTabAt(index, true);
651  }
652}
653
654void SelectLastTab(Browser* browser) {
655  content::RecordAction(UserMetricsAction("SelectLastTab"));
656  browser->tab_strip_model()->SelectLastTab();
657}
658
659void DuplicateTab(Browser* browser) {
660  content::RecordAction(UserMetricsAction("Duplicate"));
661  DuplicateTabAt(browser, browser->tab_strip_model()->active_index());
662}
663
664bool CanDuplicateTab(const Browser* browser) {
665  WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
666  return contents && contents->GetController().GetLastCommittedEntry();
667}
668
669WebContents* DuplicateTabAt(Browser* browser, int index) {
670  WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(index);
671  CHECK(contents);
672  WebContents* contents_dupe = contents->Clone();
673
674  bool pinned = false;
675  if (browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP)) {
676    // If this is a tabbed browser, just create a duplicate tab inside the same
677    // window next to the tab being duplicated.
678    int index = browser->tab_strip_model()->GetIndexOfWebContents(contents);
679    pinned = browser->tab_strip_model()->IsTabPinned(index);
680    int add_types = TabStripModel::ADD_ACTIVE |
681        TabStripModel::ADD_INHERIT_GROUP |
682        (pinned ? TabStripModel::ADD_PINNED : 0);
683    browser->tab_strip_model()->InsertWebContentsAt(
684        index + 1, contents_dupe, add_types);
685  } else {
686    Browser* new_browser = NULL;
687    if (browser->is_app() && !browser->is_type_popup()) {
688      new_browser = new Browser(
689          Browser::CreateParams::CreateForApp(browser->app_name(),
690                                              browser->is_trusted_source(),
691                                              gfx::Rect(),
692                                              browser->profile(),
693                                              browser->host_desktop_type()));
694    } else {
695      new_browser = new Browser(
696          Browser::CreateParams(browser->type(), browser->profile(),
697                                browser->host_desktop_type()));
698    }
699    // Preserve the size of the original window. The new window has already
700    // been given an offset by the OS, so we shouldn't copy the old bounds.
701    BrowserWindow* new_window = new_browser->window();
702    new_window->SetBounds(gfx::Rect(new_window->GetRestoredBounds().origin(),
703                          browser->window()->GetRestoredBounds().size()));
704
705    // We need to show the browser now.  Otherwise ContainerWin assumes the
706    // WebContents is invisible and won't size it.
707    new_browser->window()->Show();
708
709    // The page transition below is only for the purpose of inserting the tab.
710    new_browser->tab_strip_model()->AddWebContents(
711        contents_dupe, -1,
712        ui::PAGE_TRANSITION_LINK,
713        TabStripModel::ADD_ACTIVE);
714  }
715
716  SessionService* session_service =
717      SessionServiceFactory::GetForProfileIfExisting(browser->profile());
718  if (session_service)
719    session_service->TabRestored(contents_dupe, pinned);
720  return contents_dupe;
721}
722
723bool CanDuplicateTabAt(Browser* browser, int index) {
724  content::NavigationController& nc =
725      browser->tab_strip_model()->GetWebContentsAt(index)->GetController();
726  return nc.GetWebContents() && nc.GetLastCommittedEntry();
727}
728
729void ConvertPopupToTabbedBrowser(Browser* browser) {
730  content::RecordAction(UserMetricsAction("ShowAsTab"));
731  TabStripModel* tab_strip = browser->tab_strip_model();
732  WebContents* contents =
733      tab_strip->DetachWebContentsAt(tab_strip->active_index());
734  Browser* b = new Browser(Browser::CreateParams(browser->profile(),
735                                                 browser->host_desktop_type()));
736  b->tab_strip_model()->AppendWebContents(contents, true);
737  b->window()->Show();
738}
739
740void Exit() {
741  content::RecordAction(UserMetricsAction("Exit"));
742  chrome::AttemptUserExit();
743}
744
745void BookmarkCurrentPage(Browser* browser) {
746  DCHECK(!chrome::ShouldRemoveBookmarkThisPageUI(browser->profile()));
747
748  const extensions::Extension* extension = NULL;
749  extensions::Command command;
750  extensions::CommandService::ExtensionCommandType command_type;
751  if (GetBookmarkOverrideCommand(browser->profile(),
752                                 &extension,
753                                 &command,
754                                 &command_type)) {
755    switch (command_type) {
756      case extensions::CommandService::NAMED:
757        browser->window()->ExecuteExtensionCommand(extension, command);
758        break;
759      case extensions::CommandService::BROWSER_ACTION:
760      case extensions::CommandService::PAGE_ACTION:
761        // BookmarkCurrentPage is called through a user gesture, so it is safe
762        // to grant the active tab permission.
763        extensions::ExtensionActionAPI::Get(browser->profile())->
764            ShowExtensionActionPopup(extension, browser, true);
765        break;
766    }
767    return;
768  }
769
770  BookmarkCurrentPageInternal(browser);
771}
772
773bool CanBookmarkCurrentPage(const Browser* browser) {
774  return CanBookmarkCurrentPageInternal(browser, true);
775}
776
777void BookmarkAllTabs(Browser* browser) {
778  chrome::ShowBookmarkAllTabsDialog(browser);
779}
780
781bool CanBookmarkAllTabs(const Browser* browser) {
782  return browser->tab_strip_model()->count() > 1 &&
783             !chrome::ShouldRemoveBookmarkOpenPagesUI(browser->profile()) &&
784             CanBookmarkCurrentPageInternal(browser, false);
785}
786
787void Translate(Browser* browser) {
788  if (!browser->window()->IsActive())
789    return;
790
791  WebContents* web_contents =
792      browser->tab_strip_model()->GetActiveWebContents();
793  ChromeTranslateClient* chrome_translate_client =
794      ChromeTranslateClient::FromWebContents(web_contents);
795
796  translate::TranslateStep step = translate::TRANSLATE_STEP_BEFORE_TRANSLATE;
797  if (chrome_translate_client) {
798    if (chrome_translate_client->GetLanguageState().translation_pending())
799      step = translate::TRANSLATE_STEP_TRANSLATING;
800    else if (chrome_translate_client->GetLanguageState().IsPageTranslated())
801      step = translate::TRANSLATE_STEP_AFTER_TRANSLATE;
802  }
803  browser->window()->ShowTranslateBubble(
804      web_contents, step, translate::TranslateErrors::NONE, true);
805}
806
807void ManagePasswordsForPage(Browser* browser) {
808  if (!browser->window()->IsActive())
809    return;
810
811  WebContents* web_contents =
812      browser->tab_strip_model()->GetActiveWebContents();
813  chrome::ShowManagePasswordsBubble(web_contents);
814}
815
816void TogglePagePinnedToStartScreen(Browser* browser) {
817#if defined(OS_WIN)
818  MetroPinTabHelper::FromWebContents(
819      browser->tab_strip_model()->GetActiveWebContents())->
820          TogglePinnedToStartScreen();
821#endif
822}
823
824void SavePage(Browser* browser) {
825  content::RecordAction(UserMetricsAction("SavePage"));
826  WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
827  if (current_tab && current_tab->GetContentsMimeType() == "application/pdf")
828    content::RecordAction(UserMetricsAction("PDF.SavePage"));
829  current_tab->OnSavePage();
830}
831
832bool CanSavePage(const Browser* browser) {
833  // LocalState can be NULL in tests.
834  if (g_browser_process->local_state() &&
835      !g_browser_process->local_state()->GetBoolean(
836      prefs::kAllowFileSelectionDialogs)) {
837    return false;
838  }
839  return !browser->is_devtools() &&
840      !(GetContentRestrictions(browser) & CONTENT_RESTRICTION_SAVE);
841}
842
843void ShowFindBar(Browser* browser) {
844  browser->GetFindBarController()->Show();
845}
846
847void ShowWebsiteSettings(Browser* browser,
848                         content::WebContents* web_contents,
849                         const GURL& url,
850                         const SSLStatus& ssl) {
851  browser->window()->ShowWebsiteSettings(
852      Profile::FromBrowserContext(web_contents->GetBrowserContext()),
853      web_contents, url, ssl);
854}
855
856void Print(Browser* browser) {
857#if defined(ENABLE_PRINTING)
858  WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
859
860#if defined(ENABLE_FULL_PRINTING)
861  printing::PrintViewManager* print_view_manager =
862      printing::PrintViewManager::FromWebContents(contents);
863  if (!browser->profile()->GetPrefs()->GetBoolean(
864          prefs::kPrintPreviewDisabled)) {
865    print_view_manager->PrintPreviewNow(false);
866    return;
867  }
868#else   // ENABLE_FULL_PRINTING
869  printing::PrintViewManagerBasic* print_view_manager =
870      printing::PrintViewManagerBasic::FromWebContents(contents);
871#endif  // ENABLE_FULL_PRINTING
872
873#if !defined(DISABLE_BASIC_PRINTING)
874  print_view_manager->PrintNow();
875#endif  // DISABLE_BASIC_PRINTING
876
877#endif  // defined(ENABLE_PRINTING)
878}
879
880bool CanPrint(Browser* browser) {
881  // Do not print when printing is disabled via pref or policy.
882  // Do not print when a constrained window is showing. It's confusing.
883  // TODO(gbillock): Need to re-assess the call to
884  // IsShowingWebContentsModalDialog after a popup management policy is
885  // refined -- we will probably want to just queue the print request, not
886  // block it.
887  return browser->profile()->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
888      !(IsShowingWebContentsModalDialog(browser) ||
889      GetContentRestrictions(browser) & CONTENT_RESTRICTION_PRINT);
890}
891
892#if !defined(DISABLE_BASIC_PRINTING)
893void BasicPrint(Browser* browser) {
894#if defined(ENABLE_FULL_PRINTING)
895  printing::PrintViewManager* print_view_manager =
896      printing::PrintViewManager::FromWebContents(
897          browser->tab_strip_model()->GetActiveWebContents());
898  print_view_manager->BasicPrint();
899#endif
900}
901
902bool CanBasicPrint(Browser* browser) {
903  // If printing is not disabled via pref or policy, it is always possible to
904  // advanced print when the print preview is visible.  The exception to this
905  // is under Win8 ash, since showing the advanced print dialog will open it
906  // modally on the Desktop and hang the browser.
907#if defined(OS_WIN)
908  if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH)
909    return false;
910#endif
911
912  return browser->profile()->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) &&
913      (PrintPreviewShowing(browser) || CanPrint(browser));
914}
915#endif  // !DISABLE_BASIC_PRINTING
916
917void EmailPageLocation(Browser* browser) {
918  content::RecordAction(UserMetricsAction("EmailPageLocation"));
919  WebContents* wc = browser->tab_strip_model()->GetActiveWebContents();
920  DCHECK(wc);
921
922  std::string title = net::EscapeQueryParamValue(
923      base::UTF16ToUTF8(wc->GetTitle()), false);
924  std::string page_url = net::EscapeQueryParamValue(wc->GetURL().spec(), false);
925  std::string mailto = std::string("mailto:?subject=Fwd:%20") +
926      title + "&body=%0A%0A" + page_url;
927  platform_util::OpenExternal(browser->profile(), GURL(mailto));
928}
929
930bool CanEmailPageLocation(const Browser* browser) {
931  return browser->toolbar_model()->ShouldDisplayURL() &&
932      browser->tab_strip_model()->GetActiveWebContents()->GetURL().is_valid();
933}
934
935void Cut(Browser* browser) {
936  content::RecordAction(UserMetricsAction("Cut"));
937  browser->window()->Cut();
938}
939
940void Copy(Browser* browser) {
941  content::RecordAction(UserMetricsAction("Copy"));
942  browser->window()->Copy();
943}
944
945void Paste(Browser* browser) {
946  content::RecordAction(UserMetricsAction("Paste"));
947  browser->window()->Paste();
948}
949
950void Find(Browser* browser) {
951  content::RecordAction(UserMetricsAction("Find"));
952  FindInPage(browser, false, false);
953}
954
955void FindNext(Browser* browser) {
956  content::RecordAction(UserMetricsAction("FindNext"));
957  FindInPage(browser, true, true);
958}
959
960void FindPrevious(Browser* browser) {
961  content::RecordAction(UserMetricsAction("FindPrevious"));
962  FindInPage(browser, true, false);
963}
964
965void FindInPage(Browser* browser, bool find_next, bool forward_direction) {
966  ShowFindBar(browser);
967  if (find_next) {
968    base::string16 find_text;
969    FindTabHelper* find_helper = FindTabHelper::FromWebContents(
970        browser->tab_strip_model()->GetActiveWebContents());
971#if defined(OS_MACOSX)
972    // We always want to search for the current contents of the find bar on
973    // OS X. For regular profile it's always the current find pboard. For
974    // Incognito window it's the newest value of the find pboard content and
975    // user-typed text.
976    FindBar* find_bar = browser->GetFindBarController()->find_bar();
977    find_text = find_bar->GetFindText();
978#endif
979    find_helper->StartFinding(find_text, forward_direction, false);
980  }
981}
982
983void Zoom(Browser* browser, content::PageZoom zoom) {
984  chrome_page_zoom::Zoom(browser->tab_strip_model()->GetActiveWebContents(),
985                         zoom);
986}
987
988void FocusToolbar(Browser* browser) {
989  content::RecordAction(UserMetricsAction("FocusToolbar"));
990  browser->window()->FocusToolbar();
991}
992
993void FocusLocationBar(Browser* browser) {
994  content::RecordAction(UserMetricsAction("FocusLocation"));
995  browser->window()->SetFocusToLocationBar(true);
996}
997
998void FocusSearch(Browser* browser) {
999  // TODO(beng): replace this with FocusLocationBar
1000  content::RecordAction(UserMetricsAction("FocusSearch"));
1001  browser->window()->GetLocationBar()->FocusSearch();
1002}
1003
1004void FocusAppMenu(Browser* browser) {
1005  content::RecordAction(UserMetricsAction("FocusAppMenu"));
1006  browser->window()->FocusAppMenu();
1007}
1008
1009void FocusBookmarksToolbar(Browser* browser) {
1010  content::RecordAction(UserMetricsAction("FocusBookmarksToolbar"));
1011  browser->window()->FocusBookmarksToolbar();
1012}
1013
1014void FocusInfobars(Browser* browser) {
1015  content::RecordAction(UserMetricsAction("FocusInfobars"));
1016  browser->window()->FocusInfobars();
1017}
1018
1019void FocusNextPane(Browser* browser) {
1020  content::RecordAction(UserMetricsAction("FocusNextPane"));
1021  browser->window()->RotatePaneFocus(true);
1022}
1023
1024void FocusPreviousPane(Browser* browser) {
1025  content::RecordAction(UserMetricsAction("FocusPreviousPane"));
1026  browser->window()->RotatePaneFocus(false);
1027}
1028
1029void ToggleDevToolsWindow(Browser* browser, DevToolsToggleAction action) {
1030  if (action.type() == DevToolsToggleAction::kShowConsole)
1031    content::RecordAction(UserMetricsAction("DevTools_ToggleConsole"));
1032  else
1033    content::RecordAction(UserMetricsAction("DevTools_ToggleWindow"));
1034  DevToolsWindow::ToggleDevToolsWindow(browser, action);
1035}
1036
1037bool CanOpenTaskManager() {
1038#if defined(ENABLE_TASK_MANAGER)
1039  return true;
1040#else
1041  return false;
1042#endif
1043}
1044
1045void OpenTaskManager(Browser* browser) {
1046#if defined(ENABLE_TASK_MANAGER)
1047  content::RecordAction(UserMetricsAction("TaskManager"));
1048  chrome::ShowTaskManager(browser);
1049#else
1050  NOTREACHED();
1051#endif
1052}
1053
1054void OpenFeedbackDialog(Browser* browser) {
1055  content::RecordAction(UserMetricsAction("Feedback"));
1056  chrome::ShowFeedbackPage(browser, std::string(), std::string());
1057}
1058
1059void ToggleBookmarkBar(Browser* browser) {
1060  content::RecordAction(UserMetricsAction("ShowBookmarksBar"));
1061  ToggleBookmarkBarWhenVisible(browser->profile());
1062}
1063
1064void ShowAppMenu(Browser* browser) {
1065  // We record the user metric for this event in WrenchMenu::RunMenu.
1066  browser->window()->ShowAppMenu();
1067}
1068
1069void ShowAvatarMenu(Browser* browser) {
1070  browser->window()->ShowAvatarBubbleFromAvatarButton(
1071      BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT,
1072      signin::ManageAccountsParams());
1073}
1074
1075void OpenUpdateChromeDialog(Browser* browser) {
1076  if (UpgradeDetector::GetInstance()->is_outdated_install()) {
1077    content::NotificationService::current()->Notify(
1078        chrome::NOTIFICATION_OUTDATED_INSTALL,
1079        content::NotificationService::AllSources(),
1080        content::NotificationService::NoDetails());
1081  } else if (UpgradeDetector::GetInstance()->is_outdated_install_no_au()) {
1082    content::NotificationService::current()->Notify(
1083        chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
1084        content::NotificationService::AllSources(),
1085        content::NotificationService::NoDetails());
1086  } else {
1087    content::RecordAction(UserMetricsAction("UpdateChrome"));
1088    browser->window()->ShowUpdateChromeDialog();
1089  }
1090}
1091
1092void ToggleSpeechInput(Browser* browser) {
1093  SearchTabHelper* search_tab_helper =
1094      SearchTabHelper::FromWebContents(
1095          browser->tab_strip_model()->GetActiveWebContents());
1096  // |search_tab_helper| can be null in unit tests.
1097  if (search_tab_helper)
1098    search_tab_helper->ToggleVoiceSearch();
1099}
1100
1101void DistillCurrentPage(Browser* browser) {
1102  DistillCurrentPageAndView(browser->tab_strip_model()->GetActiveWebContents());
1103}
1104
1105bool CanRequestTabletSite(WebContents* current_tab) {
1106  return current_tab &&
1107      current_tab->GetController().GetLastCommittedEntry() != NULL;
1108}
1109
1110bool IsRequestingTabletSite(Browser* browser) {
1111  WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
1112  if (!current_tab)
1113    return false;
1114  content::NavigationEntry* entry =
1115      current_tab->GetController().GetLastCommittedEntry();
1116  if (!entry)
1117    return false;
1118  return entry->GetIsOverridingUserAgent();
1119}
1120
1121void ToggleRequestTabletSite(Browser* browser) {
1122  WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents();
1123  if (!current_tab)
1124    return;
1125  NavigationController& controller = current_tab->GetController();
1126  NavigationEntry* entry = controller.GetLastCommittedEntry();
1127  if (!entry)
1128    return;
1129  if (entry->GetIsOverridingUserAgent()) {
1130    entry->SetIsOverridingUserAgent(false);
1131  } else {
1132    entry->SetIsOverridingUserAgent(true);
1133    chrome::VersionInfo version_info;
1134    std::string product;
1135    if (version_info.is_valid())
1136      product = version_info.ProductNameAndVersionForUserAgent();
1137    current_tab->SetUserAgentOverride(content::BuildUserAgentFromOSAndProduct(
1138        kOsOverrideForTabletSite, product));
1139  }
1140  controller.ReloadOriginalRequestURL(true);
1141}
1142
1143void ToggleFullscreenMode(Browser* browser) {
1144  DCHECK(browser);
1145  browser->fullscreen_controller()->ToggleBrowserFullscreenMode();
1146}
1147
1148void ClearCache(Browser* browser) {
1149  BrowsingDataRemover* remover =
1150      BrowsingDataRemover::CreateForUnboundedRange(browser->profile());
1151  remover->Remove(BrowsingDataRemover::REMOVE_CACHE,
1152                  BrowsingDataHelper::UNPROTECTED_WEB);
1153  // BrowsingDataRemover takes care of deleting itself when done.
1154}
1155
1156bool IsDebuggerAttachedToCurrentTab(Browser* browser) {
1157  WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1158  return contents ?
1159      content::DevToolsAgentHost::IsDebuggerAttached(contents) : false;
1160}
1161
1162void ViewSource(Browser* browser, WebContents* contents) {
1163  DCHECK(contents);
1164
1165  // Use the last committed entry, since the pending entry hasn't loaded yet and
1166  // won't be copied into the cloned tab.
1167  NavigationEntry* entry = contents->GetController().GetLastCommittedEntry();
1168  if (!entry)
1169    return;
1170
1171  ViewSource(browser, contents, entry->GetURL(), entry->GetPageState());
1172}
1173
1174void ViewSource(Browser* browser,
1175                WebContents* contents,
1176                const GURL& url,
1177                const content::PageState& page_state) {
1178  content::RecordAction(UserMetricsAction("ViewSource"));
1179  DCHECK(contents);
1180
1181  WebContents* view_source_contents = contents->Clone();
1182  DCHECK(view_source_contents->GetController().CanPruneAllButLastCommitted());
1183  view_source_contents->GetController().PruneAllButLastCommitted();
1184  NavigationEntry* last_committed_entry =
1185      view_source_contents->GetController().GetLastCommittedEntry();
1186  if (!last_committed_entry)
1187    return;
1188
1189  GURL view_source_url =
1190      GURL(content::kViewSourceScheme + std::string(":") + url.spec());
1191  last_committed_entry->SetVirtualURL(view_source_url);
1192
1193  // Do not restore scroller position.
1194  last_committed_entry->SetPageState(page_state.RemoveScrollOffset());
1195
1196  // Do not restore title, derive it from the url.
1197  last_committed_entry->SetTitle(base::string16());
1198
1199  // Now show view-source entry.
1200  if (browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP)) {
1201    // If this is a tabbed browser, just create a duplicate tab inside the same
1202    // window next to the tab being duplicated.
1203    int index = browser->tab_strip_model()->GetIndexOfWebContents(contents);
1204    int add_types = TabStripModel::ADD_ACTIVE |
1205        TabStripModel::ADD_INHERIT_GROUP;
1206    browser->tab_strip_model()->InsertWebContentsAt(
1207        index + 1,
1208        view_source_contents,
1209        add_types);
1210  } else {
1211    Browser* b = new Browser(
1212        Browser::CreateParams(Browser::TYPE_TABBED, browser->profile(),
1213                              browser->host_desktop_type()));
1214
1215    // Preserve the size of the original window. The new window has already
1216    // been given an offset by the OS, so we shouldn't copy the old bounds.
1217    BrowserWindow* new_window = b->window();
1218    new_window->SetBounds(gfx::Rect(new_window->GetRestoredBounds().origin(),
1219                          browser->window()->GetRestoredBounds().size()));
1220
1221    // We need to show the browser now. Otherwise ContainerWin assumes the
1222    // WebContents is invisible and won't size it.
1223    b->window()->Show();
1224
1225    // The page transition below is only for the purpose of inserting the tab.
1226    b->tab_strip_model()->AddWebContents(view_source_contents, -1,
1227                                         ui::PAGE_TRANSITION_LINK,
1228                                         TabStripModel::ADD_ACTIVE);
1229  }
1230
1231  SessionService* session_service =
1232      SessionServiceFactory::GetForProfileIfExisting(browser->profile());
1233  if (session_service)
1234    session_service->TabRestored(view_source_contents, false);
1235}
1236
1237void ViewSelectedSource(Browser* browser) {
1238  ViewSource(browser, browser->tab_strip_model()->GetActiveWebContents());
1239}
1240
1241bool CanViewSource(const Browser* browser) {
1242  return !browser->is_devtools() &&
1243      browser->tab_strip_model()->GetActiveWebContents()->GetController().
1244          CanViewSource();
1245}
1246
1247void CreateApplicationShortcuts(Browser* browser) {
1248  content::RecordAction(UserMetricsAction("CreateShortcut"));
1249  extensions::TabHelper::FromWebContents(
1250      browser->tab_strip_model()->GetActiveWebContents())->
1251          CreateApplicationShortcuts();
1252}
1253
1254void CreateBookmarkAppFromCurrentWebContents(Browser* browser) {
1255  content::RecordAction(UserMetricsAction("CreateHostedApp"));
1256  extensions::TabHelper::FromWebContents(
1257      browser->tab_strip_model()->GetActiveWebContents())->
1258          CreateHostedAppFromWebContents();
1259}
1260
1261bool CanCreateApplicationShortcuts(const Browser* browser) {
1262  return extensions::TabHelper::FromWebContents(
1263      browser->tab_strip_model()->GetActiveWebContents())->
1264          CanCreateApplicationShortcuts();
1265}
1266
1267bool CanCreateBookmarkApp(const Browser* browser) {
1268  return extensions::TabHelper::FromWebContents(
1269             browser->tab_strip_model()->GetActiveWebContents())
1270      ->CanCreateBookmarkApp();
1271}
1272
1273void ConvertTabToAppWindow(Browser* browser,
1274                           content::WebContents* contents) {
1275  const GURL& url = contents->GetController().GetLastCommittedEntry()->GetURL();
1276  std::string app_name = web_app::GenerateApplicationNameFromURL(url);
1277
1278  int index = browser->tab_strip_model()->GetIndexOfWebContents(contents);
1279  if (index >= 0)
1280    browser->tab_strip_model()->DetachWebContentsAt(index);
1281
1282  Browser* app_browser = new Browser(
1283      Browser::CreateParams::CreateForApp(app_name,
1284                                          true /* trusted_source */,
1285                                          gfx::Rect(),
1286                                          browser->profile(),
1287                                          browser->host_desktop_type()));
1288  app_browser->tab_strip_model()->AppendWebContents(contents, true);
1289
1290  contents->GetMutableRendererPrefs()->can_accept_load_drops = false;
1291  contents->GetRenderViewHost()->SyncRendererPrefs();
1292  app_browser->window()->Show();
1293}
1294
1295}  // namespace chrome
1296