Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/extension_tabs_module.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "base/base64.h"
     11 #include "base/string_number_conversions.h"
     12 #include "base/string_util.h"
     13 #include "base/stringprintf.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "chrome/browser/extensions/extension_function_dispatcher.h"
     16 #include "chrome/browser/extensions/extension_host.h"
     17 #include "chrome/browser/extensions/extension_service.h"
     18 #include "chrome/browser/extensions/extension_tabs_module_constants.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/browser/tabs/tab_strip_model.h"
     21 #include "chrome/browser/translate/translate_tab_helper.h"
     22 #include "chrome/browser/ui/browser.h"
     23 #include "chrome/browser/ui/browser_list.h"
     24 #include "chrome/browser/ui/browser_navigator.h"
     25 #include "chrome/browser/ui/browser_window.h"
     26 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     27 #include "chrome/browser/ui/window_sizer.h"
     28 #include "chrome/common/chrome_switches.h"
     29 #include "chrome/common/extensions/extension.h"
     30 #include "chrome/common/extensions/extension_error_utils.h"
     31 #include "chrome/common/extensions/extension_messages.h"
     32 #include "chrome/common/pref_names.h"
     33 #include "chrome/common/url_constants.h"
     34 #include "content/browser/renderer_host/backing_store.h"
     35 #include "content/browser/renderer_host/render_view_host.h"
     36 #include "content/browser/renderer_host/render_view_host_delegate.h"
     37 #include "content/browser/tab_contents/navigation_entry.h"
     38 #include "content/browser/tab_contents/tab_contents.h"
     39 #include "content/browser/tab_contents/tab_contents_view.h"
     40 #include "content/common/notification_service.h"
     41 #include "skia/ext/image_operations.h"
     42 #include "skia/ext/platform_canvas.h"
     43 #include "third_party/skia/include/core/SkBitmap.h"
     44 #include "ui/gfx/codec/jpeg_codec.h"
     45 #include "ui/gfx/codec/png_codec.h"
     46 
     47 namespace keys = extension_tabs_module_constants;
     48 namespace errors = extension_manifest_errors;
     49 
     50 const int CaptureVisibleTabFunction::kDefaultQuality = 90;
     51 
     52 // Forward declare static helper functions defined below.
     53 
     54 // |error_message| can optionally be passed in a will be set with an appropriate
     55 // message if the window cannot be found by id.
     56 static Browser* GetBrowserInProfileWithId(Profile* profile,
     57                                           const int window_id,
     58                                           bool include_incognito,
     59                                           std::string* error_message);
     60 
     61 // |error_message| can optionally be passed in and will be set with an
     62 // appropriate message if the tab cannot be found by id.
     63 static bool GetTabById(int tab_id, Profile* profile,
     64                        bool include_incognito,
     65                        Browser** browser,
     66                        TabStripModel** tab_strip,
     67                        TabContentsWrapper** contents,
     68                        int* tab_index, std::string* error_message);
     69 
     70 // Takes |url_string| and returns a GURL which is either valid and absolute
     71 // or invalid. If |url_string| is not directly interpretable as a valid (it is
     72 // likely a relative URL) an attempt is made to resolve it. |extension| is
     73 // provided so it can be resolved relative to its extension base
     74 // (chrome-extension://<id>/). Using the source frame url would be more correct,
     75 // but because the api shipped with urls resolved relative to their extension
     76 // base, we decided it wasn't worth breaking existing extensions to fix.
     77 static GURL ResolvePossiblyRelativeURL(const std::string& url_string,
     78                                        const Extension* extension);
     79 
     80 // Return the type name for a browser window type.
     81 static std::string GetWindowTypeText(Browser::Type type);
     82 
     83 int ExtensionTabUtil::GetWindowId(const Browser* browser) {
     84   return browser->session_id().id();
     85 }
     86 
     87 int ExtensionTabUtil::GetTabId(const TabContents* tab_contents) {
     88   return tab_contents->controller().session_id().id();
     89 }
     90 
     91 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
     92   return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
     93 }
     94 
     95 int ExtensionTabUtil::GetWindowIdOfTab(const TabContents* tab_contents) {
     96   return tab_contents->controller().window_id().id();
     97 }
     98 
     99 DictionaryValue* ExtensionTabUtil::CreateTabValue(
    100     const TabContents* contents) {
    101   // Find the tab strip and index of this guy.
    102   TabStripModel* tab_strip = NULL;
    103   int tab_index;
    104   if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index))
    105     return ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index);
    106 
    107   // Couldn't find it.  This can happen if the tab is being dragged.
    108   return ExtensionTabUtil::CreateTabValue(contents, NULL, -1);
    109 }
    110 
    111 ListValue* ExtensionTabUtil::CreateTabList(const Browser* browser) {
    112   ListValue* tab_list = new ListValue();
    113   TabStripModel* tab_strip = browser->tabstrip_model();
    114   for (int i = 0; i < tab_strip->count(); ++i) {
    115     tab_list->Append(ExtensionTabUtil::CreateTabValue(
    116         tab_strip->GetTabContentsAt(i)->tab_contents(), tab_strip, i));
    117   }
    118 
    119   return tab_list;
    120 }
    121 
    122 DictionaryValue* ExtensionTabUtil::CreateTabValue(
    123     const TabContents* contents, TabStripModel* tab_strip, int tab_index) {
    124   DictionaryValue* result = new DictionaryValue();
    125   result->SetInteger(keys::kIdKey, ExtensionTabUtil::GetTabId(contents));
    126   result->SetInteger(keys::kIndexKey, tab_index);
    127   result->SetInteger(keys::kWindowIdKey,
    128                      ExtensionTabUtil::GetWindowIdOfTab(contents));
    129   result->SetString(keys::kUrlKey, contents->GetURL().spec());
    130   result->SetString(keys::kStatusKey, GetTabStatusText(contents->is_loading()));
    131   result->SetBoolean(keys::kSelectedKey,
    132                      tab_strip && tab_index == tab_strip->active_index());
    133   result->SetBoolean(keys::kPinnedKey,
    134                      tab_strip && tab_strip->IsTabPinned(tab_index));
    135   result->SetString(keys::kTitleKey, contents->GetTitle());
    136   result->SetBoolean(keys::kIncognitoKey,
    137                      contents->profile()->IsOffTheRecord());
    138 
    139   if (!contents->is_loading()) {
    140     NavigationEntry* entry = contents->controller().GetActiveEntry();
    141     if (entry) {
    142       if (entry->favicon().is_valid())
    143         result->SetString(keys::kFaviconUrlKey, entry->favicon().url().spec());
    144     }
    145   }
    146 
    147   return result;
    148 }
    149 
    150 // if |populate| is true, each window gets a list property |tabs| which contains
    151 // fully populated tab objects.
    152 DictionaryValue* ExtensionTabUtil::CreateWindowValue(const Browser* browser,
    153                                                      bool populate_tabs) {
    154   DCHECK(browser);
    155   DCHECK(browser->window());
    156   DictionaryValue* result = new DictionaryValue();
    157   result->SetInteger(keys::kIdKey, ExtensionTabUtil::GetWindowId(browser));
    158   result->SetBoolean(keys::kIncognitoKey,
    159                      browser->profile()->IsOffTheRecord());
    160   result->SetBoolean(keys::kFocusedKey, browser->window()->IsActive());
    161   gfx::Rect bounds;
    162   if (browser->window()->IsMaximized() || browser->window()->IsFullscreen())
    163     bounds = browser->window()->GetBounds();
    164   else
    165     bounds = browser->window()->GetRestoredBounds();
    166 
    167   result->SetInteger(keys::kLeftKey, bounds.x());
    168   result->SetInteger(keys::kTopKey, bounds.y());
    169   result->SetInteger(keys::kWidthKey, bounds.width());
    170   result->SetInteger(keys::kHeightKey, bounds.height());
    171   result->SetString(keys::kWindowTypeKey, GetWindowTypeText(browser->type()));
    172 
    173   if (populate_tabs) {
    174     result->Set(keys::kTabsKey, ExtensionTabUtil::CreateTabList(browser));
    175   }
    176 
    177   return result;
    178 }
    179 
    180 bool ExtensionTabUtil::GetTabStripModel(const TabContents* tab_contents,
    181                                         TabStripModel** tab_strip_model,
    182                                         int* tab_index) {
    183   DCHECK(tab_contents);
    184   DCHECK(tab_strip_model);
    185   DCHECK(tab_index);
    186 
    187   for (BrowserList::const_iterator it = BrowserList::begin();
    188       it != BrowserList::end(); ++it) {
    189     TabStripModel* tab_strip = (*it)->tabstrip_model();
    190     int index = tab_strip->GetWrapperIndex(tab_contents);
    191     if (index != -1) {
    192       *tab_strip_model = tab_strip;
    193       *tab_index = index;
    194       return true;
    195     }
    196   }
    197 
    198   return false;
    199 }
    200 
    201 bool ExtensionTabUtil::GetDefaultTab(Browser* browser,
    202                                      TabContentsWrapper** contents,
    203                                      int* tab_id) {
    204   DCHECK(browser);
    205   DCHECK(contents);
    206   DCHECK(tab_id);
    207 
    208   *contents = browser->GetSelectedTabContentsWrapper();
    209   if (*contents) {
    210     if (tab_id)
    211       *tab_id = ExtensionTabUtil::GetTabId((*contents)->tab_contents());
    212     return true;
    213   }
    214 
    215   return false;
    216 }
    217 
    218 bool ExtensionTabUtil::GetTabById(int tab_id, Profile* profile,
    219                                   bool include_incognito,
    220                                   Browser** browser,
    221                                   TabStripModel** tab_strip,
    222                                   TabContentsWrapper** contents,
    223                                   int* tab_index) {
    224   Profile* incognito_profile =
    225       include_incognito && profile->HasOffTheRecordProfile() ?
    226           profile->GetOffTheRecordProfile() : NULL;
    227   for (BrowserList::const_iterator iter = BrowserList::begin();
    228        iter != BrowserList::end(); ++iter) {
    229     Browser* target_browser = *iter;
    230     if (target_browser->profile() == profile ||
    231         target_browser->profile() == incognito_profile) {
    232       TabStripModel* target_tab_strip = target_browser->tabstrip_model();
    233       for (int i = 0; i < target_tab_strip->count(); ++i) {
    234         TabContentsWrapper* target_contents =
    235             target_tab_strip->GetTabContentsAt(i);
    236         if (target_contents->controller().session_id().id() == tab_id) {
    237           if (browser)
    238             *browser = target_browser;
    239           if (tab_strip)
    240             *tab_strip = target_tab_strip;
    241           if (contents)
    242             *contents = target_contents;
    243           if (tab_index)
    244             *tab_index = i;
    245           return true;
    246         }
    247       }
    248     }
    249   }
    250   return false;
    251 }
    252 
    253 // Windows ---------------------------------------------------------------------
    254 
    255 bool GetWindowFunction::RunImpl() {
    256   int window_id;
    257   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
    258 
    259   Browser* browser = GetBrowserInProfileWithId(profile(), window_id,
    260                                                include_incognito(), &error_);
    261   if (!browser || !browser->window()) {
    262     error_ = ExtensionErrorUtils::FormatErrorMessage(
    263         keys::kWindowNotFoundError, base::IntToString(window_id));
    264     return false;
    265   }
    266 
    267   result_.reset(ExtensionTabUtil::CreateWindowValue(browser, false));
    268   return true;
    269 }
    270 
    271 bool GetCurrentWindowFunction::RunImpl() {
    272   Browser* browser = GetCurrentBrowser();
    273   if (!browser || !browser->window()) {
    274     error_ = keys::kNoCurrentWindowError;
    275     return false;
    276   }
    277   result_.reset(ExtensionTabUtil::CreateWindowValue(browser, false));
    278   return true;
    279 }
    280 
    281 bool GetLastFocusedWindowFunction::RunImpl() {
    282   Browser* browser = BrowserList::FindBrowserWithType(
    283       profile(), Browser::TYPE_ANY, include_incognito());
    284   if (!browser || !browser->window()) {
    285     error_ = keys::kNoLastFocusedWindowError;
    286     return false;
    287   }
    288   result_.reset(ExtensionTabUtil::CreateWindowValue(browser, false));
    289   return true;
    290 }
    291 
    292 bool GetAllWindowsFunction::RunImpl() {
    293   bool populate_tabs = false;
    294   if (HasOptionalArgument(0)) {
    295     DictionaryValue* args;
    296     EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
    297 
    298     if (args->HasKey(keys::kPopulateKey)) {
    299       EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPopulateKey,
    300           &populate_tabs));
    301     }
    302   }
    303 
    304   result_.reset(new ListValue());
    305   Profile* incognito_profile =
    306       include_incognito() && profile()->HasOffTheRecordProfile() ?
    307           profile()->GetOffTheRecordProfile() : NULL;
    308   for (BrowserList::const_iterator browser = BrowserList::begin();
    309     browser != BrowserList::end(); ++browser) {
    310       // Only examine browsers in the current profile that have windows.
    311       if (((*browser)->profile() == profile() ||
    312            (*browser)->profile() == incognito_profile) &&
    313           (*browser)->window()) {
    314         static_cast<ListValue*>(result_.get())->
    315           Append(ExtensionTabUtil::CreateWindowValue(*browser, populate_tabs));
    316       }
    317   }
    318 
    319   return true;
    320 }
    321 
    322 bool CreateWindowFunction::RunImpl() {
    323   DictionaryValue* args = NULL;
    324   std::vector<GURL> urls;
    325   TabContentsWrapper* contents = NULL;
    326 
    327   if (HasOptionalArgument(0))
    328     EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
    329 
    330   // Look for optional url.
    331   if (args) {
    332     if (args->HasKey(keys::kUrlKey)) {
    333       Value* url_value;
    334       std::vector<std::string> url_strings;
    335       args->Get(keys::kUrlKey, &url_value);
    336 
    337       // First, get all the URLs the client wants to open.
    338       if (url_value->IsType(Value::TYPE_STRING)) {
    339         std::string url_string;
    340         url_value->GetAsString(&url_string);
    341         url_strings.push_back(url_string);
    342       } else if (url_value->IsType(Value::TYPE_LIST)) {
    343         const ListValue* url_list = static_cast<const ListValue*>(url_value);
    344         for (size_t i = 0; i < url_list->GetSize(); ++i) {
    345           std::string url_string;
    346           EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string));
    347           url_strings.push_back(url_string);
    348         }
    349       }
    350 
    351       // Second, resolve, validate and convert them to GURLs.
    352       for (std::vector<std::string>::iterator i = url_strings.begin();
    353            i != url_strings.end(); ++i) {
    354         GURL url = ResolvePossiblyRelativeURL(*i, GetExtension());
    355         if (!url.is_valid()) {
    356           error_ = ExtensionErrorUtils::FormatErrorMessage(
    357               keys::kInvalidUrlError, *i);
    358           return false;
    359         }
    360         urls.push_back(url);
    361       }
    362     }
    363   }
    364 
    365   // Don't let the extension crash the browser or renderers.
    366   GURL browser_crash(chrome::kAboutBrowserCrash);
    367   GURL renderer_crash(chrome::kAboutCrashURL);
    368   if (std::find(urls.begin(), urls.end(), browser_crash) != urls.end() ||
    369       std::find(urls.begin(), urls.end(), renderer_crash) != urls.end()) {
    370     error_ = keys::kNoCrashBrowserError;
    371     return false;
    372   }
    373 
    374   // Look for optional tab id.
    375   if (args) {
    376     int tab_id;
    377     if (args->HasKey(keys::kTabIdKey)) {
    378       EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id));
    379 
    380       // Find the tab and detach it from the original window.
    381       Browser* source_browser = NULL;
    382       TabStripModel* source_tab_strip = NULL;
    383       int tab_index = -1;
    384       if (!GetTabById(tab_id, profile(), include_incognito(),
    385                       &source_browser, &source_tab_strip, &contents,
    386                       &tab_index, &error_))
    387         return false;
    388       contents = source_tab_strip->DetachTabContentsAt(tab_index);
    389       if (!contents) {
    390         error_ = ExtensionErrorUtils::FormatErrorMessage(
    391             keys::kTabNotFoundError, base::IntToString(tab_id));
    392         return false;
    393       }
    394     }
    395   }
    396 
    397   // Try to position the new browser relative its originating browser window.
    398   gfx::Rect  window_bounds;
    399   bool maximized;
    400   // The call offsets the bounds by kWindowTilePixels (defined in WindowSizer to
    401   // be 10)
    402   //
    403   // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
    404   // GetBrowserWindowBounds will default to saved "default" values for the app.
    405   WindowSizer::GetBrowserWindowBounds(std::string(), gfx::Rect(),
    406                                       GetCurrentBrowser(), &window_bounds,
    407                                       &maximized);
    408 
    409   // Calculate popup bounds separately. In ChromiumOS the default is 0x0 which
    410   // indicates default window sizes in PanelBrowserView. In other OSs popups
    411   // use the same default bounds as windows.
    412   gfx::Rect popup_bounds;
    413 #if !defined(OS_CHROMEOS)
    414   popup_bounds = window_bounds;  // Use window size as default for popups
    415 #endif
    416 
    417   Profile* window_profile = profile();
    418   Browser::Type window_type = Browser::TYPE_NORMAL;
    419   bool focused = true;
    420 
    421   if (args) {
    422     // Any part of the bounds can optionally be set by the caller.
    423     int bounds_val;
    424     if (args->HasKey(keys::kLeftKey)) {
    425       EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey,
    426                                                    &bounds_val));
    427       window_bounds.set_x(bounds_val);
    428       popup_bounds.set_x(bounds_val);
    429     }
    430 
    431     if (args->HasKey(keys::kTopKey)) {
    432       EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey,
    433                                                    &bounds_val));
    434       window_bounds.set_y(bounds_val);
    435       popup_bounds.set_y(bounds_val);
    436     }
    437 
    438     if (args->HasKey(keys::kWidthKey)) {
    439       EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey,
    440                                                    &bounds_val));
    441       window_bounds.set_width(bounds_val);
    442       popup_bounds.set_width(bounds_val);
    443     }
    444 
    445     if (args->HasKey(keys::kHeightKey)) {
    446       EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey,
    447                                                    &bounds_val));
    448       window_bounds.set_height(bounds_val);
    449       popup_bounds.set_height(bounds_val);
    450     }
    451 
    452     bool incognito = false;
    453     if (args->HasKey(keys::kIncognitoKey)) {
    454       EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey,
    455                                                    &incognito));
    456       if (!profile_->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled)) {
    457         error_ = keys::kIncognitoModeIsDisabled;
    458         return false;
    459       }
    460 
    461       if (incognito)
    462         window_profile = window_profile->GetOffTheRecordProfile();
    463     }
    464 
    465     if (args->HasKey(keys::kFocusedKey))
    466       EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey,
    467                                                    &focused));
    468 
    469     std::string type_str;
    470     if (args->HasKey(keys::kWindowTypeKey)) {
    471       EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey,
    472                                                   &type_str));
    473       if (type_str == keys::kWindowTypeValueNormal) {
    474         window_type = Browser::TYPE_NORMAL;
    475       } else if (type_str == keys::kWindowTypeValuePopup) {
    476         window_type = Browser::TYPE_APP_POPUP;
    477       } else if (type_str == keys::kWindowTypeValuePanel) {
    478         if (GetExtension()->HasApiPermission(
    479                 Extension::kExperimentalPermission)) {
    480           window_type = Browser::TYPE_APP_PANEL;
    481         } else {
    482           error_ = errors::kExperimentalFeature;
    483           return false;
    484         }
    485       } else {
    486         EXTENSION_FUNCTION_VALIDATE(false);
    487       }
    488     }
    489   }
    490 
    491   Browser* new_window = Browser::CreateForType(window_type, window_profile);
    492   for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i)
    493     new_window->AddSelectedTabWithURL(*i, PageTransition::LINK);
    494   if (contents) {
    495     TabStripModel* target_tab_strip = new_window->tabstrip_model();
    496     target_tab_strip->InsertTabContentsAt(urls.size(), contents,
    497                                           TabStripModel::ADD_NONE);
    498   } else if (urls.empty()) {
    499     new_window->NewTab();
    500   }
    501   new_window->SelectNumberedTab(0);
    502   if (window_type & Browser::TYPE_POPUP)
    503     new_window->window()->SetBounds(popup_bounds);
    504   else
    505     new_window->window()->SetBounds(window_bounds);
    506 
    507   if (focused)
    508     new_window->window()->Show();
    509   else
    510     new_window->window()->ShowInactive();
    511 
    512   if (new_window->profile()->IsOffTheRecord() && !include_incognito()) {
    513     // Don't expose incognito windows if the extension isn't allowed.
    514     result_.reset(Value::CreateNullValue());
    515   } else {
    516     result_.reset(ExtensionTabUtil::CreateWindowValue(new_window, true));
    517   }
    518 
    519   return true;
    520 }
    521 
    522 bool UpdateWindowFunction::RunImpl() {
    523   int window_id;
    524   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
    525   DictionaryValue* update_props;
    526   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
    527 
    528   Browser* browser = GetBrowserInProfileWithId(profile(), window_id,
    529                                                include_incognito(), &error_);
    530   if (!browser || !browser->window()) {
    531     error_ = ExtensionErrorUtils::FormatErrorMessage(
    532         keys::kWindowNotFoundError, base::IntToString(window_id));
    533     return false;
    534   }
    535 
    536   gfx::Rect bounds = browser->window()->GetRestoredBounds();
    537   bool set_bounds = false;
    538   // Any part of the bounds can optionally be set by the caller.
    539   int bounds_val;
    540   if (update_props->HasKey(keys::kLeftKey)) {
    541     EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    542         keys::kLeftKey,
    543         &bounds_val));
    544     bounds.set_x(bounds_val);
    545     set_bounds = true;
    546   }
    547 
    548   if (update_props->HasKey(keys::kTopKey)) {
    549     EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    550         keys::kTopKey,
    551         &bounds_val));
    552     bounds.set_y(bounds_val);
    553     set_bounds = true;
    554   }
    555 
    556   if (update_props->HasKey(keys::kWidthKey)) {
    557     EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    558         keys::kWidthKey,
    559         &bounds_val));
    560     bounds.set_width(bounds_val);
    561     set_bounds = true;
    562   }
    563 
    564   if (update_props->HasKey(keys::kHeightKey)) {
    565     EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    566         keys::kHeightKey,
    567         &bounds_val));
    568     bounds.set_height(bounds_val);
    569     set_bounds = true;
    570   }
    571   if (set_bounds)
    572     browser->window()->SetBounds(bounds);
    573 
    574   bool selected_val = false;
    575   if (update_props->HasKey(keys::kFocusedKey)) {
    576     EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
    577         keys::kFocusedKey, &selected_val));
    578     if (selected_val)
    579       browser->window()->Activate();
    580     else
    581       browser->window()->Deactivate();
    582   }
    583 
    584   result_.reset(ExtensionTabUtil::CreateWindowValue(browser, false));
    585 
    586   return true;
    587 }
    588 
    589 bool RemoveWindowFunction::RunImpl() {
    590   int window_id;
    591   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
    592 
    593   Browser* browser = GetBrowserInProfileWithId(profile(), window_id,
    594                                                include_incognito(), &error_);
    595   if (!browser)
    596     return false;
    597 
    598   // Don't let the extension remove the window if the user is dragging tabs
    599   // in that window.
    600   if (!browser->IsTabStripEditable()) {
    601     error_ = keys::kTabStripNotEditableError;
    602     return false;
    603   }
    604 
    605   browser->CloseWindow();
    606 
    607   return true;
    608 }
    609 
    610 // Tabs ------------------------------------------------------------------------
    611 
    612 bool GetSelectedTabFunction::RunImpl() {
    613   Browser* browser;
    614   // windowId defaults to "current" window.
    615   int window_id = -1;
    616 
    617   if (HasOptionalArgument(0)) {
    618     EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
    619     browser = GetBrowserInProfileWithId(profile(), window_id,
    620                                         include_incognito(), &error_);
    621   } else {
    622     browser = GetCurrentBrowser();
    623     if (!browser)
    624       error_ = keys::kNoCurrentWindowError;
    625   }
    626   if (!browser)
    627     return false;
    628 
    629   TabStripModel* tab_strip = browser->tabstrip_model();
    630   TabContentsWrapper* contents = tab_strip->GetSelectedTabContents();
    631   if (!contents) {
    632     error_ = keys::kNoSelectedTabError;
    633     return false;
    634   }
    635   result_.reset(ExtensionTabUtil::CreateTabValue(contents->tab_contents(),
    636       tab_strip,
    637       tab_strip->active_index()));
    638   return true;
    639 }
    640 
    641 bool GetAllTabsInWindowFunction::RunImpl() {
    642   Browser* browser;
    643   // windowId defaults to "current" window.
    644   int window_id = -1;
    645   if (HasOptionalArgument(0)) {
    646     EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
    647     browser = GetBrowserInProfileWithId(profile(), window_id,
    648                                         include_incognito(), &error_);
    649   } else {
    650     browser = GetCurrentBrowser();
    651     if (!browser)
    652       error_ = keys::kNoCurrentWindowError;
    653   }
    654   if (!browser)
    655     return false;
    656 
    657   result_.reset(ExtensionTabUtil::CreateTabList(browser));
    658 
    659   return true;
    660 }
    661 
    662 bool CreateTabFunction::RunImpl() {
    663   DictionaryValue* args;
    664   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
    665 
    666   Browser *browser;
    667   // windowId defaults to "current" window.
    668   int window_id = -1;
    669   if (args->HasKey(keys::kWindowIdKey)) {
    670     EXTENSION_FUNCTION_VALIDATE(args->GetInteger(
    671         keys::kWindowIdKey, &window_id));
    672     browser = GetBrowserInProfileWithId(profile(), window_id,
    673                                         include_incognito(), &error_);
    674   } else {
    675     browser = GetCurrentBrowser();
    676     if (!browser)
    677       error_ = keys::kNoCurrentWindowError;
    678   }
    679   if (!browser)
    680     return false;
    681 
    682   // TODO(rafaelw): handle setting remaining tab properties:
    683   // -title
    684   // -favIconUrl
    685 
    686   std::string url_string;
    687   GURL url;
    688   if (args->HasKey(keys::kUrlKey)) {
    689     EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kUrlKey,
    690                                                 &url_string));
    691     url = ResolvePossiblyRelativeURL(url_string, GetExtension());
    692     if (!url.is_valid()) {
    693       error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
    694                                                        url_string);
    695       return false;
    696     }
    697   }
    698 
    699   // Don't let extensions crash the browser or renderers.
    700   if (url == GURL(chrome::kAboutBrowserCrash) ||
    701       url == GURL(chrome::kAboutCrashURL)) {
    702     error_ = keys::kNoCrashBrowserError;
    703     return false;
    704   }
    705 
    706   // Default to foreground for the new tab. The presence of 'selected' property
    707   // will override this default.
    708   bool selected = true;
    709   if (args->HasKey(keys::kSelectedKey))
    710     EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey,
    711                                                  &selected));
    712 
    713   // Default to not pinning the tab. Setting the 'pinned' property to true
    714   // will override this default.
    715   bool pinned = false;
    716   if (args->HasKey(keys::kPinnedKey))
    717     EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned));
    718 
    719   // We can't load extension URLs into incognito windows unless the extension
    720   // uses split mode. Special case to fall back to a normal window.
    721   if (url.SchemeIs(chrome::kExtensionScheme) &&
    722       !GetExtension()->incognito_split_mode() &&
    723       browser->profile()->IsOffTheRecord()) {
    724     Profile* profile = browser->profile()->GetOriginalProfile();
    725     browser = BrowserList::FindBrowserWithType(profile,
    726                                                Browser::TYPE_NORMAL, false);
    727     if (!browser) {
    728       browser = Browser::Create(profile);
    729       browser->window()->Show();
    730     }
    731   }
    732 
    733   // If index is specified, honor the value, but keep it bound to
    734   // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
    735   int index = -1;
    736   if (args->HasKey(keys::kIndexKey))
    737     EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kIndexKey, &index));
    738 
    739   TabStripModel* tab_strip = browser->tabstrip_model();
    740 
    741   index = std::min(std::max(index, -1), tab_strip->count());
    742 
    743   int add_types = selected ? TabStripModel::ADD_ACTIVE :
    744                              TabStripModel::ADD_NONE;
    745   add_types |= TabStripModel::ADD_FORCE_INDEX;
    746   if (pinned)
    747     add_types |= TabStripModel::ADD_PINNED;
    748   browser::NavigateParams params(browser, url, PageTransition::LINK);
    749   params.disposition = selected ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
    750   params.tabstrip_index = index;
    751   params.tabstrip_add_types = add_types;
    752   browser::Navigate(&params);
    753 
    754   if (selected)
    755     params.target_contents->view()->SetInitialFocus();
    756 
    757   // Return data about the newly created tab.
    758   if (has_callback()) {
    759     result_.reset(ExtensionTabUtil::CreateTabValue(
    760         params.target_contents->tab_contents(),
    761         params.browser->tabstrip_model(),
    762         params.browser->tabstrip_model()->GetIndexOfTabContents(
    763             params.target_contents)));
    764   }
    765 
    766   return true;
    767 }
    768 
    769 bool GetTabFunction::RunImpl() {
    770   int tab_id;
    771   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    772 
    773   TabStripModel* tab_strip = NULL;
    774   TabContentsWrapper* contents = NULL;
    775   int tab_index = -1;
    776   if (!GetTabById(tab_id, profile(), include_incognito(),
    777                   NULL, &tab_strip, &contents, &tab_index, &error_))
    778     return false;
    779 
    780   result_.reset(ExtensionTabUtil::CreateTabValue(contents->tab_contents(),
    781       tab_strip,
    782       tab_index));
    783   return true;
    784 }
    785 
    786 bool GetCurrentTabFunction::RunImpl() {
    787   DCHECK(dispatcher());
    788 
    789   TabContents* contents = dispatcher()->delegate()->associated_tab_contents();
    790   if (contents)
    791     result_.reset(ExtensionTabUtil::CreateTabValue(contents));
    792 
    793   return true;
    794 }
    795 
    796 UpdateTabFunction::UpdateTabFunction()
    797     : ALLOW_THIS_IN_INITIALIZER_LIST(registrar_(this)) {
    798 }
    799 
    800 bool UpdateTabFunction::RunImpl() {
    801   int tab_id;
    802   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    803   DictionaryValue* update_props;
    804   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
    805 
    806   TabStripModel* tab_strip = NULL;
    807   TabContentsWrapper* contents = NULL;
    808   int tab_index = -1;
    809   if (!GetTabById(tab_id, profile(), include_incognito(),
    810                   NULL, &tab_strip, &contents, &tab_index, &error_))
    811     return false;
    812 
    813   NavigationController& controller = contents->controller();
    814 
    815   // TODO(rafaelw): handle setting remaining tab properties:
    816   // -title
    817   // -favIconUrl
    818 
    819   // Navigate the tab to a new location if the url different.
    820   std::string url_string;
    821   if (update_props->HasKey(keys::kUrlKey)) {
    822     EXTENSION_FUNCTION_VALIDATE(update_props->GetString(
    823         keys::kUrlKey, &url_string));
    824     GURL url = ResolvePossiblyRelativeURL(url_string, GetExtension());
    825 
    826     if (!url.is_valid()) {
    827       error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
    828                                                        url_string);
    829       return false;
    830     }
    831 
    832     // Don't let the extension crash the browser or renderers.
    833     if (url == GURL(chrome::kAboutBrowserCrash) ||
    834         url == GURL(chrome::kAboutCrashURL)) {
    835       error_ = keys::kNoCrashBrowserError;
    836       return false;
    837     }
    838 
    839     // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
    840     // we need to check host permissions before allowing them.
    841     if (url.SchemeIs(chrome::kJavaScriptScheme)) {
    842       if (!GetExtension()->CanExecuteScriptOnPage(
    843               contents->tab_contents()->GetURL(), NULL, &error_)) {
    844         return false;
    845       }
    846 
    847       ExtensionMsg_ExecuteCode_Params params;
    848       params.request_id = request_id();
    849       params.extension_id = extension_id();
    850       params.is_javascript = true;
    851       params.code = url.path();
    852       params.all_frames = false;
    853       params.in_main_world = true;
    854 
    855       RenderViewHost* render_view_host =
    856           contents->tab_contents()->render_view_host();
    857       render_view_host->Send(
    858           new ExtensionMsg_ExecuteCode(render_view_host->routing_id(),
    859                                        params));
    860 
    861       registrar_.Observe(contents->tab_contents());
    862       AddRef();  // balanced in Observe()
    863 
    864       return true;
    865     }
    866 
    867     controller.LoadURL(url, GURL(), PageTransition::LINK);
    868 
    869     // The URL of a tab contents never actually changes to a JavaScript URL, so
    870     // this check only makes sense in other cases.
    871     if (!url.SchemeIs(chrome::kJavaScriptScheme))
    872       DCHECK_EQ(url.spec(), contents->tab_contents()->GetURL().spec());
    873   }
    874 
    875   bool selected = false;
    876   // TODO(rafaelw): Setting |selected| from js doesn't make much sense.
    877   // Move tab selection management up to window.
    878   if (update_props->HasKey(keys::kSelectedKey)) {
    879     EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
    880         keys::kSelectedKey,
    881         &selected));
    882     if (selected) {
    883       if (tab_strip->active_index() != tab_index) {
    884         tab_strip->ActivateTabAt(tab_index, false);
    885         DCHECK_EQ(contents, tab_strip->GetSelectedTabContents());
    886       }
    887       contents->tab_contents()->Focus();
    888     }
    889   }
    890 
    891   bool pinned = false;
    892   if (update_props->HasKey(keys::kPinnedKey)) {
    893     EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(keys::kPinnedKey,
    894                                                          &pinned));
    895     tab_strip->SetTabPinned(tab_index, pinned);
    896 
    897     // Update the tab index because it may move when being pinned.
    898     tab_index = tab_strip->GetIndexOfTabContents(contents);
    899   }
    900 
    901   if (has_callback())
    902     result_.reset(ExtensionTabUtil::CreateTabValue(contents->tab_contents(),
    903         tab_strip,
    904         tab_index));
    905 
    906   SendResponse(true);
    907   return true;
    908 }
    909 
    910 bool UpdateTabFunction::OnMessageReceived(const IPC::Message& message) {
    911   if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
    912     return false;
    913 
    914   int message_request_id;
    915   void* iter = NULL;
    916   if (!message.ReadInt(&iter, &message_request_id)) {
    917     NOTREACHED() << "malformed extension message";
    918     return true;
    919   }
    920 
    921   if (message_request_id != request_id())
    922     return false;
    923 
    924   IPC_BEGIN_MESSAGE_MAP(UpdateTabFunction, message)
    925     IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
    926                         OnExecuteCodeFinished)
    927   IPC_END_MESSAGE_MAP()
    928   return true;
    929 }
    930 
    931 void UpdateTabFunction::OnExecuteCodeFinished(int request_id,
    932                                               bool success,
    933                                               const std::string& error) {
    934   if (!error.empty()) {
    935     CHECK(!success);
    936     error_ = error;
    937   }
    938 
    939   SendResponse(success);
    940 
    941   registrar_.Observe(NULL);
    942   Release();  // balanced in Execute()
    943 }
    944 
    945 bool MoveTabFunction::RunImpl() {
    946   int tab_id;
    947   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    948   DictionaryValue* update_props;
    949   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
    950 
    951   int new_index;
    952   EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    953       keys::kIndexKey, &new_index));
    954   EXTENSION_FUNCTION_VALIDATE(new_index >= 0);
    955 
    956   Browser* source_browser = NULL;
    957   TabStripModel* source_tab_strip = NULL;
    958   TabContentsWrapper* contents = NULL;
    959   int tab_index = -1;
    960   if (!GetTabById(tab_id, profile(), include_incognito(),
    961                   &source_browser, &source_tab_strip, &contents,
    962                   &tab_index, &error_))
    963     return false;
    964 
    965   // Don't let the extension move the tab if the user is dragging tabs.
    966   if (!source_browser->IsTabStripEditable()) {
    967     error_ = keys::kTabStripNotEditableError;
    968     return false;
    969   }
    970 
    971   if (update_props->HasKey(keys::kWindowIdKey)) {
    972     Browser* target_browser;
    973     int window_id;
    974     EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
    975         keys::kWindowIdKey, &window_id));
    976     target_browser = GetBrowserInProfileWithId(profile(), window_id,
    977                                                include_incognito(), &error_);
    978     if (!target_browser)
    979       return false;
    980 
    981     if (!target_browser->IsTabStripEditable()) {
    982       error_ = keys::kTabStripNotEditableError;
    983       return false;
    984     }
    985 
    986     if (target_browser->type() != Browser::TYPE_NORMAL) {
    987       error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
    988       return false;
    989     }
    990 
    991     if (target_browser->profile() != source_browser->profile()) {
    992       error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
    993       return false;
    994     }
    995 
    996     // If windowId is different from the current window, move between windows.
    997     if (ExtensionTabUtil::GetWindowId(target_browser) !=
    998         ExtensionTabUtil::GetWindowId(source_browser)) {
    999       TabStripModel* target_tab_strip = target_browser->tabstrip_model();
   1000       contents = source_tab_strip->DetachTabContentsAt(tab_index);
   1001       if (!contents) {
   1002         error_ = ExtensionErrorUtils::FormatErrorMessage(
   1003             keys::kTabNotFoundError, base::IntToString(tab_id));
   1004         return false;
   1005       }
   1006 
   1007       // Clamp move location to the last position.
   1008       // This is ">" because it can append to a new index position.
   1009       if (new_index > target_tab_strip->count())
   1010         new_index = target_tab_strip->count();
   1011 
   1012       target_tab_strip->InsertTabContentsAt(new_index, contents,
   1013                                             TabStripModel::ADD_NONE);
   1014 
   1015       if (has_callback())
   1016         result_.reset(ExtensionTabUtil::CreateTabValue(contents->tab_contents(),
   1017             target_tab_strip, new_index));
   1018 
   1019       return true;
   1020     }
   1021   }
   1022 
   1023   // Perform a simple within-window move.
   1024   // Clamp move location to the last position.
   1025   // This is ">=" because the move must be to an existing location.
   1026   if (new_index >= source_tab_strip->count())
   1027     new_index = source_tab_strip->count() - 1;
   1028 
   1029   if (new_index != tab_index)
   1030     source_tab_strip->MoveTabContentsAt(tab_index, new_index, false);
   1031 
   1032   if (has_callback())
   1033     result_.reset(ExtensionTabUtil::CreateTabValue(contents->tab_contents(),
   1034                                                    source_tab_strip,
   1035                                                    new_index));
   1036   return true;
   1037 }
   1038 
   1039 
   1040 bool RemoveTabFunction::RunImpl() {
   1041   int tab_id;
   1042   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
   1043 
   1044   Browser* browser = NULL;
   1045   TabContentsWrapper* contents = NULL;
   1046   if (!GetTabById(tab_id, profile(), include_incognito(),
   1047                   &browser, NULL, &contents, NULL, &error_))
   1048     return false;
   1049 
   1050   // Don't let the extension remove a tab if the user is dragging tabs around.
   1051   if (!browser->IsTabStripEditable()) {
   1052     error_ = keys::kTabStripNotEditableError;
   1053     return false;
   1054   }
   1055 
   1056   // Close the tab in this convoluted way, since there's a chance that the tab
   1057   // is being dragged, or we're in some other nested event loop. This code path
   1058   // should ensure that the tab is safely closed under such circumstances,
   1059   // whereas |Browser::CloseTabContents()| does not.
   1060   RenderViewHost* render_view_host = contents->render_view_host();
   1061   render_view_host->delegate()->Close(render_view_host);
   1062   return true;
   1063 }
   1064 
   1065 bool CaptureVisibleTabFunction::RunImpl() {
   1066   Browser* browser;
   1067   // windowId defaults to "current" window.
   1068   int window_id = -1;
   1069 
   1070   if (HasOptionalArgument(0)) {
   1071     EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
   1072     browser = GetBrowserInProfileWithId(profile(), window_id,
   1073                                         include_incognito(), &error_);
   1074   } else {
   1075     browser = GetCurrentBrowser();
   1076   }
   1077 
   1078   if (!browser) {
   1079     error_ = keys::kNoCurrentWindowError;
   1080     return false;
   1081   }
   1082 
   1083   image_format_ = FORMAT_JPEG;  // Default format is JPEG.
   1084   image_quality_ = kDefaultQuality;  // Default quality setting.
   1085 
   1086   if (HasOptionalArgument(1)) {
   1087     DictionaryValue* options;
   1088     EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
   1089 
   1090     if (options->HasKey(keys::kFormatKey)) {
   1091       std::string format;
   1092       EXTENSION_FUNCTION_VALIDATE(
   1093           options->GetString(keys::kFormatKey, &format));
   1094 
   1095       if (format == keys::kFormatValueJpeg) {
   1096         image_format_ = FORMAT_JPEG;
   1097       } else if (format == keys::kFormatValuePng) {
   1098         image_format_ = FORMAT_PNG;
   1099       } else {
   1100         // Schema validation should make this unreachable.
   1101         EXTENSION_FUNCTION_VALIDATE(0);
   1102       }
   1103     }
   1104 
   1105     if (options->HasKey(keys::kQualityKey)) {
   1106       EXTENSION_FUNCTION_VALIDATE(
   1107           options->GetInteger(keys::kQualityKey, &image_quality_));
   1108     }
   1109   }
   1110 
   1111   TabContents* tab_contents = browser->GetSelectedTabContents();
   1112   if (!tab_contents) {
   1113     error_ = keys::kInternalVisibleTabCaptureError;
   1114     return false;
   1115   }
   1116 
   1117   // captureVisibleTab() can return an image containing sensitive information
   1118   // that the browser would otherwise protect.  Ensure the extension has
   1119   // permission to do this.
   1120   if (!GetExtension()->CanCaptureVisiblePage(tab_contents->GetURL(), &error_))
   1121     return false;
   1122 
   1123   RenderViewHost* render_view_host = tab_contents->render_view_host();
   1124 
   1125   // If a backing store is cached for the tab we want to capture,
   1126   // and it can be copied into a bitmap, then use it to generate the image.
   1127   BackingStore* backing_store = render_view_host->GetBackingStore(false);
   1128   if (backing_store && CaptureSnapshotFromBackingStore(backing_store))
   1129     return true;
   1130 
   1131   // Ask the renderer for a snapshot of the tab.
   1132   render_view_host->CaptureSnapshot();
   1133   registrar_.Add(this,
   1134                  NotificationType::TAB_SNAPSHOT_TAKEN,
   1135                  NotificationService::AllSources());
   1136   AddRef();  // Balanced in CaptureVisibleTabFunction::Observe().
   1137 
   1138   return true;
   1139 }
   1140 
   1141 // Build the image of a tab's contents out of a backing store.
   1142 // This may fail if we can not copy a backing store into a bitmap.
   1143 // For example, some uncommon X11 visual modes are not supported by
   1144 // CopyFromBackingStore().
   1145 bool CaptureVisibleTabFunction::CaptureSnapshotFromBackingStore(
   1146     BackingStore* backing_store) {
   1147 
   1148   skia::PlatformCanvas temp_canvas;
   1149   if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()),
   1150                                            &temp_canvas)) {
   1151     return false;
   1152   }
   1153   VLOG(1) << "captureVisibleTab() got image from backing store.";
   1154 
   1155   SendResultFromBitmap(
   1156       temp_canvas.getTopPlatformDevice().accessBitmap(false));
   1157   return true;
   1158 }
   1159 
   1160 // If a backing store was not available in CaptureVisibleTabFunction::RunImpl,
   1161 // than the renderer was asked for a snapshot.  Listen for a notification
   1162 // that the snapshot is available.
   1163 void CaptureVisibleTabFunction::Observe(NotificationType type,
   1164                                         const NotificationSource& source,
   1165                                         const NotificationDetails& details) {
   1166   DCHECK(type == NotificationType::TAB_SNAPSHOT_TAKEN);
   1167 
   1168   const SkBitmap *screen_capture = Details<const SkBitmap>(details).ptr();
   1169   const bool error = screen_capture->empty();
   1170 
   1171   if (error) {
   1172     error_ = keys::kInternalVisibleTabCaptureError;
   1173     SendResponse(false);
   1174   } else {
   1175     VLOG(1) << "captureVisibleTab() got image from renderer.";
   1176     SendResultFromBitmap(*screen_capture);
   1177   }
   1178 
   1179   Release();  // Balanced in CaptureVisibleTabFunction::RunImpl().
   1180 }
   1181 
   1182 // Turn a bitmap of the screen into an image, set that image as the result,
   1183 // and call SendResponse().
   1184 void CaptureVisibleTabFunction::SendResultFromBitmap(
   1185     const SkBitmap& screen_capture) {
   1186   scoped_refptr<RefCountedBytes> image_data(new RefCountedBytes);
   1187   SkAutoLockPixels screen_capture_lock(screen_capture);
   1188   bool encoded = false;
   1189   std::string mime_type;
   1190   switch (image_format_) {
   1191     case FORMAT_JPEG:
   1192       encoded = gfx::JPEGCodec::Encode(
   1193           reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)),
   1194           gfx::JPEGCodec::FORMAT_SkBitmap,
   1195           screen_capture.width(),
   1196           screen_capture.height(),
   1197           static_cast<int>(screen_capture.rowBytes()),
   1198           image_quality_,
   1199           &image_data->data);
   1200       mime_type = keys::kMimeTypeJpeg;
   1201       break;
   1202     case FORMAT_PNG:
   1203       encoded = gfx::PNGCodec::EncodeBGRASkBitmap(
   1204           screen_capture,
   1205           true,  // Discard transparency.
   1206           &image_data->data);
   1207       mime_type = keys::kMimeTypePng;
   1208       break;
   1209     default:
   1210       NOTREACHED() << "Invalid image format.";
   1211   }
   1212 
   1213   if (!encoded) {
   1214     error_ = ExtensionErrorUtils::FormatErrorMessage(
   1215         keys::kInternalVisibleTabCaptureError, "");
   1216     SendResponse(false);
   1217     return;
   1218   }
   1219 
   1220   std::string base64_result;
   1221   std::string stream_as_string;
   1222   stream_as_string.resize(image_data->data.size());
   1223   memcpy(&stream_as_string[0],
   1224       reinterpret_cast<const char*>(&image_data->data[0]),
   1225       image_data->data.size());
   1226 
   1227   base::Base64Encode(stream_as_string, &base64_result);
   1228   base64_result.insert(0, base::StringPrintf("data:%s;base64,",
   1229                                              mime_type.c_str()));
   1230   result_.reset(new StringValue(base64_result));
   1231   SendResponse(true);
   1232 }
   1233 
   1234 bool DetectTabLanguageFunction::RunImpl() {
   1235   int tab_id = 0;
   1236   Browser* browser = NULL;
   1237   TabContentsWrapper* contents = NULL;
   1238 
   1239   // If |tab_id| is specified, look for it. Otherwise default to selected tab
   1240   // in the current window.
   1241   if (HasOptionalArgument(0)) {
   1242     EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
   1243     if (!GetTabById(tab_id, profile(), include_incognito(),
   1244                     &browser, NULL, &contents, NULL, &error_)) {
   1245       return false;
   1246     }
   1247     if (!browser || !contents)
   1248       return false;
   1249   } else {
   1250     browser = GetCurrentBrowser();
   1251     if (!browser)
   1252       return false;
   1253     contents = browser->tabstrip_model()->GetSelectedTabContents();
   1254     if (!contents)
   1255       return false;
   1256   }
   1257 
   1258   if (contents->controller().needs_reload()) {
   1259     // If the tab hasn't been loaded, don't wait for the tab to load.
   1260     error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
   1261     return false;
   1262   }
   1263 
   1264   AddRef();  // Balanced in GotLanguage()
   1265 
   1266   TranslateTabHelper* helper = contents->translate_tab_helper();
   1267   if (!helper->language_state().original_language().empty()) {
   1268     // Delay the callback invocation until after the current JS call has
   1269     // returned.
   1270     MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
   1271         this, &DetectTabLanguageFunction::GotLanguage,
   1272         helper->language_state().original_language()));
   1273     return true;
   1274   }
   1275   // The tab contents does not know its language yet.  Let's  wait until it
   1276   // receives it, or until the tab is closed/navigates to some other page.
   1277   registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
   1278                  Source<TabContents>(contents->tab_contents()));
   1279   registrar_.Add(this, NotificationType::TAB_CLOSING,
   1280                  Source<NavigationController>(&(contents->controller())));
   1281   registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
   1282                  Source<NavigationController>(&(contents->controller())));
   1283   return true;
   1284 }
   1285 
   1286 void DetectTabLanguageFunction::Observe(NotificationType type,
   1287                                         const NotificationSource& source,
   1288                                         const NotificationDetails& details) {
   1289   std::string language;
   1290   if (type == NotificationType::TAB_LANGUAGE_DETERMINED)
   1291     language = *Details<std::string>(details).ptr();
   1292 
   1293   registrar_.RemoveAll();
   1294 
   1295   // Call GotLanguage in all cases as we want to guarantee the callback is
   1296   // called for every API call the extension made.
   1297   GotLanguage(language);
   1298 }
   1299 
   1300 void DetectTabLanguageFunction::GotLanguage(const std::string& language) {
   1301   result_.reset(Value::CreateStringValue(language.c_str()));
   1302   SendResponse(true);
   1303 
   1304   Release();  // Balanced in Run()
   1305 }
   1306 
   1307 // static helpers
   1308 // TODO(jhawkins): Move these to unnamed namespace and remove static modifier.
   1309 
   1310 static Browser* GetBrowserInProfileWithId(Profile* profile,
   1311                                           const int window_id,
   1312                                           bool include_incognito,
   1313                                           std::string* error_message) {
   1314   Profile* incognito_profile =
   1315       include_incognito && profile->HasOffTheRecordProfile() ?
   1316           profile->GetOffTheRecordProfile() : NULL;
   1317   for (BrowserList::const_iterator browser = BrowserList::begin();
   1318        browser != BrowserList::end(); ++browser) {
   1319     if (((*browser)->profile() == profile ||
   1320          (*browser)->profile() == incognito_profile) &&
   1321         ExtensionTabUtil::GetWindowId(*browser) == window_id)
   1322       return *browser;
   1323   }
   1324 
   1325   if (error_message)
   1326     *error_message = ExtensionErrorUtils::FormatErrorMessage(
   1327         keys::kWindowNotFoundError, base::IntToString(window_id));
   1328 
   1329   return NULL;
   1330 }
   1331 
   1332 static bool GetTabById(int tab_id, Profile* profile,
   1333                        bool include_incognito,
   1334                        Browser** browser,
   1335                        TabStripModel** tab_strip,
   1336                        TabContentsWrapper** contents,
   1337                        int* tab_index,
   1338                        std::string* error_message) {
   1339   if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
   1340                                    browser, tab_strip, contents, tab_index))
   1341     return true;
   1342 
   1343   if (error_message)
   1344     *error_message = ExtensionErrorUtils::FormatErrorMessage(
   1345         keys::kTabNotFoundError, base::IntToString(tab_id));
   1346 
   1347   return false;
   1348 }
   1349 
   1350 static std::string GetWindowTypeText(Browser::Type type) {
   1351   if (type == Browser::TYPE_APP_PANEL &&
   1352       CommandLine::ForCurrentProcess()->HasSwitch(
   1353           switches::kEnableExperimentalExtensionApis))
   1354     return keys::kWindowTypeValuePanel;
   1355 
   1356   if ((type & Browser::TYPE_POPUP) == Browser::TYPE_POPUP)
   1357     return keys::kWindowTypeValuePopup;
   1358 
   1359   if ((type & Browser::TYPE_APP) == Browser::TYPE_APP)
   1360     return keys::kWindowTypeValueApp;
   1361 
   1362   DCHECK(type == Browser::TYPE_NORMAL);
   1363   return keys::kWindowTypeValueNormal;
   1364 }
   1365 
   1366 static GURL ResolvePossiblyRelativeURL(const std::string& url_string,
   1367                                        const Extension* extension) {
   1368   GURL url = GURL(url_string);
   1369   if (!url.is_valid())
   1370     url = extension->GetResourceURL(url_string);
   1371 
   1372   return url;
   1373 }
   1374