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 "apps/shell_window.h"
12 #include "base/base64.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/logging.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/stl_util.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 "chrome/browser/chrome_notification_types.h"
26 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
27 #include "chrome/browser/extensions/api/tabs/windows_util.h"
28 #include "chrome/browser/extensions/extension_function_dispatcher.h"
29 #include "chrome/browser/extensions/extension_function_util.h"
30 #include "chrome/browser/extensions/extension_host.h"
31 #include "chrome/browser/extensions/extension_service.h"
32 #include "chrome/browser/extensions/extension_tab_util.h"
33 #include "chrome/browser/extensions/script_executor.h"
34 #include "chrome/browser/extensions/tab_helper.h"
35 #include "chrome/browser/extensions/window_controller.h"
36 #include "chrome/browser/extensions/window_controller_list.h"
37 #include "chrome/browser/prefs/incognito_mode_prefs.h"
38 #include "chrome/browser/profiles/profile.h"
39 #include "chrome/browser/translate/translate_tab_helper.h"
40 #include "chrome/browser/ui/apps/chrome_shell_window_delegate.h"
41 #include "chrome/browser/ui/browser.h"
42 #include "chrome/browser/ui/browser_commands.h"
43 #include "chrome/browser/ui/browser_finder.h"
44 #include "chrome/browser/ui/browser_iterator.h"
45 #include "chrome/browser/ui/browser_navigator.h"
46 #include "chrome/browser/ui/browser_tabstrip.h"
47 #include "chrome/browser/ui/browser_window.h"
48 #include "chrome/browser/ui/host_desktop.h"
49 #include "chrome/browser/ui/panels/panel_manager.h"
50 #include "chrome/browser/ui/tabs/tab_strip_model.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/i18n/default_locale_handler.h"
55 #include "chrome/common/extensions/api/tabs.h"
56 #include "chrome/common/extensions/api/windows.h"
57 #include "chrome/common/extensions/extension.h"
58 #include "chrome/common/extensions/extension_constants.h"
59 #include "chrome/common/extensions/extension_file_util.h"
60 #include "chrome/common/extensions/extension_l10n_util.h"
61 #include "chrome/common/extensions/extension_messages.h"
62 #include "chrome/common/extensions/incognito_handler.h"
63 #include "chrome/common/extensions/message_bundle.h"
64 #include "chrome/common/extensions/permissions/permissions_data.h"
65 #include "chrome/common/pref_names.h"
66 #include "chrome/common/translate/language_detection_details.h"
67 #include "chrome/common/url_constants.h"
68 #include "components/user_prefs/pref_registry_syncable.h"
69 #include "content/public/browser/navigation_controller.h"
70 #include "content/public/browser/navigation_entry.h"
71 #include "content/public/browser/notification_details.h"
72 #include "content/public/browser/notification_source.h"
73 #include "content/public/browser/render_process_host.h"
74 #include "content/public/browser/render_view_host.h"
75 #include "content/public/browser/render_widget_host_view.h"
76 #include "content/public/browser/web_contents.h"
77 #include "content/public/browser/web_contents_view.h"
78 #include "content/public/common/url_constants.h"
79 #include "extensions/browser/file_reader.h"
80 #include "extensions/common/constants.h"
81 #include "extensions/common/error_utils.h"
82 #include "extensions/common/manifest_constants.h"
83 #include "extensions/common/user_script.h"
84 #include "skia/ext/image_operations.h"
85 #include "skia/ext/platform_canvas.h"
86 #include "third_party/skia/include/core/SkBitmap.h"
87 #include "ui/base/models/list_selection_model.h"
88 #include "ui/base/ui_base_types.h"
89 #include "ui/gfx/codec/jpeg_codec.h"
90 #include "ui/gfx/codec/png_codec.h"
93 #include "win8/util/win8_util.h"
97 #include "apps/shell_window_registry.h"
98 #include "ash/ash_switches.h"
99 #include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
102 using apps::ShellWindow;
103 using content::BrowserThread;
104 using content::NavigationController;
105 using content::NavigationEntry;
106 using content::OpenURLParams;
107 using content::Referrer;
108 using content::RenderViewHost;
109 using content::WebContents;
111 namespace extensions {
113 namespace windows = api::windows;
114 namespace errors = manifest_errors;
115 namespace keys = tabs_constants;
116 namespace tabs = api::tabs;
117 typedef tabs::CaptureVisibleTab::Params::Options FormatEnum;
119 using api::tabs::InjectDetails;
121 const int TabsCaptureVisibleTabFunction::kDefaultQuality = 90;
125 // |error_message| can optionally be passed in a will be set with an appropriate
126 // message if the window cannot be found by id.
127 Browser* GetBrowserInProfileWithId(Profile* profile,
129 bool include_incognito,
130 std::string* error_message) {
131 Profile* incognito_profile =
132 include_incognito && profile->HasOffTheRecordProfile() ?
133 profile->GetOffTheRecordProfile() : NULL;
134 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
135 Browser* browser = *it;
136 if ((browser->profile() == profile ||
137 browser->profile() == incognito_profile) &&
138 ExtensionTabUtil::GetWindowId(browser) == window_id &&
145 *error_message = ErrorUtils::FormatErrorMessage(
146 keys::kWindowNotFoundError, base::IntToString(window_id));
151 bool GetBrowserFromWindowID(ChromeAsyncExtensionFunction* function,
154 if (window_id == extension_misc::kCurrentWindowId) {
155 *browser = function->GetCurrentBrowser();
156 if (!(*browser) || !(*browser)->window()) {
157 function->SetError(keys::kNoCurrentWindowError);
162 *browser = GetBrowserInProfileWithId(function->GetProfile(),
164 function->include_incognito(),
167 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))
189 *error_message = ErrorUtils::FormatErrorMessage(
190 keys::kTabNotFoundError, base::IntToString(tab_id));
195 // Returns true if either |boolean| is a null pointer, or if |*boolean| and
196 // |value| are equal. This function is used to check if a tab's parameters match
197 // those of the browser.
198 bool MatchesBool(bool* boolean, bool value) {
199 return !boolean || *boolean == value;
202 Browser* CreateBrowserWindow(const Browser::CreateParams& params,
204 const std::string& extension_id) {
205 bool use_existing_browser_window = false;
208 // In windows 8 metro mode we don't allow windows to be created.
209 if (win8::IsSingleWindowMetroMode())
210 use_existing_browser_window = true;
213 Browser* new_window = NULL;
214 if (use_existing_browser_window)
215 // The false parameter passed below is to ensure that we find a browser
216 // object matching the profile passed in, instead of the original profile
217 new_window = chrome::FindTabbedBrowser(profile, false,
218 params.host_desktop_type);
221 new_window = new Browser(params);
227 // Windows ---------------------------------------------------------------------
229 bool WindowsGetFunction::RunImpl() {
230 scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
231 EXTENSION_FUNCTION_VALIDATE(params.get());
233 bool populate_tabs = false;
234 if (params->get_info.get() && params->get_info->populate.get())
235 populate_tabs = *params->get_info->populate;
237 WindowController* controller;
238 if (!windows_util::GetWindowFromWindowID(this,
245 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
247 SetResult(controller->CreateWindowValue());
251 bool WindowsGetCurrentFunction::RunImpl() {
252 scoped_ptr<windows::GetCurrent::Params> params(
253 windows::GetCurrent::Params::Create(*args_));
254 EXTENSION_FUNCTION_VALIDATE(params.get());
256 bool populate_tabs = false;
257 if (params->get_info.get() && params->get_info->populate.get())
258 populate_tabs = *params->get_info->populate;
260 WindowController* controller;
261 if (!windows_util::GetWindowFromWindowID(this,
262 extension_misc::kCurrentWindowId,
267 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
269 SetResult(controller->CreateWindowValue());
273 bool WindowsGetLastFocusedFunction::RunImpl() {
274 scoped_ptr<windows::GetLastFocused::Params> params(
275 windows::GetLastFocused::Params::Create(*args_));
276 EXTENSION_FUNCTION_VALIDATE(params.get());
278 bool populate_tabs = false;
279 if (params->get_info.get() && params->get_info->populate.get())
280 populate_tabs = *params->get_info->populate;
282 // Note: currently this returns the last active browser. If we decide to
283 // include other window types (e.g. panels), we will need to add logic to
284 // WindowControllerList that mirrors the active behavior of BrowserList.
285 Browser* browser = chrome::FindAnyBrowser(
286 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
287 if (!browser || !browser->window()) {
288 error_ = keys::kNoLastFocusedWindowError;
291 WindowController* controller =
292 browser->extension_window_controller();
294 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
296 SetResult(controller->CreateWindowValue());
300 bool WindowsGetAllFunction::RunImpl() {
301 scoped_ptr<windows::GetAll::Params> params(
302 windows::GetAll::Params::Create(*args_));
303 EXTENSION_FUNCTION_VALIDATE(params.get());
305 bool populate_tabs = false;
306 if (params->get_info.get() && params->get_info->populate.get())
307 populate_tabs = *params->get_info->populate;
309 base::ListValue* window_list = new base::ListValue();
310 const WindowControllerList::ControllerList& windows =
311 WindowControllerList::GetInstance()->windows();
312 for (WindowControllerList::ControllerList::const_iterator iter =
314 iter != windows.end(); ++iter) {
315 if (!this->CanOperateOnWindow(*iter))
318 window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension()));
320 window_list->Append((*iter)->CreateWindowValue());
322 SetResult(window_list);
326 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
327 const windows::Create::Params::CreateData* create_data,
328 std::vector<GURL>* urls, bool* is_error) {
330 const IncognitoModePrefs::Availability incognito_availability =
331 IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
332 bool incognito = false;
333 if (create_data && create_data->incognito) {
334 incognito = *create_data->incognito;
335 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
336 error_ = keys::kIncognitoModeIsDisabled;
340 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
341 error_ = keys::kIncognitoModeIsForced;
345 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
346 // If incognito argument is not specified explicitly, we default to
347 // incognito when forced so by policy.
351 // Remove all URLs that are not allowed in an incognito session. Note that a
352 // ChromeOS guest session is not considered incognito in this case.
353 if (incognito && !GetProfile()->IsGuestSession()) {
354 std::string first_url_erased;
355 for (size_t i = 0; i < urls->size();) {
356 if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
359 if (first_url_erased.empty())
360 first_url_erased = (*urls)[i].spec();
361 urls->erase(urls->begin() + i);
364 if (urls->empty() && !first_url_erased.empty()) {
365 error_ = ErrorUtils::FormatErrorMessage(
366 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
374 bool WindowsCreateFunction::RunImpl() {
375 scoped_ptr<windows::Create::Params> params(
376 windows::Create::Params::Create(*args_));
377 EXTENSION_FUNCTION_VALIDATE(params);
378 std::vector<GURL> urls;
379 TabStripModel* source_tab_strip = NULL;
382 windows::Create::Params::CreateData* create_data = params->create_data.get();
384 // Look for optional url.
385 if (create_data && create_data->url) {
386 std::vector<std::string> url_strings;
387 // First, get all the URLs the client wants to open.
388 if (create_data->url->as_string)
389 url_strings.push_back(*create_data->url->as_string);
390 else if (create_data->url->as_strings)
391 url_strings.swap(*create_data->url->as_strings);
393 // Second, resolve, validate and convert them to GURLs.
394 for (std::vector<std::string>::iterator i = url_strings.begin();
395 i != url_strings.end(); ++i) {
396 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
398 if (!url.is_valid()) {
399 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
402 // Don't let the extension crash the browser or renderers.
403 if (ExtensionTabUtil::IsCrashURL(url)) {
404 error_ = keys::kNoCrashBrowserError;
411 // Look for optional tab id.
412 if (create_data && create_data->tab_id) {
413 // Find the tab. |source_tab_strip| and |tab_index| will later be used to
414 // move the tab into the created window.
415 if (!GetTabById(*create_data->tab_id,
426 Profile* window_profile = GetProfile();
427 Browser::Type window_type = Browser::TYPE_TABBED;
428 bool create_panel = false;
430 // panel_create_mode only applies if create_panel = true
431 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
433 gfx::Rect window_bounds;
435 bool saw_focus_key = false;
436 std::string extension_id;
438 // Decide whether we are opening a normal window or an incognito window.
439 bool is_error = true;
440 bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
443 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
446 if (open_incognito_window) {
447 window_profile = window_profile->GetOffTheRecordProfile();
451 // Figure out window type before figuring out bounds so that default
452 // bounds can be set according to the window type.
453 switch (create_data->type) {
454 case windows::Create::Params::CreateData::TYPE_POPUP:
455 window_type = Browser::TYPE_POPUP;
456 extension_id = GetExtension()->id();
458 case windows::Create::Params::CreateData::TYPE_PANEL:
459 case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
460 extension_id = GetExtension()->id();
461 bool use_panels = false;
462 #if !defined(OS_ANDROID)
463 use_panels = PanelManager::ShouldUsePanels(extension_id);
467 // Non-ash supports both docked and detached panel types.
468 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
470 windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
471 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
474 window_type = Browser::TYPE_POPUP;
478 case windows::Create::Params::CreateData::TYPE_NONE:
479 case windows::Create::Params::CreateData::TYPE_NORMAL:
482 error_ = keys::kInvalidWindowTypeError;
486 // Initialize default window bounds according to window type.
487 if (window_type == Browser::TYPE_TABBED ||
488 window_type == Browser::TYPE_POPUP ||
490 // Try to position the new browser relative to its originating
491 // browser window. The call offsets the bounds by kWindowTilePixels
492 // (defined in WindowSizer to be 10).
494 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
495 // GetBrowserWindowBounds will default to saved "default" values for
497 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
498 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
505 if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
506 window_bounds.set_origin(
507 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
510 // Any part of the bounds can optionally be set by the caller.
511 if (create_data->left)
512 window_bounds.set_x(*create_data->left);
514 if (create_data->top)
515 window_bounds.set_y(*create_data->top);
517 if (create_data->width)
518 window_bounds.set_width(*create_data->width);
520 if (create_data->height)
521 window_bounds.set_height(*create_data->height);
523 if (create_data->focused) {
524 focused = *create_data->focused;
525 saw_focus_key = true;
531 urls.push_back(GURL(chrome::kChromeUINewTabURL));
534 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
535 ShellWindow::CreateParams create_params;
536 create_params.window_type = ShellWindow::WINDOW_TYPE_V1_PANEL;
537 create_params.bounds = window_bounds;
538 create_params.focused = saw_focus_key && focused;
539 ShellWindow* shell_window = new ShellWindow(
540 window_profile, new ChromeShellWindowDelegate(),
542 AshPanelContents* ash_panel_contents = new AshPanelContents(shell_window);
543 shell_window->Init(urls[0], ash_panel_contents, create_params);
544 SetResult(ash_panel_contents->GetExtensionWindowController()->
545 CreateWindowValueWithTabs(GetExtension()));
550 web_app::GenerateApplicationNameFromExtensionId(extension_id);
551 // Note: Panels ignore all but the first url provided.
552 Panel* panel = PanelManager::GetInstance()->CreatePanel(
553 title, window_profile, urls[0], window_bounds, panel_create_mode);
555 // Unlike other window types, Panels do not take focus by default.
556 if (!saw_focus_key || !focused)
557 panel->ShowInactive();
562 panel->extension_window_controller()->CreateWindowValueWithTabs(
567 // Create a new BrowserWindow.
568 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
570 window_type = Browser::TYPE_POPUP;
571 Browser::CreateParams create_params(window_type, window_profile,
573 if (extension_id.empty()) {
574 create_params.initial_bounds = window_bounds;
576 create_params = Browser::CreateParams::CreateForApp(
578 web_app::GenerateApplicationNameFromExtensionId(extension_id),
583 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
584 create_params.host_desktop_type = chrome::GetActiveDesktop();
586 Browser* new_window = CreateBrowserWindow(create_params, window_profile,
589 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
590 WebContents* tab = chrome::AddSelectedTabWithURL(
591 new_window, *i, content::PAGE_TRANSITION_LINK);
593 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
597 WebContents* contents = NULL;
598 // Move the tab into the created window only if it's an empty popup or it's
600 if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
601 window_type == Browser::TYPE_TABBED) {
602 if (source_tab_strip)
603 contents = source_tab_strip->DetachWebContentsAt(tab_index);
605 TabStripModel* target_tab_strip = new_window->tab_strip_model();
606 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
607 TabStripModel::ADD_NONE);
610 // Create a new tab if the created window is still empty. Don't create a new
611 // tab when it is intended to create an empty popup.
612 if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
613 chrome::NewTab(new_window);
615 chrome::SelectNumberedTab(new_window, 0);
617 // Unlike other window types, Panels do not take focus by default.
618 if (!saw_focus_key && create_panel)
622 new_window->window()->Show();
624 new_window->window()->ShowInactive();
626 if (new_window->profile()->IsOffTheRecord() && !include_incognito()) {
627 // Don't expose incognito windows if the extension isn't allowed.
628 SetResult(Value::CreateNullValue());
631 new_window->extension_window_controller()->CreateWindowValueWithTabs(
638 bool WindowsUpdateFunction::RunImpl() {
639 scoped_ptr<windows::Update::Params> params(
640 windows::Update::Params::Create(*args_));
641 EXTENSION_FUNCTION_VALIDATE(params);
643 WindowController* controller;
644 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
649 // Silently ignore changes on the window for metro mode.
650 if (win8::IsSingleWindowMetroMode()) {
651 SetResult(controller->CreateWindowValue());
656 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change.
657 switch (params->update_info.state) {
658 case windows::Update::Params::UpdateInfo::STATE_NORMAL:
659 show_state = ui::SHOW_STATE_NORMAL;
661 case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
662 show_state = ui::SHOW_STATE_MINIMIZED;
664 case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
665 show_state = ui::SHOW_STATE_MAXIMIZED;
667 case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
668 show_state = ui::SHOW_STATE_FULLSCREEN;
670 case windows::Update::Params::UpdateInfo::STATE_NONE:
673 error_ = keys::kInvalidWindowStateError;
677 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
678 show_state != ui::SHOW_STATE_DEFAULT)
679 controller->SetFullscreenMode(false, GetExtension()->url());
681 switch (show_state) {
682 case ui::SHOW_STATE_MINIMIZED:
683 controller->window()->Minimize();
685 case ui::SHOW_STATE_MAXIMIZED:
686 controller->window()->Maximize();
688 case ui::SHOW_STATE_FULLSCREEN:
689 if (controller->window()->IsMinimized() ||
690 controller->window()->IsMaximized())
691 controller->window()->Restore();
692 controller->SetFullscreenMode(true, GetExtension()->url());
694 case ui::SHOW_STATE_NORMAL:
695 controller->window()->Restore();
702 if (controller->window()->IsMinimized())
703 bounds = controller->window()->GetRestoredBounds();
705 bounds = controller->window()->GetBounds();
706 bool set_bounds = false;
708 // Any part of the bounds can optionally be set by the caller.
709 if (params->update_info.left) {
710 bounds.set_x(*params->update_info.left);
714 if (params->update_info.top) {
715 bounds.set_y(*params->update_info.top);
719 if (params->update_info.width) {
720 bounds.set_width(*params->update_info.width);
724 if (params->update_info.height) {
725 bounds.set_height(*params->update_info.height);
730 if (show_state == ui::SHOW_STATE_MINIMIZED ||
731 show_state == ui::SHOW_STATE_MAXIMIZED ||
732 show_state == ui::SHOW_STATE_FULLSCREEN) {
733 error_ = keys::kInvalidWindowStateError;
736 // TODO(varkha): Updating bounds during a drag can cause problems and a more
737 // general solution is needed. See http://crbug.com/251813 .
738 controller->window()->SetBounds(bounds);
741 if (params->update_info.focused) {
742 if (*params->update_info.focused) {
743 if (show_state == ui::SHOW_STATE_MINIMIZED) {
744 error_ = keys::kInvalidWindowStateError;
747 controller->window()->Activate();
749 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
750 show_state == ui::SHOW_STATE_FULLSCREEN) {
751 error_ = keys::kInvalidWindowStateError;
754 controller->window()->Deactivate();
758 if (params->update_info.draw_attention)
759 controller->window()->FlashFrame(*params->update_info.draw_attention);
761 SetResult(controller->CreateWindowValue());
766 bool WindowsRemoveFunction::RunImpl() {
767 scoped_ptr<windows::Remove::Params> params(
768 windows::Remove::Params::Create(*args_));
769 EXTENSION_FUNCTION_VALIDATE(params);
771 WindowController* controller;
772 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
777 // In Windows 8 metro mode, an existing Browser instance is reused for
778 // hosting the extension tab. We should not be closing it as we don't own it.
779 if (win8::IsSingleWindowMetroMode())
783 WindowController::Reason reason;
784 if (!controller->CanClose(&reason)) {
785 if (reason == WindowController::REASON_NOT_EDITABLE)
786 error_ = keys::kTabStripNotEditableError;
789 controller->window()->Close();
793 // Tabs ------------------------------------------------------------------------
795 bool TabsGetSelectedFunction::RunImpl() {
796 // windowId defaults to "current" window.
797 int window_id = extension_misc::kCurrentWindowId;
799 scoped_ptr<tabs::GetSelected::Params> params(
800 tabs::GetSelected::Params::Create(*args_));
801 EXTENSION_FUNCTION_VALIDATE(params.get());
802 if (params->window_id.get())
803 window_id = *params->window_id;
805 Browser* browser = NULL;
806 if (!GetBrowserFromWindowID(this, window_id, &browser))
809 TabStripModel* tab_strip = browser->tab_strip_model();
810 WebContents* contents = tab_strip->GetActiveWebContents();
812 error_ = keys::kNoSelectedTabError;
815 SetResult(ExtensionTabUtil::CreateTabValue(contents,
817 tab_strip->active_index(),
822 bool TabsGetAllInWindowFunction::RunImpl() {
823 scoped_ptr<tabs::GetAllInWindow::Params> params(
824 tabs::GetAllInWindow::Params::Create(*args_));
825 EXTENSION_FUNCTION_VALIDATE(params.get());
826 // windowId defaults to "current" window.
827 int window_id = extension_misc::kCurrentWindowId;
828 if (params->window_id.get())
829 window_id = *params->window_id;
831 Browser* browser = NULL;
832 if (!GetBrowserFromWindowID(this, window_id, &browser))
835 SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension()));
840 bool TabsQueryFunction::RunImpl() {
841 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
842 EXTENSION_FUNCTION_VALIDATE(params.get());
844 bool loading_status_set = params->query_info.status !=
845 tabs::Query::Params::QueryInfo::STATUS_NONE;
846 bool loading = params->query_info.status ==
847 tabs::Query::Params::QueryInfo::STATUS_LOADING;
849 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
850 // not grant access to the content of the tabs, only to seeing their URLs and
852 URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
853 if (params->query_info.url.get())
854 url_pattern = URLPattern(URLPattern::SCHEME_ALL, *params->query_info.url);
857 if (params->query_info.title.get())
858 title = *params->query_info.title;
860 int window_id = extension_misc::kUnknownWindowId;
861 if (params->query_info.window_id.get())
862 window_id = *params->query_info.window_id;
865 if (params->query_info.index.get())
866 index = *params->query_info.index;
868 std::string window_type;
869 if (params->query_info.window_type !=
870 tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) {
871 window_type = tabs::Query::Params::QueryInfo::ToString(
872 params->query_info.window_type);
875 base::ListValue* result = new base::ListValue();
876 Browser* last_active_browser = chrome::FindAnyBrowser(
877 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
878 Browser* current_browser = GetCurrentBrowser();
879 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
880 Browser* browser = *it;
881 if (!GetProfile()->IsSameProfile(browser->profile()))
884 if (!browser->window())
887 if (!include_incognito() && GetProfile() != browser->profile())
890 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
893 if (window_id == extension_misc::kCurrentWindowId &&
894 browser != current_browser) {
898 if (!MatchesBool(params->query_info.current_window.get(),
899 browser == current_browser)) {
903 if (!MatchesBool(params->query_info.last_focused_window.get(),
904 browser == last_active_browser)) {
908 if (!window_type.empty() &&
910 browser->extension_window_controller()->GetWindowTypeText()) {
914 TabStripModel* tab_strip = browser->tab_strip_model();
915 for (int i = 0; i < tab_strip->count(); ++i) {
916 const WebContents* web_contents = tab_strip->GetWebContentsAt(i);
918 if (index > -1 && i != index)
921 if (!MatchesBool(params->query_info.highlighted.get(),
922 tab_strip->IsTabSelected(i))) {
926 if (!MatchesBool(params->query_info.active.get(),
927 i == tab_strip->active_index())) {
931 if (!MatchesBool(params->query_info.pinned.get(),
932 tab_strip->IsTabPinned(i))) {
936 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
940 if (!url_pattern.MatchesURL(web_contents->GetURL()))
943 if (loading_status_set && loading != web_contents->IsLoading())
946 result->Append(ExtensionTabUtil::CreateTabValue(
947 web_contents, tab_strip, i, GetExtension()));
955 bool TabsCreateFunction::RunImpl() {
956 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
957 EXTENSION_FUNCTION_VALIDATE(params.get());
959 // windowId defaults to "current" window.
960 int window_id = extension_misc::kCurrentWindowId;
961 if (params->create_properties.window_id.get())
962 window_id = *params->create_properties.window_id;
964 Browser* browser = NULL;
965 if (!GetBrowserFromWindowID(this, window_id, &browser))
968 // Ensure the selected browser is tabbed.
969 if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser())
970 browser = chrome::FindTabbedBrowser(
971 GetProfile(), include_incognito(), browser->host_desktop_type());
973 if (!browser || !browser->window())
976 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that
977 // represents the active tab.
978 WebContents* opener = NULL;
979 if (params->create_properties.opener_tab_id.get()) {
980 int opener_id = *params->create_properties.opener_tab_id;
982 if (!ExtensionTabUtil::GetTabById(opener_id,
993 // TODO(rafaelw): handle setting remaining tab properties:
997 std::string url_string;
999 if (params->create_properties.url.get()) {
1000 url_string = *params->create_properties.url;
1001 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
1003 if (!url.is_valid()) {
1004 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
1010 // Don't let extensions crash the browser or renderers.
1011 if (ExtensionTabUtil::IsCrashURL(url)) {
1012 error_ = keys::kNoCrashBrowserError;
1016 // Default to foreground for the new tab. The presence of 'selected' property
1017 // will override this default. This property is deprecated ('active' should
1018 // be used instead).
1020 if (params->create_properties.selected.get())
1021 active = *params->create_properties.selected;
1023 // The 'active' property has replaced the 'selected' property.
1024 if (params->create_properties.active.get())
1025 active = *params->create_properties.active;
1027 // Default to not pinning the tab. Setting the 'pinned' property to true
1028 // will override this default.
1029 bool pinned = false;
1030 if (params->create_properties.pinned.get())
1031 pinned = *params->create_properties.pinned;
1033 // We can't load extension URLs into incognito windows unless the extension
1034 // uses split mode. Special case to fall back to a tabbed window.
1035 if (url.SchemeIs(kExtensionScheme) &&
1036 !IncognitoInfo::IsSplitMode(GetExtension()) &&
1037 browser->profile()->IsOffTheRecord()) {
1038 Profile* profile = browser->profile()->GetOriginalProfile();
1039 chrome::HostDesktopType desktop_type = browser->host_desktop_type();
1041 browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
1043 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED,
1044 profile, desktop_type));
1045 browser->window()->Show();
1049 // If index is specified, honor the value, but keep it bound to
1050 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior.
1052 if (params->create_properties.index.get())
1053 index = *params->create_properties.index;
1055 TabStripModel* tab_strip = browser->tab_strip_model();
1057 index = std::min(std::max(index, -1), tab_strip->count());
1059 int add_types = active ? TabStripModel::ADD_ACTIVE :
1060 TabStripModel::ADD_NONE;
1061 add_types |= TabStripModel::ADD_FORCE_INDEX;
1063 add_types |= TabStripModel::ADD_PINNED;
1064 chrome::NavigateParams navigate_params(
1065 browser, url, content::PAGE_TRANSITION_LINK);
1066 navigate_params.disposition =
1067 active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1068 navigate_params.tabstrip_index = index;
1069 navigate_params.tabstrip_add_types = add_types;
1070 chrome::Navigate(&navigate_params);
1072 // The tab may have been created in a different window, so make sure we look
1073 // at the right tab strip.
1074 tab_strip = navigate_params.browser->tab_strip_model();
1075 int new_index = tab_strip->GetIndexOfWebContents(
1076 navigate_params.target_contents);
1078 tab_strip->SetOpenerOfWebContentsAt(new_index, opener);
1081 navigate_params.target_contents->GetView()->SetInitialFocus();
1083 // Return data about the newly created tab.
1084 if (has_callback()) {
1085 SetResult(ExtensionTabUtil::CreateTabValue(
1086 navigate_params.target_contents,
1087 tab_strip, new_index, GetExtension()));
1093 bool TabsDuplicateFunction::RunImpl() {
1094 scoped_ptr<tabs::Duplicate::Params> params(
1095 tabs::Duplicate::Params::Create(*args_));
1096 EXTENSION_FUNCTION_VALIDATE(params.get());
1097 int tab_id = params->tab_id;
1099 Browser* browser = NULL;
1100 TabStripModel* tab_strip = NULL;
1102 if (!GetTabById(tab_id,
1104 include_incognito(),
1113 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
1114 if (!has_callback())
1117 // Duplicated tab may not be in the same window as the original, so find
1118 // the window and the tab.
1119 TabStripModel* new_tab_strip = NULL;
1120 int new_tab_index = -1;
1121 ExtensionTabUtil::GetTabStripModel(new_contents,
1124 if (!new_tab_strip || new_tab_index == -1) {
1128 // Return data about the newly created tab.
1129 SetResult(ExtensionTabUtil::CreateTabValue(
1131 new_tab_strip, new_tab_index, GetExtension()));
1136 bool TabsGetFunction::RunImpl() {
1137 scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
1138 EXTENSION_FUNCTION_VALIDATE(params.get());
1139 int tab_id = params->tab_id;
1141 TabStripModel* tab_strip = NULL;
1142 WebContents* contents = NULL;
1144 if (!GetTabById(tab_id,
1146 include_incognito(),
1154 SetResult(ExtensionTabUtil::CreateTabValue(contents,
1161 bool TabsGetCurrentFunction::RunImpl() {
1162 DCHECK(dispatcher());
1164 WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
1166 SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension()));
1171 bool TabsHighlightFunction::RunImpl() {
1172 scoped_ptr<tabs::Highlight::Params> params(
1173 tabs::Highlight::Params::Create(*args_));
1174 EXTENSION_FUNCTION_VALIDATE(params.get());
1176 // Get the window id from the params; default to current window if omitted.
1177 int window_id = extension_misc::kCurrentWindowId;
1178 if (params->highlight_info.window_id.get())
1179 window_id = *params->highlight_info.window_id;
1181 Browser* browser = NULL;
1182 if (!GetBrowserFromWindowID(this, window_id, &browser))
1185 TabStripModel* tabstrip = browser->tab_strip_model();
1186 ui::ListSelectionModel selection;
1187 int active_index = -1;
1189 if (params->highlight_info.tabs.as_integers) {
1190 std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1191 // Create a new selection model as we read the list of tab indices.
1192 for (size_t i = 0; i < tab_indices.size(); ++i) {
1193 if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1197 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1198 if (!HighlightTab(tabstrip,
1201 *params->highlight_info.tabs.as_integer)) {
1206 // Make sure they actually specified tabs to select.
1207 if (selection.empty()) {
1208 error_ = keys::kNoHighlightedTabError;
1212 selection.set_active(active_index);
1213 browser->tab_strip_model()->SetSelectionFromModel(selection);
1215 browser->extension_window_controller()->CreateWindowValueWithTabs(
1220 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1221 ui::ListSelectionModel* selection,
1224 // Make sure the index is in range.
1225 if (!tabstrip->ContainsIndex(index)) {
1226 error_ = ErrorUtils::FormatErrorMessage(
1227 keys::kTabIndexNotFoundError, base::IntToString(index));
1231 // By default, we make the first tab in the list active.
1232 if (*active_index == -1)
1233 *active_index = index;
1235 selection->AddIndexToSelection(index);
1239 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1242 bool TabsUpdateFunction::RunImpl() {
1243 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1244 EXTENSION_FUNCTION_VALIDATE(params.get());
1247 WebContents* contents = NULL;
1248 if (!params->tab_id.get()) {
1249 Browser* browser = GetCurrentBrowser();
1251 error_ = keys::kNoCurrentWindowError;
1254 contents = browser->tab_strip_model()->GetActiveWebContents();
1256 error_ = keys::kNoSelectedTabError;
1259 tab_id = SessionID::IdForTab(contents);
1261 tab_id = *params->tab_id;
1265 TabStripModel* tab_strip = NULL;
1266 if (!GetTabById(tab_id,
1268 include_incognito(),
1277 web_contents_ = contents;
1279 // TODO(rafaelw): handle setting remaining tab properties:
1283 // Navigate the tab to a new location if the url is different.
1284 bool is_async = false;
1285 if (params->update_properties.url.get() &&
1286 !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1290 bool active = false;
1291 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1292 // Move tab selection management up to window.
1293 if (params->update_properties.selected.get())
1294 active = *params->update_properties.selected;
1296 // The 'active' property has replaced 'selected'.
1297 if (params->update_properties.active.get())
1298 active = *params->update_properties.active;
1301 if (tab_strip->active_index() != tab_index) {
1302 tab_strip->ActivateTabAt(tab_index, false);
1303 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1307 if (params->update_properties.highlighted.get()) {
1308 bool highlighted = *params->update_properties.highlighted;
1309 if (highlighted != tab_strip->IsTabSelected(tab_index))
1310 tab_strip->ToggleSelectionAt(tab_index);
1313 if (params->update_properties.pinned.get()) {
1314 bool pinned = *params->update_properties.pinned;
1315 tab_strip->SetTabPinned(tab_index, pinned);
1317 // Update the tab index because it may move when being pinned.
1318 tab_index = tab_strip->GetIndexOfWebContents(contents);
1321 if (params->update_properties.opener_tab_id.get()) {
1322 int opener_id = *params->update_properties.opener_tab_id;
1324 WebContents* opener_contents = NULL;
1325 if (!ExtensionTabUtil::GetTabById(opener_id,
1327 include_incognito(),
1334 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1344 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1347 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1348 url_string, GetExtension());
1350 if (!url.is_valid()) {
1351 error_ = ErrorUtils::FormatErrorMessage(
1352 keys::kInvalidUrlError, url_string);
1356 // Don't let the extension crash the browser or renderers.
1357 if (ExtensionTabUtil::IsCrashURL(url)) {
1358 error_ = keys::kNoCrashBrowserError;
1362 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1363 // we need to check host permissions before allowing them.
1364 if (url.SchemeIs(content::kJavaScriptScheme)) {
1365 content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1366 if (!PermissionsData::CanExecuteScriptOnPage(
1368 web_contents_->GetURL(),
1369 web_contents_->GetURL(),
1372 process ? process->GetID() : -1,
1377 TabHelper::FromWebContents(web_contents_)->
1378 script_executor()->ExecuteScript(
1380 ScriptExecutor::JAVASCRIPT,
1382 ScriptExecutor::TOP_FRAME,
1383 UserScript::DOCUMENT_IDLE,
1384 ScriptExecutor::MAIN_WORLD,
1385 ScriptExecutor::DEFAULT_PROCESS,
1387 ScriptExecutor::NO_RESULT,
1388 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1394 web_contents_->GetController().LoadURL(
1395 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1397 // The URL of a tab contents never actually changes to a JavaScript URL, so
1398 // this check only makes sense in other cases.
1399 if (!url.SchemeIs(content::kJavaScriptScheme))
1400 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1405 void TabsUpdateFunction::PopulateResult() {
1406 if (!has_callback())
1409 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension()));
1412 void TabsUpdateFunction::OnExecuteCodeFinished(
1413 const std::string& error,
1416 const base::ListValue& script_result) {
1421 SendResponse(error.empty());
1424 bool TabsMoveFunction::RunImpl() {
1425 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1426 EXTENSION_FUNCTION_VALIDATE(params.get());
1428 int new_index = params->move_properties.index;
1429 int* window_id = params->move_properties.window_id.get();
1430 base::ListValue tab_values;
1432 size_t num_tabs = 0;
1433 if (params->tab_ids.as_integers) {
1434 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1435 num_tabs = tab_ids.size();
1436 for (size_t i = 0; i < tab_ids.size(); ++i) {
1437 if (!MoveTab(tab_ids[i], &new_index, i, &tab_values, window_id))
1441 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1443 if (!MoveTab(*params->tab_ids.as_integer,
1452 if (!has_callback())
1455 // Only return the results as an array if there are multiple tabs.
1457 SetResult(tab_values.DeepCopy());
1459 Value* value = NULL;
1460 CHECK(tab_values.Get(0, &value));
1461 SetResult(value->DeepCopy());
1466 bool TabsMoveFunction::MoveTab(int tab_id,
1469 base::ListValue* tab_values,
1471 Browser* source_browser = NULL;
1472 TabStripModel* source_tab_strip = NULL;
1473 WebContents* contents = NULL;
1475 if (!GetTabById(tab_id,
1477 include_incognito(),
1486 // Don't let the extension move the tab if the user is dragging tabs.
1487 if (!source_browser->window()->IsTabStripEditable()) {
1488 error_ = keys::kTabStripNotEditableError;
1492 // Insert the tabs one after another.
1493 *new_index += iteration;
1496 Browser* target_browser = NULL;
1498 if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1501 if (!target_browser->window()->IsTabStripEditable()) {
1502 error_ = keys::kTabStripNotEditableError;
1506 if (!target_browser->is_type_tabbed()) {
1507 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1511 if (target_browser->profile() != source_browser->profile()) {
1512 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1516 // If windowId is different from the current window, move between windows.
1517 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1518 ExtensionTabUtil::GetWindowId(source_browser)) {
1519 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1520 WebContents* web_contents =
1521 source_tab_strip->DetachWebContentsAt(tab_index);
1522 if (!web_contents) {
1523 error_ = ErrorUtils::FormatErrorMessage(
1524 keys::kTabNotFoundError, base::IntToString(tab_id));
1528 // Clamp move location to the last position.
1529 // This is ">" because it can append to a new index position.
1530 // -1 means set the move location to the last position.
1531 if (*new_index > target_tab_strip->count() || *new_index < 0)
1532 *new_index = target_tab_strip->count();
1534 target_tab_strip->InsertWebContentsAt(
1535 *new_index, web_contents, TabStripModel::ADD_NONE);
1537 if (has_callback()) {
1538 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1549 // Perform a simple within-window move.
1550 // Clamp move location to the last position.
1551 // This is ">=" because the move must be to an existing location.
1552 // -1 means set the move location to the last position.
1553 if (*new_index >= source_tab_strip->count() || *new_index < 0)
1554 *new_index = source_tab_strip->count() - 1;
1556 if (*new_index != tab_index)
1557 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1559 if (has_callback()) {
1560 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1561 contents, source_tab_strip, *new_index, GetExtension()));
1567 bool TabsReloadFunction::RunImpl() {
1568 scoped_ptr<tabs::Reload::Params> params(
1569 tabs::Reload::Params::Create(*args_));
1570 EXTENSION_FUNCTION_VALIDATE(params.get());
1572 bool bypass_cache = false;
1573 if (params->reload_properties.get() &&
1574 params->reload_properties->bypass_cache.get()) {
1575 bypass_cache = *params->reload_properties->bypass_cache;
1578 content::WebContents* web_contents = NULL;
1580 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1581 // in the current window.
1582 if (!params->tab_id.get()) {
1583 Browser* browser = GetCurrentBrowser();
1585 error_ = keys::kNoCurrentWindowError;
1589 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1592 int tab_id = *params->tab_id;
1594 Browser* browser = NULL;
1595 if (!GetTabById(tab_id,
1597 include_incognito(),
1606 if (web_contents->ShowingInterstitialPage()) {
1607 // This does as same as Browser::ReloadInternal.
1608 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1609 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1610 content::PAGE_TRANSITION_RELOAD, false);
1611 GetCurrentBrowser()->OpenURL(params);
1612 } else if (bypass_cache) {
1613 web_contents->GetController().ReloadIgnoringCache(true);
1615 web_contents->GetController().Reload(true);
1621 bool TabsRemoveFunction::RunImpl() {
1622 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1623 EXTENSION_FUNCTION_VALIDATE(params.get());
1625 if (params->tab_ids.as_integers) {
1626 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1627 for (size_t i = 0; i < tab_ids.size(); ++i) {
1628 if (!RemoveTab(tab_ids[i]))
1632 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1633 if (!RemoveTab(*params->tab_ids.as_integer.get()))
1639 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1640 Browser* browser = NULL;
1641 WebContents* contents = NULL;
1642 if (!GetTabById(tab_id,
1644 include_incognito(),
1653 // Don't let the extension remove a tab if the user is dragging tabs around.
1654 if (!browser->window()->IsTabStripEditable()) {
1655 error_ = keys::kTabStripNotEditableError;
1658 // There's a chance that the tab is being dragged, or we're in some other
1659 // nested event loop. This code path ensures that the tab is safely closed
1660 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1666 bool TabsCaptureVisibleTabFunction::GetTabToCapture(
1667 WebContents** web_contents) {
1668 scoped_ptr<tabs::CaptureVisibleTab::Params> params(
1669 tabs::CaptureVisibleTab::Params::Create(*args_));
1670 EXTENSION_FUNCTION_VALIDATE(params.get());
1672 Browser* browser = NULL;
1673 // windowId defaults to "current" window.
1674 int window_id = extension_misc::kCurrentWindowId;
1676 if (params->window_id.get())
1677 window_id = *params->window_id;
1679 if (!GetBrowserFromWindowID(this, window_id, &browser))
1682 *web_contents = browser->tab_strip_model()->GetActiveWebContents();
1683 if (*web_contents == NULL) {
1684 error_ = keys::kInternalVisibleTabCaptureError;
1691 bool TabsCaptureVisibleTabFunction::RunImpl() {
1692 scoped_ptr<tabs::CaptureVisibleTab::Params> params(
1693 tabs::CaptureVisibleTab::Params::Create(*args_));
1694 EXTENSION_FUNCTION_VALIDATE(params.get());
1696 PrefService* service = GetProfile()->GetPrefs();
1697 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1698 error_ = keys::kScreenshotsDisabled;
1702 WebContents* web_contents = NULL;
1703 if (!GetTabToCapture(&web_contents))
1706 image_format_ = FormatEnum::FORMAT_JPEG; // Default format is JPEG.
1707 image_quality_ = kDefaultQuality; // Default quality setting.
1709 if (params->options.get()) {
1710 if (params->options->format != FormatEnum::FORMAT_NONE)
1711 image_format_ = params->options->format;
1713 if (params->options->quality.get())
1714 image_quality_ = *params->options->quality;
1717 // Use the last committed URL rather than the active URL for permissions
1718 // checking, since the visible page won't be updated until it has been
1719 // committed. A canonical example of this is interstitials, which show the
1720 // URL of the new/loading page (active) but would capture the content of the
1721 // old page (last committed).
1723 // TODO(creis): Use WebContents::GetLastCommittedURL instead.
1724 // http://crbug.com/237908.
1725 NavigationEntry* last_committed_entry =
1726 web_contents->GetController().GetLastCommittedEntry();
1727 GURL last_committed_url = last_committed_entry ?
1728 last_committed_entry->GetURL() : GURL();
1729 if (!PermissionsData::CanCaptureVisiblePage(
1732 SessionID::IdForTab(web_contents),
1737 RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
1738 content::RenderWidgetHostView* view = render_view_host->GetView();
1740 error_ = keys::kInternalVisibleTabCaptureError;
1743 render_view_host->CopyFromBackingStore(
1745 view->GetViewBounds().size(),
1746 base::Bind(&TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete,
1751 void TabsCaptureVisibleTabFunction::CopyFromBackingStoreComplete(
1753 const SkBitmap& bitmap) {
1755 VLOG(1) << "captureVisibleTab() got image from backing store.";
1756 SendResultFromBitmap(bitmap);
1760 WebContents* web_contents = NULL;
1761 if (!GetTabToCapture(&web_contents)) {
1762 SendInternalError();
1766 // Ask the renderer for a snapshot of the tab.
1767 content::RenderWidgetHost* render_widget_host =
1768 web_contents->GetRenderViewHost();
1769 if (!render_widget_host) {
1770 SendInternalError();
1774 render_widget_host->GetSnapshotFromRenderer(
1777 &TabsCaptureVisibleTabFunction::GetSnapshotFromRendererComplete,
1781 // If a backing store was not available in
1782 // TabsCaptureVisibleTabFunction::RunImpl, than the renderer was asked for a
1784 void TabsCaptureVisibleTabFunction::GetSnapshotFromRendererComplete(
1786 const SkBitmap& bitmap) {
1788 SendInternalError();
1790 VLOG(1) << "captureVisibleTab() got image from renderer.";
1791 SendResultFromBitmap(bitmap);
1795 void TabsCaptureVisibleTabFunction::SendInternalError() {
1796 error_ = keys::kInternalVisibleTabCaptureError;
1797 SendResponse(false);
1800 // Turn a bitmap of the screen into an image, set that image as the result,
1801 // and call SendResponse().
1802 void TabsCaptureVisibleTabFunction::SendResultFromBitmap(
1803 const SkBitmap& screen_capture) {
1804 std::vector<unsigned char> data;
1805 SkAutoLockPixels screen_capture_lock(screen_capture);
1806 bool encoded = false;
1807 std::string mime_type;
1808 switch (image_format_) {
1809 case FormatEnum::FORMAT_JPEG:
1810 encoded = gfx::JPEGCodec::Encode(
1811 reinterpret_cast<unsigned char*>(screen_capture.getAddr32(0, 0)),
1812 gfx::JPEGCodec::FORMAT_SkBitmap,
1813 screen_capture.width(),
1814 screen_capture.height(),
1815 static_cast<int>(screen_capture.rowBytes()),
1818 mime_type = keys::kMimeTypeJpeg;
1820 case FormatEnum::FORMAT_PNG:
1821 encoded = gfx::PNGCodec::EncodeBGRASkBitmap(
1823 true, // Discard transparency.
1825 mime_type = keys::kMimeTypePng;
1828 NOTREACHED() << "Invalid image format.";
1832 error_ = keys::kInternalVisibleTabCaptureError;
1833 SendResponse(false);
1837 std::string base64_result;
1838 base::StringPiece stream_as_string(
1839 reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
1841 base::Base64Encode(stream_as_string, &base64_result);
1842 base64_result.insert(0, base::StringPrintf("data:%s;base64,",
1843 mime_type.c_str()));
1844 SetResult(new StringValue(base64_result));
1848 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1849 user_prefs::PrefRegistrySyncable* registry) {
1850 registry->RegisterBooleanPref(
1851 prefs::kDisableScreenshots,
1853 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1856 bool TabsDetectLanguageFunction::RunImpl() {
1857 scoped_ptr<tabs::DetectLanguage::Params> params(
1858 tabs::DetectLanguage::Params::Create(*args_));
1859 EXTENSION_FUNCTION_VALIDATE(params.get());
1862 Browser* browser = NULL;
1863 WebContents* contents = NULL;
1865 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1866 // in the current window.
1867 if (params->tab_id.get()) {
1868 tab_id = *params->tab_id;
1869 if (!GetTabById(tab_id,
1871 include_incognito(),
1879 if (!browser || !contents)
1882 browser = GetCurrentBrowser();
1885 contents = browser->tab_strip_model()->GetActiveWebContents();
1890 if (contents->GetController().NeedsReload()) {
1891 // If the tab hasn't been loaded, don't wait for the tab to load.
1892 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1896 AddRef(); // Balanced in GotLanguage().
1898 TranslateTabHelper* translate_tab_helper =
1899 TranslateTabHelper::FromWebContents(contents);
1900 if (!translate_tab_helper->language_state().original_language().empty()) {
1901 // Delay the callback invocation until after the current JS call has
1903 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
1904 &TabsDetectLanguageFunction::GotLanguage, this,
1905 translate_tab_helper->language_state().original_language()));
1908 // The tab contents does not know its language yet. Let's wait until it
1909 // receives it, or until the tab is closed/navigates to some other page.
1910 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1911 content::Source<WebContents>(contents));
1913 this, chrome::NOTIFICATION_TAB_CLOSING,
1914 content::Source<NavigationController>(&(contents->GetController())));
1916 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1917 content::Source<NavigationController>(&(contents->GetController())));
1921 void TabsDetectLanguageFunction::Observe(
1923 const content::NotificationSource& source,
1924 const content::NotificationDetails& details) {
1925 std::string language;
1926 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1927 const LanguageDetectionDetails* lang_det_details =
1928 content::Details<const LanguageDetectionDetails>(details).ptr();
1929 language = lang_det_details->adopted_language;
1932 registrar_.RemoveAll();
1934 // Call GotLanguage in all cases as we want to guarantee the callback is
1935 // called for every API call the extension made.
1936 GotLanguage(language);
1939 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1940 SetResult(new base::StringValue(language.c_str()));
1943 Release(); // Balanced in Run()
1946 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1947 : execute_tab_id_(-1) {
1950 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1952 bool ExecuteCodeInTabFunction::HasPermission() {
1953 if (Init() && PermissionsData::HasAPIPermissionForTab(
1954 extension_.get(), execute_tab_id_, APIPermission::kTab)) {
1957 return ExtensionFunction::HasPermission();
1960 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1961 content::WebContents* contents = NULL;
1963 // If |tab_id| is specified, look for the tab. Otherwise default to selected
1964 // tab in the current window.
1965 CHECK_GE(execute_tab_id_, 0);
1966 if (!GetTabById(execute_tab_id_,
1968 include_incognito(),
1979 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1980 // we check again in the renderer.
1981 content::RenderProcessHost* process = contents->GetRenderProcessHost();
1982 if (!PermissionsData::CanExecuteScriptOnPage(
1988 process ? process->GetID() : -1,
1996 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1997 Browser* browser = NULL;
1998 content::WebContents* contents = NULL;
2000 bool success = GetTabById(execute_tab_id_,
2002 include_incognito(),
2008 contents && browser;
2013 return TabHelper::FromWebContents(contents)->script_executor();
2016 bool ExecuteCodeInTabFunction::IsWebView() const {
2020 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
2024 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
2025 const std::string& error,
2028 const base::ListValue& result) {
2030 SetResult(result.DeepCopy());
2031 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
2035 bool ExecuteCodeInTabFunction::Init() {
2039 // |tab_id| is optional so it's ok if it's not there.
2041 if (args_->GetInteger(0, &tab_id))
2042 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
2044 // |details| are not optional.
2045 base::DictionaryValue* details_value = NULL;
2046 if (!args_->GetDictionary(1, &details_value))
2048 scoped_ptr<InjectDetails> details(new InjectDetails());
2049 if (!InjectDetails::Populate(*details_value, details.get()))
2052 // If the tab ID wasn't given then it needs to be converted to the
2053 // currently active tab's ID.
2055 Browser* browser = GetCurrentBrowser();
2058 content::WebContents* web_contents = NULL;
2059 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
2063 execute_tab_id_ = tab_id;
2064 details_ = details.Pass();
2068 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
2072 } // namespace extensions