webview_plugin.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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. #include "components/plugins/renderer/webview_plugin.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <string>
  8. #include "base/auto_reset.h"
  9. #include "base/bind.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/location.h"
  12. #include "base/metrics/histogram_macros.h"
  13. #include "base/numerics/safe_conversions.h"
  14. #include "base/task/single_thread_task_runner.h"
  15. #include "base/threading/thread_task_runner_handle.h"
  16. #include "gin/converter.h"
  17. #include "mojo/public/cpp/bindings/pending_remote.h"
  18. #include "skia/ext/platform_canvas.h"
  19. #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
  20. #include "third_party/blink/public/common/page/page_zoom.h"
  21. #include "third_party/blink/public/common/tokens/tokens.h"
  22. #include "third_party/blink/public/common/web_preferences/web_preferences.h"
  23. #include "third_party/blink/public/mojom/input/focus_type.mojom.h"
  24. #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
  25. #include "third_party/blink/public/platform/web_policy_container.h"
  26. #include "third_party/blink/public/platform/web_url.h"
  27. #include "third_party/blink/public/platform/web_url_response.h"
  28. #include "third_party/blink/public/web/web_document.h"
  29. #include "third_party/blink/public/web/web_element.h"
  30. #include "third_party/blink/public/web/web_frame_widget.h"
  31. #include "third_party/blink/public/web/web_local_frame.h"
  32. #include "third_party/blink/public/web/web_navigation_control.h"
  33. #include "third_party/blink/public/web/web_plugin_container.h"
  34. #include "third_party/blink/public/web/web_view.h"
  35. using blink::DragOperationsMask;
  36. using blink::WebDragData;
  37. using blink::WebFrameWidget;
  38. using blink::WebLocalFrame;
  39. using blink::WebMouseEvent;
  40. using blink::WebPlugin;
  41. using blink::WebPluginContainer;
  42. using blink::WebString;
  43. using blink::WebURLError;
  44. using blink::WebURLResponse;
  45. using blink::WebVector;
  46. using blink::WebView;
  47. using blink::web_pref::WebPreferences;
  48. WebViewPlugin::WebViewPlugin(WebView* web_view,
  49. WebViewPlugin::Delegate* delegate,
  50. const WebPreferences& preferences)
  51. : blink::WebViewObserver(web_view),
  52. delegate_(delegate),
  53. container_(nullptr),
  54. finished_loading_(false),
  55. focused_(false),
  56. is_painting_(false),
  57. is_resizing_(false),
  58. web_view_helper_(this, preferences, web_view->GetRendererPreferences()) {}
  59. // static
  60. WebViewPlugin* WebViewPlugin::Create(WebView* web_view,
  61. WebViewPlugin::Delegate* delegate,
  62. const WebPreferences& preferences,
  63. const std::string& html_data,
  64. const GURL& url) {
  65. DCHECK(url.is_valid()) << "Blink requires the WebView to have a valid URL.";
  66. WebViewPlugin* plugin = new WebViewPlugin(web_view, delegate, preferences);
  67. // Loading may synchronously access |delegate| which could be
  68. // uninitialized just yet, so load in another task.
  69. plugin->GetTaskRunner()->PostTask(
  70. FROM_HERE,
  71. base::BindOnce(&WebViewPlugin::LoadHTML,
  72. plugin->weak_factory_.GetWeakPtr(), html_data, url));
  73. return plugin;
  74. }
  75. WebViewPlugin::~WebViewPlugin() {
  76. DCHECK(!weak_factory_.HasWeakPtrs());
  77. }
  78. void WebViewPlugin::ReplayReceivedData(WebPlugin* plugin) {
  79. if (!response_.IsNull()) {
  80. plugin->DidReceiveResponse(response_);
  81. for (auto it = data_.begin(); it != data_.end(); ++it) {
  82. plugin->DidReceiveData(it->c_str(), it->length());
  83. }
  84. }
  85. // We need to transfer the |focused_| to new plugin after it loaded.
  86. if (focused_)
  87. plugin->UpdateFocus(true, blink::mojom::FocusType::kNone);
  88. if (finished_loading_)
  89. plugin->DidFinishLoading();
  90. if (error_)
  91. plugin->DidFailLoading(*error_);
  92. }
  93. WebPluginContainer* WebViewPlugin::Container() const {
  94. return container_;
  95. }
  96. bool WebViewPlugin::Initialize(WebPluginContainer* container) {
  97. DCHECK(container);
  98. DCHECK_EQ(this, container->Plugin());
  99. container_ = container;
  100. // We must call layout again here to ensure that the container is laid
  101. // out before we next try to paint it, which is a requirement of the
  102. // document life cycle in Blink. In most cases, needsLayout is set by
  103. // scheduleAnimation, but due to timers controlling widget update,
  104. // scheduleAnimation may be invoked before this initialize call (which
  105. // comes through the widget update process). It doesn't hurt to mark
  106. // for animation again, and it does help us in the race-condition situation.
  107. container_->ScheduleAnimation();
  108. old_title_ = container_->GetElement().GetAttribute("title");
  109. web_view()->SetZoomLevel(
  110. blink::PageZoomFactorToZoomLevel(container_->PageZoomFactor()));
  111. return true;
  112. }
  113. void WebViewPlugin::Destroy() {
  114. weak_factory_.InvalidateWeakPtrs();
  115. if (delegate_) {
  116. delegate_->PluginDestroyed();
  117. delegate_ = nullptr;
  118. }
  119. container_ = nullptr;
  120. blink::WebViewObserver::Observe(nullptr);
  121. base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
  122. }
  123. v8::Local<v8::Object> WebViewPlugin::V8ScriptableObject(v8::Isolate* isolate) {
  124. if (!delegate_)
  125. return v8::Local<v8::Object>();
  126. return delegate_->GetV8ScriptableObject(isolate);
  127. }
  128. void WebViewPlugin::UpdateAllLifecyclePhases(
  129. blink::DocumentUpdateReason reason) {
  130. DCHECK(web_view()->MainFrameWidget());
  131. web_view()->MainFrameWidget()->UpdateAllLifecyclePhases(reason);
  132. }
  133. bool WebViewPlugin::IsErrorPlaceholder() {
  134. if (!delegate_)
  135. return false;
  136. return delegate_->IsErrorPlaceholder();
  137. }
  138. void WebViewPlugin::Paint(cc::PaintCanvas* canvas, const gfx::Rect& rect) {
  139. gfx::Rect paint_rect = gfx::IntersectRects(rect_, rect);
  140. if (paint_rect.IsEmpty())
  141. return;
  142. base::AutoReset<bool> is_painting(
  143. &is_painting_, true);
  144. paint_rect.Offset(-rect_.x(), -rect_.y());
  145. canvas->save();
  146. canvas->translate(SkIntToScalar(rect_.x()), SkIntToScalar(rect_.y()));
  147. web_view()->MainFrameWidget()->UpdateLifecycle(
  148. blink::WebLifecycleUpdate::kAll,
  149. blink::DocumentUpdateReason::kBeginMainFrame);
  150. web_view()->PaintContent(canvas, paint_rect);
  151. canvas->restore();
  152. }
  153. // Coordinates are relative to the containing window.
  154. void WebViewPlugin::UpdateGeometry(const gfx::Rect& window_rect,
  155. const gfx::Rect& clip_rect,
  156. const gfx::Rect& unobscured_rect,
  157. bool is_visible) {
  158. DCHECK(container_);
  159. base::AutoReset<bool> is_resizing(&is_resizing_, true);
  160. if (window_rect != rect_) {
  161. rect_ = window_rect;
  162. DCHECK(web_view()->MainFrameWidget());
  163. web_view()->MainFrameWidget()->Resize(rect_.size());
  164. }
  165. // Plugin updates are forbidden during Blink layout. Therefore,
  166. // UpdatePluginForNewGeometry must be posted to a task to run asynchronously.
  167. web_view_helper_.main_frame()
  168. ->GetAgentGroupScheduler()
  169. ->CompositorTaskRunner()
  170. ->PostTask(FROM_HERE,
  171. base::BindOnce(&WebViewPlugin::UpdatePluginForNewGeometry,
  172. weak_factory_.GetWeakPtr(), window_rect,
  173. unobscured_rect));
  174. }
  175. void WebViewPlugin::UpdateFocus(bool focused,
  176. blink::mojom::FocusType focus_type) {
  177. focused_ = focused;
  178. }
  179. blink::WebInputEventResult WebViewPlugin::HandleInputEvent(
  180. const blink::WebCoalescedInputEvent& coalesced_event,
  181. ui::Cursor* cursor) {
  182. const blink::WebInputEvent& event = coalesced_event.Event();
  183. // For tap events, don't handle them. They will be converted to
  184. // mouse events later and passed to here.
  185. if (event.GetType() == blink::WebInputEvent::Type::kGestureTap)
  186. return blink::WebInputEventResult::kNotHandled;
  187. // For LongPress events we return false, since otherwise the context menu will
  188. // be suppressed. https://crbug.com/482842
  189. if (event.GetType() == blink::WebInputEvent::Type::kGestureLongPress)
  190. return blink::WebInputEventResult::kNotHandled;
  191. if (event.GetType() == blink::WebInputEvent::Type::kContextMenu) {
  192. if (delegate_) {
  193. const WebMouseEvent& mouse_event =
  194. static_cast<const WebMouseEvent&>(event);
  195. delegate_->ShowContextMenu(mouse_event);
  196. }
  197. return blink::WebInputEventResult::kHandledSuppressed;
  198. }
  199. current_cursor_ = *cursor;
  200. DCHECK(web_view()->MainFrameWidget());
  201. blink::WebInputEventResult handled =
  202. web_view()->MainFrameWidget()->HandleInputEvent(coalesced_event);
  203. *cursor = current_cursor_;
  204. return handled;
  205. }
  206. void WebViewPlugin::DidReceiveResponse(const WebURLResponse& response) {
  207. DCHECK(response_.IsNull());
  208. response_ = response;
  209. }
  210. void WebViewPlugin::DidReceiveData(const char* data, size_t data_length) {
  211. data_.push_back(std::string(data, data_length));
  212. }
  213. void WebViewPlugin::DidFinishLoading() {
  214. DCHECK(!finished_loading_);
  215. finished_loading_ = true;
  216. }
  217. void WebViewPlugin::DidFailLoading(const WebURLError& error) {
  218. DCHECK(!error_);
  219. error_ = std::make_unique<WebURLError>(error);
  220. }
  221. WebViewPlugin::WebViewHelper::WebViewHelper(
  222. WebViewPlugin* plugin,
  223. const WebPreferences& parent_web_preferences,
  224. const blink::RendererPreferences& parent_renderer_preferences)
  225. : plugin_(plugin),
  226. agent_group_scheduler_(
  227. blink::scheduler::WebThreadScheduler::MainThreadScheduler()
  228. ->CreateAgentGroupScheduler()) {
  229. web_view_ = WebView::Create(
  230. /*client=*/this,
  231. /*is_hidden=*/false,
  232. /*is_prerendering=*/false,
  233. /*is_inside_portal=*/false,
  234. /*fenced_frame_mode=*/absl::nullopt,
  235. /*compositing_enabled=*/false,
  236. /*widgets_never_composited=*/false,
  237. /*opener=*/nullptr, mojo::NullAssociatedReceiver(),
  238. *agent_group_scheduler_,
  239. /*session_storage_namespace_id=*/base::EmptyString(),
  240. /*page_base_background_color=*/absl::nullopt);
  241. // ApplyWebPreferences before making a WebLocalFrame so that the frame sees a
  242. // consistent view of our preferences.
  243. blink::WebView::ApplyWebPreferences(parent_web_preferences, web_view_);
  244. // Turn off AcceptLoadDrops for this plugin webview.
  245. blink::RendererPreferences renderer_preferences = parent_renderer_preferences;
  246. renderer_preferences.can_accept_load_drops = false;
  247. web_view_->SetRendererPreferences(renderer_preferences);
  248. WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
  249. web_view_, this, nullptr, blink::LocalFrameToken(), nullptr);
  250. blink::WebFrameWidget* frame_widget = web_frame->InitializeFrameWidget(
  251. blink::CrossVariantMojoAssociatedRemote<
  252. blink::mojom::FrameWidgetHostInterfaceBase>(),
  253. blink::CrossVariantMojoAssociatedReceiver<
  254. blink::mojom::FrameWidgetInterfaceBase>(),
  255. blink_widget_host_receiver_.BindNewEndpointAndPassDedicatedRemote(),
  256. blink_widget_.BindNewEndpointAndPassDedicatedReceiver(),
  257. viz::FrameSinkId());
  258. frame_widget->InitializeNonCompositing(this);
  259. frame_widget->DisableDragAndDrop();
  260. // The WebFrame created here was already attached to the Page as its main
  261. // frame, and the WebFrameWidget has been initialized, so we can call
  262. // WebView's DidAttachLocalMainFrame().
  263. web_view_->DidAttachLocalMainFrame();
  264. }
  265. WebViewPlugin::WebViewHelper::~WebViewHelper() {
  266. web_view_->Close();
  267. }
  268. void WebViewPlugin::WebViewHelper::UpdateTooltipUnderCursor(
  269. const std::u16string& tooltip_text,
  270. base::i18n::TextDirection hint) {
  271. UpdateTooltip(tooltip_text);
  272. }
  273. void WebViewPlugin::WebViewHelper::UpdateTooltipFromKeyboard(
  274. const std::u16string& tooltip_text,
  275. base::i18n::TextDirection hint,
  276. const gfx::Rect& bounds) {
  277. UpdateTooltip(tooltip_text);
  278. }
  279. void WebViewPlugin::WebViewHelper::ClearKeyboardTriggeredTooltip() {
  280. // This is an exception to the "only clear it if its set from keyboard" since
  281. // there are no way of knowing whether the tooltips were set from keyboard or
  282. // cursor in this class. In any case, this will clear the tooltip.
  283. UpdateTooltip(std::u16string());
  284. }
  285. void WebViewPlugin::WebViewHelper::UpdateTooltip(
  286. const std::u16string& tooltip_text) {
  287. if (plugin_->container_) {
  288. plugin_->container_->GetElement().SetAttribute(
  289. "title", WebString::FromUTF16(tooltip_text));
  290. }
  291. }
  292. void WebViewPlugin::WebViewHelper::InvalidateContainer() {
  293. if (plugin_->container_)
  294. plugin_->container_->Invalidate();
  295. }
  296. void WebViewPlugin::WebViewHelper::SetCursor(const ui::Cursor& cursor) {
  297. plugin_->current_cursor_ = cursor;
  298. }
  299. void WebViewPlugin::WebViewHelper::ScheduleNonCompositedAnimation() {
  300. // Resizes must be self-contained: any lifecycle updating must
  301. // be triggerd from within the WebView or this WebViewPlugin.
  302. // This is because this WebViewPlugin is contained in another
  303. // WebView which may be in the middle of updating its lifecycle,
  304. // but after layout is done, and it is illegal to dirty earlier
  305. // lifecycle stages during later ones.
  306. if (plugin_->is_resizing_)
  307. return;
  308. if (plugin_->container_) {
  309. // This should never happen; see also crbug.com/545039 for context.
  310. DCHECK(!plugin_->is_painting_);
  311. // This goes to compositor of the containing frame.
  312. plugin_->container_->ScheduleAnimation();
  313. }
  314. }
  315. std::unique_ptr<blink::WebURLLoaderFactory>
  316. WebViewPlugin::WebViewHelper::CreateURLLoaderFactory() {
  317. return plugin_->Container()
  318. ->GetDocument()
  319. .GetFrame()
  320. ->Client()
  321. ->CreateURLLoaderFactory();
  322. }
  323. void WebViewPlugin::WebViewHelper::BindToFrame(
  324. blink::WebNavigationControl* frame) {
  325. frame_ = frame;
  326. }
  327. void WebViewPlugin::WebViewHelper::DidClearWindowObject() {
  328. if (!plugin_->delegate_)
  329. return;
  330. v8::Isolate* isolate = blink::MainThreadIsolate();
  331. v8::HandleScope handle_scope(isolate);
  332. v8::MicrotasksScope microtasks_scope(
  333. isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
  334. v8::Local<v8::Context> context = frame_->MainWorldScriptContext();
  335. DCHECK(!context.IsEmpty());
  336. v8::Context::Scope context_scope(context);
  337. v8::Local<v8::Object> global = context->Global();
  338. global
  339. ->Set(context, gin::StringToV8(isolate, "plugin"),
  340. plugin_->delegate_->GetV8Handle(isolate))
  341. .Check();
  342. }
  343. void WebViewPlugin::WebViewHelper::FrameDetached() {
  344. frame_->Close();
  345. frame_ = nullptr;
  346. }
  347. void WebViewPlugin::OnZoomLevelChanged() {
  348. if (container_) {
  349. web_view()->SetZoomLevel(
  350. blink::PageZoomFactorToZoomLevel(container_->PageZoomFactor()));
  351. }
  352. }
  353. void WebViewPlugin::LoadHTML(const std::string& html_data, const GURL& url) {
  354. auto params = std::make_unique<blink::WebNavigationParams>();
  355. params->url = url;
  356. params->policy_container = std::make_unique<blink::WebPolicyContainer>();
  357. // The |html_data| comes from files in: chrome/renderer/resources/plugins/
  358. // Executing scripts is the only capability required.
  359. //
  360. // WebSandboxFlags is a bit field. This removes all the capabilities, except
  361. // script execution.
  362. using network::mojom::WebSandboxFlags;
  363. params->policy_container->policies.sandbox_flags =
  364. static_cast<WebSandboxFlags>(
  365. ~static_cast<int>(WebSandboxFlags::kScripts));
  366. blink::WebNavigationParams::FillStaticResponse(params.get(), "text/html",
  367. "UTF-8", html_data);
  368. web_view_helper_.main_frame()->CommitNavigation(std::move(params),
  369. /*extra_data=*/nullptr);
  370. }
  371. void WebViewPlugin::UpdatePluginForNewGeometry(
  372. const gfx::Rect& window_rect,
  373. const gfx::Rect& unobscured_rect) {
  374. DCHECK(container_);
  375. if (!delegate_)
  376. return;
  377. // The delegate may instantiate a new plugin.
  378. delegate_->OnUnobscuredRectUpdate(gfx::Rect(unobscured_rect));
  379. // The delegate may have dirtied style and layout of the WebView.
  380. // Run the lifecycle now so that it is clean.
  381. DCHECK(web_view()->MainFrameWidget());
  382. web_view()->MainFrameWidget()->UpdateAllLifecyclePhases(
  383. blink::DocumentUpdateReason::kPlugin);
  384. }
  385. scoped_refptr<base::SingleThreadTaskRunner> WebViewPlugin::GetTaskRunner() {
  386. return web_view_helper_.main_frame()->GetTaskRunner(
  387. blink::TaskType::kInternalDefault);
  388. }