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