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