| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/plugins/renderer/loadable_plugin_placeholder.h" | 5 #include "components/plugins/renderer/loadable_plugin_placeholder.h" |
| 6 | 6 |
| 7 #include "base/auto_reset.h" |
| 7 #include "base/bind.h" | 8 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 9 #include "base/bind_helpers.h" |
| 9 #include "base/json/string_escape.h" | 10 #include "base/json/string_escape.h" |
| 10 #include "base/strings/string_piece.h" | 11 #include "base/strings/string_piece.h" |
| 11 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/thread_task_runner_handle.h" | 14 #include "base/thread_task_runner_handle.h" |
| 14 #include "base/values.h" | 15 #include "base/values.h" |
| 15 #include "content/public/child/v8_value_converter.h" | 16 #include "content/public/child/v8_value_converter.h" |
| 16 #include "content/public/renderer/render_frame.h" | 17 #include "content/public/renderer/render_frame.h" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 27 #include "third_party/WebKit/public/web/WebView.h" | 28 #include "third_party/WebKit/public/web/WebView.h" |
| 28 #include "url/gurl.h" | 29 #include "url/gurl.h" |
| 29 #include "url/origin.h" | 30 #include "url/origin.h" |
| 30 | 31 |
| 31 using base::UserMetricsAction; | 32 using base::UserMetricsAction; |
| 32 using content::PluginInstanceThrottler; | 33 using content::PluginInstanceThrottler; |
| 33 using content::RenderThread; | 34 using content::RenderThread; |
| 34 | 35 |
| 35 namespace plugins { | 36 namespace plugins { |
| 36 | 37 |
| 38 // TODO(tommycli): After a size update, re-check the size after this delay, as |
| 39 // Blink can report incorrect sizes to plugins while the compositing state is |
| 40 // dirty. Chosen because it seems to work. |
| 41 const int kSizeChangeRecheckDelayMilliseconds = 100; |
| 42 |
| 37 void LoadablePluginPlaceholder::BlockForPowerSaverPoster() { | 43 void LoadablePluginPlaceholder::BlockForPowerSaverPoster() { |
| 38 DCHECK(!is_blocked_for_power_saver_poster_); | 44 DCHECK(!is_blocked_for_power_saver_poster_); |
| 39 is_blocked_for_power_saver_poster_ = true; | 45 is_blocked_for_power_saver_poster_ = true; |
| 40 | 46 |
| 41 render_frame()->RegisterPeripheralPlugin( | 47 render_frame()->RegisterPeripheralPlugin( |
| 42 url::Origin(GURL(GetPluginParams().url)), | 48 url::Origin(GURL(GetPluginParams().url)), |
| 43 base::Bind(&LoadablePluginPlaceholder::MarkPluginEssential, | 49 base::Bind(&LoadablePluginPlaceholder::MarkPluginEssential, |
| 44 weak_factory_.GetWeakPtr(), | 50 weak_factory_.GetWeakPtr(), |
| 45 PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_WHITELIST)); | 51 PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_WHITELIST)); |
| 46 } | 52 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 58 const blink::WebPluginParams& params, | 64 const blink::WebPluginParams& params, |
| 59 const std::string& html_data) | 65 const std::string& html_data) |
| 60 : PluginPlaceholderBase(render_frame, frame, params, html_data), | 66 : PluginPlaceholderBase(render_frame, frame, params, html_data), |
| 61 is_blocked_for_background_tab_(false), | 67 is_blocked_for_background_tab_(false), |
| 62 is_blocked_for_prerendering_(false), | 68 is_blocked_for_prerendering_(false), |
| 63 is_blocked_for_power_saver_poster_(false), | 69 is_blocked_for_power_saver_poster_(false), |
| 64 power_saver_enabled_(false), | 70 power_saver_enabled_(false), |
| 65 premade_throttler_(nullptr), | 71 premade_throttler_(nullptr), |
| 66 allow_loading_(false), | 72 allow_loading_(false), |
| 67 finished_loading_(false), | 73 finished_loading_(false), |
| 74 in_size_recheck_(false), |
| 68 heuristic_run_before_(premade_throttler_ != nullptr), | 75 heuristic_run_before_(premade_throttler_ != nullptr), |
| 69 weak_factory_(this) {} | 76 weak_factory_(this) {} |
| 70 | 77 |
| 71 LoadablePluginPlaceholder::~LoadablePluginPlaceholder() { | 78 LoadablePluginPlaceholder::~LoadablePluginPlaceholder() { |
| 72 } | 79 } |
| 73 | 80 |
| 74 void LoadablePluginPlaceholder::MarkPluginEssential( | 81 void LoadablePluginPlaceholder::MarkPluginEssential( |
| 75 PluginInstanceThrottler::PowerSaverUnthrottleMethod method) { | 82 PluginInstanceThrottler::PowerSaverUnthrottleMethod method) { |
| 76 if (!power_saver_enabled_) | 83 if (!power_saver_enabled_) |
| 77 return; | 84 return; |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 // Pass through JavaScript access to the underlying throttled plugin. | 183 // Pass through JavaScript access to the underlying throttled plugin. |
| 177 if (premade_throttler_ && premade_throttler_->GetWebPlugin()) { | 184 if (premade_throttler_ && premade_throttler_->GetWebPlugin()) { |
| 178 return premade_throttler_->GetWebPlugin()->v8ScriptableObject(isolate); | 185 return premade_throttler_->GetWebPlugin()->v8ScriptableObject(isolate); |
| 179 } | 186 } |
| 180 return v8::Local<v8::Object>(); | 187 return v8::Local<v8::Object>(); |
| 181 } | 188 } |
| 182 | 189 |
| 183 void LoadablePluginPlaceholder::OnUnobscuredRectUpdate( | 190 void LoadablePluginPlaceholder::OnUnobscuredRectUpdate( |
| 184 const gfx::Rect& unobscured_rect) { | 191 const gfx::Rect& unobscured_rect) { |
| 185 DCHECK(content::RenderThread::Get()); | 192 DCHECK(content::RenderThread::Get()); |
| 186 if (!plugin() || !power_saver_enabled_ || !finished_loading_) | 193 if (!power_saver_enabled_ || !finished_loading_) |
| 187 return; | 194 return; |
| 188 | 195 |
| 189 if (unobscured_rect_ == unobscured_rect) | 196 // Only update the unobscured rect during the recheck phase. Also early exit |
| 197 // to prevent reentrancy issues. |
| 198 if (in_size_recheck_) { |
| 199 unobscured_rect_ = unobscured_rect; |
| 200 return; |
| 201 } |
| 202 |
| 203 if (!size_update_timer_.IsRunning()) { |
| 204 // TODO(tommycli): We have to post a delayed task to recheck the size, as |
| 205 // Blink can report wrong sizes for partially obscured plugins while the |
| 206 // compositing state is dirty. https://crbug.com/343769 |
| 207 size_update_timer_.Start( |
| 208 FROM_HERE, |
| 209 base::TimeDelta::FromMilliseconds(kSizeChangeRecheckDelayMilliseconds), |
| 210 base::Bind(&LoadablePluginPlaceholder::RecheckSizeAndMaybeUnthrottle, |
| 211 weak_factory_.GetWeakPtr())); |
| 212 } |
| 213 } |
| 214 |
| 215 void LoadablePluginPlaceholder::WasShown() { |
| 216 if (is_blocked_for_background_tab_) { |
| 217 is_blocked_for_background_tab_ = false; |
| 218 if (!LoadingBlocked()) |
| 219 LoadPlugin(); |
| 220 } |
| 221 } |
| 222 |
| 223 void LoadablePluginPlaceholder::OnLoadBlockedPlugins( |
| 224 const std::string& identifier) { |
| 225 if (!identifier.empty() && identifier != identifier_) |
| 190 return; | 226 return; |
| 191 | 227 |
| 192 unobscured_rect_ = unobscured_rect; | 228 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_UI")); |
| 229 LoadPlugin(); |
| 230 } |
| 231 |
| 232 void LoadablePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { |
| 233 // Prerendering can only be enabled prior to a RenderView's first navigation, |
| 234 // so no BlockedPlugin should see the notification that enables prerendering. |
| 235 DCHECK(!is_prerendering); |
| 236 if (is_blocked_for_prerendering_) { |
| 237 is_blocked_for_prerendering_ = false; |
| 238 if (!LoadingBlocked()) |
| 239 LoadPlugin(); |
| 240 } |
| 241 } |
| 242 |
| 243 void LoadablePluginPlaceholder::LoadPlugin() { |
| 244 // This is not strictly necessary but is an important defense in case the |
| 245 // event propagation changes between "close" vs. "click-to-play". |
| 246 if (hidden()) |
| 247 return; |
| 248 if (!plugin()) |
| 249 return; |
| 250 if (!allow_loading_) { |
| 251 NOTREACHED(); |
| 252 return; |
| 253 } |
| 254 |
| 255 if (premade_throttler_) { |
| 256 premade_throttler_->SetHiddenForPlaceholder(false /* hidden */); |
| 257 ReplacePlugin(premade_throttler_->GetWebPlugin()); |
| 258 premade_throttler_ = nullptr; |
| 259 } else { |
| 260 ReplacePlugin(CreatePlugin()); |
| 261 } |
| 262 } |
| 263 |
| 264 void LoadablePluginPlaceholder::LoadCallback() { |
| 265 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Click")); |
| 266 // If the user specifically clicks on the plugin content's placeholder, |
| 267 // disable power saver throttling for this instance. |
| 268 MarkPluginEssential(PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK); |
| 269 LoadPlugin(); |
| 270 } |
| 271 |
| 272 void LoadablePluginPlaceholder::DidFinishLoadingCallback() { |
| 273 finished_loading_ = true; |
| 274 if (message_.length() > 0) |
| 275 UpdateMessage(); |
| 276 |
| 277 // Wait for the placeholder to finish loading to hide the premade plugin. |
| 278 // This is necessary to prevent a flicker. |
| 279 if (premade_throttler_ && power_saver_enabled_) |
| 280 premade_throttler_->SetHiddenForPlaceholder(true /* hidden */); |
| 281 |
| 282 // In case our initial geometry was reported before the placeholder finished |
| 283 // loading, request another one. Needed for correct large poster unthrottling. |
| 284 if (plugin()) { |
| 285 CHECK(plugin()->container()); |
| 286 plugin()->container()->reportGeometry(); |
| 287 } |
| 288 } |
| 289 |
| 290 void LoadablePluginPlaceholder::DidFinishIconRepositionForTestingCallback() { |
| 291 // Set an attribute and post an event, so browser tests can wait for the |
| 292 // placeholder to be ready to receive simulated user input. |
| 293 blink::WebElement element = plugin()->container()->element(); |
| 294 element.setAttribute("placeholderReady", "true"); |
| 295 |
| 296 scoped_ptr<content::V8ValueConverter> converter( |
| 297 content::V8ValueConverter::create()); |
| 298 base::StringValue value("placeholderReady"); |
| 299 blink::WebSerializedScriptValue message_data = |
| 300 blink::WebSerializedScriptValue::serialize(converter->ToV8Value( |
| 301 &value, element.document().frame()->mainWorldScriptContext())); |
| 302 blink::WebDOMMessageEvent msg_event(message_data); |
| 303 |
| 304 element.dispatchEvent(msg_event); |
| 305 } |
| 306 |
| 307 void LoadablePluginPlaceholder::SetPluginInfo( |
| 308 const content::WebPluginInfo& plugin_info) { |
| 309 plugin_info_ = plugin_info; |
| 310 } |
| 311 |
| 312 const content::WebPluginInfo& LoadablePluginPlaceholder::GetPluginInfo() const { |
| 313 return plugin_info_; |
| 314 } |
| 315 |
| 316 void LoadablePluginPlaceholder::SetIdentifier(const std::string& identifier) { |
| 317 identifier_ = identifier; |
| 318 } |
| 319 |
| 320 const std::string& LoadablePluginPlaceholder::GetIdentifier() const { |
| 321 return identifier_; |
| 322 } |
| 323 |
| 324 bool LoadablePluginPlaceholder::LoadingBlocked() const { |
| 325 DCHECK(allow_loading_); |
| 326 return is_blocked_for_background_tab_ || is_blocked_for_power_saver_poster_ || |
| 327 is_blocked_for_prerendering_; |
| 328 } |
| 329 |
| 330 void LoadablePluginPlaceholder::RecheckSizeAndMaybeUnthrottle() { |
| 331 DCHECK(content::RenderThread::Get()); |
| 332 DCHECK(!in_size_recheck_); |
| 333 DCHECK(finished_loading_); |
| 334 |
| 335 base::AutoReset<bool> recheck_scope(&in_size_recheck_, true); |
| 336 |
| 337 if (!plugin()) |
| 338 return; |
| 339 |
| 340 gfx::Rect old_rect = unobscured_rect_; |
| 341 |
| 342 // Re-check the size in case the reported size was incorrect. |
| 343 plugin()->container()->reportGeometry(); |
| 344 |
| 345 if (old_rect == unobscured_rect_) |
| 346 return; |
| 193 | 347 |
| 194 float zoom_factor = plugin()->container()->pageZoomFactor(); | 348 float zoom_factor = plugin()->container()->pageZoomFactor(); |
| 195 int width = roundf(unobscured_rect_.width() / zoom_factor); | 349 int width = roundf(unobscured_rect_.width() / zoom_factor); |
| 196 int height = roundf(unobscured_rect_.height() / zoom_factor); | 350 int height = roundf(unobscured_rect_.height() / zoom_factor); |
| 197 | 351 |
| 198 if (is_blocked_for_power_saver_poster_) { | 352 if (is_blocked_for_power_saver_poster_) { |
| 199 // Adjust poster container padding and dimensions to center play button for | 353 // Adjust poster container padding and dimensions to center play button for |
| 200 // plugins and plugin posters that have their top or left portions obscured. | 354 // plugins and plugin posters that have their top or left portions obscured. |
| 201 int x = roundf(unobscured_rect_.x() / zoom_factor); | 355 int x = roundf(unobscured_rect_.x() / zoom_factor); |
| 202 int y = roundf(unobscured_rect_.y() / zoom_factor); | 356 int y = roundf(unobscured_rect_.y() / zoom_factor); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 221 status == | 375 status == |
| 222 content::RenderFrame::CONTENT_STATUS_ESSENTIAL_CROSS_ORIGIN_BIG) { | 376 content::RenderFrame::CONTENT_STATUS_ESSENTIAL_CROSS_ORIGIN_BIG) { |
| 223 render_frame()->WhitelistContentOrigin(content_origin); | 377 render_frame()->WhitelistContentOrigin(content_origin); |
| 224 } | 378 } |
| 225 } | 379 } |
| 226 | 380 |
| 227 heuristic_run_before_ = true; | 381 heuristic_run_before_ = true; |
| 228 } | 382 } |
| 229 } | 383 } |
| 230 | 384 |
| 231 void LoadablePluginPlaceholder::WasShown() { | |
| 232 if (is_blocked_for_background_tab_) { | |
| 233 is_blocked_for_background_tab_ = false; | |
| 234 if (!LoadingBlocked()) | |
| 235 LoadPlugin(); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void LoadablePluginPlaceholder::OnLoadBlockedPlugins( | |
| 240 const std::string& identifier) { | |
| 241 if (!identifier.empty() && identifier != identifier_) | |
| 242 return; | |
| 243 | |
| 244 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_UI")); | |
| 245 LoadPlugin(); | |
| 246 } | |
| 247 | |
| 248 void LoadablePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { | |
| 249 // Prerendering can only be enabled prior to a RenderView's first navigation, | |
| 250 // so no BlockedPlugin should see the notification that enables prerendering. | |
| 251 DCHECK(!is_prerendering); | |
| 252 if (is_blocked_for_prerendering_) { | |
| 253 is_blocked_for_prerendering_ = false; | |
| 254 if (!LoadingBlocked()) | |
| 255 LoadPlugin(); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 void LoadablePluginPlaceholder::LoadPlugin() { | |
| 260 // This is not strictly necessary but is an important defense in case the | |
| 261 // event propagation changes between "close" vs. "click-to-play". | |
| 262 if (hidden()) | |
| 263 return; | |
| 264 if (!plugin()) | |
| 265 return; | |
| 266 if (!allow_loading_) { | |
| 267 NOTREACHED(); | |
| 268 return; | |
| 269 } | |
| 270 | |
| 271 if (premade_throttler_) { | |
| 272 premade_throttler_->SetHiddenForPlaceholder(false /* hidden */); | |
| 273 ReplacePlugin(premade_throttler_->GetWebPlugin()); | |
| 274 premade_throttler_ = nullptr; | |
| 275 } else { | |
| 276 ReplacePlugin(CreatePlugin()); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void LoadablePluginPlaceholder::LoadCallback() { | |
| 281 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Click")); | |
| 282 // If the user specifically clicks on the plugin content's placeholder, | |
| 283 // disable power saver throttling for this instance. | |
| 284 MarkPluginEssential(PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK); | |
| 285 LoadPlugin(); | |
| 286 } | |
| 287 | |
| 288 void LoadablePluginPlaceholder::DidFinishLoadingCallback() { | |
| 289 finished_loading_ = true; | |
| 290 if (message_.length() > 0) | |
| 291 UpdateMessage(); | |
| 292 | |
| 293 // Wait for the placeholder to finish loading to hide the premade plugin. | |
| 294 // This is necessary to prevent a flicker. | |
| 295 if (premade_throttler_ && power_saver_enabled_) | |
| 296 premade_throttler_->SetHiddenForPlaceholder(true /* hidden */); | |
| 297 | |
| 298 // In case our initial geometry was reported before the placeholder finished | |
| 299 // loading, request another one. Needed for correct large poster unthrottling. | |
| 300 if (plugin()) { | |
| 301 CHECK(plugin()->container()); | |
| 302 plugin()->container()->reportGeometry(); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 void LoadablePluginPlaceholder::DidFinishIconRepositionForTestingCallback() { | |
| 307 // Set an attribute and post an event, so browser tests can wait for the | |
| 308 // placeholder to be ready to receive simulated user input. | |
| 309 blink::WebElement element = plugin()->container()->element(); | |
| 310 element.setAttribute("placeholderReady", "true"); | |
| 311 | |
| 312 scoped_ptr<content::V8ValueConverter> converter( | |
| 313 content::V8ValueConverter::create()); | |
| 314 base::StringValue value("placeholderReady"); | |
| 315 blink::WebSerializedScriptValue message_data = | |
| 316 blink::WebSerializedScriptValue::serialize(converter->ToV8Value( | |
| 317 &value, element.document().frame()->mainWorldScriptContext())); | |
| 318 blink::WebDOMMessageEvent msg_event(message_data); | |
| 319 | |
| 320 element.dispatchEvent(msg_event); | |
| 321 } | |
| 322 | |
| 323 void LoadablePluginPlaceholder::SetPluginInfo( | |
| 324 const content::WebPluginInfo& plugin_info) { | |
| 325 plugin_info_ = plugin_info; | |
| 326 } | |
| 327 | |
| 328 const content::WebPluginInfo& LoadablePluginPlaceholder::GetPluginInfo() const { | |
| 329 return plugin_info_; | |
| 330 } | |
| 331 | |
| 332 void LoadablePluginPlaceholder::SetIdentifier(const std::string& identifier) { | |
| 333 identifier_ = identifier; | |
| 334 } | |
| 335 | |
| 336 const std::string& LoadablePluginPlaceholder::GetIdentifier() const { | |
| 337 return identifier_; | |
| 338 } | |
| 339 | |
| 340 bool LoadablePluginPlaceholder::LoadingBlocked() const { | |
| 341 DCHECK(allow_loading_); | |
| 342 return is_blocked_for_background_tab_ || is_blocked_for_power_saver_poster_ || | |
| 343 is_blocked_for_prerendering_; | |
| 344 } | |
| 345 | |
| 346 } // namespace plugins | 385 } // namespace plugins |
| OLD | NEW |