tabs_api.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_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/url_constants.h" 62#include "components/translate/core/common/language_detection_details.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/app_window_registry.h" 94#include "ash/ash_switches.h" 95#include "chrome/browser/extensions/api/tabs/ash_panel_contents.h" 96#endif 97 98using apps::AppWindow; 99using content::BrowserThread; 100using content::NavigationController; 101using content::NavigationEntry; 102using content::OpenURLParams; 103using content::Referrer; 104using content::WebContents; 105 106namespace extensions { 107 108namespace windows = api::windows; 109namespace keys = tabs_constants; 110namespace tabs = api::tabs; 111 112using api::tabs::InjectDetails; 113 114namespace { 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. 118Browser* 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 142bool 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. 167bool 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. 189bool MatchesBool(bool* boolean, bool value) { 190 return !boolean || *boolean == value; 191} 192 193Browser* 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 220bool 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 242bool 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 264bool 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 291bool 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 317bool 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 365bool 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 AppWindow::CreateParams create_params; 527 create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL; 528 create_params.bounds = window_bounds; 529 create_params.focused = saw_focus_key && focused; 530 AppWindow* app_window = new AppWindow( 531 window_profile, new ChromeShellWindowDelegate(), GetExtension()); 532 AshPanelContents* ash_panel_contents = new AshPanelContents(app_window); 533 app_window->Init(urls[0], ash_panel_contents, create_params); 534 SetResult(ash_panel_contents->GetExtensionWindowController()-> 535 CreateWindowValueWithTabs(GetExtension())); 536 return true; 537 } 538#endif 539 std::string title = 540 web_app::GenerateApplicationNameFromExtensionId(extension_id); 541 // Note: Panels ignore all but the first url provided. 542 Panel* panel = PanelManager::GetInstance()->CreatePanel( 543 title, window_profile, urls[0], window_bounds, panel_create_mode); 544 545 // Unlike other window types, Panels do not take focus by default. 546 if (!saw_focus_key || !focused) 547 panel->ShowInactive(); 548 else 549 panel->Show(); 550 551 SetResult( 552 panel->extension_window_controller()->CreateWindowValueWithTabs( 553 GetExtension())); 554 return true; 555 } 556 557 // Create a new BrowserWindow. 558 chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop(); 559 if (create_panel) 560 window_type = Browser::TYPE_POPUP; 561 Browser::CreateParams create_params(window_type, window_profile, 562 host_desktop_type); 563 if (extension_id.empty()) { 564 create_params.initial_bounds = window_bounds; 565 } else { 566 create_params = Browser::CreateParams::CreateForApp( 567 window_type, 568 web_app::GenerateApplicationNameFromExtensionId(extension_id), 569 window_bounds, 570 window_profile, 571 host_desktop_type); 572 } 573 create_params.initial_show_state = ui::SHOW_STATE_NORMAL; 574 create_params.host_desktop_type = chrome::GetActiveDesktop(); 575 576 Browser* new_window = CreateBrowserWindow(create_params, window_profile, 577 extension_id); 578 579 for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) { 580 WebContents* tab = chrome::AddSelectedTabWithURL( 581 new_window, *i, content::PAGE_TRANSITION_LINK); 582 if (create_panel) { 583 TabHelper::FromWebContents(tab)->SetExtensionAppIconById(extension_id); 584 } 585 } 586 587 WebContents* contents = NULL; 588 // Move the tab into the created window only if it's an empty popup or it's 589 // a tabbed window. 590 if ((window_type == Browser::TYPE_POPUP && urls.empty()) || 591 window_type == Browser::TYPE_TABBED) { 592 if (source_tab_strip) 593 contents = source_tab_strip->DetachWebContentsAt(tab_index); 594 if (contents) { 595 TabStripModel* target_tab_strip = new_window->tab_strip_model(); 596 target_tab_strip->InsertWebContentsAt(urls.size(), contents, 597 TabStripModel::ADD_NONE); 598 } 599 } 600 // Create a new tab if the created window is still empty. Don't create a new 601 // tab when it is intended to create an empty popup. 602 if (!contents && urls.empty() && window_type != Browser::TYPE_POPUP) { 603 chrome::NewTab(new_window); 604 } 605 chrome::SelectNumberedTab(new_window, 0); 606 607 // Unlike other window types, Panels do not take focus by default. 608 if (!saw_focus_key && create_panel) 609 focused = false; 610 611 if (focused) 612 new_window->window()->Show(); 613 else 614 new_window->window()->ShowInactive(); 615 616 if (new_window->profile()->IsOffTheRecord() && 617 !GetProfile()->IsOffTheRecord() && !include_incognito()) { 618 // Don't expose incognito windows if extension itself works in non-incognito 619 // profile and CanCrossIncognito isn't allowed. 620 SetResult(base::Value::CreateNullValue()); 621 } else { 622 SetResult( 623 new_window->extension_window_controller()->CreateWindowValueWithTabs( 624 GetExtension())); 625 } 626 627 return true; 628} 629 630bool WindowsUpdateFunction::RunImpl() { 631 scoped_ptr<windows::Update::Params> params( 632 windows::Update::Params::Create(*args_)); 633 EXTENSION_FUNCTION_VALIDATE(params); 634 635 WindowController* controller; 636 if (!windows_util::GetWindowFromWindowID(this, params->window_id, 637 &controller)) 638 return false; 639 640#if defined(OS_WIN) 641 // Silently ignore changes on the window for metro mode. 642 if (win8::IsSingleWindowMetroMode()) { 643 SetResult(controller->CreateWindowValue()); 644 return true; 645 } 646#endif 647 648 ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT; // No change. 649 switch (params->update_info.state) { 650 case windows::Update::Params::UpdateInfo::STATE_NORMAL: 651 show_state = ui::SHOW_STATE_NORMAL; 652 break; 653 case windows::Update::Params::UpdateInfo::STATE_MINIMIZED: 654 show_state = ui::SHOW_STATE_MINIMIZED; 655 break; 656 case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED: 657 show_state = ui::SHOW_STATE_MAXIMIZED; 658 break; 659 case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN: 660 show_state = ui::SHOW_STATE_FULLSCREEN; 661 break; 662 case windows::Update::Params::UpdateInfo::STATE_NONE: 663 break; 664 default: 665 error_ = keys::kInvalidWindowStateError; 666 return false; 667 } 668 669 if (show_state != ui::SHOW_STATE_FULLSCREEN && 670 show_state != ui::SHOW_STATE_DEFAULT) 671 controller->SetFullscreenMode(false, GetExtension()->url()); 672 673 switch (show_state) { 674 case ui::SHOW_STATE_MINIMIZED: 675 controller->window()->Minimize(); 676 break; 677 case ui::SHOW_STATE_MAXIMIZED: 678 controller->window()->Maximize(); 679 break; 680 case ui::SHOW_STATE_FULLSCREEN: 681 if (controller->window()->IsMinimized() || 682 controller->window()->IsMaximized()) 683 controller->window()->Restore(); 684 controller->SetFullscreenMode(true, GetExtension()->url()); 685 break; 686 case ui::SHOW_STATE_NORMAL: 687 controller->window()->Restore(); 688 break; 689 default: 690 break; 691 } 692 693 gfx::Rect bounds; 694 if (controller->window()->IsMinimized()) 695 bounds = controller->window()->GetRestoredBounds(); 696 else 697 bounds = controller->window()->GetBounds(); 698 bool set_bounds = false; 699 700 // Any part of the bounds can optionally be set by the caller. 701 if (params->update_info.left) { 702 bounds.set_x(*params->update_info.left); 703 set_bounds = true; 704 } 705 706 if (params->update_info.top) { 707 bounds.set_y(*params->update_info.top); 708 set_bounds = true; 709 } 710 711 if (params->update_info.width) { 712 bounds.set_width(*params->update_info.width); 713 set_bounds = true; 714 } 715 716 if (params->update_info.height) { 717 bounds.set_height(*params->update_info.height); 718 set_bounds = true; 719 } 720 721 if (set_bounds) { 722 if (show_state == ui::SHOW_STATE_MINIMIZED || 723 show_state == ui::SHOW_STATE_MAXIMIZED || 724 show_state == ui::SHOW_STATE_FULLSCREEN) { 725 error_ = keys::kInvalidWindowStateError; 726 return false; 727 } 728 // TODO(varkha): Updating bounds during a drag can cause problems and a more 729 // general solution is needed. See http://crbug.com/251813 . 730 controller->window()->SetBounds(bounds); 731 } 732 733 if (params->update_info.focused) { 734 if (*params->update_info.focused) { 735 if (show_state == ui::SHOW_STATE_MINIMIZED) { 736 error_ = keys::kInvalidWindowStateError; 737 return false; 738 } 739 controller->window()->Activate(); 740 } else { 741 if (show_state == ui::SHOW_STATE_MAXIMIZED || 742 show_state == ui::SHOW_STATE_FULLSCREEN) { 743 error_ = keys::kInvalidWindowStateError; 744 return false; 745 } 746 controller->window()->Deactivate(); 747 } 748 } 749 750 if (params->update_info.draw_attention) 751 controller->window()->FlashFrame(*params->update_info.draw_attention); 752 753 SetResult(controller->CreateWindowValue()); 754 755 return true; 756} 757 758bool WindowsRemoveFunction::RunImpl() { 759 scoped_ptr<windows::Remove::Params> params( 760 windows::Remove::Params::Create(*args_)); 761 EXTENSION_FUNCTION_VALIDATE(params); 762 763 WindowController* controller; 764 if (!windows_util::GetWindowFromWindowID(this, params->window_id, 765 &controller)) 766 return false; 767 768#if defined(OS_WIN) 769 // In Windows 8 metro mode, an existing Browser instance is reused for 770 // hosting the extension tab. We should not be closing it as we don't own it. 771 if (win8::IsSingleWindowMetroMode()) 772 return false; 773#endif 774 775 WindowController::Reason reason; 776 if (!controller->CanClose(&reason)) { 777 if (reason == WindowController::REASON_NOT_EDITABLE) 778 error_ = keys::kTabStripNotEditableError; 779 return false; 780 } 781 controller->window()->Close(); 782 return true; 783} 784 785// Tabs ------------------------------------------------------------------------ 786 787bool TabsGetSelectedFunction::RunImpl() { 788 // windowId defaults to "current" window. 789 int window_id = extension_misc::kCurrentWindowId; 790 791 scoped_ptr<tabs::GetSelected::Params> params( 792 tabs::GetSelected::Params::Create(*args_)); 793 EXTENSION_FUNCTION_VALIDATE(params.get()); 794 if (params->window_id.get()) 795 window_id = *params->window_id; 796 797 Browser* browser = NULL; 798 if (!GetBrowserFromWindowID(this, window_id, &browser)) 799 return false; 800 801 TabStripModel* tab_strip = browser->tab_strip_model(); 802 WebContents* contents = tab_strip->GetActiveWebContents(); 803 if (!contents) { 804 error_ = keys::kNoSelectedTabError; 805 return false; 806 } 807 SetResult(ExtensionTabUtil::CreateTabValue(contents, 808 tab_strip, 809 tab_strip->active_index(), 810 GetExtension())); 811 return true; 812} 813 814bool TabsGetAllInWindowFunction::RunImpl() { 815 scoped_ptr<tabs::GetAllInWindow::Params> params( 816 tabs::GetAllInWindow::Params::Create(*args_)); 817 EXTENSION_FUNCTION_VALIDATE(params.get()); 818 // windowId defaults to "current" window. 819 int window_id = extension_misc::kCurrentWindowId; 820 if (params->window_id.get()) 821 window_id = *params->window_id; 822 823 Browser* browser = NULL; 824 if (!GetBrowserFromWindowID(this, window_id, &browser)) 825 return false; 826 827 SetResult(ExtensionTabUtil::CreateTabList(browser, GetExtension())); 828 829 return true; 830} 831 832bool TabsQueryFunction::RunImpl() { 833 scoped_ptr<tabs::Query::Params> params(tabs::Query::Params::Create(*args_)); 834 EXTENSION_FUNCTION_VALIDATE(params.get()); 835 836 bool loading_status_set = params->query_info.status != 837 tabs::Query::Params::QueryInfo::STATUS_NONE; 838 bool loading = params->query_info.status == 839 tabs::Query::Params::QueryInfo::STATUS_LOADING; 840 841 // It is o.k. to use URLPattern::SCHEME_ALL here because this function does 842 // not grant access to the content of the tabs, only to seeing their URLs and 843 // meta data. 844 URLPattern url_pattern(URLPattern::SCHEME_ALL, "<all_urls>"); 845 if (params->query_info.url.get()) 846 url_pattern = URLPattern(URLPattern::SCHEME_ALL, *params->query_info.url); 847 848 std::string title; 849 if (params->query_info.title.get()) 850 title = *params->query_info.title; 851 852 int window_id = extension_misc::kUnknownWindowId; 853 if (params->query_info.window_id.get()) 854 window_id = *params->query_info.window_id; 855 856 int index = -1; 857 if (params->query_info.index.get()) 858 index = *params->query_info.index; 859 860 std::string window_type; 861 if (params->query_info.window_type != 862 tabs::Query::Params::QueryInfo::WINDOW_TYPE_NONE) { 863 window_type = tabs::Query::Params::QueryInfo::ToString( 864 params->query_info.window_type); 865 } 866 867 base::ListValue* result = new base::ListValue(); 868 Browser* last_active_browser = chrome::FindAnyBrowser( 869 GetProfile(), include_incognito(), chrome::GetActiveDesktop()); 870 Browser* current_browser = GetCurrentBrowser(); 871 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 872 Browser* browser = *it; 873 if (!GetProfile()->IsSameProfile(browser->profile())) 874 continue; 875 876 if (!browser->window()) 877 continue; 878 879 if (!include_incognito() && GetProfile() != browser->profile()) 880 continue; 881 882 if (window_id >= 0 && window_id != ExtensionTabUtil::GetWindowId(browser)) 883 continue; 884 885 if (window_id == extension_misc::kCurrentWindowId && 886 browser != current_browser) { 887 continue; 888 } 889 890 if (!MatchesBool(params->query_info.current_window.get(), 891 browser == current_browser)) { 892 continue; 893 } 894 895 if (!MatchesBool(params->query_info.last_focused_window.get(), 896 browser == last_active_browser)) { 897 continue; 898 } 899 900 if (!window_type.empty() && 901 window_type != 902 browser->extension_window_controller()->GetWindowTypeText()) { 903 continue; 904 } 905 906 TabStripModel* tab_strip = browser->tab_strip_model(); 907 for (int i = 0; i < tab_strip->count(); ++i) { 908 const WebContents* web_contents = tab_strip->GetWebContentsAt(i); 909 910 if (index > -1 && i != index) 911 continue; 912 913 if (!MatchesBool(params->query_info.highlighted.get(), 914 tab_strip->IsTabSelected(i))) { 915 continue; 916 } 917 918 if (!MatchesBool(params->query_info.active.get(), 919 i == tab_strip->active_index())) { 920 continue; 921 } 922 923 if (!MatchesBool(params->query_info.pinned.get(), 924 tab_strip->IsTabPinned(i))) { 925 continue; 926 } 927 928 if (!title.empty() && !MatchPattern(web_contents->GetTitle(), 929 base::UTF8ToUTF16(title))) 930 continue; 931 932 if (!url_pattern.MatchesURL(web_contents->GetURL())) 933 continue; 934 935 if (loading_status_set && loading != web_contents->IsLoading()) 936 continue; 937 938 result->Append(ExtensionTabUtil::CreateTabValue( 939 web_contents, tab_strip, i, GetExtension())); 940 } 941 } 942 943 SetResult(result); 944 return true; 945} 946 947bool TabsCreateFunction::RunImpl() { 948 scoped_ptr<tabs::Create::Params> params(tabs::Create::Params::Create(*args_)); 949 EXTENSION_FUNCTION_VALIDATE(params.get()); 950 951 // windowId defaults to "current" window. 952 int window_id = extension_misc::kCurrentWindowId; 953 if (params->create_properties.window_id.get()) 954 window_id = *params->create_properties.window_id; 955 956 Browser* browser = NULL; 957 if (!GetBrowserFromWindowID(this, window_id, &browser)) 958 return false; 959 960 // Ensure the selected browser is tabbed. 961 if (!browser->is_type_tabbed() && browser->IsAttemptingToCloseBrowser()) 962 browser = chrome::FindTabbedBrowser( 963 GetProfile(), include_incognito(), browser->host_desktop_type()); 964 965 if (!browser || !browser->window()) 966 return false; 967 968 // TODO(jstritar): Add a constant, chrome.tabs.TAB_ID_ACTIVE, that 969 // represents the active tab. 970 WebContents* opener = NULL; 971 if (params->create_properties.opener_tab_id.get()) { 972 int opener_id = *params->create_properties.opener_tab_id; 973 974 if (!ExtensionTabUtil::GetTabById(opener_id, 975 GetProfile(), 976 include_incognito(), 977 NULL, 978 NULL, 979 &opener, 980 NULL)) { 981 return false; 982 } 983 } 984 985 // TODO(rafaelw): handle setting remaining tab properties: 986 // -title 987 // -favIconUrl 988 989 std::string url_string; 990 GURL url; 991 if (params->create_properties.url.get()) { 992 url_string = *params->create_properties.url; 993 url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string, 994 GetExtension()); 995 if (!url.is_valid()) { 996 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, 997 url_string); 998 return false; 999 } 1000 } 1001 1002 // Don't let extensions crash the browser or renderers. 1003 if (ExtensionTabUtil::IsCrashURL(url)) { 1004 error_ = keys::kNoCrashBrowserError; 1005 return false; 1006 } 1007 1008 // Default to foreground for the new tab. The presence of 'selected' property 1009 // will override this default. This property is deprecated ('active' should 1010 // be used instead). 1011 bool active = true; 1012 if (params->create_properties.selected.get()) 1013 active = *params->create_properties.selected; 1014 1015 // The 'active' property has replaced the 'selected' property. 1016 if (params->create_properties.active.get()) 1017 active = *params->create_properties.active; 1018 1019 // Default to not pinning the tab. Setting the 'pinned' property to true 1020 // will override this default. 1021 bool pinned = false; 1022 if (params->create_properties.pinned.get()) 1023 pinned = *params->create_properties.pinned; 1024 1025 // We can't load extension URLs into incognito windows unless the extension 1026 // uses split mode. Special case to fall back to a tabbed window. 1027 if (url.SchemeIs(kExtensionScheme) && 1028 !IncognitoInfo::IsSplitMode(GetExtension()) && 1029 browser->profile()->IsOffTheRecord()) { 1030 Profile* profile = browser->profile()->GetOriginalProfile(); 1031 chrome::HostDesktopType desktop_type = browser->host_desktop_type(); 1032 1033 browser = chrome::FindTabbedBrowser(profile, false, desktop_type); 1034 if (!browser) { 1035 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, 1036 profile, desktop_type)); 1037 browser->window()->Show(); 1038 } 1039 } 1040 1041 // If index is specified, honor the value, but keep it bound to 1042 // -1 <= index <= tab_strip->count() where -1 invokes the default behavior. 1043 int index = -1; 1044 if (params->create_properties.index.get()) 1045 index = *params->create_properties.index; 1046 1047 TabStripModel* tab_strip = browser->tab_strip_model(); 1048 1049 index = std::min(std::max(index, -1), tab_strip->count()); 1050 1051 int add_types = active ? TabStripModel::ADD_ACTIVE : 1052 TabStripModel::ADD_NONE; 1053 add_types |= TabStripModel::ADD_FORCE_INDEX; 1054 if (pinned) 1055 add_types |= TabStripModel::ADD_PINNED; 1056 chrome::NavigateParams navigate_params( 1057 browser, url, content::PAGE_TRANSITION_LINK); 1058 navigate_params.disposition = 1059 active ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; 1060 navigate_params.tabstrip_index = index; 1061 navigate_params.tabstrip_add_types = add_types; 1062 chrome::Navigate(&navigate_params); 1063 1064 // The tab may have been created in a different window, so make sure we look 1065 // at the right tab strip. 1066 tab_strip = navigate_params.browser->tab_strip_model(); 1067 int new_index = tab_strip->GetIndexOfWebContents( 1068 navigate_params.target_contents); 1069 if (opener) 1070 tab_strip->SetOpenerOfWebContentsAt(new_index, opener); 1071 1072 if (active) 1073 navigate_params.target_contents->GetView()->SetInitialFocus(); 1074 1075 // Return data about the newly created tab. 1076 if (has_callback()) { 1077 SetResult(ExtensionTabUtil::CreateTabValue( 1078 navigate_params.target_contents, 1079 tab_strip, new_index, GetExtension())); 1080 } 1081 1082 return true; 1083} 1084 1085bool TabsDuplicateFunction::RunImpl() { 1086 scoped_ptr<tabs::Duplicate::Params> params( 1087 tabs::Duplicate::Params::Create(*args_)); 1088 EXTENSION_FUNCTION_VALIDATE(params.get()); 1089 int tab_id = params->tab_id; 1090 1091 Browser* browser = NULL; 1092 TabStripModel* tab_strip = NULL; 1093 int tab_index = -1; 1094 if (!GetTabById(tab_id, 1095 GetProfile(), 1096 include_incognito(), 1097 &browser, 1098 &tab_strip, 1099 NULL, 1100 &tab_index, 1101 &error_)) { 1102 return false; 1103 } 1104 1105 WebContents* new_contents = chrome::DuplicateTabAt(browser, tab_index); 1106 if (!has_callback()) 1107 return true; 1108 1109 // Duplicated tab may not be in the same window as the original, so find 1110 // the window and the tab. 1111 TabStripModel* new_tab_strip = NULL; 1112 int new_tab_index = -1; 1113 ExtensionTabUtil::GetTabStripModel(new_contents, 1114 &new_tab_strip, 1115 &new_tab_index); 1116 if (!new_tab_strip || new_tab_index == -1) { 1117 return false; 1118 } 1119 1120 // Return data about the newly created tab. 1121 SetResult(ExtensionTabUtil::CreateTabValue( 1122 new_contents, 1123 new_tab_strip, new_tab_index, GetExtension())); 1124 1125 return true; 1126} 1127 1128bool TabsGetFunction::RunImpl() { 1129 scoped_ptr<tabs::Get::Params> params(tabs::Get::Params::Create(*args_)); 1130 EXTENSION_FUNCTION_VALIDATE(params.get()); 1131 int tab_id = params->tab_id; 1132 1133 TabStripModel* tab_strip = NULL; 1134 WebContents* contents = NULL; 1135 int tab_index = -1; 1136 if (!GetTabById(tab_id, 1137 GetProfile(), 1138 include_incognito(), 1139 NULL, 1140 &tab_strip, 1141 &contents, 1142 &tab_index, 1143 &error_)) 1144 return false; 1145 1146 SetResult(ExtensionTabUtil::CreateTabValue(contents, 1147 tab_strip, 1148 tab_index, 1149 GetExtension())); 1150 return true; 1151} 1152 1153bool TabsGetCurrentFunction::RunImpl() { 1154 DCHECK(dispatcher()); 1155 1156 WebContents* contents = dispatcher()->delegate()->GetAssociatedWebContents(); 1157 if (contents) 1158 SetResult(ExtensionTabUtil::CreateTabValue(contents, GetExtension())); 1159 1160 return true; 1161} 1162 1163bool TabsHighlightFunction::RunImpl() { 1164 scoped_ptr<tabs::Highlight::Params> params( 1165 tabs::Highlight::Params::Create(*args_)); 1166 EXTENSION_FUNCTION_VALIDATE(params.get()); 1167 1168 // Get the window id from the params; default to current window if omitted. 1169 int window_id = extension_misc::kCurrentWindowId; 1170 if (params->highlight_info.window_id.get()) 1171 window_id = *params->highlight_info.window_id; 1172 1173 Browser* browser = NULL; 1174 if (!GetBrowserFromWindowID(this, window_id, &browser)) 1175 return false; 1176 1177 TabStripModel* tabstrip = browser->tab_strip_model(); 1178 ui::ListSelectionModel selection; 1179 int active_index = -1; 1180 1181 if (params->highlight_info.tabs.as_integers) { 1182 std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers; 1183 // Create a new selection model as we read the list of tab indices. 1184 for (size_t i = 0; i < tab_indices.size(); ++i) { 1185 if (!HighlightTab(tabstrip, &selection, &active_index, tab_indices[i])) 1186 return false; 1187 } 1188 } else { 1189 EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer); 1190 if (!HighlightTab(tabstrip, 1191 &selection, 1192 &active_index, 1193 *params->highlight_info.tabs.as_integer)) { 1194 return false; 1195 } 1196 } 1197 1198 // Make sure they actually specified tabs to select. 1199 if (selection.empty()) { 1200 error_ = keys::kNoHighlightedTabError; 1201 return false; 1202 } 1203 1204 selection.set_active(active_index); 1205 browser->tab_strip_model()->SetSelectionFromModel(selection); 1206 SetResult( 1207 browser->extension_window_controller()->CreateWindowValueWithTabs( 1208 GetExtension())); 1209 return true; 1210} 1211 1212bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip, 1213 ui::ListSelectionModel* selection, 1214 int *active_index, 1215 int index) { 1216 // Make sure the index is in range. 1217 if (!tabstrip->ContainsIndex(index)) { 1218 error_ = ErrorUtils::FormatErrorMessage( 1219 keys::kTabIndexNotFoundError, base::IntToString(index)); 1220 return false; 1221 } 1222 1223 // By default, we make the first tab in the list active. 1224 if (*active_index == -1) 1225 *active_index = index; 1226 1227 selection->AddIndexToSelection(index); 1228 return true; 1229} 1230 1231TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) { 1232} 1233 1234bool TabsUpdateFunction::RunImpl() { 1235 scoped_ptr<tabs::Update::Params> params(tabs::Update::Params::Create(*args_)); 1236 EXTENSION_FUNCTION_VALIDATE(params.get()); 1237 1238 int tab_id = -1; 1239 WebContents* contents = NULL; 1240 if (!params->tab_id.get()) { 1241 Browser* browser = GetCurrentBrowser(); 1242 if (!browser) { 1243 error_ = keys::kNoCurrentWindowError; 1244 return false; 1245 } 1246 contents = browser->tab_strip_model()->GetActiveWebContents(); 1247 if (!contents) { 1248 error_ = keys::kNoSelectedTabError; 1249 return false; 1250 } 1251 tab_id = SessionID::IdForTab(contents); 1252 } else { 1253 tab_id = *params->tab_id; 1254 } 1255 1256 int tab_index = -1; 1257 TabStripModel* tab_strip = NULL; 1258 if (!GetTabById(tab_id, 1259 GetProfile(), 1260 include_incognito(), 1261 NULL, 1262 &tab_strip, 1263 &contents, 1264 &tab_index, 1265 &error_)) { 1266 return false; 1267 } 1268 1269 web_contents_ = contents; 1270 1271 // TODO(rafaelw): handle setting remaining tab properties: 1272 // -title 1273 // -favIconUrl 1274 1275 // Navigate the tab to a new location if the url is different. 1276 bool is_async = false; 1277 if (params->update_properties.url.get() && 1278 !UpdateURL(*params->update_properties.url, tab_id, &is_async)) { 1279 return false; 1280 } 1281 1282 bool active = false; 1283 // TODO(rafaelw): Setting |active| from js doesn't make much sense. 1284 // Move tab selection management up to window. 1285 if (params->update_properties.selected.get()) 1286 active = *params->update_properties.selected; 1287 1288 // The 'active' property has replaced 'selected'. 1289 if (params->update_properties.active.get()) 1290 active = *params->update_properties.active; 1291 1292 if (active) { 1293 if (tab_strip->active_index() != tab_index) { 1294 tab_strip->ActivateTabAt(tab_index, false); 1295 DCHECK_EQ(contents, tab_strip->GetActiveWebContents()); 1296 } 1297 } 1298 1299 if (params->update_properties.highlighted.get()) { 1300 bool highlighted = *params->update_properties.highlighted; 1301 if (highlighted != tab_strip->IsTabSelected(tab_index)) 1302 tab_strip->ToggleSelectionAt(tab_index); 1303 } 1304 1305 if (params->update_properties.pinned.get()) { 1306 bool pinned = *params->update_properties.pinned; 1307 tab_strip->SetTabPinned(tab_index, pinned); 1308 1309 // Update the tab index because it may move when being pinned. 1310 tab_index = tab_strip->GetIndexOfWebContents(contents); 1311 } 1312 1313 if (params->update_properties.opener_tab_id.get()) { 1314 int opener_id = *params->update_properties.opener_tab_id; 1315 1316 WebContents* opener_contents = NULL; 1317 if (!ExtensionTabUtil::GetTabById(opener_id, 1318 GetProfile(), 1319 include_incognito(), 1320 NULL, 1321 NULL, 1322 &opener_contents, 1323 NULL)) 1324 return false; 1325 1326 tab_strip->SetOpenerOfWebContentsAt(tab_index, opener_contents); 1327 } 1328 1329 if (!is_async) { 1330 PopulateResult(); 1331 SendResponse(true); 1332 } 1333 return true; 1334} 1335 1336bool TabsUpdateFunction::UpdateURL(const std::string &url_string, 1337 int tab_id, 1338 bool* is_async) { 1339 GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL( 1340 url_string, GetExtension()); 1341 1342 if (!url.is_valid()) { 1343 error_ = ErrorUtils::FormatErrorMessage( 1344 keys::kInvalidUrlError, url_string); 1345 return false; 1346 } 1347 1348 // Don't let the extension crash the browser or renderers. 1349 if (ExtensionTabUtil::IsCrashURL(url)) { 1350 error_ = keys::kNoCrashBrowserError; 1351 return false; 1352 } 1353 1354 // JavaScript URLs can do the same kinds of things as cross-origin XHR, so 1355 // we need to check host permissions before allowing them. 1356 if (url.SchemeIs(content::kJavaScriptScheme)) { 1357 content::RenderProcessHost* process = web_contents_->GetRenderProcessHost(); 1358 if (!PermissionsData::CanExecuteScriptOnPage( 1359 GetExtension(), 1360 web_contents_->GetURL(), 1361 web_contents_->GetURL(), 1362 tab_id, 1363 NULL, 1364 process ? process->GetID() : -1, 1365 &error_)) { 1366 return false; 1367 } 1368 1369 TabHelper::FromWebContents(web_contents_)-> 1370 script_executor()->ExecuteScript( 1371 extension_id(), 1372 ScriptExecutor::JAVASCRIPT, 1373 url.GetContent(), 1374 ScriptExecutor::TOP_FRAME, 1375 UserScript::DOCUMENT_IDLE, 1376 ScriptExecutor::MAIN_WORLD, 1377 ScriptExecutor::DEFAULT_PROCESS, 1378 GURL(), 1379 ScriptExecutor::NO_RESULT, 1380 base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this)); 1381 1382 *is_async = true; 1383 return true; 1384 } 1385 1386 web_contents_->GetController().LoadURL( 1387 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string()); 1388 1389 // The URL of a tab contents never actually changes to a JavaScript URL, so 1390 // this check only makes sense in other cases. 1391 if (!url.SchemeIs(content::kJavaScriptScheme)) 1392 DCHECK_EQ(url.spec(), web_contents_->GetURL().spec()); 1393 1394 return true; 1395} 1396 1397void TabsUpdateFunction::PopulateResult() { 1398 if (!has_callback()) 1399 return; 1400 1401 SetResult(ExtensionTabUtil::CreateTabValue(web_contents_, GetExtension())); 1402} 1403 1404void TabsUpdateFunction::OnExecuteCodeFinished( 1405 const std::string& error, 1406 int32 on_page_id, 1407 const GURL& url, 1408 const base::ListValue& script_result) { 1409 if (error.empty()) 1410 PopulateResult(); 1411 else 1412 error_ = error; 1413 SendResponse(error.empty()); 1414} 1415 1416bool TabsMoveFunction::RunImpl() { 1417 scoped_ptr<tabs::Move::Params> params(tabs::Move::Params::Create(*args_)); 1418 EXTENSION_FUNCTION_VALIDATE(params.get()); 1419 1420 int new_index = params->move_properties.index; 1421 int* window_id = params->move_properties.window_id.get(); 1422 base::ListValue tab_values; 1423 1424 size_t num_tabs = 0; 1425 if (params->tab_ids.as_integers) { 1426 std::vector<int>& tab_ids = *params->tab_ids.as_integers; 1427 num_tabs = tab_ids.size(); 1428 for (size_t i = 0; i < tab_ids.size(); ++i) { 1429 if (!MoveTab(tab_ids[i], &new_index, i, &tab_values, window_id)) 1430 return false; 1431 } 1432 } else { 1433 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer); 1434 num_tabs = 1; 1435 if (!MoveTab(*params->tab_ids.as_integer, 1436 &new_index, 1437 0, 1438 &tab_values, 1439 window_id)) { 1440 return false; 1441 } 1442 } 1443 1444 if (!has_callback()) 1445 return true; 1446 1447 // Only return the results as an array if there are multiple tabs. 1448 if (num_tabs > 1) { 1449 SetResult(tab_values.DeepCopy()); 1450 } else { 1451 base::Value* value = NULL; 1452 CHECK(tab_values.Get(0, &value)); 1453 SetResult(value->DeepCopy()); 1454 } 1455 return true; 1456} 1457 1458bool TabsMoveFunction::MoveTab(int tab_id, 1459 int *new_index, 1460 int iteration, 1461 base::ListValue* tab_values, 1462 int* window_id) { 1463 Browser* source_browser = NULL; 1464 TabStripModel* source_tab_strip = NULL; 1465 WebContents* contents = NULL; 1466 int tab_index = -1; 1467 if (!GetTabById(tab_id, 1468 GetProfile(), 1469 include_incognito(), 1470 &source_browser, 1471 &source_tab_strip, 1472 &contents, 1473 &tab_index, 1474 &error_)) { 1475 return false; 1476 } 1477 1478 // Don't let the extension move the tab if the user is dragging tabs. 1479 if (!source_browser->window()->IsTabStripEditable()) { 1480 error_ = keys::kTabStripNotEditableError; 1481 return false; 1482 } 1483 1484 // Insert the tabs one after another. 1485 *new_index += iteration; 1486 1487 if (window_id) { 1488 Browser* target_browser = NULL; 1489 1490 if (!GetBrowserFromWindowID(this, *window_id, &target_browser)) 1491 return false; 1492 1493 if (!target_browser->window()->IsTabStripEditable()) { 1494 error_ = keys::kTabStripNotEditableError; 1495 return false; 1496 } 1497 1498 if (!target_browser->is_type_tabbed()) { 1499 error_ = keys::kCanOnlyMoveTabsWithinNormalWindowsError; 1500 return false; 1501 } 1502 1503 if (target_browser->profile() != source_browser->profile()) { 1504 error_ = keys::kCanOnlyMoveTabsWithinSameProfileError; 1505 return false; 1506 } 1507 1508 // If windowId is different from the current window, move between windows. 1509 if (ExtensionTabUtil::GetWindowId(target_browser) != 1510 ExtensionTabUtil::GetWindowId(source_browser)) { 1511 TabStripModel* target_tab_strip = target_browser->tab_strip_model(); 1512 WebContents* web_contents = 1513 source_tab_strip->DetachWebContentsAt(tab_index); 1514 if (!web_contents) { 1515 error_ = ErrorUtils::FormatErrorMessage( 1516 keys::kTabNotFoundError, base::IntToString(tab_id)); 1517 return false; 1518 } 1519 1520 // Clamp move location to the last position. 1521 // This is ">" because it can append to a new index position. 1522 // -1 means set the move location to the last position. 1523 if (*new_index > target_tab_strip->count() || *new_index < 0) 1524 *new_index = target_tab_strip->count(); 1525 1526 target_tab_strip->InsertWebContentsAt( 1527 *new_index, web_contents, TabStripModel::ADD_NONE); 1528 1529 if (has_callback()) { 1530 tab_values->Append(ExtensionTabUtil::CreateTabValue( 1531 web_contents, 1532 target_tab_strip, 1533 *new_index, 1534 GetExtension())); 1535 } 1536 1537 return true; 1538 } 1539 } 1540 1541 // Perform a simple within-window move. 1542 // Clamp move location to the last position. 1543 // This is ">=" because the move must be to an existing location. 1544 // -1 means set the move location to the last position. 1545 if (*new_index >= source_tab_strip->count() || *new_index < 0) 1546 *new_index = source_tab_strip->count() - 1; 1547 1548 if (*new_index != tab_index) 1549 source_tab_strip->MoveWebContentsAt(tab_index, *new_index, false); 1550 1551 if (has_callback()) { 1552 tab_values->Append(ExtensionTabUtil::CreateTabValue( 1553 contents, source_tab_strip, *new_index, GetExtension())); 1554 } 1555 1556 return true; 1557} 1558 1559bool TabsReloadFunction::RunImpl() { 1560 scoped_ptr<tabs::Reload::Params> params( 1561 tabs::Reload::Params::Create(*args_)); 1562 EXTENSION_FUNCTION_VALIDATE(params.get()); 1563 1564 bool bypass_cache = false; 1565 if (params->reload_properties.get() && 1566 params->reload_properties->bypass_cache.get()) { 1567 bypass_cache = *params->reload_properties->bypass_cache; 1568 } 1569 1570 content::WebContents* web_contents = NULL; 1571 1572 // If |tab_id| is specified, look for it. Otherwise default to selected tab 1573 // in the current window. 1574 if (!params->tab_id.get()) { 1575 Browser* browser = GetCurrentBrowser(); 1576 if (!browser) { 1577 error_ = keys::kNoCurrentWindowError; 1578 return false; 1579 } 1580 1581 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL)) 1582 return false; 1583 } else { 1584 int tab_id = *params->tab_id; 1585 1586 Browser* browser = NULL; 1587 if (!GetTabById(tab_id, 1588 GetProfile(), 1589 include_incognito(), 1590 &browser, 1591 NULL, 1592 &web_contents, 1593 NULL, 1594 &error_)) 1595 return false; 1596 } 1597 1598 if (web_contents->ShowingInterstitialPage()) { 1599 // This does as same as Browser::ReloadInternal. 1600 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); 1601 OpenURLParams params(entry->GetURL(), Referrer(), CURRENT_TAB, 1602 content::PAGE_TRANSITION_RELOAD, false); 1603 GetCurrentBrowser()->OpenURL(params); 1604 } else if (bypass_cache) { 1605 web_contents->GetController().ReloadIgnoringCache(true); 1606 } else { 1607 web_contents->GetController().Reload(true); 1608 } 1609 1610 return true; 1611} 1612 1613bool TabsRemoveFunction::RunImpl() { 1614 scoped_ptr<tabs::Remove::Params> params(tabs::Remove::Params::Create(*args_)); 1615 EXTENSION_FUNCTION_VALIDATE(params.get()); 1616 1617 if (params->tab_ids.as_integers) { 1618 std::vector<int>& tab_ids = *params->tab_ids.as_integers; 1619 for (size_t i = 0; i < tab_ids.size(); ++i) { 1620 if (!RemoveTab(tab_ids[i])) 1621 return false; 1622 } 1623 } else { 1624 EXTENSION_FUNCTION_VALIDATE(params->tab_ids.as_integer); 1625 if (!RemoveTab(*params->tab_ids.as_integer.get())) 1626 return false; 1627 } 1628 return true; 1629} 1630 1631bool TabsRemoveFunction::RemoveTab(int tab_id) { 1632 Browser* browser = NULL; 1633 WebContents* contents = NULL; 1634 if (!GetTabById(tab_id, 1635 GetProfile(), 1636 include_incognito(), 1637 &browser, 1638 NULL, 1639 &contents, 1640 NULL, 1641 &error_)) { 1642 return false; 1643 } 1644 1645 // Don't let the extension remove a tab if the user is dragging tabs around. 1646 if (!browser->window()->IsTabStripEditable()) { 1647 error_ = keys::kTabStripNotEditableError; 1648 return false; 1649 } 1650 // There's a chance that the tab is being dragged, or we're in some other 1651 // nested event loop. This code path ensures that the tab is safely closed 1652 // under such circumstances, whereas |TabStripModel::CloseWebContentsAt()| 1653 // does not. 1654 contents->Close(); 1655 return true; 1656} 1657 1658bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() { 1659 PrefService* service = GetProfile()->GetPrefs(); 1660 if (service->GetBoolean(prefs::kDisableScreenshots)) { 1661 error_ = keys::kScreenshotsDisabled; 1662 return false; 1663 } 1664 return true; 1665} 1666 1667WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(int window_id) { 1668 Browser* browser = NULL; 1669 if (!GetBrowserFromWindowID(this, window_id, &browser)) 1670 return NULL; 1671 1672 WebContents* contents = browser->tab_strip_model()->GetActiveWebContents(); 1673 if (!contents) { 1674 error_ = keys::kInternalVisibleTabCaptureError; 1675 return NULL; 1676 } 1677 1678 if (!PermissionsData::CanCaptureVisiblePage(GetExtension(), 1679 SessionID::IdForTab(contents), 1680 &error_)) { 1681 return NULL; 1682 } 1683 return contents; 1684} 1685 1686void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) { 1687 error_ = keys::kInternalVisibleTabCaptureError; 1688 SendResponse(false); 1689} 1690 1691void TabsCaptureVisibleTabFunction::RegisterProfilePrefs( 1692 user_prefs::PrefRegistrySyncable* registry) { 1693 registry->RegisterBooleanPref( 1694 prefs::kDisableScreenshots, 1695 false, 1696 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 1697} 1698 1699bool TabsDetectLanguageFunction::RunImpl() { 1700 scoped_ptr<tabs::DetectLanguage::Params> params( 1701 tabs::DetectLanguage::Params::Create(*args_)); 1702 EXTENSION_FUNCTION_VALIDATE(params.get()); 1703 1704 int tab_id = 0; 1705 Browser* browser = NULL; 1706 WebContents* contents = NULL; 1707 1708 // If |tab_id| is specified, look for it. Otherwise default to selected tab 1709 // in the current window. 1710 if (params->tab_id.get()) { 1711 tab_id = *params->tab_id; 1712 if (!GetTabById(tab_id, 1713 GetProfile(), 1714 include_incognito(), 1715 &browser, 1716 NULL, 1717 &contents, 1718 NULL, 1719 &error_)) { 1720 return false; 1721 } 1722 if (!browser || !contents) 1723 return false; 1724 } else { 1725 browser = GetCurrentBrowser(); 1726 if (!browser) 1727 return false; 1728 contents = browser->tab_strip_model()->GetActiveWebContents(); 1729 if (!contents) 1730 return false; 1731 } 1732 1733 if (contents->GetController().NeedsReload()) { 1734 // If the tab hasn't been loaded, don't wait for the tab to load. 1735 error_ = keys::kCannotDetermineLanguageOfUnloadedTab; 1736 return false; 1737 } 1738 1739 AddRef(); // Balanced in GotLanguage(). 1740 1741 TranslateTabHelper* translate_tab_helper = 1742 TranslateTabHelper::FromWebContents(contents); 1743 if (!translate_tab_helper->GetLanguageState().original_language().empty()) { 1744 // Delay the callback invocation until after the current JS call has 1745 // returned. 1746 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( 1747 &TabsDetectLanguageFunction::GotLanguage, this, 1748 translate_tab_helper->GetLanguageState().original_language())); 1749 return true; 1750 } 1751 // The tab contents does not know its language yet. Let's wait until it 1752 // receives it, or until the tab is closed/navigates to some other page. 1753 registrar_.Add(this, chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, 1754 content::Source<WebContents>(contents)); 1755 registrar_.Add( 1756 this, chrome::NOTIFICATION_TAB_CLOSING, 1757 content::Source<NavigationController>(&(contents->GetController()))); 1758 registrar_.Add( 1759 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 1760 content::Source<NavigationController>(&(contents->GetController()))); 1761 return true; 1762} 1763 1764void TabsDetectLanguageFunction::Observe( 1765 int type, 1766 const content::NotificationSource& source, 1767 const content::NotificationDetails& details) { 1768 std::string language; 1769 if (type == chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED) { 1770 const LanguageDetectionDetails* lang_det_details = 1771 content::Details<const LanguageDetectionDetails>(details).ptr(); 1772 language = lang_det_details->adopted_language; 1773 } 1774 1775 registrar_.RemoveAll(); 1776 1777 // Call GotLanguage in all cases as we want to guarantee the callback is 1778 // called for every API call the extension made. 1779 GotLanguage(language); 1780} 1781 1782void TabsDetectLanguageFunction::GotLanguage(const std::string& language) { 1783 SetResult(new base::StringValue(language.c_str())); 1784 SendResponse(true); 1785 1786 Release(); // Balanced in Run() 1787} 1788 1789ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() 1790 : execute_tab_id_(-1) { 1791} 1792 1793ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} 1794 1795bool ExecuteCodeInTabFunction::HasPermission() { 1796 if (Init() && PermissionsData::HasAPIPermissionForTab( 1797 extension_.get(), execute_tab_id_, APIPermission::kTab)) { 1798 return true; 1799 } 1800 return ExtensionFunction::HasPermission(); 1801} 1802 1803bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage() { 1804 content::WebContents* contents = NULL; 1805 1806 // If |tab_id| is specified, look for the tab. Otherwise default to selected 1807 // tab in the current window. 1808 CHECK_GE(execute_tab_id_, 0); 1809 if (!GetTabById(execute_tab_id_, 1810 GetProfile(), 1811 include_incognito(), 1812 NULL, 1813 NULL, 1814 &contents, 1815 NULL, 1816 &error_)) { 1817 return false; 1818 } 1819 1820 CHECK(contents); 1821 1822 // NOTE: This can give the wrong answer due to race conditions, but it is OK, 1823 // we check again in the renderer. 1824 content::RenderProcessHost* process = contents->GetRenderProcessHost(); 1825 if (!PermissionsData::CanExecuteScriptOnPage( 1826 GetExtension(), 1827 contents->GetURL(), 1828 contents->GetURL(), 1829 execute_tab_id_, 1830 NULL, 1831 process ? process->GetID() : -1, 1832 &error_)) { 1833 return false; 1834 } 1835 1836 return true; 1837} 1838 1839ScriptExecutor* ExecuteCodeInTabFunction::GetScriptExecutor() { 1840 Browser* browser = NULL; 1841 content::WebContents* contents = NULL; 1842 1843 bool success = GetTabById(execute_tab_id_, 1844 GetProfile(), 1845 include_incognito(), 1846 &browser, 1847 NULL, 1848 &contents, 1849 NULL, 1850 &error_) && 1851 contents && browser; 1852 1853 if (!success) 1854 return NULL; 1855 1856 return TabHelper::FromWebContents(contents)->script_executor(); 1857} 1858 1859bool ExecuteCodeInTabFunction::IsWebView() const { 1860 return false; 1861} 1862 1863bool TabsExecuteScriptFunction::ShouldInsertCSS() const { 1864 return false; 1865} 1866 1867void TabsExecuteScriptFunction::OnExecuteCodeFinished( 1868 const std::string& error, 1869 int32 on_page_id, 1870 const GURL& on_url, 1871 const base::ListValue& result) { 1872 if (error.empty()) 1873 SetResult(result.DeepCopy()); 1874 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url, 1875 result); 1876} 1877 1878bool ExecuteCodeInTabFunction::Init() { 1879 if (details_.get()) 1880 return true; 1881 1882 // |tab_id| is optional so it's ok if it's not there. 1883 int tab_id = -1; 1884 if (args_->GetInteger(0, &tab_id)) 1885 EXTENSION_FUNCTION_VALIDATE(tab_id >= 0); 1886 1887 // |details| are not optional. 1888 base::DictionaryValue* details_value = NULL; 1889 if (!args_->GetDictionary(1, &details_value)) 1890 return false; 1891 scoped_ptr<InjectDetails> details(new InjectDetails()); 1892 if (!InjectDetails::Populate(*details_value, details.get())) 1893 return false; 1894 1895 // If the tab ID wasn't given then it needs to be converted to the 1896 // currently active tab's ID. 1897 if (tab_id == -1) { 1898 Browser* browser = GetCurrentBrowser(); 1899 if (!browser) 1900 return false; 1901 content::WebContents* web_contents = NULL; 1902 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) 1903 return false; 1904 } 1905 1906 execute_tab_id_ = tab_id; 1907 details_ = details.Pass(); 1908 return true; 1909} 1910 1911bool TabsInsertCSSFunction::ShouldInsertCSS() const { 1912 return true; 1913} 1914 1915} // namespace extensions 1916