aw_render_frame_ext.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // Copyright 2014 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 "android_webview/renderer/aw_render_frame_ext.h"
  5. #include <map>
  6. #include <memory>
  7. #include "android_webview/common/aw_features.h"
  8. #include "android_webview/common/mojom/frame.mojom.h"
  9. #include "base/no_destructor.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "components/autofill/content/renderer/autofill_agent.h"
  13. #include "components/autofill/content/renderer/password_autofill_agent.h"
  14. #include "components/content_capture/common/content_capture_features.h"
  15. #include "components/content_capture/renderer/content_capture_sender.h"
  16. #include "content/public/renderer/render_frame.h"
  17. #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
  18. #include "third_party/blink/public/platform/web_security_origin.h"
  19. #include "third_party/blink/public/web/web_element.h"
  20. #include "third_party/blink/public/web/web_element_collection.h"
  21. #include "third_party/blink/public/web/web_frame_widget.h"
  22. #include "third_party/blink/public/web/web_hit_test_result.h"
  23. #include "third_party/blink/public/web/web_image_cache.h"
  24. #include "third_party/blink/public/web/web_local_frame.h"
  25. #include "third_party/blink/public/web/web_meaningful_layout.h"
  26. #include "third_party/blink/public/web/web_node.h"
  27. #include "third_party/blink/public/web/web_view.h"
  28. #include "url/url_canon.h"
  29. #include "url/url_constants.h"
  30. #include "url/url_util.h"
  31. namespace android_webview {
  32. namespace {
  33. const char kAddressPrefix[] = "geo:0,0?q=";
  34. const char kEmailPrefix[] = "mailto:";
  35. const char kPhoneNumberPrefix[] = "tel:";
  36. GURL GetAbsoluteUrl(const blink::WebNode& node,
  37. const std::u16string& url_fragment) {
  38. return GURL(node.GetDocument().CompleteURL(
  39. blink::WebString::FromUTF16(url_fragment)));
  40. }
  41. std::u16string GetHref(const blink::WebElement& element) {
  42. // Get the actual 'href' attribute, which might relative if valid or can
  43. // possibly contain garbage otherwise, so not using absoluteLinkURL here.
  44. return element.GetAttribute("href").Utf16();
  45. }
  46. GURL GetAbsoluteSrcUrl(const blink::WebElement& element) {
  47. if (element.IsNull())
  48. return GURL();
  49. return GetAbsoluteUrl(element, element.GetAttribute("src").Utf16());
  50. }
  51. blink::WebElement GetImgChild(const blink::WebNode& node) {
  52. // This implementation is incomplete (for example if is an area tag) but
  53. // matches the original WebViewClassic implementation.
  54. blink::WebElementCollection collection = node.GetElementsByHTMLTagName("img");
  55. DCHECK(!collection.IsNull());
  56. return collection.FirstItem();
  57. }
  58. GURL GetChildImageUrlFromElement(const blink::WebElement& element) {
  59. const blink::WebElement child_img = GetImgChild(element);
  60. if (child_img.IsNull())
  61. return GURL();
  62. return GetAbsoluteSrcUrl(child_img);
  63. }
  64. bool RemovePrefixAndAssignIfMatches(const base::StringPiece& prefix,
  65. const GURL& url,
  66. std::string* dest) {
  67. const base::StringPiece spec(url.possibly_invalid_spec());
  68. if (base::StartsWith(spec, prefix)) {
  69. url::RawCanonOutputW<1024> output;
  70. url::DecodeURLEscapeSequences(
  71. spec.data() + prefix.length(), spec.length() - prefix.length(),
  72. url::DecodeURLMode::kUTF8OrIsomorphic, &output);
  73. *dest =
  74. base::UTF16ToUTF8(base::StringPiece16(output.data(), output.length()));
  75. return true;
  76. }
  77. return false;
  78. }
  79. void DistinguishAndAssignSrcLinkType(const GURL& url,
  80. mojom::HitTestData* data) {
  81. if (RemovePrefixAndAssignIfMatches(kAddressPrefix, url,
  82. &data->extra_data_for_type)) {
  83. data->type = mojom::HitTestDataType::kGeo;
  84. } else if (RemovePrefixAndAssignIfMatches(kPhoneNumberPrefix, url,
  85. &data->extra_data_for_type)) {
  86. data->type = mojom::HitTestDataType::kPhone;
  87. } else if (RemovePrefixAndAssignIfMatches(kEmailPrefix, url,
  88. &data->extra_data_for_type)) {
  89. data->type = mojom::HitTestDataType::kEmail;
  90. } else {
  91. data->type = mojom::HitTestDataType::kSrcLink;
  92. data->extra_data_for_type = url.possibly_invalid_spec();
  93. if (!data->extra_data_for_type.empty())
  94. data->href = base::UTF8ToUTF16(data->extra_data_for_type);
  95. }
  96. }
  97. void PopulateHitTestData(const GURL& absolute_link_url,
  98. const GURL& absolute_image_url,
  99. bool is_editable,
  100. mojom::HitTestData* data) {
  101. // Note: Using GURL::is_empty instead of GURL:is_valid due to the
  102. // WebViewClassic allowing any kind of protocol which GURL::is_valid
  103. // disallows. Similar reasons for using GURL::possibly_invalid_spec instead of
  104. // GURL::spec.
  105. if (!absolute_image_url.is_empty())
  106. data->img_src = absolute_image_url;
  107. const bool is_javascript_scheme =
  108. absolute_link_url.SchemeIs(url::kJavaScriptScheme);
  109. const bool has_link_url = !absolute_link_url.is_empty();
  110. const bool has_image_url = !absolute_image_url.is_empty();
  111. if (has_link_url && !has_image_url && !is_javascript_scheme) {
  112. DistinguishAndAssignSrcLinkType(absolute_link_url, data);
  113. } else if (has_link_url && has_image_url && !is_javascript_scheme) {
  114. data->type = mojom::HitTestDataType::kSrcImageLink;
  115. data->extra_data_for_type = data->img_src.possibly_invalid_spec();
  116. if (absolute_link_url.is_valid())
  117. data->href = base::UTF8ToUTF16(absolute_link_url.possibly_invalid_spec());
  118. } else if (!has_link_url && has_image_url) {
  119. data->type = mojom::HitTestDataType::kImage;
  120. data->extra_data_for_type = data->img_src.possibly_invalid_spec();
  121. } else if (is_editable) {
  122. data->type = mojom::HitTestDataType::kEditText;
  123. DCHECK_EQ(0u, data->extra_data_for_type.length());
  124. }
  125. }
  126. } // namespace
  127. // Registry for RenderFrame => AwRenderFrameExt lookups
  128. typedef std::map<content::RenderFrame*, AwRenderFrameExt*> FrameExtMap;
  129. FrameExtMap* GetFrameExtMap() {
  130. static base::NoDestructor<FrameExtMap> map;
  131. return map.get();
  132. }
  133. AwRenderFrameExt::AwRenderFrameExt(content::RenderFrame* render_frame)
  134. : content::RenderFrameObserver(render_frame) {
  135. // TODO(sgurun) do not create a password autofill agent (change
  136. // autofill agent to store a weakptr).
  137. autofill::PasswordAutofillAgent* password_autofill_agent =
  138. new autofill::PasswordAutofillAgent(render_frame, &registry_);
  139. new autofill::AutofillAgent(render_frame, password_autofill_agent, nullptr,
  140. nullptr, &registry_);
  141. if (content_capture::features::IsContentCaptureEnabled())
  142. new content_capture::ContentCaptureSender(render_frame, &registry_);
  143. // If we are the main frame register an additional mojo interface.
  144. if (render_frame->IsMainFrame()) {
  145. registry_.AddInterface<mojom::LocalMainFrame>(base::BindRepeating(
  146. &AwRenderFrameExt::BindLocalMainFrame, base::Unretained(this)));
  147. }
  148. // Add myself to the RenderFrame => AwRenderFrameExt register.
  149. GetFrameExtMap()->emplace(render_frame, this);
  150. }
  151. AwRenderFrameExt::~AwRenderFrameExt() {
  152. // Remove myself from the RenderFrame => AwRenderFrameExt register. Ideally,
  153. // we'd just use render_frame() and erase by key. However, by this time the
  154. // render_frame has already been cleared so we have to iterate over all
  155. // render_frames in the map and wipe the one(s) that point to this
  156. // AwRenderFrameExt
  157. auto* map = GetFrameExtMap();
  158. auto it = map->begin();
  159. while (it != map->end()) {
  160. if (it->second == this) {
  161. it = map->erase(it);
  162. } else {
  163. ++it;
  164. }
  165. }
  166. }
  167. AwRenderFrameExt* AwRenderFrameExt::FromRenderFrame(
  168. content::RenderFrame* render_frame) {
  169. DCHECK(render_frame != nullptr);
  170. auto iter = GetFrameExtMap()->find(render_frame);
  171. DCHECK(GetFrameExtMap()->end() != iter)
  172. << "Should always exist a render_frame_ext for a render_frame";
  173. AwRenderFrameExt* render_frame_ext = iter->second;
  174. return render_frame_ext;
  175. }
  176. const mojo::AssociatedRemote<mojom::FrameHost>&
  177. AwRenderFrameExt::GetFrameHost() {
  178. if (!frame_host_remote_) {
  179. render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(
  180. &frame_host_remote_);
  181. }
  182. return frame_host_remote_;
  183. }
  184. bool AwRenderFrameExt::OnAssociatedInterfaceRequestForFrame(
  185. const std::string& interface_name,
  186. mojo::ScopedInterfaceEndpointHandle* handle) {
  187. return registry_.TryBindInterface(interface_name, handle);
  188. }
  189. void AwRenderFrameExt::DidCreateDocumentElement() {
  190. if (!base::FeatureList::IsEnabled(
  191. features::kWebViewHitTestInBlinkOnTouchStart)) {
  192. return;
  193. }
  194. render_frame()->GetWebFrame()->AddHitTestOnTouchStartCallback(
  195. base::BindRepeating(&AwRenderFrameExt::HandleHitTestResult,
  196. base::Unretained(this)));
  197. }
  198. void AwRenderFrameExt::DidCommitProvisionalLoad(
  199. ui::PageTransition transition) {
  200. // Clear the cache when we cross site boundaries in the main frame.
  201. //
  202. // We're trying to approximate what happens with a multi-process Chromium,
  203. // where navigation across origins would cause a new render process to spin
  204. // up, and thus start with a clear cache. Wiring up a signal from browser to
  205. // renderer code to say "this navigation would have switched processes" would
  206. // be disruptive, so this clearing of the cache is the compromise.
  207. blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  208. if (!frame->Parent()) {
  209. url::Origin new_origin = url::Origin::Create(frame->GetDocument().Url());
  210. if (!new_origin.IsSameOriginWith(last_origin_)) {
  211. last_origin_ = new_origin;
  212. blink::WebImageCache::Clear();
  213. }
  214. }
  215. }
  216. void AwRenderFrameExt::FocusedElementChanged(const blink::WebElement& element) {
  217. if (element.IsNull() || !render_frame())
  218. return;
  219. auto data = mojom::HitTestData::New();
  220. data->href = GetHref(element);
  221. data->anchor_text = element.TextContent().Utf16();
  222. GURL absolute_link_url;
  223. if (element.IsLink())
  224. absolute_link_url = GetAbsoluteUrl(element, data->href);
  225. GURL absolute_image_url = GetChildImageUrlFromElement(element);
  226. PopulateHitTestData(absolute_link_url, absolute_image_url,
  227. element.IsEditable(), data.get());
  228. GetFrameHost()->UpdateHitTestData(std::move(data));
  229. }
  230. // Only main frame needs to *receive* the hit test request, because all we need
  231. // is to get the blink::webView object and invoke a the hitTestResultForTap API
  232. // from it.
  233. void AwRenderFrameExt::HitTest(const gfx::PointF& touch_center,
  234. const gfx::SizeF& touch_area) {
  235. blink::WebView* webview = GetWebView();
  236. if (!webview)
  237. return;
  238. const blink::WebHitTestResult result = webview->HitTestResultForTap(
  239. gfx::Point(touch_center.x(), touch_center.y()),
  240. gfx::Size(touch_area.width(), touch_area.height()));
  241. HandleHitTestResult(result);
  242. }
  243. void AwRenderFrameExt::HandleHitTestResult(
  244. const blink::WebHitTestResult& result) {
  245. auto data = mojom::HitTestData::New();
  246. GURL absolute_image_url = result.AbsoluteImageURL();
  247. if (!result.UrlElement().IsNull()) {
  248. data->anchor_text = result.UrlElement().TextContent().Utf16();
  249. data->href = GetHref(result.UrlElement());
  250. // If we hit an image that failed to load, Blink won't give us its URL.
  251. // Fall back to walking the DOM in this case.
  252. if (absolute_image_url.is_empty())
  253. absolute_image_url = GetChildImageUrlFromElement(result.UrlElement());
  254. }
  255. PopulateHitTestData(result.AbsoluteLinkURL(), absolute_image_url,
  256. result.IsContentEditable(), data.get());
  257. GetFrameHost()->UpdateHitTestData(std::move(data));
  258. }
  259. void AwRenderFrameExt::SetInitialPageScale(double page_scale_factor) {
  260. blink::WebView* webview = GetWebView();
  261. if (!webview)
  262. return;
  263. webview->SetInitialPageScaleOverride(page_scale_factor);
  264. }
  265. void AwRenderFrameExt::SetTextZoomFactor(float zoom_factor) {
  266. // TODO(crbug.com/1085428): This will need to be set on every local root
  267. // when site isolation is used in android webview.
  268. DCHECK(render_frame()->IsMainFrame());
  269. blink::WebView* webview = GetWebView();
  270. if (!webview)
  271. return;
  272. // Hide selection and autofill popups.
  273. webview->CancelPagePopup();
  274. render_frame()->GetWebFrame()->FrameWidget()->SetTextZoomFactor(zoom_factor);
  275. }
  276. void AwRenderFrameExt::DocumentHasImage(DocumentHasImageCallback callback) {
  277. blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
  278. // DocumentHasImages Mojo message should only be sent to the outermost main
  279. // frame.
  280. DCHECK(frame);
  281. DCHECK(frame->IsOutermostMainFrame());
  282. const blink::WebElement child_img = GetImgChild(frame->GetDocument());
  283. bool has_images = !child_img.IsNull();
  284. std::move(callback).Run(has_images);
  285. }
  286. void AwRenderFrameExt::SmoothScroll(int32_t target_x,
  287. int32_t target_y,
  288. base::TimeDelta duration) {
  289. blink::WebView* webview = GetWebView();
  290. if (!webview)
  291. return;
  292. webview->SmoothScroll(target_x, target_y, duration);
  293. }
  294. void AwRenderFrameExt::ResetScrollAndScaleState() {
  295. blink::WebView* webview = GetWebView();
  296. if (!webview)
  297. return;
  298. webview->ResetScrollAndScaleState();
  299. }
  300. blink::WebView* AwRenderFrameExt::GetWebView() {
  301. if (!render_frame())
  302. return nullptr;
  303. return render_frame()->GetWebView();
  304. }
  305. blink::WebFrameWidget* AwRenderFrameExt::GetWebFrameWidget() {
  306. return render_frame()
  307. ? render_frame()->GetWebFrame()->LocalRoot()->FrameWidget()
  308. : nullptr;
  309. }
  310. void AwRenderFrameExt::OnDestruct() {
  311. delete this;
  312. }
  313. void AwRenderFrameExt::BindLocalMainFrame(
  314. mojo::PendingAssociatedReceiver<mojom::LocalMainFrame> pending_receiver) {
  315. // When bfcache is enabled, this bind can occur multiple times.
  316. // receiver should be reset before.
  317. local_main_frame_receiver_.reset();
  318. local_main_frame_receiver_.Bind(std::move(pending_receiver));
  319. }
  320. } // namespace android_webview