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 "apps/app_window.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.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/script_executor.h"
29 #include "chrome/browser/extensions/tab_helper.h"
30 #include "chrome/browser/extensions/window_controller.h"
31 #include "chrome/browser/extensions/window_controller_list.h"
32 #include "chrome/browser/prefs/incognito_mode_prefs.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/translate/chrome_translate_client.h"
35 #include "chrome/browser/ui/apps/chrome_app_window_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/web_applications/web_app.h"
48 #include "chrome/common/chrome_switches.h"
49 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
50 #include "chrome/common/extensions/api/tabs.h"
51 #include "chrome/common/extensions/api/windows.h"
52 #include "chrome/common/extensions/extension_constants.h"
53 #include "chrome/common/pref_names.h"
54 #include "chrome/common/url_constants.h"
55 #include "components/pref_registry/pref_registry_syncable.h"
56 #include "components/translate/core/browser/language_state.h"
57 #include "components/translate/core/common/language_detection_details.h"
58 #include "content/public/browser/navigation_controller.h"
59 #include "content/public/browser/navigation_entry.h"
60 #include "content/public/browser/notification_details.h"
61 #include "content/public/browser/notification_source.h"
62 #include "content/public/browser/render_process_host.h"
63 #include "content/public/browser/render_view_host.h"
64 #include "content/public/browser/render_widget_host_view.h"
65 #include "content/public/browser/web_contents.h"
66 #include "content/public/common/url_constants.h"
67 #include "extensions/browser/extension_function_dispatcher.h"
68 #include "extensions/browser/extension_function_util.h"
69 #include "extensions/browser/extension_host.h"
70 #include "extensions/browser/file_reader.h"
71 #include "extensions/common/constants.h"
72 #include "extensions/common/error_utils.h"
73 #include "extensions/common/extension.h"
74 #include "extensions/common/extension_l10n_util.h"
75 #include "extensions/common/extension_messages.h"
76 #include "extensions/common/manifest_constants.h"
77 #include "extensions/common/message_bundle.h"
78 #include "extensions/common/permissions/permissions_data.h"
79 #include "extensions/common/user_script.h"
80 #include "skia/ext/image_operations.h"
81 #include "skia/ext/platform_canvas.h"
82 #include "third_party/skia/include/core/SkBitmap.h"
83 #include "ui/base/models/list_selection_model.h"
84 #include "ui/base/ui_base_types.h"
85
86 #if defined(USE_ASH)
87 #include "apps/app_window_registry.h"
88 #include "ash/ash_switches.h"
89 #include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
90 #endif
91
92 using apps::AppWindow;
93 using content::BrowserThread;
94 using content::NavigationController;
95 using content::NavigationEntry;
96 using content::OpenURLParams;
97 using content::Referrer;
98 using content::WebContents;
99
100 namespace extensions {
101
102 namespace windows = api::windows;
103 namespace keys = tabs_constants;
104 namespace tabs = api::tabs;
105
106 using api::tabs::InjectDetails;
107
108 namespace {
109
GetBrowserFromWindowID(ChromeUIThreadExtensionFunction * function,int window_id,Browser ** browser)110 bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction* function,
111 int window_id,
112 Browser** browser) {
113 std::string error;
114 Browser* result;
115 result =
116 ExtensionTabUtil::GetBrowserFromWindowID(function, window_id, &error);
117 if (!result) {
118 function->SetError(error);
119 return false;
120 }
121
122 *browser = result;
123 return true;
124 }
125
126 // |error_message| can optionally be passed in and will be set with an
127 // 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)128 bool GetTabById(int tab_id,
129 Profile* profile,
130 bool include_incognito,
131 Browser** browser,
132 TabStripModel** tab_strip,
133 content::WebContents** contents,
134 int* tab_index,
135 std::string* error_message) {
136 if (ExtensionTabUtil::GetTabById(tab_id, profile, include_incognito,
137 browser, tab_strip, contents, tab_index)) {
138 return true;
139 }
140
141 if (error_message) {
142 *error_message = ErrorUtils::FormatErrorMessage(
143 keys::kTabNotFoundError, base::IntToString(tab_id));
144 }
145
146 return false;
147 }
148
149 // Returns true if either |boolean| is a null pointer, or if |*boolean| and
150 // |value| are equal. This function is used to check if a tab's parameters match
151 // those of the browser.
MatchesBool(bool * boolean,bool value)152 bool MatchesBool(bool* boolean, bool value) {
153 return !boolean || *boolean == value;
154 }
155
156 template <typename T>
AssignOptionalValue(const scoped_ptr<T> & source,scoped_ptr<T> & destination)157 void AssignOptionalValue(const scoped_ptr<T>& source,
158 scoped_ptr<T>& destination) {
159 if (source.get()) {
160 destination.reset(new T(*source.get()));
161 }
162 }
163
164 } // namespace
165
166 // Windows ---------------------------------------------------------------------
167
RunSync()168 bool WindowsGetFunction::RunSync() {
169 scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
170 EXTENSION_FUNCTION_VALIDATE(params.get());
171
172 bool populate_tabs = false;
173 if (params->get_info.get() && params->get_info->populate.get())
174 populate_tabs = *params->get_info->populate;
175
176 WindowController* controller;
177 if (!windows_util::GetWindowFromWindowID(this,
178 params->window_id,
179 &controller)) {
180 return false;
181 }
182
183 if (populate_tabs)
184 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
185 else
186 SetResult(controller->CreateWindowValue());
187 return true;
188 }
189
RunSync()190 bool WindowsGetCurrentFunction::RunSync() {
191 scoped_ptr<windows::GetCurrent::Params> params(
192 windows::GetCurrent::Params::Create(*args_));
193 EXTENSION_FUNCTION_VALIDATE(params.get());
194
195 bool populate_tabs = false;
196 if (params->get_info.get() && params->get_info->populate.get())
197 populate_tabs = *params->get_info->populate;
198
199 WindowController* controller;
200 if (!windows_util::GetWindowFromWindowID(this,
201 extension_misc::kCurrentWindowId,
202 &controller)) {
203 return false;
204 }
205 if (populate_tabs)
206 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
207 else
208 SetResult(controller->CreateWindowValue());
209 return true;
210 }
211
RunSync()212 bool WindowsGetLastFocusedFunction::RunSync() {
213 scoped_ptr<windows::GetLastFocused::Params> params(
214 windows::GetLastFocused::Params::Create(*args_));
215 EXTENSION_FUNCTION_VALIDATE(params.get());
216
217 bool populate_tabs = false;
218 if (params->get_info.get() && params->get_info->populate.get())
219 populate_tabs = *params->get_info->populate;
220
221 // Note: currently this returns the last active browser. If we decide to
222 // include other window types (e.g. panels), we will need to add logic to
223 // WindowControllerList that mirrors the active behavior of BrowserList.
224 Browser* browser = chrome::FindAnyBrowser(
225 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
226 if (!browser || !browser->window()) {
227 error_ = keys::kNoLastFocusedWindowError;
228 return false;
229 }
230 WindowController* controller =
231 browser->extension_window_controller();
232 if (populate_tabs)
233 SetResult(controller->CreateWindowValueWithTabs(GetExtension()));
234 else
235 SetResult(controller->CreateWindowValue());
236 return true;
237 }
238
RunSync()239 bool WindowsGetAllFunction::RunSync() {
240 scoped_ptr<windows::GetAll::Params> params(
241 windows::GetAll::Params::Create(*args_));
242 EXTENSION_FUNCTION_VALIDATE(params.get());
243
244 bool populate_tabs = false;
245 if (params->get_info.get() && params->get_info->populate.get())
246 populate_tabs = *params->get_info->populate;
247
248 base::ListValue* window_list = new base::ListValue();
249 const WindowControllerList::ControllerList& windows =
250 WindowControllerList::GetInstance()->windows();
251 for (WindowControllerList::ControllerList::const_iterator iter =
252 windows.begin();
253 iter != windows.end(); ++iter) {
254 if (!this->CanOperateOnWindow(*iter))
255 continue;
256 if (populate_tabs)
257 window_list->Append((*iter)->CreateWindowValueWithTabs(GetExtension()));
258 else
259 window_list->Append((*iter)->CreateWindowValue());
260 }
261 SetResult(window_list);
262 return true;
263 }
264
ShouldOpenIncognitoWindow(const windows::Create::Params::CreateData * create_data,std::vector<GURL> * urls,bool * is_error)265 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
266 const windows::Create::Params::CreateData* create_data,
267 std::vector<GURL>* urls, bool* is_error) {
268 *is_error = false;
269 const IncognitoModePrefs::Availability incognito_availability =
270 IncognitoModePrefs::GetAvailability(GetProfile()->GetPrefs());
271 bool incognito = false;
272 if (create_data && create_data->incognito) {
273 incognito = *create_data->incognito;
274 if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
275 error_ = keys::kIncognitoModeIsDisabled;
276 *is_error = true;
277 return false;
278 }
279 if (!incognito && incognito_availability == IncognitoModePrefs::FORCED) {
280 error_ = keys::kIncognitoModeIsForced;
281 *is_error = true;
282 return false;
283 }
284 } else if (incognito_availability == IncognitoModePrefs::FORCED) {
285 // If incognito argument is not specified explicitly, we default to
286 // incognito when forced so by policy.
287 incognito = true;
288 }
289
290 // Remove all URLs that are not allowed in an incognito session. Note that a
291 // ChromeOS guest session is not considered incognito in this case.
292 if (incognito && !GetProfile()->IsGuestSession()) {
293 std::string first_url_erased;
294 for (size_t i = 0; i < urls->size();) {
295 if (chrome::IsURLAllowedInIncognito((*urls)[i], GetProfile())) {
296 i++;
297 } else {
298 if (first_url_erased.empty())
299 first_url_erased = (*urls)[i].spec();
300 urls->erase(urls->begin() + i);
301 }
302 }
303 if (urls->empty() && !first_url_erased.empty()) {
304 error_ = ErrorUtils::FormatErrorMessage(
305 keys::kURLsNotAllowedInIncognitoError, first_url_erased);
306 *is_error = true;
307 return false;
308 }
309 }
310 return incognito;
311 }
312
RunSync()313 bool WindowsCreateFunction::RunSync() {
314 scoped_ptr<windows::Create::Params> params(
315 windows::Create::Params::Create(*args_));
316 EXTENSION_FUNCTION_VALIDATE(params);
317 std::vector<GURL> urls;
318 TabStripModel* source_tab_strip = NULL;
319 int tab_index = -1;
320
321 windows::Create::Params::CreateData* create_data = params->create_data.get();
322
323 // Look for optional url.
324 if (create_data && create_data->url) {
325 std::vector<std::string> url_strings;
326 // First, get all the URLs the client wants to open.
327 if (create_data->url->as_string)
328 url_strings.push_back(*create_data->url->as_string);
329 else if (create_data->url->as_strings)
330 url_strings.swap(*create_data->url->as_strings);
331
332 // Second, resolve, validate and convert them to GURLs.
333 for (std::vector<std::string>::iterator i = url_strings.begin();
334 i != url_strings.end(); ++i) {
335 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
336 *i, GetExtension());
337 if (!url.is_valid()) {
338 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
339 return false;
340 }
341 // Don't let the extension crash the browser or renderers.
342 if (ExtensionTabUtil::IsCrashURL(url)) {
343 error_ = keys::kNoCrashBrowserError;
344 return false;
345 }
346 urls.push_back(url);
347 }
348 }
349
350 // Look for optional tab id.
351 if (create_data && create_data->tab_id) {
352 // Find the tab. |source_tab_strip| and |tab_index| will later be used to
353 // move the tab into the created window.
354 if (!GetTabById(*create_data->tab_id,
355 GetProfile(),
356 include_incognito(),
357 NULL,
358 &source_tab_strip,
359 NULL,
360 &tab_index,
361 &error_))
362 return false;
363 }
364
365 Profile* window_profile = GetProfile();
366 Browser::Type window_type = Browser::TYPE_TABBED;
367 bool create_panel = false;
368
369 // panel_create_mode only applies if create_panel = true
370 PanelManager::CreateMode panel_create_mode = PanelManager::CREATE_AS_DOCKED;
371
372 gfx::Rect window_bounds;
373 bool focused = true;
374 bool saw_focus_key = false;
375 std::string extension_id;
376
377 // Decide whether we are opening a normal window or an incognito window.
378 bool is_error = true;
379 bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
380 &is_error);
381 if (is_error) {
382 // error_ member variable is set inside of ShouldOpenIncognitoWindow.
383 return false;
384 }
385 if (open_incognito_window) {
386 window_profile = window_profile->GetOffTheRecordProfile();
387 }
388
389 if (create_data) {
390 // Figure out window type before figuring out bounds so that default
391 // bounds can be set according to the window type.
392 switch (create_data->type) {
393 case windows::Create::Params::CreateData::TYPE_POPUP:
394 window_type = Browser::TYPE_POPUP;
395 extension_id = GetExtension()->id();
396 break;
397 case windows::Create::Params::CreateData::TYPE_PANEL:
398 case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
399 extension_id = GetExtension()->id();
400 bool use_panels = false;
401 #if !defined(OS_ANDROID)
402 use_panels = PanelManager::ShouldUsePanels(extension_id);
403 #endif
404 if (use_panels) {
405 create_panel = true;
406 // Non-ash supports both docked and detached panel types.
407 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
408 create_data->type ==
409 windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
410 panel_create_mode = PanelManager::CREATE_AS_DETACHED;
411 }
412 } else {
413 window_type = Browser::TYPE_POPUP;
414 }
415 break;
416 }
417 case windows::Create::Params::CreateData::TYPE_NONE:
418 case windows::Create::Params::CreateData::TYPE_NORMAL:
419 break;
420 default:
421 error_ = keys::kInvalidWindowTypeError;
422 return false;
423 }
424
425 // Initialize default window bounds according to window type.
426 if (window_type == Browser::TYPE_TABBED ||
427 window_type == Browser::TYPE_POPUP ||
428 create_panel) {
429 // Try to position the new browser relative to its originating
430 // browser window. The call offsets the bounds by kWindowTilePixels
431 // (defined in WindowSizer to be 10).
432 //
433 // NOTE(rafaelw): It's ok if GetCurrentBrowser() returns NULL here.
434 // GetBrowserWindowBounds will default to saved "default" values for
435 // the app.
436 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
437 WindowSizer::GetBrowserWindowBoundsAndShowState(std::string(),
438 gfx::Rect(),
439 GetCurrentBrowser(),
440 &window_bounds,
441 &show_state);
442 }
443
444 if (create_panel && PanelManager::CREATE_AS_DETACHED == panel_create_mode) {
445 window_bounds.set_origin(
446 PanelManager::GetInstance()->GetDefaultDetachedPanelOrigin());
447 }
448
449 // Any part of the bounds can optionally be set by the caller.
450 if (create_data->left)
451 window_bounds.set_x(*create_data->left);
452
453 if (create_data->top)
454 window_bounds.set_y(*create_data->top);
455
456 if (create_data->width)
457 window_bounds.set_width(*create_data->width);
458
459 if (create_data->height)
460 window_bounds.set_height(*create_data->height);
461
462 if (create_data->focused) {
463 focused = *create_data->focused;
464 saw_focus_key = true;
465 }
466 }
467
468 if (create_panel) {
469 if (urls.empty())
470 urls.push_back(GURL(chrome::kChromeUINewTabURL));
471
472 #if defined(USE_ASH)
473 if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
474 AppWindow::CreateParams create_params;
475 create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL;
476 create_params.window_spec.bounds = window_bounds;
477 create_params.focused = saw_focus_key && focused;
478 AppWindow* app_window = new AppWindow(
479 window_profile, new ChromeAppWindowDelegate(), GetExtension());
480 AshPanelContents* ash_panel_contents = new AshPanelContents(app_window);
481 app_window->Init(urls[0], ash_panel_contents, create_params);
482 SetResult(ash_panel_contents->GetExtensionWindowController()->
483 CreateWindowValueWithTabs(GetExtension()));
484 return true;
485 }
486 #endif
487 std::string title =
488 web_app::GenerateApplicationNameFromExtensionId(extension_id);
489 // Note: Panels ignore all but the first url provided.
490 Panel* panel = PanelManager::GetInstance()->CreatePanel(
491 title, window_profile, urls[0], window_bounds, panel_create_mode);
492
493 // Unlike other window types, Panels do not take focus by default.
494 if (!saw_focus_key || !focused)
495 panel->ShowInactive();
496 else
497 panel->Show();
498
499 SetResult(
500 panel->extension_window_controller()->CreateWindowValueWithTabs(
501 GetExtension()));
502 return true;
503 }
504
505 // Create a new BrowserWindow.
506 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
507 if (create_panel)
508 window_type = Browser::TYPE_POPUP;
509 Browser::CreateParams create_params(window_type, window_profile,
510 host_desktop_type);
511 if (extension_id.empty()) {
512 create_params.initial_bounds = window_bounds;
513 } else {
514 create_params = Browser::CreateParams::CreateForApp(
515 web_app::GenerateApplicationNameFromExtensionId(extension_id),
516 false /* trusted_source */,
517 window_bounds,
518 window_profile,
519 host_desktop_type);
520 }
521 create_params.initial_show_state = ui::SHOW_STATE_NORMAL;
522 create_params.host_desktop_type = chrome::GetActiveDesktop();
523
524 Browser* new_window = new Browser(create_params);
525
526 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) {
527 WebContents* tab = chrome::AddSelectedTabWithURL(
528 new_window, *i, content::PAGE_TRANSITION_LINK);
529 if (create_panel) {
530 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id);
531 }
532 }
533
534 WebContents* contents = NULL;
535 // Move the tab into the created window only if it's an empty popup or it's
536 // a tabbed window.
537 if ((window_type == Browser::TYPE_POPUP && urls.empty()) ||
538 window_type == Browser::TYPE_TABBED) {
539 if (source_tab_strip)
540 contents = source_tab_strip->DetachWebContentsAt(tab_index);
541 if (contents) {
542 TabStripModel* target_tab_strip = new_window->tab_strip_model();
543 target_tab_strip->InsertWebContentsAt(urls.size(), contents,
544 TabStripModel::ADD_NONE);
545 }
546 }
547 // Create a new tab if the created window is still empty. Don't create a new
548 // tab when it is intended to create an empty popup.
549 if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) {
550 chrome::NewTab(new_window);
551 }
552 chrome::SelectNumberedTab(new_window, 0);
553
554 // Unlike other window types, Panels do not take focus by default.
555 if (!saw_focus_key && create_panel)
556 focused = false;
557
558 if (focused)
559 new_window->window()->Show();
560 else
561 new_window->window()->ShowInactive();
562
563 if (new_window->profile()->IsOffTheRecord() &&
564 !GetProfile()->IsOffTheRecord() && !include_incognito()) {
565 // Don't expose incognito windows if extension itself works in non-incognito
566 // profile and CanCrossIncognito isn't allowed.
567 SetResult(base::Value::CreateNullValue());
568 } else {
569 SetResult(
570 new_window->extension_window_controller()->CreateWindowValueWithTabs(
571 GetExtension()));
572 }
573
574 return true;
575 }
576
RunSync()577 bool WindowsUpdateFunction::RunSync() {
578 scoped_ptr<windows::Update::Params> params(
579 windows::Update::Params::Create(*args_));
580 EXTENSION_FUNCTION_VALIDATE(params);
581
582 WindowController* controller;
583 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
584 &controller))
585 return false;
586
587 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change.
588 switch (params->update_info.state) {
589 case windows::Update::Params::UpdateInfo::STATE_NORMAL:
590 show_state = ui::SHOW_STATE_NORMAL;
591 break;
592 case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
593 show_state = ui::SHOW_STATE_MINIMIZED;
594 break;
595 case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
596 show_state = ui::SHOW_STATE_MAXIMIZED;
597 break;
598 case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
599 show_state = ui::SHOW_STATE_FULLSCREEN;
600 break;
601 case windows::Update::Params::UpdateInfo::STATE_NONE:
602 break;
603 default:
604 error_ = keys::kInvalidWindowStateError;
605 return false;
606 }
607
608 if (show_state != ui::SHOW_STATE_FULLSCREEN &&
609 show_state != ui::SHOW_STATE_DEFAULT)
610 controller->SetFullscreenMode(false, GetExtension()->url());
611
612 switch (show_state) {
613 case ui::SHOW_STATE_MINIMIZED:
614 controller->window()->Minimize();
615 break;
616 case ui::SHOW_STATE_MAXIMIZED:
617 controller->window()->Maximize();
618 break;
619 case ui::SHOW_STATE_FULLSCREEN:
620 if (controller->window()->IsMinimized() ||
621 controller->window()->IsMaximized())
622 controller->window()->Restore();
623 controller->SetFullscreenMode(true, GetExtension()->url());
624 break;
625 case ui::SHOW_STATE_NORMAL:
626 controller->window()->Restore();
627 break;
628 default:
629 break;
630 }
631
632 gfx::Rect bounds;
633 if (controller->window()->IsMinimized())
634 bounds = controller->window()->GetRestoredBounds();
635 else
636 bounds = controller->window()->GetBounds();
637 bool set_bounds = false;
638
639 // Any part of the bounds can optionally be set by the caller.
640 if (params->update_info.left) {
641 bounds.set_x(*params->update_info.left);
642 set_bounds = true;
643 }
644
645 if (params->update_info.top) {
646 bounds.set_y(*params->update_info.top);
647 set_bounds = true;
648 }
649
650 if (params->update_info.width) {
651 bounds.set_width(*params->update_info.width);
652 set_bounds = true;
653 }
654
655 if (params->update_info.height) {
656 bounds.set_height(*params->update_info.height);
657 set_bounds = true;
658 }
659
660 if (set_bounds) {
661 if (show_state == ui::SHOW_STATE_MINIMIZED ||
662 show_state == ui::SHOW_STATE_MAXIMIZED ||
663 show_state == ui::SHOW_STATE_FULLSCREEN) {
664 error_ = keys::kInvalidWindowStateError;
665 return false;
666 }
667 // TODO(varkha): Updating bounds during a drag can cause problems and a more
668 // general solution is needed. See http://crbug.com/251813 .
669 controller->window()->SetBounds(bounds);
670 }
671
672 if (params->update_info.focused) {
673 if (*params->update_info.focused) {
674 if (show_state == ui::SHOW_STATE_MINIMIZED) {
675 error_ = keys::kInvalidWindowStateError;
676 return false;
677 }
678 controller->window()->Activate();
679 } else {
680 if (show_state == ui::SHOW_STATE_MAXIMIZED ||
681 show_state == ui::SHOW_STATE_FULLSCREEN) {
682 error_ = keys::kInvalidWindowStateError;
683 return false;
684 }
685 controller->window()->Deactivate();
686 }
687 }
688
689 if (params->update_info.draw_attention)
690 controller->window()->FlashFrame(*params->update_info.draw_attention);
691
692 SetResult(controller->CreateWindowValue());
693
694 return true;
695 }
696
RunSync()697 bool WindowsRemoveFunction::RunSync() {
698 scoped_ptr<windows::Remove::Params> params(
699 windows::Remove::Params::Create(*args_));
700 EXTENSION_FUNCTION_VALIDATE(params);
701
702 WindowController* controller;
703 if (!windows_util::GetWindowFromWindowID(this, params->window_id,
704 &controller))
705 return false;
706
707 WindowController::Reason reason;
708 if (!controller->CanClose(&reason)) {
709 if (reason == WindowController::REASON_NOT_EDITABLE)
710 error_ = keys::kTabStripNotEditableError;
711 return false;
712 }
713 controller->window()->Close();
714 return true;
715 }
716
717 // Tabs ------------------------------------------------------------------------
718
RunSync()719 bool TabsGetSelectedFunction::RunSync() {
720 // windowId defaults to "current" window.
721 int window_id = extension_misc::kCurrentWindowId;
722
723 scoped_ptr<tabs::GetSelected::Params> params(
724 tabs::GetSelected::Params::Create(*args_));
725 EXTENSION_FUNCTION_VALIDATE(params.get());
726 if (params->window_id.get())
727 window_id = *params->window_id;
728
729 Browser* browser = NULL;
730 if (!GetBrowserFromWindowID(this, window_id, &browser))
731 return false;
732
733 TabStripModel* tab_strip = browser->tab_strip_model();
734 WebContents* contents = tab_strip->GetActiveWebContents();
735 if (!contents) {
736 error_ = keys::kNoSelectedTabError;
737 return false;
738 }
739 SetResult(ExtensionTabUtil::CreateTabValue(contents,
740 tab_strip,
741 tab_strip->active_index(),
742 GetExtension()));
743 return true;
744 }
745
RunSync()746 bool TabsGetAllInWindowFunction::RunSync() {
747 scoped_ptr<tabs::GetAllInWindow::Params> params(
748 tabs::GetAllInWindow::Params::Create(*args_));
749 EXTENSION_FUNCTION_VALIDATE(params.get());
750 // windowId defaults to "current" window.
751 int window_id = extension_misc::kCurrentWindowId;
752 if (params->window_id.get())
753 window_id = *params->window_id;
754
755 Browser* browser = NULL;
756 if (!GetBrowserFromWindowID(this, window_id, &browser))
757 return false;
758
759 SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension()));
760
761 return true;
762 }
763
RunSync()764 bool TabsQueryFunction::RunSync() {
765 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_));
766 EXTENSION_FUNCTION_VALIDATE(params.get());
767
768 bool loading_status_set = params->query_info.status !=
769 tabs::Query::Params::QueryInfo::STATUS_NONE;
770 bool loading = params->query_info.status ==
771 tabs::Query::Params::QueryInfo::STATUS_LOADING;
772
773 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does
774 // not grant access to the content of the tabs, only to seeing their URLs and
775 // meta data.
776 URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
777 if (params->query_info.url.get())
778 url_pattern = URLPattern(URLPattern::SCHEME_ALL, *params->query_info.url);
779
780 std::string title;
781 if (params->query_info.title.get())
782 title = *params->query_info.title;
783
784 int window_id = extension_misc::kUnknownWindowId;
785 if (params->query_info.window_id.get())
786 window_id = *params->query_info.window_id;
787
788 int index = -1;
789 if (params->query_info.index.get())
790 index = *params->query_info.index;
791
792 std::string window_type;
793 if (params->query_info.window_type !=
794 tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) {
795 window_type = tabs::Query::Params::QueryInfo::ToString(
796 params->query_info.window_type);
797 }
798
799 base::ListValue* result = new base::ListValue();
800 Browser* last_active_browser = chrome::FindAnyBrowser(
801 GetProfile(), include_incognito(), chrome::GetActiveDesktop());
802 Browser* current_browser = GetCurrentBrowser();
803 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
804 Browser* browser = *it;
805 if (!GetProfile()->IsSameProfile(browser->profile()))
806 continue;
807
808 if (!browser->window())
809 continue;
810
811 if (!include_incognito() && GetProfile() != browser->profile())
812 continue;
813
814 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser))
815 continue;
816
817 if (window_id == extension_misc::kCurrentWindowId &&
818 browser != current_browser) {
819 continue;
820 }
821
822 if (!MatchesBool(params->query_info.current_window.get(),
823 browser == current_browser)) {
824 continue;
825 }
826
827 if (!MatchesBool(params->query_info.last_focused_window.get(),
828 browser == last_active_browser)) {
829 continue;
830 }
831
832 if (!window_type.empty() &&
833 window_type !=
834 browser->extension_window_controller()->GetWindowTypeText()) {
835 continue;
836 }
837
838 TabStripModel* tab_strip = browser->tab_strip_model();
839 for (int i = 0; i < tab_strip->count(); ++i) {
840 WebContents* web_contents = tab_strip->GetWebContentsAt(i);
841
842 if (index > -1 && i != index)
843 continue;
844
845 if (!MatchesBool(params->query_info.highlighted.get(),
846 tab_strip->IsTabSelected(i))) {
847 continue;
848 }
849
850 if (!MatchesBool(params->query_info.active.get(),
851 i == tab_strip->active_index())) {
852 continue;
853 }
854
855 if (!MatchesBool(params->query_info.pinned.get(),
856 tab_strip->IsTabPinned(i))) {
857 continue;
858 }
859
860 if (!title.empty() && !MatchPattern(web_contents->GetTitle(),
861 base::UTF8ToUTF16(title)))
862 continue;
863
864 if (!url_pattern.MatchesURL(web_contents->GetURL()))
865 continue;
866
867 if (loading_status_set && loading != web_contents->IsLoading())
868 continue;
869
870 result->Append(ExtensionTabUtil::CreateTabValue(
871 web_contents, tab_strip, i, GetExtension()));
872 }
873 }
874
875 SetResult(result);
876 return true;
877 }
878
RunSync()879 bool TabsCreateFunction::RunSync() {
880 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_));
881 EXTENSION_FUNCTION_VALIDATE(params.get());
882
883 ExtensionTabUtil::OpenTabParams options;
884 AssignOptionalValue(params->create_properties.window_id, options.window_id);
885 AssignOptionalValue(params->create_properties.opener_tab_id,
886 options.opener_tab_id);
887 AssignOptionalValue(params->create_properties.selected, options.active);
888 // The 'active' property has replaced the 'selected' property.
889 AssignOptionalValue(params->create_properties.active, options.active);
890 AssignOptionalValue(params->create_properties.pinned, options.pinned);
891 AssignOptionalValue(params->create_properties.index, options.index);
892 AssignOptionalValue(params->create_properties.url, options.url);
893
894 std::string error;
895 scoped_ptr<base::DictionaryValue> result(
896 ExtensionTabUtil::OpenTab(this, options, &error));
897 if (!result) {
898 SetError(error);
899 return false;
900 }
901
902 // Return data about the newly created tab.
903 if (has_callback()) {
904 SetResult(result.release());
905 }
906 return true;
907 }
908
RunSync()909 bool TabsDuplicateFunction::RunSync() {
910 scoped_ptr<tabs::Duplicate::Params> params(
911 tabs::Duplicate::Params::Create(*args_));
912 EXTENSION_FUNCTION_VALIDATE(params.get());
913 int tab_id = params->tab_id;
914
915 Browser* browser = NULL;
916 TabStripModel* tab_strip = NULL;
917 int tab_index = -1;
918 if (!GetTabById(tab_id,
919 GetProfile(),
920 include_incognito(),
921 &browser,
922 &tab_strip,
923 NULL,
924 &tab_index,
925 &error_)) {
926 return false;
927 }
928
929 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index);
930 if (!has_callback())
931 return true;
932
933 // Duplicated tab may not be in the same window as the original, so find
934 // the window and the tab.
935 TabStripModel* new_tab_strip = NULL;
936 int new_tab_index = -1;
937 ExtensionTabUtil::GetTabStripModel(new_contents,
938 &new_tab_strip,
939 &new_tab_index);
940 if (!new_tab_strip || new_tab_index == -1) {
941 return false;
942 }
943
944 // Return data about the newly created tab.
945 SetResult(ExtensionTabUtil::CreateTabValue(
946 new_contents,
947 new_tab_strip, new_tab_index, GetExtension()));
948
949 return true;
950 }
951
RunSync()952 bool TabsGetFunction::RunSync() {
953 scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_));
954 EXTENSION_FUNCTION_VALIDATE(params.get());
955 int tab_id = params->tab_id;
956
957 TabStripModel* tab_strip = NULL;
958 WebContents* contents = NULL;
959 int tab_index = -1;
960 if (!GetTabById(tab_id,
961 GetProfile(),
962 include_incognito(),
963 NULL,
964 &tab_strip,
965 &contents,
966 &tab_index,
967 &error_))
968 return false;
969
970 SetResult(ExtensionTabUtil::CreateTabValue(contents,
971 tab_strip,
972 tab_index,
973 GetExtension()));
974 return true;
975 }
976
RunSync()977 bool TabsGetCurrentFunction::RunSync() {
978 DCHECK(dispatcher());
979
980 WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents();
981 if (contents)
982 SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension()));
983
984 return true;
985 }
986
RunSync()987 bool TabsHighlightFunction::RunSync() {
988 scoped_ptr<tabs::Highlight::Params> params(
989 tabs::Highlight::Params::Create(*args_));
990 EXTENSION_FUNCTION_VALIDATE(params.get());
991
992 // Get the window id from the params; default to current window if omitted.
993 int window_id = extension_misc::kCurrentWindowId;
994 if (params->highlight_info.window_id.get())
995 window_id = *params->highlight_info.window_id;
996
997 Browser* browser = NULL;
998 if (!GetBrowserFromWindowID(this, window_id, &browser))
999 return false;
1000
1001 TabStripModel* tabstrip = browser->tab_strip_model();
1002 ui::ListSelectionModel selection;
1003 int active_index = -1;
1004
1005 if (params->highlight_info.tabs.as_integers) {
1006 std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
1007 // Create a new selection model as we read the list of tab indices.
1008 for (size_t i = 0; i < tab_indices.size(); ++i) {
1009 if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i]))
1010 return false;
1011 }
1012 } else {
1013 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
1014 if (!HighlightTab(tabstrip,
1015 &selection,
1016 &active_index,
1017 *params->highlight_info.tabs.as_integer)) {
1018 return false;
1019 }
1020 }
1021
1022 // Make sure they actually specified tabs to select.
1023 if (selection.empty()) {
1024 error_ = keys::kNoHighlightedTabError;
1025 return false;
1026 }
1027
1028 selection.set_active(active_index);
1029 browser->tab_strip_model()->SetSelectionFromModel(selection);
1030 SetResult(
1031 browser->extension_window_controller()->CreateWindowValueWithTabs(
1032 GetExtension()));
1033 return true;
1034 }
1035
HighlightTab(TabStripModel * tabstrip,ui::ListSelectionModel * selection,int * active_index,int index)1036 bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
1037 ui::ListSelectionModel* selection,
1038 int *active_index,
1039 int index) {
1040 // Make sure the index is in range.
1041 if (!tabstrip->ContainsIndex(index)) {
1042 error_ = ErrorUtils::FormatErrorMessage(
1043 keys::kTabIndexNotFoundError, base::IntToString(index));
1044 return false;
1045 }
1046
1047 // By default, we make the first tab in the list active.
1048 if (*active_index == -1)
1049 *active_index = index;
1050
1051 selection->AddIndexToSelection(index);
1052 return true;
1053 }
1054
TabsUpdateFunction()1055 TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) {
1056 }
1057
RunAsync()1058 bool TabsUpdateFunction::RunAsync() {
1059 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_));
1060 EXTENSION_FUNCTION_VALIDATE(params.get());
1061
1062 int tab_id = -1;
1063 WebContents* contents = NULL;
1064 if (!params->tab_id.get()) {
1065 Browser* browser = GetCurrentBrowser();
1066 if (!browser) {
1067 error_ = keys::kNoCurrentWindowError;
1068 return false;
1069 }
1070 contents = browser->tab_strip_model()->GetActiveWebContents();
1071 if (!contents) {
1072 error_ = keys::kNoSelectedTabError;
1073 return false;
1074 }
1075 tab_id = SessionID::IdForTab(contents);
1076 } else {
1077 tab_id = *params->tab_id;
1078 }
1079
1080 int tab_index = -1;
1081 TabStripModel* tab_strip = NULL;
1082 if (!GetTabById(tab_id,
1083 GetProfile(),
1084 include_incognito(),
1085 NULL,
1086 &tab_strip,
1087 &contents,
1088 &tab_index,
1089 &error_)) {
1090 return false;
1091 }
1092
1093 web_contents_ = contents;
1094
1095 // TODO(rafaelw): handle setting remaining tab properties:
1096 // -title
1097 // -favIconUrl
1098
1099 // Navigate the tab to a new location if the url is different.
1100 bool is_async = false;
1101 if (params->update_properties.url.get() &&
1102 !UpdateURL(*params->update_properties.url, tab_id, &is_async)) {
1103 return false;
1104 }
1105
1106 bool active = false;
1107 // TODO(rafaelw): Setting |active| from js doesn't make much sense.
1108 // Move tab selection management up to window.
1109 if (params->update_properties.selected.get())
1110 active = *params->update_properties.selected;
1111
1112 // The 'active' property has replaced 'selected'.
1113 if (params->update_properties.active.get())
1114 active = *params->update_properties.active;
1115
1116 if (active) {
1117 if (tab_strip->active_index() != tab_index) {
1118 tab_strip->ActivateTabAt(tab_index, false);
1119 DCHECK_EQ(contents, tab_strip->GetActiveWebContents());
1120 }
1121 }
1122
1123 if (params->update_properties.highlighted.get()) {
1124 bool highlighted = *params->update_properties.highlighted;
1125 if (highlighted != tab_strip->IsTabSelected(tab_index))
1126 tab_strip->ToggleSelectionAt(tab_index);
1127 }
1128
1129 if (params->update_properties.pinned.get()) {
1130 bool pinned = *params->update_properties.pinned;
1131 tab_strip->SetTabPinned(tab_index, pinned);
1132
1133 // Update the tab index because it may move when being pinned.
1134 tab_index = tab_strip->GetIndexOfWebContents(contents);
1135 }
1136
1137 if (params->update_properties.opener_tab_id.get()) {
1138 int opener_id = *params->update_properties.opener_tab_id;
1139
1140 WebContents* opener_contents = NULL;
1141 if (!ExtensionTabUtil::GetTabById(opener_id,
1142 GetProfile(),
1143 include_incognito(),
1144 NULL,
1145 NULL,
1146 &opener_contents,
1147 NULL))
1148 return false;
1149
1150 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents);
1151 }
1152
1153 if (!is_async) {
1154 PopulateResult();
1155 SendResponse(true);
1156 }
1157 return true;
1158 }
1159
UpdateURL(const std::string & url_string,int tab_id,bool * is_async)1160 bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
1161 int tab_id,
1162 bool* is_async) {
1163 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
1164 url_string, GetExtension());
1165
1166 if (!url.is_valid()) {
1167 error_ = ErrorUtils::FormatErrorMessage(
1168 keys::kInvalidUrlError, url_string);
1169 return false;
1170 }
1171
1172 // Don't let the extension crash the browser or renderers.
1173 if (ExtensionTabUtil::IsCrashURL(url)) {
1174 error_ = keys::kNoCrashBrowserError;
1175 return false;
1176 }
1177
1178 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so
1179 // we need to check host permissions before allowing them.
1180 if (url.SchemeIs(url::kJavaScriptScheme)) {
1181 content::RenderProcessHost* process = web_contents_->GetRenderProcessHost();
1182 if (!GetExtension()->permissions_data()->CanAccessPage(
1183 GetExtension(),
1184 web_contents_->GetURL(),
1185 web_contents_->GetURL(),
1186 tab_id,
1187 process ? process->GetID() : -1,
1188 &error_)) {
1189 return false;
1190 }
1191
1192 TabHelper::FromWebContents(web_contents_)->script_executor()->ExecuteScript(
1193 extension_id(),
1194 ScriptExecutor::JAVASCRIPT,
1195 url.GetContent(),
1196 ScriptExecutor::TOP_FRAME,
1197 ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
1198 UserScript::DOCUMENT_IDLE,
1199 ScriptExecutor::MAIN_WORLD,
1200 ScriptExecutor::DEFAULT_PROCESS,
1201 GURL(),
1202 GURL(),
1203 user_gesture_,
1204 ScriptExecutor::NO_RESULT,
1205 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
1206
1207 *is_async = true;
1208 return true;
1209 }
1210
1211 web_contents_->GetController().LoadURL(
1212 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
1213
1214 // The URL of a tab contents never actually changes to a JavaScript URL, so
1215 // this check only makes sense in other cases.
1216 if (!url.SchemeIs(url::kJavaScriptScheme))
1217 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
1218
1219 return true;
1220 }
1221
PopulateResult()1222 void TabsUpdateFunction::PopulateResult() {
1223 if (!has_callback())
1224 return;
1225
1226 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension()));
1227 }
1228
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & url,const base::ListValue & script_result)1229 void TabsUpdateFunction::OnExecuteCodeFinished(
1230 const std::string& error,
1231 int32 on_page_id,
1232 const GURL& url,
1233 const base::ListValue& script_result) {
1234 if (error.empty())
1235 PopulateResult();
1236 else
1237 error_ = error;
1238 SendResponse(error.empty());
1239 }
1240
RunSync()1241 bool TabsMoveFunction::RunSync() {
1242 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_));
1243 EXTENSION_FUNCTION_VALIDATE(params.get());
1244
1245 int new_index = params->move_properties.index;
1246 int* window_id = params->move_properties.window_id.get();
1247 scoped_ptr<base::ListValue> tab_values(new base::ListValue());
1248
1249 size_t num_tabs = 0;
1250 if (params->tab_ids.as_integers) {
1251 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1252 num_tabs = tab_ids.size();
1253 for (size_t i = 0; i < tab_ids.size(); ++i) {
1254 if (!MoveTab(tab_ids[i], &new_index, i, tab_values.get(), window_id))
1255 return false;
1256 }
1257 } else {
1258 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1259 num_tabs = 1;
1260 if (!MoveTab(*params->tab_ids.as_integer,
1261 &new_index,
1262 0,
1263 tab_values.get(),
1264 window_id)) {
1265 return false;
1266 }
1267 }
1268
1269 if (!has_callback())
1270 return true;
1271
1272 if (num_tabs == 0) {
1273 error_ = "No tabs given.";
1274 return false;
1275 } else if (num_tabs == 1) {
1276 scoped_ptr<base::Value> value;
1277 CHECK(tab_values.get()->Remove(0, &value));
1278 SetResult(value.release());
1279 } else {
1280 // Only return the results as an array if there are multiple tabs.
1281 SetResult(tab_values.release());
1282 }
1283
1284 return true;
1285 }
1286
MoveTab(int tab_id,int * new_index,int iteration,base::ListValue * tab_values,int * window_id)1287 bool TabsMoveFunction::MoveTab(int tab_id,
1288 int *new_index,
1289 int iteration,
1290 base::ListValue* tab_values,
1291 int* window_id) {
1292 Browser* source_browser = NULL;
1293 TabStripModel* source_tab_strip = NULL;
1294 WebContents* contents = NULL;
1295 int tab_index = -1;
1296 if (!GetTabById(tab_id,
1297 GetProfile(),
1298 include_incognito(),
1299 &source_browser,
1300 &source_tab_strip,
1301 &contents,
1302 &tab_index,
1303 &error_)) {
1304 return false;
1305 }
1306
1307 // Don't let the extension move the tab if the user is dragging tabs.
1308 if (!source_browser->window()->IsTabStripEditable()) {
1309 error_ = keys::kTabStripNotEditableError;
1310 return false;
1311 }
1312
1313 // Insert the tabs one after another.
1314 *new_index += iteration;
1315
1316 if (window_id) {
1317 Browser* target_browser = NULL;
1318
1319 if (!GetBrowserFromWindowID(this, *window_id, &target_browser))
1320 return false;
1321
1322 if (!target_browser->window()->IsTabStripEditable()) {
1323 error_ = keys::kTabStripNotEditableError;
1324 return false;
1325 }
1326
1327 if (!target_browser->is_type_tabbed()) {
1328 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError;
1329 return false;
1330 }
1331
1332 if (target_browser->profile() != source_browser->profile()) {
1333 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError;
1334 return false;
1335 }
1336
1337 // If windowId is different from the current window, move between windows.
1338 if (ExtensionTabUtil::GetWindowId(target_browser) !=
1339 ExtensionTabUtil::GetWindowId(source_browser)) {
1340 TabStripModel* target_tab_strip = target_browser->tab_strip_model();
1341 WebContents* web_contents =
1342 source_tab_strip->DetachWebContentsAt(tab_index);
1343 if (!web_contents) {
1344 error_ = ErrorUtils::FormatErrorMessage(
1345 keys::kTabNotFoundError, base::IntToString(tab_id));
1346 return false;
1347 }
1348
1349 // Clamp move location to the last position.
1350 // This is ">" because it can append to a new index position.
1351 // -1 means set the move location to the last position.
1352 if (*new_index > target_tab_strip->count() || *new_index < 0)
1353 *new_index = target_tab_strip->count();
1354
1355 target_tab_strip->InsertWebContentsAt(
1356 *new_index, web_contents, TabStripModel::ADD_NONE);
1357
1358 if (has_callback()) {
1359 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1360 web_contents,
1361 target_tab_strip,
1362 *new_index,
1363 GetExtension()));
1364 }
1365
1366 return true;
1367 }
1368 }
1369
1370 // Perform a simple within-window move.
1371 // Clamp move location to the last position.
1372 // This is ">=" because the move must be to an existing location.
1373 // -1 means set the move location to the last position.
1374 if (*new_index >= source_tab_strip->count() || *new_index < 0)
1375 *new_index = source_tab_strip->count() - 1;
1376
1377 if (*new_index != tab_index)
1378 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false);
1379
1380 if (has_callback()) {
1381 tab_values->Append(ExtensionTabUtil::CreateTabValue(
1382 contents, source_tab_strip, *new_index, GetExtension()));
1383 }
1384
1385 return true;
1386 }
1387
RunSync()1388 bool TabsReloadFunction::RunSync() {
1389 scoped_ptr<tabs::Reload::Params> params(
1390 tabs::Reload::Params::Create(*args_));
1391 EXTENSION_FUNCTION_VALIDATE(params.get());
1392
1393 bool bypass_cache = false;
1394 if (params->reload_properties.get() &&
1395 params->reload_properties->bypass_cache.get()) {
1396 bypass_cache = *params->reload_properties->bypass_cache;
1397 }
1398
1399 content::WebContents* web_contents = NULL;
1400
1401 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1402 // in the current window.
1403 if (!params->tab_id.get()) {
1404 Browser* browser = GetCurrentBrowser();
1405 if (!browser) {
1406 error_ = keys::kNoCurrentWindowError;
1407 return false;
1408 }
1409
1410 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
1411 return false;
1412 } else {
1413 int tab_id = *params->tab_id;
1414
1415 Browser* browser = NULL;
1416 if (!GetTabById(tab_id,
1417 GetProfile(),
1418 include_incognito(),
1419 &browser,
1420 NULL,
1421 &web_contents,
1422 NULL,
1423 &error_))
1424 return false;
1425 }
1426
1427 if (web_contents->ShowingInterstitialPage()) {
1428 // This does as same as Browser::ReloadInternal.
1429 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
1430 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB,
1431 content::PAGE_TRANSITION_RELOAD, false);
1432 GetCurrentBrowser()->OpenURL(params);
1433 } else if (bypass_cache) {
1434 web_contents->GetController().ReloadIgnoringCache(true);
1435 } else {
1436 web_contents->GetController().Reload(true);
1437 }
1438
1439 return true;
1440 }
1441
RunSync()1442 bool TabsRemoveFunction::RunSync() {
1443 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_));
1444 EXTENSION_FUNCTION_VALIDATE(params.get());
1445
1446 if (params->tab_ids.as_integers) {
1447 std::vector<int>& tab_ids = *params->tab_ids.as_integers;
1448 for (size_t i = 0; i < tab_ids.size(); ++i) {
1449 if (!RemoveTab(tab_ids[i]))
1450 return false;
1451 }
1452 } else {
1453 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer);
1454 if (!RemoveTab(*params->tab_ids.as_integer.get()))
1455 return false;
1456 }
1457 return true;
1458 }
1459
RemoveTab(int tab_id)1460 bool TabsRemoveFunction::RemoveTab(int tab_id) {
1461 Browser* browser = NULL;
1462 WebContents* contents = NULL;
1463 if (!GetTabById(tab_id,
1464 GetProfile(),
1465 include_incognito(),
1466 &browser,
1467 NULL,
1468 &contents,
1469 NULL,
1470 &error_)) {
1471 return false;
1472 }
1473
1474 // Don't let the extension remove a tab if the user is dragging tabs around.
1475 if (!browser->window()->IsTabStripEditable()) {
1476 error_ = keys::kTabStripNotEditableError;
1477 return false;
1478 }
1479 // There's a chance that the tab is being dragged, or we're in some other
1480 // nested event loop. This code path ensures that the tab is safely closed
1481 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()|
1482 // does not.
1483 contents->Close();
1484 return true;
1485 }
1486
IsScreenshotEnabled()1487 bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
1488 PrefService* service = GetProfile()->GetPrefs();
1489 if (service->GetBoolean(prefs::kDisableScreenshots)) {
1490 error_ = keys::kScreenshotsDisabled;
1491 return false;
1492 }
1493 return true;
1494 }
1495
GetWebContentsForID(int window_id)1496 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) {
1497 Browser* browser = NULL;
1498 if (!GetBrowserFromWindowID(this, window_id, &browser))
1499 return NULL;
1500
1501 WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
1502 if (!contents) {
1503 error_ = keys::kInternalVisibleTabCaptureError;
1504 return NULL;
1505 }
1506
1507 if (!GetExtension()->permissions_data()->CanCaptureVisiblePage(
1508 SessionID::IdForTab(contents), &error_)) {
1509 return NULL;
1510 }
1511 return contents;
1512 }
1513
OnCaptureFailure(FailureReason reason)1514 void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
1515 error_ = keys::kInternalVisibleTabCaptureError;
1516 SendResponse(false);
1517 }
1518
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)1519 void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
1520 user_prefs::PrefRegistrySyncable* registry) {
1521 registry->RegisterBooleanPref(
1522 prefs::kDisableScreenshots,
1523 false,
1524 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1525 }
1526
RunAsync()1527 bool TabsDetectLanguageFunction::RunAsync() {
1528 scoped_ptr<tabs::DetectLanguage::Params> params(
1529 tabs::DetectLanguage::Params::Create(*args_));
1530 EXTENSION_FUNCTION_VALIDATE(params.get());
1531
1532 int tab_id = 0;
1533 Browser* browser = NULL;
1534 WebContents* contents = NULL;
1535
1536 // If |tab_id| is specified, look for it. Otherwise default to selected tab
1537 // in the current window.
1538 if (params->tab_id.get()) {
1539 tab_id = *params->tab_id;
1540 if (!GetTabById(tab_id,
1541 GetProfile(),
1542 include_incognito(),
1543 &browser,
1544 NULL,
1545 &contents,
1546 NULL,
1547 &error_)) {
1548 return false;
1549 }
1550 if (!browser || !contents)
1551 return false;
1552 } else {
1553 browser = GetCurrentBrowser();
1554 if (!browser)
1555 return false;
1556 contents = browser->tab_strip_model()->GetActiveWebContents();
1557 if (!contents)
1558 return false;
1559 }
1560
1561 if (contents->GetController().NeedsReload()) {
1562 // If the tab hasn't been loaded, don't wait for the tab to load.
1563 error_ = keys::kCannotDetermineLanguageOfUnloadedTab;
1564 return false;
1565 }
1566
1567 AddRef(); // Balanced in GotLanguage().
1568
1569 ChromeTranslateClient* chrome_translate_client =
1570 ChromeTranslateClient::FromWebContents(contents);
1571 if (!chrome_translate_client->GetLanguageState()
1572 .original_language()
1573 .empty()) {
1574 // Delay the callback invocation until after the current JS call has
1575 // returned.
1576 base::MessageLoop::current()->PostTask(
1577 FROM_HERE,
1578 base::Bind(
1579 &TabsDetectLanguageFunction::GotLanguage,
1580 this,
1581 chrome_translate_client->GetLanguageState().original_language()));
1582 return true;
1583 }
1584 // The tab contents does not know its language yet. Let's wait until it
1585 // receives it, or until the tab is closed/navigates to some other page.
1586 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
1587 content::Source<WebContents>(contents));
1588 registrar_.Add(
1589 this, chrome::NOTIFICATION_TAB_CLOSING,
1590 content::Source<NavigationController>(&(contents->GetController())));
1591 registrar_.Add(
1592 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1593 content::Source<NavigationController>(&(contents->GetController())));
1594 return true;
1595 }
1596
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1597 void TabsDetectLanguageFunction::Observe(
1598 int type,
1599 const content::NotificationSource& source,
1600 const content::NotificationDetails& details) {
1601 std::string language;
1602 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) {
1603 const LanguageDetectionDetails* lang_det_details =
1604 content::Details<const LanguageDetectionDetails>(details).ptr();
1605 language = lang_det_details->adopted_language;
1606 }
1607
1608 registrar_.RemoveAll();
1609
1610 // Call GotLanguage in all cases as we want to guarantee the callback is
1611 // called for every API call the extension made.
1612 GotLanguage(language);
1613 }
1614
GotLanguage(const std::string & language)1615 void TabsDetectLanguageFunction::GotLanguage(const std::string& language) {
1616 SetResult(new base::StringValue(language.c_str()));
1617 SendResponse(true);
1618
1619 Release(); // Balanced in Run()
1620 }
1621
ExecuteCodeInTabFunction()1622 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
1623 : execute_tab_id_(-1) {
1624 }
1625
~ExecuteCodeInTabFunction()1626 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
1627
HasPermission()1628 bool ExecuteCodeInTabFunction::HasPermission() {
1629 if (Init() &&
1630 extension_->permissions_data()->HasAPIPermissionForTab(
1631 execute_tab_id_, APIPermission::kTab)) {
1632 return true;
1633 }
1634 return ExtensionFunction::HasPermission();
1635 }
1636
CanExecuteScriptOnPage()1637 bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() {
1638 content::WebContents* contents = NULL;
1639
1640 // If |tab_id| is specified, look for the tab. Otherwise default to selected
1641 // tab in the current window.
1642 CHECK_GE(execute_tab_id_, 0);
1643 if (!GetTabById(execute_tab_id_,
1644 GetProfile(),
1645 include_incognito(),
1646 NULL,
1647 NULL,
1648 &contents,
1649 NULL,
1650 &error_)) {
1651 return false;
1652 }
1653
1654 CHECK(contents);
1655
1656 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
1657 // we check again in the renderer.
1658 content::RenderProcessHost* process = contents->GetRenderProcessHost();
1659 if (!GetExtension()->permissions_data()->CanAccessPage(
1660 GetExtension(),
1661 contents->GetURL(),
1662 contents->GetURL(),
1663 execute_tab_id_,
1664 process ? process->GetID() : -1,
1665 &error_)) {
1666 return false;
1667 }
1668
1669 return true;
1670 }
1671
GetScriptExecutor()1672 ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() {
1673 Browser* browser = NULL;
1674 content::WebContents* contents = NULL;
1675
1676 bool success = GetTabById(execute_tab_id_,
1677 GetProfile(),
1678 include_incognito(),
1679 &browser,
1680 NULL,
1681 &contents,
1682 NULL,
1683 &error_) &&
1684 contents && browser;
1685
1686 if (!success)
1687 return NULL;
1688
1689 return TabHelper::FromWebContents(contents)->script_executor();
1690 }
1691
IsWebView() const1692 bool ExecuteCodeInTabFunction::IsWebView() const {
1693 return false;
1694 }
1695
GetWebViewSrc() const1696 const GURL& ExecuteCodeInTabFunction::GetWebViewSrc() const {
1697 return GURL::EmptyGURL();
1698 }
1699
ShouldInsertCSS() const1700 bool TabsExecuteScriptFunction::ShouldInsertCSS() const {
1701 return false;
1702 }
1703
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & on_url,const base::ListValue & result)1704 void TabsExecuteScriptFunction::OnExecuteCodeFinished(
1705 const std::string& error,
1706 int32 on_page_id,
1707 const GURL& on_url,
1708 const base::ListValue& result) {
1709 if (error.empty())
1710 SetResult(result.DeepCopy());
1711 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
1712 result);
1713 }
1714
Init()1715 bool ExecuteCodeInTabFunction::Init() {
1716 if (details_.get())
1717 return true;
1718
1719 // |tab_id| is optional so it's ok if it's not there.
1720 int tab_id = -1;
1721 if (args_->GetInteger(0, &tab_id))
1722 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0);
1723
1724 // |details| are not optional.
1725 base::DictionaryValue* details_value = NULL;
1726 if (!args_->GetDictionary(1, &details_value))
1727 return false;
1728 scoped_ptr<InjectDetails> details(new InjectDetails());
1729 if (!InjectDetails::Populate(*details_value, details.get()))
1730 return false;
1731
1732 // If the tab ID wasn't given then it needs to be converted to the
1733 // currently active tab's ID.
1734 if (tab_id == -1) {
1735 Browser* browser = GetCurrentBrowser();
1736 if (!browser)
1737 return false;
1738 content::WebContents* web_contents = NULL;
1739 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id))
1740 return false;
1741 }
1742
1743 execute_tab_id_ = tab_id;
1744 details_ = details.Pass();
1745 return true;
1746 }
1747
ShouldInsertCSS() const1748 bool TabsInsertCSSFunction::ShouldInsertCSS() const {
1749 return true;
1750 }
1751
1752 } // namespace extensions
1753