• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "components/plugins/renderer/plugin_placeholder.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/json/string_escape.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "content/public/common/content_constants.h"
15 #include "content/public/common/context_menu_params.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "content/public/renderer/render_thread.h"
18 #include "third_party/WebKit/public/web/WebDocument.h"
19 #include "third_party/WebKit/public/web/WebElement.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebInputEvent.h"
22 #include "third_party/WebKit/public/web/WebPluginContainer.h"
23 #include "third_party/WebKit/public/web/WebScriptSource.h"
24 #include "third_party/WebKit/public/web/WebView.h"
25 #include "third_party/re2/re2/re2.h"
26 
27 using content::RenderThread;
28 using content::UserMetricsAction;
29 using blink::WebElement;
30 using blink::WebFrame;
31 using blink::WebMouseEvent;
32 using blink::WebNode;
33 using blink::WebPlugin;
34 using blink::WebPluginContainer;
35 using blink::WebPluginParams;
36 using blink::WebScriptSource;
37 using blink::WebURLRequest;
38 using webkit_glue::CppArgumentList;
39 using webkit_glue::CppVariant;
40 
41 namespace plugins {
42 
PluginPlaceholder(content::RenderFrame * render_frame,WebFrame * frame,const WebPluginParams & params,const std::string & html_data,GURL placeholderDataUrl)43 PluginPlaceholder::PluginPlaceholder(content::RenderFrame* render_frame,
44                                      WebFrame* frame,
45                                      const WebPluginParams& params,
46                                      const std::string& html_data,
47                                      GURL placeholderDataUrl)
48     : content::RenderFrameObserver(render_frame),
49       frame_(frame),
50       plugin_params_(params),
51       plugin_(WebViewPlugin::Create(this,
52                                     render_frame->GetWebkitPreferences(),
53                                     html_data,
54                                     placeholderDataUrl)),
55       is_blocked_for_prerendering_(false),
56       allow_loading_(false),
57       hidden_(false),
58       finished_loading_(false) {}
59 
~PluginPlaceholder()60 PluginPlaceholder::~PluginPlaceholder() {}
61 
BindWebFrame(WebFrame * frame)62 void PluginPlaceholder::BindWebFrame(WebFrame* frame) {
63   BindToJavascript(frame, "plugin");
64   BindCallback(
65       "load",
66       base::Bind(&PluginPlaceholder::LoadCallback, base::Unretained(this)));
67   BindCallback(
68       "hide",
69       base::Bind(&PluginPlaceholder::HideCallback, base::Unretained(this)));
70   BindCallback("didFinishLoading",
71                base::Bind(&PluginPlaceholder::DidFinishLoadingCallback,
72                           base::Unretained(this)));
73 }
74 
ReplacePlugin(WebPlugin * new_plugin)75 void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) {
76   CHECK(plugin_);
77   if (!new_plugin) return;
78   WebPluginContainer* container = plugin_->container();
79   // Set the new plug-in on the container before initializing it.
80   container->setPlugin(new_plugin);
81   // Save the element in case the plug-in is removed from the page during
82   // initialization.
83   WebElement element = container->element();
84   if (!new_plugin->initialize(container)) {
85     // We couldn't initialize the new plug-in. Restore the old one and abort.
86     container->setPlugin(plugin_);
87     return;
88   }
89 
90   // The plug-in has been removed from the page. Destroy the old plug-in
91   // (which will destroy us).
92   if (!element.pluginContainer()) {
93     plugin_->destroy();
94     return;
95   }
96 
97   // During initialization, the new plug-in might have replaced itself in turn
98   // with another plug-in. Make sure not to use the passed in |new_plugin| after
99   // this point.
100   new_plugin = container->plugin();
101 
102   plugin_->RestoreTitleText();
103   container->invalidate();
104   container->reportGeometry();
105   plugin_->ReplayReceivedData(new_plugin);
106   plugin_->destroy();
107 }
108 
HidePlugin()109 void PluginPlaceholder::HidePlugin() {
110   hidden_ = true;
111   WebPluginContainer* container = plugin_->container();
112   WebElement element = container->element();
113   element.setAttribute("style", "display: none;");
114   // If we have a width and height, search for a parent (often <div>) with the
115   // same dimensions. If we find such a parent, hide that as well.
116   // This makes much more uncovered page content usable (including clickable)
117   // as opposed to merely visible.
118   // TODO(cevans) -- it's a foul heurisitc but we're going to tolerate it for
119   // now for these reasons:
120   // 1) Makes the user experience better.
121   // 2) Foulness is encapsulated within this single function.
122   // 3) Confidence in no fasle positives.
123   // 4) Seems to have a good / low false negative rate at this time.
124   if (element.hasAttribute("width") && element.hasAttribute("height")) {
125     std::string width_str("width:[\\s]*");
126     width_str += element.getAttribute("width").utf8().data();
127     if (EndsWith(width_str, "px", false)) {
128       width_str = width_str.substr(0, width_str.length() - 2);
129     }
130     TrimWhitespace(width_str, TRIM_TRAILING, &width_str);
131     width_str += "[\\s]*px";
132     std::string height_str("height:[\\s]*");
133     height_str += element.getAttribute("height").utf8().data();
134     if (EndsWith(height_str, "px", false)) {
135       height_str = height_str.substr(0, height_str.length() - 2);
136     }
137     TrimWhitespace(height_str, TRIM_TRAILING, &height_str);
138     height_str += "[\\s]*px";
139     WebNode parent = element;
140     while (!parent.parentNode().isNull()) {
141       parent = parent.parentNode();
142       if (!parent.isElementNode())
143         continue;
144       element = parent.toConst<WebElement>();
145       if (element.hasAttribute("style")) {
146         std::string style_str = element.getAttribute("style").utf8();
147         if (RE2::PartialMatch(style_str, width_str) &&
148             RE2::PartialMatch(style_str, height_str))
149           element.setAttribute("style", "display: none;");
150       }
151     }
152   }
153 }
154 
WillDestroyPlugin()155 void PluginPlaceholder::WillDestroyPlugin() { delete this; }
156 
SetMessage(const base::string16 & message)157 void PluginPlaceholder::SetMessage(const base::string16& message) {
158   message_ = message;
159   if (finished_loading_)
160     UpdateMessage();
161 }
162 
UpdateMessage()163 void PluginPlaceholder::UpdateMessage() {
164   std::string script =
165       "window.setMessage(" + base::GetQuotedJSONString(message_) + ")";
166   plugin_->web_view()->mainFrame()->executeScript(
167       WebScriptSource(ASCIIToUTF16(script)));
168 }
169 
ShowContextMenu(const WebMouseEvent & event)170 void PluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) {
171   // Does nothing by default. Will be overridden if a specific browser wants
172   // a context menu.
173   return;
174 }
175 
OnLoadBlockedPlugins(const std::string & identifier)176 void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) {
177   if (!identifier.empty() && identifier != identifier_)
178     return;
179 
180   RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_UI"));
181   LoadPlugin();
182 }
183 
OnSetIsPrerendering(bool is_prerendering)184 void PluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) {
185   // Prerendering can only be enabled prior to a RenderView's first navigation,
186   // so no BlockedPlugin should see the notification that enables prerendering.
187   DCHECK(!is_prerendering);
188   if (is_blocked_for_prerendering_ && !is_prerendering)
189     LoadPlugin();
190 }
191 
LoadPlugin()192 void PluginPlaceholder::LoadPlugin() {
193   // This is not strictly necessary but is an important defense in case the
194   // event propagation changes between "close" vs. "click-to-play".
195   if (hidden_)
196     return;
197   if (!allow_loading_) {
198     NOTREACHED();
199     return;
200   }
201 
202   // TODO(mmenke):  In the case of prerendering, feed into
203   //                ChromeContentRendererClient::CreatePlugin instead, to
204   //                reduce the chance of future regressions.
205   WebPlugin* plugin =
206       render_frame()->CreatePlugin(frame_, plugin_info_, plugin_params_);
207   ReplacePlugin(plugin);
208 }
209 
LoadCallback(const CppArgumentList & args,CppVariant * result)210 void PluginPlaceholder::LoadCallback(const CppArgumentList& args,
211                                      CppVariant* result) {
212   RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Click"));
213   LoadPlugin();
214 }
215 
HideCallback(const CppArgumentList & args,CppVariant * result)216 void PluginPlaceholder::HideCallback(const CppArgumentList& args,
217                                      CppVariant* result) {
218   RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Hide_Click"));
219   HidePlugin();
220 }
221 
DidFinishLoadingCallback(const CppArgumentList & args,CppVariant * result)222 void PluginPlaceholder::DidFinishLoadingCallback(const CppArgumentList& args,
223                                                  CppVariant* result) {
224   finished_loading_ = true;
225   if (message_.length() > 0)
226     UpdateMessage();
227 }
228 
SetPluginInfo(const content::WebPluginInfo & plugin_info)229 void PluginPlaceholder::SetPluginInfo(
230     const content::WebPluginInfo& plugin_info) {
231   plugin_info_ = plugin_info;
232 }
233 
GetPluginInfo() const234 const content::WebPluginInfo& PluginPlaceholder::GetPluginInfo() const {
235   return plugin_info_;
236 }
237 
SetIdentifier(const std::string & identifier)238 void PluginPlaceholder::SetIdentifier(const std::string& identifier) {
239   identifier_ = identifier;
240 }
241 
GetFrame()242 blink::WebFrame* PluginPlaceholder::GetFrame() { return frame_; }
243 
GetPluginParams() const244 const blink::WebPluginParams& PluginPlaceholder::GetPluginParams() const {
245   return plugin_params_;
246 }
247 
248 }  // namespace plugins
249