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