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