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 |