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