• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetWindowId(const Browser * browser)83 int ExtensionTabUtil::GetWindowId(const Browser* browser) {
84   return browser->session_id().id();
85 }
86 
GetTabId(const TabContents * tab_contents)87 int ExtensionTabUtil::GetTabId(const TabContents* tab_contents) {
88   return tab_contents->controller().session_id().id();
89 }
90 
GetTabStatusText(bool is_loading)91 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
92   return is_loading ? keys::kStatusValueLoading : keys::kStatusValueComplete;
93 }
94 
GetWindowIdOfTab(const TabContents * tab_contents)95 int ExtensionTabUtil::GetWindowIdOfTab(const TabContents* tab_contents) {
96   return tab_contents->controller().window_id().id();
97 }
98 
CreateTabValue(const TabContents * contents)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 
CreateTabList(const Browser * browser)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 
CreateTabValue(const TabContents * contents,TabStripModel * tab_strip,int tab_index)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.
CreateWindowValue(const Browser * browser,bool populate_tabs)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 
GetTabStripModel(const TabContents * tab_contents,TabStripModel ** tab_strip_model,int * tab_index)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 
GetDefaultTab(Browser * browser,TabContentsWrapper ** contents,int * tab_id)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 
GetTabById(int tab_id,Profile * profile,bool include_incognito,Browser ** browser,TabStripModel ** tab_strip,TabContentsWrapper ** contents,int * tab_index)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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
RunImpl()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 
UpdateTabFunction()796 UpdateTabFunction::UpdateTabFunction()
797     : ALLOW_THIS_IN_INITIALIZER_LIST(registrar_(this)) {
798 }
799 
RunImpl()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 
OnMessageReceived(const IPC::Message & message)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 
OnExecuteCodeFinished(int request_id,bool success,const std::string & error)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 
RunImpl()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 
RunImpl()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 
RunImpl()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().
CaptureSnapshotFromBackingStore(BackingStore * backing_store)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.
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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().
SendResultFromBitmap(const SkBitmap & screen_capture)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 
RunImpl()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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
GotLanguage(const std::string & language)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 
GetBrowserInProfileWithId(Profile * profile,const int window_id,bool include_incognito,std::string * error_message)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 
GetTabById(int tab_id,Profile * profile,bool include_incognito,Browser ** browser,TabStripModel ** tab_strip,TabContentsWrapper ** contents,int * tab_index,std::string * error_message)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 
GetWindowTypeText(Browser::Type type)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 
ResolvePossiblyRelativeURL(const std::string & url_string,const Extension * extension)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