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/logging.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/browser/apps/scoped_keep_alive.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
25 #include "chrome/browser/extensions/api/tabs/windows_util.h"
26 #include "chrome/browser/extensions/extension_service.h"
27 #include "chrome/browser/extensions/extension_tab_util.h"
28 #include "chrome/browser/extensions/tab_helper.h"
29 #include "chrome/browser/extensions/window_controller.h"
30 #include "chrome/browser/extensions/window_controller_list.h"
31 #include "chrome/browser/prefs/incognito_mode_prefs.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/sessions/session_tab_helper.h"
34 #include "chrome/browser/translate/chrome_translate_client.h"
35 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
36 #include "chrome/browser/ui/browser.h"
37 #include "chrome/browser/ui/browser_commands.h"
38 #include "chrome/browser/ui/browser_finder.h"
39 #include "chrome/browser/ui/browser_iterator.h"
40 #include "chrome/browser/ui/browser_navigator.h"
41 #include "chrome/browser/ui/browser_tabstrip.h"
42 #include "chrome/browser/ui/browser_window.h"
43 #include "chrome/browser/ui/host_desktop.h"
44 #include "chrome/browser/ui/panels/panel_manager.h"
45 #include "chrome/browser/ui/tabs/tab_strip_model.h"
46 #include "chrome/browser/ui/window_sizer/window_sizer.h"
47 #include "chrome/browser/ui/zoom/zoom_controller.h"
48 #include "chrome/browser/web_applications/web_app.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
51 #include "chrome/common/extensions/api/tabs.h"
52 #include "chrome/common/extensions/api/windows.h"
53 #include "chrome/common/extensions/extension_constants.h"
54 #include "chrome/common/pref_names.h"
55 #include "chrome/common/url_constants.h"
56 #include "components/pref_registry/pref_registry_syncable.h"
57 #include "components/translate/core/browser/language_state.h"
58 #include "components/translate/core/common/language_detection_details.h"
59 #include "content/public/browser/navigation_controller.h"
60 #include "content/public/browser/navigation_entry.h"
61 #include "content/public/browser/notification_details.h"
62 #include "content/public/browser/notification_source.h"
63 #include "content/public/browser/render_process_host.h"
64 #include "content/public/browser/render_view_host.h"
65 #include "content/public/browser/render_widget_host_view.h"
66 #include "content/public/browser/web_contents.h"
67 #include "content/public/common/url_constants.h"
68 #include "extensions/browser/app_window/app_window.h"
69 #include "extensions/browser/extension_function_dispatcher.h"
70 #include "extensions/browser/extension_function_util.h"
71 #include "extensions/browser/extension_host.h"
72 #include "extensions/browser/file_reader.h"
73 #include "extensions/browser/script_executor.h"
74 #include "extensions/common/api/extension_types.h"
75 #include "extensions/common/constants.h"
76 #include "extensions/common/error_utils.h"
77 #include "extensions/common/extension.h"
78 #include "extensions/common/manifest_constants.h"
79 #include "extensions/common/message_bundle.h"
80 #include "extensions/common/permissions/permissions_data.h"
81 #include "extensions/common/user_script.h"
82 #include "skia/ext/image_operations.h"
83 #include "skia/ext/platform_canvas.h"
84 #include "third_party/skia/include/core/SkBitmap.h"
85 #include "ui/base/models/list_selection_model.h"
86 #include "ui/base/ui_base_types.h"
89 #include "ash/ash_switches.h"
90 #include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
91 #include "extensions/browser/app_window/app_window_registry.h"
94 using content::BrowserThread;
95 using content::NavigationController;
96 using content::NavigationEntry;
97 using content::OpenURLParams;
98 using content::Referrer;
99 using content::WebContents;
101 namespace extensions {
103 namespace windows = api::windows;
104 namespace keys = tabs_constants;
105 namespace tabs = api::tabs;
107 using core_api::extension_types::InjectDetails;
111 bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction* function,
117 ExtensionTabUtil::GetBrowserFromWindowID(function, window_id, &error);
119 function->SetError(error);
127 bool GetBrowserFromWindowID(ChromeExtensionFunctionDetails* details,
133 ExtensionTabUtil::GetBrowserFromWindowID(*details, window_id, &error);
135 details->function()->SetError(error);
143 // |error_message| can optionally be passed in and will be set with an
144 // appropriate message if the tab cannot be found by id.
145 bool GetTabById(int tab_id,
147 bool include_incognito,
149 TabStripModel** tab_strip,
150 content::WebContents** contents,
152 std::string* error_message) {
153 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
154 browser, tab_strip, contents, tab_index)) {
159 *error_message = ErrorUtils::FormatErrorMessage(
160 keys::kTabNotFoundError, base::IntToString(tab_id));
166 // Returns true if either |boolean| is a null pointer, or if |*boolean| and
167 // |value| are equal. This function is used to check if a tab's parameters match
168 // those of the browser.
169 bool MatchesBool(bool* boolean, bool value) {
170 return !boolean || *boolean == value;
173 template <typename T>
174 void AssignOptionalValue(const scoped_ptr<T>& source,
175 scoped_ptr<T>& destination) {
177 destination.reset(new T(*source.get()));
183 void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,
184 api::tabs::ZoomSettings* zoom_settings) {
185 DCHECK(zoom_settings);
187 case ZoomController::ZOOM_MODE_DEFAULT:
188 zoom_settings->mode = api::tabs::ZoomSettings::MODE_AUTOMATIC;
189 zoom_settings->scope = api::tabs::ZoomSettings::SCOPE_PER_ORIGIN;
191 case ZoomController::ZOOM_MODE_ISOLATED:
192 zoom_settings->mode = api::tabs::ZoomSettings::MODE_AUTOMATIC;
193 zoom_settings->scope = api::tabs::ZoomSettings::SCOPE_PER_TAB;
195 case ZoomController::ZOOM_MODE_MANUAL:
196 zoom_settings->mode = api::tabs::ZoomSettings::MODE_MANUAL;
197 zoom_settings->scope = api::tabs::ZoomSettings::SCOPE_PER_TAB;
199 case ZoomController::ZOOM_MODE_DISABLED:
200 zoom_settings->mode = api::tabs::ZoomSettings::MODE_DISABLED;
201 zoom_settings->scope = api::tabs::ZoomSettings::SCOPE_PER_TAB;
206 // Windows ---------------------------------------------------------------------
208 bool WindowsGetFunction::RunSync() {
209 scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
210 EXTENSION_FUNCTION_VALIDATE(params.get());
212 bool populate_tabs = false;
213 if (params->get_info.get() && params->get_info->populate.get())
214 populate_tabs = *params->get_info->populate;
216 WindowController* controller;
217 if (!windows_util::GetWindowFromWindowID(this,
224 SetResult(controller->CreateWindowValueWithTabs(extension()));
226 SetResult(controller->CreateWindowValue());
230 bool WindowsGetCurrentFunction::RunSync() {
231 scoped_ptr<windows::GetCurrent::Params> params(
232 windows::GetCurrent::Params::Create(*args_));
233 EXTENSION_FUNCTION_VALIDATE(params.get());
235 bool populate_tabs = false;
236 if (params->get_info.get() && params->get_info->populate.get())
237 populate_tabs = *params->get_info->populate;
239 WindowController* controller;
240 if (!windows_util::GetWindowFromWindowID(this,
241 extension_misc::kCurrentWindowId,
246 SetResult(controller->CreateWindowValueWithTabs(extension()));
248 SetResult(controller->CreateWindowValue());
252 bool WindowsGetLastFocusedFunction::RunSync() {
253 scoped_ptr<windows::GetLastFocused::Params> params(
254 windows::GetLastFocused::Params::Create(*args_));
255 EXTENSION_FUNCTION_VALIDATE(params.get());
257 bool populate_tabs = false;
258 if (params->get_info.get() && params->get_info->populate.get())
259 populate_tabs = *params->get_info->populate;
261 // Note: currently this returns the last active browser. If we decide to
262 // include other window types (e.g. panels), we will need to add logic to
263 // WindowControllerList that mirrors the active behavior of BrowserList.
264 Browser* browser = chrome::FindAnyBrowser(
265 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
266 if (!browser || !browser->window()) {
267 error_ = keys::kNoLastFocusedWindowError;
270 WindowController* controller =
271 browser->extension_window_controller();
273 SetResult(controller->CreateWindowValueWithTabs(extension()));
275 SetResult(controller->CreateWindowValue());
279 bool WindowsGetAllFunction::RunSync() {
280 scoped_ptr<windows::GetAll::Params> params(
281 windows::GetAll::Params::Create(*args_));
282 EXTENSION_FUNCTION_VALIDATE(params.get());
284 bool populate_tabs = false;
285 if (params->get_info.get() && params->get_info->populate.get())
286 populate_tabs = *params->get_info->populate;
288 base::ListValue* window_list = new base::ListValue();
289 const WindowControllerList::ControllerList& windows =
290 WindowControllerList::GetInstance()->windows();
291 for (WindowControllerList::ControllerList::const_iterator iter =
293 iter != windows.end(); ++iter) {
294 if (!this->CanOperateOnWindow(*iter))
297 window_list->Append((*iter)->CreateWindowValueWithTabs(extension()));
299 window_list->Append((*iter)->CreateWindowValue());
301 SetResult(window_list);
305 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
306 const windows::Create::Params::CreateData* create_data,
307 std::vector<GURL>* urls, bool* is_error) {
309 const IncognitoModePrefs::Availability incognito_availability =
310 IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
311 bool incognito = false;
312 if (create_data && create_data->incognito) {
313 incognito = *create_data->incognito;
314 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
315 error_ = keys::kIncognitoModeIsDisabled;
319 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
320 error_ = keys::kIncognitoModeIsForced;
324 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
325 // If incognito argument is not specified explicitly, we default to
326 // incognito when forced so by policy.
330 // Remove all URLs that are not allowed in an incognito session. Note that a
331 // ChromeOS guest session is not considered incognito in this case.
332 if (incognito && !GetProfile()->IsGuestSession()) {
333 std::string first_url_erased;
334 for (size_t i = 0; i < urls->size();) {
335 if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
338 if (first_url_erased.empty())
339 first_url_erased = (*urls)[i].spec();
340 urls->erase(urls->begin() + i);
343 if (urls->empty() && !first_url_erased.empty()) {
344 error_ = ErrorUtils::FormatErrorMessage(
345 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
353 bool WindowsCreateFunction::RunSync() {
354 scoped_ptr<windows::Create::Params> params(
355 windows::Create::Params::Create(*args_));
356 EXTENSION_FUNCTION_VALIDATE(params);
357 std::vector<GURL> urls;
358 TabStripModel* source_tab_strip = NULL;
361 windows::Create::Params::CreateData* create_data = params->create_data.get();
363 // Look for optional url.
364 if (create_data && create_data->url) {
365 std::vector<std::string> url_strings;
366 // First, get all the URLs the client wants to open.
367 if (create_data->url->as_string)
368 url_strings.push_back(*create_data->url->as_string);
369 else if (create_data->url->as_strings)
370 url_strings.swap(*create_data->url->as_strings);
372 // Second, resolve, validate and convert them to GURLs.
373 for (std::vector<std::string>::iterator i = url_strings.begin();
374 i != url_strings.end(); ++i) {
375 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(*i, extension());
376 if (!url.is_valid()) {
377 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
380 // Don't let the extension crash the browser or renderers.
381 if (ExtensionTabUtil::IsCrashURL(url)) {
382 error_ = keys::kNoCrashBrowserError;
389 // Look for optional tab id.
390 if (create_data && create_data->tab_id) {
391 // Find the tab. |source_tab_strip| and |tab_index| will later be used to
392 // move the tab into the created window.
393 if (!GetTabById(*create_data->tab_id,
404 Profile* window_profile = GetProfile();
405 Browser::Type window_type = Browser::TYPE_TABBED;
406 bool create_panel = false;
408 // panel_create_mode only applies if create_panel = true
409 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
411 gfx::Rect window_bounds;
413 bool saw_focus_key = false;
414 std::string extension_id;
416 // Decide whether we are opening a normal window or an incognito window.
417 bool is_error = true;
418 bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
421 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
424 if (open_incognito_window) {
425 window_profile = window_profile->GetOffTheRecordProfile();
429 // Figure out window type before figuring out bounds so that default
430 // bounds can be set according to the window type.
431 switch (create_data->type) {
432 case windows::Create::Params::CreateData::TYPE_POPUP:
433 window_type = Browser::TYPE_POPUP;
434 extension_id = extension()->id();
436 case windows::Create::Params::CreateData::TYPE_PANEL:
437 case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
438 extension_id = extension()->id();
439 bool use_panels = PanelManager::ShouldUsePanels(extension_id);
442 // Non-ash supports both docked and detached panel types.
443 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
445 windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
446 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
449 window_type = Browser::TYPE_POPUP;
453 case windows::Create::Params::CreateData::TYPE_NONE:
454 case windows::Create::Params::CreateData::TYPE_NORMAL:
457 error_ = keys::kInvalidWindowTypeError;
461 // Initialize default window bounds according to window type.
462 if (window_type == Browser::TYPE_TABBED ||
463 window_type == Browser::TYPE_POPUP ||
465 // Try to position the new browser relative to its originating
466 // browser window. The call offsets the bounds by kWindowTilePixels
467 // (defined in WindowSizer to be 10).
469 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
470 // GetBrowserWindowBounds will default to saved "default" values for
472 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
473 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
480 if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
481 window_bounds.set_origin(
482 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
485 // Any part of the bounds can optionally be set by the caller.
486 if (create_data->left)
487 window_bounds.set_x(*create_data->left);
489 if (create_data->top)
490 window_bounds.set_y(*create_data->top);
492 if (create_data->width)
493 window_bounds.set_width(*create_data->width);
495 if (create_data->height)
496 window_bounds.set_height(*create_data->height);
498 if (create_data->focused) {
499 focused = *create_data->focused;
500 saw_focus_key = true;
506 urls.push_back(GURL(chrome::kChromeUINewTabURL));
509 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
510 AppWindow::CreateParams create_params;
511 create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL;
512 create_params.window_spec.bounds = window_bounds;
513 create_params.focused = saw_focus_key && focused;
514 AppWindow* app_window = new AppWindow(
516 new ChromeAppDelegate(make_scoped_ptr(new ScopedKeepAlive)),
518 AshPanelContents* ash_panel_contents = new AshPanelContents(app_window);
519 app_window->Init(urls[0], ash_panel_contents, create_params);
520 SetResult(ash_panel_contents->GetExtensionWindowController()
521 ->CreateWindowValueWithTabs(extension()));
526 web_app::GenerateApplicationNameFromExtensionId(extension_id);
527 // Note: Panels ignore all but the first url provided.
528 Panel* panel = PanelManager::GetInstance()->CreatePanel(
529 title, window_profile, urls[0], window_bounds, panel_create_mode);
531 // Unlike other window types, Panels do not take focus by default.
532 if (!saw_focus_key || !focused)
533 panel->ShowInactive();
537 SetResult(panel->extension_window_controller()->CreateWindowValueWithTabs(
542 // Create a new BrowserWindow.
543 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
545 window_type = Browser::TYPE_POPUP;
546 Browser::CreateParams create_params(window_type, window_profile,
548 if (extension_id.empty()) {
549 create_params.initial_bounds = window_bounds;
551 create_params = Browser::CreateParams::CreateForApp(
552 web_app::GenerateApplicationNameFromExtensionId(extension_id),
553 false /* trusted_source */,
558 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
559 create_params.host_desktop_type = chrome::GetActiveDesktop();
561 Browser* new_window = new Browser(create_params);
563 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
564 WebContents* tab = chrome::AddSelectedTabWithURL(
565 new_window, *i, ui::PAGE_TRANSITION_LINK);
567 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
571 WebContents* contents = NULL;
572 // Move the tab into the created window only if it's an empty popup or it's
574 if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
575 window_type == Browser::TYPE_TABBED) {
576 if (source_tab_strip)
577 contents = source_tab_strip->DetachWebContentsAt(tab_index);
579 TabStripModel* target_tab_strip = new_window->tab_strip_model();
580 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
581 TabStripModel::ADD_NONE);
584 // Create a new tab if the created window is still empty. Don't create a new
585 // tab when it is intended to create an empty popup.
586 if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
587 chrome::NewTab(new_window);
589 chrome::SelectNumberedTab(new_window, 0);
591 // Unlike other window types, Panels do not take focus by default.
592 if (!saw_focus_key && create_panel)
596 new_window->window()->Show();
598 new_window->window()->ShowInactive();
600 if (new_window->profile()->IsOffTheRecord() &&
601 !GetProfile()->IsOffTheRecord() && !include_incognito()) {
602 // Don't expose incognito windows if extension itself works in non-incognito
603 // profile and CanCrossIncognito isn't allowed.
604 SetResult(base::Value::CreateNullValue());
607 new_window->extension_window_controller()->CreateWindowValueWithTabs(
614 bool WindowsUpdateFunction::RunSync() {
615 scoped_ptr<windows::Update::Params> params(
616 windows::Update::Params::Create(*args_));
617 EXTENSION_FUNCTION_VALIDATE(params);
619 WindowController* controller;
620 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
624 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change.
625 switch (params->update_info.state) {
626 case windows::Update::Params::UpdateInfo::STATE_NORMAL:
627 show_state = ui::SHOW_STATE_NORMAL;
629 case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
630 show_state = ui::SHOW_STATE_MINIMIZED;
632 case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
633 show_state = ui::SHOW_STATE_MAXIMIZED;
635 case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
636 show_state = ui::SHOW_STATE_FULLSCREEN;
638 case windows::Update::Params::UpdateInfo::STATE_NONE:
641 error_ = keys::kInvalidWindowStateError;
645 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
646 show_state != ui::SHOW_STATE_DEFAULT)
647 controller->SetFullscreenMode(false, extension()->url());
649 switch (show_state) {
650 case ui::SHOW_STATE_MINIMIZED:
651 controller->window()->Minimize();
653 case ui::SHOW_STATE_MAXIMIZED:
654 controller->window()->Maximize();
656 case ui::SHOW_STATE_FULLSCREEN:
657 if (controller->window()->IsMinimized() ||
658 controller->window()->IsMaximized())
659 controller->window()->Restore();
660 controller->SetFullscreenMode(true, extension()->url());
662 case ui::SHOW_STATE_NORMAL:
663 controller->window()->Restore();
670 if (controller->window()->IsMinimized())
671 bounds = controller->window()->GetRestoredBounds();
673 bounds = controller->window()->GetBounds();
674 bool set_bounds = false;
676 // Any part of the bounds can optionally be set by the caller.
677 if (params->update_info.left) {
678 bounds.set_x(*params->update_info.left);
682 if (params->update_info.top) {
683 bounds.set_y(*params->update_info.top);
687 if (params->update_info.width) {
688 bounds.set_width(*params->update_info.width);
692 if (params->update_info.height) {
693 bounds.set_height(*params->update_info.height);
698 if (show_state == ui::SHOW_STATE_MINIMIZED ||
699 show_state == ui::SHOW_STATE_MAXIMIZED ||
700 show_state == ui::SHOW_STATE_FULLSCREEN) {
701 error_ = keys::kInvalidWindowStateError;
704 // TODO(varkha): Updating bounds during a drag can cause problems and a more
705 // general solution is needed. See http://crbug.com/251813 .
706 controller->window()->SetBounds(bounds);
709 if (params->update_info.focused) {
710 if (*params->update_info.focused) {
711 if (show_state == ui::SHOW_STATE_MINIMIZED) {
712 error_ = keys::kInvalidWindowStateError;
715 controller->window()->Activate();
717 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
718 show_state == ui::SHOW_STATE_FULLSCREEN) {
719 error_ = keys::kInvalidWindowStateError;
722 controller->window()->Deactivate();
726 if (params->update_info.draw_attention)
727 controller->window()->FlashFrame(*params->update_info.draw_attention);
729 SetResult(controller->CreateWindowValue());
734 bool WindowsRemoveFunction::RunSync() {
735 scoped_ptr<windows::Remove::Params> params(
736 windows::Remove::Params::Create(*args_));
737 EXTENSION_FUNCTION_VALIDATE(params);
739 WindowController* controller;
740 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
744 WindowController::Reason reason;
745 if (!controller->CanClose(&reason)) {
746 if (reason == WindowController::REASON_NOT_EDITABLE)
747 error_ = keys::kTabStripNotEditableError;
750 controller->window()->Close();
754 // Tabs ------------------------------------------------------------------------
756 bool TabsGetSelectedFunction::RunSync() {
757 // windowId defaults to "current" window.
758 int window_id = extension_misc::kCurrentWindowId;
760 scoped_ptr<tabs::GetSelected::Params> params(
761 tabs::GetSelected::Params::Create(*args_));
762 EXTENSION_FUNCTION_VALIDATE(params.get());
763 if (params->window_id.get())
764 window_id = *params->window_id;
766 Browser* browser = NULL;
767 if (!GetBrowserFromWindowID(this, window_id, &browser))
770 TabStripModel* tab_strip = browser->tab_strip_model();
771 WebContents* contents = tab_strip->GetActiveWebContents();
773 error_ = keys::kNoSelectedTabError;
776 SetResult(ExtensionTabUtil::CreateTabValue(
777 contents, tab_strip, tab_strip->active_index(), extension()));
781 bool TabsGetAllInWindowFunction::RunSync() {
782 scoped_ptr<tabs::GetAllInWindow::Params> params(
783 tabs::GetAllInWindow::Params::Create(*args_));
784 EXTENSION_FUNCTION_VALIDATE(params.get());
785 // windowId defaults to "current" window.
786 int window_id = extension_misc::kCurrentWindowId;
787 if (params->window_id.get())
788 window_id = *params->window_id;
790 Browser* browser = NULL;
791 if (!GetBrowserFromWindowID(this, window_id, &browser))
794 SetResult(ExtensionTabUtil::CreateTabList(browser, extension()));
799 bool TabsQueryFunction::RunSync() {
800 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
801 EXTENSION_FUNCTION_VALIDATE(params.get());
803 bool loading_status_set = params->query_info.status !=
804 tabs::Query::Params::QueryInfo::STATUS_NONE;
805 bool loading = params->query_info.status ==
806 tabs::Query::Params::QueryInfo::STATUS_LOADING;
808 URLPatternSet url_patterns;
809 if (params->query_info.url.get()) {
810 std::vector<std::string> url_pattern_strings;
811 if (params->query_info.url->as_string)
812 url_pattern_strings.push_back(*params->query_info.url->as_string);
813 else if (params->query_info.url->as_strings)
814 url_pattern_strings.swap(*params->query_info.url->as_strings);
815 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
816 // not grant access to the content of the tabs, only to seeing their URLs
818 if (!url_patterns.Populate(url_pattern_strings, URLPattern::SCHEME_ALL,
825 if (params->query_info.title.get())
826 title = *params->query_info.title;
828 int window_id = extension_misc::kUnknownWindowId;
829 if (params->query_info.window_id.get())
830 window_id = *params->query_info.window_id;
833 if (params->query_info.index.get())
834 index = *params->query_info.index;
836 std::string window_type;
837 if (params->query_info.window_type !=
838 tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) {
839 window_type = tabs::Query::Params::QueryInfo::ToString(
840 params->query_info.window_type);
843 base::ListValue* result = new base::ListValue();
844 Browser* last_active_browser = chrome::FindAnyBrowser(
845 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
846 Browser* current_browser = GetCurrentBrowser();
847 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
848 Browser* browser = *it;
849 if (!GetProfile()->IsSameProfile(browser->profile()))
852 if (!browser->window())
855 if (!include_incognito() && GetProfile() != browser->profile())
858 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
861 if (window_id == extension_misc::kCurrentWindowId &&
862 browser != current_browser) {
866 if (!MatchesBool(params->query_info.current_window.get(),
867 browser == current_browser)) {
871 if (!MatchesBool(params->query_info.last_focused_window.get(),
872 browser == last_active_browser)) {
876 if (!window_type.empty() &&
878 browser->extension_window_controller()->GetWindowTypeText()) {
882 TabStripModel* tab_strip = browser->tab_strip_model();
883 for (int i = 0; i < tab_strip->count(); ++i) {
884 WebContents* web_contents = tab_strip->GetWebContentsAt(i);
886 if (index > -1 && i != index)
889 if (!MatchesBool(params->query_info.highlighted.get(),
890 tab_strip->IsTabSelected(i))) {
894 if (!MatchesBool(params->query_info.active.get(),
895 i == tab_strip->active_index())) {
899 if (!MatchesBool(params->query_info.pinned.get(),
900 tab_strip->IsTabPinned(i))) {
904 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
905 base::UTF8ToUTF16(title)))
908 if (!url_patterns.is_empty() &&
909 !url_patterns.MatchesURL(web_contents->GetURL()))
912 if (loading_status_set && loading != web_contents->IsLoading())
915 result->Append(ExtensionTabUtil::CreateTabValue(
916 web_contents, tab_strip, i, extension()));
924 bool TabsCreateFunction::RunSync() {
925 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
926 EXTENSION_FUNCTION_VALIDATE(params.get());
928 ExtensionTabUtil::OpenTabParams options;
929 AssignOptionalValue(params->create_properties.window_id, options.window_id);
930 AssignOptionalValue(params->create_properties.opener_tab_id,
931 options.opener_tab_id);
932 AssignOptionalValue(params->create_properties.selected, options.active);
933 // The 'active' property has replaced the 'selected' property.
934 AssignOptionalValue(params->create_properties.active, options.active);
935 AssignOptionalValue(params->create_properties.pinned, options.pinned);
936 AssignOptionalValue(params->create_properties.index, options.index);
937 AssignOptionalValue(params->create_properties.url, options.url);
940 scoped_ptr<base::DictionaryValue> result(
941 ExtensionTabUtil::OpenTab(this, options, &error));
947 // Return data about the newly created tab.
948 if (has_callback()) {
949 SetResult(result.release());
954 bool TabsDuplicateFunction::RunSync() {
955 scoped_ptr<tabs::Duplicate::Params> params(
956 tabs::Duplicate::Params::Create(*args_));
957 EXTENSION_FUNCTION_VALIDATE(params.get());
958 int tab_id = params->tab_id;
960 Browser* browser = NULL;
961 TabStripModel* tab_strip = NULL;
963 if (!GetTabById(tab_id,
974 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
978 // Duplicated tab may not be in the same window as the original, so find
979 // the window and the tab.
980 TabStripModel* new_tab_strip = NULL;
981 int new_tab_index = -1;
982 ExtensionTabUtil::GetTabStripModel(new_contents,
985 if (!new_tab_strip || new_tab_index == -1) {
989 // Return data about the newly created tab.
990 SetResult(ExtensionTabUtil::CreateTabValue(
991 new_contents, new_tab_strip, new_tab_index, extension()));
996 bool TabsGetFunction::RunSync() {
997 scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
998 EXTENSION_FUNCTION_VALIDATE(params.get());
999 int tab_id = params->tab_id;
1001 TabStripModel* tab_strip = NULL;
1002 WebContents* contents = NULL;
1004 if (!GetTabById(tab_id,
1006 include_incognito(),
1014 SetResult(ExtensionTabUtil::CreateTabValue(
1015 contents, tab_strip, tab_index, extension()));
1019 bool TabsGetCurrentFunction::RunSync() {
1020 DCHECK(dispatcher());
1022 // Return the caller, if it's a tab. If not the result isn't an error but an
1023 // empty tab (hence returning true).
1024 WebContents* caller_contents =
1025 WebContents::FromRenderViewHost(render_view_host());
1026 if (caller_contents && ExtensionTabUtil::GetTabId(caller_contents) >= 0)
1027 SetResult(ExtensionTabUtil::CreateTabValue(caller_contents, extension()));
1032 bool TabsHighlightFunction::RunSync() {
1033 scoped_ptr<tabs::Highlight::Params> params(
1034 tabs::Highlight::Params::Create(*args_));
1035 EXTENSION_FUNCTION_VALIDATE(params.get());
1037 // Get the window id from the params; default to current window if omitted.
1038 int window_id = extension_misc::kCurrentWindowId;
1039 if (params->highlight_info.window_id.get())
1040 window_id = *params->highlight_info.window_id;
1042 Browser* browser = NULL;
1043 if (!GetBrowserFromWindowID(this, window_id, &browser))
1046 TabStripModel* tabstrip = browser->tab_strip_model();
1047 ui::ListSelectionModel selection;
1048 int active_index = -1;
1050 if (params->highlight_info.tabs.as_integers) {
1051 std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1052 // Create a new selection model as we read the list of tab indices.
1053 for (size_t i = 0; i < tab_indices.size(); ++i) {
1054 if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1058 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1059 if (!HighlightTab(tabstrip,
1062 *params->highlight_info.tabs.as_integer)) {
1067 // Make sure they actually specified tabs to select.
1068 if (selection.empty()) {
1069 error_ = keys::kNoHighlightedTabError;
1073 selection.set_active(active_index);
1074 browser->tab_strip_model()->SetSelectionFromModel(selection);
1075 SetResult(browser->extension_window_controller()->CreateWindowValueWithTabs(
1080 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1081 ui::ListSelectionModel* selection,
1084 // Make sure the index is in range.
1085 if (!tabstrip->ContainsIndex(index)) {
1086 error_ = ErrorUtils::FormatErrorMessage(
1087 keys::kTabIndexNotFoundError, base::IntToString(index));
1091 // By default, we make the first tab in the list active.
1092 if (*active_index == -1)
1093 *active_index = index;
1095 selection->AddIndexToSelection(index);
1099 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1102 bool TabsUpdateFunction::RunAsync() {
1103 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1104 EXTENSION_FUNCTION_VALIDATE(params.get());
1107 WebContents* contents = NULL;
1108 if (!params->tab_id.get()) {
1109 Browser* browser = GetCurrentBrowser();
1111 error_ = keys::kNoCurrentWindowError;
1114 contents = browser->tab_strip_model()->GetActiveWebContents();
1116 error_ = keys::kNoSelectedTabError;
1119 tab_id = SessionTabHelper::IdForTab(contents);
1121 tab_id = *params->tab_id;
1125 TabStripModel* tab_strip = NULL;
1126 if (!GetTabById(tab_id,
1128 include_incognito(),
1137 web_contents_ = contents;
1139 // TODO(rafaelw): handle setting remaining tab properties:
1143 // Navigate the tab to a new location if the url is different.
1144 bool is_async = false;
1145 if (params->update_properties.url.get() &&
1146 !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1150 bool active = false;
1151 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1152 // Move tab selection management up to window.
1153 if (params->update_properties.selected.get())
1154 active = *params->update_properties.selected;
1156 // The 'active' property has replaced 'selected'.
1157 if (params->update_properties.active.get())
1158 active = *params->update_properties.active;
1161 if (tab_strip->active_index() != tab_index) {
1162 tab_strip->ActivateTabAt(tab_index, false);
1163 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1167 if (params->update_properties.highlighted.get()) {
1168 bool highlighted = *params->update_properties.highlighted;
1169 if (highlighted != tab_strip->IsTabSelected(tab_index))
1170 tab_strip->ToggleSelectionAt(tab_index);
1173 if (params->update_properties.pinned.get()) {
1174 bool pinned = *params->update_properties.pinned;
1175 tab_strip->SetTabPinned(tab_index, pinned);
1177 // Update the tab index because it may move when being pinned.
1178 tab_index = tab_strip->GetIndexOfWebContents(contents);
1181 if (params->update_properties.opener_tab_id.get()) {
1182 int opener_id = *params->update_properties.opener_tab_id;
1184 WebContents* opener_contents = NULL;
1185 if (!ExtensionTabUtil::GetTabById(opener_id,
1187 include_incognito(),
1194 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1204 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1208 ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, extension());
1210 if (!url.is_valid()) {
1211 error_ = ErrorUtils::FormatErrorMessage(
1212 keys::kInvalidUrlError, url_string);
1216 // Don't let the extension crash the browser or renderers.
1217 if (ExtensionTabUtil::IsCrashURL(url)) {
1218 error_ = keys::kNoCrashBrowserError;
1222 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1223 // we need to check host permissions before allowing them.
1224 if (url.SchemeIs(url::kJavaScriptScheme)) {
1225 content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1226 if (!extension()->permissions_data()->CanAccessPage(
1228 web_contents_->GetURL(),
1229 web_contents_->GetURL(),
1231 process ? process->GetID() : -1,
1236 TabHelper::FromWebContents(web_contents_)->script_executor()->ExecuteScript(
1238 ScriptExecutor::JAVASCRIPT,
1240 ScriptExecutor::TOP_FRAME,
1241 ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
1242 UserScript::DOCUMENT_IDLE,
1243 ScriptExecutor::MAIN_WORLD,
1244 ScriptExecutor::DEFAULT_PROCESS,
1248 ScriptExecutor::NO_RESULT,
1249 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1255 web_contents_->GetController().LoadURL(
1256 url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1258 // The URL of a tab contents never actually changes to a JavaScript URL, so
1259 // this check only makes sense in other cases.
1260 if (!url.SchemeIs(url::kJavaScriptScheme))
1261 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1266 void TabsUpdateFunction::PopulateResult() {
1267 if (!has_callback())
1270 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, extension()));
1273 void TabsUpdateFunction::OnExecuteCodeFinished(
1274 const std::string& error,
1276 const base::ListValue& script_result) {
1281 SendResponse(error.empty());
1284 bool TabsMoveFunction::RunSync() {
1285 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1286 EXTENSION_FUNCTION_VALIDATE(params.get());
1288 int new_index = params->move_properties.index;
1289 int* window_id = params->move_properties.window_id.get();
1290 scoped_ptr<base::ListValue> tab_values(new base::ListValue());
1292 size_t num_tabs = 0;
1293 if (params->tab_ids.as_integers) {
1294 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1295 num_tabs = tab_ids.size();
1296 for (size_t i = 0; i < tab_ids.size(); ++i) {
1297 if (!MoveTab(tab_ids[i], &new_index, i, tab_values.get(), window_id))
1301 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1303 if (!MoveTab(*params->tab_ids.as_integer,
1312 if (!has_callback())
1315 if (num_tabs == 0) {
1316 error_ = "No tabs given.";
1318 } else if (num_tabs == 1) {
1319 scoped_ptr<base::Value> value;
1320 CHECK(tab_values.get()->Remove(0, &value));
1321 SetResult(value.release());
1323 // Only return the results as an array if there are multiple tabs.
1324 SetResult(tab_values.release());
1330 bool TabsMoveFunction::MoveTab(int tab_id,
1333 base::ListValue* tab_values,
1335 Browser* source_browser = NULL;
1336 TabStripModel* source_tab_strip = NULL;
1337 WebContents* contents = NULL;
1339 if (!GetTabById(tab_id,
1341 include_incognito(),
1350 // Don't let the extension move the tab if the user is dragging tabs.
1351 if (!source_browser->window()->IsTabStripEditable()) {
1352 error_ = keys::kTabStripNotEditableError;
1356 // Insert the tabs one after another.
1357 *new_index += iteration;
1360 Browser* target_browser = NULL;
1362 if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1365 if (!target_browser->window()->IsTabStripEditable()) {
1366 error_ = keys::kTabStripNotEditableError;
1370 if (!target_browser->is_type_tabbed()) {
1371 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1375 if (target_browser->profile() != source_browser->profile()) {
1376 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1380 // If windowId is different from the current window, move between windows.
1381 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1382 ExtensionTabUtil::GetWindowId(source_browser)) {
1383 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1384 WebContents* web_contents =
1385 source_tab_strip->DetachWebContentsAt(tab_index);
1386 if (!web_contents) {
1387 error_ = ErrorUtils::FormatErrorMessage(
1388 keys::kTabNotFoundError, base::IntToString(tab_id));
1392 // Clamp move location to the last position.
1393 // This is ">" because it can append to a new index position.
1394 // -1 means set the move location to the last position.
1395 if (*new_index > target_tab_strip->count() || *new_index < 0)
1396 *new_index = target_tab_strip->count();
1398 target_tab_strip->InsertWebContentsAt(
1399 *new_index, web_contents, TabStripModel::ADD_NONE);
1401 if (has_callback()) {
1402 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1403 web_contents, target_tab_strip, *new_index, extension()));
1410 // Perform a simple within-window move.
1411 // Clamp move location to the last position.
1412 // This is ">=" because the move must be to an existing location.
1413 // -1 means set the move location to the last position.
1414 if (*new_index >= source_tab_strip->count() || *new_index < 0)
1415 *new_index = source_tab_strip->count() - 1;
1417 if (*new_index != tab_index)
1418 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1420 if (has_callback()) {
1421 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1422 contents, source_tab_strip, *new_index, extension()));
1428 bool TabsReloadFunction::RunSync() {
1429 scoped_ptr<tabs::Reload::Params> params(
1430 tabs::Reload::Params::Create(*args_));
1431 EXTENSION_FUNCTION_VALIDATE(params.get());
1433 bool bypass_cache = false;
1434 if (params->reload_properties.get() &&
1435 params->reload_properties->bypass_cache.get()) {
1436 bypass_cache = *params->reload_properties->bypass_cache;
1439 content::WebContents* web_contents = NULL;
1441 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1442 // in the current window.
1443 if (!params->tab_id.get()) {
1444 Browser* browser = GetCurrentBrowser();
1446 error_ = keys::kNoCurrentWindowError;
1450 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1453 int tab_id = *params->tab_id;
1455 Browser* browser = NULL;
1456 if (!GetTabById(tab_id,
1458 include_incognito(),
1468 if (web_contents->ShowingInterstitialPage()) {
1469 // This does as same as Browser::ReloadInternal.
1470 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1471 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1472 ui::PAGE_TRANSITION_RELOAD, false);
1473 GetCurrentBrowser()->OpenURL(params);
1474 } else if (bypass_cache) {
1475 web_contents->GetController().ReloadIgnoringCache(true);
1477 web_contents->GetController().Reload(true);
1483 bool TabsRemoveFunction::RunSync() {
1484 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1485 EXTENSION_FUNCTION_VALIDATE(params.get());
1487 if (params->tab_ids.as_integers) {
1488 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1489 for (size_t i = 0; i < tab_ids.size(); ++i) {
1490 if (!RemoveTab(tab_ids[i]))
1494 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1495 if (!RemoveTab(*params->tab_ids.as_integer.get()))
1501 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1502 Browser* browser = NULL;
1503 WebContents* contents = NULL;
1504 if (!GetTabById(tab_id,
1506 include_incognito(),
1515 // Don't let the extension remove a tab if the user is dragging tabs around.
1516 if (!browser->window()->IsTabStripEditable()) {
1517 error_ = keys::kTabStripNotEditableError;
1520 // There's a chance that the tab is being dragged, or we're in some other
1521 // nested event loop. This code path ensures that the tab is safely closed
1522 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1528 TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
1529 : chrome_details_(this) {
1532 bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1533 PrefService* service = chrome_details_.GetProfile()->GetPrefs();
1534 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1535 error_ = keys::kScreenshotsDisabled;
1541 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1542 Browser* browser = NULL;
1543 if (!GetBrowserFromWindowID(&chrome_details_, window_id, &browser))
1546 WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1548 error_ = keys::kInternalVisibleTabCaptureError;
1552 if (!extension()->permissions_data()->CanCaptureVisiblePage(
1553 SessionTabHelper::IdForTab(contents), &error_)) {
1559 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1560 error_ = keys::kInternalVisibleTabCaptureError;
1561 SendResponse(false);
1564 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1565 user_prefs::PrefRegistrySyncable* registry) {
1566 registry->RegisterBooleanPref(
1567 prefs::kDisableScreenshots,
1569 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1572 bool TabsDetectLanguageFunction::RunAsync() {
1573 scoped_ptr<tabs::DetectLanguage::Params> params(
1574 tabs::DetectLanguage::Params::Create(*args_));
1575 EXTENSION_FUNCTION_VALIDATE(params.get());
1578 Browser* browser = NULL;
1579 WebContents* contents = NULL;
1581 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1582 // in the current window.
1583 if (params->tab_id.get()) {
1584 tab_id = *params->tab_id;
1585 if (!GetTabById(tab_id,
1587 include_incognito(),
1595 if (!browser || !contents)
1598 browser = GetCurrentBrowser();
1601 contents = browser->tab_strip_model()->GetActiveWebContents();
1606 if (contents->GetController().NeedsReload()) {
1607 // If the tab hasn't been loaded, don't wait for the tab to load.
1608 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1612 AddRef(); // Balanced in GotLanguage().
1614 ChromeTranslateClient* chrome_translate_client =
1615 ChromeTranslateClient::FromWebContents(contents);
1616 if (!chrome_translate_client->GetLanguageState()
1617 .original_language()
1619 // Delay the callback invocation until after the current JS call has
1621 base::MessageLoop::current()->PostTask(
1624 &TabsDetectLanguageFunction::GotLanguage,
1626 chrome_translate_client->GetLanguageState().original_language()));
1629 // The tab contents does not know its language yet. Let's wait until it
1630 // receives it, or until the tab is closed/navigates to some other page.
1631 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1632 content::Source<WebContents>(contents));
1634 this, chrome::NOTIFICATION_TAB_CLOSING,
1635 content::Source<NavigationController>(&(contents->GetController())));
1637 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1638 content::Source<NavigationController>(&(contents->GetController())));
1642 void TabsDetectLanguageFunction::Observe(
1644 const content::NotificationSource& source,
1645 const content::NotificationDetails& details) {
1646 std::string language;
1647 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1648 const translate::LanguageDetectionDetails* lang_det_details =
1649 content::Details<const translate::LanguageDetectionDetails>(details)
1651 language = lang_det_details->adopted_language;
1654 registrar_.RemoveAll();
1656 // Call GotLanguage in all cases as we want to guarantee the callback is
1657 // called for every API call the extension made.
1658 GotLanguage(language);
1661 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1662 SetResult(new base::StringValue(language.c_str()));
1665 Release(); // Balanced in Run()
1668 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1669 : chrome_details_(this), execute_tab_id_(-1) {
1672 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1674 bool ExecuteCodeInTabFunction::HasPermission() {
1676 extension_->permissions_data()->HasAPIPermissionForTab(
1677 execute_tab_id_, APIPermission::kTab)) {
1680 return ExtensionFunction::HasPermission();
1683 bool ExecuteCodeInTabFunction::Init() {
1687 // |tab_id| is optional so it's ok if it's not there.
1689 if (args_->GetInteger(0, &tab_id))
1690 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1692 // |details| are not optional.
1693 base::DictionaryValue* details_value = NULL;
1694 if (!args_->GetDictionary(1, &details_value))
1696 scoped_ptr<InjectDetails> details(new InjectDetails());
1697 if (!InjectDetails::Populate(*details_value, details.get()))
1700 // If the tab ID wasn't given then it needs to be converted to the
1701 // currently active tab's ID.
1703 Browser* browser = chrome_details_.GetCurrentBrowser();
1706 content::WebContents* web_contents = NULL;
1707 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1711 execute_tab_id_ = tab_id;
1712 details_ = details.Pass();
1716 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1717 content::WebContents* contents = NULL;
1719 // If |tab_id| is specified, look for the tab. Otherwise default to selected
1720 // tab in the current window.
1721 CHECK_GE(execute_tab_id_, 0);
1722 if (!GetTabById(execute_tab_id_,
1723 chrome_details_.GetProfile(),
1724 include_incognito(),
1735 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1736 // we check again in the renderer.
1737 content::RenderProcessHost* process = contents->GetRenderProcessHost();
1738 if (!extension()->permissions_data()->CanAccessPage(
1743 process ? process->GetID() : -1,
1751 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1752 Browser* browser = NULL;
1753 content::WebContents* contents = NULL;
1755 bool success = GetTabById(execute_tab_id_,
1756 chrome_details_.GetProfile(),
1757 include_incognito(),
1763 contents && browser;
1768 return TabHelper::FromWebContents(contents)->script_executor();
1771 bool ExecuteCodeInTabFunction::IsWebView() const {
1775 const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1776 return GURL::EmptyGURL();
1779 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1783 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1784 const std::string& error,
1786 const base::ListValue& result) {
1788 SetResult(result.DeepCopy());
1789 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_url, result);
1792 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1796 content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
1797 content::WebContents* web_contents = NULL;
1799 // We assume this call leaves web_contents unchanged if it is unsuccessful.
1802 include_incognito(),
1803 NULL /* ignore Browser* output */,
1804 NULL /* ignore TabStripModel* output */,
1806 NULL /* ignore int tab_index output */,
1809 Browser* browser = GetCurrentBrowser();
1811 error_ = keys::kNoCurrentWindowError;
1812 else if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1813 error_ = keys::kNoSelectedTabError;
1815 return web_contents;
1818 bool TabsSetZoomFunction::RunAsync() {
1819 scoped_ptr<tabs::SetZoom::Params> params(
1820 tabs::SetZoom::Params::Create(*args_));
1821 EXTENSION_FUNCTION_VALIDATE(params);
1823 int tab_id = params->tab_id ? *params->tab_id : -1;
1824 WebContents* web_contents = GetWebContents(tab_id);
1828 GURL url(web_contents->GetVisibleURL());
1829 if (PermissionsData::IsRestrictedUrl(url, url, extension(), &error_))
1832 ZoomController* zoom_controller =
1833 ZoomController::FromWebContents(web_contents);
1834 double zoom_level = content::ZoomFactorToZoomLevel(params->zoom_factor);
1836 if (!zoom_controller->SetZoomLevelByExtension(zoom_level, extension())) {
1837 // Tried to zoom a tab in disabled mode.
1838 error_ = keys::kCannotZoomDisabledTabError;
1846 bool TabsGetZoomFunction::RunAsync() {
1847 scoped_ptr<tabs::GetZoom::Params> params(
1848 tabs::GetZoom::Params::Create(*args_));
1849 EXTENSION_FUNCTION_VALIDATE(params);
1851 int tab_id = params->tab_id ? *params->tab_id : -1;
1852 WebContents* web_contents = GetWebContents(tab_id);
1857 ZoomController::FromWebContents(web_contents)->GetZoomLevel();
1858 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
1859 results_ = tabs::GetZoom::Results::Create(zoom_factor);
1864 bool TabsSetZoomSettingsFunction::RunAsync() {
1865 using api::tabs::ZoomSettings;
1867 scoped_ptr<tabs::SetZoomSettings::Params> params(
1868 tabs::SetZoomSettings::Params::Create(*args_));
1869 EXTENSION_FUNCTION_VALIDATE(params);
1871 int tab_id = params->tab_id ? *params->tab_id : -1;
1872 WebContents* web_contents = GetWebContents(tab_id);
1876 GURL url(web_contents->GetVisibleURL());
1877 if (PermissionsData::IsRestrictedUrl(url, url, extension(), &error_))
1880 // "per-origin" scope is only available in "automatic" mode.
1881 if (params->zoom_settings.scope == ZoomSettings::SCOPE_PER_ORIGIN &&
1882 params->zoom_settings.mode != ZoomSettings::MODE_AUTOMATIC &&
1883 params->zoom_settings.mode != ZoomSettings::MODE_NONE) {
1884 error_ = keys::kPerOriginOnlyInAutomaticError;
1888 // Determine the correct internal zoom mode to set |web_contents| to from the
1889 // user-specified |zoom_settings|.
1890 ZoomController::ZoomMode zoom_mode = ZoomController::ZOOM_MODE_DEFAULT;
1891 switch (params->zoom_settings.mode) {
1892 case ZoomSettings::MODE_NONE:
1893 case ZoomSettings::MODE_AUTOMATIC:
1894 switch (params->zoom_settings.scope) {
1895 case ZoomSettings::SCOPE_NONE:
1896 case ZoomSettings::SCOPE_PER_ORIGIN:
1897 zoom_mode = ZoomController::ZOOM_MODE_DEFAULT;
1899 case ZoomSettings::SCOPE_PER_TAB:
1900 zoom_mode = ZoomController::ZOOM_MODE_ISOLATED;
1903 case ZoomSettings::MODE_MANUAL:
1904 zoom_mode = ZoomController::ZOOM_MODE_MANUAL;
1906 case ZoomSettings::MODE_DISABLED:
1907 zoom_mode = ZoomController::ZOOM_MODE_DISABLED;
1910 ZoomController::FromWebContents(web_contents)->SetZoomMode(zoom_mode);
1916 bool TabsGetZoomSettingsFunction::RunAsync() {
1917 scoped_ptr<tabs::GetZoomSettings::Params> params(
1918 tabs::GetZoomSettings::Params::Create(*args_));
1919 EXTENSION_FUNCTION_VALIDATE(params);
1921 int tab_id = params->tab_id ? *params->tab_id : -1;
1922 WebContents* web_contents = GetWebContents(tab_id);
1925 ZoomController* zoom_controller =
1926 ZoomController::FromWebContents(web_contents);
1928 ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
1929 api::tabs::ZoomSettings zoom_settings;
1930 ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
1932 results_ = api::tabs::GetZoomSettings::Results::Create(zoom_settings);
1937 } // namespace extensions