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