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