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"
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"
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"
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
;
119 template <typename T
>
120 class ApiParameterExtractor
{
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
;
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
;
142 bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction
* function
,
148 ExtensionTabUtil::GetBrowserFromWindowID(function
, window_id
, &error
);
150 function
->SetError(error
);
158 bool GetBrowserFromWindowID(ChromeExtensionFunctionDetails
* details
,
164 ExtensionTabUtil::GetBrowserFromWindowID(*details
, window_id
, &error
);
166 details
->function()->SetError(error
);
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
,
178 bool include_incognito
,
180 TabStripModel
** tab_strip
,
181 content::WebContents
** contents
,
183 std::string
* error_message
) {
184 if (ExtensionTabUtil::GetTabById(tab_id
, profile
, include_incognito
,
185 browser
, tab_strip
, contents
, tab_index
)) {
190 *error_message
= ErrorUtils::FormatErrorMessage(
191 keys::kTabNotFoundError
, base::IntToString(tab_id
));
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
) {
208 destination
.reset(new T(*source
.get()));
212 ui::WindowShowState
ConvertToWindowShowState(windows::WindowState 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
;
227 return ui::SHOW_STATE_DEFAULT
;
230 bool IsValidStateForWindowsCreateFunction(
231 const windows::Create::Params::CreateData
* create_data
) {
235 bool has_bound
= create_data
->left
|| create_data
->top
||
236 create_data
->width
|| create_data
->height
;
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
&&
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
&&
251 case windows::WINDOW_STATE_NORMAL
:
252 case windows::WINDOW_STATE_DOCKED
:
253 case windows::WINDOW_STATE_NONE
:
262 void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode
,
263 api::tabs::ZoomSettings
* zoom_settings
) {
264 DCHECK(zoom_settings
);
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
;
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
;
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
;
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
;
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
)) {
298 if (extractor
.populate_tabs())
299 SetResult(controller
->CreateWindowValueWithTabs(extension()));
301 SetResult(controller
->CreateWindowValue());
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(),
317 if (extractor
.populate_tabs())
318 SetResult(controller
->CreateWindowValueWithTabs(extension()));
320 SetResult(controller
->CreateWindowValue());
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(
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())) {
338 if (controller
->window()->IsActive())
339 break; // Use focused window.
343 error_
= keys::kNoLastFocusedWindowError
;
346 if (extractor
.populate_tabs())
347 SetResult(controller
->CreateWindowValueWithTabs(extension()));
349 SetResult(controller
->CreateWindowValue());
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
=
364 iter
!= windows
.end(); ++iter
) {
365 if (!windows_util::CanOperateOnWindow(this, *iter
,
366 extractor
.type_filters()))
368 if (extractor
.populate_tabs())
369 window_list
->Append((*iter
)->CreateWindowValueWithTabs(extension()));
371 window_list
->Append((*iter
)->CreateWindowValue());
373 SetResult(window_list
);
377 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
378 const windows::Create::Params::CreateData
* create_data
,
379 std::vector
<GURL
>* urls
, bool* is_error
) {
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
;
391 if (!incognito
&& incognito_availability
== IncognitoModePrefs::FORCED
) {
392 error_
= keys::kIncognitoModeIsForced
;
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.
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())) {
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
);
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
;
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
);
452 // Don't let the extension crash the browser or renderers.
453 if (ExtensionTabUtil::IsKillURL(url
)) {
454 error_
= keys::kNoCrashBrowserError
;
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
,
476 if (!IsValidStateForWindowsCreateFunction(create_data
)) {
477 error_
= keys::kInvalidWindowStateError
;
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
;
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
,
498 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
501 if (open_incognito_window
) {
502 window_profile
= window_profile
->GetOffTheRecordProfile();
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();
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
);
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
;
525 window_type
= Browser::TYPE_POPUP
;
529 case windows::CREATE_TYPE_NONE
:
530 case windows::CREATE_TYPE_NORMAL
:
533 error_
= keys::kInvalidWindowTypeError
;
537 // Initialize default window bounds according to window type.
538 if (window_type
== Browser::TYPE_TABBED
||
539 window_type
== Browser::TYPE_POPUP
||
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
548 ui::WindowShowState show_state
= ui::SHOW_STATE_DEFAULT
;
549 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
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;
582 urls
.push_back(GURL(chrome::kChromeUINewTabURL
));
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(
593 new ChromeAppDelegate(make_scoped_ptr(new ScopedKeepAlive
)),
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
)
602 SetResult(window_controller
->CreateWindowValueWithTabs(extension()));
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();
618 SetResult(panel
->extension_window_controller()->CreateWindowValueWithTabs(
623 // Create a new BrowserWindow.
624 chrome::HostDesktopType host_desktop_type
= chrome::GetActiveDesktop();
626 window_type
= Browser::TYPE_POPUP
;
627 Browser::CreateParams
create_params(window_type
, window_profile
,
629 if (extension_id
.empty()) {
630 create_params
.initial_bounds
= window_bounds
;
632 create_params
= Browser::CreateParams::CreateForApp(
633 web_app::GenerateApplicationNameFromExtensionId(extension_id
),
634 false /* trusted_source */,
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
);
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
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
);
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
)
681 new_window
->window()->Show();
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();
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());
700 SetResult(controller
->CreateWindowValueWithTabs(extension()));
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(),
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();
729 case ui::SHOW_STATE_MAXIMIZED
:
730 controller
->window()->Maximize();
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());
738 case ui::SHOW_STATE_NORMAL
:
739 controller
->window()->Restore();
746 if (controller
->window()->IsMinimized())
747 bounds
= controller
->window()->GetRestoredBounds();
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
);
758 if (params
->update_info
.top
) {
759 bounds
.set_y(*params
->update_info
.top
);
763 if (params
->update_info
.width
) {
764 bounds
.set_width(*params
->update_info
.width
);
768 if (params
->update_info
.height
) {
769 bounds
.set_height(*params
->update_info
.height
);
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
;
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
;
791 controller
->window()->Activate();
793 if (show_state
== ui::SHOW_STATE_MAXIMIZED
||
794 show_state
== ui::SHOW_STATE_FULLSCREEN
) {
795 error_
= keys::kInvalidWindowStateError
;
798 controller
->window()->Deactivate();
802 if (params
->update_info
.draw_attention
)
803 controller
->window()->FlashFrame(*params
->update_info
.draw_attention
);
805 SetResult(controller
->CreateWindowValue());
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
,
822 WindowController::Reason reason
;
823 if (!controller
->CanClose(&reason
)) {
824 if (reason
== WindowController::REASON_NOT_EDITABLE
)
825 error_
= keys::kTabStripNotEditableError
;
828 controller
->window()->Close();
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
))
848 TabStripModel
* tab_strip
= browser
->tab_strip_model();
849 WebContents
* contents
= tab_strip
->GetActiveWebContents();
851 error_
= keys::kNoSelectedTabError
;
854 SetResult(ExtensionTabUtil::CreateTabValue(
855 contents
, tab_strip
, tab_strip
->active_index(), extension()));
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
))
872 SetResult(ExtensionTabUtil::CreateTabList(browser
, extension()));
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
894 if (!url_patterns
.Populate(url_pattern_strings
, URLPattern::SCHEME_ALL
,
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
;
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()))
925 if (!browser
->window())
928 if (!include_incognito() && GetProfile() != browser
->profile())
931 if (!browser
->extension_window_controller()->IsVisibleToExtension(
936 if (window_id
>= 0 && window_id
!= ExtensionTabUtil::GetWindowId(browser
))
939 if (window_id
== extension_misc::kCurrentWindowId
&&
940 browser
!= current_browser
) {
944 if (!MatchesBool(params
->query_info
.current_window
.get(),
945 browser
== current_browser
)) {
949 if (!MatchesBool(params
->query_info
.last_focused_window
.get(),
950 browser
== last_active_browser
)) {
954 if (!window_type
.empty() &&
956 browser
->extension_window_controller()->GetWindowTypeText()) {
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
)
967 if (!MatchesBool(params
->query_info
.highlighted
.get(),
968 tab_strip
->IsTabSelected(i
))) {
972 if (!MatchesBool(params
->query_info
.active
.get(),
973 i
== tab_strip
->active_index())) {
977 if (!MatchesBool(params
->query_info
.pinned
.get(),
978 tab_strip
->IsTabPinned(i
))) {
982 if (!MatchesBool(params
->query_info
.audible
.get(),
983 web_contents
->WasRecentlyAudible())) {
987 if (!MatchesBool(params
->query_info
.muted
.get(),
988 web_contents
->IsAudioMuted())) {
992 if (!title
.empty() && !base::MatchPattern(web_contents
->GetTitle(),
993 base::UTF8ToUTF16(title
)))
996 if (!url_patterns
.is_empty() &&
997 !url_patterns
.MatchesURL(web_contents
->GetURL()))
1000 if (loading_status_set
&& loading
!= web_contents
->IsLoading())
1003 result
->Append(ExtensionTabUtil::CreateTabValue(
1004 web_contents
, tab_strip
, i
, extension()));
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
);
1028 scoped_ptr
<base::DictionaryValue
> result(
1029 ExtensionTabUtil::OpenTab(this, options
, &error
));
1035 // Return data about the newly created tab.
1036 if (has_callback()) {
1037 SetResult(result
.release());
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
;
1051 if (!GetTabById(tab_id
,
1053 include_incognito(),
1062 WebContents
* new_contents
= chrome::DuplicateTabAt(browser
, tab_index
);
1063 if (!has_callback())
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
,
1073 if (!new_tab_strip
|| new_tab_index
== -1) {
1077 // Return data about the newly created tab.
1078 SetResult(ExtensionTabUtil::CreateTabValue(
1079 new_contents
, new_tab_strip
, new_tab_index
, extension()));
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
;
1092 if (!GetTabById(tab_id
,
1094 include_incognito(),
1102 SetResult(ExtensionTabUtil::CreateTabValue(
1103 contents
, tab_strip
, tab_index
, extension()));
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()));
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
))
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
]))
1145 EXTENSION_FUNCTION_VALIDATE(params
->highlight_info
.tabs
.as_integer
);
1146 if (!HighlightTab(tabstrip
,
1149 *params
->highlight_info
.tabs
.as_integer
)) {
1154 // Make sure they actually specified tabs to select.
1155 if (selection
.empty()) {
1156 error_
= keys::kNoHighlightedTabError
;
1160 selection
.set_active(active_index
);
1161 browser
->tab_strip_model()->SetSelectionFromModel(selection
);
1162 SetResult(browser
->extension_window_controller()->CreateWindowValueWithTabs(
1167 bool TabsHighlightFunction::HighlightTab(TabStripModel
* tabstrip
,
1168 ui::ListSelectionModel
* selection
,
1171 // Make sure the index is in range.
1172 if (!tabstrip
->ContainsIndex(index
)) {
1173 error_
= ErrorUtils::FormatErrorMessage(
1174 keys::kTabIndexNotFoundError
, base::IntToString(index
));
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
);
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());
1194 WebContents
* contents
= NULL
;
1195 if (!params
->tab_id
.get()) {
1196 Browser
* browser
= GetCurrentBrowser();
1198 error_
= keys::kNoCurrentWindowError
;
1201 contents
= browser
->tab_strip_model()->GetActiveWebContents();
1203 error_
= keys::kNoSelectedTabError
;
1206 tab_id
= SessionTabHelper::IdForTab(contents
);
1208 tab_id
= *params
->tab_id
;
1212 TabStripModel
* tab_strip
= NULL
;
1213 if (!GetTabById(tab_id
,
1215 include_incognito(),
1224 web_contents_
= contents
;
1226 // TODO(rafaelw): handle setting remaining tab properties:
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
)) {
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
;
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
:
1276 case TAB_MUTED_RESULT_FAIL_NOT_ENABLED
:
1277 error_
= ErrorUtils::FormatErrorMessage(
1278 keys::kCannotUpdateMuteDisabled
, base::IntToString(tab_id
),
1279 switches::kEnableTabAudioMuting
);
1281 case TAB_MUTED_RESULT_FAIL_TABCAPTURE
:
1282 error_
= ErrorUtils::FormatErrorMessage(keys::kCannotUpdateMuteCaptured
,
1283 base::IntToString(tab_id
));
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
,
1294 include_incognito(),
1301 tab_strip
->SetOpenerOfWebContentsAt(tab_index
, opener_contents
);
1311 bool TabsUpdateFunction::UpdateURL(const std::string
&url_string
,
1315 ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string
, extension());
1317 if (!url
.is_valid()) {
1318 error_
= ErrorUtils::FormatErrorMessage(
1319 keys::kInvalidUrlError
, url_string
);
1323 // Don't let the extension crash the browser or renderers.
1324 if (ExtensionTabUtil::IsKillURL(url
)) {
1325 error_
= keys::kNoCrashBrowserError
;
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(
1335 web_contents_
->GetURL(),
1337 process
? process
->GetID() : -1,
1342 TabHelper::FromWebContents(web_contents_
)
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));
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());
1371 void TabsUpdateFunction::PopulateResult() {
1372 if (!has_callback())
1375 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_
, extension()));
1378 void TabsUpdateFunction::OnExecuteCodeFinished(
1379 const std::string
& error
,
1381 const base::ListValue
& script_result
) {
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
))
1406 EXTENSION_FUNCTION_VALIDATE(params
->tab_ids
.as_integer
);
1408 if (!MoveTab(*params
->tab_ids
.as_integer
,
1417 if (!has_callback())
1420 if (num_tabs
== 0) {
1421 error_
= "No tabs given.";
1423 } else if (num_tabs
== 1) {
1424 scoped_ptr
<base::Value
> value
;
1425 CHECK(tab_values
.get()->Remove(0, &value
));
1426 SetResult(value
.release());
1428 // Only return the results as an array if there are multiple tabs.
1429 SetResult(tab_values
.release());
1435 bool TabsMoveFunction::MoveTab(int tab_id
,
1438 base::ListValue
* tab_values
,
1440 Browser
* source_browser
= NULL
;
1441 TabStripModel
* source_tab_strip
= NULL
;
1442 WebContents
* contents
= NULL
;
1444 if (!GetTabById(tab_id
,
1446 include_incognito(),
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
;
1461 // Insert the tabs one after another.
1462 *new_index
+= iteration
;
1465 Browser
* target_browser
= NULL
;
1467 if (!GetBrowserFromWindowID(this, *window_id
, &target_browser
))
1470 if (!target_browser
->window()->IsTabStripEditable()) {
1471 error_
= keys::kTabStripNotEditableError
;
1475 if (!target_browser
->is_type_tabbed()) {
1476 error_
= keys::kCanOnlyMoveTabsWithinNormalWindowsError
;
1480 if (target_browser
->profile() != source_browser
->profile()) {
1481 error_
= keys::kCanOnlyMoveTabsWithinSameProfileError
;
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
));
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()));
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()));
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();
1551 error_
= keys::kNoCurrentWindowError
;
1555 if (!ExtensionTabUtil::GetDefaultTab(browser
, &web_contents
, NULL
))
1558 int tab_id
= *params
->tab_id
;
1560 Browser
* browser
= NULL
;
1561 if (!GetTabById(tab_id
,
1563 include_incognito(),
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);
1583 web_contents
->GetController().Reload(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
]))
1600 EXTENSION_FUNCTION_VALIDATE(params
->tab_ids
.as_integer
);
1601 if (!RemoveTab(*params
->tab_ids
.as_integer
.get()))
1607 bool TabsRemoveFunction::RemoveTab(int tab_id
) {
1608 Browser
* browser
= NULL
;
1609 WebContents
* contents
= NULL
;
1610 if (!GetTabById(tab_id
,
1612 include_incognito(),
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
;
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()|
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
;
1647 WebContents
* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id
) {
1648 Browser
* browser
= NULL
;
1649 if (!GetBrowserFromWindowID(&chrome_details_
, window_id
, &browser
))
1652 WebContents
* contents
= browser
->tab_strip_model()->GetActiveWebContents();
1654 error_
= "No active web contents to capture";
1658 if (!extension()->permissions_data()->CanCaptureVisiblePage(
1659 SessionTabHelper::IdForTab(contents
), &error_
)) {
1665 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason
) {
1666 const char* reason_description
= "internal error";
1668 case FAILURE_REASON_UNKNOWN
:
1669 reason_description
= "unknown error";
1671 case FAILURE_REASON_ENCODING_FAILED
:
1672 reason_description
= "encoding failed";
1674 case FAILURE_REASON_VIEW_INVISIBLE
:
1675 reason_description
= "view is invisible";
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());
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
,
1703 include_incognito(),
1711 if (!browser
|| !contents
)
1714 browser
= GetCurrentBrowser();
1717 contents
= browser
->tab_strip_model()->GetActiveWebContents();
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
;
1728 AddRef(); // Balanced in GotLanguage().
1730 ChromeTranslateClient
* chrome_translate_client
=
1731 ChromeTranslateClient::FromWebContents(contents
);
1732 if (!chrome_translate_client
->GetLanguageState()
1733 .original_language()
1735 // Delay the callback invocation until after the current JS call has
1737 base::ThreadTaskRunnerHandle::Get()->PostTask(
1740 &TabsDetectLanguageFunction::GotLanguage
, this,
1741 chrome_translate_client
->GetLanguageState().original_language()));
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
));
1749 this, chrome::NOTIFICATION_TAB_CLOSING
,
1750 content::Source
<NavigationController
>(&(contents
->GetController())));
1752 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
1753 content::Source
<NavigationController
>(&(contents
->GetController())));
1757 void TabsDetectLanguageFunction::Observe(
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
)
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()));
1780 Release(); // Balanced in Run()
1783 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1784 : chrome_details_(this), execute_tab_id_(-1) {
1787 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1789 bool ExecuteCodeInTabFunction::HasPermission() {
1791 extension_
->permissions_data()->HasAPIPermissionForTab(
1792 execute_tab_id_
, APIPermission::kTab
)) {
1795 return ExtensionFunction::HasPermission();
1798 bool ExecuteCodeInTabFunction::Init() {
1802 // |tab_id| is optional so it's ok if it's not there.
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
))
1811 scoped_ptr
<InjectDetails
> details(new InjectDetails());
1812 if (!InjectDetails::Populate(*details_value
, details
.get()))
1815 // If the tab ID wasn't given then it needs to be converted to the
1816 // currently active tab's ID.
1818 Browser
* browser
= chrome_details_
.GetCurrentBrowser();
1821 content::WebContents
* web_contents
= NULL
;
1822 if (!ExtensionTabUtil::GetDefaultTab(browser
, &web_contents
, &tab_id
))
1826 execute_tab_id_
= tab_id
;
1827 details_
= details
.Pass();
1828 set_host_id(HostID(HostID::EXTENSIONS
, extension()->id()));
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(),
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(
1858 process
? process
->GetID() : -1,
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(),
1878 contents
&& browser
;
1883 return TabHelper::FromWebContents(contents
)->script_executor();
1886 bool ExecuteCodeInTabFunction::IsWebView() const {
1890 const GURL
& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1891 return GURL::EmptyGURL();
1894 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1898 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1899 const std::string
& error
,
1901 const base::ListValue
& result
) {
1903 SetResult(result
.DeepCopy());
1904 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error
, on_url
, result
);
1907 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1911 content::WebContents
* ZoomAPIFunction::GetWebContents(int tab_id
) {
1912 content::WebContents
* web_contents
= NULL
;
1914 // We assume this call leaves web_contents unchanged if it is unsuccessful.
1917 include_incognito(),
1918 NULL
/* ignore Browser* output */,
1919 NULL
/* ignore TabStripModel* output */,
1921 NULL
/* ignore int tab_index output */,
1924 Browser
* browser
= GetCurrentBrowser();
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
);
1943 GURL
url(web_contents
->GetVisibleURL());
1944 if (PermissionsData::IsRestrictedUrl(url
, extension(), &error_
))
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
;
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
);
1976 ZoomController::FromWebContents(web_contents
)->GetZoomLevel();
1977 double zoom_factor
= content::ZoomLevelToZoomFactor(zoom_level
);
1978 results_
= tabs::GetZoom::Results::Create(zoom_factor
);
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
);
1995 GURL
url(web_contents
->GetVisibleURL());
1996 if (PermissionsData::IsRestrictedUrl(url
, extension(), &error_
))
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
;
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
;
2018 case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB
:
2019 zoom_mode
= ZoomController::ZOOM_MODE_ISOLATED
;
2022 case tabs::ZOOM_SETTINGS_MODE_MANUAL
:
2023 zoom_mode
= ZoomController::ZOOM_MODE_MANUAL
;
2025 case tabs::ZOOM_SETTINGS_MODE_DISABLED
:
2026 zoom_mode
= ZoomController::ZOOM_MODE_DISABLED
;
2029 ZoomController::FromWebContents(web_contents
)->SetZoomMode(zoom_mode
);
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
);
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
);
2058 } // namespace extensions