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