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