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