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
94using content::BrowserThread;
95using content::NavigationController;
96using content::NavigationEntry;
97using content::OpenURLParams;
98using content::Referrer;
99using content::WebContents;
100
101namespace extensions {
102
103namespace windows = api::windows;
104namespace keys = tabs_constants;
105namespace tabs = api::tabs;
106
107using core_api::extension_types::InjectDetails;
108
109namespace {
110
111bool 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
127bool 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.
145bool 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.
169bool MatchesBool(bool* boolean, bool value) {
170  return !boolean || *boolean == value;
171}
172
173template <typename T>
174void 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
183void 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
208bool 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
230bool 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
252bool 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
279bool 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
305bool 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
353bool 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
614bool 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
734bool 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
756bool 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
781bool 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
799bool 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
924bool 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
954bool 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
996bool 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
1019bool 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
1032bool 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
1080bool 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
1099TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1100}
1101
1102bool 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
1204bool 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
1266void TabsUpdateFunction::PopulateResult() {
1267  if (!has_callback())
1268    return;
1269
1270  SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, extension()));
1271}
1272
1273void 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
1284bool 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
1330bool 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
1428bool 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
1483bool 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
1501bool 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
1528TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
1529    : chrome_details_(this) {
1530}
1531
1532bool 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
1541WebContents* 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
1559void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1560  error_ = keys::kInternalVisibleTabCaptureError;
1561  SendResponse(false);
1562}
1563
1564void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1565    user_prefs::PrefRegistrySyncable* registry) {
1566  registry->RegisterBooleanPref(
1567      prefs::kDisableScreenshots,
1568      false,
1569      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1570}
1571
1572bool 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
1642void 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
1661void 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
1668ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1669    : chrome_details_(this), execute_tab_id_(-1) {
1670}
1671
1672ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1673
1674bool 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
1683bool 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
1716bool 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
1751ScriptExecutor* 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
1771bool ExecuteCodeInTabFunction::IsWebView() const {
1772  return false;
1773}
1774
1775const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1776  return GURL::EmptyGURL();
1777}
1778
1779bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1780  return false;
1781}
1782
1783void 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
1792bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1793  return true;
1794}
1795
1796content::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
1818bool 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
1846bool 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
1864bool 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
1916bool 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
1938