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