Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / tabs / tabs_api.cc
blob6cb9c4fe6c5d53c5720d659ab0e54717db24e821
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.
5 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/pattern.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "chrome/browser/apps/scoped_keep_alive.h"
27 #include "chrome/browser/chrome_notification_types.h"
28 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
29 #include "chrome/browser/extensions/api/tabs/windows_util.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_tab_util.h"
32 #include "chrome/browser/extensions/tab_helper.h"
33 #include "chrome/browser/extensions/window_controller.h"
34 #include "chrome/browser/extensions/window_controller_list.h"
35 #include "chrome/browser/prefs/incognito_mode_prefs.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/sessions/session_tab_helper.h"
38 #include "chrome/browser/translate/chrome_translate_client.h"
39 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
40 #include "chrome/browser/ui/browser.h"
41 #include "chrome/browser/ui/browser_commands.h"
42 #include "chrome/browser/ui/browser_finder.h"
43 #include "chrome/browser/ui/browser_iterator.h"
44 #include "chrome/browser/ui/browser_navigator.h"
45 #include "chrome/browser/ui/browser_tabstrip.h"
46 #include "chrome/browser/ui/browser_window.h"
47 #include "chrome/browser/ui/host_desktop.h"
48 #include "chrome/browser/ui/panels/panel_manager.h"
49 #include "chrome/browser/ui/tabs/tab_strip_model.h"
50 #include "chrome/browser/ui/tabs/tab_utils.h"
51 #include "chrome/browser/ui/window_sizer/window_sizer.h"
52 #include "chrome/browser/web_applications/web_app.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_constants.h"
57 #include "chrome/common/pref_names.h"
58 #include "chrome/common/url_constants.h"
59 #include "components/pref_registry/pref_registry_syncable.h"
60 #include "components/translate/core/browser/language_state.h"
61 #include "components/translate/core/common/language_detection_details.h"
62 #include "components/ui/zoom/zoom_controller.h"
63 #include "content/public/browser/navigation_controller.h"
64 #include "content/public/browser/navigation_entry.h"
65 #include "content/public/browser/notification_details.h"
66 #include "content/public/browser/notification_source.h"
67 #include "content/public/browser/render_process_host.h"
68 #include "content/public/browser/render_widget_host_view.h"
69 #include "content/public/browser/web_contents.h"
70 #include "content/public/common/url_constants.h"
71 #include "extensions/browser/app_window/app_window.h"
72 #include "extensions/browser/extension_function_dispatcher.h"
73 #include "extensions/browser/extension_function_util.h"
74 #include "extensions/browser/extension_host.h"
75 #include "extensions/browser/extension_zoom_request_client.h"
76 #include "extensions/browser/file_reader.h"
77 #include "extensions/browser/script_executor.h"
78 #include "extensions/common/api/extension_types.h"
79 #include "extensions/common/constants.h"
80 #include "extensions/common/error_utils.h"
81 #include "extensions/common/extension.h"
82 #include "extensions/common/host_id.h"
83 #include "extensions/common/manifest_constants.h"
84 #include "extensions/common/manifest_handlers/default_locale_handler.h"
85 #include "extensions/common/message_bundle.h"
86 #include "extensions/common/permissions/permissions_data.h"
87 #include "extensions/common/user_script.h"
88 #include "net/base/escape.h"
89 #include "skia/ext/image_operations.h"
90 #include "skia/ext/platform_canvas.h"
91 #include "third_party/skia/include/core/SkBitmap.h"
92 #include "ui/base/models/list_selection_model.h"
93 #include "ui/base/ui_base_types.h"
95 #if defined(USE_ASH)
96 #include "ash/ash_switches.h"
97 #include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
98 #include "extensions/browser/app_window/app_window_registry.h"
99 #endif
101 using content::BrowserThread;
102 using content::NavigationController;
103 using content::NavigationEntry;
104 using content::OpenURLParams;
105 using content::Referrer;
106 using content::WebContents;
107 using ui_zoom::ZoomController;
109 namespace extensions {
111 namespace windows = api::windows;
112 namespace keys = tabs_constants;
113 namespace tabs = api::tabs;
115 using api::extension_types::InjectDetails;
117 namespace {
119 template <typename T>
120 class ApiParameterExtractor {
121 public:
122 explicit ApiParameterExtractor(T* params) : params_(params) {}
123 ~ApiParameterExtractor() {}
125 bool populate_tabs() {
126 if (params_->get_info.get() && params_->get_info->populate.get())
127 return *params_->get_info->populate;
128 return false;
131 WindowController::TypeFilter type_filters() {
132 if (params_->get_info.get() && params_->get_info->window_types.get())
133 return WindowController::GetFilterFromWindowTypes(
134 *params_->get_info->window_types.get());
135 return WindowController::kNoWindowFilter;
138 private:
139 T* params_;
142 bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction* function,
143 int window_id,
144 Browser** browser) {
145 std::string error;
146 Browser* result;
147 result =
148 ExtensionTabUtil::GetBrowserFromWindowID(function, window_id, &error);
149 if (!result) {
150 function->SetError(error);
151 return false;
154 *browser = result;
155 return true;
158 bool GetBrowserFromWindowID(ChromeExtensionFunctionDetails* details,
159 int window_id,
160 Browser** browser) {
161 std::string error;
162 Browser* result;
163 result =
164 ExtensionTabUtil::GetBrowserFromWindowID(*details, window_id, &error);
165 if (!result) {
166 details->function()->SetError(error);
167 return false;
170 *browser = result;
171 return true;
174 // |error_message| can optionally be passed in and will be set with an
175 // appropriate message if the tab cannot be found by id.
176 bool GetTabById(int tab_id,
177 Profile* profile,
178 bool include_incognito,
179 Browser** browser,
180 TabStripModel** tab_strip,
181 content::WebContents** contents,
182 int* tab_index,
183 std::string* error_message) {
184 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
185 browser, tab_strip, contents, tab_index)) {
186 return true;
189 if (error_message) {
190 *error_message = ErrorUtils::FormatErrorMessage(
191 keys::kTabNotFoundError, base::IntToString(tab_id));
194 return false;
197 // Returns true if either |boolean| is a null pointer, or if |*boolean| and
198 // |value| are equal. This function is used to check if a tab's parameters match
199 // those of the browser.
200 bool MatchesBool(bool* boolean, bool value) {
201 return !boolean || *boolean == value;
204 template <typename T>
205 void AssignOptionalValue(const scoped_ptr<T>& source,
206 scoped_ptr<T>& destination) {
207 if (source.get()) {
208 destination.reset(new T(*source.get()));
212 ui::WindowShowState ConvertToWindowShowState(windows::WindowState state) {
213 switch (state) {
214 case windows::WINDOW_STATE_NORMAL:
215 case windows::WINDOW_STATE_DOCKED:
216 return ui::SHOW_STATE_NORMAL;
217 case windows::WINDOW_STATE_MINIMIZED:
218 return ui::SHOW_STATE_MINIMIZED;
219 case windows::WINDOW_STATE_MAXIMIZED:
220 return ui::SHOW_STATE_MAXIMIZED;
221 case windows::WINDOW_STATE_FULLSCREEN:
222 return ui::SHOW_STATE_FULLSCREEN;
223 case windows::WINDOW_STATE_NONE:
224 return ui::SHOW_STATE_DEFAULT;
226 NOTREACHED();
227 return ui::SHOW_STATE_DEFAULT;
230 bool IsValidStateForWindowsCreateFunction(
231 const windows::Create::Params::CreateData* create_data) {
232 if (!create_data)
233 return true;
235 bool has_bound = create_data->left || create_data->top ||
236 create_data->width || create_data->height;
237 bool is_panel =
238 create_data->type == windows::CreateType::CREATE_TYPE_PANEL ||
239 create_data->type == windows::CreateType::CREATE_TYPE_DETACHED_PANEL;
241 switch (create_data->state) {
242 case windows::WINDOW_STATE_MINIMIZED:
243 // If minimised, default focused state should be unfocused.
244 return !(create_data->focused && *create_data->focused) && !has_bound &&
245 !is_panel;
246 case windows::WINDOW_STATE_MAXIMIZED:
247 case windows::WINDOW_STATE_FULLSCREEN:
248 // If maximised/fullscreen, default focused state should be focused.
249 return !(create_data->focused && !*create_data->focused) && !has_bound &&
250 !is_panel;
251 case windows::WINDOW_STATE_NORMAL:
252 case windows::WINDOW_STATE_DOCKED:
253 case windows::WINDOW_STATE_NONE:
254 return true;
256 NOTREACHED();
257 return true;
260 } // namespace
262 void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,
263 api::tabs::ZoomSettings* zoom_settings) {
264 DCHECK(zoom_settings);
265 switch (zoom_mode) {
266 case ZoomController::ZOOM_MODE_DEFAULT:
267 zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
268 zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN;
269 break;
270 case ZoomController::ZOOM_MODE_ISOLATED:
271 zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
272 zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
273 break;
274 case ZoomController::ZOOM_MODE_MANUAL:
275 zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL;
276 zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
277 break;
278 case ZoomController::ZOOM_MODE_DISABLED:
279 zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED;
280 zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
281 break;
285 // Windows ---------------------------------------------------------------------
287 bool WindowsGetFunction::RunSync() {
288 scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
289 EXTENSION_FUNCTION_VALIDATE(params.get());
291 ApiParameterExtractor<windows::Get::Params> extractor(params.get());
292 WindowController* controller;
293 if (!windows_util::GetWindowFromWindowID(
294 this, params->window_id, extractor.type_filters(), &controller)) {
295 return false;
298 if (extractor.populate_tabs())
299 SetResult(controller->CreateWindowValueWithTabs(extension()));
300 else
301 SetResult(controller->CreateWindowValue());
302 return true;
305 bool WindowsGetCurrentFunction::RunSync() {
306 scoped_ptr<windows::GetCurrent::Params> params(
307 windows::GetCurrent::Params::Create(*args_));
308 EXTENSION_FUNCTION_VALIDATE(params.get());
310 ApiParameterExtractor<windows::GetCurrent::Params> extractor(params.get());
311 WindowController* controller;
312 if (!windows_util::GetWindowFromWindowID(
313 this, extension_misc::kCurrentWindowId, extractor.type_filters(),
314 &controller)) {
315 return false;
317 if (extractor.populate_tabs())
318 SetResult(controller->CreateWindowValueWithTabs(extension()));
319 else
320 SetResult(controller->CreateWindowValue());
321 return true;
324 bool WindowsGetLastFocusedFunction::RunSync() {
325 scoped_ptr<windows::GetLastFocused::Params> params(
326 windows::GetLastFocused::Params::Create(*args_));
327 EXTENSION_FUNCTION_VALIDATE(params.get());
329 ApiParameterExtractor<windows::GetLastFocused::Params> extractor(
330 params.get());
331 // The WindowControllerList should contain a list of application,
332 // browser and devtools windows.
333 WindowController* controller = nullptr;
334 for (auto iter : WindowControllerList::GetInstance()->windows()) {
335 if (windows_util::CanOperateOnWindow(this, iter,
336 extractor.type_filters())) {
337 controller = iter;
338 if (controller->window()->IsActive())
339 break; // Use focused window.
342 if (!controller) {
343 error_ = keys::kNoLastFocusedWindowError;
344 return false;
346 if (extractor.populate_tabs())
347 SetResult(controller->CreateWindowValueWithTabs(extension()));
348 else
349 SetResult(controller->CreateWindowValue());
350 return true;
353 bool WindowsGetAllFunction::RunSync() {
354 scoped_ptr<windows::GetAll::Params> params(
355 windows::GetAll::Params::Create(*args_));
356 EXTENSION_FUNCTION_VALIDATE(params.get());
358 ApiParameterExtractor<windows::GetAll::Params> extractor(params.get());
359 base::ListValue* window_list = new base::ListValue();
360 const WindowControllerList::ControllerList& windows =
361 WindowControllerList::GetInstance()->windows();
362 for (WindowControllerList::ControllerList::const_iterator iter =
363 windows.begin();
364 iter != windows.end(); ++iter) {
365 if (!windows_util::CanOperateOnWindow(this, *iter,
366 extractor.type_filters()))
367 continue;
368 if (extractor.populate_tabs())
369 window_list->Append((*iter)->CreateWindowValueWithTabs(extension()));
370 else
371 window_list->Append((*iter)->CreateWindowValue());
373 SetResult(window_list);
374 return true;
377 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
378 const windows::Create::Params::CreateData* create_data,
379 std::vector<GURL>* urls, bool* is_error) {
380 *is_error = false;
381 const IncognitoModePrefs::Availability incognito_availability =
382 IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
383 bool incognito = false;
384 if (create_data && create_data->incognito) {
385 incognito = *create_data->incognito;
386 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
387 error_ = keys::kIncognitoModeIsDisabled;
388 *is_error = true;
389 return false;
391 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
392 error_ = keys::kIncognitoModeIsForced;
393 *is_error = true;
394 return false;
396 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
397 // If incognito argument is not specified explicitly, we default to
398 // incognito when forced so by policy.
399 incognito = true;
402 // Remove all URLs that are not allowed in an incognito session. Note that a
403 // ChromeOS guest session is not considered incognito in this case.
404 if (incognito && !GetProfile()->IsGuestSession()) {
405 std::string first_url_erased;
406 for (size_t i = 0; i < urls->size();) {
407 if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
408 i++;
409 } else {
410 if (first_url_erased.empty())
411 first_url_erased = (*urls)[i].spec();
412 urls->erase(urls->begin() + i);
415 if (urls->empty() && !first_url_erased.empty()) {
416 error_ = ErrorUtils::FormatErrorMessage(
417 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
418 *is_error = true;
419 return false;
422 return incognito;
425 bool WindowsCreateFunction::RunSync() {
426 scoped_ptr<windows::Create::Params> params(
427 windows::Create::Params::Create(*args_));
428 EXTENSION_FUNCTION_VALIDATE(params);
429 std::vector<GURL> urls;
430 TabStripModel* source_tab_strip = NULL;
431 int tab_index = -1;
433 windows::Create::Params::CreateData* create_data = params->create_data.get();
435 // Look for optional url.
436 if (create_data && create_data->url) {
437 std::vector<std::string> url_strings;
438 // First, get all the URLs the client wants to open.
439 if (create_data->url->as_string)
440 url_strings.push_back(*create_data->url->as_string);
441 else if (create_data->url->as_strings)
442 url_strings.swap(*create_data->url->as_strings);
444 // Second, resolve, validate and convert them to GURLs.
445 for (std::vector<std::string>::iterator i = url_strings.begin();
446 i != url_strings.end(); ++i) {
447 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(*i, extension());
448 if (!url.is_valid()) {
449 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
450 return false;
452 // Don't let the extension crash the browser or renderers.
453 if (ExtensionTabUtil::IsKillURL(url)) {
454 error_ = keys::kNoCrashBrowserError;
455 return false;
457 urls.push_back(url);
461 // Look for optional tab id.
462 if (create_data && create_data->tab_id) {
463 // Find the tab. |source_tab_strip| and |tab_index| will later be used to
464 // move the tab into the created window.
465 if (!GetTabById(*create_data->tab_id,
466 GetProfile(),
467 include_incognito(),
468 NULL,
469 &source_tab_strip,
470 NULL,
471 &tab_index,
472 &error_))
473 return false;
476 if (!IsValidStateForWindowsCreateFunction(create_data)) {
477 error_ = keys::kInvalidWindowStateError;
478 return false;
481 Profile* window_profile = GetProfile();
482 Browser::Type window_type = Browser::TYPE_TABBED;
483 bool create_panel = false;
485 // panel_create_mode only applies if create_panel = true
486 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
488 gfx::Rect window_bounds;
489 bool focused = true;
490 bool saw_focus_key = false;
491 std::string extension_id;
493 // Decide whether we are opening a normal window or an incognito window.
494 bool is_error = true;
495 bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
496 &is_error);
497 if (is_error) {
498 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
499 return false;
501 if (open_incognito_window) {
502 window_profile = window_profile->GetOffTheRecordProfile();
505 if (create_data) {
506 // Figure out window type before figuring out bounds so that default
507 // bounds can be set according to the window type.
508 switch (create_data->type) {
509 case windows::CREATE_TYPE_POPUP:
510 window_type = Browser::TYPE_POPUP;
511 extension_id = extension()->id();
512 break;
513 case windows::CREATE_TYPE_PANEL:
514 case windows::CREATE_TYPE_DETACHED_PANEL: {
515 extension_id = extension()->id();
516 bool use_panels = PanelManager::ShouldUsePanels(extension_id);
517 if (use_panels) {
518 create_panel = true;
519 // Non-ash supports both docked and detached panel types.
520 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
521 create_data->type == windows::CREATE_TYPE_DETACHED_PANEL) {
522 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
524 } else {
525 window_type = Browser::TYPE_POPUP;
527 break;
529 case windows::CREATE_TYPE_NONE:
530 case windows::CREATE_TYPE_NORMAL:
531 break;
532 default:
533 error_ = keys::kInvalidWindowTypeError;
534 return false;
537 // Initialize default window bounds according to window type.
538 if (window_type == Browser::TYPE_TABBED ||
539 window_type == Browser::TYPE_POPUP ||
540 create_panel) {
541 // Try to position the new browser relative to its originating
542 // browser window. The call offsets the bounds by kWindowTilePixels
543 // (defined in WindowSizer to be 10).
545 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
546 // GetBrowserWindowBounds will default to saved "default" values for
547 // the app.
548 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
549 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
550 gfx::Rect(),
551 GetCurrentBrowser(),
552 &window_bounds,
553 &show_state);
556 if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
557 window_bounds.set_origin(
558 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
561 // Any part of the bounds can optionally be set by the caller.
562 if (create_data->left)
563 window_bounds.set_x(*create_data->left);
565 if (create_data->top)
566 window_bounds.set_y(*create_data->top);
568 if (create_data->width)
569 window_bounds.set_width(*create_data->width);
571 if (create_data->height)
572 window_bounds.set_height(*create_data->height);
574 if (create_data->focused) {
575 focused = *create_data->focused;
576 saw_focus_key = true;
580 if (create_panel) {
581 if (urls.empty())
582 urls.push_back(GURL(chrome::kChromeUINewTabURL));
584 #if defined(USE_ASH)
585 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
586 AppWindow::CreateParams create_params;
587 create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL;
588 create_params.window_key = extension_id;
589 create_params.window_spec.bounds = window_bounds;
590 create_params.focused = saw_focus_key && focused;
591 AppWindow* app_window = new AppWindow(
592 window_profile,
593 new ChromeAppDelegate(make_scoped_ptr(new ScopedKeepAlive)),
594 extension());
595 AshPanelContents* ash_panel_contents = new AshPanelContents(app_window);
596 app_window->Init(urls[0], ash_panel_contents, create_params);
597 WindowController* window_controller =
598 WindowControllerList::GetInstance()->FindWindowById(
599 app_window->session_id().id());
600 if (!window_controller)
601 return false;
602 SetResult(window_controller->CreateWindowValueWithTabs(extension()));
603 return true;
605 #endif
606 std::string title =
607 web_app::GenerateApplicationNameFromExtensionId(extension_id);
608 // Note: Panels ignore all but the first url provided.
609 Panel* panel = PanelManager::GetInstance()->CreatePanel(
610 title, window_profile, urls[0], window_bounds, panel_create_mode);
612 // Unlike other window types, Panels do not take focus by default.
613 if (!saw_focus_key || !focused)
614 panel->ShowInactive();
615 else
616 panel->Show();
618 SetResult(panel->extension_window_controller()->CreateWindowValueWithTabs(
619 extension()));
620 return true;
623 // Create a new BrowserWindow.
624 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
625 if (create_panel)
626 window_type = Browser::TYPE_POPUP;
627 Browser::CreateParams create_params(window_type, window_profile,
628 host_desktop_type);
629 if (extension_id.empty()) {
630 create_params.initial_bounds = window_bounds;
631 } else {
632 create_params = Browser::CreateParams::CreateForApp(
633 web_app::GenerateApplicationNameFromExtensionId(extension_id),
634 false /* trusted_source */,
635 window_bounds,
636 window_profile,
637 host_desktop_type);
639 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
640 if (create_data && create_data->state) {
641 create_params.initial_show_state =
642 ConvertToWindowShowState(create_data->state);
644 create_params.host_desktop_type = chrome::GetActiveDesktop();
646 Browser* new_window = new Browser(create_params);
648 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
649 WebContents* tab = chrome::AddSelectedTabWithURL(
650 new_window, *i, ui::PAGE_TRANSITION_LINK);
651 if (create_panel) {
652 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
656 WebContents* contents = NULL;
657 // Move the tab into the created window only if it's an empty popup or it's
658 // a tabbed window.
659 if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
660 window_type == Browser::TYPE_TABBED) {
661 if (source_tab_strip)
662 contents = source_tab_strip->DetachWebContentsAt(tab_index);
663 if (contents) {
664 TabStripModel* target_tab_strip = new_window->tab_strip_model();
665 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
666 TabStripModel::ADD_NONE);
669 // Create a new tab if the created window is still empty. Don't create a new
670 // tab when it is intended to create an empty popup.
671 if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
672 chrome::NewTab(new_window);
674 chrome::SelectNumberedTab(new_window, 0);
676 // Unlike other window types, Panels do not take focus by default.
677 if (!saw_focus_key && create_panel)
678 focused = false;
680 if (focused)
681 new_window->window()->Show();
682 else
683 new_window->window()->ShowInactive();
685 WindowController* controller = new_window->extension_window_controller();
687 #if defined(OS_CHROMEOS)
688 // For ChromeOS, manually Minimize(). Because minimzied window is not
689 // considered to create new window. See http://crbug.com/473228.
690 if (create_params.initial_show_state == ui::SHOW_STATE_MINIMIZED)
691 new_window->window()->Minimize();
692 #endif
694 if (new_window->profile()->IsOffTheRecord() &&
695 !GetProfile()->IsOffTheRecord() && !include_incognito()) {
696 // Don't expose incognito windows if extension itself works in non-incognito
697 // profile and CanCrossIncognito isn't allowed.
698 SetResult(base::Value::CreateNullValue());
699 } else {
700 SetResult(controller->CreateWindowValueWithTabs(extension()));
703 return true;
706 bool WindowsUpdateFunction::RunSync() {
707 scoped_ptr<windows::Update::Params> params(
708 windows::Update::Params::Create(*args_));
709 EXTENSION_FUNCTION_VALIDATE(params);
711 WindowController* controller;
712 if (!windows_util::GetWindowFromWindowID(
713 this, params->window_id, WindowController::GetAllWindowFilter(),
714 &controller)) {
715 return false;
718 ui::WindowShowState show_state =
719 ConvertToWindowShowState(params->update_info.state);
721 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
722 show_state != ui::SHOW_STATE_DEFAULT)
723 controller->SetFullscreenMode(false, extension()->url());
725 switch (show_state) {
726 case ui::SHOW_STATE_MINIMIZED:
727 controller->window()->Minimize();
728 break;
729 case ui::SHOW_STATE_MAXIMIZED:
730 controller->window()->Maximize();
731 break;
732 case ui::SHOW_STATE_FULLSCREEN:
733 if (controller->window()->IsMinimized() ||
734 controller->window()->IsMaximized())
735 controller->window()->Restore();
736 controller->SetFullscreenMode(true, extension()->url());
737 break;
738 case ui::SHOW_STATE_NORMAL:
739 controller->window()->Restore();
740 break;
741 default:
742 break;
745 gfx::Rect bounds;
746 if (controller->window()->IsMinimized())
747 bounds = controller->window()->GetRestoredBounds();
748 else
749 bounds = controller->window()->GetBounds();
750 bool set_bounds = false;
752 // Any part of the bounds can optionally be set by the caller.
753 if (params->update_info.left) {
754 bounds.set_x(*params->update_info.left);
755 set_bounds = true;
758 if (params->update_info.top) {
759 bounds.set_y(*params->update_info.top);
760 set_bounds = true;
763 if (params->update_info.width) {
764 bounds.set_width(*params->update_info.width);
765 set_bounds = true;
768 if (params->update_info.height) {
769 bounds.set_height(*params->update_info.height);
770 set_bounds = true;
773 if (set_bounds) {
774 if (show_state == ui::SHOW_STATE_MINIMIZED ||
775 show_state == ui::SHOW_STATE_MAXIMIZED ||
776 show_state == ui::SHOW_STATE_FULLSCREEN) {
777 error_ = keys::kInvalidWindowStateError;
778 return false;
780 // TODO(varkha): Updating bounds during a drag can cause problems and a more
781 // general solution is needed. See http://crbug.com/251813 .
782 controller->window()->SetBounds(bounds);
785 if (params->update_info.focused) {
786 if (*params->update_info.focused) {
787 if (show_state == ui::SHOW_STATE_MINIMIZED) {
788 error_ = keys::kInvalidWindowStateError;
789 return false;
791 controller->window()->Activate();
792 } else {
793 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
794 show_state == ui::SHOW_STATE_FULLSCREEN) {
795 error_ = keys::kInvalidWindowStateError;
796 return false;
798 controller->window()->Deactivate();
802 if (params->update_info.draw_attention)
803 controller->window()->FlashFrame(*params->update_info.draw_attention);
805 SetResult(controller->CreateWindowValue());
807 return true;
810 bool WindowsRemoveFunction::RunSync() {
811 scoped_ptr<windows::Remove::Params> params(
812 windows::Remove::Params::Create(*args_));
813 EXTENSION_FUNCTION_VALIDATE(params);
815 WindowController* controller;
816 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
817 WindowController::kNoWindowFilter,
818 &controller)) {
819 return false;
822 WindowController::Reason reason;
823 if (!controller->CanClose(&reason)) {
824 if (reason == WindowController::REASON_NOT_EDITABLE)
825 error_ = keys::kTabStripNotEditableError;
826 return false;
828 controller->window()->Close();
829 return true;
832 // Tabs ------------------------------------------------------------------------
834 bool TabsGetSelectedFunction::RunSync() {
835 // windowId defaults to "current" window.
836 int window_id = extension_misc::kCurrentWindowId;
838 scoped_ptr<tabs::GetSelected::Params> params(
839 tabs::GetSelected::Params::Create(*args_));
840 EXTENSION_FUNCTION_VALIDATE(params.get());
841 if (params->window_id.get())
842 window_id = *params->window_id;
844 Browser* browser = NULL;
845 if (!GetBrowserFromWindowID(this, window_id, &browser))
846 return false;
848 TabStripModel* tab_strip = browser->tab_strip_model();
849 WebContents* contents = tab_strip->GetActiveWebContents();
850 if (!contents) {
851 error_ = keys::kNoSelectedTabError;
852 return false;
854 SetResult(ExtensionTabUtil::CreateTabValue(
855 contents, tab_strip, tab_strip->active_index(), extension()));
856 return true;
859 bool TabsGetAllInWindowFunction::RunSync() {
860 scoped_ptr<tabs::GetAllInWindow::Params> params(
861 tabs::GetAllInWindow::Params::Create(*args_));
862 EXTENSION_FUNCTION_VALIDATE(params.get());
863 // windowId defaults to "current" window.
864 int window_id = extension_misc::kCurrentWindowId;
865 if (params->window_id.get())
866 window_id = *params->window_id;
868 Browser* browser = NULL;
869 if (!GetBrowserFromWindowID(this, window_id, &browser))
870 return false;
872 SetResult(ExtensionTabUtil::CreateTabList(browser, extension()));
874 return true;
877 bool TabsQueryFunction::RunSync() {
878 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
879 EXTENSION_FUNCTION_VALIDATE(params.get());
881 bool loading_status_set = params->query_info.status != tabs::TAB_STATUS_NONE;
882 bool loading = params->query_info.status == tabs::TAB_STATUS_LOADING;
884 URLPatternSet url_patterns;
885 if (params->query_info.url.get()) {
886 std::vector<std::string> url_pattern_strings;
887 if (params->query_info.url->as_string)
888 url_pattern_strings.push_back(*params->query_info.url->as_string);
889 else if (params->query_info.url->as_strings)
890 url_pattern_strings.swap(*params->query_info.url->as_strings);
891 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
892 // not grant access to the content of the tabs, only to seeing their URLs
893 // and meta data.
894 if (!url_patterns.Populate(url_pattern_strings, URLPattern::SCHEME_ALL,
895 true, &error_)) {
896 return false;
900 std::string title;
901 if (params->query_info.title.get())
902 title = *params->query_info.title;
904 int window_id = extension_misc::kUnknownWindowId;
905 if (params->query_info.window_id.get())
906 window_id = *params->query_info.window_id;
908 int index = -1;
909 if (params->query_info.index.get())
910 index = *params->query_info.index;
912 std::string window_type;
913 if (params->query_info.window_type != tabs::WINDOW_TYPE_NONE)
914 window_type = tabs::ToString(params->query_info.window_type);
916 base::ListValue* result = new base::ListValue();
917 Browser* last_active_browser = chrome::FindAnyBrowser(
918 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
919 Browser* current_browser = GetCurrentBrowser();
920 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
921 Browser* browser = *it;
922 if (!GetProfile()->IsSameProfile(browser->profile()))
923 continue;
925 if (!browser->window())
926 continue;
928 if (!include_incognito() && GetProfile() != browser->profile())
929 continue;
931 if (!browser->extension_window_controller()->IsVisibleToExtension(
932 extension())) {
933 continue;
936 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
937 continue;
939 if (window_id == extension_misc::kCurrentWindowId &&
940 browser != current_browser) {
941 continue;
944 if (!MatchesBool(params->query_info.current_window.get(),
945 browser == current_browser)) {
946 continue;
949 if (!MatchesBool(params->query_info.last_focused_window.get(),
950 browser == last_active_browser)) {
951 continue;
954 if (!window_type.empty() &&
955 window_type !=
956 browser->extension_window_controller()->GetWindowTypeText()) {
957 continue;
960 TabStripModel* tab_strip = browser->tab_strip_model();
961 for (int i = 0; i < tab_strip->count(); ++i) {
962 WebContents* web_contents = tab_strip->GetWebContentsAt(i);
964 if (index > -1 && i != index)
965 continue;
967 if (!MatchesBool(params->query_info.highlighted.get(),
968 tab_strip->IsTabSelected(i))) {
969 continue;
972 if (!MatchesBool(params->query_info.active.get(),
973 i == tab_strip->active_index())) {
974 continue;
977 if (!MatchesBool(params->query_info.pinned.get(),
978 tab_strip->IsTabPinned(i))) {
979 continue;
982 if (!MatchesBool(params->query_info.audible.get(),
983 web_contents->WasRecentlyAudible())) {
984 continue;
987 if (!MatchesBool(params->query_info.muted.get(),
988 web_contents->IsAudioMuted())) {
989 continue;
992 if (!title.empty() && !base::MatchPattern(web_contents->GetTitle(),
993 base::UTF8ToUTF16(title)))
994 continue;
996 if (!url_patterns.is_empty() &&
997 !url_patterns.MatchesURL(web_contents->GetURL()))
998 continue;
1000 if (loading_status_set && loading != web_contents->IsLoading())
1001 continue;
1003 result->Append(ExtensionTabUtil::CreateTabValue(
1004 web_contents, tab_strip, i, extension()));
1008 SetResult(result);
1009 return true;
1012 bool TabsCreateFunction::RunSync() {
1013 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
1014 EXTENSION_FUNCTION_VALIDATE(params.get());
1016 ExtensionTabUtil::OpenTabParams options;
1017 AssignOptionalValue(params->create_properties.window_id, options.window_id);
1018 AssignOptionalValue(params->create_properties.opener_tab_id,
1019 options.opener_tab_id);
1020 AssignOptionalValue(params->create_properties.selected, options.active);
1021 // The 'active' property has replaced the 'selected' property.
1022 AssignOptionalValue(params->create_properties.active, options.active);
1023 AssignOptionalValue(params->create_properties.pinned, options.pinned);
1024 AssignOptionalValue(params->create_properties.index, options.index);
1025 AssignOptionalValue(params->create_properties.url, options.url);
1027 std::string error;
1028 scoped_ptr<base::DictionaryValue> result(
1029 ExtensionTabUtil::OpenTab(this, options, &error));
1030 if (!result) {
1031 SetError(error);
1032 return false;
1035 // Return data about the newly created tab.
1036 if (has_callback()) {
1037 SetResult(result.release());
1039 return true;
1042 bool TabsDuplicateFunction::RunSync() {
1043 scoped_ptr<tabs::Duplicate::Params> params(
1044 tabs::Duplicate::Params::Create(*args_));
1045 EXTENSION_FUNCTION_VALIDATE(params.get());
1046 int tab_id = params->tab_id;
1048 Browser* browser = NULL;
1049 TabStripModel* tab_strip = NULL;
1050 int tab_index = -1;
1051 if (!GetTabById(tab_id,
1052 GetProfile(),
1053 include_incognito(),
1054 &browser,
1055 &tab_strip,
1056 NULL,
1057 &tab_index,
1058 &error_)) {
1059 return false;
1062 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
1063 if (!has_callback())
1064 return true;
1066 // Duplicated tab may not be in the same window as the original, so find
1067 // the window and the tab.
1068 TabStripModel* new_tab_strip = NULL;
1069 int new_tab_index = -1;
1070 ExtensionTabUtil::GetTabStripModel(new_contents,
1071 &new_tab_strip,
1072 &new_tab_index);
1073 if (!new_tab_strip || new_tab_index == -1) {
1074 return false;
1077 // Return data about the newly created tab.
1078 SetResult(ExtensionTabUtil::CreateTabValue(
1079 new_contents, new_tab_strip, new_tab_index, extension()));
1081 return true;
1084 bool TabsGetFunction::RunSync() {
1085 scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
1086 EXTENSION_FUNCTION_VALIDATE(params.get());
1087 int tab_id = params->tab_id;
1089 TabStripModel* tab_strip = NULL;
1090 WebContents* contents = NULL;
1091 int tab_index = -1;
1092 if (!GetTabById(tab_id,
1093 GetProfile(),
1094 include_incognito(),
1095 NULL,
1096 &tab_strip,
1097 &contents,
1098 &tab_index,
1099 &error_))
1100 return false;
1102 SetResult(ExtensionTabUtil::CreateTabValue(
1103 contents, tab_strip, tab_index, extension()));
1104 return true;
1107 bool TabsGetCurrentFunction::RunSync() {
1108 DCHECK(dispatcher());
1110 // Return the caller, if it's a tab. If not the result isn't an error but an
1111 // empty tab (hence returning true).
1112 WebContents* caller_contents = GetSenderWebContents();
1113 if (caller_contents && ExtensionTabUtil::GetTabId(caller_contents) >= 0)
1114 SetResult(ExtensionTabUtil::CreateTabValue(caller_contents, extension()));
1116 return true;
1119 bool TabsHighlightFunction::RunSync() {
1120 scoped_ptr<tabs::Highlight::Params> params(
1121 tabs::Highlight::Params::Create(*args_));
1122 EXTENSION_FUNCTION_VALIDATE(params.get());
1124 // Get the window id from the params; default to current window if omitted.
1125 int window_id = extension_misc::kCurrentWindowId;
1126 if (params->highlight_info.window_id.get())
1127 window_id = *params->highlight_info.window_id;
1129 Browser* browser = NULL;
1130 if (!GetBrowserFromWindowID(this, window_id, &browser))
1131 return false;
1133 TabStripModel* tabstrip = browser->tab_strip_model();
1134 ui::ListSelectionModel selection;
1135 int active_index = -1;
1137 if (params->highlight_info.tabs.as_integers) {
1138 std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1139 // Create a new selection model as we read the list of tab indices.
1140 for (size_t i = 0; i < tab_indices.size(); ++i) {
1141 if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1142 return false;
1144 } else {
1145 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1146 if (!HighlightTab(tabstrip,
1147 &selection,
1148 &active_index,
1149 *params->highlight_info.tabs.as_integer)) {
1150 return false;
1154 // Make sure they actually specified tabs to select.
1155 if (selection.empty()) {
1156 error_ = keys::kNoHighlightedTabError;
1157 return false;
1160 selection.set_active(active_index);
1161 browser->tab_strip_model()->SetSelectionFromModel(selection);
1162 SetResult(browser->extension_window_controller()->CreateWindowValueWithTabs(
1163 extension()));
1164 return true;
1167 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1168 ui::ListSelectionModel* selection,
1169 int* active_index,
1170 int index) {
1171 // Make sure the index is in range.
1172 if (!tabstrip->ContainsIndex(index)) {
1173 error_ = ErrorUtils::FormatErrorMessage(
1174 keys::kTabIndexNotFoundError, base::IntToString(index));
1175 return false;
1178 // By default, we make the first tab in the list active.
1179 if (*active_index == -1)
1180 *active_index = index;
1182 selection->AddIndexToSelection(index);
1183 return true;
1186 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1189 bool TabsUpdateFunction::RunAsync() {
1190 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1191 EXTENSION_FUNCTION_VALIDATE(params.get());
1193 int tab_id = -1;
1194 WebContents* contents = NULL;
1195 if (!params->tab_id.get()) {
1196 Browser* browser = GetCurrentBrowser();
1197 if (!browser) {
1198 error_ = keys::kNoCurrentWindowError;
1199 return false;
1201 contents = browser->tab_strip_model()->GetActiveWebContents();
1202 if (!contents) {
1203 error_ = keys::kNoSelectedTabError;
1204 return false;
1206 tab_id = SessionTabHelper::IdForTab(contents);
1207 } else {
1208 tab_id = *params->tab_id;
1211 int tab_index = -1;
1212 TabStripModel* tab_strip = NULL;
1213 if (!GetTabById(tab_id,
1214 GetProfile(),
1215 include_incognito(),
1216 NULL,
1217 &tab_strip,
1218 &contents,
1219 &tab_index,
1220 &error_)) {
1221 return false;
1224 web_contents_ = contents;
1226 // TODO(rafaelw): handle setting remaining tab properties:
1227 // -title
1228 // -favIconUrl
1230 // Navigate the tab to a new location if the url is different.
1231 bool is_async = false;
1232 if (params->update_properties.url.get() &&
1233 !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1234 return false;
1237 bool active = false;
1238 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1239 // Move tab selection management up to window.
1240 if (params->update_properties.selected.get())
1241 active = *params->update_properties.selected;
1243 // The 'active' property has replaced 'selected'.
1244 if (params->update_properties.active.get())
1245 active = *params->update_properties.active;
1247 if (active) {
1248 if (tab_strip->active_index() != tab_index) {
1249 tab_strip->ActivateTabAt(tab_index, false);
1250 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1254 if (params->update_properties.highlighted.get()) {
1255 bool highlighted = *params->update_properties.highlighted;
1256 if (highlighted != tab_strip->IsTabSelected(tab_index))
1257 tab_strip->ToggleSelectionAt(tab_index);
1260 if (params->update_properties.pinned.get()) {
1261 bool pinned = *params->update_properties.pinned;
1262 tab_strip->SetTabPinned(tab_index, pinned);
1264 // Update the tab index because it may move when being pinned.
1265 tab_index = tab_strip->GetIndexOfWebContents(contents);
1268 if (params->update_properties.muted.get()) {
1269 TabMutedResult tab_muted_result = chrome::SetTabAudioMuted(
1270 contents, *params->update_properties.muted,
1271 TAB_MUTED_REASON_EXTENSION, extension()->id());
1273 switch (tab_muted_result) {
1274 case TAB_MUTED_RESULT_SUCCESS:
1275 break;
1276 case TAB_MUTED_RESULT_FAIL_NOT_ENABLED:
1277 error_ = ErrorUtils::FormatErrorMessage(
1278 keys::kCannotUpdateMuteDisabled, base::IntToString(tab_id),
1279 switches::kEnableTabAudioMuting);
1280 return false;
1281 case TAB_MUTED_RESULT_FAIL_TABCAPTURE:
1282 error_ = ErrorUtils::FormatErrorMessage(keys::kCannotUpdateMuteCaptured,
1283 base::IntToString(tab_id));
1284 return false;
1288 if (params->update_properties.opener_tab_id.get()) {
1289 int opener_id = *params->update_properties.opener_tab_id;
1291 WebContents* opener_contents = NULL;
1292 if (!ExtensionTabUtil::GetTabById(opener_id,
1293 GetProfile(),
1294 include_incognito(),
1295 NULL,
1296 NULL,
1297 &opener_contents,
1298 NULL))
1299 return false;
1301 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1304 if (!is_async) {
1305 PopulateResult();
1306 SendResponse(true);
1308 return true;
1311 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1312 int tab_id,
1313 bool* is_async) {
1314 GURL url =
1315 ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, extension());
1317 if (!url.is_valid()) {
1318 error_ = ErrorUtils::FormatErrorMessage(
1319 keys::kInvalidUrlError, url_string);
1320 return false;
1323 // Don't let the extension crash the browser or renderers.
1324 if (ExtensionTabUtil::IsKillURL(url)) {
1325 error_ = keys::kNoCrashBrowserError;
1326 return false;
1329 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1330 // we need to check host permissions before allowing them.
1331 if (url.SchemeIs(url::kJavaScriptScheme)) {
1332 content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1333 if (!extension()->permissions_data()->CanAccessPage(
1334 extension(),
1335 web_contents_->GetURL(),
1336 tab_id,
1337 process ? process->GetID() : -1,
1338 &error_)) {
1339 return false;
1342 TabHelper::FromWebContents(web_contents_)
1343 ->script_executor()
1344 ->ExecuteScript(
1345 HostID(HostID::EXTENSIONS, extension_id()),
1346 ScriptExecutor::JAVASCRIPT,
1347 net::UnescapeURLComponent(url.GetContent(),
1348 net::UnescapeRule::URL_SPECIAL_CHARS |
1349 net::UnescapeRule::SPACES),
1350 ScriptExecutor::TOP_FRAME, ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
1351 UserScript::DOCUMENT_IDLE, ScriptExecutor::MAIN_WORLD,
1352 ScriptExecutor::DEFAULT_PROCESS, GURL(), GURL(), user_gesture_,
1353 ScriptExecutor::NO_RESULT,
1354 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1356 *is_async = true;
1357 return true;
1360 web_contents_->GetController().LoadURL(
1361 url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1363 // The URL of a tab contents never actually changes to a JavaScript URL, so
1364 // this check only makes sense in other cases.
1365 if (!url.SchemeIs(url::kJavaScriptScheme))
1366 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1368 return true;
1371 void TabsUpdateFunction::PopulateResult() {
1372 if (!has_callback())
1373 return;
1375 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, extension()));
1378 void TabsUpdateFunction::OnExecuteCodeFinished(
1379 const std::string& error,
1380 const GURL& url,
1381 const base::ListValue& script_result) {
1382 if (error.empty())
1383 PopulateResult();
1384 else
1385 error_ = error;
1386 SendResponse(error.empty());
1389 bool TabsMoveFunction::RunSync() {
1390 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1391 EXTENSION_FUNCTION_VALIDATE(params.get());
1393 int new_index = params->move_properties.index;
1394 int* window_id = params->move_properties.window_id.get();
1395 scoped_ptr<base::ListValue> tab_values(new base::ListValue());
1397 size_t num_tabs = 0;
1398 if (params->tab_ids.as_integers) {
1399 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1400 num_tabs = tab_ids.size();
1401 for (size_t i = 0; i < tab_ids.size(); ++i) {
1402 if (!MoveTab(tab_ids[i], &new_index, i, tab_values.get(), window_id))
1403 return false;
1405 } else {
1406 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1407 num_tabs = 1;
1408 if (!MoveTab(*params->tab_ids.as_integer,
1409 &new_index,
1411 tab_values.get(),
1412 window_id)) {
1413 return false;
1417 if (!has_callback())
1418 return true;
1420 if (num_tabs == 0) {
1421 error_ = "No tabs given.";
1422 return false;
1423 } else if (num_tabs == 1) {
1424 scoped_ptr<base::Value> value;
1425 CHECK(tab_values.get()->Remove(0, &value));
1426 SetResult(value.release());
1427 } else {
1428 // Only return the results as an array if there are multiple tabs.
1429 SetResult(tab_values.release());
1432 return true;
1435 bool TabsMoveFunction::MoveTab(int tab_id,
1436 int* new_index,
1437 int iteration,
1438 base::ListValue* tab_values,
1439 int* window_id) {
1440 Browser* source_browser = NULL;
1441 TabStripModel* source_tab_strip = NULL;
1442 WebContents* contents = NULL;
1443 int tab_index = -1;
1444 if (!GetTabById(tab_id,
1445 GetProfile(),
1446 include_incognito(),
1447 &source_browser,
1448 &source_tab_strip,
1449 &contents,
1450 &tab_index,
1451 &error_)) {
1452 return false;
1455 // Don't let the extension move the tab if the user is dragging tabs.
1456 if (!source_browser->window()->IsTabStripEditable()) {
1457 error_ = keys::kTabStripNotEditableError;
1458 return false;
1461 // Insert the tabs one after another.
1462 *new_index += iteration;
1464 if (window_id) {
1465 Browser* target_browser = NULL;
1467 if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1468 return false;
1470 if (!target_browser->window()->IsTabStripEditable()) {
1471 error_ = keys::kTabStripNotEditableError;
1472 return false;
1475 if (!target_browser->is_type_tabbed()) {
1476 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1477 return false;
1480 if (target_browser->profile() != source_browser->profile()) {
1481 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1482 return false;
1485 // If windowId is different from the current window, move between windows.
1486 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1487 ExtensionTabUtil::GetWindowId(source_browser)) {
1488 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1489 WebContents* web_contents =
1490 source_tab_strip->DetachWebContentsAt(tab_index);
1491 if (!web_contents) {
1492 error_ = ErrorUtils::FormatErrorMessage(
1493 keys::kTabNotFoundError, base::IntToString(tab_id));
1494 return false;
1497 // Clamp move location to the last position.
1498 // This is ">" because it can append to a new index position.
1499 // -1 means set the move location to the last position.
1500 if (*new_index > target_tab_strip->count() || *new_index < 0)
1501 *new_index = target_tab_strip->count();
1503 target_tab_strip->InsertWebContentsAt(
1504 *new_index, web_contents, TabStripModel::ADD_NONE);
1506 if (has_callback()) {
1507 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1508 web_contents, target_tab_strip, *new_index, extension()));
1511 return true;
1515 // Perform a simple within-window move.
1516 // Clamp move location to the last position.
1517 // This is ">=" because the move must be to an existing location.
1518 // -1 means set the move location to the last position.
1519 if (*new_index >= source_tab_strip->count() || *new_index < 0)
1520 *new_index = source_tab_strip->count() - 1;
1522 if (*new_index != tab_index)
1523 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1525 if (has_callback()) {
1526 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1527 contents, source_tab_strip, *new_index, extension()));
1530 return true;
1533 bool TabsReloadFunction::RunSync() {
1534 scoped_ptr<tabs::Reload::Params> params(
1535 tabs::Reload::Params::Create(*args_));
1536 EXTENSION_FUNCTION_VALIDATE(params.get());
1538 bool bypass_cache = false;
1539 if (params->reload_properties.get() &&
1540 params->reload_properties->bypass_cache.get()) {
1541 bypass_cache = *params->reload_properties->bypass_cache;
1544 content::WebContents* web_contents = NULL;
1546 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1547 // in the current window.
1548 if (!params->tab_id.get()) {
1549 Browser* browser = GetCurrentBrowser();
1550 if (!browser) {
1551 error_ = keys::kNoCurrentWindowError;
1552 return false;
1555 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1556 return false;
1557 } else {
1558 int tab_id = *params->tab_id;
1560 Browser* browser = NULL;
1561 if (!GetTabById(tab_id,
1562 GetProfile(),
1563 include_incognito(),
1564 &browser,
1565 NULL,
1566 &web_contents,
1567 NULL,
1568 &error_)) {
1569 return false;
1573 if (web_contents->ShowingInterstitialPage()) {
1574 // This does as same as Browser::ReloadInternal.
1575 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1576 GURL reload_url = entry ? entry->GetURL() : GURL(url::kAboutBlankURL);
1577 OpenURLParams params(reload_url, Referrer(), CURRENT_TAB,
1578 ui::PAGE_TRANSITION_RELOAD, false);
1579 GetCurrentBrowser()->OpenURL(params);
1580 } else if (bypass_cache) {
1581 web_contents->GetController().ReloadIgnoringCache(true);
1582 } else {
1583 web_contents->GetController().Reload(true);
1586 return true;
1589 bool TabsRemoveFunction::RunSync() {
1590 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1591 EXTENSION_FUNCTION_VALIDATE(params.get());
1593 if (params->tab_ids.as_integers) {
1594 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1595 for (size_t i = 0; i < tab_ids.size(); ++i) {
1596 if (!RemoveTab(tab_ids[i]))
1597 return false;
1599 } else {
1600 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1601 if (!RemoveTab(*params->tab_ids.as_integer.get()))
1602 return false;
1604 return true;
1607 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1608 Browser* browser = NULL;
1609 WebContents* contents = NULL;
1610 if (!GetTabById(tab_id,
1611 GetProfile(),
1612 include_incognito(),
1613 &browser,
1614 NULL,
1615 &contents,
1616 NULL,
1617 &error_)) {
1618 return false;
1621 // Don't let the extension remove a tab if the user is dragging tabs around.
1622 if (!browser->window()->IsTabStripEditable()) {
1623 error_ = keys::kTabStripNotEditableError;
1624 return false;
1626 // There's a chance that the tab is being dragged, or we're in some other
1627 // nested event loop. This code path ensures that the tab is safely closed
1628 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1629 // does not.
1630 contents->Close();
1631 return true;
1634 TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
1635 : chrome_details_(this) {
1638 bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1639 PrefService* service = chrome_details_.GetProfile()->GetPrefs();
1640 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1641 error_ = keys::kScreenshotsDisabled;
1642 return false;
1644 return true;
1647 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1648 Browser* browser = NULL;
1649 if (!GetBrowserFromWindowID(&chrome_details_, window_id, &browser))
1650 return NULL;
1652 WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1653 if (!contents) {
1654 error_ = "No active web contents to capture";
1655 return NULL;
1658 if (!extension()->permissions_data()->CanCaptureVisiblePage(
1659 SessionTabHelper::IdForTab(contents), &error_)) {
1660 return NULL;
1662 return contents;
1665 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1666 const char* reason_description = "internal error";
1667 switch (reason) {
1668 case FAILURE_REASON_UNKNOWN:
1669 reason_description = "unknown error";
1670 break;
1671 case FAILURE_REASON_ENCODING_FAILED:
1672 reason_description = "encoding failed";
1673 break;
1674 case FAILURE_REASON_VIEW_INVISIBLE:
1675 reason_description = "view is invisible";
1676 break;
1678 error_ = ErrorUtils::FormatErrorMessage("Failed to capture tab: *",
1679 reason_description);
1680 SendResponse(false);
1683 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1684 user_prefs::PrefRegistrySyncable* registry) {
1685 registry->RegisterBooleanPref(prefs::kDisableScreenshots, false);
1688 bool TabsDetectLanguageFunction::RunAsync() {
1689 scoped_ptr<tabs::DetectLanguage::Params> params(
1690 tabs::DetectLanguage::Params::Create(*args_));
1691 EXTENSION_FUNCTION_VALIDATE(params.get());
1693 int tab_id = 0;
1694 Browser* browser = NULL;
1695 WebContents* contents = NULL;
1697 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1698 // in the current window.
1699 if (params->tab_id.get()) {
1700 tab_id = *params->tab_id;
1701 if (!GetTabById(tab_id,
1702 GetProfile(),
1703 include_incognito(),
1704 &browser,
1705 NULL,
1706 &contents,
1707 NULL,
1708 &error_)) {
1709 return false;
1711 if (!browser || !contents)
1712 return false;
1713 } else {
1714 browser = GetCurrentBrowser();
1715 if (!browser)
1716 return false;
1717 contents = browser->tab_strip_model()->GetActiveWebContents();
1718 if (!contents)
1719 return false;
1722 if (contents->GetController().NeedsReload()) {
1723 // If the tab hasn't been loaded, don't wait for the tab to load.
1724 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1725 return false;
1728 AddRef(); // Balanced in GotLanguage().
1730 ChromeTranslateClient* chrome_translate_client =
1731 ChromeTranslateClient::FromWebContents(contents);
1732 if (!chrome_translate_client->GetLanguageState()
1733 .original_language()
1734 .empty()) {
1735 // Delay the callback invocation until after the current JS call has
1736 // returned.
1737 base::ThreadTaskRunnerHandle::Get()->PostTask(
1738 FROM_HERE,
1739 base::Bind(
1740 &TabsDetectLanguageFunction::GotLanguage, this,
1741 chrome_translate_client->GetLanguageState().original_language()));
1742 return true;
1744 // The tab contents does not know its language yet. Let's wait until it
1745 // receives it, or until the tab is closed/navigates to some other page.
1746 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1747 content::Source<WebContents>(contents));
1748 registrar_.Add(
1749 this, chrome::NOTIFICATION_TAB_CLOSING,
1750 content::Source<NavigationController>(&(contents->GetController())));
1751 registrar_.Add(
1752 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1753 content::Source<NavigationController>(&(contents->GetController())));
1754 return true;
1757 void TabsDetectLanguageFunction::Observe(
1758 int type,
1759 const content::NotificationSource& source,
1760 const content::NotificationDetails& details) {
1761 std::string language;
1762 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1763 const translate::LanguageDetectionDetails* lang_det_details =
1764 content::Details<const translate::LanguageDetectionDetails>(details)
1765 .ptr();
1766 language = lang_det_details->adopted_language;
1769 registrar_.RemoveAll();
1771 // Call GotLanguage in all cases as we want to guarantee the callback is
1772 // called for every API call the extension made.
1773 GotLanguage(language);
1776 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1777 SetResult(new base::StringValue(language.c_str()));
1778 SendResponse(true);
1780 Release(); // Balanced in Run()
1783 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1784 : chrome_details_(this), execute_tab_id_(-1) {
1787 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1789 bool ExecuteCodeInTabFunction::HasPermission() {
1790 if (Init() &&
1791 extension_->permissions_data()->HasAPIPermissionForTab(
1792 execute_tab_id_, APIPermission::kTab)) {
1793 return true;
1795 return ExtensionFunction::HasPermission();
1798 bool ExecuteCodeInTabFunction::Init() {
1799 if (details_.get())
1800 return true;
1802 // |tab_id| is optional so it's ok if it's not there.
1803 int tab_id = -1;
1804 if (args_->GetInteger(0, &tab_id))
1805 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1807 // |details| are not optional.
1808 base::DictionaryValue* details_value = NULL;
1809 if (!args_->GetDictionary(1, &details_value))
1810 return false;
1811 scoped_ptr<InjectDetails> details(new InjectDetails());
1812 if (!InjectDetails::Populate(*details_value, details.get()))
1813 return false;
1815 // If the tab ID wasn't given then it needs to be converted to the
1816 // currently active tab's ID.
1817 if (tab_id == -1) {
1818 Browser* browser = chrome_details_.GetCurrentBrowser();
1819 if (!browser)
1820 return false;
1821 content::WebContents* web_contents = NULL;
1822 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1823 return false;
1826 execute_tab_id_ = tab_id;
1827 details_ = details.Pass();
1828 set_host_id(HostID(HostID::EXTENSIONS, extension()->id()));
1829 return true;
1832 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1833 content::WebContents* contents = NULL;
1835 // If |tab_id| is specified, look for the tab. Otherwise default to selected
1836 // tab in the current window.
1837 CHECK_GE(execute_tab_id_, 0);
1838 if (!GetTabById(execute_tab_id_,
1839 chrome_details_.GetProfile(),
1840 include_incognito(),
1841 NULL,
1842 NULL,
1843 &contents,
1844 NULL,
1845 &error_)) {
1846 return false;
1849 CHECK(contents);
1851 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1852 // we check again in the renderer.
1853 content::RenderProcessHost* process = contents->GetRenderProcessHost();
1854 if (!extension()->permissions_data()->CanAccessPage(
1855 extension(),
1856 contents->GetURL(),
1857 execute_tab_id_,
1858 process ? process->GetID() : -1,
1859 &error_)) {
1860 return false;
1863 return true;
1866 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1867 Browser* browser = NULL;
1868 content::WebContents* contents = NULL;
1870 bool success = GetTabById(execute_tab_id_,
1871 chrome_details_.GetProfile(),
1872 include_incognito(),
1873 &browser,
1874 NULL,
1875 &contents,
1876 NULL,
1877 &error_) &&
1878 contents && browser;
1880 if (!success)
1881 return NULL;
1883 return TabHelper::FromWebContents(contents)->script_executor();
1886 bool ExecuteCodeInTabFunction::IsWebView() const {
1887 return false;
1890 const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1891 return GURL::EmptyGURL();
1894 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1895 return false;
1898 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1899 const std::string& error,
1900 const GURL& on_url,
1901 const base::ListValue& result) {
1902 if (error.empty())
1903 SetResult(result.DeepCopy());
1904 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_url, result);
1907 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1908 return true;
1911 content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
1912 content::WebContents* web_contents = NULL;
1913 if (tab_id != -1) {
1914 // We assume this call leaves web_contents unchanged if it is unsuccessful.
1915 GetTabById(tab_id,
1916 GetProfile(),
1917 include_incognito(),
1918 NULL /* ignore Browser* output */,
1919 NULL /* ignore TabStripModel* output */,
1920 &web_contents,
1921 NULL /* ignore int tab_index output */,
1922 &error_);
1923 } else {
1924 Browser* browser = GetCurrentBrowser();
1925 if (!browser)
1926 error_ = keys::kNoCurrentWindowError;
1927 else if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1928 error_ = keys::kNoSelectedTabError;
1930 return web_contents;
1933 bool TabsSetZoomFunction::RunAsync() {
1934 scoped_ptr<tabs::SetZoom::Params> params(
1935 tabs::SetZoom::Params::Create(*args_));
1936 EXTENSION_FUNCTION_VALIDATE(params);
1938 int tab_id = params->tab_id ? *params->tab_id : -1;
1939 WebContents* web_contents = GetWebContents(tab_id);
1940 if (!web_contents)
1941 return false;
1943 GURL url(web_contents->GetVisibleURL());
1944 if (PermissionsData::IsRestrictedUrl(url, extension(), &error_))
1945 return false;
1947 ZoomController* zoom_controller =
1948 ZoomController::FromWebContents(web_contents);
1949 double zoom_level = params->zoom_factor > 0
1950 ? content::ZoomFactorToZoomLevel(params->zoom_factor)
1951 : zoom_controller->GetDefaultZoomLevel();
1953 scoped_refptr<ExtensionZoomRequestClient> client(
1954 new ExtensionZoomRequestClient(extension()));
1955 if (!zoom_controller->SetZoomLevelByClient(zoom_level, client)) {
1956 // Tried to zoom a tab in disabled mode.
1957 error_ = keys::kCannotZoomDisabledTabError;
1958 return false;
1961 SendResponse(true);
1962 return true;
1965 bool TabsGetZoomFunction::RunAsync() {
1966 scoped_ptr<tabs::GetZoom::Params> params(
1967 tabs::GetZoom::Params::Create(*args_));
1968 EXTENSION_FUNCTION_VALIDATE(params);
1970 int tab_id = params->tab_id ? *params->tab_id : -1;
1971 WebContents* web_contents = GetWebContents(tab_id);
1972 if (!web_contents)
1973 return false;
1975 double zoom_level =
1976 ZoomController::FromWebContents(web_contents)->GetZoomLevel();
1977 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
1978 results_ = tabs::GetZoom::Results::Create(zoom_factor);
1979 SendResponse(true);
1980 return true;
1983 bool TabsSetZoomSettingsFunction::RunAsync() {
1984 using api::tabs::ZoomSettings;
1986 scoped_ptr<tabs::SetZoomSettings::Params> params(
1987 tabs::SetZoomSettings::Params::Create(*args_));
1988 EXTENSION_FUNCTION_VALIDATE(params);
1990 int tab_id = params->tab_id ? *params->tab_id : -1;
1991 WebContents* web_contents = GetWebContents(tab_id);
1992 if (!web_contents)
1993 return false;
1995 GURL url(web_contents->GetVisibleURL());
1996 if (PermissionsData::IsRestrictedUrl(url, extension(), &error_))
1997 return false;
1999 // "per-origin" scope is only available in "automatic" mode.
2000 if (params->zoom_settings.scope == tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN &&
2001 params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_AUTOMATIC &&
2002 params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_NONE) {
2003 error_ = keys::kPerOriginOnlyInAutomaticError;
2004 return false;
2007 // Determine the correct internal zoom mode to set |web_contents| to from the
2008 // user-specified |zoom_settings|.
2009 ZoomController::ZoomMode zoom_mode = ZoomController::ZOOM_MODE_DEFAULT;
2010 switch (params->zoom_settings.mode) {
2011 case tabs::ZOOM_SETTINGS_MODE_NONE:
2012 case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC:
2013 switch (params->zoom_settings.scope) {
2014 case tabs::ZOOM_SETTINGS_SCOPE_NONE:
2015 case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN:
2016 zoom_mode = ZoomController::ZOOM_MODE_DEFAULT;
2017 break;
2018 case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB:
2019 zoom_mode = ZoomController::ZOOM_MODE_ISOLATED;
2021 break;
2022 case tabs::ZOOM_SETTINGS_MODE_MANUAL:
2023 zoom_mode = ZoomController::ZOOM_MODE_MANUAL;
2024 break;
2025 case tabs::ZOOM_SETTINGS_MODE_DISABLED:
2026 zoom_mode = ZoomController::ZOOM_MODE_DISABLED;
2029 ZoomController::FromWebContents(web_contents)->SetZoomMode(zoom_mode);
2031 SendResponse(true);
2032 return true;
2035 bool TabsGetZoomSettingsFunction::RunAsync() {
2036 scoped_ptr<tabs::GetZoomSettings::Params> params(
2037 tabs::GetZoomSettings::Params::Create(*args_));
2038 EXTENSION_FUNCTION_VALIDATE(params);
2040 int tab_id = params->tab_id ? *params->tab_id : -1;
2041 WebContents* web_contents = GetWebContents(tab_id);
2042 if (!web_contents)
2043 return false;
2044 ZoomController* zoom_controller =
2045 ZoomController::FromWebContents(web_contents);
2047 ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
2048 api::tabs::ZoomSettings zoom_settings;
2049 ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
2050 zoom_settings.default_zoom_factor.reset(new double(
2051 content::ZoomLevelToZoomFactor(zoom_controller->GetDefaultZoomLevel())));
2053 results_ = api::tabs::GetZoomSettings::Results::Create(zoom_settings);
2054 SendResponse(true);
2055 return true;
2058 } // namespace extensions