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