• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/api/tabs/tabs_api.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
10 
11 #include "apps/shell_window.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
25 #include "chrome/browser/extensions/api/tabs/windows_util.h"
26 #include "chrome/browser/extensions/extension_function_dispatcher.h"
27 #include "chrome/browser/extensions/extension_function_util.h"
28 #include "chrome/browser/extensions/extension_host.h"
29 #include "chrome/browser/extensions/extension_service.h"
30 #include "chrome/browser/extensions/extension_tab_util.h"
31 #include "chrome/browser/extensions/script_executor.h"
32 #include "chrome/browser/extensions/tab_helper.h"
33 #include "chrome/browser/extensions/window_controller.h"
34 #include "chrome/browser/extensions/window_controller_list.h"
35 #include "chrome/browser/prefs/incognito_mode_prefs.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/translate/translate_tab_helper.h"
38 #include "chrome/browser/ui/apps/chrome_shell_window_delegate.h"
39 #include "chrome/browser/ui/browser.h"
40 #include "chrome/browser/ui/browser_commands.h"
41 #include "chrome/browser/ui/browser_finder.h"
42 #include "chrome/browser/ui/browser_iterator.h"
43 #include "chrome/browser/ui/browser_navigator.h"
44 #include "chrome/browser/ui/browser_tabstrip.h"
45 #include "chrome/browser/ui/browser_window.h"
46 #include "chrome/browser/ui/host_desktop.h"
47 #include "chrome/browser/ui/panels/panel_manager.h"
48 #include "chrome/browser/ui/tabs/tab_strip_model.h"
49 #include "chrome/browser/ui/window_sizer/window_sizer.h"
50 #include "chrome/browser/web_applications/web_app.h"
51 #include "chrome/common/chrome_switches.h"
52 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
53 #include "chrome/common/extensions/api/tabs.h"
54 #include "chrome/common/extensions/api/windows.h"
55 #include "chrome/common/extensions/extension_constants.h"
56 #include "chrome/common/extensions/extension_file_util.h"
57 #include "chrome/common/extensions/extension_l10n_util.h"
58 #include "chrome/common/extensions/extension_messages.h"
59 #include "chrome/common/extensions/message_bundle.h"
60 #include "chrome/common/pref_names.h"
61 #include "chrome/common/translate/language_detection_details.h"
62 #include "chrome/common/url_constants.h"
63 #include "components/user_prefs/pref_registry_syncable.h"
64 #include "content/public/browser/navigation_controller.h"
65 #include "content/public/browser/navigation_entry.h"
66 #include "content/public/browser/notification_details.h"
67 #include "content/public/browser/notification_source.h"
68 #include "content/public/browser/render_process_host.h"
69 #include "content/public/browser/render_view_host.h"
70 #include "content/public/browser/render_widget_host_view.h"
71 #include "content/public/browser/web_contents.h"
72 #include "content/public/browser/web_contents_view.h"
73 #include "content/public/common/url_constants.h"
74 #include "extensions/browser/file_reader.h"
75 #include "extensions/common/constants.h"
76 #include "extensions/common/error_utils.h"
77 #include "extensions/common/extension.h"
78 #include "extensions/common/manifest_constants.h"
79 #include "extensions/common/manifest_handlers/incognito_info.h"
80 #include "extensions/common/permissions/permissions_data.h"
81 #include "extensions/common/user_script.h"
82 #include "skia/ext/image_operations.h"
83 #include "skia/ext/platform_canvas.h"
84 #include "third_party/skia/include/core/SkBitmap.h"
85 #include "ui/base/models/list_selection_model.h"
86 #include "ui/base/ui_base_types.h"
87 
88 #if defined(OS_WIN)
89 #include "win8/util/win8_util.h"
90 #endif  // OS_WIN
91 
92 #if defined(USE_ASH)
93 #include "apps/shell_window_registry.h"
94 #include "ash/ash_switches.h"
95 #include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
96 #endif
97 
98 using apps::ShellWindow;
99 using content::BrowserThread;
100 using content::NavigationController;
101 using content::NavigationEntry;
102 using content::OpenURLParams;
103 using content::Referrer;
104 using content::WebContents;
105 
106 namespace extensions {
107 
108 namespace windows = api::windows;
109 namespace keys = tabs_constants;
110 namespace tabs = api::tabs;
111 
112 using api::tabs::InjectDetails;
113 
114 namespace {
115 
116 // |error_message| can optionally be passed in a will be set with an appropriate
117 // message if the window cannot be found by id.
GetBrowserInProfileWithId(Profile * profile,const int window_id,bool include_incognito,std::string * error_message)118 Browser* GetBrowserInProfileWithId(Profile* profile,
119                                    const int window_id,
120                                    bool include_incognito,
121                                    std::string* error_message) {
122   Profile* incognito_profile =
123       include_incognito && profile->HasOffTheRecordProfile() ?
124           profile->GetOffTheRecordProfile() : NULL;
125   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
126     Browser* browser = *it;
127     if ((browser->profile() == profile ||
128          browser->profile() == incognito_profile) &&
129         ExtensionTabUtil::GetWindowId(browser) == window_id &&
130         browser->window()) {
131       return browser;
132     }
133   }
134 
135   if (error_message)
136     *error_message = ErrorUtils::FormatErrorMessage(
137         keys::kWindowNotFoundError, base::IntToString(window_id));
138 
139   return NULL;
140 }
141 
GetBrowserFromWindowID(ChromeAsyncExtensionFunction * function,int window_id,Browser ** browser)142 bool GetBrowserFromWindowID(ChromeAsyncExtensionFunction* function,
143                             int window_id,
144                             Browser** browser) {
145   if (window_id == extension_misc::kCurrentWindowId) {
146     *browser = function->GetCurrentBrowser();
147     if (!(*browser) || !(*browser)->window()) {
148       function->SetError(keys::kNoCurrentWindowError);
149       return false;
150     }
151   } else {
152     std::string error;
153     *browser = GetBrowserInProfileWithId(function->GetProfile(),
154                                          window_id,
155                                          function->include_incognito(),
156                                          &error);
157     if (!*browser) {
158       function->SetError(error);
159       return false;
160     }
161   }
162   return true;
163 }
164 
165 // |error_message| can optionally be passed in and will be set with an
166 // appropriate message if the tab cannot be found by id.
GetTabById(int tab_id,Profile * profile,bool include_incognito,Browser ** browser,TabStripModel ** tab_strip,content::WebContents ** contents,int * tab_index,std::string * error_message)167 bool GetTabById(int tab_id,
168                 Profile* profile,
169                 bool include_incognito,
170                 Browser** browser,
171                 TabStripModel** tab_strip,
172                 content::WebContents** contents,
173                 int* tab_index,
174                 std::string* error_message) {
175   if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
176                                    browser, tab_strip, contents, tab_index))
177     return true;
178 
179   if (error_message)
180     *error_message = ErrorUtils::FormatErrorMessage(
181         keys::kTabNotFoundError, base::IntToString(tab_id));
182 
183   return false;
184 }
185 
186 // Returns true if either |boolean| is a null pointer, or if |*boolean| and
187 // |value| are equal. This function is used to check if a tab's parameters match
188 // those of the browser.
MatchesBool(bool * boolean,bool value)189 bool MatchesBool(bool* boolean, bool value) {
190   return !boolean || *boolean == value;
191 }
192 
CreateBrowserWindow(const Browser::CreateParams & params,Profile * profile,const std::string & extension_id)193 Browser* CreateBrowserWindow(const Browser::CreateParams& params,
194                              Profile* profile,
195                              const std::string& extension_id) {
196   bool use_existing_browser_window = false;
197 
198 #if defined(OS_WIN)
199   // In windows 8 metro mode we don't allow windows to be created.
200   if (win8::IsSingleWindowMetroMode())
201     use_existing_browser_window = true;
202 #endif  // OS_WIN
203 
204   Browser* new_window = NULL;
205   if (use_existing_browser_window)
206     // The false parameter passed below is to ensure that we find a browser
207     // object matching the profile passed in, instead of the original profile
208     new_window = chrome::FindTabbedBrowser(profile, false,
209                                            params.host_desktop_type);
210 
211   if (!new_window)
212     new_window = new Browser(params);
213   return new_window;
214 }
215 
216 }  // namespace
217 
218 // Windows ---------------------------------------------------------------------
219 
RunImpl()220 bool WindowsGetFunction::RunImpl() {
221   scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
222   EXTENSION_FUNCTION_VALIDATE(params.get());
223 
224   bool populate_tabs = false;
225   if (params->get_info.get() && params->get_info->populate.get())
226     populate_tabs = *params->get_info->populate;
227 
228   WindowController* controller;
229   if (!windows_util::GetWindowFromWindowID(this,
230                                            params->window_id,
231                                            &controller)) {
232     return false;
233   }
234 
235   if (populate_tabs)
236     SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
237   else
238     SetResult(controller->CreateWindowValue());
239   return true;
240 }
241 
RunImpl()242 bool WindowsGetCurrentFunction::RunImpl() {
243   scoped_ptr<windows::GetCurrent::Params> params(
244       windows::GetCurrent::Params::Create(*args_));
245   EXTENSION_FUNCTION_VALIDATE(params.get());
246 
247   bool populate_tabs = false;
248   if (params->get_info.get() && params->get_info->populate.get())
249     populate_tabs = *params->get_info->populate;
250 
251   WindowController* controller;
252   if (!windows_util::GetWindowFromWindowID(this,
253                                            extension_misc::kCurrentWindowId,
254                                            &controller)) {
255     return false;
256   }
257   if (populate_tabs)
258     SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
259   else
260     SetResult(controller->CreateWindowValue());
261   return true;
262 }
263 
RunImpl()264 bool WindowsGetLastFocusedFunction::RunImpl() {
265   scoped_ptr<windows::GetLastFocused::Params> params(
266       windows::GetLastFocused::Params::Create(*args_));
267   EXTENSION_FUNCTION_VALIDATE(params.get());
268 
269   bool populate_tabs = false;
270   if (params->get_info.get() && params->get_info->populate.get())
271     populate_tabs = *params->get_info->populate;
272 
273   // Note: currently this returns the last active browser. If we decide to
274   // include other window types (e.g. panels), we will need to add logic to
275   // WindowControllerList that mirrors the active behavior of BrowserList.
276   Browser* browser = chrome::FindAnyBrowser(
277       GetProfile(), include_incognito(), chrome::GetActiveDesktop());
278   if (!browser || !browser->window()) {
279     error_ = keys::kNoLastFocusedWindowError;
280     return false;
281   }
282   WindowController* controller =
283       browser->extension_window_controller();
284   if (populate_tabs)
285     SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
286   else
287     SetResult(controller->CreateWindowValue());
288   return true;
289 }
290 
RunImpl()291 bool WindowsGetAllFunction::RunImpl() {
292   scoped_ptr<windows::GetAll::Params> params(
293       windows::GetAll::Params::Create(*args_));
294   EXTENSION_FUNCTION_VALIDATE(params.get());
295 
296   bool populate_tabs = false;
297   if (params->get_info.get() && params->get_info->populate.get())
298     populate_tabs = *params->get_info->populate;
299 
300   base::ListValue* window_list = new base::ListValue();
301   const WindowControllerList::ControllerList& windows =
302       WindowControllerList::GetInstance()->windows();
303   for (WindowControllerList::ControllerList::const_iterator iter =
304            windows.begin();
305        iter != windows.end(); ++iter) {
306     if (!this->CanOperateOnWindow(*iter))
307       continue;
308     if (populate_tabs)
309       window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension()));
310     else
311       window_list->Append((*iter)->CreateWindowValue());
312   }
313   SetResult(window_list);
314   return true;
315 }
316 
ShouldOpenIncognitoWindow(const windows::Create::Params::CreateData * create_data,std::vector<GURL> * urls,bool * is_error)317 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
318     const windows::Create::Params::CreateData* create_data,
319     std::vector<GURL>* urls, bool* is_error) {
320   *is_error = false;
321   const IncognitoModePrefs::Availability incognito_availability =
322       IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
323   bool incognito = false;
324   if (create_data && create_data->incognito) {
325     incognito = *create_data->incognito;
326     if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
327       error_ = keys::kIncognitoModeIsDisabled;
328       *is_error = true;
329       return false;
330     }
331     if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
332       error_ = keys::kIncognitoModeIsForced;
333       *is_error = true;
334       return false;
335     }
336   } else if (incognito_availability == IncognitoModePrefs::FORCED) {
337     // If incognito argument is not specified explicitly, we default to
338     // incognito when forced so by policy.
339     incognito = true;
340   }
341 
342   // Remove all URLs that are not allowed in an incognito session. Note that a
343   // ChromeOS guest session is not considered incognito in this case.
344   if (incognito && !GetProfile()->IsGuestSession()) {
345     std::string first_url_erased;
346     for (size_t i = 0; i < urls->size();) {
347       if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
348         i++;
349       } else {
350         if (first_url_erased.empty())
351           first_url_erased = (*urls)[i].spec();
352         urls->erase(urls->begin() + i);
353       }
354     }
355     if (urls->empty() && !first_url_erased.empty()) {
356       error_ = ErrorUtils::FormatErrorMessage(
357           keys::kURLsNotAllowedInIncognitoError, first_url_erased);
358       *is_error = true;
359       return false;
360     }
361   }
362   return incognito;
363 }
364 
RunImpl()365 bool WindowsCreateFunction::RunImpl() {
366   scoped_ptr<windows::Create::Params> params(
367       windows::Create::Params::Create(*args_));
368   EXTENSION_FUNCTION_VALIDATE(params);
369   std::vector<GURL> urls;
370   TabStripModel* source_tab_strip = NULL;
371   int tab_index = -1;
372 
373   windows::Create::Params::CreateData* create_data = params->create_data.get();
374 
375   // Look for optional url.
376   if (create_data && create_data->url) {
377     std::vector<std::string> url_strings;
378     // First, get all the URLs the client wants to open.
379     if (create_data->url->as_string)
380       url_strings.push_back(*create_data->url->as_string);
381     else if (create_data->url->as_strings)
382       url_strings.swap(*create_data->url->as_strings);
383 
384     // Second, resolve, validate and convert them to GURLs.
385     for (std::vector<std::string>::iterator i = url_strings.begin();
386          i != url_strings.end(); ++i) {
387       GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
388           *i, GetExtension());
389       if (!url.is_valid()) {
390         error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
391         return false;
392       }
393       // Don't let the extension crash the browser or renderers.
394       if (ExtensionTabUtil::IsCrashURL(url)) {
395         error_ = keys::kNoCrashBrowserError;
396         return false;
397       }
398       urls.push_back(url);
399     }
400   }
401 
402   // Look for optional tab id.
403   if (create_data && create_data->tab_id) {
404     // Find the tab. |source_tab_strip| and |tab_index| will later be used to
405     // move the tab into the created window.
406     if (!GetTabById(*create_data->tab_id,
407                     GetProfile(),
408                     include_incognito(),
409                     NULL,
410                     &source_tab_strip,
411                     NULL,
412                     &tab_index,
413                     &error_))
414       return false;
415   }
416 
417   Profile* window_profile = GetProfile();
418   Browser::Type window_type = Browser::TYPE_TABBED;
419   bool create_panel = false;
420 
421   // panel_create_mode only applies if create_panel = true
422   PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
423 
424   gfx::Rect window_bounds;
425   bool focused = true;
426   bool saw_focus_key = false;
427   std::string extension_id;
428 
429   // Decide whether we are opening a normal window or an incognito window.
430   bool is_error = true;
431   bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
432                                                          &is_error);
433   if (is_error) {
434     // error_ member variable is set inside of ShouldOpenIncognitoWindow.
435     return false;
436   }
437   if (open_incognito_window) {
438     window_profile = window_profile->GetOffTheRecordProfile();
439   }
440 
441   if (create_data) {
442     // Figure out window type before figuring out bounds so that default
443     // bounds can be set according to the window type.
444     switch (create_data->type) {
445       case windows::Create::Params::CreateData::TYPE_POPUP:
446         window_type = Browser::TYPE_POPUP;
447         extension_id = GetExtension()->id();
448         break;
449       case windows::Create::Params::CreateData::TYPE_PANEL:
450       case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
451         extension_id = GetExtension()->id();
452         bool use_panels = false;
453 #if !defined(OS_ANDROID)
454         use_panels = PanelManager::ShouldUsePanels(extension_id);
455 #endif
456         if (use_panels) {
457           create_panel = true;
458           // Non-ash supports both docked and detached panel types.
459           if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
460               create_data->type ==
461               windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
462             panel_create_mode = PanelManager::CREATE_AS_DETACHED;
463           }
464         } else {
465           window_type = Browser::TYPE_POPUP;
466         }
467         break;
468       }
469       case windows::Create::Params::CreateData::TYPE_NONE:
470       case windows::Create::Params::CreateData::TYPE_NORMAL:
471         break;
472       default:
473         error_ = keys::kInvalidWindowTypeError;
474         return false;
475     }
476 
477     // Initialize default window bounds according to window type.
478     if (window_type == Browser::TYPE_TABBED ||
479         window_type == Browser::TYPE_POPUP ||
480         create_panel) {
481       // Try to position the new browser relative to its originating
482       // browser window. The call offsets the bounds by kWindowTilePixels
483       // (defined in WindowSizer to be 10).
484       //
485       // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
486       // GetBrowserWindowBounds will default to saved "default" values for
487       // the app.
488       ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
489       WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
490                                                       gfx::Rect(),
491                                                       GetCurrentBrowser(),
492                                                       &window_bounds,
493                                                       &show_state);
494     }
495 
496     if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
497       window_bounds.set_origin(
498           PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
499     }
500 
501     // Any part of the bounds can optionally be set by the caller.
502     if (create_data->left)
503       window_bounds.set_x(*create_data->left);
504 
505     if (create_data->top)
506       window_bounds.set_y(*create_data->top);
507 
508     if (create_data->width)
509       window_bounds.set_width(*create_data->width);
510 
511     if (create_data->height)
512       window_bounds.set_height(*create_data->height);
513 
514     if (create_data->focused) {
515       focused = *create_data->focused;
516       saw_focus_key = true;
517     }
518   }
519 
520   if (create_panel) {
521     if (urls.empty())
522       urls.push_back(GURL(chrome::kChromeUINewTabURL));
523 
524 #if defined(USE_ASH)
525     if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
526       ShellWindow::CreateParams create_params;
527       create_params.window_type = ShellWindow::WINDOW_TYPE_V1_PANEL;
528       create_params.bounds = window_bounds;
529       create_params.focused = saw_focus_key && focused;
530       ShellWindow* shell_window = new ShellWindow(
531           window_profile, new ChromeShellWindowDelegate(),
532           GetExtension());
533       AshPanelContents* ash_panel_contents = new AshPanelContents(shell_window);
534       shell_window->Init(urls[0], ash_panel_contents, create_params);
535       SetResult(ash_panel_contents->GetExtensionWindowController()->
536                 CreateWindowValueWithTabs(GetExtension()));
537       return true;
538     }
539 #endif
540     std::string title =
541         web_app::GenerateApplicationNameFromExtensionId(extension_id);
542     // Note: Panels ignore all but the first url provided.
543     Panel* panel = PanelManager::GetInstance()->CreatePanel(
544         title, window_profile, urls[0], window_bounds, panel_create_mode);
545 
546     // Unlike other window types, Panels do not take focus by default.
547     if (!saw_focus_key || !focused)
548       panel->ShowInactive();
549     else
550       panel->Show();
551 
552     SetResult(
553         panel->extension_window_controller()->CreateWindowValueWithTabs(
554             GetExtension()));
555     return true;
556   }
557 
558   // Create a new BrowserWindow.
559   chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
560   if (create_panel)
561     window_type = Browser::TYPE_POPUP;
562   Browser::CreateParams create_params(window_type, window_profile,
563                                       host_desktop_type);
564   if (extension_id.empty()) {
565     create_params.initial_bounds = window_bounds;
566   } else {
567     create_params = Browser::CreateParams::CreateForApp(
568         window_type,
569         web_app::GenerateApplicationNameFromExtensionId(extension_id),
570         window_bounds,
571         window_profile,
572         host_desktop_type);
573   }
574   create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
575   create_params.host_desktop_type = chrome::GetActiveDesktop();
576 
577   Browser* new_window = CreateBrowserWindow(create_params, window_profile,
578                                             extension_id);
579 
580   for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
581     WebContents* tab = chrome::AddSelectedTabWithURL(
582         new_window, *i, content::PAGE_TRANSITION_LINK);
583     if (create_panel) {
584       TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
585     }
586   }
587 
588   WebContents* contents = NULL;
589   // Move the tab into the created window only if it's an empty popup or it's
590   // a tabbed window.
591   if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
592       window_type == Browser::TYPE_TABBED) {
593     if (source_tab_strip)
594       contents = source_tab_strip->DetachWebContentsAt(tab_index);
595     if (contents) {
596       TabStripModel* target_tab_strip = new_window->tab_strip_model();
597       target_tab_strip->InsertWebContentsAt(urls.size(), contents,
598                                             TabStripModel::ADD_NONE);
599     }
600   }
601   // Create a new tab if the created window is still empty. Don't create a new
602   // tab when it is intended to create an empty popup.
603   if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
604     chrome::NewTab(new_window);
605   }
606   chrome::SelectNumberedTab(new_window, 0);
607 
608   // Unlike other window types, Panels do not take focus by default.
609   if (!saw_focus_key && create_panel)
610     focused = false;
611 
612   if (focused)
613     new_window->window()->Show();
614   else
615     new_window->window()->ShowInactive();
616 
617   if (new_window->profile()->IsOffTheRecord() &&
618       !GetProfile()->IsOffTheRecord() && !include_incognito()) {
619     // Don't expose incognito windows if extension itself works in non-incognito
620     // profile and CanCrossIncognito isn't allowed.
621     SetResult(Value::CreateNullValue());
622   } else {
623     SetResult(
624         new_window->extension_window_controller()->CreateWindowValueWithTabs(
625             GetExtension()));
626   }
627 
628   return true;
629 }
630 
RunImpl()631 bool WindowsUpdateFunction::RunImpl() {
632   scoped_ptr<windows::Update::Params> params(
633       windows::Update::Params::Create(*args_));
634   EXTENSION_FUNCTION_VALIDATE(params);
635 
636   WindowController* controller;
637   if (!windows_util::GetWindowFromWindowID(this, params->window_id,
638                                             &controller))
639     return false;
640 
641 #if defined(OS_WIN)
642   // Silently ignore changes on the window for metro mode.
643   if (win8::IsSingleWindowMetroMode()) {
644     SetResult(controller->CreateWindowValue());
645     return true;
646   }
647 #endif
648 
649   ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;  // No change.
650   switch (params->update_info.state) {
651     case windows::Update::Params::UpdateInfo::STATE_NORMAL:
652       show_state = ui::SHOW_STATE_NORMAL;
653       break;
654     case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
655       show_state = ui::SHOW_STATE_MINIMIZED;
656       break;
657     case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
658       show_state = ui::SHOW_STATE_MAXIMIZED;
659       break;
660     case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
661       show_state = ui::SHOW_STATE_FULLSCREEN;
662       break;
663     case windows::Update::Params::UpdateInfo::STATE_NONE:
664       break;
665     default:
666       error_ = keys::kInvalidWindowStateError;
667       return false;
668   }
669 
670   if (show_state != ui::SHOW_STATE_FULLSCREEN &&
671       show_state != ui::SHOW_STATE_DEFAULT)
672     controller->SetFullscreenMode(false, GetExtension()->url());
673 
674   switch (show_state) {
675     case ui::SHOW_STATE_MINIMIZED:
676       controller->window()->Minimize();
677       break;
678     case ui::SHOW_STATE_MAXIMIZED:
679       controller->window()->Maximize();
680       break;
681     case ui::SHOW_STATE_FULLSCREEN:
682       if (controller->window()->IsMinimized() ||
683           controller->window()->IsMaximized())
684         controller->window()->Restore();
685       controller->SetFullscreenMode(true, GetExtension()->url());
686       break;
687     case ui::SHOW_STATE_NORMAL:
688       controller->window()->Restore();
689       break;
690     default:
691       break;
692   }
693 
694   gfx::Rect bounds;
695   if (controller->window()->IsMinimized())
696     bounds = controller->window()->GetRestoredBounds();
697   else
698     bounds = controller->window()->GetBounds();
699   bool set_bounds = false;
700 
701   // Any part of the bounds can optionally be set by the caller.
702   if (params->update_info.left) {
703     bounds.set_x(*params->update_info.left);
704     set_bounds = true;
705   }
706 
707   if (params->update_info.top) {
708     bounds.set_y(*params->update_info.top);
709     set_bounds = true;
710   }
711 
712   if (params->update_info.width) {
713     bounds.set_width(*params->update_info.width);
714     set_bounds = true;
715   }
716 
717   if (params->update_info.height) {
718     bounds.set_height(*params->update_info.height);
719     set_bounds = true;
720   }
721 
722   if (set_bounds) {
723     if (show_state == ui::SHOW_STATE_MINIMIZED ||
724         show_state == ui::SHOW_STATE_MAXIMIZED ||
725         show_state == ui::SHOW_STATE_FULLSCREEN) {
726       error_ = keys::kInvalidWindowStateError;
727       return false;
728     }
729     // TODO(varkha): Updating bounds during a drag can cause problems and a more
730     // general solution is needed. See http://crbug.com/251813 .
731     controller->window()->SetBounds(bounds);
732   }
733 
734   if (params->update_info.focused) {
735     if (*params->update_info.focused) {
736       if (show_state == ui::SHOW_STATE_MINIMIZED) {
737         error_ = keys::kInvalidWindowStateError;
738         return false;
739       }
740       controller->window()->Activate();
741     } else {
742       if (show_state == ui::SHOW_STATE_MAXIMIZED ||
743           show_state == ui::SHOW_STATE_FULLSCREEN) {
744         error_ = keys::kInvalidWindowStateError;
745         return false;
746       }
747       controller->window()->Deactivate();
748     }
749   }
750 
751   if (params->update_info.draw_attention)
752     controller->window()->FlashFrame(*params->update_info.draw_attention);
753 
754   SetResult(controller->CreateWindowValue());
755 
756   return true;
757 }
758 
RunImpl()759 bool WindowsRemoveFunction::RunImpl() {
760   scoped_ptr<windows::Remove::Params> params(
761       windows::Remove::Params::Create(*args_));
762   EXTENSION_FUNCTION_VALIDATE(params);
763 
764   WindowController* controller;
765   if (!windows_util::GetWindowFromWindowID(this, params->window_id,
766                                            &controller))
767     return false;
768 
769 #if defined(OS_WIN)
770   // In Windows 8 metro mode, an existing Browser instance is reused for
771   // hosting the extension tab. We should not be closing it as we don't own it.
772   if (win8::IsSingleWindowMetroMode())
773     return false;
774 #endif
775 
776   WindowController::Reason reason;
777   if (!controller->CanClose(&reason)) {
778     if (reason == WindowController::REASON_NOT_EDITABLE)
779       error_ = keys::kTabStripNotEditableError;
780     return false;
781   }
782   controller->window()->Close();
783   return true;
784 }
785 
786 // Tabs ------------------------------------------------------------------------
787 
RunImpl()788 bool TabsGetSelectedFunction::RunImpl() {
789   // windowId defaults to "current" window.
790   int window_id = extension_misc::kCurrentWindowId;
791 
792   scoped_ptr<tabs::GetSelected::Params> params(
793       tabs::GetSelected::Params::Create(*args_));
794   EXTENSION_FUNCTION_VALIDATE(params.get());
795   if (params->window_id.get())
796     window_id = *params->window_id;
797 
798   Browser* browser = NULL;
799   if (!GetBrowserFromWindowID(this, window_id, &browser))
800     return false;
801 
802   TabStripModel* tab_strip = browser->tab_strip_model();
803   WebContents* contents = tab_strip->GetActiveWebContents();
804   if (!contents) {
805     error_ = keys::kNoSelectedTabError;
806     return false;
807   }
808   SetResult(ExtensionTabUtil::CreateTabValue(contents,
809                                              tab_strip,
810                                              tab_strip->active_index(),
811                                              GetExtension()));
812   return true;
813 }
814 
RunImpl()815 bool TabsGetAllInWindowFunction::RunImpl() {
816   scoped_ptr<tabs::GetAllInWindow::Params> params(
817       tabs::GetAllInWindow::Params::Create(*args_));
818   EXTENSION_FUNCTION_VALIDATE(params.get());
819   // windowId defaults to "current" window.
820   int window_id = extension_misc::kCurrentWindowId;
821   if (params->window_id.get())
822     window_id = *params->window_id;
823 
824   Browser* browser = NULL;
825   if (!GetBrowserFromWindowID(this, window_id, &browser))
826     return false;
827 
828   SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension()));
829 
830   return true;
831 }
832 
RunImpl()833 bool TabsQueryFunction::RunImpl() {
834   scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
835   EXTENSION_FUNCTION_VALIDATE(params.get());
836 
837   bool loading_status_set = params->query_info.status !=
838       tabs::Query::Params::QueryInfo::STATUS_NONE;
839   bool loading = params->query_info.status ==
840       tabs::Query::Params::QueryInfo::STATUS_LOADING;
841 
842   // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
843   // not grant access to the content of the tabs, only to seeing their URLs and
844   // meta data.
845   URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
846   if (params->query_info.url.get())
847     url_pattern = URLPattern(URLPattern::SCHEME_ALL, *params->query_info.url);
848 
849   std::string title;
850   if (params->query_info.title.get())
851     title = *params->query_info.title;
852 
853   int window_id = extension_misc::kUnknownWindowId;
854   if (params->query_info.window_id.get())
855     window_id = *params->query_info.window_id;
856 
857   int index = -1;
858   if (params->query_info.index.get())
859     index = *params->query_info.index;
860 
861   std::string window_type;
862   if (params->query_info.window_type !=
863       tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) {
864     window_type = tabs::Query::Params::QueryInfo::ToString(
865         params->query_info.window_type);
866   }
867 
868   base::ListValue* result = new base::ListValue();
869   Browser* last_active_browser = chrome::FindAnyBrowser(
870       GetProfile(), include_incognito(), chrome::GetActiveDesktop());
871   Browser* current_browser = GetCurrentBrowser();
872   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
873     Browser* browser = *it;
874     if (!GetProfile()->IsSameProfile(browser->profile()))
875       continue;
876 
877     if (!browser->window())
878       continue;
879 
880     if (!include_incognito() && GetProfile() != browser->profile())
881       continue;
882 
883     if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
884       continue;
885 
886     if (window_id == extension_misc::kCurrentWindowId &&
887         browser != current_browser) {
888       continue;
889     }
890 
891     if (!MatchesBool(params->query_info.current_window.get(),
892                      browser == current_browser)) {
893       continue;
894     }
895 
896     if (!MatchesBool(params->query_info.last_focused_window.get(),
897                      browser == last_active_browser)) {
898       continue;
899     }
900 
901     if (!window_type.empty() &&
902         window_type !=
903             browser->extension_window_controller()->GetWindowTypeText()) {
904       continue;
905     }
906 
907     TabStripModel* tab_strip = browser->tab_strip_model();
908     for (int i = 0; i < tab_strip->count(); ++i) {
909       const WebContents* web_contents = tab_strip->GetWebContentsAt(i);
910 
911       if (index > -1 && i != index)
912         continue;
913 
914       if (!MatchesBool(params->query_info.highlighted.get(),
915                        tab_strip->IsTabSelected(i))) {
916         continue;
917       }
918 
919       if (!MatchesBool(params->query_info.active.get(),
920                        i == tab_strip->active_index())) {
921         continue;
922       }
923 
924       if (!MatchesBool(params->query_info.pinned.get(),
925                        tab_strip->IsTabPinned(i))) {
926         continue;
927       }
928 
929       if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
930                                           UTF8ToUTF16(title)))
931         continue;
932 
933       if (!url_pattern.MatchesURL(web_contents->GetURL()))
934         continue;
935 
936       if (loading_status_set && loading != web_contents->IsLoading())
937         continue;
938 
939       result->Append(ExtensionTabUtil::CreateTabValue(
940           web_contents, tab_strip, i, GetExtension()));
941     }
942   }
943 
944   SetResult(result);
945   return true;
946 }
947 
RunImpl()948 bool TabsCreateFunction::RunImpl() {
949   scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
950   EXTENSION_FUNCTION_VALIDATE(params.get());
951 
952   // windowId defaults to "current" window.
953   int window_id = extension_misc::kCurrentWindowId;
954   if (params->create_properties.window_id.get())
955     window_id = *params->create_properties.window_id;
956 
957   Browser* browser = NULL;
958   if (!GetBrowserFromWindowID(this, window_id, &browser))
959     return false;
960 
961   // Ensure the selected browser is tabbed.
962   if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
963     browser = chrome::FindTabbedBrowser(
964         GetProfile(), include_incognito(), browser->host_desktop_type());
965 
966   if (!browser || !browser->window())
967     return false;
968 
969   // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
970   // represents the active tab.
971   WebContents* opener = NULL;
972   if (params->create_properties.opener_tab_id.get()) {
973     int opener_id = *params->create_properties.opener_tab_id;
974 
975     if (!ExtensionTabUtil::GetTabById(opener_id,
976                                       GetProfile(),
977                                       include_incognito(),
978                                       NULL,
979                                       NULL,
980                                       &opener,
981                                       NULL)) {
982       return false;
983     }
984   }
985 
986   // TODO(rafaelw): handle setting remaining tab properties:
987   // -title
988   // -favIconUrl
989 
990   std::string url_string;
991   GURL url;
992   if (params->create_properties.url.get()) {
993     url_string = *params->create_properties.url;
994     url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
995                                                        GetExtension());
996     if (!url.is_valid()) {
997       error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
998                                                        url_string);
999       return false;
1000     }
1001   }
1002 
1003   // Don't let extensions crash the browser or renderers.
1004   if (ExtensionTabUtil::IsCrashURL(url)) {
1005     error_ = keys::kNoCrashBrowserError;
1006     return false;
1007   }
1008 
1009   // Default to foreground for the new tab. The presence of 'selected' property
1010   // will override this default. This property is deprecated ('active' should
1011   // be used instead).
1012   bool active = true;
1013   if (params->create_properties.selected.get())
1014     active = *params->create_properties.selected;
1015 
1016   // The 'active' property has replaced the 'selected' property.
1017   if (params->create_properties.active.get())
1018     active = *params->create_properties.active;
1019 
1020   // Default to not pinning the tab. Setting the 'pinned' property to true
1021   // will override this default.
1022   bool pinned = false;
1023   if (params->create_properties.pinned.get())
1024     pinned = *params->create_properties.pinned;
1025 
1026   // We can't load extension URLs into incognito windows unless the extension
1027   // uses split mode. Special case to fall back to a tabbed window.
1028   if (url.SchemeIs(kExtensionScheme) &&
1029       !IncognitoInfo::IsSplitMode(GetExtension()) &&
1030       browser->profile()->IsOffTheRecord()) {
1031     Profile* profile = browser->profile()->GetOriginalProfile();
1032     chrome::HostDesktopType desktop_type = browser->host_desktop_type();
1033 
1034     browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
1035     if (!browser) {
1036       browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED,
1037                                                   profile, desktop_type));
1038       browser->window()->Show();
1039     }
1040   }
1041 
1042   // If index is specified, honor the value, but keep it bound to
1043   // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
1044   int index = -1;
1045   if (params->create_properties.index.get())
1046     index = *params->create_properties.index;
1047 
1048   TabStripModel* tab_strip = browser->tab_strip_model();
1049 
1050   index = std::min(std::max(index, -1), tab_strip->count());
1051 
1052   int add_types = active ? TabStripModel::ADD_ACTIVE :
1053                              TabStripModel::ADD_NONE;
1054   add_types |= TabStripModel::ADD_FORCE_INDEX;
1055   if (pinned)
1056     add_types |= TabStripModel::ADD_PINNED;
1057   chrome::NavigateParams navigate_params(
1058       browser, url, content::PAGE_TRANSITION_LINK);
1059   navigate_params.disposition =
1060       active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1061   navigate_params.tabstrip_index = index;
1062   navigate_params.tabstrip_add_types = add_types;
1063   chrome::Navigate(&navigate_params);
1064 
1065   // The tab may have been created in a different window, so make sure we look
1066   // at the right tab strip.
1067   tab_strip = navigate_params.browser->tab_strip_model();
1068   int new_index = tab_strip->GetIndexOfWebContents(
1069       navigate_params.target_contents);
1070   if (opener)
1071     tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
1072 
1073   if (active)
1074     navigate_params.target_contents->GetView()->SetInitialFocus();
1075 
1076   // Return data about the newly created tab.
1077   if (has_callback()) {
1078     SetResult(ExtensionTabUtil::CreateTabValue(
1079         navigate_params.target_contents,
1080         tab_strip, new_index, GetExtension()));
1081   }
1082 
1083   return true;
1084 }
1085 
RunImpl()1086 bool TabsDuplicateFunction::RunImpl() {
1087   scoped_ptr<tabs::Duplicate::Params> params(
1088       tabs::Duplicate::Params::Create(*args_));
1089   EXTENSION_FUNCTION_VALIDATE(params.get());
1090   int tab_id = params->tab_id;
1091 
1092   Browser* browser = NULL;
1093   TabStripModel* tab_strip = NULL;
1094   int tab_index = -1;
1095   if (!GetTabById(tab_id,
1096                   GetProfile(),
1097                   include_incognito(),
1098                   &browser,
1099                   &tab_strip,
1100                   NULL,
1101                   &tab_index,
1102                   &error_)) {
1103     return false;
1104   }
1105 
1106   WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
1107   if (!has_callback())
1108     return true;
1109 
1110   // Duplicated tab may not be in the same window as the original, so find
1111   // the window and the tab.
1112   TabStripModel* new_tab_strip = NULL;
1113   int new_tab_index = -1;
1114   ExtensionTabUtil::GetTabStripModel(new_contents,
1115                                      &new_tab_strip,
1116                                      &new_tab_index);
1117   if (!new_tab_strip || new_tab_index == -1) {
1118     return false;
1119   }
1120 
1121   // Return data about the newly created tab.
1122   SetResult(ExtensionTabUtil::CreateTabValue(
1123       new_contents,
1124       new_tab_strip, new_tab_index, GetExtension()));
1125 
1126   return true;
1127 }
1128 
RunImpl()1129 bool TabsGetFunction::RunImpl() {
1130   scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
1131   EXTENSION_FUNCTION_VALIDATE(params.get());
1132   int tab_id = params->tab_id;
1133 
1134   TabStripModel* tab_strip = NULL;
1135   WebContents* contents = NULL;
1136   int tab_index = -1;
1137   if (!GetTabById(tab_id,
1138                   GetProfile(),
1139                   include_incognito(),
1140                   NULL,
1141                   &tab_strip,
1142                   &contents,
1143                   &tab_index,
1144                   &error_))
1145     return false;
1146 
1147   SetResult(ExtensionTabUtil::CreateTabValue(contents,
1148                                              tab_strip,
1149                                              tab_index,
1150                                              GetExtension()));
1151   return true;
1152 }
1153 
RunImpl()1154 bool TabsGetCurrentFunction::RunImpl() {
1155   DCHECK(dispatcher());
1156 
1157   WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
1158   if (contents)
1159     SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension()));
1160 
1161   return true;
1162 }
1163 
RunImpl()1164 bool TabsHighlightFunction::RunImpl() {
1165   scoped_ptr<tabs::Highlight::Params> params(
1166       tabs::Highlight::Params::Create(*args_));
1167   EXTENSION_FUNCTION_VALIDATE(params.get());
1168 
1169   // Get the window id from the params; default to current window if omitted.
1170   int window_id = extension_misc::kCurrentWindowId;
1171   if (params->highlight_info.window_id.get())
1172     window_id = *params->highlight_info.window_id;
1173 
1174   Browser* browser = NULL;
1175   if (!GetBrowserFromWindowID(this, window_id, &browser))
1176     return false;
1177 
1178   TabStripModel* tabstrip = browser->tab_strip_model();
1179   ui::ListSelectionModel selection;
1180   int active_index = -1;
1181 
1182   if (params->highlight_info.tabs.as_integers) {
1183     std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1184     // Create a new selection model as we read the list of tab indices.
1185     for (size_t i = 0; i < tab_indices.size(); ++i) {
1186       if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1187         return false;
1188     }
1189   } else {
1190     EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1191     if (!HighlightTab(tabstrip,
1192                       &selection,
1193                       &active_index,
1194                       *params->highlight_info.tabs.as_integer)) {
1195       return false;
1196     }
1197   }
1198 
1199   // Make sure they actually specified tabs to select.
1200   if (selection.empty()) {
1201     error_ = keys::kNoHighlightedTabError;
1202     return false;
1203   }
1204 
1205   selection.set_active(active_index);
1206   browser->tab_strip_model()->SetSelectionFromModel(selection);
1207   SetResult(
1208       browser->extension_window_controller()->CreateWindowValueWithTabs(
1209           GetExtension()));
1210   return true;
1211 }
1212 
HighlightTab(TabStripModel * tabstrip,ui::ListSelectionModel * selection,int * active_index,int index)1213 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1214                                          ui::ListSelectionModel* selection,
1215                                          int *active_index,
1216                                          int index) {
1217   // Make sure the index is in range.
1218   if (!tabstrip->ContainsIndex(index)) {
1219     error_ = ErrorUtils::FormatErrorMessage(
1220         keys::kTabIndexNotFoundError, base::IntToString(index));
1221     return false;
1222   }
1223 
1224   // By default, we make the first tab in the list active.
1225   if (*active_index == -1)
1226     *active_index = index;
1227 
1228   selection->AddIndexToSelection(index);
1229   return true;
1230 }
1231 
TabsUpdateFunction()1232 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1233 }
1234 
RunImpl()1235 bool TabsUpdateFunction::RunImpl() {
1236   scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1237   EXTENSION_FUNCTION_VALIDATE(params.get());
1238 
1239   int tab_id = -1;
1240   WebContents* contents = NULL;
1241   if (!params->tab_id.get()) {
1242     Browser* browser = GetCurrentBrowser();
1243     if (!browser) {
1244       error_ = keys::kNoCurrentWindowError;
1245       return false;
1246     }
1247     contents = browser->tab_strip_model()->GetActiveWebContents();
1248     if (!contents) {
1249       error_ = keys::kNoSelectedTabError;
1250       return false;
1251     }
1252     tab_id = SessionID::IdForTab(contents);
1253   } else {
1254     tab_id = *params->tab_id;
1255   }
1256 
1257   int tab_index = -1;
1258   TabStripModel* tab_strip = NULL;
1259   if (!GetTabById(tab_id,
1260                   GetProfile(),
1261                   include_incognito(),
1262                   NULL,
1263                   &tab_strip,
1264                   &contents,
1265                   &tab_index,
1266                   &error_)) {
1267     return false;
1268   }
1269 
1270   web_contents_ = contents;
1271 
1272   // TODO(rafaelw): handle setting remaining tab properties:
1273   // -title
1274   // -favIconUrl
1275 
1276   // Navigate the tab to a new location if the url is different.
1277   bool is_async = false;
1278   if (params->update_properties.url.get() &&
1279       !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1280     return false;
1281   }
1282 
1283   bool active = false;
1284   // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1285   // Move tab selection management up to window.
1286   if (params->update_properties.selected.get())
1287     active = *params->update_properties.selected;
1288 
1289   // The 'active' property has replaced 'selected'.
1290   if (params->update_properties.active.get())
1291     active = *params->update_properties.active;
1292 
1293   if (active) {
1294     if (tab_strip->active_index() != tab_index) {
1295       tab_strip->ActivateTabAt(tab_index, false);
1296       DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1297     }
1298   }
1299 
1300   if (params->update_properties.highlighted.get()) {
1301     bool highlighted = *params->update_properties.highlighted;
1302     if (highlighted != tab_strip->IsTabSelected(tab_index))
1303       tab_strip->ToggleSelectionAt(tab_index);
1304   }
1305 
1306   if (params->update_properties.pinned.get()) {
1307     bool pinned = *params->update_properties.pinned;
1308     tab_strip->SetTabPinned(tab_index, pinned);
1309 
1310     // Update the tab index because it may move when being pinned.
1311     tab_index = tab_strip->GetIndexOfWebContents(contents);
1312   }
1313 
1314   if (params->update_properties.opener_tab_id.get()) {
1315     int opener_id = *params->update_properties.opener_tab_id;
1316 
1317     WebContents* opener_contents = NULL;
1318     if (!ExtensionTabUtil::GetTabById(opener_id,
1319                                       GetProfile(),
1320                                       include_incognito(),
1321                                       NULL,
1322                                       NULL,
1323                                       &opener_contents,
1324                                       NULL))
1325       return false;
1326 
1327     tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1328   }
1329 
1330   if (!is_async) {
1331     PopulateResult();
1332     SendResponse(true);
1333   }
1334   return true;
1335 }
1336 
UpdateURL(const std::string & url_string,int tab_id,bool * is_async)1337 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1338                                    int tab_id,
1339                                    bool* is_async) {
1340   GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1341       url_string, GetExtension());
1342 
1343   if (!url.is_valid()) {
1344     error_ = ErrorUtils::FormatErrorMessage(
1345         keys::kInvalidUrlError, url_string);
1346     return false;
1347   }
1348 
1349   // Don't let the extension crash the browser or renderers.
1350   if (ExtensionTabUtil::IsCrashURL(url)) {
1351     error_ = keys::kNoCrashBrowserError;
1352     return false;
1353   }
1354 
1355   // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1356   // we need to check host permissions before allowing them.
1357   if (url.SchemeIs(content::kJavaScriptScheme)) {
1358     content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1359     if (!PermissionsData::CanExecuteScriptOnPage(
1360             GetExtension(),
1361             web_contents_->GetURL(),
1362             web_contents_->GetURL(),
1363             tab_id,
1364             NULL,
1365             process ? process->GetID() : -1,
1366             &error_)) {
1367       return false;
1368     }
1369 
1370     TabHelper::FromWebContents(web_contents_)->
1371         script_executor()->ExecuteScript(
1372             extension_id(),
1373             ScriptExecutor::JAVASCRIPT,
1374             url.GetContent(),
1375             ScriptExecutor::TOP_FRAME,
1376             UserScript::DOCUMENT_IDLE,
1377             ScriptExecutor::MAIN_WORLD,
1378             ScriptExecutor::DEFAULT_PROCESS,
1379             GURL(),
1380             ScriptExecutor::NO_RESULT,
1381             base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1382 
1383     *is_async = true;
1384     return true;
1385   }
1386 
1387   web_contents_->GetController().LoadURL(
1388       url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1389 
1390   // The URL of a tab contents never actually changes to a JavaScript URL, so
1391   // this check only makes sense in other cases.
1392   if (!url.SchemeIs(content::kJavaScriptScheme))
1393     DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1394 
1395   return true;
1396 }
1397 
PopulateResult()1398 void TabsUpdateFunction::PopulateResult() {
1399   if (!has_callback())
1400     return;
1401 
1402   SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension()));
1403 }
1404 
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & url,const base::ListValue & script_result)1405 void TabsUpdateFunction::OnExecuteCodeFinished(
1406     const std::string& error,
1407     int32 on_page_id,
1408     const GURL& url,
1409     const base::ListValue& script_result) {
1410   if (error.empty())
1411     PopulateResult();
1412   else
1413     error_ = error;
1414   SendResponse(error.empty());
1415 }
1416 
RunImpl()1417 bool TabsMoveFunction::RunImpl() {
1418   scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1419   EXTENSION_FUNCTION_VALIDATE(params.get());
1420 
1421   int new_index = params->move_properties.index;
1422   int* window_id = params->move_properties.window_id.get();
1423   base::ListValue tab_values;
1424 
1425   size_t num_tabs = 0;
1426   if (params->tab_ids.as_integers) {
1427     std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1428     num_tabs = tab_ids.size();
1429     for (size_t i = 0; i < tab_ids.size(); ++i) {
1430       if (!MoveTab(tab_ids[i], &new_index, i, &tab_values, window_id))
1431         return false;
1432     }
1433   } else {
1434     EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1435     num_tabs = 1;
1436     if (!MoveTab(*params->tab_ids.as_integer,
1437                  &new_index,
1438                  0,
1439                  &tab_values,
1440                  window_id)) {
1441       return false;
1442     }
1443   }
1444 
1445   if (!has_callback())
1446     return true;
1447 
1448   // Only return the results as an array if there are multiple tabs.
1449   if (num_tabs > 1) {
1450     SetResult(tab_values.DeepCopy());
1451   } else {
1452     Value* value = NULL;
1453     CHECK(tab_values.Get(0, &value));
1454     SetResult(value->DeepCopy());
1455   }
1456   return true;
1457 }
1458 
MoveTab(int tab_id,int * new_index,int iteration,base::ListValue * tab_values,int * window_id)1459 bool TabsMoveFunction::MoveTab(int tab_id,
1460                                int *new_index,
1461                                int iteration,
1462                                base::ListValue* tab_values,
1463                                int* window_id) {
1464   Browser* source_browser = NULL;
1465   TabStripModel* source_tab_strip = NULL;
1466   WebContents* contents = NULL;
1467   int tab_index = -1;
1468   if (!GetTabById(tab_id,
1469                   GetProfile(),
1470                   include_incognito(),
1471                   &source_browser,
1472                   &source_tab_strip,
1473                   &contents,
1474                   &tab_index,
1475                   &error_)) {
1476     return false;
1477   }
1478 
1479   // Don't let the extension move the tab if the user is dragging tabs.
1480   if (!source_browser->window()->IsTabStripEditable()) {
1481     error_ = keys::kTabStripNotEditableError;
1482     return false;
1483   }
1484 
1485   // Insert the tabs one after another.
1486   *new_index += iteration;
1487 
1488   if (window_id) {
1489     Browser* target_browser = NULL;
1490 
1491     if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1492       return false;
1493 
1494     if (!target_browser->window()->IsTabStripEditable()) {
1495       error_ = keys::kTabStripNotEditableError;
1496       return false;
1497     }
1498 
1499     if (!target_browser->is_type_tabbed()) {
1500       error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1501       return false;
1502     }
1503 
1504     if (target_browser->profile() != source_browser->profile()) {
1505       error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1506       return false;
1507     }
1508 
1509     // If windowId is different from the current window, move between windows.
1510     if (ExtensionTabUtil::GetWindowId(target_browser) !=
1511         ExtensionTabUtil::GetWindowId(source_browser)) {
1512       TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1513       WebContents* web_contents =
1514           source_tab_strip->DetachWebContentsAt(tab_index);
1515       if (!web_contents) {
1516         error_ = ErrorUtils::FormatErrorMessage(
1517             keys::kTabNotFoundError, base::IntToString(tab_id));
1518         return false;
1519       }
1520 
1521       // Clamp move location to the last position.
1522       // This is ">" because it can append to a new index position.
1523       // -1 means set the move location to the last position.
1524       if (*new_index > target_tab_strip->count() || *new_index < 0)
1525         *new_index = target_tab_strip->count();
1526 
1527       target_tab_strip->InsertWebContentsAt(
1528           *new_index, web_contents, TabStripModel::ADD_NONE);
1529 
1530       if (has_callback()) {
1531         tab_values->Append(ExtensionTabUtil::CreateTabValue(
1532             web_contents,
1533             target_tab_strip,
1534             *new_index,
1535             GetExtension()));
1536       }
1537 
1538       return true;
1539     }
1540   }
1541 
1542   // Perform a simple within-window move.
1543   // Clamp move location to the last position.
1544   // This is ">=" because the move must be to an existing location.
1545   // -1 means set the move location to the last position.
1546   if (*new_index >= source_tab_strip->count() || *new_index < 0)
1547     *new_index = source_tab_strip->count() - 1;
1548 
1549   if (*new_index != tab_index)
1550     source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1551 
1552   if (has_callback()) {
1553     tab_values->Append(ExtensionTabUtil::CreateTabValue(
1554         contents, source_tab_strip, *new_index, GetExtension()));
1555   }
1556 
1557   return true;
1558 }
1559 
RunImpl()1560 bool TabsReloadFunction::RunImpl() {
1561   scoped_ptr<tabs::Reload::Params> params(
1562       tabs::Reload::Params::Create(*args_));
1563   EXTENSION_FUNCTION_VALIDATE(params.get());
1564 
1565   bool bypass_cache = false;
1566   if (params->reload_properties.get() &&
1567       params->reload_properties->bypass_cache.get()) {
1568     bypass_cache = *params->reload_properties->bypass_cache;
1569   }
1570 
1571   content::WebContents* web_contents = NULL;
1572 
1573   // If |tab_id| is specified, look for it. Otherwise default to selected tab
1574   // in the current window.
1575   if (!params->tab_id.get()) {
1576     Browser* browser = GetCurrentBrowser();
1577     if (!browser) {
1578       error_ = keys::kNoCurrentWindowError;
1579       return false;
1580     }
1581 
1582     if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1583       return false;
1584   } else {
1585     int tab_id = *params->tab_id;
1586 
1587     Browser* browser = NULL;
1588     if (!GetTabById(tab_id,
1589                     GetProfile(),
1590                     include_incognito(),
1591                     &browser,
1592                     NULL,
1593                     &web_contents,
1594                     NULL,
1595                     &error_))
1596     return false;
1597   }
1598 
1599   if (web_contents->ShowingInterstitialPage()) {
1600     // This does as same as Browser::ReloadInternal.
1601     NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1602     OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1603                          content::PAGE_TRANSITION_RELOAD, false);
1604     GetCurrentBrowser()->OpenURL(params);
1605   } else if (bypass_cache) {
1606     web_contents->GetController().ReloadIgnoringCache(true);
1607   } else {
1608     web_contents->GetController().Reload(true);
1609   }
1610 
1611   return true;
1612 }
1613 
RunImpl()1614 bool TabsRemoveFunction::RunImpl() {
1615   scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1616   EXTENSION_FUNCTION_VALIDATE(params.get());
1617 
1618   if (params->tab_ids.as_integers) {
1619     std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1620     for (size_t i = 0; i < tab_ids.size(); ++i) {
1621       if (!RemoveTab(tab_ids[i]))
1622         return false;
1623     }
1624   } else {
1625     EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1626     if (!RemoveTab(*params->tab_ids.as_integer.get()))
1627       return false;
1628   }
1629   return true;
1630 }
1631 
RemoveTab(int tab_id)1632 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1633   Browser* browser = NULL;
1634   WebContents* contents = NULL;
1635   if (!GetTabById(tab_id,
1636                   GetProfile(),
1637                   include_incognito(),
1638                   &browser,
1639                   NULL,
1640                   &contents,
1641                   NULL,
1642                   &error_)) {
1643     return false;
1644   }
1645 
1646   // Don't let the extension remove a tab if the user is dragging tabs around.
1647   if (!browser->window()->IsTabStripEditable()) {
1648     error_ = keys::kTabStripNotEditableError;
1649     return false;
1650   }
1651   // There's a chance that the tab is being dragged, or we're in some other
1652   // nested event loop. This code path ensures that the tab is safely closed
1653   // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1654   // does not.
1655   contents->Close();
1656   return true;
1657 }
1658 
IsScreenshotEnabled()1659 bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1660   PrefService* service = GetProfile()->GetPrefs();
1661   if (service->GetBoolean(prefs::kDisableScreenshots)) {
1662     error_ = keys::kScreenshotsDisabled;
1663     return false;
1664   }
1665   return true;
1666 }
1667 
GetWebContentsForID(int window_id)1668 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1669   Browser* browser = NULL;
1670   if (!GetBrowserFromWindowID(this, window_id, &browser))
1671     return NULL;
1672 
1673   WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1674   if (!contents) {
1675     error_ = keys::kInternalVisibleTabCaptureError;
1676     return NULL;
1677   }
1678 
1679   // Use the last committed URL rather than the active URL for permissions
1680   // checking, since the visible page won't be updated until it has been
1681   // committed. A canonical example of this is interstitials, which show the
1682   // URL of the new/loading page (active) but would capture the content of the
1683   // old page (last committed).
1684   //
1685   // TODO(creis): Use WebContents::GetLastCommittedURL instead.
1686   // http://crbug.com/237908.
1687   NavigationEntry* last_committed_entry =
1688       contents->GetController().GetLastCommittedEntry();
1689   GURL last_committed_url = last_committed_entry ?
1690       last_committed_entry->GetURL() : GURL();
1691   if (!PermissionsData::CanCaptureVisiblePage(GetExtension(),
1692                                               last_committed_url,
1693                                               SessionID::IdForTab(contents),
1694                                               &error_)) {
1695     return NULL;
1696   }
1697   return contents;
1698 }
1699 
OnCaptureFailure(FailureReason reason)1700 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1701   error_ = keys::kInternalVisibleTabCaptureError;
1702   SendResponse(false);
1703 }
1704 
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)1705 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1706     user_prefs::PrefRegistrySyncable* registry) {
1707   registry->RegisterBooleanPref(
1708       prefs::kDisableScreenshots,
1709       false,
1710       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1711 }
1712 
RunImpl()1713 bool TabsDetectLanguageFunction::RunImpl() {
1714   scoped_ptr<tabs::DetectLanguage::Params> params(
1715       tabs::DetectLanguage::Params::Create(*args_));
1716   EXTENSION_FUNCTION_VALIDATE(params.get());
1717 
1718   int tab_id = 0;
1719   Browser* browser = NULL;
1720   WebContents* contents = NULL;
1721 
1722   // If |tab_id| is specified, look for it. Otherwise default to selected tab
1723   // in the current window.
1724   if (params->tab_id.get()) {
1725     tab_id = *params->tab_id;
1726     if (!GetTabById(tab_id,
1727                     GetProfile(),
1728                     include_incognito(),
1729                     &browser,
1730                     NULL,
1731                     &contents,
1732                     NULL,
1733                     &error_)) {
1734       return false;
1735     }
1736     if (!browser || !contents)
1737       return false;
1738   } else {
1739     browser = GetCurrentBrowser();
1740     if (!browser)
1741       return false;
1742     contents = browser->tab_strip_model()->GetActiveWebContents();
1743     if (!contents)
1744       return false;
1745   }
1746 
1747   if (contents->GetController().NeedsReload()) {
1748     // If the tab hasn't been loaded, don't wait for the tab to load.
1749     error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1750     return false;
1751   }
1752 
1753   AddRef();  // Balanced in GotLanguage().
1754 
1755   TranslateTabHelper* translate_tab_helper =
1756       TranslateTabHelper::FromWebContents(contents);
1757   if (!translate_tab_helper->language_state().original_language().empty()) {
1758     // Delay the callback invocation until after the current JS call has
1759     // returned.
1760     base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
1761         &TabsDetectLanguageFunction::GotLanguage, this,
1762         translate_tab_helper->language_state().original_language()));
1763     return true;
1764   }
1765   // The tab contents does not know its language yet.  Let's wait until it
1766   // receives it, or until the tab is closed/navigates to some other page.
1767   registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1768                  content::Source<WebContents>(contents));
1769   registrar_.Add(
1770       this, chrome::NOTIFICATION_TAB_CLOSING,
1771       content::Source<NavigationController>(&(contents->GetController())));
1772   registrar_.Add(
1773       this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1774       content::Source<NavigationController>(&(contents->GetController())));
1775   return true;
1776 }
1777 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1778 void TabsDetectLanguageFunction::Observe(
1779     int type,
1780     const content::NotificationSource& source,
1781     const content::NotificationDetails& details) {
1782   std::string language;
1783   if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1784     const LanguageDetectionDetails* lang_det_details =
1785         content::Details<const LanguageDetectionDetails>(details).ptr();
1786     language = lang_det_details->adopted_language;
1787   }
1788 
1789   registrar_.RemoveAll();
1790 
1791   // Call GotLanguage in all cases as we want to guarantee the callback is
1792   // called for every API call the extension made.
1793   GotLanguage(language);
1794 }
1795 
GotLanguage(const std::string & language)1796 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1797   SetResult(new base::StringValue(language.c_str()));
1798   SendResponse(true);
1799 
1800   Release();  // Balanced in Run()
1801 }
1802 
ExecuteCodeInTabFunction()1803 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1804     : execute_tab_id_(-1) {
1805 }
1806 
~ExecuteCodeInTabFunction()1807 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1808 
HasPermission()1809 bool ExecuteCodeInTabFunction::HasPermission() {
1810   if (Init() && PermissionsData::HasAPIPermissionForTab(
1811                     extension_.get(), execute_tab_id_, APIPermission::kTab)) {
1812     return true;
1813   }
1814   return ExtensionFunction::HasPermission();
1815 }
1816 
CanExecuteScriptOnPage()1817 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1818   content::WebContents* contents = NULL;
1819 
1820   // If |tab_id| is specified, look for the tab. Otherwise default to selected
1821   // tab in the current window.
1822   CHECK_GE(execute_tab_id_, 0);
1823   if (!GetTabById(execute_tab_id_,
1824                   GetProfile(),
1825                   include_incognito(),
1826                   NULL,
1827                   NULL,
1828                   &contents,
1829                   NULL,
1830                   &error_)) {
1831     return false;
1832   }
1833 
1834   CHECK(contents);
1835 
1836   // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1837   // we check again in the renderer.
1838   content::RenderProcessHost* process = contents->GetRenderProcessHost();
1839   if (!PermissionsData::CanExecuteScriptOnPage(
1840           GetExtension(),
1841           contents->GetURL(),
1842           contents->GetURL(),
1843           execute_tab_id_,
1844           NULL,
1845           process ? process->GetID() : -1,
1846           &error_)) {
1847     return false;
1848   }
1849 
1850   return true;
1851 }
1852 
GetScriptExecutor()1853 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1854   Browser* browser = NULL;
1855   content::WebContents* contents = NULL;
1856 
1857   bool success = GetTabById(execute_tab_id_,
1858                             GetProfile(),
1859                             include_incognito(),
1860                             &browser,
1861                             NULL,
1862                             &contents,
1863                             NULL,
1864                             &error_) &&
1865                  contents && browser;
1866 
1867   if (!success)
1868     return NULL;
1869 
1870   return TabHelper::FromWebContents(contents)->script_executor();
1871 }
1872 
IsWebView() const1873 bool ExecuteCodeInTabFunction::IsWebView() const {
1874   return false;
1875 }
1876 
ShouldInsertCSS() const1877 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1878   return false;
1879 }
1880 
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & on_url,const base::ListValue & result)1881 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1882     const std::string& error,
1883     int32 on_page_id,
1884     const GURL& on_url,
1885     const base::ListValue& result) {
1886   if (error.empty())
1887     SetResult(result.DeepCopy());
1888   ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
1889                                                   result);
1890 }
1891 
Init()1892 bool ExecuteCodeInTabFunction::Init() {
1893   if (details_.get())
1894     return true;
1895 
1896   // |tab_id| is optional so it's ok if it's not there.
1897   int tab_id = -1;
1898   if (args_->GetInteger(0, &tab_id))
1899     EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1900 
1901   // |details| are not optional.
1902   base::DictionaryValue* details_value = NULL;
1903   if (!args_->GetDictionary(1, &details_value))
1904     return false;
1905   scoped_ptr<InjectDetails> details(new InjectDetails());
1906   if (!InjectDetails::Populate(*details_value, details.get()))
1907     return false;
1908 
1909   // If the tab ID wasn't given then it needs to be converted to the
1910   // currently active tab's ID.
1911   if (tab_id == -1) {
1912     Browser* browser = GetCurrentBrowser();
1913     if (!browser)
1914       return false;
1915     content::WebContents* web_contents = NULL;
1916     if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1917       return false;
1918   }
1919 
1920   execute_tab_id_ = tab_id;
1921   details_ = details.Pass();
1922   return true;
1923 }
1924 
ShouldInsertCSS() const1925 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1926   return true;
1927 }
1928 
1929 }  // namespace extensions
1930