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