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