Index: ios/web/web_state/js/resources/context_menu.js |
diff --git a/ios/web/web_state/js/resources/context_menu.js b/ios/web/web_state/js/resources/context_menu.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ffd654bc361ad3ac54019befef6a87f13cc96606 |
--- /dev/null |
+++ b/ios/web/web_state/js/resources/context_menu.js |
@@ -0,0 +1,269 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+/** |
+ * @fileoverview APIs used by CRWContextMenuController. |
+ */ |
+ |
+goog.provide('__crWeb.contextMenu'); |
+ |
+/** Beginning of anonymous object */ |
+(function() { |
+ |
+ /** |
+ * Returns the url of the image or link under the selected point. Returns an |
+ * empty string if no links or images are found. |
+ * @param {number} x Horizontal center of the selected point. |
+ * @param {number} y Vertical center of the selected point. |
+ * @return {!Object} An object of the form { |
+ * href, // URL of the link under the point |
+ * innerText, // innerText of the link, if the selected element is a link |
+ * src, // src of the image, if the selected element is an image |
+ * title, // title of the image, if the selected |
+ * referrerPolicy |
+ * } |
+ * where: |
+ * <ul> |
+ * <li>href, innerText are set if the selected element is a link. |
+ * <li>src, title are set if the selected element is an image. |
+ * <li>href is also set if the selected element is an image with a link. |
+ * <li>referrerPolicy is the referrer policy to use for navigations away |
+ * from the current page. |
+ * </ul> |
+ */ |
+ __gCrWeb['getElementFromPoint'] = function(x, y) { |
+ var hitCoordinates = spiralCoordinates_(x, y); |
+ for (var index = 0; index < hitCoordinates.length; index++) { |
+ var coordinates = hitCoordinates[index]; |
+ |
+ var element = elementFromPoint_(coordinates.x, coordinates.y); |
+ if (!element || !element.tagName) { |
+ // Nothing under the hit point. Try the next hit point. |
+ continue; |
+ } |
+ |
+ if (getComputedWebkitTouchCallout_(element) === 'none') |
+ continue; |
+ // Also check element's ancestors. A bound on the level is used here to |
+ // avoid large overhead when no links or images are found. |
+ var level = 0; |
+ while (++level < 8 && element && element != document) { |
+ var tagName = element.tagName; |
+ if (!tagName) |
+ continue; |
+ tagName = tagName.toLowerCase(); |
+ |
+ if (tagName === 'input' || tagName === 'textarea' || |
+ tagName === 'select' || tagName === 'option') { |
+ // If the element is a known input element, stop the spiral search and |
+ // return empty results. |
+ return {}; |
+ } |
+ |
+ if (tagName === 'a' && element.href) { |
+ // Found a link. |
+ return { |
+ href: element.href, |
+ referrerPolicy: getReferrerPolicy_(element), |
+ innerText: element.innerText |
+ }; |
+ } |
+ |
+ if (tagName === 'img' && element.src) { |
+ // Found an image. |
+ var result = { |
+ src: element.src, |
+ referrerPolicy: getReferrerPolicy_() |
+ }; |
+ // Copy the title, if any. |
+ if (element.title) { |
+ result.title = element.title; |
+ } |
+ // Check if the image is also a link. |
+ var parent = element.parentNode; |
+ while (parent) { |
+ if (parent.tagName && |
+ parent.tagName.toLowerCase() === 'a' && |
+ parent.href) { |
+ // This regex identifies strings like void(0), |
+ // void(0) ;void(0);, ;;;; |
+ // which result in a NOP when executed as JavaScript. |
+ var regex = RegExp("^javascript:(?:(?:void\\(0\\)|;)\\s*)+$"); |
+ if (parent.href.match(regex)) { |
+ parent = parent.parentNode; |
+ continue; |
+ } |
+ result.href = parent.href; |
+ result.referrerPolicy = getReferrerPolicy_(parent); |
+ break; |
+ } |
+ parent = parent.parentNode; |
+ } |
+ return result; |
+ } |
+ element = element.parentNode; |
+ } |
+ } |
+ return {}; |
+ }; |
+ |
+ /** |
+ * Suppresses the next click such that they are not handled by JS click |
+ * event handlers. |
+ * @type {void} |
+ */ |
+ __gCrWeb['suppressNextClick'] = function() { |
+ var suppressNextClick = function(evt) { |
+ evt.preventDefault(); |
+ document.removeEventListener('click', suppressNextClick, false); |
+ }; |
+ document.addEventListener('click', suppressNextClick); |
+ }; |
+ |
+ /** |
+ * Returns the margin in points around touchable elements (e.g. links for |
+ * custom context menu). |
+ * @type {number} |
+ */ |
+ __gCrWeb['getPageWidth'] = function() { |
+ var documentElement = document.documentElement; |
+ var documentBody = document.body; |
+ return Math.max(documentElement.clientWidth, |
+ documentElement.scrollWidth, |
+ documentElement.offsetWidth, |
+ documentBody.scrollWidth, |
+ documentBody.offsetWidth); |
+ }; |
+ |
+ /** |
+ * Implementation of document.elementFromPoint that is working for iOS4 and |
+ * iOS5 and that also goes into frames and iframes. |
+ * @private |
+ */ |
+ var elementFromPoint_ = function(x, y) { |
+ var elementFromPointIsUsingViewPortCoordinates = function(win) { |
+ if (win.pageYOffset > 0) { // Page scrolled down. |
+ return (win.document.elementFromPoint( |
+ 0, win.pageYOffset + win.innerHeight - 1) === null); |
+ } |
+ if (win.pageXOffset > 0) { // Page scrolled to the right. |
+ return (win.document.elementFromPoint( |
+ win.pageXOffset + win.innerWidth - 1, 0) === null); |
+ } |
+ return false; // No scrolling, don't care. |
+ }; |
+ |
+ var newCoordinate = function(x, y) { |
+ var coordinates = { |
+ x: x, y: y, |
+ viewPortX: x - window.pageXOffset, viewPortY: y - window.pageYOffset, |
+ useViewPortCoordinates: false, |
+ window: window |
+ }; |
+ return coordinates; |
+ }; |
+ |
+ // Returns the coordinates of the upper left corner of |obj| in the |
+ // coordinates of the window that |obj| is in. |
+ var getPositionInWindow = function(obj) { |
+ var coord = { x: 0, y: 0 }; |
+ while (obj.offsetParent) { |
+ coord.x += obj.offsetLeft; |
+ coord.y += obj.offsetTop; |
+ obj = obj.offsetParent; |
+ } |
+ return coord; |
+ }; |
+ |
+ var elementsFromCoordinates = function(coordinates) { |
+ coordinates.useViewPortCoordinates = coordinates.useViewPortCoordinates || |
+ elementFromPointIsUsingViewPortCoordinates(coordinates.window); |
+ |
+ var currentElement = null; |
+ if (coordinates.useViewPortCoordinates) { |
+ currentElement = coordinates.window.document.elementFromPoint( |
+ coordinates.viewPortX, coordinates.viewPortY); |
+ } else { |
+ currentElement = coordinates.window.document.elementFromPoint( |
+ coordinates.x, coordinates.y); |
+ } |
+ // We have to check for tagName, because if a selection is made by the |
+ // UIWebView, the element we will get won't have one. |
+ if (!currentElement || !currentElement.tagName) { |
+ return null; |
+ } |
+ if (currentElement.tagName.toLowerCase() === 'iframe' || |
+ currentElement.tagName.toLowerCase() === 'frame') { |
+ // The following condition is true if the iframe is in a different |
+ // domain; no further information is accessible. |
+ if (typeof(currentElement.contentWindow.document) == 'undefined') { |
+ return currentElement; |
+ } |
+ var framePosition = getPositionInWindow(currentElement); |
+ coordinates.viewPortX -= |
+ framePosition.x - coordinates.window.pageXOffset; |
+ coordinates.viewPortY -= |
+ framePosition.y - coordinates.window.pageYOffset; |
+ coordinates.window = currentElement.contentWindow; |
+ coordinates.x -= framePosition.x + coordinates.window.pageXOffset; |
+ coordinates.y -= framePosition.y + coordinates.window.pageYOffset; |
+ return elementsFromCoordinates(coordinates); |
+ } |
+ return currentElement; |
+ }; |
+ |
+ return elementsFromCoordinates(newCoordinate(x, y)); |
+ }; |
+ |
+ /** @private */ |
+ var spiralCoordinates_ = function(x, y) { |
+ var MAX_ANGLE = Math.PI * 2.0 * 3.0; |
+ var POINT_COUNT = 30; |
+ var ANGLE_STEP = MAX_ANGLE / POINT_COUNT; |
+ var TOUCH_MARGIN = 25; |
+ var SPEED = TOUCH_MARGIN / MAX_ANGLE; |
+ |
+ var coordinates = []; |
+ for (var index = 0; index < POINT_COUNT; index++) { |
+ var angle = ANGLE_STEP * index; |
+ var radius = angle * SPEED; |
+ |
+ coordinates.push({x: x + Math.round(Math.cos(angle) * radius), |
+ y: y + Math.round(Math.sin(angle) * radius)}); |
+ } |
+ |
+ return coordinates; |
+ }; |
+ |
+ /** @private */ |
+ var getComputedWebkitTouchCallout_ = function(element) { |
+ return window.getComputedStyle(element, null)['webkitTouchCallout']; |
+ }; |
+ |
+ /** |
+ * Gets the referrer policy to use for navigations away from the current page. |
+ * If a link element is passed, and it includes a rel=noreferrer tag, that |
+ * will override the page setting. |
+ * @param {HTMLElement=} opt_linkElement The link triggering the navigation. |
+ * @return {string} The policy string. |
+ * @private |
+ */ |
+ var getReferrerPolicy_ = function(opt_linkElement) { |
+ if (opt_linkElement) { |
+ var rel = opt_linkElement.getAttribute('rel'); |
+ if (rel && rel.toLowerCase() == 'noreferrer') { |
+ return 'never'; |
+ } |
+ } |
+ |
+ var metaTags = document.getElementsByTagName('meta'); |
+ for (var i = 0; i < metaTags.length; ++i) { |
+ if (metaTags[i].name.toLowerCase() == 'referrer') { |
+ return metaTags[i].content.toLowerCase(); |
+ } |
+ } |
+ return 'default'; |
+ }; |
+ |
+}()); // End of anonymouse object |