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
3 // found in the LICENSE file.
5 #include "components/plugins/renderer/loadable_plugin_placeholder.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/json/string_escape.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "content/public/child/v8_value_converter.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "content/public/renderer/render_thread.h"
18 #include "gin/handle.h"
19 #include "gin/object_template_builder.h"
20 #include "third_party/WebKit/public/web/WebDOMMessageEvent.h"
21 #include "third_party/WebKit/public/web/WebDocument.h"
22 #include "third_party/WebKit/public/web/WebElement.h"
23 #include "third_party/WebKit/public/web/WebInputEvent.h"
24 #include "third_party/WebKit/public/web/WebKit.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebPluginContainer.h"
27 #include "third_party/WebKit/public/web/WebScriptSource.h"
28 #include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
29 #include "third_party/WebKit/public/web/WebView.h"
31 using base::UserMetricsAction
;
32 using content::PluginInstanceThrottler
;
33 using content::RenderThread
;
37 void LoadablePluginPlaceholder::BlockForPowerSaverPoster() {
38 DCHECK(!is_blocked_for_power_saver_poster_
);
39 is_blocked_for_power_saver_poster_
= true;
41 render_frame()->RegisterPeripheralPlugin(
42 GURL(GetPluginParams().url
).GetOrigin(),
43 base::Bind(&LoadablePluginPlaceholder::MarkPluginEssential
,
44 weak_factory_
.GetWeakPtr(),
45 PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_WHITELIST
));
48 void LoadablePluginPlaceholder::SetPremadePlugin(
49 content::PluginInstanceThrottler
* throttler
) {
51 DCHECK(!premade_throttler_
);
52 premade_throttler_
= throttler
;
55 LoadablePluginPlaceholder::LoadablePluginPlaceholder(
56 content::RenderFrame
* render_frame
,
57 blink::WebLocalFrame
* frame
,
58 const blink::WebPluginParams
& params
,
59 const std::string
& html_data
)
60 : PluginPlaceholder(render_frame
, frame
, params
, html_data
),
61 is_blocked_for_background_tab_(false),
62 is_blocked_for_prerendering_(false),
63 is_blocked_for_power_saver_poster_(false),
64 power_saver_enabled_(false),
65 premade_throttler_(nullptr),
67 finished_loading_(false),
71 LoadablePluginPlaceholder::~LoadablePluginPlaceholder() {
74 void LoadablePluginPlaceholder::MarkPluginEssential(
75 PluginInstanceThrottler::PowerSaverUnthrottleMethod method
) {
76 if (!power_saver_enabled_
)
79 power_saver_enabled_
= false;
81 if (premade_throttler_
)
82 premade_throttler_
->MarkPluginEssential(method
);
84 PluginInstanceThrottler::RecordUnthrottleMethodMetric(method
);
86 if (is_blocked_for_power_saver_poster_
) {
87 is_blocked_for_power_saver_poster_
= false;
88 if (!LoadingBlocked())
93 gin::ObjectTemplateBuilder
LoadablePluginPlaceholder::GetObjectTemplateBuilder(
94 v8::Isolate
* isolate
) {
95 return PluginPlaceholder::GetObjectTemplateBuilder(isolate
)
96 .SetMethod("load", &LoadablePluginPlaceholder::LoadCallback
)
97 .SetMethod("didFinishLoading",
98 &LoadablePluginPlaceholder::DidFinishLoadingCallback
);
101 void LoadablePluginPlaceholder::ReplacePlugin(blink::WebPlugin
* new_plugin
) {
105 blink::WebPluginContainer
* container
= plugin()->container();
106 // Set the new plugin on the container before initializing it.
107 container
->setPlugin(new_plugin
);
108 // Save the element in case the plugin is removed from the page during
110 blink::WebElement element
= container
->element();
111 bool plugin_needs_initialization
=
112 !premade_throttler_
|| new_plugin
!= premade_throttler_
->GetWebPlugin();
113 if (plugin_needs_initialization
&& !new_plugin
->initialize(container
)) {
114 // We couldn't initialize the new plugin. Restore the old one and abort.
115 container
->setPlugin(plugin());
119 // The plugin has been removed from the page. Destroy the old plugin. We
120 // will be destroyed as soon as V8 garbage collects us.
121 if (!element
.pluginContainer()) {
126 // During initialization, the new plugin might have replaced itself in turn
127 // with another plugin. Make sure not to use the passed in |new_plugin| after
129 new_plugin
= container
->plugin();
131 plugin()->RestoreTitleText();
132 container
->invalidate();
133 container
->reportGeometry();
134 plugin()->ReplayReceivedData(new_plugin
);
138 void LoadablePluginPlaceholder::SetMessage(const base::string16
& message
) {
140 if (finished_loading_
)
144 void LoadablePluginPlaceholder::UpdateMessage() {
148 "window.setMessage(" + base::GetQuotedJSONString(message_
) + ")";
149 plugin()->web_view()->mainFrame()->executeScript(
150 blink::WebScriptSource(base::UTF8ToUTF16(script
)));
153 void LoadablePluginPlaceholder::PluginDestroyed() {
154 if (power_saver_enabled_
) {
155 if (premade_throttler_
) {
156 // Since the premade plugin has been detached from the container, it will
157 // not be automatically destroyed along with the page.
158 premade_throttler_
->GetWebPlugin()->destroy();
159 premade_throttler_
= nullptr;
160 } else if (is_blocked_for_power_saver_poster_
) {
161 // Record the NEVER unthrottle count only if there is no throttler.
162 PluginInstanceThrottler::RecordUnthrottleMethodMetric(
163 PluginInstanceThrottler::UNTHROTTLE_METHOD_NEVER
);
166 // Prevent processing subsequent calls to MarkPluginEssential.
167 power_saver_enabled_
= false;
170 PluginPlaceholder::PluginDestroyed();
173 v8::Local
<v8::Object
> LoadablePluginPlaceholder::GetV8ScriptableObject(
174 v8::Isolate
* isolate
) const {
175 // Pass through JavaScript access to the underlying throttled plugin.
176 if (premade_throttler_
&& premade_throttler_
->GetWebPlugin()) {
177 return premade_throttler_
->GetWebPlugin()->v8ScriptableObject(isolate
);
179 return v8::Local
<v8::Object
>();
182 void LoadablePluginPlaceholder::WasShown() {
183 if (is_blocked_for_background_tab_
) {
184 is_blocked_for_background_tab_
= false;
185 if (!LoadingBlocked())
190 void LoadablePluginPlaceholder::OnLoadBlockedPlugins(
191 const std::string
& identifier
) {
192 if (!identifier
.empty() && identifier
!= identifier_
)
195 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_UI"));
199 void LoadablePluginPlaceholder::OnSetIsPrerendering(bool is_prerendering
) {
200 // Prerendering can only be enabled prior to a RenderView's first navigation,
201 // so no BlockedPlugin should see the notification that enables prerendering.
202 DCHECK(!is_prerendering
);
203 if (is_blocked_for_prerendering_
) {
204 is_blocked_for_prerendering_
= false;
205 if (!LoadingBlocked())
210 void LoadablePluginPlaceholder::LoadPlugin() {
211 // This is not strictly necessary but is an important defense in case the
212 // event propagation changes between "close" vs. "click-to-play".
217 if (!allow_loading_
) {
222 if (premade_throttler_
) {
223 premade_throttler_
->SetHiddenForPlaceholder(false /* hidden */);
224 ReplacePlugin(premade_throttler_
->GetWebPlugin());
225 premade_throttler_
= nullptr;
227 ReplacePlugin(CreatePlugin());
231 void LoadablePluginPlaceholder::LoadCallback() {
232 RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Click"));
233 // If the user specifically clicks on the plugin content's placeholder,
234 // disable power saver throttling for this instance.
235 MarkPluginEssential(PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK
);
239 void LoadablePluginPlaceholder::DidFinishLoadingCallback() {
240 finished_loading_
= true;
241 if (message_
.length() > 0)
244 // Wait for the placeholder to finish loading to hide the premade plugin.
245 // This is necessary to prevent a flicker.
246 if (premade_throttler_
&& power_saver_enabled_
)
247 premade_throttler_
->SetHiddenForPlaceholder(true /* hidden */);
249 // Set an attribute and post an event, so browser tests can wait for the
250 // placeholder to be ready to receive simulated user input.
251 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
252 switches::kEnablePluginPlaceholderTesting
)) {
253 blink::WebElement element
= plugin()->container()->element();
254 element
.setAttribute("placeholderLoaded", "true");
256 scoped_ptr
<content::V8ValueConverter
> converter(
257 content::V8ValueConverter::create());
258 base::StringValue
value("placeholderLoaded");
259 blink::WebSerializedScriptValue message_data
=
260 blink::WebSerializedScriptValue::serialize(converter
->ToV8Value(
261 &value
, element
.document().frame()->mainWorldScriptContext()));
263 blink::WebDOMEvent event
= element
.document().createEvent("MessageEvent");
264 blink::WebDOMMessageEvent msg_event
= event
.to
<blink::WebDOMMessageEvent
>();
265 msg_event
.initMessageEvent("message", // type
268 message_data
, // data
272 element
.dispatchEvent(msg_event
);
276 void LoadablePluginPlaceholder::SetPluginInfo(
277 const content::WebPluginInfo
& plugin_info
) {
278 plugin_info_
= plugin_info
;
281 const content::WebPluginInfo
& LoadablePluginPlaceholder::GetPluginInfo() const {
285 void LoadablePluginPlaceholder::SetIdentifier(const std::string
& identifier
) {
286 identifier_
= identifier
;
289 const std::string
& LoadablePluginPlaceholder::GetIdentifier() const {
293 bool LoadablePluginPlaceholder::LoadingBlocked() const {
294 DCHECK(allow_loading_
);
295 return is_blocked_for_background_tab_
|| is_blocked_for_power_saver_poster_
||
296 is_blocked_for_prerendering_
;
299 } // namespace plugins