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