extension_tab_util.cc revision 116680a4aac90f2aa7413d9095a592090648e557
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file.
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "apps/app_window.h"
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "apps/app_window_registry.h"
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/chrome_extension_function.h"
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/window_controller.h"
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/window_controller_list.h"
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/sessions/session_id.h"
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser_iterator.h"
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/extensions/api/tabs.h"
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/extensions/manifest_url_handler.h"
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/url_constants.h"
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "components/url_fixer/url_fixer.h"
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/browser/favicon_status.h"
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/browser/navigation_entry.h"
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "content/public/browser/web_contents.h"
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/common/constants.h"
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/common/error_utils.h"
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/common/extension.h"
3409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)#include "extensions/common/manifest_constants.h"
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/common/manifest_handlers/incognito_info.h"
36c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)#include "extensions/common/permissions/api_permission.h"
37591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "extensions/common/permissions/permissions_data.h"
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "url/gurl.h"
397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
40591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdochusing apps::AppWindow;
4109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)using content::NavigationEntry;
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using content::WebContents;
43e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
44e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)namespace extensions {
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)namespace {
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
48c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace keys = tabs_constants;
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WindowController* GetAppWindowController(const WebContents* contents) {
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  apps::AppWindowRegistry* registry = apps::AppWindowRegistry::Get(profile);
53  if (!registry)
54    return NULL;
55  AppWindow* app_window =
56      registry->GetAppWindowForRenderViewHost(contents->GetRenderViewHost());
57  if (!app_window)
58    return NULL;
59  return WindowControllerList::GetInstance()->FindWindowById(
60      app_window->session_id().id());
61}
62
63// |error_message| can optionally be passed in and will be set with an
64// appropriate message if the window cannot be found by id.
65Browser* GetBrowserInProfileWithId(Profile* profile,
66                                   const int window_id,
67                                   bool include_incognito,
68                                   std::string* error_message) {
69  Profile* incognito_profile =
70      include_incognito && profile->HasOffTheRecordProfile()
71          ? profile->GetOffTheRecordProfile()
72          : NULL;
73  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
74    Browser* browser = *it;
75    if ((browser->profile() == profile ||
76         browser->profile() == incognito_profile) &&
77        ExtensionTabUtil::GetWindowId(browser) == window_id &&
78        browser->window()) {
79      return browser;
80    }
81  }
82
83  if (error_message)
84    *error_message = ErrorUtils::FormatErrorMessage(
85        keys::kWindowNotFoundError, base::IntToString(window_id));
86
87  return NULL;
88}
89
90Browser* CreateBrowser(ChromeUIThreadExtensionFunction* function,
91                       int window_id,
92                       std::string* error) {
93  content::WebContents* web_contents = function->GetAssociatedWebContents();
94  DCHECK(web_contents);
95  DCHECK(web_contents->GetNativeView());
96  DCHECK(!chrome::FindBrowserWithWebContents(web_contents));
97
98  chrome::HostDesktopType desktop_type =
99      chrome::GetHostDesktopTypeForNativeView(web_contents->GetNativeView());
100  Browser::CreateParams params(
101      Browser::TYPE_TABBED, function->GetProfile(), desktop_type);
102  Browser* browser = new Browser(params);
103  browser->window()->Show();
104  return browser;
105}
106
107}  // namespace
108
109ExtensionTabUtil::OpenTabParams::OpenTabParams()
110    : create_browser_if_needed(false) {
111}
112
113ExtensionTabUtil::OpenTabParams::~OpenTabParams() {
114}
115
116// Opens a new tab for a given extension. Returns NULL and sets |error| if an
117// error occurs.
118base::DictionaryValue* ExtensionTabUtil::OpenTab(
119    ChromeUIThreadExtensionFunction* function,
120    const OpenTabParams& params,
121    std::string* error) {
122  // windowId defaults to "current" window.
123  int window_id = extension_misc::kCurrentWindowId;
124  if (params.window_id.get())
125    window_id = *params.window_id;
126
127  Browser* browser = GetBrowserFromWindowID(function, window_id, error);
128  if (!browser) {
129    if (!params.create_browser_if_needed) {
130      return NULL;
131    }
132    browser = CreateBrowser(function, window_id, error);
133    if (!browser)
134      return NULL;
135  }
136
137  // Ensure the selected browser is tabbed.
138  if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
139    browser = chrome::FindTabbedBrowser(function->GetProfile(),
140                                        function->include_incognito(),
141                                        browser->host_desktop_type());
142
143  if (!browser || !browser->window()) {
144    // TODO(rpaquay): Error message?
145    return NULL;
146  }
147
148  // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
149  // represents the active tab.
150  WebContents* opener = NULL;
151  if (params.opener_tab_id.get()) {
152    int opener_id = *params.opener_tab_id;
153
154    if (!ExtensionTabUtil::GetTabById(opener_id,
155                                      function->GetProfile(),
156                                      function->include_incognito(),
157                                      NULL,
158                                      NULL,
159                                      &opener,
160                                      NULL)) {
161      // TODO(rpaquay): Error message?
162      return NULL;
163    }
164  }
165
166  // TODO(rafaelw): handle setting remaining tab properties:
167  // -title
168  // -favIconUrl
169
170  GURL url;
171  if (params.url.get()) {
172    std::string url_string= *params.url;
173    url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
174        url_string, function->GetExtension());
175    if (!url.is_valid()) {
176      *error =
177          ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
178      return NULL;
179    }
180  } else {
181    url = GURL(chrome::kChromeUINewTabURL);
182  }
183
184  // Don't let extensions crash the browser or renderers.
185  if (ExtensionTabUtil::IsCrashURL(url)) {
186    *error = keys::kNoCrashBrowserError;
187    return NULL;
188  }
189
190  // Default to foreground for the new tab. The presence of 'active' property
191  // will override this default.
192  bool active = true;
193  if (params.active.get())
194    active = *params.active;
195
196  // Default to not pinning the tab. Setting the 'pinned' property to true
197  // will override this default.
198  bool pinned = false;
199  if (params.pinned.get())
200    pinned = *params.pinned;
201
202  // We can't load extension URLs into incognito windows unless the extension
203  // uses split mode. Special case to fall back to a tabbed window.
204  if (url.SchemeIs(kExtensionScheme) &&
205      !IncognitoInfo::IsSplitMode(function->GetExtension()) &&
206      browser->profile()->IsOffTheRecord()) {
207    Profile* profile = browser->profile()->GetOriginalProfile();
208    chrome::HostDesktopType desktop_type = browser->host_desktop_type();
209
210    browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
211    if (!browser) {
212      browser = new Browser(
213          Browser::CreateParams(Browser::TYPE_TABBED, profile, desktop_type));
214      browser->window()->Show();
215    }
216  }
217
218  // If index is specified, honor the value, but keep it bound to
219  // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
220  int index = -1;
221  if (params.index.get())
222    index = *params.index;
223
224  TabStripModel* tab_strip = browser->tab_strip_model();
225
226  index = std::min(std::max(index, -1), tab_strip->count());
227
228  int add_types = active ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE;
229  add_types |= TabStripModel::ADD_FORCE_INDEX;
230  if (pinned)
231    add_types |= TabStripModel::ADD_PINNED;
232  chrome::NavigateParams navigate_params(
233      browser, url, content::PAGE_TRANSITION_LINK);
234  navigate_params.disposition =
235      active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
236  navigate_params.tabstrip_index = index;
237  navigate_params.tabstrip_add_types = add_types;
238  chrome::Navigate(&navigate_params);
239
240  // The tab may have been created in a different window, so make sure we look
241  // at the right tab strip.
242  tab_strip = navigate_params.browser->tab_strip_model();
243  int new_index =
244      tab_strip->GetIndexOfWebContents(navigate_params.target_contents);
245  if (opener)
246    tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
247
248  if (active)
249    navigate_params.target_contents->SetInitialFocus();
250
251  // Return data about the newly created tab.
252  return ExtensionTabUtil::CreateTabValue(navigate_params.target_contents,
253                                          tab_strip,
254                                          new_index,
255                                          function->GetExtension());
256}
257
258Browser* ExtensionTabUtil::GetBrowserFromWindowID(
259    ChromeUIThreadExtensionFunction* function,
260    int window_id,
261    std::string* error) {
262  if (window_id == extension_misc::kCurrentWindowId) {
263    Browser* result = function->GetCurrentBrowser();
264    if (!result || !result->window()) {
265      if (error)
266        *error = keys::kNoCurrentWindowError;
267      return NULL;
268    }
269    return result;
270  } else {
271    return GetBrowserInProfileWithId(function->GetProfile(),
272                                     window_id,
273                                     function->include_incognito(),
274                                     error);
275  }
276}
277
278int ExtensionTabUtil::GetWindowId(const Browser* browser) {
279  return browser->session_id().id();
280}
281
282int ExtensionTabUtil::GetWindowIdOfTabStripModel(
283    const TabStripModel* tab_strip_model) {
284  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
285    if (it->tab_strip_model() == tab_strip_model)
286      return GetWindowId(*it);
287  }
288  return -1;
289}
290
291int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
292  return SessionID::IdForTab(web_contents);
293}
294
295std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
296  return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
297}
298
299int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
300  return SessionID::IdForWindowContainingTab(web_contents);
301}
302
303base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
304    WebContents* contents,
305    TabStripModel* tab_strip,
306    int tab_index,
307    const Extension* extension) {
308  // If we have a matching AppWindow with a controller, get the tab value
309  // from its controller instead.
310  WindowController* controller = GetAppWindowController(contents);
311  if (controller &&
312      (!extension || controller->IsVisibleToExtension(extension))) {
313    return controller->CreateTabValue(extension, tab_index);
314  }
315  base::DictionaryValue* result =
316      CreateTabValue(contents, tab_strip, tab_index);
317  ScrubTabValueForExtension(contents, extension, result);
318  return result;
319}
320
321base::ListValue* ExtensionTabUtil::CreateTabList(
322    const Browser* browser,
323    const Extension* extension) {
324  base::ListValue* tab_list = new base::ListValue();
325  TabStripModel* tab_strip = browser->tab_strip_model();
326  for (int i = 0; i < tab_strip->count(); ++i) {
327    tab_list->Append(CreateTabValue(tab_strip->GetWebContentsAt(i),
328                                    tab_strip,
329                                    i,
330                                    extension));
331  }
332
333  return tab_list;
334}
335
336base::DictionaryValue* ExtensionTabUtil::CreateTabValue(
337    WebContents* contents,
338    TabStripModel* tab_strip,
339    int tab_index) {
340  // If we have a matching AppWindow with a controller, get the tab value
341  // from its controller instead.
342  WindowController* controller = GetAppWindowController(contents);
343  if (controller)
344    return controller->CreateTabValue(NULL, tab_index);
345
346  if (!tab_strip)
347    ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
348
349  base::DictionaryValue* result = new base::DictionaryValue();
350  bool is_loading = contents->IsLoading();
351  result->SetInteger(keys::kIdKey, GetTabId(contents));
352  result->SetInteger(keys::kIndexKey, tab_index);
353  result->SetInteger(keys::kWindowIdKey, GetWindowIdOfTab(contents));
354  result->SetString(keys::kStatusKey, GetTabStatusText(is_loading));
355  result->SetBoolean(keys::kActiveKey,
356                     tab_strip && tab_index == tab_strip->active_index());
357  result->SetBoolean(keys::kSelectedKey,
358                     tab_strip && tab_index == tab_strip->active_index());
359  result->SetBoolean(keys::kHighlightedKey,
360                   tab_strip && tab_strip->IsTabSelected(tab_index));
361  result->SetBoolean(keys::kPinnedKey,
362                     tab_strip && tab_strip->IsTabPinned(tab_index));
363  result->SetBoolean(keys::kIncognitoKey,
364                     contents->GetBrowserContext()->IsOffTheRecord());
365  result->SetInteger(keys::kWidthKey,
366                     contents->GetContainerBounds().size().width());
367  result->SetInteger(keys::kHeightKey,
368                     contents->GetContainerBounds().size().height());
369
370  // Privacy-sensitive fields: these should be stripped off by
371  // ScrubTabValueForExtension if the extension should not see them.
372  result->SetString(keys::kUrlKey, contents->GetURL().spec());
373  result->SetString(keys::kTitleKey, contents->GetTitle());
374  if (!is_loading) {
375    NavigationEntry* entry = contents->GetController().GetVisibleEntry();
376    if (entry && entry->GetFavicon().valid)
377      result->SetString(keys::kFaviconUrlKey, entry->GetFavicon().url.spec());
378  }
379
380  if (tab_strip) {
381    WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
382    if (opener)
383      result->SetInteger(keys::kOpenerTabIdKey, GetTabId(opener));
384  }
385
386  return result;
387}
388
389void ExtensionTabUtil::ScrubTabValueForExtension(
390    WebContents* contents,
391    const Extension* extension,
392    base::DictionaryValue* tab_info) {
393  bool has_permission = extension &&
394                        extension->permissions_data()->HasAPIPermissionForTab(
395                            GetTabId(contents), APIPermission::kTab);
396
397  if (!has_permission) {
398    tab_info->Remove(keys::kUrlKey, NULL);
399    tab_info->Remove(keys::kTitleKey, NULL);
400    tab_info->Remove(keys::kFaviconUrlKey, NULL);
401  }
402}
403
404void ExtensionTabUtil::ScrubTabForExtension(const Extension* extension,
405                                            api::tabs::Tab* tab) {
406  bool has_permission =
407      extension &&
408      extension->permissions_data()->HasAPIPermission(APIPermission::kTab);
409
410  if (!has_permission) {
411    tab->url.reset();
412    tab->title.reset();
413    tab->fav_icon_url.reset();
414  }
415}
416
417bool ExtensionTabUtil::GetTabStripModel(const WebContents* web_contents,
418                                        TabStripModel** tab_strip_model,
419                                        int* tab_index) {
420  DCHECK(web_contents);
421  DCHECK(tab_strip_model);
422  DCHECK(tab_index);
423
424  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
425    TabStripModel* tab_strip = it->tab_strip_model();
426    int index = tab_strip->GetIndexOfWebContents(web_contents);
427    if (index != -1) {
428      *tab_strip_model = tab_strip;
429      *tab_index = index;
430      return true;
431    }
432  }
433
434  return false;
435}
436
437bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
438                                     WebContents** contents,
439                                     int* tab_id) {
440  DCHECK(browser);
441  DCHECK(contents);
442
443  *contents = browser->tab_strip_model()->GetActiveWebContents();
444  if (*contents) {
445    if (tab_id)
446      *tab_id = GetTabId(*contents);
447    return true;
448  }
449
450  return false;
451}
452
453bool ExtensionTabUtil::GetTabById(int tab_id,
454                                  Profile* profile,
455                                  bool include_incognito,
456                                  Browser** browser,
457                                  TabStripModel** tab_strip,
458                                  WebContents** contents,
459                                  int* tab_index) {
460  Profile* incognito_profile =
461      include_incognito && profile->HasOffTheRecordProfile() ?
462          profile->GetOffTheRecordProfile() : NULL;
463  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
464    Browser* target_browser = *it;
465    if (target_browser->profile() == profile ||
466        target_browser->profile() == incognito_profile) {
467      TabStripModel* target_tab_strip = target_browser->tab_strip_model();
468      for (int i = 0; i < target_tab_strip->count(); ++i) {
469        WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
470        if (SessionID::IdForTab(target_contents) == tab_id) {
471          if (browser)
472            *browser = target_browser;
473          if (tab_strip)
474            *tab_strip = target_tab_strip;
475          if (contents)
476            *contents = target_contents;
477          if (tab_index)
478            *tab_index = i;
479          return true;
480        }
481      }
482    }
483  }
484  return false;
485}
486
487GURL ExtensionTabUtil::ResolvePossiblyRelativeURL(const std::string& url_string,
488                                                  const Extension* extension) {
489  GURL url = GURL(url_string);
490  if (!url.is_valid())
491    url = extension->GetResourceURL(url_string);
492
493  return url;
494}
495
496bool ExtensionTabUtil::IsCrashURL(const GURL& url) {
497  // Check a fixed-up URL, to normalize the scheme and parse hosts correctly.
498  GURL fixed_url =
499      url_fixer::FixupURL(url.possibly_invalid_spec(), std::string());
500  return (fixed_url.SchemeIs(content::kChromeUIScheme) &&
501          (fixed_url.host() == content::kChromeUIBrowserCrashHost ||
502           fixed_url.host() == chrome::kChromeUICrashHost));
503}
504
505void ExtensionTabUtil::CreateTab(WebContents* web_contents,
506                                 const std::string& extension_id,
507                                 WindowOpenDisposition disposition,
508                                 const gfx::Rect& initial_pos,
509                                 bool user_gesture) {
510  Profile* profile =
511      Profile::FromBrowserContext(web_contents->GetBrowserContext());
512  chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
513  Browser* browser = chrome::FindTabbedBrowser(profile, false, active_desktop);
514  const bool browser_created = !browser;
515  if (!browser)
516    browser = new Browser(Browser::CreateParams(profile, active_desktop));
517  chrome::NavigateParams params(browser, web_contents);
518
519  // The extension_app_id parameter ends up as app_name in the Browser
520  // which causes the Browser to return true for is_app().  This affects
521  // among other things, whether the location bar gets displayed.
522  // TODO(mpcomplete): This seems wrong. What if the extension content is hosted
523  // in a tab?
524  if (disposition == NEW_POPUP)
525    params.extension_app_id = extension_id;
526
527  params.disposition = disposition;
528  params.window_bounds = initial_pos;
529  params.window_action = chrome::NavigateParams::SHOW_WINDOW;
530  params.user_gesture = user_gesture;
531  chrome::Navigate(&params);
532
533  // Close the browser if chrome::Navigate created a new one.
534  if (browser_created && (browser != params.browser))
535    browser->window()->Close();
536}
537
538// static
539void ExtensionTabUtil::ForEachTab(
540    const base::Callback<void(WebContents*)>& callback) {
541  for (TabContentsIterator iterator; !iterator.done(); iterator.Next())
542    callback.Run(*iterator);
543}
544
545// static
546WindowController* ExtensionTabUtil::GetWindowControllerOfTab(
547    const WebContents* web_contents) {
548  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
549  if (browser != NULL)
550    return browser->extension_window_controller();
551
552  return NULL;
553}
554
555void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
556                                       Browser* browser) {
557  DCHECK(!ManifestURL::GetOptionsPage(extension).is_empty());
558
559  // Force the options page to open in non-OTR window, because it won't be
560  // able to save settings from OTR.
561  scoped_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
562  if (browser->profile()->IsOffTheRecord()) {
563    displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
564        browser->profile()->GetOriginalProfile(),
565        browser->host_desktop_type()));
566    browser = displayer->browser();
567  }
568
569  content::OpenURLParams params(ManifestURL::GetOptionsPage(extension),
570                                content::Referrer(),
571                                SINGLETON_TAB,
572                                content::PAGE_TRANSITION_LINK,
573                                false);
574  browser->OpenURL(params);
575  browser->window()->Show();
576  WebContents* web_contents =
577      browser->tab_strip_model()->GetActiveWebContents();
578  web_contents->GetDelegate()->ActivateContents(web_contents);
579}
580
581}  // namespace extensions
582