browser_navigator.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/browser_navigator.h"
6
7#include <algorithm>
8
9#include "base/command_line.h"
10#include "base/prefs/pref_service.h"
11#include "base/stringprintf.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/browser_about_handler.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/tab_helper.h"
16#include "chrome/browser/google/google_url_tracker.h"
17#include "chrome/browser/prefs/incognito_mode_prefs.h"
18#include "chrome/browser/prerender/prerender_manager.h"
19#include "chrome/browser/prerender/prerender_manager_factory.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/tab_contents/tab_util.h"
22#include "chrome/browser/ui/browser.h"
23#include "chrome/browser/ui/browser_finder.h"
24#include "chrome/browser/ui/browser_instant_controller.h"
25#include "chrome/browser/ui/browser_tab_contents.h"
26#include "chrome/browser/ui/browser_window.h"
27#include "chrome/browser/ui/host_desktop.h"
28#include "chrome/browser/ui/omnibox/location_bar.h"
29#include "chrome/browser/ui/singleton_tabs.h"
30#include "chrome/browser/ui/status_bubble.h"
31#include "chrome/browser/ui/tabs/tab_strip_model.h"
32#include "chrome/browser/web_applications/web_app.h"
33#include "chrome/common/chrome_notification_types.h"
34#include "chrome/common/extensions/extension.h"
35#include "chrome/common/pref_names.h"
36#include "chrome/common/url_constants.h"
37#include "content/public/browser/browser_url_handler.h"
38#include "content/public/browser/notification_service.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/browser/web_contents_view.h"
42
43#if defined(USE_AURA)
44#include "ui/aura/window.h"
45#endif
46
47using content::GlobalRequestID;
48using content::WebContents;
49
50class BrowserNavigatorWebContentsAdoption {
51 public:
52  static void AttachTabHelpers(content::WebContents* contents) {
53    BrowserTabContents::AttachTabHelpers(contents);
54  }
55};
56
57namespace {
58
59// Returns true if the specified Browser can open tabs. Not all Browsers support
60// multiple tabs, such as app frames and popups. This function returns false for
61// those types of Browser.
62bool WindowCanOpenTabs(Browser* browser) {
63  return browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP) ||
64      browser->tab_strip_model()->empty();
65}
66
67// Finds an existing Browser compatible with |profile|, making a new one if no
68// such Browser is located.
69Browser* GetOrCreateBrowser(Profile* profile,
70                            chrome::HostDesktopType host_desktop_type) {
71  Browser* browser = chrome::FindTabbedBrowser(profile, false,
72                                               host_desktop_type);
73  return browser ? browser : new Browser(
74      Browser::CreateParams(profile, host_desktop_type));
75}
76
77// Change some of the navigation parameters based on the particular URL.
78// Currently this applies to some chrome:// pages which we always want to open
79// in a non-incognito window. Note that even though a ChromeOS guest session is
80// technically an incognito window, these URLs are allowed.
81// Returns true on success. Otherwise, if changing params leads the browser into
82// an erroneous state, returns false.
83bool AdjustNavigateParamsForURL(chrome::NavigateParams* params) {
84  if (params->target_contents != NULL ||
85      chrome::IsURLAllowedInIncognito(params->url,
86                                      params->initiating_profile) ||
87      params->initiating_profile->IsGuestSession()) {
88    return true;
89  }
90
91  Profile* profile = params->initiating_profile;
92
93  if (profile->IsOffTheRecord() || params->disposition == OFF_THE_RECORD) {
94    profile = profile->GetOriginalProfile();
95
96    // If incognito is forced, we punt.
97    PrefService* prefs = profile->GetPrefs();
98    if (prefs && IncognitoModePrefs::GetAvailability(prefs) ==
99            IncognitoModePrefs::FORCED) {
100      return false;
101    }
102
103    params->disposition = SINGLETON_TAB;
104    params->browser =
105        chrome::FindOrCreateTabbedBrowser(profile, params->host_desktop_type);
106    params->window_action = chrome::NavigateParams::SHOW_WINDOW;
107  }
108
109  return true;
110}
111
112// Returns a Browser that can host the navigation or tab addition specified in
113// |params|. This might just return the same Browser specified in |params|, or
114// some other if that Browser is deemed incompatible.
115Browser* GetBrowserForDisposition(chrome::NavigateParams* params) {
116  // If no source WebContents was specified, we use the selected one from
117  // the target browser. This must happen first, before
118  // GetBrowserForDisposition() has a chance to replace |params->browser| with
119  // another one.
120  if (!params->source_contents && params->browser) {
121    params->source_contents =
122        params->browser->tab_strip_model()->GetActiveWebContents();
123  }
124
125  Profile* profile = params->initiating_profile;
126
127  switch (params->disposition) {
128    case CURRENT_TAB:
129      if (params->browser)
130        return params->browser;
131      // Find a compatible window and re-execute this command in it. Otherwise
132      // re-run with NEW_WINDOW.
133      return GetOrCreateBrowser(profile, params->host_desktop_type);
134    case SINGLETON_TAB:
135    case NEW_FOREGROUND_TAB:
136    case NEW_BACKGROUND_TAB:
137      // See if we can open the tab in the window this navigator is bound to.
138      if (params->browser && WindowCanOpenTabs(params->browser))
139        return params->browser;
140      // Find a compatible window and re-execute this command in it. Otherwise
141      // re-run with NEW_WINDOW.
142      return GetOrCreateBrowser(profile, params->host_desktop_type);
143    case NEW_POPUP: {
144      // Make a new popup window.
145      // Coerce app-style if |source| represents an app.
146      std::string app_name;
147      if (!params->extension_app_id.empty()) {
148        app_name = web_app::GenerateApplicationNameFromExtensionId(
149            params->extension_app_id);
150      } else if (!params->browser->app_name().empty()) {
151        app_name = params->browser->app_name();
152      } else if (params->source_contents) {
153        extensions::TabHelper* extensions_tab_helper =
154            extensions::TabHelper::FromWebContents(params->source_contents);
155        if (extensions_tab_helper && extensions_tab_helper->is_app()) {
156          app_name = web_app::GenerateApplicationNameFromExtensionId(
157              extensions_tab_helper->extension_app()->id());
158        }
159      }
160      if (app_name.empty()) {
161        Browser::CreateParams browser_params(
162            Browser::TYPE_POPUP, profile, params->host_desktop_type);
163        browser_params.initial_bounds = params->window_bounds;
164        return new Browser(browser_params);
165      }
166
167      return new Browser(Browser::CreateParams::CreateForApp(
168          Browser::TYPE_POPUP, app_name, params->window_bounds, profile,
169          params->host_desktop_type));
170    }
171    case NEW_WINDOW: {
172      // Make a new normal browser window.
173      return new Browser(Browser::CreateParams(profile,
174                                               params->host_desktop_type));
175    }
176    case OFF_THE_RECORD:
177      // Make or find an incognito window.
178      return GetOrCreateBrowser(profile->GetOffTheRecordProfile(),
179                                params->host_desktop_type);
180    // The following types all result in no navigation.
181    case SUPPRESS_OPEN:
182    case SAVE_TO_DISK:
183    case IGNORE_ACTION:
184      return NULL;
185    default:
186      NOTREACHED();
187  }
188  return NULL;
189}
190
191// Fix disposition and other parameter values depending on prevailing
192// conditions.
193void NormalizeDisposition(chrome::NavigateParams* params) {
194  // Calculate the WindowOpenDisposition if necessary.
195  if (params->browser->tab_strip_model()->empty() &&
196      (params->disposition == NEW_BACKGROUND_TAB ||
197       params->disposition == CURRENT_TAB ||
198       params->disposition == SINGLETON_TAB)) {
199    params->disposition = NEW_FOREGROUND_TAB;
200  }
201  if (params->browser->profile()->IsOffTheRecord() &&
202      params->disposition == OFF_THE_RECORD) {
203    params->disposition = NEW_FOREGROUND_TAB;
204  }
205  if (!params->source_contents && params->disposition == CURRENT_TAB)
206    params->disposition = NEW_FOREGROUND_TAB;
207
208  switch (params->disposition) {
209    case NEW_BACKGROUND_TAB:
210      // Disposition trumps add types. ADD_ACTIVE is a default, so we need to
211      // remove it if disposition implies the tab is going to open in the
212      // background.
213      params->tabstrip_add_types &= ~TabStripModel::ADD_ACTIVE;
214      break;
215
216    case NEW_WINDOW:
217    case NEW_POPUP:
218      // Code that wants to open a new window typically expects it to be shown
219      // automatically.
220      if (params->window_action == chrome::NavigateParams::NO_ACTION)
221        params->window_action = chrome::NavigateParams::SHOW_WINDOW;
222      // Fall-through.
223    case NEW_FOREGROUND_TAB:
224    case SINGLETON_TAB:
225      params->tabstrip_add_types |= TabStripModel::ADD_ACTIVE;
226      break;
227
228    default:
229      break;
230  }
231}
232
233// Obtain the profile used by the code that originated the Navigate() request.
234Profile* GetSourceProfile(chrome::NavigateParams* params) {
235  if (params->source_contents) {
236    return Profile::FromBrowserContext(
237        params->source_contents->GetBrowserContext());
238  }
239
240  return params->initiating_profile;
241}
242
243void LoadURLInContents(WebContents* target_contents,
244                       const GURL& url,
245                       chrome::NavigateParams* params) {
246  content::NavigationController::LoadURLParams load_url_params(url);
247  load_url_params.referrer = params->referrer;
248  load_url_params.transition_type = params->transition;
249  load_url_params.extra_headers = params->extra_headers;
250  load_url_params.is_cross_site_redirect = params->is_cross_site_redirect;
251
252  if (params->transferred_global_request_id != GlobalRequestID()) {
253    load_url_params.is_renderer_initiated = params->is_renderer_initiated;
254    load_url_params.transferred_global_request_id =
255        params->transferred_global_request_id;
256  } else if (params->is_renderer_initiated) {
257    load_url_params.is_renderer_initiated = true;
258  }
259  target_contents->GetController().LoadURLWithParams(load_url_params);
260}
261
262// This class makes sure the Browser object held in |params| is made visible
263// by the time it goes out of scope, provided |params| wants it to be shown.
264class ScopedBrowserDisplayer {
265 public:
266  explicit ScopedBrowserDisplayer(chrome::NavigateParams* params)
267      : params_(params) {
268  }
269  ~ScopedBrowserDisplayer() {
270    if (params_->window_action == chrome::NavigateParams::SHOW_WINDOW_INACTIVE)
271      params_->browser->window()->ShowInactive();
272    else if (params_->window_action == chrome::NavigateParams::SHOW_WINDOW)
273      params_->browser->window()->Show();
274  }
275 private:
276  chrome::NavigateParams* params_;
277  DISALLOW_COPY_AND_ASSIGN(ScopedBrowserDisplayer);
278};
279
280// This class manages the lifetime of a WebContents created by the
281// Navigate() function. When Navigate() creates a WebContents for a URL,
282// an instance of this class takes ownership of it via TakeOwnership() until the
283// WebContents is added to a tab strip at which time ownership is
284// relinquished via ReleaseOwnership(). If this object goes out of scope without
285// being added to a tab strip, the created WebContents is deleted to
286// avoid a leak and the params->target_contents field is set to NULL.
287class ScopedTargetContentsOwner {
288 public:
289  explicit ScopedTargetContentsOwner(chrome::NavigateParams* params)
290      : params_(params) {
291  }
292  ~ScopedTargetContentsOwner() {
293    if (target_contents_owner_.get())
294      params_->target_contents = NULL;
295  }
296
297  // Assumes ownership of |params_|' target_contents until ReleaseOwnership
298  // is called.
299  void TakeOwnership() {
300    target_contents_owner_.reset(params_->target_contents);
301  }
302
303  // Relinquishes ownership of |params_|' target_contents.
304  WebContents* ReleaseOwnership() {
305    return target_contents_owner_.release();
306  }
307
308 private:
309  chrome::NavigateParams* params_;
310  scoped_ptr<WebContents> target_contents_owner_;
311  DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner);
312};
313
314content::WebContents* CreateTargetContents(const chrome::NavigateParams& params,
315                                           const GURL& url) {
316  WebContents::CreateParams create_params(
317      params.browser->profile(),
318      tab_util::GetSiteInstanceForNewTab(params.browser->profile(), url));
319  if (params.source_contents) {
320    create_params.initial_size =
321        params.source_contents->GetView()->GetContainerSize();
322  }
323#if defined(USE_AURA)
324  if (params.browser->window() &&
325      params.browser->window()->GetNativeWindow()) {
326    create_params.context =
327        params.browser->window()->GetNativeWindow();
328  }
329#endif
330
331  content::WebContents* target_contents = WebContents::Create(create_params);
332  // New tabs can have WebUI URLs that will make calls back to arbitrary
333  // tab helpers, so the entire set of tab helpers needs to be set up
334  // immediately.
335  BrowserNavigatorWebContentsAdoption::AttachTabHelpers(target_contents);
336  extensions::TabHelper::FromWebContents(target_contents)->
337      SetExtensionAppById(params.extension_app_id);
338  // TODO(sky): Figure out why this is needed. Without it we seem to get
339  // failures in startup tests.
340  // By default, content believes it is not hidden.  When adding contents
341  // in the background, tell it that it's hidden.
342  if ((params.tabstrip_add_types & TabStripModel::ADD_ACTIVE) == 0) {
343    // TabStripModel::AddWebContents invokes WasHidden if not foreground.
344    target_contents->WasHidden();
345  }
346  return target_contents;
347}
348
349// If a prerendered page exists for |url|, replace the page at |target_contents|
350// with it.
351bool SwapInPrerender(WebContents* target_contents, const GURL& url) {
352  prerender::PrerenderManager* prerender_manager =
353      prerender::PrerenderManagerFactory::GetForProfile(
354          Profile::FromBrowserContext(target_contents->GetBrowserContext()));
355  return prerender_manager &&
356      prerender_manager->MaybeUsePrerenderedPage(target_contents, url);
357}
358
359bool SwapInInstantNTP(chrome::NavigateParams* params,
360                      const GURL& url,
361                      content::WebContents* source_contents) {
362  BrowserInstantController* instant = params->browser->instant_controller();
363  return instant && instant->MaybeSwapInInstantNTPContents(
364      url, source_contents, &params->target_contents);
365}
366
367}  // namespace
368
369namespace chrome {
370
371NavigateParams::NavigateParams(Browser* a_browser,
372                               const GURL& a_url,
373                               content::PageTransition a_transition)
374    : url(a_url),
375      target_contents(NULL),
376      source_contents(NULL),
377      disposition(CURRENT_TAB),
378      transition(a_transition),
379      is_renderer_initiated(false),
380      tabstrip_index(-1),
381      tabstrip_add_types(TabStripModel::ADD_ACTIVE),
382      window_action(NO_ACTION),
383      user_gesture(true),
384      path_behavior(RESPECT),
385      ref_behavior(IGNORE_REF),
386      browser(a_browser),
387      initiating_profile(NULL),
388      is_cross_site_redirect(false) {
389        if (a_browser)
390          host_desktop_type = a_browser->host_desktop_type();
391        else
392          host_desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE;
393      }
394
395NavigateParams::NavigateParams(Browser* a_browser,
396                               WebContents* a_target_contents)
397    : target_contents(a_target_contents),
398      source_contents(NULL),
399      disposition(CURRENT_TAB),
400      transition(content::PAGE_TRANSITION_LINK),
401      is_renderer_initiated(false),
402      tabstrip_index(-1),
403      tabstrip_add_types(TabStripModel::ADD_ACTIVE),
404      window_action(NO_ACTION),
405      user_gesture(true),
406      path_behavior(RESPECT),
407      ref_behavior(IGNORE_REF),
408      browser(a_browser),
409      initiating_profile(NULL),
410      is_cross_site_redirect(false) {
411        if (a_browser)
412          host_desktop_type = a_browser->host_desktop_type();
413        else
414          host_desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE;
415      }
416
417NavigateParams::NavigateParams(Profile* a_profile,
418                               const GURL& a_url,
419                               content::PageTransition a_transition)
420    : url(a_url),
421      target_contents(NULL),
422      source_contents(NULL),
423      disposition(NEW_FOREGROUND_TAB),
424      transition(a_transition),
425      is_renderer_initiated(false),
426      tabstrip_index(-1),
427      tabstrip_add_types(TabStripModel::ADD_ACTIVE),
428      window_action(SHOW_WINDOW),
429      user_gesture(true),
430      path_behavior(RESPECT),
431      ref_behavior(IGNORE_REF),
432      browser(NULL),
433      initiating_profile(a_profile),
434      host_desktop_type(chrome::HOST_DESKTOP_TYPE_NATIVE),
435      is_cross_site_redirect(false) {}
436
437NavigateParams::~NavigateParams() {}
438
439void FillNavigateParamsFromOpenURLParams(chrome::NavigateParams* nav_params,
440                                         const content::OpenURLParams& params) {
441  nav_params->referrer = params.referrer;
442  nav_params->extra_headers = params.extra_headers;
443  nav_params->disposition = params.disposition;
444  nav_params->override_encoding = params.override_encoding;
445  nav_params->is_renderer_initiated = params.is_renderer_initiated;
446  nav_params->transferred_global_request_id =
447      params.transferred_global_request_id;
448  nav_params->is_cross_site_redirect = params.is_cross_site_redirect;
449}
450
451void Navigate(NavigateParams* params) {
452  Browser* source_browser = params->browser;
453  if (source_browser)
454    params->initiating_profile = source_browser->profile();
455  DCHECK(params->initiating_profile);
456
457  if (!AdjustNavigateParamsForURL(params))
458    return;
459
460  ExtensionService* service = params->initiating_profile->GetExtensionService();
461  if (service)
462    service->ShouldBlockUrlInBrowserTab(&params->url);
463
464  // The browser window may want to adjust the disposition.
465  if (params->disposition == NEW_POPUP &&
466      source_browser &&
467      source_browser->window()) {
468    params->disposition =
469        source_browser->window()->GetDispositionForPopupBounds(
470            params->window_bounds);
471  }
472
473  params->browser = GetBrowserForDisposition(params);
474  if (!params->browser)
475    return;
476
477  // Navigate() must not return early after this point.
478
479  if (GetSourceProfile(params) != params->browser->profile()) {
480    // A tab is being opened from a link from a different profile, we must reset
481    // source information that may cause state to be shared.
482    params->source_contents = NULL;
483    params->referrer = content::Referrer();
484  }
485
486  // Make sure the Browser is shown if params call for it.
487  ScopedBrowserDisplayer displayer(params);
488
489  // Makes sure any WebContents created by this function is destroyed if
490  // not properly added to a tab strip.
491  ScopedTargetContentsOwner target_contents_owner(params);
492
493  // Some dispositions need coercion to base types.
494  NormalizeDisposition(params);
495
496  // If a new window has been created, it needs to be displayed.
497  if (params->window_action == NavigateParams::NO_ACTION &&
498      source_browser != params->browser &&
499      params->browser->tab_strip_model()->empty()) {
500    params->window_action = NavigateParams::SHOW_WINDOW;
501  }
502
503  // If we create a popup window from a non user-gesture, don't activate it.
504  if (params->window_action == NavigateParams::SHOW_WINDOW &&
505      params->disposition == NEW_POPUP &&
506      params->user_gesture == false) {
507    params->window_action = NavigateParams::SHOW_WINDOW_INACTIVE;
508  }
509
510  // Determine if the navigation was user initiated. If it was, we need to
511  // inform the target WebContents, and we may need to update the UI.
512  content::PageTransition base_transition =
513      content::PageTransitionStripQualifier(params->transition);
514  bool user_initiated =
515      params->transition & content::PAGE_TRANSITION_FROM_ADDRESS_BAR ||
516      base_transition == content::PAGE_TRANSITION_TYPED ||
517      base_transition == content::PAGE_TRANSITION_AUTO_BOOKMARK ||
518      base_transition == content::PAGE_TRANSITION_GENERATED ||
519      base_transition == content::PAGE_TRANSITION_AUTO_TOPLEVEL ||
520      base_transition == content::PAGE_TRANSITION_RELOAD ||
521      base_transition == content::PAGE_TRANSITION_KEYWORD;
522
523  // Check if this is a singleton tab that already exists
524  int singleton_index = chrome::GetIndexOfSingletonTab(params);
525
526  // Did we use Instant's NTP contents?
527  bool swapped_in_instant = false;
528
529  // If no target WebContents was specified, we need to construct one if
530  // we are supposed to target a new tab; unless it's a singleton that already
531  // exists.
532  if (!params->target_contents && singleton_index < 0) {
533    GURL url;
534    if (params->url.is_empty()) {
535      url = params->browser->profile()->GetHomePage();
536      params->transition = content::PageTransitionFromInt(
537          params->transition | content::PAGE_TRANSITION_HOME_PAGE);
538    } else {
539      url = params->url;
540    }
541
542    if (params->disposition != CURRENT_TAB) {
543      swapped_in_instant = SwapInInstantNTP(params, url, NULL);
544      if (!swapped_in_instant)
545        params->target_contents = CreateTargetContents(*params, url);
546
547      // This function takes ownership of |params->target_contents| until it
548      // is added to a TabStripModel.
549      target_contents_owner.TakeOwnership();
550    } else {
551      // ... otherwise if we're loading in the current tab, the target is the
552      // same as the source.
553      DCHECK(params->source_contents);
554      swapped_in_instant = SwapInInstantNTP(params, url,
555                                            params->source_contents);
556      if (!swapped_in_instant)
557        params->target_contents = params->source_contents;
558      DCHECK(params->target_contents);
559    }
560
561    if (user_initiated)
562      params->target_contents->UserGestureDone();
563
564    if (!swapped_in_instant) {
565      if (SwapInPrerender(params->target_contents, url))
566        return;
567
568      // Try to handle non-navigational URLs that popup dialogs and such, these
569      // should not actually navigate.
570      if (!HandleNonNavigationAboutURL(url)) {
571        // Perform the actual navigation, tracking whether it came from the
572        // renderer.
573
574        LoadURLInContents(params->target_contents, url, params);
575      }
576    }
577  } else {
578    // |target_contents| was specified non-NULL, and so we assume it has already
579    // been navigated appropriately. We need to do nothing more other than
580    // add it to the appropriate tabstrip.
581  }
582
583  // If the user navigated from the omnibox, and the selected tab is going to
584  // lose focus, then make sure the focus for the source tab goes away from the
585  // omnibox.
586  if (params->source_contents &&
587      (params->disposition == NEW_FOREGROUND_TAB ||
588       params->disposition == NEW_WINDOW) &&
589      (params->tabstrip_add_types & TabStripModel::ADD_INHERIT_OPENER))
590    params->source_contents->GetView()->Focus();
591
592  if (params->source_contents == params->target_contents ||
593      (swapped_in_instant && params->disposition == CURRENT_TAB)) {
594    // The navigation occurred in the source tab.
595    params->browser->UpdateUIForNavigationInTab(params->target_contents,
596                                                params->transition,
597                                                user_initiated);
598  } else if (singleton_index == -1) {
599    // If some non-default value is set for the index, we should tell the
600    // TabStripModel to respect it.
601    if (params->tabstrip_index != -1)
602      params->tabstrip_add_types |= TabStripModel::ADD_FORCE_INDEX;
603
604    // The navigation should insert a new tab into the target Browser.
605    params->browser->tab_strip_model()->AddWebContents(
606        params->target_contents,
607        params->tabstrip_index,
608        params->transition,
609        params->tabstrip_add_types);
610    // Now that the |params->target_contents| is safely owned by the target
611    // Browser's TabStripModel, we can release ownership.
612    target_contents_owner.ReleaseOwnership();
613  }
614
615  if (singleton_index >= 0) {
616    WebContents* target =
617        params->browser->tab_strip_model()->GetWebContentsAt(singleton_index);
618
619    if (target->IsCrashed()) {
620      target->GetController().Reload(true);
621    } else if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE &&
622        target->GetURL() != params->url) {
623      LoadURLInContents(target, params->url, params);
624    }
625
626    // If the singleton tab isn't already selected, select it.
627    if (params->source_contents != params->target_contents) {
628      params->browser->tab_strip_model()->ActivateTabAt(singleton_index,
629                                                        user_initiated);
630    }
631  }
632
633  if (params->disposition != CURRENT_TAB) {
634    content::NotificationService::current()->Notify(
635        chrome::NOTIFICATION_TAB_ADDED,
636        content::Source<content::WebContentsDelegate>(params->browser),
637        content::Details<WebContents>(params->target_contents));
638  }
639}
640
641bool IsURLAllowedInIncognito(const GURL& url,
642                             content::BrowserContext* browser_context) {
643  if (url.scheme() == chrome::kViewSourceScheme) {
644    // A view-source URL is allowed in incognito mode only if the URL itself
645    // is allowed in incognito mode. Remove the "view-source:" from the start
646    // of the URL and validate the rest.
647    std::string stripped_spec = url.spec();
648    DCHECK_GT(stripped_spec.size(), strlen(kViewSourceScheme));
649    stripped_spec.erase(0, strlen(kViewSourceScheme)+1);
650    GURL stripped_url(stripped_spec);
651    return stripped_url.is_valid() &&
652        IsURLAllowedInIncognito(stripped_url, browser_context);
653  }
654  // Most URLs are allowed in incognito; the following are exceptions.
655  // chrome://extensions is on the list because it redirects to
656  // chrome://settings.
657  if (url.scheme() == chrome::kChromeUIScheme &&
658      (url.host() == chrome::kChromeUISettingsHost ||
659       url.host() == chrome::kChromeUISettingsFrameHost ||
660       url.host() == chrome::kChromeUIExtensionsHost ||
661       url.host() == chrome::kChromeUIBookmarksHost ||
662       url.host() == chrome::kChromeUISyncPromoHost ||
663       url.host() == chrome::kChromeUIUberHost)) {
664    return false;
665  }
666
667  GURL rewritten_url = url;
668  bool reverse_on_redirect = false;
669  content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary(
670      &rewritten_url, browser_context, &reverse_on_redirect);
671
672  // Some URLs are mapped to uber subpages. Do not allow them in incognito.
673  return !(rewritten_url.scheme() == chrome::kChromeUIScheme &&
674           rewritten_url.host() == chrome::kChromeUIUberHost);
675}
676
677}  // namespace chrome
678