1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <vector>
10
11 #include "base/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"
87
88 #if defined(USE_ASH)
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"
92 #endif
93
94 using content::BrowserThread;
95 using content::NavigationController;
96 using content::NavigationEntry;
97 using content::OpenURLParams;
98 using content::Referrer;
99 using content::WebContents;
100
101 namespace extensions {
102
103 namespace windows = api::windows;
104 namespace keys = tabs_constants;
105 namespace tabs = api::tabs;
106
107 using core_api::extension_types::InjectDetails;
108
109 namespace {
110
GetBrowserFromWindowID(ChromeUIThreadExtensionFunction * function,int window_id,Browser ** browser)111 bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction* function,
112 int window_id,
113 Browser** browser) {
114 std::string error;
115 Browser* result;
116 result =
117 ExtensionTabUtil::GetBrowserFromWindowID(function, window_id, &error);
118 if (!result) {
119 function->SetError(error);
120 return false;
121 }
122
123 *browser = result;
124 return true;
125 }
126
GetBrowserFromWindowID(ChromeExtensionFunctionDetails * details,int window_id,Browser ** browser)127 bool GetBrowserFromWindowID(ChromeExtensionFunctionDetails* details,
128 int window_id,
129 Browser** browser) {
130 std::string error;
131 Browser* result;
132 result =
133 ExtensionTabUtil::GetBrowserFromWindowID(*details, window_id, &error);
134 if (!result) {
135 details->function()->SetError(error);
136 return false;
137 }
138
139 *browser = result;
140 return true;
141 }
142
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.
GetTabById(int tab_id,Profile * profile,bool include_incognito,Browser ** browser,TabStripModel ** tab_strip,content::WebContents ** contents,int * tab_index,std::string * error_message)145 bool GetTabById(int tab_id,
146 Profile* profile,
147 bool include_incognito,
148 Browser** browser,
149 TabStripModel** tab_strip,
150 content::WebContents** contents,
151 int* tab_index,
152 std::string* error_message) {
153 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
154 browser, tab_strip, contents, tab_index)) {
155 return true;
156 }
157
158 if (error_message) {
159 *error_message = ErrorUtils::FormatErrorMessage(
160 keys::kTabNotFoundError, base::IntToString(tab_id));
161 }
162
163 return false;
164 }
165
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.
MatchesBool(bool * boolean,bool value)169 bool MatchesBool(bool* boolean, bool value) {
170 return !boolean || *boolean == value;
171 }
172
173 template <typename T>
AssignOptionalValue(const scoped_ptr<T> & source,scoped_ptr<T> & destination)174 void AssignOptionalValue(const scoped_ptr<T>& source,
175 scoped_ptr<T>& destination) {
176 if (source.get()) {
177 destination.reset(new T(*source.get()));
178 }
179 }
180
181 } // namespace
182
ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,api::tabs::ZoomSettings * zoom_settings)183 void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,
184 api::tabs::ZoomSettings* zoom_settings) {
185 DCHECK(zoom_settings);
186 switch (zoom_mode) {
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;
190 break;
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;
194 break;
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;
198 break;
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;
202 break;
203 }
204 }
205
206 // Windows ---------------------------------------------------------------------
207
RunSync()208 bool WindowsGetFunction::RunSync() {
209 scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
210 EXTENSION_FUNCTION_VALIDATE(params.get());
211
212 bool populate_tabs = false;
213 if (params->get_info.get() && params->get_info->populate.get())
214 populate_tabs = *params->get_info->populate;
215
216 WindowController* controller;
217 if (!windows_util::GetWindowFromWindowID(this,
218 params->window_id,
219 &controller)) {
220 return false;
221 }
222
223 if (populate_tabs)
224 SetResult(controller->CreateWindowValueWithTabs(extension()));
225 else
226 SetResult(controller->CreateWindowValue());
227 return true;
228 }
229
RunSync()230 bool WindowsGetCurrentFunction::RunSync() {
231 scoped_ptr<windows::GetCurrent::Params> params(
232 windows::GetCurrent::Params::Create(*args_));
233 EXTENSION_FUNCTION_VALIDATE(params.get());
234
235 bool populate_tabs = false;
236 if (params->get_info.get() && params->get_info->populate.get())
237 populate_tabs = *params->get_info->populate;
238
239 WindowController* controller;
240 if (!windows_util::GetWindowFromWindowID(this,
241 extension_misc::kCurrentWindowId,
242 &controller)) {
243 return false;
244 }
245 if (populate_tabs)
246 SetResult(controller->CreateWindowValueWithTabs(extension()));
247 else
248 SetResult(controller->CreateWindowValue());
249 return true;
250 }
251
RunSync()252 bool WindowsGetLastFocusedFunction::RunSync() {
253 scoped_ptr<windows::GetLastFocused::Params> params(
254 windows::GetLastFocused::Params::Create(*args_));
255 EXTENSION_FUNCTION_VALIDATE(params.get());
256
257 bool populate_tabs = false;
258 if (params->get_info.get() && params->get_info->populate.get())
259 populate_tabs = *params->get_info->populate;
260
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;
268 return false;
269 }
270 WindowController* controller =
271 browser->extension_window_controller();
272 if (populate_tabs)
273 SetResult(controller->CreateWindowValueWithTabs(extension()));
274 else
275 SetResult(controller->CreateWindowValue());
276 return true;
277 }
278
RunSync()279 bool WindowsGetAllFunction::RunSync() {
280 scoped_ptr<windows::GetAll::Params> params(
281 windows::GetAll::Params::Create(*args_));
282 EXTENSION_FUNCTION_VALIDATE(params.get());
283
284 bool populate_tabs = false;
285 if (params->get_info.get() && params->get_info->populate.get())
286 populate_tabs = *params->get_info->populate;
287
288 base::ListValue* window_list = new base::ListValue();
289 const WindowControllerList::ControllerList& windows =
290 WindowControllerList::GetInstance()->windows();
291 for (WindowControllerList::ControllerList::const_iterator iter =
292 windows.begin();
293 iter != windows.end(); ++iter) {
294 if (!this->CanOperateOnWindow(*iter))
295 continue;
296 if (populate_tabs)
297 window_list->Append((*iter)->CreateWindowValueWithTabs(extension()));
298 else
299 window_list->Append((*iter)->CreateWindowValue());
300 }
301 SetResult(window_list);
302 return true;
303 }
304
ShouldOpenIncognitoWindow(const windows::Create::Params::CreateData * create_data,std::vector<GURL> * urls,bool * is_error)305 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
306 const windows::Create::Params::CreateData* create_data,
307 std::vector<GURL>* urls, bool* is_error) {
308 *is_error = false;
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;
316 *is_error = true;
317 return false;
318 }
319 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
320 error_ = keys::kIncognitoModeIsForced;
321 *is_error = true;
322 return false;
323 }
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.
327 incognito = true;
328 }
329
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())) {
336 i++;
337 } else {
338 if (first_url_erased.empty())
339 first_url_erased = (*urls)[i].spec();
340 urls->erase(urls->begin() + i);
341 }
342 }
343 if (urls->empty() && !first_url_erased.empty()) {
344 error_ = ErrorUtils::FormatErrorMessage(
345 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
346 *is_error = true;
347 return false;
348 }
349 }
350 return incognito;
351 }
352
RunSync()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;
359 int tab_index = -1;
360
361 windows::Create::Params::CreateData* create_data = params->create_data.get();
362
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);
371
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);
378 return false;
379 }
380 // Don't let the extension crash the browser or renderers.
381 if (ExtensionTabUtil::IsCrashURL(url)) {
382 error_ = keys::kNoCrashBrowserError;
383 return false;
384 }
385 urls.push_back(url);
386 }
387 }
388
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,
394 GetProfile(),
395 include_incognito(),
396 NULL,
397 &source_tab_strip,
398 NULL,
399 &tab_index,
400 &error_))
401 return false;
402 }
403
404 Profile* window_profile = GetProfile();
405 Browser::Type window_type = Browser::TYPE_TABBED;
406 bool create_panel = false;
407
408 // panel_create_mode only applies if create_panel = true
409 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
410
411 gfx::Rect window_bounds;
412 bool focused = true;
413 bool saw_focus_key = false;
414 std::string extension_id;
415
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,
419 &is_error);
420 if (is_error) {
421 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
422 return false;
423 }
424 if (open_incognito_window) {
425 window_profile = window_profile->GetOffTheRecordProfile();
426 }
427
428 if (create_data) {
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();
435 break;
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);
440 if (use_panels) {
441 create_panel = true;
442 // Non-ash supports both docked and detached panel types.
443 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
444 create_data->type ==
445 windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
446 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
447 }
448 } else {
449 window_type = Browser::TYPE_POPUP;
450 }
451 break;
452 }
453 case windows::Create::Params::CreateData::TYPE_NONE:
454 case windows::Create::Params::CreateData::TYPE_NORMAL:
455 break;
456 default:
457 error_ = keys::kInvalidWindowTypeError;
458 return false;
459 }
460
461 // Initialize default window bounds according to window type.
462 if (window_type == Browser::TYPE_TABBED ||
463 window_type == Browser::TYPE_POPUP ||
464 create_panel) {
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).
468 //
469 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
470 // GetBrowserWindowBounds will default to saved "default" values for
471 // the app.
472 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
473 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
474 gfx::Rect(),
475 GetCurrentBrowser(),
476 &window_bounds,
477 &show_state);
478 }
479
480 if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
481 window_bounds.set_origin(
482 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
483 }
484
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);
488
489 if (create_data->top)
490 window_bounds.set_y(*create_data->top);
491
492 if (create_data->width)
493 window_bounds.set_width(*create_data->width);
494
495 if (create_data->height)
496 window_bounds.set_height(*create_data->height);
497
498 if (create_data->focused) {
499 focused = *create_data->focused;
500 saw_focus_key = true;
501 }
502 }
503
504 if (create_panel) {
505 if (urls.empty())
506 urls.push_back(GURL(chrome::kChromeUINewTabURL));
507
508 #if defined(USE_ASH)
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(
515 window_profile,
516 new ChromeAppDelegate(make_scoped_ptr(new ScopedKeepAlive)),
517 extension());
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()));
522 return true;
523 }
524 #endif
525 std::string title =
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);
530
531 // Unlike other window types, Panels do not take focus by default.
532 if (!saw_focus_key || !focused)
533 panel->ShowInactive();
534 else
535 panel->Show();
536
537 SetResult(panel->extension_window_controller()->CreateWindowValueWithTabs(
538 extension()));
539 return true;
540 }
541
542 // Create a new BrowserWindow.
543 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
544 if (create_panel)
545 window_type = Browser::TYPE_POPUP;
546 Browser::CreateParams create_params(window_type, window_profile,
547 host_desktop_type);
548 if (extension_id.empty()) {
549 create_params.initial_bounds = window_bounds;
550 } else {
551 create_params = Browser::CreateParams::CreateForApp(
552 web_app::GenerateApplicationNameFromExtensionId(extension_id),
553 false /* trusted_source */,
554 window_bounds,
555 window_profile,
556 host_desktop_type);
557 }
558 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
559 create_params.host_desktop_type = chrome::GetActiveDesktop();
560
561 Browser* new_window = new Browser(create_params);
562
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);
566 if (create_panel) {
567 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
568 }
569 }
570
571 WebContents* contents = NULL;
572 // Move the tab into the created window only if it's an empty popup or it's
573 // a tabbed window.
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);
578 if (contents) {
579 TabStripModel* target_tab_strip = new_window->tab_strip_model();
580 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
581 TabStripModel::ADD_NONE);
582 }
583 }
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);
588 }
589 chrome::SelectNumberedTab(new_window, 0);
590
591 // Unlike other window types, Panels do not take focus by default.
592 if (!saw_focus_key && create_panel)
593 focused = false;
594
595 if (focused)
596 new_window->window()->Show();
597 else
598 new_window->window()->ShowInactive();
599
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());
605 } else {
606 SetResult(
607 new_window->extension_window_controller()->CreateWindowValueWithTabs(
608 extension()));
609 }
610
611 return true;
612 }
613
RunSync()614 bool WindowsUpdateFunction::RunSync() {
615 scoped_ptr<windows::Update::Params> params(
616 windows::Update::Params::Create(*args_));
617 EXTENSION_FUNCTION_VALIDATE(params);
618
619 WindowController* controller;
620 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
621 &controller))
622 return false;
623
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;
628 break;
629 case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
630 show_state = ui::SHOW_STATE_MINIMIZED;
631 break;
632 case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
633 show_state = ui::SHOW_STATE_MAXIMIZED;
634 break;
635 case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
636 show_state = ui::SHOW_STATE_FULLSCREEN;
637 break;
638 case windows::Update::Params::UpdateInfo::STATE_NONE:
639 break;
640 default:
641 error_ = keys::kInvalidWindowStateError;
642 return false;
643 }
644
645 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
646 show_state != ui::SHOW_STATE_DEFAULT)
647 controller->SetFullscreenMode(false, extension()->url());
648
649 switch (show_state) {
650 case ui::SHOW_STATE_MINIMIZED:
651 controller->window()->Minimize();
652 break;
653 case ui::SHOW_STATE_MAXIMIZED:
654 controller->window()->Maximize();
655 break;
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());
661 break;
662 case ui::SHOW_STATE_NORMAL:
663 controller->window()->Restore();
664 break;
665 default:
666 break;
667 }
668
669 gfx::Rect bounds;
670 if (controller->window()->IsMinimized())
671 bounds = controller->window()->GetRestoredBounds();
672 else
673 bounds = controller->window()->GetBounds();
674 bool set_bounds = false;
675
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);
679 set_bounds = true;
680 }
681
682 if (params->update_info.top) {
683 bounds.set_y(*params->update_info.top);
684 set_bounds = true;
685 }
686
687 if (params->update_info.width) {
688 bounds.set_width(*params->update_info.width);
689 set_bounds = true;
690 }
691
692 if (params->update_info.height) {
693 bounds.set_height(*params->update_info.height);
694 set_bounds = true;
695 }
696
697 if (set_bounds) {
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;
702 return false;
703 }
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);
707 }
708
709 if (params->update_info.focused) {
710 if (*params->update_info.focused) {
711 if (show_state == ui::SHOW_STATE_MINIMIZED) {
712 error_ = keys::kInvalidWindowStateError;
713 return false;
714 }
715 controller->window()->Activate();
716 } else {
717 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
718 show_state == ui::SHOW_STATE_FULLSCREEN) {
719 error_ = keys::kInvalidWindowStateError;
720 return false;
721 }
722 controller->window()->Deactivate();
723 }
724 }
725
726 if (params->update_info.draw_attention)
727 controller->window()->FlashFrame(*params->update_info.draw_attention);
728
729 SetResult(controller->CreateWindowValue());
730
731 return true;
732 }
733
RunSync()734 bool WindowsRemoveFunction::RunSync() {
735 scoped_ptr<windows::Remove::Params> params(
736 windows::Remove::Params::Create(*args_));
737 EXTENSION_FUNCTION_VALIDATE(params);
738
739 WindowController* controller;
740 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
741 &controller))
742 return false;
743
744 WindowController::Reason reason;
745 if (!controller->CanClose(&reason)) {
746 if (reason == WindowController::REASON_NOT_EDITABLE)
747 error_ = keys::kTabStripNotEditableError;
748 return false;
749 }
750 controller->window()->Close();
751 return true;
752 }
753
754 // Tabs ------------------------------------------------------------------------
755
RunSync()756 bool TabsGetSelectedFunction::RunSync() {
757 // windowId defaults to "current" window.
758 int window_id = extension_misc::kCurrentWindowId;
759
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;
765
766 Browser* browser = NULL;
767 if (!GetBrowserFromWindowID(this, window_id, &browser))
768 return false;
769
770 TabStripModel* tab_strip = browser->tab_strip_model();
771 WebContents* contents = tab_strip->GetActiveWebContents();
772 if (!contents) {
773 error_ = keys::kNoSelectedTabError;
774 return false;
775 }
776 SetResult(ExtensionTabUtil::CreateTabValue(
777 contents, tab_strip, tab_strip->active_index(), extension()));
778 return true;
779 }
780
RunSync()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;
789
790 Browser* browser = NULL;
791 if (!GetBrowserFromWindowID(this, window_id, &browser))
792 return false;
793
794 SetResult(ExtensionTabUtil::CreateTabList(browser, extension()));
795
796 return true;
797 }
798
RunSync()799 bool TabsQueryFunction::RunSync() {
800 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
801 EXTENSION_FUNCTION_VALIDATE(params.get());
802
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;
807
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
817 // and meta data.
818 if (!url_patterns.Populate(url_pattern_strings, URLPattern::SCHEME_ALL,
819 true, &error_)) {
820 return false;
821 }
822 }
823
824 std::string title;
825 if (params->query_info.title.get())
826 title = *params->query_info.title;
827
828 int window_id = extension_misc::kUnknownWindowId;
829 if (params->query_info.window_id.get())
830 window_id = *params->query_info.window_id;
831
832 int index = -1;
833 if (params->query_info.index.get())
834 index = *params->query_info.index;
835
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);
841 }
842
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()))
850 continue;
851
852 if (!browser->window())
853 continue;
854
855 if (!include_incognito() && GetProfile() != browser->profile())
856 continue;
857
858 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
859 continue;
860
861 if (window_id == extension_misc::kCurrentWindowId &&
862 browser != current_browser) {
863 continue;
864 }
865
866 if (!MatchesBool(params->query_info.current_window.get(),
867 browser == current_browser)) {
868 continue;
869 }
870
871 if (!MatchesBool(params->query_info.last_focused_window.get(),
872 browser == last_active_browser)) {
873 continue;
874 }
875
876 if (!window_type.empty() &&
877 window_type !=
878 browser->extension_window_controller()->GetWindowTypeText()) {
879 continue;
880 }
881
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);
885
886 if (index > -1 && i != index)
887 continue;
888
889 if (!MatchesBool(params->query_info.highlighted.get(),
890 tab_strip->IsTabSelected(i))) {
891 continue;
892 }
893
894 if (!MatchesBool(params->query_info.active.get(),
895 i == tab_strip->active_index())) {
896 continue;
897 }
898
899 if (!MatchesBool(params->query_info.pinned.get(),
900 tab_strip->IsTabPinned(i))) {
901 continue;
902 }
903
904 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
905 base::UTF8ToUTF16(title)))
906 continue;
907
908 if (!url_patterns.is_empty() &&
909 !url_patterns.MatchesURL(web_contents->GetURL()))
910 continue;
911
912 if (loading_status_set && loading != web_contents->IsLoading())
913 continue;
914
915 result->Append(ExtensionTabUtil::CreateTabValue(
916 web_contents, tab_strip, i, extension()));
917 }
918 }
919
920 SetResult(result);
921 return true;
922 }
923
RunSync()924 bool TabsCreateFunction::RunSync() {
925 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
926 EXTENSION_FUNCTION_VALIDATE(params.get());
927
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);
938
939 std::string error;
940 scoped_ptr<base::DictionaryValue> result(
941 ExtensionTabUtil::OpenTab(this, options, &error));
942 if (!result) {
943 SetError(error);
944 return false;
945 }
946
947 // Return data about the newly created tab.
948 if (has_callback()) {
949 SetResult(result.release());
950 }
951 return true;
952 }
953
RunSync()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;
959
960 Browser* browser = NULL;
961 TabStripModel* tab_strip = NULL;
962 int tab_index = -1;
963 if (!GetTabById(tab_id,
964 GetProfile(),
965 include_incognito(),
966 &browser,
967 &tab_strip,
968 NULL,
969 &tab_index,
970 &error_)) {
971 return false;
972 }
973
974 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
975 if (!has_callback())
976 return true;
977
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,
983 &new_tab_strip,
984 &new_tab_index);
985 if (!new_tab_strip || new_tab_index == -1) {
986 return false;
987 }
988
989 // Return data about the newly created tab.
990 SetResult(ExtensionTabUtil::CreateTabValue(
991 new_contents, new_tab_strip, new_tab_index, extension()));
992
993 return true;
994 }
995
RunSync()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;
1000
1001 TabStripModel* tab_strip = NULL;
1002 WebContents* contents = NULL;
1003 int tab_index = -1;
1004 if (!GetTabById(tab_id,
1005 GetProfile(),
1006 include_incognito(),
1007 NULL,
1008 &tab_strip,
1009 &contents,
1010 &tab_index,
1011 &error_))
1012 return false;
1013
1014 SetResult(ExtensionTabUtil::CreateTabValue(
1015 contents, tab_strip, tab_index, extension()));
1016 return true;
1017 }
1018
RunSync()1019 bool TabsGetCurrentFunction::RunSync() {
1020 DCHECK(dispatcher());
1021
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()));
1028
1029 return true;
1030 }
1031
RunSync()1032 bool TabsHighlightFunction::RunSync() {
1033 scoped_ptr<tabs::Highlight::Params> params(
1034 tabs::Highlight::Params::Create(*args_));
1035 EXTENSION_FUNCTION_VALIDATE(params.get());
1036
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;
1041
1042 Browser* browser = NULL;
1043 if (!GetBrowserFromWindowID(this, window_id, &browser))
1044 return false;
1045
1046 TabStripModel* tabstrip = browser->tab_strip_model();
1047 ui::ListSelectionModel selection;
1048 int active_index = -1;
1049
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]))
1055 return false;
1056 }
1057 } else {
1058 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1059 if (!HighlightTab(tabstrip,
1060 &selection,
1061 &active_index,
1062 *params->highlight_info.tabs.as_integer)) {
1063 return false;
1064 }
1065 }
1066
1067 // Make sure they actually specified tabs to select.
1068 if (selection.empty()) {
1069 error_ = keys::kNoHighlightedTabError;
1070 return false;
1071 }
1072
1073 selection.set_active(active_index);
1074 browser->tab_strip_model()->SetSelectionFromModel(selection);
1075 SetResult(browser->extension_window_controller()->CreateWindowValueWithTabs(
1076 extension()));
1077 return true;
1078 }
1079
HighlightTab(TabStripModel * tabstrip,ui::ListSelectionModel * selection,int * active_index,int index)1080 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1081 ui::ListSelectionModel* selection,
1082 int* active_index,
1083 int index) {
1084 // Make sure the index is in range.
1085 if (!tabstrip->ContainsIndex(index)) {
1086 error_ = ErrorUtils::FormatErrorMessage(
1087 keys::kTabIndexNotFoundError, base::IntToString(index));
1088 return false;
1089 }
1090
1091 // By default, we make the first tab in the list active.
1092 if (*active_index == -1)
1093 *active_index = index;
1094
1095 selection->AddIndexToSelection(index);
1096 return true;
1097 }
1098
TabsUpdateFunction()1099 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1100 }
1101
RunAsync()1102 bool TabsUpdateFunction::RunAsync() {
1103 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1104 EXTENSION_FUNCTION_VALIDATE(params.get());
1105
1106 int tab_id = -1;
1107 WebContents* contents = NULL;
1108 if (!params->tab_id.get()) {
1109 Browser* browser = GetCurrentBrowser();
1110 if (!browser) {
1111 error_ = keys::kNoCurrentWindowError;
1112 return false;
1113 }
1114 contents = browser->tab_strip_model()->GetActiveWebContents();
1115 if (!contents) {
1116 error_ = keys::kNoSelectedTabError;
1117 return false;
1118 }
1119 tab_id = SessionTabHelper::IdForTab(contents);
1120 } else {
1121 tab_id = *params->tab_id;
1122 }
1123
1124 int tab_index = -1;
1125 TabStripModel* tab_strip = NULL;
1126 if (!GetTabById(tab_id,
1127 GetProfile(),
1128 include_incognito(),
1129 NULL,
1130 &tab_strip,
1131 &contents,
1132 &tab_index,
1133 &error_)) {
1134 return false;
1135 }
1136
1137 web_contents_ = contents;
1138
1139 // TODO(rafaelw): handle setting remaining tab properties:
1140 // -title
1141 // -favIconUrl
1142
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)) {
1147 return false;
1148 }
1149
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;
1155
1156 // The 'active' property has replaced 'selected'.
1157 if (params->update_properties.active.get())
1158 active = *params->update_properties.active;
1159
1160 if (active) {
1161 if (tab_strip->active_index() != tab_index) {
1162 tab_strip->ActivateTabAt(tab_index, false);
1163 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1164 }
1165 }
1166
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);
1171 }
1172
1173 if (params->update_properties.pinned.get()) {
1174 bool pinned = *params->update_properties.pinned;
1175 tab_strip->SetTabPinned(tab_index, pinned);
1176
1177 // Update the tab index because it may move when being pinned.
1178 tab_index = tab_strip->GetIndexOfWebContents(contents);
1179 }
1180
1181 if (params->update_properties.opener_tab_id.get()) {
1182 int opener_id = *params->update_properties.opener_tab_id;
1183
1184 WebContents* opener_contents = NULL;
1185 if (!ExtensionTabUtil::GetTabById(opener_id,
1186 GetProfile(),
1187 include_incognito(),
1188 NULL,
1189 NULL,
1190 &opener_contents,
1191 NULL))
1192 return false;
1193
1194 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1195 }
1196
1197 if (!is_async) {
1198 PopulateResult();
1199 SendResponse(true);
1200 }
1201 return true;
1202 }
1203
UpdateURL(const std::string & url_string,int tab_id,bool * is_async)1204 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1205 int tab_id,
1206 bool* is_async) {
1207 GURL url =
1208 ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, extension());
1209
1210 if (!url.is_valid()) {
1211 error_ = ErrorUtils::FormatErrorMessage(
1212 keys::kInvalidUrlError, url_string);
1213 return false;
1214 }
1215
1216 // Don't let the extension crash the browser or renderers.
1217 if (ExtensionTabUtil::IsCrashURL(url)) {
1218 error_ = keys::kNoCrashBrowserError;
1219 return false;
1220 }
1221
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(
1227 extension(),
1228 web_contents_->GetURL(),
1229 web_contents_->GetURL(),
1230 tab_id,
1231 process ? process->GetID() : -1,
1232 &error_)) {
1233 return false;
1234 }
1235
1236 TabHelper::FromWebContents(web_contents_)->script_executor()->ExecuteScript(
1237 extension_id(),
1238 ScriptExecutor::JAVASCRIPT,
1239 url.GetContent(),
1240 ScriptExecutor::TOP_FRAME,
1241 ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
1242 UserScript::DOCUMENT_IDLE,
1243 ScriptExecutor::MAIN_WORLD,
1244 ScriptExecutor::DEFAULT_PROCESS,
1245 GURL(),
1246 GURL(),
1247 user_gesture_,
1248 ScriptExecutor::NO_RESULT,
1249 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1250
1251 *is_async = true;
1252 return true;
1253 }
1254
1255 web_contents_->GetController().LoadURL(
1256 url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1257
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());
1262
1263 return true;
1264 }
1265
PopulateResult()1266 void TabsUpdateFunction::PopulateResult() {
1267 if (!has_callback())
1268 return;
1269
1270 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, extension()));
1271 }
1272
OnExecuteCodeFinished(const std::string & error,const GURL & url,const base::ListValue & script_result)1273 void TabsUpdateFunction::OnExecuteCodeFinished(
1274 const std::string& error,
1275 const GURL& url,
1276 const base::ListValue& script_result) {
1277 if (error.empty())
1278 PopulateResult();
1279 else
1280 error_ = error;
1281 SendResponse(error.empty());
1282 }
1283
RunSync()1284 bool TabsMoveFunction::RunSync() {
1285 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1286 EXTENSION_FUNCTION_VALIDATE(params.get());
1287
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());
1291
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))
1298 return false;
1299 }
1300 } else {
1301 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1302 num_tabs = 1;
1303 if (!MoveTab(*params->tab_ids.as_integer,
1304 &new_index,
1305 0,
1306 tab_values.get(),
1307 window_id)) {
1308 return false;
1309 }
1310 }
1311
1312 if (!has_callback())
1313 return true;
1314
1315 if (num_tabs == 0) {
1316 error_ = "No tabs given.";
1317 return false;
1318 } else if (num_tabs == 1) {
1319 scoped_ptr<base::Value> value;
1320 CHECK(tab_values.get()->Remove(0, &value));
1321 SetResult(value.release());
1322 } else {
1323 // Only return the results as an array if there are multiple tabs.
1324 SetResult(tab_values.release());
1325 }
1326
1327 return true;
1328 }
1329
MoveTab(int tab_id,int * new_index,int iteration,base::ListValue * tab_values,int * window_id)1330 bool TabsMoveFunction::MoveTab(int tab_id,
1331 int* new_index,
1332 int iteration,
1333 base::ListValue* tab_values,
1334 int* window_id) {
1335 Browser* source_browser = NULL;
1336 TabStripModel* source_tab_strip = NULL;
1337 WebContents* contents = NULL;
1338 int tab_index = -1;
1339 if (!GetTabById(tab_id,
1340 GetProfile(),
1341 include_incognito(),
1342 &source_browser,
1343 &source_tab_strip,
1344 &contents,
1345 &tab_index,
1346 &error_)) {
1347 return false;
1348 }
1349
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;
1353 return false;
1354 }
1355
1356 // Insert the tabs one after another.
1357 *new_index += iteration;
1358
1359 if (window_id) {
1360 Browser* target_browser = NULL;
1361
1362 if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1363 return false;
1364
1365 if (!target_browser->window()->IsTabStripEditable()) {
1366 error_ = keys::kTabStripNotEditableError;
1367 return false;
1368 }
1369
1370 if (!target_browser->is_type_tabbed()) {
1371 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1372 return false;
1373 }
1374
1375 if (target_browser->profile() != source_browser->profile()) {
1376 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1377 return false;
1378 }
1379
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));
1389 return false;
1390 }
1391
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();
1397
1398 target_tab_strip->InsertWebContentsAt(
1399 *new_index, web_contents, TabStripModel::ADD_NONE);
1400
1401 if (has_callback()) {
1402 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1403 web_contents, target_tab_strip, *new_index, extension()));
1404 }
1405
1406 return true;
1407 }
1408 }
1409
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;
1416
1417 if (*new_index != tab_index)
1418 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1419
1420 if (has_callback()) {
1421 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1422 contents, source_tab_strip, *new_index, extension()));
1423 }
1424
1425 return true;
1426 }
1427
RunSync()1428 bool TabsReloadFunction::RunSync() {
1429 scoped_ptr<tabs::Reload::Params> params(
1430 tabs::Reload::Params::Create(*args_));
1431 EXTENSION_FUNCTION_VALIDATE(params.get());
1432
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;
1437 }
1438
1439 content::WebContents* web_contents = NULL;
1440
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();
1445 if (!browser) {
1446 error_ = keys::kNoCurrentWindowError;
1447 return false;
1448 }
1449
1450 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1451 return false;
1452 } else {
1453 int tab_id = *params->tab_id;
1454
1455 Browser* browser = NULL;
1456 if (!GetTabById(tab_id,
1457 GetProfile(),
1458 include_incognito(),
1459 &browser,
1460 NULL,
1461 &web_contents,
1462 NULL,
1463 &error_)) {
1464 return false;
1465 }
1466 }
1467
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);
1476 } else {
1477 web_contents->GetController().Reload(true);
1478 }
1479
1480 return true;
1481 }
1482
RunSync()1483 bool TabsRemoveFunction::RunSync() {
1484 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1485 EXTENSION_FUNCTION_VALIDATE(params.get());
1486
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]))
1491 return false;
1492 }
1493 } else {
1494 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1495 if (!RemoveTab(*params->tab_ids.as_integer.get()))
1496 return false;
1497 }
1498 return true;
1499 }
1500
RemoveTab(int tab_id)1501 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1502 Browser* browser = NULL;
1503 WebContents* contents = NULL;
1504 if (!GetTabById(tab_id,
1505 GetProfile(),
1506 include_incognito(),
1507 &browser,
1508 NULL,
1509 &contents,
1510 NULL,
1511 &error_)) {
1512 return false;
1513 }
1514
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;
1518 return false;
1519 }
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()|
1523 // does not.
1524 contents->Close();
1525 return true;
1526 }
1527
TabsCaptureVisibleTabFunction()1528 TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
1529 : chrome_details_(this) {
1530 }
1531
IsScreenshotEnabled()1532 bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1533 PrefService* service = chrome_details_.GetProfile()->GetPrefs();
1534 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1535 error_ = keys::kScreenshotsDisabled;
1536 return false;
1537 }
1538 return true;
1539 }
1540
GetWebContentsForID(int window_id)1541 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1542 Browser* browser = NULL;
1543 if (!GetBrowserFromWindowID(&chrome_details_, window_id, &browser))
1544 return NULL;
1545
1546 WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1547 if (!contents) {
1548 error_ = keys::kInternalVisibleTabCaptureError;
1549 return NULL;
1550 }
1551
1552 if (!extension()->permissions_data()->CanCaptureVisiblePage(
1553 SessionTabHelper::IdForTab(contents), &error_)) {
1554 return NULL;
1555 }
1556 return contents;
1557 }
1558
OnCaptureFailure(FailureReason reason)1559 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1560 error_ = keys::kInternalVisibleTabCaptureError;
1561 SendResponse(false);
1562 }
1563
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)1564 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1565 user_prefs::PrefRegistrySyncable* registry) {
1566 registry->RegisterBooleanPref(
1567 prefs::kDisableScreenshots,
1568 false,
1569 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1570 }
1571
RunAsync()1572 bool TabsDetectLanguageFunction::RunAsync() {
1573 scoped_ptr<tabs::DetectLanguage::Params> params(
1574 tabs::DetectLanguage::Params::Create(*args_));
1575 EXTENSION_FUNCTION_VALIDATE(params.get());
1576
1577 int tab_id = 0;
1578 Browser* browser = NULL;
1579 WebContents* contents = NULL;
1580
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,
1586 GetProfile(),
1587 include_incognito(),
1588 &browser,
1589 NULL,
1590 &contents,
1591 NULL,
1592 &error_)) {
1593 return false;
1594 }
1595 if (!browser || !contents)
1596 return false;
1597 } else {
1598 browser = GetCurrentBrowser();
1599 if (!browser)
1600 return false;
1601 contents = browser->tab_strip_model()->GetActiveWebContents();
1602 if (!contents)
1603 return false;
1604 }
1605
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;
1609 return false;
1610 }
1611
1612 AddRef(); // Balanced in GotLanguage().
1613
1614 ChromeTranslateClient* chrome_translate_client =
1615 ChromeTranslateClient::FromWebContents(contents);
1616 if (!chrome_translate_client->GetLanguageState()
1617 .original_language()
1618 .empty()) {
1619 // Delay the callback invocation until after the current JS call has
1620 // returned.
1621 base::MessageLoop::current()->PostTask(
1622 FROM_HERE,
1623 base::Bind(
1624 &TabsDetectLanguageFunction::GotLanguage,
1625 this,
1626 chrome_translate_client->GetLanguageState().original_language()));
1627 return true;
1628 }
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));
1633 registrar_.Add(
1634 this, chrome::NOTIFICATION_TAB_CLOSING,
1635 content::Source<NavigationController>(&(contents->GetController())));
1636 registrar_.Add(
1637 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1638 content::Source<NavigationController>(&(contents->GetController())));
1639 return true;
1640 }
1641
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1642 void TabsDetectLanguageFunction::Observe(
1643 int type,
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)
1650 .ptr();
1651 language = lang_det_details->adopted_language;
1652 }
1653
1654 registrar_.RemoveAll();
1655
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);
1659 }
1660
GotLanguage(const std::string & language)1661 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1662 SetResult(new base::StringValue(language.c_str()));
1663 SendResponse(true);
1664
1665 Release(); // Balanced in Run()
1666 }
1667
ExecuteCodeInTabFunction()1668 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1669 : chrome_details_(this), execute_tab_id_(-1) {
1670 }
1671
~ExecuteCodeInTabFunction()1672 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1673
HasPermission()1674 bool ExecuteCodeInTabFunction::HasPermission() {
1675 if (Init() &&
1676 extension_->permissions_data()->HasAPIPermissionForTab(
1677 execute_tab_id_, APIPermission::kTab)) {
1678 return true;
1679 }
1680 return ExtensionFunction::HasPermission();
1681 }
1682
Init()1683 bool ExecuteCodeInTabFunction::Init() {
1684 if (details_.get())
1685 return true;
1686
1687 // |tab_id| is optional so it's ok if it's not there.
1688 int tab_id = -1;
1689 if (args_->GetInteger(0, &tab_id))
1690 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1691
1692 // |details| are not optional.
1693 base::DictionaryValue* details_value = NULL;
1694 if (!args_->GetDictionary(1, &details_value))
1695 return false;
1696 scoped_ptr<InjectDetails> details(new InjectDetails());
1697 if (!InjectDetails::Populate(*details_value, details.get()))
1698 return false;
1699
1700 // If the tab ID wasn't given then it needs to be converted to the
1701 // currently active tab's ID.
1702 if (tab_id == -1) {
1703 Browser* browser = chrome_details_.GetCurrentBrowser();
1704 if (!browser)
1705 return false;
1706 content::WebContents* web_contents = NULL;
1707 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1708 return false;
1709 }
1710
1711 execute_tab_id_ = tab_id;
1712 details_ = details.Pass();
1713 return true;
1714 }
1715
CanExecuteScriptOnPage()1716 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1717 content::WebContents* contents = NULL;
1718
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(),
1725 NULL,
1726 NULL,
1727 &contents,
1728 NULL,
1729 &error_)) {
1730 return false;
1731 }
1732
1733 CHECK(contents);
1734
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(
1739 extension(),
1740 contents->GetURL(),
1741 contents->GetURL(),
1742 execute_tab_id_,
1743 process ? process->GetID() : -1,
1744 &error_)) {
1745 return false;
1746 }
1747
1748 return true;
1749 }
1750
GetScriptExecutor()1751 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1752 Browser* browser = NULL;
1753 content::WebContents* contents = NULL;
1754
1755 bool success = GetTabById(execute_tab_id_,
1756 chrome_details_.GetProfile(),
1757 include_incognito(),
1758 &browser,
1759 NULL,
1760 &contents,
1761 NULL,
1762 &error_) &&
1763 contents && browser;
1764
1765 if (!success)
1766 return NULL;
1767
1768 return TabHelper::FromWebContents(contents)->script_executor();
1769 }
1770
IsWebView() const1771 bool ExecuteCodeInTabFunction::IsWebView() const {
1772 return false;
1773 }
1774
GetWebViewSrc() const1775 const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1776 return GURL::EmptyGURL();
1777 }
1778
ShouldInsertCSS() const1779 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1780 return false;
1781 }
1782
OnExecuteCodeFinished(const std::string & error,const GURL & on_url,const base::ListValue & result)1783 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1784 const std::string& error,
1785 const GURL& on_url,
1786 const base::ListValue& result) {
1787 if (error.empty())
1788 SetResult(result.DeepCopy());
1789 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_url, result);
1790 }
1791
ShouldInsertCSS() const1792 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1793 return true;
1794 }
1795
GetWebContents(int tab_id)1796 content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
1797 content::WebContents* web_contents = NULL;
1798 if (tab_id != -1) {
1799 // We assume this call leaves web_contents unchanged if it is unsuccessful.
1800 GetTabById(tab_id,
1801 GetProfile(),
1802 include_incognito(),
1803 NULL /* ignore Browser* output */,
1804 NULL /* ignore TabStripModel* output */,
1805 &web_contents,
1806 NULL /* ignore int tab_index output */,
1807 &error_);
1808 } else {
1809 Browser* browser = GetCurrentBrowser();
1810 if (!browser)
1811 error_ = keys::kNoCurrentWindowError;
1812 else if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1813 error_ = keys::kNoSelectedTabError;
1814 }
1815 return web_contents;
1816 }
1817
RunAsync()1818 bool TabsSetZoomFunction::RunAsync() {
1819 scoped_ptr<tabs::SetZoom::Params> params(
1820 tabs::SetZoom::Params::Create(*args_));
1821 EXTENSION_FUNCTION_VALIDATE(params);
1822
1823 int tab_id = params->tab_id ? *params->tab_id : -1;
1824 WebContents* web_contents = GetWebContents(tab_id);
1825 if (!web_contents)
1826 return false;
1827
1828 GURL url(web_contents->GetVisibleURL());
1829 if (PermissionsData::IsRestrictedUrl(url, url, extension(), &error_))
1830 return false;
1831
1832 ZoomController* zoom_controller =
1833 ZoomController::FromWebContents(web_contents);
1834 double zoom_level = content::ZoomFactorToZoomLevel(params->zoom_factor);
1835
1836 if (!zoom_controller->SetZoomLevelByExtension(zoom_level, extension())) {
1837 // Tried to zoom a tab in disabled mode.
1838 error_ = keys::kCannotZoomDisabledTabError;
1839 return false;
1840 }
1841
1842 SendResponse(true);
1843 return true;
1844 }
1845
RunAsync()1846 bool TabsGetZoomFunction::RunAsync() {
1847 scoped_ptr<tabs::GetZoom::Params> params(
1848 tabs::GetZoom::Params::Create(*args_));
1849 EXTENSION_FUNCTION_VALIDATE(params);
1850
1851 int tab_id = params->tab_id ? *params->tab_id : -1;
1852 WebContents* web_contents = GetWebContents(tab_id);
1853 if (!web_contents)
1854 return false;
1855
1856 double zoom_level =
1857 ZoomController::FromWebContents(web_contents)->GetZoomLevel();
1858 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
1859 results_ = tabs::GetZoom::Results::Create(zoom_factor);
1860 SendResponse(true);
1861 return true;
1862 }
1863
RunAsync()1864 bool TabsSetZoomSettingsFunction::RunAsync() {
1865 using api::tabs::ZoomSettings;
1866
1867 scoped_ptr<tabs::SetZoomSettings::Params> params(
1868 tabs::SetZoomSettings::Params::Create(*args_));
1869 EXTENSION_FUNCTION_VALIDATE(params);
1870
1871 int tab_id = params->tab_id ? *params->tab_id : -1;
1872 WebContents* web_contents = GetWebContents(tab_id);
1873 if (!web_contents)
1874 return false;
1875
1876 GURL url(web_contents->GetVisibleURL());
1877 if (PermissionsData::IsRestrictedUrl(url, url, extension(), &error_))
1878 return false;
1879
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;
1885 return false;
1886 }
1887
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;
1898 break;
1899 case ZoomSettings::SCOPE_PER_TAB:
1900 zoom_mode = ZoomController::ZOOM_MODE_ISOLATED;
1901 }
1902 break;
1903 case ZoomSettings::MODE_MANUAL:
1904 zoom_mode = ZoomController::ZOOM_MODE_MANUAL;
1905 break;
1906 case ZoomSettings::MODE_DISABLED:
1907 zoom_mode = ZoomController::ZOOM_MODE_DISABLED;
1908 }
1909
1910 ZoomController::FromWebContents(web_contents)->SetZoomMode(zoom_mode);
1911
1912 SendResponse(true);
1913 return true;
1914 }
1915
RunAsync()1916 bool TabsGetZoomSettingsFunction::RunAsync() {
1917 scoped_ptr<tabs::GetZoomSettings::Params> params(
1918 tabs::GetZoomSettings::Params::Create(*args_));
1919 EXTENSION_FUNCTION_VALIDATE(params);
1920
1921 int tab_id = params->tab_id ? *params->tab_id : -1;
1922 WebContents* web_contents = GetWebContents(tab_id);
1923 if (!web_contents)
1924 return false;
1925 ZoomController* zoom_controller =
1926 ZoomController::FromWebContents(web_contents);
1927
1928 ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
1929 api::tabs::ZoomSettings zoom_settings;
1930 ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
1931
1932 results_ = api::tabs::GetZoomSettings::Results::Create(zoom_settings);
1933 SendResponse(true);
1934 return true;
1935 }
1936
1937 } // namespace extensions
1938