Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1857)

Side by Side Diff: chrome/browser/extensions/api/tabs/tabs_api.cc

Issue 11778097: Revert revision 176015 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
10
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/message_loop.h"
17 #include "base/stl_util.h"
18 #include "base/string16.h"
19 #include "base/string_number_conversions.h"
20 #include "base/string_util.h"
21 #include "base/stringprintf.h"
22 #include "base/utf_string_conversions.h"
23 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
24 #include "chrome/browser/extensions/extension_function_dispatcher.h"
25 #include "chrome/browser/extensions/extension_function_util.h"
26 #include "chrome/browser/extensions/extension_host.h"
27 #include "chrome/browser/extensions/extension_service.h"
28 #include "chrome/browser/extensions/extension_tab_util.h"
29 #include "chrome/browser/extensions/file_reader.h"
30 #include "chrome/browser/extensions/script_executor.h"
31 #include "chrome/browser/extensions/tab_helper.h"
32 #include "chrome/browser/extensions/window_controller.h"
33 #include "chrome/browser/extensions/window_controller_list.h"
34 #include "chrome/browser/prefs/incognito_mode_prefs.h"
35 #include "chrome/browser/prefs/pref_service.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/translate/translate_tab_helper.h"
38 #include "chrome/browser/ui/browser.h"
39 #include "chrome/browser/ui/browser_commands.h"
40 #include "chrome/browser/ui/browser_finder.h"
41 #include "chrome/browser/ui/browser_list.h"
42 #include "chrome/browser/ui/browser_navigator.h"
43 #include "chrome/browser/ui/browser_tabstrip.h"
44 #include "chrome/browser/ui/browser_window.h"
45 #include "chrome/browser/ui/extensions/shell_window.h"
46 #include "chrome/browser/ui/host_desktop.h"
47 #include "chrome/browser/ui/panels/panel_manager.h"
48 #include "chrome/browser/ui/snapshot_tab_helper.h"
49 #include "chrome/browser/ui/tabs/tab_strip_model.h"
50 #include "chrome/browser/ui/window_sizer/window_sizer.h"
51 #include "chrome/browser/web_applications/web_app.h"
52 #include "chrome/common/chrome_notification_types.h"
53 #include "chrome/common/chrome_switches.h"
54 #include "chrome/common/extensions/api/tabs.h"
55 #include "chrome/common/extensions/api/windows.h"
56 #include "chrome/common/extensions/extension.h"
57 #include "chrome/common/extensions/extension_constants.h"
58 #include "chrome/common/extensions/extension_file_util.h"
59 #include "chrome/common/extensions/extension_l10n_util.h"
60 #include "chrome/common/extensions/extension_manifest_constants.h"
61 #include "chrome/common/extensions/extension_messages.h"
62 #include "chrome/common/extensions/message_bundle.h"
63 #include "chrome/common/extensions/user_script.h"
64 #include "chrome/common/pref_names.h"
65 #include "chrome/common/url_constants.h"
66 #include "content/public/browser/navigation_controller.h"
67 #include "content/public/browser/navigation_entry.h"
68 #include "content/public/browser/notification_details.h"
69 #include "content/public/browser/notification_source.h"
70 #include "content/public/browser/render_view_host.h"
71 #include "content/public/browser/render_widget_host_view.h"
72 #include "content/public/browser/web_contents.h"
73 #include "content/public/browser/web_contents_view.h"
74 #include "content/public/common/url_constants.h"
75 #include "extensions/common/constants.h"
76 #include "extensions/common/error_utils.h"
77 #include "skia/ext/image_operations.h"
78 #include "skia/ext/platform_canvas.h"
79 #include "third_party/skia/include/core/SkBitmap.h"
80 #include "ui/base/models/list_selection_model.h"
81 #include "ui/base/ui_base_types.h"
82 #include "ui/gfx/codec/jpeg_codec.h"
83 #include "ui/gfx/codec/png_codec.h"
84
85 #if defined(OS_WIN)
86 #include "win8/util/win8_util.h"
87 #endif // OS_WIN
88
89 namespace Get = extensions::api::windows::Get;
90 namespace GetAll = extensions::api::windows::GetAll;
91 namespace GetCurrent = extensions::api::windows::GetCurrent;
92 namespace GetLastFocused = extensions::api::windows::GetLastFocused;
93 namespace errors = extension_manifest_errors;
94 namespace keys = extensions::tabs_constants;
95
96 using content::BrowserThread;
97 using content::NavigationController;
98 using content::NavigationEntry;
99 using content::OpenURLParams;
100 using content::Referrer;
101 using content::RenderViewHost;
102 using content::WebContents;
103 using extensions::ErrorUtils;
104 using extensions::ScriptExecutor;
105 using extensions::UserScript;
106 using extensions::WindowController;
107 using extensions::WindowControllerList;
108 using extensions::api::tabs::InjectDetails;
109
110 const int TabsCaptureVisibleTabFunction::kDefaultQuality = 90;
111
112 namespace {
113
114 // |error_message| can optionally be passed in a will be set with an appropriate
115 // message if the window cannot be found by id.
116 Browser* GetBrowserInProfileWithId(Profile* profile,
117 const int window_id,
118 bool include_incognito,
119 std::string* error_message) {
120 Profile* incognito_profile =
121 include_incognito && profile->HasOffTheRecordProfile() ?
122 profile->GetOffTheRecordProfile() : NULL;
123 for (BrowserList::const_iterator browser = BrowserList::begin();
124 browser != BrowserList::end(); ++browser) {
125 if (((*browser)->profile() == profile ||
126 (*browser)->profile() == incognito_profile) &&
127 ExtensionTabUtil::GetWindowId(*browser) == window_id &&
128 ((*browser)->window()))
129 return *browser;
130 }
131
132 if (error_message)
133 *error_message = ErrorUtils::FormatErrorMessage(
134 keys::kWindowNotFoundError, base::IntToString(window_id));
135
136 return NULL;
137 }
138
139 bool GetBrowserFromWindowID(
140 UIThreadExtensionFunction* function, int window_id, Browser** browser) {
141 if (window_id == extension_misc::kCurrentWindowId) {
142 *browser = function->GetCurrentBrowser();
143 if (!(*browser) || !(*browser)->window()) {
144 function->SetError(keys::kNoCurrentWindowError);
145 return false;
146 }
147 } else {
148 std::string error;
149 *browser = GetBrowserInProfileWithId(
150 function->profile(), window_id, function->include_incognito(), &error);
151 if (!*browser) {
152 function->SetError(error);
153 return false;
154 }
155 }
156 return true;
157 }
158
159 bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
160 int window_id,
161 WindowController** controller) {
162 if (window_id == extension_misc::kCurrentWindowId) {
163 WindowController* extension_window_controller =
164 function->dispatcher()->delegate()->GetExtensionWindowController();
165 // If there is a window controller associated with this extension, use that.
166 if (extension_window_controller) {
167 *controller = extension_window_controller;
168 } else {
169 // Otherwise get the focused or most recently added window.
170 *controller = WindowControllerList::GetInstance()->
171 CurrentWindowForFunction(function);
172 }
173 if (!(*controller)) {
174 function->SetError(keys::kNoCurrentWindowError);
175 return false;
176 }
177 } else {
178 *controller = WindowControllerList::GetInstance()->
179 FindWindowForFunctionById(function, window_id);
180 if (!(*controller)) {
181 function->SetError(ErrorUtils::FormatErrorMessage(
182 keys::kWindowNotFoundError, base::IntToString(window_id)));
183 return false;
184 }
185 }
186 return true;
187 }
188
189 // |error_message| can optionally be passed in and will be set with an
190 // appropriate message if the tab cannot be found by id.
191 bool GetTabById(int tab_id,
192 Profile* profile,
193 bool include_incognito,
194 Browser** browser,
195 TabStripModel** tab_strip,
196 content::WebContents** contents,
197 int* tab_index,
198 std::string* error_message) {
199 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
200 browser, tab_strip, contents, tab_index))
201 return true;
202
203 if (error_message)
204 *error_message = ErrorUtils::FormatErrorMessage(
205 keys::kTabNotFoundError, base::IntToString(tab_id));
206
207 return false;
208 }
209
210 // A three state enum to distinguish between when a boolean query argument is
211 // set or not.
212 enum QueryArg {
213 NOT_SET = -1,
214 MATCH_FALSE,
215 MATCH_TRUE
216 };
217
218 bool MatchesQueryArg(QueryArg arg, bool value) {
219 if (arg == NOT_SET)
220 return true;
221
222 return (arg == MATCH_TRUE && value) || (arg == MATCH_FALSE && !value);
223 }
224
225 QueryArg ParseBoolQueryArg(base::DictionaryValue* query, const char* key) {
226 if (query->HasKey(key)) {
227 bool value = false;
228 CHECK(query->GetBoolean(key, &value));
229 return value ? MATCH_TRUE : MATCH_FALSE;
230 }
231 return NOT_SET;
232 }
233
234 Browser* CreateBrowserWindow(const Browser::CreateParams& params,
235 Profile* profile,
236 const std::string& extension_id) {
237 bool use_existing_browser_window = false;
238
239 #if defined(OS_WIN)
240 // In windows 8 metro mode we don't allow windows to be created.
241 if (win8::IsSingleWindowMetroMode())
242 use_existing_browser_window = true;
243 #endif // OS_WIN
244
245 Browser* new_window = NULL;
246 if (use_existing_browser_window)
247 // The false parameter passed below is to ensure that we find a browser
248 // object matching the profile passed in, instead of the original profile
249 new_window = chrome::FindTabbedBrowser(profile, false,
250 params.host_desktop_type);
251
252 if (!new_window)
253 new_window = new Browser(params);
254 return new_window;
255 }
256
257 } // namespace
258
259 // Windows ---------------------------------------------------------------------
260
261 bool WindowsGetFunction::RunImpl() {
262 scoped_ptr<Get::Params> params(Get::Params::Create(*args_));
263 EXTENSION_FUNCTION_VALIDATE(params.get());
264
265 bool populate_tabs = false;
266 if (params->get_info.get() && params->get_info->populate.get())
267 populate_tabs = *params->get_info->populate;
268
269 WindowController* controller;
270 if (!GetWindowFromWindowID(this, params->window_id, &controller))
271 return false;
272
273 if (populate_tabs)
274 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
275 else
276 SetResult(controller->CreateWindowValue());
277 return true;
278 }
279
280 bool WindowsGetCurrentFunction::RunImpl() {
281 scoped_ptr<GetCurrent::Params> params(GetCurrent::Params::Create(*args_));
282 EXTENSION_FUNCTION_VALIDATE(params.get());
283
284 bool populate_tabs = false;
285 if (params->get_info.get() && params->get_info->populate.get())
286 populate_tabs = *params->get_info->populate;
287
288 WindowController* controller;
289 if (!GetWindowFromWindowID(this,
290 extension_misc::kCurrentWindowId,
291 &controller)) {
292 return false;
293 }
294 if (populate_tabs)
295 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
296 else
297 SetResult(controller->CreateWindowValue());
298 return true;
299 }
300
301 bool WindowsGetLastFocusedFunction::RunImpl() {
302 scoped_ptr<GetLastFocused::Params> params(
303 GetLastFocused::Params::Create(*args_));
304 EXTENSION_FUNCTION_VALIDATE(params.get());
305
306 bool populate_tabs = false;
307 if (params->get_info.get() && params->get_info->populate.get())
308 populate_tabs = *params->get_info->populate;
309
310 // Note: currently this returns the last active browser. If we decide to
311 // include other window types (e.g. panels), we will need to add logic to
312 // WindowControllerList that mirrors the active behavior of BrowserList.
313 Browser* browser = chrome::FindAnyBrowser(
314 profile(), include_incognito(), chrome::GetActiveDesktop());
315 if (!browser || !browser->window()) {
316 error_ = keys::kNoLastFocusedWindowError;
317 return false;
318 }
319 WindowController* controller =
320 browser->extension_window_controller();
321 if (populate_tabs)
322 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
323 else
324 SetResult(controller->CreateWindowValue());
325 return true;
326 }
327
328 bool WindowsGetAllFunction::RunImpl() {
329 scoped_ptr<GetAll::Params> params(GetAll::Params::Create(*args_));
330 EXTENSION_FUNCTION_VALIDATE(params.get());
331
332 bool populate_tabs = false;
333 if (params->get_info.get() && params->get_info->populate.get())
334 populate_tabs = *params->get_info->populate;
335
336 ListValue* window_list = new ListValue();
337 const WindowControllerList::ControllerList& windows =
338 WindowControllerList::GetInstance()->windows();
339 for (WindowControllerList::ControllerList::const_iterator iter =
340 windows.begin();
341 iter != windows.end(); ++iter) {
342 if (!this->CanOperateOnWindow(*iter))
343 continue;
344 if (populate_tabs)
345 window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension()));
346 else
347 window_list->Append((*iter)->CreateWindowValue());
348 }
349 SetResult(window_list);
350 return true;
351 }
352
353 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
354 const base::DictionaryValue* args,
355 std::vector<GURL>* urls,
356 bool* is_error) {
357 *is_error = false;
358 const IncognitoModePrefs::Availability incognito_availability =
359 IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
360 bool incognito = false;
361 if (args && args->HasKey(keys::kIncognitoKey)) {
362 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey,
363 &incognito));
364 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
365 error_ = keys::kIncognitoModeIsDisabled;
366 *is_error = true;
367 return false;
368 }
369 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
370 error_ = keys::kIncognitoModeIsForced;
371 *is_error = true;
372 return false;
373 }
374 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
375 // If incognito argument is not specified explicitly, we default to
376 // incognito when forced so by policy.
377 incognito = true;
378 }
379
380 // Remove all URLs that are not allowed in an incognito session. Note that a
381 // ChromeOS guest session is not considered incognito in this case.
382 if (incognito && !profile_->IsGuestSession()) {
383 std::string first_url_erased;
384 for (size_t i = 0; i < urls->size();) {
385 if (chrome::IsURLAllowedInIncognito((*urls)[i], profile())) {
386 i++;
387 } else {
388 if (first_url_erased.empty())
389 first_url_erased = (*urls)[i].spec();
390 urls->erase(urls->begin() + i);
391 }
392 }
393 if (urls->empty() && !first_url_erased.empty()) {
394 error_ = ErrorUtils::FormatErrorMessage(
395 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
396 *is_error = true;
397 return false;
398 }
399 }
400 return incognito;
401 }
402
403 bool WindowsCreateFunction::RunImpl() {
404 DictionaryValue* args = NULL;
405 std::vector<GURL> urls;
406 WebContents* contents = NULL;
407
408 if (HasOptionalArgument(0))
409 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
410
411 // Look for optional url.
412 if (args) {
413 if (args->HasKey(keys::kUrlKey)) {
414 Value* url_value;
415 std::vector<std::string> url_strings;
416 args->Get(keys::kUrlKey, &url_value);
417
418 // First, get all the URLs the client wants to open.
419 if (url_value->IsType(Value::TYPE_STRING)) {
420 std::string url_string;
421 url_value->GetAsString(&url_string);
422 url_strings.push_back(url_string);
423 } else if (url_value->IsType(Value::TYPE_LIST)) {
424 const ListValue* url_list = static_cast<const ListValue*>(url_value);
425 for (size_t i = 0; i < url_list->GetSize(); ++i) {
426 std::string url_string;
427 EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string));
428 url_strings.push_back(url_string);
429 }
430 }
431
432 // Second, resolve, validate and convert them to GURLs.
433 for (std::vector<std::string>::iterator i = url_strings.begin();
434 i != url_strings.end(); ++i) {
435 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
436 *i, GetExtension());
437 if (!url.is_valid()) {
438 error_ = ErrorUtils::FormatErrorMessage(
439 keys::kInvalidUrlError, *i);
440 return false;
441 }
442 // Don't let the extension crash the browser or renderers.
443 if (ExtensionTabUtil::IsCrashURL(url)) {
444 error_ = keys::kNoCrashBrowserError;
445 return false;
446 }
447 urls.push_back(url);
448 }
449 }
450 }
451
452 // Look for optional tab id.
453 if (args) {
454 int tab_id = -1;
455 if (args->HasKey(keys::kTabIdKey)) {
456 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id));
457
458 // Find the tab and detach it from the original window.
459 TabStripModel* source_tab_strip = NULL;
460 int tab_index = -1;
461 if (!GetTabById(tab_id, profile(), include_incognito(),
462 NULL, &source_tab_strip,
463 NULL, &tab_index, &error_))
464 return false;
465 contents = source_tab_strip->DetachWebContentsAt(tab_index);
466 if (!contents) {
467 error_ = ErrorUtils::FormatErrorMessage(
468 keys::kTabNotFoundError, base::IntToString(tab_id));
469 return false;
470 }
471 }
472 }
473
474 Profile* window_profile = profile();
475 Browser::Type window_type = Browser::TYPE_TABBED;
476
477 // panel_create_mode only applies if window is TYPE_PANEL.
478 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
479
480 gfx::Rect window_bounds;
481 bool focused = true;
482 bool saw_focus_key = false;
483 std::string extension_id;
484
485 // Decide whether we are opening a normal window or an incognito window.
486 bool is_error = true;
487 bool open_incognito_window = ShouldOpenIncognitoWindow(args, &urls,
488 &is_error);
489 if (is_error) {
490 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
491 return false;
492 }
493 if (open_incognito_window) {
494 window_profile = window_profile->GetOffTheRecordProfile();
495 }
496
497 if (args) {
498 // Figure out window type before figuring out bounds so that default
499 // bounds can be set according to the window type.
500 std::string type_str;
501 if (args->HasKey(keys::kWindowTypeKey)) {
502 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey,
503 &type_str));
504 if (type_str == keys::kWindowTypeValuePopup) {
505 window_type = Browser::TYPE_POPUP;
506 extension_id = GetExtension()->id();
507 } else if (type_str == keys::kWindowTypeValuePanel ||
508 type_str == keys::kWindowTypeValueDetachedPanel) {
509 extension_id = GetExtension()->id();
510 bool use_panels = false;
511 #if !defined(OS_ANDROID)
512 use_panels = PanelManager::ShouldUsePanels(extension_id);
513 #endif
514 if (use_panels) {
515 window_type = Browser::TYPE_PANEL;
516 #if !defined(OS_CHROMEOS)
517 // Non-ChromeOS has both docked and detached panel types.
518 if (type_str == keys::kWindowTypeValueDetachedPanel)
519 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
520 #endif
521 } else {
522 window_type = Browser::TYPE_POPUP;
523 }
524 } else if (type_str != keys::kWindowTypeValueNormal) {
525 error_ = keys::kInvalidWindowTypeError;
526 return false;
527 }
528 }
529
530 // Initialize default window bounds according to window type.
531 // In ChromiumOS the default popup bounds is 0x0 which indicates default
532 // window sizes in PanelBrowserView. In other OSs use the same default
533 // bounds as windows.
534 #if !defined(OS_CHROMEOS)
535 if (Browser::TYPE_TABBED == window_type ||
536 Browser::TYPE_POPUP == window_type) {
537 #else
538 if (Browser::TYPE_TABBED == window_type) {
539 #endif
540 // Try to position the new browser relative to its originating
541 // browser window. The call offsets the bounds by kWindowTilePixels
542 // (defined in WindowSizer to be 10).
543 //
544 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
545 // GetBrowserWindowBounds will default to saved "default" values for
546 // the app.
547 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
548 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
549 gfx::Rect(),
550 GetCurrentBrowser(),
551 &window_bounds,
552 &show_state);
553 }
554
555 if (Browser::TYPE_PANEL == window_type &&
556 PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
557 window_bounds.set_origin(
558 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
559 }
560
561 // Any part of the bounds can optionally be set by the caller.
562 int bounds_val = -1;
563 if (args->HasKey(keys::kLeftKey)) {
564 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey,
565 &bounds_val));
566 window_bounds.set_x(bounds_val);
567 }
568
569 if (args->HasKey(keys::kTopKey)) {
570 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey,
571 &bounds_val));
572 window_bounds.set_y(bounds_val);
573 }
574
575 if (args->HasKey(keys::kWidthKey)) {
576 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey,
577 &bounds_val));
578 window_bounds.set_width(bounds_val);
579 }
580
581 if (args->HasKey(keys::kHeightKey)) {
582 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey,
583 &bounds_val));
584 window_bounds.set_height(bounds_val);
585 }
586
587 if (args->HasKey(keys::kFocusedKey)) {
588 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey,
589 &focused));
590 saw_focus_key = true;
591 }
592 }
593
594 #if !defined(OS_CHROMEOS)
595 if (window_type == Browser::TYPE_PANEL) {
596 std::string title =
597 web_app::GenerateApplicationNameFromExtensionId(extension_id);
598 // Note: Panels ignore all but the first url provided.
599 Panel* panel = PanelManager::GetInstance()->CreatePanel(
600 title, window_profile, urls[0], window_bounds, panel_create_mode);
601
602 // Unlike other window types, Panels do not take focus by default.
603 if (!saw_focus_key || !focused)
604 panel->ShowInactive();
605 else
606 panel->Show();
607
608 SetResult(
609 panel->extension_window_controller()->CreateWindowValueWithTabs(
610 GetExtension()));
611 return true;
612 }
613 #endif
614
615 // Create a new BrowserWindow.
616 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
617 Browser::CreateParams create_params(window_type, window_profile,
618 host_desktop_type);
619 if (extension_id.empty()) {
620 create_params.initial_bounds = window_bounds;
621 } else {
622 create_params = Browser::CreateParams::CreateForApp(
623 window_type,
624 web_app::GenerateApplicationNameFromExtensionId(extension_id),
625 window_bounds,
626 window_profile);
627 }
628 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
629 create_params.host_desktop_type = chrome::GetActiveDesktop();
630
631 Browser* new_window = CreateBrowserWindow(create_params, window_profile,
632 extension_id);
633
634 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
635 WebContents* tab = chrome::AddSelectedTabWithURL(
636 new_window, *i, content::PAGE_TRANSITION_LINK);
637 if (window_type == Browser::TYPE_PANEL) {
638 extensions::TabHelper::FromWebContents(tab)->
639 SetExtensionAppIconById(extension_id);
640 }
641 }
642 if (contents) {
643 TabStripModel* target_tab_strip = new_window->tab_strip_model();
644 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
645 TabStripModel::ADD_NONE);
646 } else if (urls.empty()) {
647 chrome::NewTab(new_window);
648 }
649 chrome::SelectNumberedTab(new_window, 0);
650
651 // Unlike other window types, Panels do not take focus by default.
652 if (!saw_focus_key && window_type == Browser::TYPE_PANEL)
653 focused = false;
654
655 if (focused)
656 new_window->window()->Show();
657 else
658 new_window->window()->ShowInactive();
659
660 if (new_window->profile()->IsOffTheRecord() && !include_incognito()) {
661 // Don't expose incognito windows if the extension isn't allowed.
662 SetResult(Value::CreateNullValue());
663 } else {
664 SetResult(
665 new_window->extension_window_controller()->CreateWindowValueWithTabs(
666 GetExtension()));
667 }
668
669 return true;
670 }
671
672 bool WindowsUpdateFunction::RunImpl() {
673 int window_id = extension_misc::kUnknownWindowId;
674 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
675 DictionaryValue* update_props;
676 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
677
678 WindowController* controller;
679 if (!GetWindowFromWindowID(this, window_id, &controller))
680 return false;
681
682 #if defined(OS_WIN)
683 // Silently ignore changes on the window for metro mode.
684 if (win8::IsSingleWindowMetroMode()) {
685 SetResult(controller->CreateWindowValue());
686 return true;
687 }
688 #endif
689
690 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change.
691 std::string state_str;
692 if (update_props->HasKey(keys::kShowStateKey)) {
693 EXTENSION_FUNCTION_VALIDATE(update_props->GetString(keys::kShowStateKey,
694 &state_str));
695 if (state_str == keys::kShowStateValueNormal) {
696 show_state = ui::SHOW_STATE_NORMAL;
697 } else if (state_str == keys::kShowStateValueMinimized) {
698 show_state = ui::SHOW_STATE_MINIMIZED;
699 } else if (state_str == keys::kShowStateValueMaximized) {
700 show_state = ui::SHOW_STATE_MAXIMIZED;
701 } else if (state_str == keys::kShowStateValueFullscreen) {
702 show_state = ui::SHOW_STATE_FULLSCREEN;
703 } else {
704 error_ = keys::kInvalidWindowStateError;
705 return false;
706 }
707 }
708
709 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
710 show_state != ui::SHOW_STATE_DEFAULT)
711 controller->SetFullscreenMode(false, GetExtension()->url());
712
713 switch (show_state) {
714 case ui::SHOW_STATE_MINIMIZED:
715 controller->window()->Minimize();
716 break;
717 case ui::SHOW_STATE_MAXIMIZED:
718 controller->window()->Maximize();
719 break;
720 case ui::SHOW_STATE_FULLSCREEN:
721 if (controller->window()->IsMinimized() ||
722 controller->window()->IsMaximized())
723 controller->window()->Restore();
724 controller->SetFullscreenMode(true, GetExtension()->url());
725 break;
726 case ui::SHOW_STATE_NORMAL:
727 controller->window()->Restore();
728 break;
729 default:
730 break;
731 }
732
733 gfx::Rect bounds;
734 if (controller->window()->IsMinimized())
735 bounds = controller->window()->GetRestoredBounds();
736 else
737 bounds = controller->window()->GetBounds();
738 bool set_bounds = false;
739
740 // Any part of the bounds can optionally be set by the caller.
741 int bounds_val;
742 if (update_props->HasKey(keys::kLeftKey)) {
743 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
744 keys::kLeftKey,
745 &bounds_val));
746 bounds.set_x(bounds_val);
747 set_bounds = true;
748 }
749
750 if (update_props->HasKey(keys::kTopKey)) {
751 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
752 keys::kTopKey,
753 &bounds_val));
754 bounds.set_y(bounds_val);
755 set_bounds = true;
756 }
757
758 if (update_props->HasKey(keys::kWidthKey)) {
759 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
760 keys::kWidthKey,
761 &bounds_val));
762 bounds.set_width(bounds_val);
763 set_bounds = true;
764 }
765
766 if (update_props->HasKey(keys::kHeightKey)) {
767 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
768 keys::kHeightKey,
769 &bounds_val));
770 bounds.set_height(bounds_val);
771 set_bounds = true;
772 }
773
774 if (set_bounds) {
775 if (show_state == ui::SHOW_STATE_MINIMIZED ||
776 show_state == ui::SHOW_STATE_MAXIMIZED ||
777 show_state == ui::SHOW_STATE_FULLSCREEN) {
778 error_ = keys::kInvalidWindowStateError;
779 return false;
780 }
781 controller->window()->SetBounds(bounds);
782 }
783
784 bool active_val = false;
785 if (update_props->HasKey(keys::kFocusedKey)) {
786 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
787 keys::kFocusedKey, &active_val));
788 if (active_val) {
789 if (show_state == ui::SHOW_STATE_MINIMIZED) {
790 error_ = keys::kInvalidWindowStateError;
791 return false;
792 }
793 controller->window()->Activate();
794 } else {
795 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
796 show_state == ui::SHOW_STATE_FULLSCREEN) {
797 error_ = keys::kInvalidWindowStateError;
798 return false;
799 }
800 controller->window()->Deactivate();
801 }
802 }
803
804 bool draw_attention = false;
805 if (update_props->HasKey(keys::kDrawAttentionKey)) {
806 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
807 keys::kDrawAttentionKey, &draw_attention));
808 controller->window()->FlashFrame(draw_attention);
809 }
810
811 SetResult(controller->CreateWindowValue());
812
813 return true;
814 }
815
816 bool WindowsRemoveFunction::RunImpl() {
817 int window_id = -1;
818 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
819
820 WindowController* controller;
821 if (!GetWindowFromWindowID(this, window_id, &controller))
822 return false;
823
824 #if defined(OS_WIN)
825 // In Windows 8 metro mode, an existing Browser instance is reused for
826 // hosting the extension tab. We should not be closing it as we don't own it.
827 if (win8::IsSingleWindowMetroMode())
828 return false;
829 #endif
830
831 WindowController::Reason reason;
832 if (!controller->CanClose(&reason)) {
833 if (reason == WindowController::REASON_NOT_EDITABLE)
834 error_ = keys::kTabStripNotEditableError;
835 return false;
836 }
837 controller->window()->Close();
838 return true;
839 }
840
841 // Tabs ------------------------------------------------------------------------
842
843 bool TabsGetSelectedFunction::RunImpl() {
844 // windowId defaults to "current" window.
845 int window_id = extension_misc::kCurrentWindowId;
846
847 if (HasOptionalArgument(0))
848 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
849
850 Browser* browser = NULL;
851 if (!GetBrowserFromWindowID(this, window_id, &browser))
852 return false;
853
854 TabStripModel* tab_strip = browser->tab_strip_model();
855 WebContents* contents = tab_strip->GetActiveWebContents();
856 if (!contents) {
857 error_ = keys::kNoSelectedTabError;
858 return false;
859 }
860 SetResult(ExtensionTabUtil::CreateTabValue(contents,
861 tab_strip,
862 tab_strip->active_index(),
863 GetExtension()));
864 return true;
865 }
866
867 bool TabsGetAllInWindowFunction::RunImpl() {
868 // windowId defaults to "current" window.
869 int window_id = extension_misc::kCurrentWindowId;
870 if (HasOptionalArgument(0))
871 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
872
873 Browser* browser = NULL;
874 if (!GetBrowserFromWindowID(this, window_id, &browser))
875 return false;
876
877 SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension()));
878
879 return true;
880 }
881
882 bool TabsQueryFunction::RunImpl() {
883 DictionaryValue* query = NULL;
884 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query));
885
886 QueryArg active = ParseBoolQueryArg(query, keys::kActiveKey);
887 QueryArg pinned = ParseBoolQueryArg(query, keys::kPinnedKey);
888 QueryArg selected = ParseBoolQueryArg(query, keys::kHighlightedKey);
889 QueryArg current_window = ParseBoolQueryArg(query, keys::kCurrentWindowKey);
890 QueryArg focused_window =
891 ParseBoolQueryArg(query, keys::kLastFocusedWindowKey);
892
893 QueryArg loading = NOT_SET;
894 if (query->HasKey(keys::kStatusKey)) {
895 std::string status;
896 EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kStatusKey, &status));
897 loading = (status == keys::kStatusValueLoading) ? MATCH_TRUE : MATCH_FALSE;
898 }
899
900 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
901 // not grant access to the content of the tabs, only to seeing their URLs and
902 // meta data.
903 URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
904 if (query->HasKey(keys::kUrlKey)) {
905 std::string value;
906 EXTENSION_FUNCTION_VALIDATE(query->GetString(keys::kUrlKey, &value));
907 url_pattern = URLPattern(URLPattern::SCHEME_ALL, value);
908 }
909
910 std::string title;
911 if (query->HasKey(keys::kTitleKey))
912 EXTENSION_FUNCTION_VALIDATE(
913 query->GetString(keys::kTitleKey, &title));
914
915 int window_id = extension_misc::kUnknownWindowId;
916 if (query->HasKey(keys::kWindowIdKey))
917 EXTENSION_FUNCTION_VALIDATE(
918 query->GetInteger(keys::kWindowIdKey, &window_id));
919
920 int index = -1;
921 if (query->HasKey(keys::kIndexKey))
922 EXTENSION_FUNCTION_VALIDATE(
923 query->GetInteger(keys::kIndexKey, &index));
924
925 std::string window_type;
926 if (query->HasKey(keys::kWindowTypeLongKey))
927 EXTENSION_FUNCTION_VALIDATE(
928 query->GetString(keys::kWindowTypeLongKey, &window_type));
929
930 ListValue* result = new ListValue();
931 for (BrowserList::const_iterator browser = BrowserList::begin();
932 browser != BrowserList::end(); ++browser) {
933 if (!profile()->IsSameProfile((*browser)->profile()))
934 continue;
935
936 if (!(*browser)->window())
937 continue;
938
939 if (!include_incognito() && profile() != (*browser)->profile())
940 continue;
941
942 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(*browser))
943 continue;
944
945 if (window_id == extension_misc::kCurrentWindowId &&
946 *browser != GetCurrentBrowser())
947 continue;
948
949 if (!MatchesQueryArg(current_window, *browser == GetCurrentBrowser()))
950 continue;
951
952 if (!MatchesQueryArg(focused_window, (*browser)->window()->IsActive()))
953 continue;
954
955 if (!window_type.empty() &&
956 window_type !=
957 (*browser)->extension_window_controller()->GetWindowTypeText())
958 continue;
959
960 TabStripModel* tab_strip = (*browser)->tab_strip_model();
961 for (int i = 0; i < tab_strip->count(); ++i) {
962 const WebContents* web_contents = tab_strip->GetWebContentsAt(i);
963
964 if (index > -1 && i != index)
965 continue;
966
967 if (!MatchesQueryArg(selected, tab_strip->IsTabSelected(i)))
968 continue;
969
970 if (!MatchesQueryArg(active, i == tab_strip->active_index()))
971 continue;
972
973 if (!MatchesQueryArg(pinned, tab_strip->IsTabPinned(i)))
974 continue;
975
976 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
977 UTF8ToUTF16(title)))
978 continue;
979
980 if (!url_pattern.MatchesURL(web_contents->GetURL()))
981 continue;
982
983 if (!MatchesQueryArg(loading, web_contents->IsLoading()))
984 continue;
985
986 result->Append(ExtensionTabUtil::CreateTabValue(
987 web_contents, tab_strip, i, GetExtension()));
988 }
989 }
990
991 SetResult(result);
992 return true;
993 }
994
995 bool TabsCreateFunction::RunImpl() {
996 DictionaryValue* args = NULL;
997 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
998
999 // windowId defaults to "current" window.
1000 int window_id = extension_misc::kCurrentWindowId;
1001 if (args->HasKey(keys::kWindowIdKey))
1002 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(
1003 keys::kWindowIdKey, &window_id));
1004
1005 Browser* browser = NULL;
1006 if (!GetBrowserFromWindowID(this, window_id, &browser))
1007 return false;
1008
1009 // Ensure the selected browser is tabbed.
1010 if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
1011 browser = chrome::FindTabbedBrowser(profile(), include_incognito(),
1012 browser->host_desktop_type());
1013
1014 if (!browser || !browser->window())
1015 return false;
1016
1017 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
1018 // represents the active tab.
1019 WebContents* opener = NULL;
1020 if (args->HasKey(keys::kOpenerTabIdKey)) {
1021 int opener_id = -1;
1022 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(
1023 keys::kOpenerTabIdKey, &opener_id));
1024
1025 if (!ExtensionTabUtil::GetTabById(
1026 opener_id, profile(), include_incognito(),
1027 NULL, NULL, &opener, NULL))
1028 return false;
1029 }
1030
1031 // TODO(rafaelw): handle setting remaining tab properties:
1032 // -title
1033 // -favIconUrl
1034
1035 std::string url_string;
1036 GURL url;
1037 if (args->HasKey(keys::kUrlKey)) {
1038 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kUrlKey,
1039 &url_string));
1040 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
1041 GetExtension());
1042 if (!url.is_valid()) {
1043 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
1044 url_string);
1045 return false;
1046 }
1047 }
1048
1049 // Don't let extensions crash the browser or renderers.
1050 if (ExtensionTabUtil::IsCrashURL(url)) {
1051 error_ = keys::kNoCrashBrowserError;
1052 return false;
1053 }
1054
1055 // Default to foreground for the new tab. The presence of 'selected' property
1056 // will override this default. This property is deprecated ('active' should
1057 // be used instead).
1058 bool active = true;
1059 if (args->HasKey(keys::kSelectedKey))
1060 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey, &active));
1061
1062 // The 'active' property has replaced the 'selected' property.
1063 if (args->HasKey(keys::kActiveKey))
1064 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kActiveKey, &active));
1065
1066 // Default to not pinning the tab. Setting the 'pinned' property to true
1067 // will override this default.
1068 bool pinned = false;
1069 if (args->HasKey(keys::kPinnedKey))
1070 EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned));
1071
1072 // We can't load extension URLs into incognito windows unless the extension
1073 // uses split mode. Special case to fall back to a tabbed window.
1074 if (url.SchemeIs(extensions::kExtensionScheme) &&
1075 !GetExtension()->incognito_split_mode() &&
1076 browser->profile()->IsOffTheRecord()) {
1077 Profile* profile = browser->profile()->GetOriginalProfile();
1078 chrome::HostDesktopType desktop_type = browser->host_desktop_type();
1079
1080 browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
1081 if (!browser) {
1082 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED,
1083 profile, desktop_type));
1084 browser->window()->Show();
1085 }
1086 }
1087
1088 // If index is specified, honor the value, but keep it bound to
1089 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
1090 int index = -1;
1091 if (args->HasKey(keys::kIndexKey))
1092 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kIndexKey, &index));
1093
1094 TabStripModel* tab_strip = browser->tab_strip_model();
1095
1096 index = std::min(std::max(index, -1), tab_strip->count());
1097
1098 int add_types = active ? TabStripModel::ADD_ACTIVE :
1099 TabStripModel::ADD_NONE;
1100 add_types |= TabStripModel::ADD_FORCE_INDEX;
1101 if (pinned)
1102 add_types |= TabStripModel::ADD_PINNED;
1103 chrome::NavigateParams params(browser, url, content::PAGE_TRANSITION_LINK);
1104 params.disposition = active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1105 params.tabstrip_index = index;
1106 params.tabstrip_add_types = add_types;
1107 chrome::Navigate(&params);
1108
1109 // The tab may have been created in a different window, so make sure we look
1110 // at the right tab strip.
1111 tab_strip = params.browser->tab_strip_model();
1112 int new_index = tab_strip->GetIndexOfWebContents(params.target_contents);
1113 if (opener)
1114 tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
1115
1116 if (active)
1117 params.target_contents->GetView()->SetInitialFocus();
1118
1119 // Return data about the newly created tab.
1120 if (has_callback()) {
1121 SetResult(ExtensionTabUtil::CreateTabValue(
1122 params.target_contents,
1123 tab_strip, new_index, GetExtension()));
1124 }
1125
1126 return true;
1127 }
1128
1129 bool TabsDuplicateFunction::RunImpl() {
1130 int tab_id = -1;
1131 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
1132
1133 Browser* browser = NULL;
1134 TabStripModel* tab_strip = NULL;
1135 int tab_index = -1;
1136 if (!GetTabById(tab_id, profile(), include_incognito(),
1137 &browser, &tab_strip, NULL, &tab_index, &error_)) {
1138 return false;
1139 }
1140
1141 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
1142 if (!has_callback())
1143 return true;
1144
1145 int new_index = tab_strip->GetIndexOfWebContents(new_contents);
1146
1147 // Return data about the newly created tab.
1148 SetResult(ExtensionTabUtil::CreateTabValue(
1149 new_contents,
1150 tab_strip, new_index, GetExtension()));
1151
1152 return true;
1153 }
1154
1155 bool TabsGetFunction::RunImpl() {
1156 int tab_id = -1;
1157 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
1158
1159 TabStripModel* tab_strip = NULL;
1160 WebContents* contents = NULL;
1161 int tab_index = -1;
1162 if (!GetTabById(tab_id, profile(), include_incognito(),
1163 NULL, &tab_strip, &contents, &tab_index, &error_))
1164 return false;
1165
1166 SetResult(ExtensionTabUtil::CreateTabValue(contents,
1167 tab_strip,
1168 tab_index,
1169 GetExtension()));
1170 return true;
1171 }
1172
1173 bool TabsGetCurrentFunction::RunImpl() {
1174 DCHECK(dispatcher());
1175
1176 WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
1177 if (contents)
1178 SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension()));
1179
1180 return true;
1181 }
1182
1183 bool TabsHighlightFunction::RunImpl() {
1184 DictionaryValue* info = NULL;
1185 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &info));
1186
1187 // Get the window id from the params; default to current window if omitted.
1188 int window_id = extension_misc::kCurrentWindowId;
1189 if (info->HasKey(keys::kWindowIdKey))
1190 EXTENSION_FUNCTION_VALIDATE(
1191 info->GetInteger(keys::kWindowIdKey, &window_id));
1192
1193 Browser* browser = NULL;
1194 if (!GetBrowserFromWindowID(this, window_id, &browser))
1195 return false;
1196
1197 TabStripModel* tabstrip = browser->tab_strip_model();
1198 ui::ListSelectionModel selection;
1199 int active_index = -1;
1200
1201 Value* tab_value = NULL;
1202 EXTENSION_FUNCTION_VALIDATE(info->Get(keys::kTabsKey, &tab_value));
1203
1204 std::vector<int> tab_indices;
1205 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1206 tab_value, &tab_indices));
1207
1208 // Create a new selection model as we read the list of tab indices.
1209 for (size_t i = 0; i < tab_indices.size(); ++i) {
1210 int index = tab_indices[i];
1211
1212 // Make sure the index is in range.
1213 if (!tabstrip->ContainsIndex(index)) {
1214 error_ = ErrorUtils::FormatErrorMessage(
1215 keys::kTabIndexNotFoundError, base::IntToString(index));
1216 return false;
1217 }
1218
1219 // By default, we make the first tab in the list active.
1220 if (active_index == -1)
1221 active_index = index;
1222
1223 selection.AddIndexToSelection(index);
1224 }
1225
1226 // Make sure they actually specified tabs to select.
1227 if (selection.empty()) {
1228 error_ = keys::kNoHighlightedTabError;
1229 return false;
1230 }
1231
1232 selection.set_active(active_index);
1233 browser->tab_strip_model()->SetSelectionFromModel(selection);
1234 SetResult(
1235 browser->extension_window_controller()->CreateWindowValueWithTabs(
1236 GetExtension()));
1237 return true;
1238 }
1239
1240 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1241 }
1242
1243 bool TabsUpdateFunction::RunImpl() {
1244 DictionaryValue* update_props = NULL;
1245 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
1246
1247 Value* tab_value = NULL;
1248 if (HasOptionalArgument(0)) {
1249 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1250 }
1251
1252 int tab_id = -1;
1253 WebContents* contents = NULL;
1254 if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) {
1255 Browser* browser = GetCurrentBrowser();
1256 if (!browser) {
1257 error_ = keys::kNoCurrentWindowError;
1258 return false;
1259 }
1260 contents = browser->tab_strip_model()->GetActiveWebContents();
1261 if (!contents) {
1262 error_ = keys::kNoSelectedTabError;
1263 return false;
1264 }
1265 tab_id = SessionID::IdForTab(contents);
1266 } else {
1267 EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id));
1268 }
1269
1270 int tab_index = -1;
1271 TabStripModel* tab_strip = NULL;
1272 if (!GetTabById(tab_id, profile(), include_incognito(),
1273 NULL, &tab_strip, &contents, &tab_index, &error_)) {
1274 return false;
1275 }
1276
1277 web_contents_ = contents;
1278
1279 // TODO(rafaelw): handle setting remaining tab properties:
1280 // -title
1281 // -favIconUrl
1282
1283 // Navigate the tab to a new location if the url is different.
1284 bool is_async = false;
1285 if (!UpdateURLIfPresent(update_props, tab_id, &is_async))
1286 return false;
1287
1288 bool active = false;
1289 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1290 // Move tab selection management up to window.
1291 if (update_props->HasKey(keys::kSelectedKey))
1292 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1293 keys::kSelectedKey, &active));
1294
1295 // The 'active' property has replaced 'selected'.
1296 if (update_props->HasKey(keys::kActiveKey))
1297 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1298 keys::kActiveKey, &active));
1299
1300 if (active) {
1301 if (tab_strip->active_index() != tab_index) {
1302 tab_strip->ActivateTabAt(tab_index, false);
1303 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1304 }
1305 web_contents_->Focus();
1306 }
1307
1308 if (update_props->HasKey(keys::kHighlightedKey)) {
1309 bool highlighted = false;
1310 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1311 keys::kHighlightedKey, &highlighted));
1312 if (highlighted != tab_strip->IsTabSelected(tab_index))
1313 tab_strip->ToggleSelectionAt(tab_index);
1314 }
1315
1316 if (update_props->HasKey(keys::kPinnedKey)) {
1317 bool pinned = false;
1318 EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
1319 keys::kPinnedKey, &pinned));
1320 tab_strip->SetTabPinned(tab_index, pinned);
1321
1322 // Update the tab index because it may move when being pinned.
1323 tab_index = tab_strip->GetIndexOfWebContents(contents);
1324 }
1325
1326 if (update_props->HasKey(keys::kOpenerTabIdKey)) {
1327 int opener_id = -1;
1328 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
1329 keys::kOpenerTabIdKey, &opener_id));
1330
1331 WebContents* opener_contents = NULL;
1332 if (!ExtensionTabUtil::GetTabById(
1333 opener_id, profile(), include_incognito(),
1334 NULL, NULL, &opener_contents, NULL))
1335 return false;
1336
1337 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1338 }
1339
1340 if (!is_async) {
1341 PopulateResult();
1342 SendResponse(true);
1343 }
1344 return true;
1345 }
1346
1347 bool TabsUpdateFunction::UpdateURLIfPresent(DictionaryValue* update_props,
1348 int tab_id,
1349 bool* is_async) {
1350 if (!update_props->HasKey(keys::kUrlKey))
1351 return true;
1352
1353 std::string url_string;
1354 EXTENSION_FUNCTION_VALIDATE(update_props->GetString(
1355 keys::kUrlKey, &url_string));
1356 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1357 url_string, GetExtension());
1358
1359 if (!url.is_valid()) {
1360 error_ = ErrorUtils::FormatErrorMessage(
1361 keys::kInvalidUrlError, url_string);
1362 return false;
1363 }
1364
1365 // Don't let the extension crash the browser or renderers.
1366 if (ExtensionTabUtil::IsCrashURL(url)) {
1367 error_ = keys::kNoCrashBrowserError;
1368 return false;
1369 }
1370
1371 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1372 // we need to check host permissions before allowing them.
1373 if (url.SchemeIs(chrome::kJavaScriptScheme)) {
1374 if (!GetExtension()->CanExecuteScriptOnPage(
1375 web_contents_->GetURL(),
1376 web_contents_->GetURL(),
1377 tab_id,
1378 NULL,
1379 &error_)) {
1380 return false;
1381 }
1382
1383 extensions::TabHelper::FromWebContents(web_contents_)->
1384 script_executor()->ExecuteScript(
1385 extension_id(),
1386 ScriptExecutor::JAVASCRIPT,
1387 url.path(),
1388 ScriptExecutor::TOP_FRAME,
1389 extensions::UserScript::DOCUMENT_IDLE,
1390 ScriptExecutor::MAIN_WORLD,
1391 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1392
1393 *is_async = true;
1394 return true;
1395 }
1396
1397 web_contents_->GetController().LoadURL(
1398 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1399
1400 // The URL of a tab contents never actually changes to a JavaScript URL, so
1401 // this check only makes sense in other cases.
1402 if (!url.SchemeIs(chrome::kJavaScriptScheme))
1403 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1404
1405 return true;
1406 }
1407
1408 void TabsUpdateFunction::PopulateResult() {
1409 if (!has_callback())
1410 return;
1411
1412 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension()));
1413 }
1414
1415 void TabsUpdateFunction::OnExecuteCodeFinished(const std::string& error,
1416 int32 on_page_id,
1417 const GURL& url,
1418 const ListValue& script_result) {
1419 if (error.empty())
1420 PopulateResult();
1421 else
1422 error_ = error;
1423 SendResponse(error.empty());
1424 }
1425
1426 bool TabsMoveFunction::RunImpl() {
1427 Value* tab_value = NULL;
1428 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1429
1430 std::vector<int> tab_ids;
1431 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1432 tab_value, &tab_ids));
1433
1434 DictionaryValue* update_props = NULL;
1435 int new_index;
1436 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
1437 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(keys::kIndexKey,
1438 &new_index));
1439
1440 ListValue tab_values;
1441 for (size_t i = 0; i < tab_ids.size(); ++i) {
1442 Browser* source_browser = NULL;
1443 TabStripModel* source_tab_strip = NULL;
1444 WebContents* contents = NULL;
1445 int tab_index = -1;
1446 if (!GetTabById(tab_ids[i], profile(), include_incognito(),
1447 &source_browser, &source_tab_strip, &contents,
1448 &tab_index, &error_))
1449 return false;
1450
1451 // Don't let the extension move the tab if the user is dragging tabs.
1452 if (!source_browser->window()->IsTabStripEditable()) {
1453 error_ = keys::kTabStripNotEditableError;
1454 return false;
1455 }
1456
1457 // Insert the tabs one after another.
1458 new_index += i;
1459
1460 if (update_props->HasKey(keys::kWindowIdKey)) {
1461 Browser* target_browser = NULL;
1462 int window_id = extension_misc::kUnknownWindowId;
1463 EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
1464 keys::kWindowIdKey, &window_id));
1465
1466 if (!GetBrowserFromWindowID(this, window_id, &target_browser))
1467 return false;
1468
1469 if (!target_browser->window()->IsTabStripEditable()) {
1470 error_ = keys::kTabStripNotEditableError;
1471 return false;
1472 }
1473
1474 if (!target_browser->is_type_tabbed()) {
1475 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1476 return false;
1477 }
1478
1479 if (target_browser->profile() != source_browser->profile()) {
1480 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1481 return false;
1482 }
1483
1484 // If windowId is different from the current window, move between windows.
1485 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1486 ExtensionTabUtil::GetWindowId(source_browser)) {
1487 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1488 WebContents* web_contents =
1489 source_tab_strip->DetachWebContentsAt(tab_index);
1490 if (!web_contents) {
1491 error_ = ErrorUtils::FormatErrorMessage(
1492 keys::kTabNotFoundError, base::IntToString(tab_ids[i]));
1493 return false;
1494 }
1495
1496 // Clamp move location to the last position.
1497 // This is ">" because it can append to a new index position.
1498 // -1 means set the move location to the last position.
1499 if (new_index > target_tab_strip->count() || new_index < 0)
1500 new_index = target_tab_strip->count();
1501
1502 target_tab_strip->InsertWebContentsAt(
1503 new_index, web_contents, TabStripModel::ADD_NONE);
1504
1505 if (has_callback()) {
1506 tab_values.Append(ExtensionTabUtil::CreateTabValue(
1507 web_contents,
1508 target_tab_strip,
1509 new_index,
1510 GetExtension()));
1511 }
1512
1513 continue;
1514 }
1515 }
1516
1517 // Perform a simple within-window move.
1518 // Clamp move location to the last position.
1519 // This is ">=" because the move must be to an existing location.
1520 // -1 means set the move location to the last position.
1521 if (new_index >= source_tab_strip->count() || new_index < 0)
1522 new_index = source_tab_strip->count() - 1;
1523
1524 if (new_index != tab_index)
1525 source_tab_strip->MoveWebContentsAt(tab_index, new_index, false);
1526
1527 if (has_callback()) {
1528 tab_values.Append(ExtensionTabUtil::CreateTabValue(
1529 contents, source_tab_strip, new_index, GetExtension()));
1530 }
1531 }
1532
1533 if (!has_callback())
1534 return true;
1535
1536 // Only return the results as an array if there are multiple tabs.
1537 if (tab_ids.size() > 1) {
1538 SetResult(tab_values.DeepCopy());
1539 } else if (tab_ids.size() == 1) {
1540 Value* value = NULL;
1541 CHECK(tab_values.Get(0, &value));
1542 SetResult(value->DeepCopy());
1543 }
1544 return true;
1545 }
1546
1547 bool TabsReloadFunction::RunImpl() {
1548 bool bypass_cache = false;
1549 if (HasOptionalArgument(1)) {
1550 DictionaryValue* reload_props = NULL;
1551 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &reload_props));
1552
1553 if (reload_props->HasKey(keys::kBypassCache)) {
1554 EXTENSION_FUNCTION_VALIDATE(reload_props->GetBoolean(
1555 keys::kBypassCache,
1556 &bypass_cache));
1557 }
1558 }
1559
1560 content::WebContents* web_contents = NULL;
1561
1562 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1563 // in the current window.
1564 Value* tab_value = NULL;
1565 if (HasOptionalArgument(0))
1566 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1567
1568 if (tab_value == NULL || tab_value->IsType(Value::TYPE_NULL)) {
1569 Browser* browser = GetCurrentBrowser();
1570 if (!browser) {
1571 error_ = keys::kNoCurrentWindowError;
1572 return false;
1573 }
1574
1575 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1576 return false;
1577 } else {
1578 int tab_id = -1;
1579 EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&tab_id));
1580
1581 Browser* browser = NULL;
1582 if (!GetTabById(tab_id, profile(), include_incognito(),
1583 &browser, NULL, &web_contents, NULL, &error_))
1584 return false;
1585 }
1586
1587 if (web_contents->ShowingInterstitialPage()) {
1588 // This does as same as Browser::ReloadInternal.
1589 NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
1590 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1591 content::PAGE_TRANSITION_RELOAD, false);
1592 GetCurrentBrowser()->OpenURL(params);
1593 } else if (bypass_cache) {
1594 web_contents->GetController().ReloadIgnoringCache(true);
1595 } else {
1596 web_contents->GetController().Reload(true);
1597 }
1598
1599 return true;
1600 }
1601
1602 bool TabsRemoveFunction::RunImpl() {
1603 Value* tab_value = NULL;
1604 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
1605
1606 std::vector<int> tab_ids;
1607 EXTENSION_FUNCTION_VALIDATE(extensions::ReadOneOrMoreIntegers(
1608 tab_value, &tab_ids));
1609
1610 for (size_t i = 0; i < tab_ids.size(); ++i) {
1611 Browser* browser = NULL;
1612 WebContents* contents = NULL;
1613 if (!GetTabById(tab_ids[i], profile(), include_incognito(),
1614 &browser, NULL, &contents, NULL, &error_))
1615 return false;
1616
1617 // Don't let the extension remove a tab if the user is dragging tabs around.
1618 if (!browser->window()->IsTabStripEditable()) {
1619 error_ = keys::kTabStripNotEditableError;
1620 return false;
1621 }
1622
1623 // There's a chance that the tab is being dragged, or we're in some other
1624 // nested event loop. This code path ensures that the tab is safely closed
1625 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1626 // does not.
1627 contents->Close();
1628 }
1629 return true;
1630 }
1631
1632 bool TabsCaptureVisibleTabFunction::GetTabToCapture(
1633 WebContents** web_contents) {
1634 Browser* browser = NULL;
1635 // windowId defaults to "current" window.
1636 int window_id = extension_misc::kCurrentWindowId;
1637
1638 if (HasOptionalArgument(0))
1639 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
1640
1641 if (!GetBrowserFromWindowID(this, window_id, &browser))
1642 return false;
1643
1644 *web_contents = chrome::GetActiveWebContents(browser);
1645 if (*web_contents == NULL) {
1646 error_ = keys::kInternalVisibleTabCaptureError;
1647 return false;
1648 }
1649
1650 return true;
1651 };
1652
1653 bool TabsCaptureVisibleTabFunction::RunImpl() {
1654 PrefServiceBase* service = profile()->GetPrefs();
1655 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1656 error_ = keys::kScreenshotsDisabled;
1657 return false;
1658 }
1659
1660 WebContents* web_contents = NULL;
1661 if (!GetTabToCapture(&web_contents))
1662 return false;
1663
1664 image_format_ = FORMAT_JPEG; // Default format is JPEG.
1665 image_quality_ = kDefaultQuality; // Default quality setting.
1666
1667 if (HasOptionalArgument(1)) {
1668 DictionaryValue* options = NULL;
1669 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
1670
1671 if (options->HasKey(keys::kFormatKey)) {
1672 std::string format;
1673 EXTENSION_FUNCTION_VALIDATE(
1674 options->GetString(keys::kFormatKey, &format));
1675
1676 if (format == keys::kFormatValueJpeg) {
1677 image_format_ = FORMAT_JPEG;
1678 } else if (format == keys::kFormatValuePng) {
1679 image_format_ = FORMAT_PNG;
1680 } else {
1681 // Schema validation should make this unreachable.
1682 EXTENSION_FUNCTION_VALIDATE(0);
1683 }
1684 }
1685
1686 if (options->HasKey(keys::kQualityKey)) {
1687 EXTENSION_FUNCTION_VALIDATE(
1688 options->GetInteger(keys::kQualityKey, &image_quality_));
1689 }
1690 }
1691
1692 // captureVisibleTab() can return an image containing sensitive information
1693 // that the browser would otherwise protect. Ensure the extension has
1694 // permission to do this.
1695 if (!GetExtension()->CanCaptureVisiblePage(
1696 web_contents->GetURL(),
1697 SessionID::IdForTab(web_contents),
1698 &error_)) {
1699 return false;
1700 }
1701
1702 RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
1703 content::RenderWidgetHostView* view = render_view_host->GetView();
1704 if (!view) {
1705 error_ = keys::kInternalVisibleTabCaptureError;
1706 return false;
1707 }
1708 skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap;
1709 render_view_host->CopyFromBackingStore(
1710 gfx::Rect(),
1711 view->GetViewBounds().size(),
1712 base::Bind(&TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete,
1713 this,
1714 base::Owned(temp_bitmap)),
1715 temp_bitmap);
1716 return true;
1717 }
1718
1719 void TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete(
1720 skia::PlatformBitmap* bitmap,
1721 bool succeeded) {
1722 if (succeeded) {
1723 VLOG(1) << "captureVisibleTab() got image from backing store.";
1724 SendResultFromBitmap(bitmap->GetBitmap());
1725 return;
1726 }
1727
1728 WebContents* web_contents = NULL;
1729 if (!GetTabToCapture(&web_contents)) {
1730 error_ = keys::kInternalVisibleTabCaptureError;
1731 SendResponse(false);
1732 return;
1733 }
1734
1735 // Ask the renderer for a snapshot of the tab.
1736 registrar_.Add(this,
1737 chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN,
1738 content::Source<WebContents>(web_contents));
1739 AddRef(); // Balanced in TabsCaptureVisibleTabFunction::Observe().
1740 SnapshotTabHelper::FromWebContents(web_contents)->CaptureSnapshot();
1741 }
1742
1743 // If a backing store was not available in
1744 // TabsCaptureVisibleTabFunction::RunImpl, than the renderer was asked for a
1745 // snapshot. Listen for a notification that the snapshot is available.
1746 void TabsCaptureVisibleTabFunction::Observe(
1747 int type,
1748 const content::NotificationSource& source,
1749 const content::NotificationDetails& details) {
1750 DCHECK(type == chrome::NOTIFICATION_TAB_SNAPSHOT_TAKEN);
1751
1752 const SkBitmap *screen_capture =
1753 content::Details<const SkBitmap>(details).ptr();
1754 const bool error = screen_capture->empty();
1755
1756 if (error) {
1757 error_ = keys::kInternalVisibleTabCaptureError;
1758 SendResponse(false);
1759 } else {
1760 VLOG(1) << "captureVisibleTab() got image from renderer.";
1761 SendResultFromBitmap(*screen_capture);
1762 }
1763
1764 Release(); // Balanced in TabsCaptureVisibleTabFunction::RunImpl().
1765 }
1766
1767 // Turn a bitmap of the screen into an image, set that image as the result,
1768 // and call SendResponse().
1769 void TabsCaptureVisibleTabFunction::SendResultFromBitmap(
1770 const SkBitmap& screen_capture) {
1771 std::vector<unsigned char> data;
1772 SkAutoLockPixels screen_capture_lock(screen_capture);
1773 bool encoded = false;
1774 std::string mime_type;
1775 switch (image_format_) {
1776 case FORMAT_JPEG:
1777 encoded = gfx::JPEGCodec::Encode(
1778 reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)),
1779 gfx::JPEGCodec::FORMAT_SkBitmap,
1780 screen_capture.width(),
1781 screen_capture.height(),
1782 static_cast<int>(screen_capture.rowBytes()),
1783 image_quality_,
1784 &data);
1785 mime_type = keys::kMimeTypeJpeg;
1786 break;
1787 case FORMAT_PNG:
1788 encoded = gfx::PNGCodec::EncodeBGRASkBitmap(
1789 screen_capture,
1790 true, // Discard transparency.
1791 &data);
1792 mime_type = keys::kMimeTypePng;
1793 break;
1794 default:
1795 NOTREACHED() << "Invalid image format.";
1796 }
1797
1798 if (!encoded) {
1799 error_ = keys::kInternalVisibleTabCaptureError;
1800 SendResponse(false);
1801 return;
1802 }
1803
1804 std::string base64_result;
1805 base::StringPiece stream_as_string(
1806 reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
1807
1808 base::Base64Encode(stream_as_string, &base64_result);
1809 base64_result.insert(0, base::StringPrintf("data:%s;base64,",
1810 mime_type.c_str()));
1811 SetResult(new StringValue(base64_result));
1812 SendResponse(true);
1813 }
1814
1815 void TabsCaptureVisibleTabFunction::RegisterUserPrefs(
1816 PrefServiceSyncable* service) {
1817 service->RegisterBooleanPref(prefs::kDisableScreenshots, false,
1818 PrefServiceSyncable::UNSYNCABLE_PREF);
1819 }
1820
1821 bool TabsDetectLanguageFunction::RunImpl() {
1822 int tab_id = 0;
1823 Browser* browser = NULL;
1824 WebContents* contents = NULL;
1825
1826 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1827 // in the current window.
1828 if (HasOptionalArgument(0)) {
1829 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
1830 if (!GetTabById(tab_id, profile(), include_incognito(),
1831 &browser, NULL, &contents, NULL, &error_)) {
1832 return false;
1833 }
1834 if (!browser || !contents)
1835 return false;
1836 } else {
1837 browser = GetCurrentBrowser();
1838 if (!browser)
1839 return false;
1840 contents = browser->tab_strip_model()->GetActiveWebContents();
1841 if (!contents)
1842 return false;
1843 }
1844
1845 if (contents->GetController().NeedsReload()) {
1846 // If the tab hasn't been loaded, don't wait for the tab to load.
1847 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1848 return false;
1849 }
1850
1851 AddRef(); // Balanced in GotLanguage().
1852
1853 TranslateTabHelper* translate_tab_helper =
1854 TranslateTabHelper::FromWebContents(contents);
1855 if (!translate_tab_helper->language_state().original_language().empty()) {
1856 // Delay the callback invocation until after the current JS call has
1857 // returned.
1858 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
1859 &TabsDetectLanguageFunction::GotLanguage, this,
1860 translate_tab_helper->language_state().original_language()));
1861 return true;
1862 }
1863 // The tab contents does not know its language yet. Let's wait until it
1864 // receives it, or until the tab is closed/navigates to some other page.
1865 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1866 content::Source<WebContents>(contents));
1867 registrar_.Add(
1868 this, chrome::NOTIFICATION_TAB_CLOSING,
1869 content::Source<NavigationController>(&(contents->GetController())));
1870 registrar_.Add(
1871 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1872 content::Source<NavigationController>(&(contents->GetController())));
1873 return true;
1874 }
1875
1876 void TabsDetectLanguageFunction::Observe(
1877 int type,
1878 const content::NotificationSource& source,
1879 const content::NotificationDetails& details) {
1880 std::string language;
1881 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED)
1882 language = *content::Details<std::string>(details).ptr();
1883
1884 registrar_.RemoveAll();
1885
1886 // Call GotLanguage in all cases as we want to guarantee the callback is
1887 // called for every API call the extension made.
1888 GotLanguage(language);
1889 }
1890
1891 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1892 SetResult(Value::CreateStringValue(language.c_str()));
1893 SendResponse(true);
1894
1895 Release(); // Balanced in Run()
1896 }
1897
1898 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1899 : execute_tab_id_(-1) {
1900 }
1901
1902 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1903
1904 bool ExecuteCodeInTabFunction::HasPermission() {
1905 if (Init() &&
1906 extension_->HasAPIPermissionForTab(execute_tab_id_,
1907 extensions::APIPermission::kTab)) {
1908 return true;
1909 }
1910 return ExtensionFunction::HasPermission();
1911 }
1912
1913 bool ExecuteCodeInTabFunction::RunImpl() {
1914 EXTENSION_FUNCTION_VALIDATE(Init());
1915
1916 if (!details_->code.get() && !details_->file.get()) {
1917 error_ = keys::kNoCodeOrFileToExecuteError;
1918 return false;
1919 }
1920 if (details_->code.get() && details_->file.get()) {
1921 error_ = keys::kMoreThanOneValuesError;
1922 return false;
1923 }
1924
1925 content::WebContents* contents = NULL;
1926
1927 // If |tab_id| is specified, look for the tab. Otherwise default to selected
1928 // tab in the current window.
1929 CHECK_GE(execute_tab_id_, 0);
1930 if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(),
1931 include_incognito(),
1932 NULL, NULL, &contents, NULL)) {
1933 return false;
1934 }
1935
1936 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1937 // we check again in the renderer.
1938 CHECK(contents);
1939 if (!GetExtension()->CanExecuteScriptOnPage(contents->GetURL(),
1940 contents->GetURL(),
1941 execute_tab_id_,
1942 NULL,
1943 &error_)) {
1944 return false;
1945 }
1946
1947 if (details_->code.get())
1948 return Execute(*details_->code);
1949
1950 CHECK(details_->file.get());
1951 resource_ = GetExtension()->GetResource(*details_->file);
1952
1953 if (resource_.extension_root().empty() || resource_.relative_path().empty()) {
1954 error_ = keys::kNoCodeOrFileToExecuteError;
1955 return false;
1956 }
1957
1958 scoped_refptr<FileReader> file_reader(new FileReader(
1959 resource_, base::Bind(&ExecuteCodeInTabFunction::DidLoadFile, this)));
1960 file_reader->Start();
1961
1962 return true;
1963 }
1964
1965 void ExecuteCodeInTabFunction::OnExecuteCodeFinished(const std::string& error,
1966 int32 on_page_id,
1967 const GURL& on_url,
1968 const ListValue& result) {
1969 if (!error.empty())
1970 SetError(error);
1971
1972 SendResponse(error.empty());
1973 }
1974
1975 void TabsExecuteScriptFunction::OnExecuteCodeFinished(const std::string& error,
1976 int32 on_page_id,
1977 const GURL& on_url,
1978 const ListValue& result) {
1979 if (error.empty())
1980 SetResult(result.DeepCopy());
1981 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
1982 result);
1983 }
1984
1985 bool ExecuteCodeInTabFunction::Init() {
1986 if (details_.get())
1987 return true;
1988
1989 // |tab_id| is optional so it's ok if it's not there.
1990 int tab_id = -1;
1991 args_->GetInteger(0, &tab_id);
1992
1993 // |details| are not optional.
1994 DictionaryValue* details_value = NULL;
1995 if (!args_->GetDictionary(1, &details_value))
1996 return false;
1997 scoped_ptr<InjectDetails> details(new InjectDetails());
1998 if (!InjectDetails::Populate(*details_value, details.get()))
1999 return false;
2000
2001 // If the tab ID is -1 then it needs to be converted to the currently active
2002 // tab's ID.
2003 if (tab_id == -1) {
2004 Browser* browser = GetCurrentBrowser();
2005 if (!browser)
2006 return false;
2007 content::WebContents* web_contents = NULL;
2008 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
2009 return false;
2010 }
2011
2012 execute_tab_id_ = tab_id;
2013 details_ = details.Pass();
2014 return true;
2015 }
2016
2017 void ExecuteCodeInTabFunction::DidLoadFile(bool success,
2018 const std::string& data) {
2019 std::string function_name = name();
2020 const extensions::Extension* extension = GetExtension();
2021
2022 // Check if the file is CSS and needs localization.
2023 if (success &&
2024 function_name == TabsInsertCSSFunction::function_name() &&
2025 extension != NULL &&
2026 data.find(
2027 extensions::MessageBundle::kMessageBegin) != std::string::npos) {
2028 BrowserThread::PostTask(
2029 BrowserThread::FILE, FROM_HERE,
2030 base::Bind(&ExecuteCodeInTabFunction::LocalizeCSS, this,
2031 data,
2032 extension->id(),
2033 extension->path(),
2034 extension->default_locale()));
2035 } else {
2036 DidLoadAndLocalizeFile(success, data);
2037 }
2038 }
2039
2040 void ExecuteCodeInTabFunction::LocalizeCSS(
2041 const std::string& data,
2042 const std::string& extension_id,
2043 const FilePath& extension_path,
2044 const std::string& extension_default_locale) {
2045 scoped_ptr<SubstitutionMap> localization_messages(
2046 extension_file_util::LoadMessageBundleSubstitutionMap(
2047 extension_path, extension_id, extension_default_locale));
2048
2049 // We need to do message replacement on the data, so it has to be mutable.
2050 std::string css_data = data;
2051 std::string error;
2052 extensions::MessageBundle::ReplaceMessagesWithExternalDictionary(
2053 *localization_messages, &css_data, &error);
2054
2055 // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter
2056 // is always true, because if loading had failed, we wouldn't have had
2057 // anything to localize.
2058 BrowserThread::PostTask(
2059 BrowserThread::UI, FROM_HERE,
2060 base::Bind(&ExecuteCodeInTabFunction::DidLoadAndLocalizeFile, this,
2061 true, css_data));
2062 }
2063
2064 void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success,
2065 const std::string& data) {
2066 if (success) {
2067 if (!Execute(data))
2068 SendResponse(false);
2069 } else {
2070 #if defined(OS_POSIX)
2071 // TODO(viettrungluu): bug: there's no particular reason the path should be
2072 // UTF-8, in which case this may fail.
2073 error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError,
2074 resource_.relative_path().value());
2075 #elif defined(OS_WIN)
2076 error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError,
2077 WideToUTF8(resource_.relative_path().value()));
2078 #endif // OS_WIN
2079 SendResponse(false);
2080 }
2081 }
2082
2083 bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) {
2084 content::WebContents* contents = NULL;
2085 Browser* browser = NULL;
2086
2087 bool success = ExtensionTabUtil::GetTabById(
2088 execute_tab_id_, profile(), include_incognito(), &browser, NULL,
2089 &contents, NULL) && contents && browser;
2090
2091 if (!success)
2092 return false;
2093
2094 const extensions::Extension* extension = GetExtension();
2095 if (!extension)
2096 return false;
2097
2098 ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT;
2099 std::string function_name = name();
2100 if (function_name == TabsInsertCSSFunction::function_name()) {
2101 script_type = ScriptExecutor::CSS;
2102 } else if (function_name != TabsExecuteScriptFunction::function_name()) {
2103 NOTREACHED();
2104 }
2105
2106 ScriptExecutor::FrameScope frame_scope =
2107 details_->all_frames.get() && *details_->all_frames ?
2108 ScriptExecutor::ALL_FRAMES :
2109 ScriptExecutor::TOP_FRAME;
2110
2111 UserScript::RunLocation run_at = UserScript::UNDEFINED;
2112 switch (details_->run_at) {
2113 case InjectDetails::RUN_AT_NONE:
2114 case InjectDetails::RUN_AT_DOCUMENT_IDLE:
2115 run_at = UserScript::DOCUMENT_IDLE;
2116 break;
2117 case InjectDetails::RUN_AT_DOCUMENT_START:
2118 run_at = UserScript::DOCUMENT_START;
2119 break;
2120 case InjectDetails::RUN_AT_DOCUMENT_END:
2121 run_at = UserScript::DOCUMENT_END;
2122 break;
2123 }
2124 CHECK_NE(UserScript::UNDEFINED, run_at);
2125
2126 extensions::TabHelper::FromWebContents(contents)->
2127 script_executor()->ExecuteScript(
2128 extension->id(),
2129 script_type,
2130 code_string,
2131 frame_scope,
2132 run_at,
2133 ScriptExecutor::ISOLATED_WORLD,
2134 base::Bind(&ExecuteCodeInTabFunction::OnExecuteCodeFinished, this));
2135 return true;
2136 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/api/tabs/tabs_api.h ('k') | chrome/browser/extensions/api/tabs/tabs_interactive_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698