OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
Jói
2013/09/09 17:46:17
Likewise for this file, let's see if --similarity=
aberent
2013/09/10 13:24:01
I will experiment
| |
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 "components/plugins/renderer/plugin_uma.h" | |
15 #include "content/public/common/content_constants.h" | |
16 #include "content/public/common/context_menu_params.h" | |
17 #include "content/public/renderer/render_thread.h" | |
18 #include "content/public/renderer/render_view.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 content::RenderThread; | |
29 using WebKit::WebElement; | |
30 using WebKit::WebFrame; | |
31 using WebKit::WebMouseEvent; | |
32 using WebKit::WebNode; | |
33 using WebKit::WebPlugin; | |
34 using WebKit::WebPluginContainer; | |
35 using WebKit::WebPluginParams; | |
36 using WebKit::WebScriptSource; | |
37 using WebKit::WebURLRequest; | |
38 using webkit_glue::CppArgumentList; | |
39 using webkit_glue::CppVariant; | |
40 | |
41 const char* const kPluginPlaceholderDataURL = "chrome://pluginplaceholderdata/"; | |
42 | |
43 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
44 // Strings we used to parse the youtube plugin url. | |
45 const char* const kSlashVSlash = "/v/"; | |
46 const char* const kSlashESlash = "/e/"; | |
47 #endif | |
48 | |
49 PluginPlaceholder::PluginPlaceholder(content::RenderView* render_view, | |
50 WebFrame* frame, | |
51 const WebPluginParams& params, | |
52 const std::string& html_data) | |
53 : content::RenderViewObserver(render_view), | |
54 frame_(frame), | |
55 plugin_params_(params), | |
56 plugin_(WebViewPlugin::Create(this, | |
57 render_view->GetWebkitPreferences(), | |
58 html_data, | |
59 GURL(kPluginPlaceholderDataURL))), | |
60 is_blocked_for_prerendering_(false), | |
61 allow_loading_(false), | |
62 hidden_(false), | |
63 finished_loading_(false) {} | |
64 | |
65 PluginPlaceholder::~PluginPlaceholder() {} | |
66 | |
67 void PluginPlaceholder::BindWebFrame(WebFrame* frame) { | |
68 BindToJavascript(frame, "plugin"); | |
69 BindCallback( | |
70 "load", | |
71 base::Bind(&PluginPlaceholder::LoadCallback, base::Unretained(this))); | |
72 BindCallback( | |
73 "hide", | |
74 base::Bind(&PluginPlaceholder::HideCallback, base::Unretained(this))); | |
75 BindCallback("openAboutPlugins", | |
76 base::Bind(&PluginPlaceholder::OpenAboutPluginsCallback, | |
77 base::Unretained(this))); | |
78 BindCallback("didFinishLoading", | |
79 base::Bind(&PluginPlaceholder::DidFinishLoadingCallback, | |
80 base::Unretained(this))); | |
81 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
82 BindCallback("openYoutubeURL", | |
83 base::Bind(&PluginPlaceholder::OpenYoutubeUrlCallback, | |
84 base::Unretained(this))); | |
85 #endif | |
86 } | |
87 | |
88 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
89 // Helper function to get the youtube id from plugin params for old style | |
90 // embedded youtube video. | |
91 std::string PluginPlaceholder::GetYoutubeVideoId( | |
92 const WebPluginParams& params) { | |
93 GURL url(params.url); | |
94 std::string video_id = url.path().substr(strlen(kSlashVSlash)); | |
95 | |
96 // Extract just the video id | |
97 size_t video_id_end = video_id.find('&'); | |
98 if (video_id_end != std::string::npos) | |
99 video_id = video_id.substr(0, video_id_end); | |
100 return video_id; | |
101 } | |
102 #endif | |
103 | |
104 void PluginPlaceholder::ReplacePlugin(WebPlugin* new_plugin) { | |
105 CHECK(plugin_); | |
106 if (!new_plugin) { | |
107 PluginUMAReporter::GetInstance()->ReportPluginMissing( | |
108 plugin_params_.mimeType.utf8(), plugin_params_.url); | |
109 return; | |
110 } | |
111 | |
112 WebPluginContainer* container = plugin_->container(); | |
113 // Set the new plug-in on the container before initializing it. | |
114 container->setPlugin(new_plugin); | |
115 // Save the element in case the plug-in is removed from the page during | |
116 // initialization. | |
117 WebElement element = container->element(); | |
118 if (!new_plugin->initialize(container)) { | |
119 // We couldn't initialize the new plug-in. Restore the old one and abort. | |
120 container->setPlugin(plugin_); | |
121 return; | |
122 } | |
123 | |
124 // The plug-in has been removed from the page. Destroy the old plug-in | |
125 // (which will destroy us). | |
126 if (!element.pluginContainer()) { | |
127 plugin_->destroy(); | |
128 return; | |
129 } | |
130 | |
131 // During initialization, the new plug-in might have replaced itself in turn | |
132 // with another plug-in. Make sure not to use the passed in |new_plugin| after | |
133 // this point. | |
134 new_plugin = container->plugin(); | |
135 | |
136 plugin_->RestoreTitleText(); | |
137 container->invalidate(); | |
138 container->reportGeometry(); | |
139 plugin_->ReplayReceivedData(new_plugin); | |
140 plugin_->destroy(); | |
141 } | |
142 | |
143 void PluginPlaceholder::HidePlugin() { | |
144 hidden_ = true; | |
145 WebPluginContainer* container = plugin_->container(); | |
146 WebElement element = container->element(); | |
147 element.setAttribute("style", "display: none;"); | |
148 // If we have a width and height, search for a parent (often <div>) with the | |
149 // same dimensions. If we find such a parent, hide that as well. | |
150 // This makes much more uncovered page content usable (including clickable) | |
151 // as opposed to merely visible. | |
152 // TODO(cevans) -- it's a foul heurisitc but we're going to tolerate it for | |
153 // now for these reasons: | |
154 // 1) Makes the user experience better. | |
155 // 2) Foulness is encapsulated within this single function. | |
156 // 3) Confidence in no fasle positives. | |
157 // 4) Seems to have a good / low false negative rate at this time. | |
158 if (element.hasAttribute("width") && element.hasAttribute("height")) { | |
159 std::string width_str("width:[\\s]*"); | |
160 width_str += element.getAttribute("width").utf8().data(); | |
161 if (EndsWith(width_str, "px", false)) { | |
162 width_str = width_str.substr(0, width_str.length() - 2); | |
163 } | |
164 TrimWhitespace(width_str, TRIM_TRAILING, &width_str); | |
165 width_str += "[\\s]*px"; | |
166 std::string height_str("height:[\\s]*"); | |
167 height_str += element.getAttribute("height").utf8().data(); | |
168 if (EndsWith(height_str, "px", false)) { | |
169 height_str = height_str.substr(0, height_str.length() - 2); | |
170 } | |
171 TrimWhitespace(height_str, TRIM_TRAILING, &height_str); | |
172 height_str += "[\\s]*px"; | |
173 WebNode parent = element; | |
174 while (!parent.parentNode().isNull()) { | |
175 parent = parent.parentNode(); | |
176 if (!parent.isElementNode()) | |
177 continue; | |
178 element = parent.toConst<WebElement>(); | |
179 if (element.hasAttribute("style")) { | |
180 std::string style_str = element.getAttribute("style").utf8(); | |
181 if (RE2::PartialMatch(style_str, width_str) && | |
182 RE2::PartialMatch(style_str, height_str)) | |
183 element.setAttribute("style", "display: none;"); | |
184 } | |
185 } | |
186 } | |
187 } | |
188 | |
189 void PluginPlaceholder::WillDestroyPlugin() { delete this; } | |
190 | |
191 void PluginPlaceholder::SetMessage(const string16& message) { | |
192 message_ = message; | |
193 if (finished_loading_) | |
194 UpdateMessage(); | |
195 } | |
196 | |
197 void PluginPlaceholder::UpdateMessage() { | |
198 std::string script = | |
199 "window.setMessage(" + base::GetDoubleQuotedJson(message_) + ")"; | |
200 plugin_->web_view()->mainFrame()->executeScript( | |
201 WebScriptSource(ASCIIToUTF16(script))); | |
202 } | |
203 | |
204 void PluginPlaceholder::ShowContextMenu(const WebMouseEvent& event) { | |
205 // Does nothing by default. Will be overridden if a specific browser wants | |
206 // a context menu. | |
207 return; | |
208 } | |
209 | |
210 void PluginPlaceholder::OnLoadBlockedPlugins(const std::string& identifier) { | |
211 if (!identifier.empty() && identifier != identifier_) | |
212 return; | |
213 | |
214 RenderThread::Get()->RecordUserMetrics("Plugin_Load_UI"); | |
215 LoadPlugin(); | |
216 } | |
217 | |
218 void PluginPlaceholder::OnSetIsPrerendering(bool is_prerendering) { | |
219 // Prerendering can only be enabled prior to a RenderView's first navigation, | |
220 // so no BlockedPlugin should see the notification that enables prerendering. | |
221 DCHECK(!is_prerendering); | |
222 if (is_blocked_for_prerendering_ && !is_prerendering) | |
223 LoadPlugin(); | |
224 } | |
225 | |
226 void PluginPlaceholder::LoadPlugin() { | |
227 // This is not strictly necessary but is an important defense in case the | |
228 // event propagation changes between "close" vs. "click-to-play". | |
229 if (hidden_) | |
230 return; | |
231 if (!allow_loading_) { | |
232 NOTREACHED(); | |
233 return; | |
234 } | |
235 | |
236 // TODO(mmenke): In the case of prerendering, feed into | |
237 // ChromeContentRendererClient::CreatePlugin instead, to | |
238 // reduce the chance of future regressions. | |
239 WebPlugin* plugin = | |
240 render_view()->CreatePlugin(frame_, plugin_info_, plugin_params_); | |
241 ReplacePlugin(plugin); | |
242 } | |
243 | |
244 void PluginPlaceholder::LoadCallback(const CppArgumentList& args, | |
245 CppVariant* result) { | |
246 RenderThread::Get()->RecordUserMetrics("Plugin_Load_Click"); | |
247 LoadPlugin(); | |
248 } | |
249 | |
250 void PluginPlaceholder::HideCallback(const CppArgumentList& args, | |
251 CppVariant* result) { | |
252 RenderThread::Get()->RecordUserMetrics("Plugin_Hide_Click"); | |
253 HidePlugin(); | |
254 } | |
255 | |
256 void PluginPlaceholder::OpenAboutPluginsCallback(const CppArgumentList& args, | |
257 CppVariant* result) { | |
258 OpenAboutPlugins(); | |
259 } | |
260 | |
261 void PluginPlaceholder::OpenAboutPlugins() { | |
262 // Default version does nothing. | |
263 } | |
264 | |
265 void PluginPlaceholder::DidFinishLoadingCallback(const CppArgumentList& args, | |
266 CppVariant* result) { | |
267 finished_loading_ = true; | |
268 if (message_.length() > 0) | |
269 UpdateMessage(); | |
270 } | |
271 | |
272 #if defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
273 void PluginPlaceholder::OpenYoutubeUrlCallback(const CppArgumentList& args, | |
274 CppVariant* result) { | |
275 std::string youtube("vnd.youtube:"); | |
276 GURL url(youtube.append(GetYoutubeVideoId(plugin_params_))); | |
277 WebURLRequest request; | |
278 request.initialize(); | |
279 request.setURL(url); | |
280 render_view()->LoadURLExternally( | |
281 frame_, request, WebKit::WebNavigationPolicyNewForegroundTab); | |
282 } | |
283 | |
284 bool PluginPlaceholder::IsValidYouTubeVideo(const std::string& path) { | |
285 unsigned len = strlen(kSlashVSlash); | |
286 | |
287 // check for more than just /v/ or /e/. | |
288 if (path.length() <= len) | |
289 return false; | |
290 | |
291 std::string str = StringToLowerASCII(path); | |
292 // Youtube flash url can start with /v/ or /e/. | |
293 if (strncmp(str.data(), kSlashVSlash, len) != 0 && | |
294 strncmp(str.data(), kSlashESlash, len) != 0) | |
295 return false; | |
296 | |
297 // Start after /v/ | |
298 for (unsigned i = len; i < path.length(); i++) { | |
299 char c = str[i]; | |
300 if (isalpha(c) || isdigit(c) || c == '_' || c == '-') | |
301 continue; | |
302 // The url can have more parameters such as &hl=en after the video id. | |
303 // Once we start seeing extra parameters we can return true. | |
304 return c == '&' && i > len; | |
305 } | |
306 return true; | |
307 } | |
308 | |
309 bool PluginPlaceholder::IsYouTubeURL(const GURL& url, | |
310 const std::string& mime_type) { | |
311 std::string host = url.host(); | |
312 bool is_youtube = EndsWith(host, "youtube.com", true) || | |
313 EndsWith(host, "youtube-nocookie.com", true); | |
314 | |
315 return is_youtube && IsValidYouTubeVideo(url.path()) && | |
316 LowerCaseEqualsASCII(mime_type, content::kFlashPluginSwfMimeType); | |
317 } | |
318 #endif // defined(ENABLE_MOBILE_YOUTUBE_PLUGIN) | |
319 | |
320 void PluginPlaceholder::SetPluginInfo( | |
321 const content::WebPluginInfo& plugin_info) { | |
322 plugin_info_ = plugin_info; | |
323 } | |
324 | |
325 const content::WebPluginInfo& PluginPlaceholder::GetPluginInfo() const { | |
326 return plugin_info_; | |
327 } | |
328 | |
329 void PluginPlaceholder::SetIdentifier(const std::string& identifier) { | |
330 identifier_ = identifier; | |
331 } | |
332 | |
333 WebKit::WebFrame* PluginPlaceholder::GetFrame() { return frame_; } | |
334 | |
335 const WebKit::WebPluginParams& PluginPlaceholder::GetPluginParams() const { | |
336 return plugin_params_; | |
337 } | |
OLD | NEW |