OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 /** |
| 6 * @fileoverview APIs used by CRWContextMenuController. |
| 7 */ |
| 8 |
| 9 goog.provide('__crWeb.contextMenu'); |
| 10 |
| 11 /** Beginning of anonymous object */ |
| 12 (function() { |
| 13 |
| 14 /** |
| 15 * Returns the url of the image or link under the selected point. Returns an |
| 16 * empty string if no links or images are found. |
| 17 * @param {number} x Horizontal center of the selected point. |
| 18 * @param {number} y Vertical center of the selected point. |
| 19 * @return {!Object} An object of the form { |
| 20 * href, // URL of the link under the point |
| 21 * innerText, // innerText of the link, if the selected element is a link |
| 22 * src, // src of the image, if the selected element is an image |
| 23 * title, // title of the image, if the selected |
| 24 * referrerPolicy |
| 25 * } |
| 26 * where: |
| 27 * <ul> |
| 28 * <li>href, innerText are set if the selected element is a link. |
| 29 * <li>src, title are set if the selected element is an image. |
| 30 * <li>href is also set if the selected element is an image with a link. |
| 31 * <li>referrerPolicy is the referrer policy to use for navigations away |
| 32 * from the current page. |
| 33 * </ul> |
| 34 */ |
| 35 __gCrWeb['getElementFromPoint'] = function(x, y) { |
| 36 var hitCoordinates = spiralCoordinates_(x, y); |
| 37 for (var index = 0; index < hitCoordinates.length; index++) { |
| 38 var coordinates = hitCoordinates[index]; |
| 39 |
| 40 var element = elementFromPoint_(coordinates.x, coordinates.y); |
| 41 if (!element || !element.tagName) { |
| 42 // Nothing under the hit point. Try the next hit point. |
| 43 continue; |
| 44 } |
| 45 |
| 46 if (getComputedWebkitTouchCallout_(element) === 'none') |
| 47 continue; |
| 48 // Also check element's ancestors. A bound on the level is used here to |
| 49 // avoid large overhead when no links or images are found. |
| 50 var level = 0; |
| 51 while (++level < 8 && element && element != document) { |
| 52 var tagName = element.tagName; |
| 53 if (!tagName) |
| 54 continue; |
| 55 tagName = tagName.toLowerCase(); |
| 56 |
| 57 if (tagName === 'input' || tagName === 'textarea' || |
| 58 tagName === 'select' || tagName === 'option') { |
| 59 // If the element is a known input element, stop the spiral search and |
| 60 // return empty results. |
| 61 return {}; |
| 62 } |
| 63 |
| 64 if (tagName === 'a' && element.href) { |
| 65 // Found a link. |
| 66 return { |
| 67 href: element.href, |
| 68 referrerPolicy: getReferrerPolicy_(element), |
| 69 innerText: element.innerText |
| 70 }; |
| 71 } |
| 72 |
| 73 if (tagName === 'img' && element.src) { |
| 74 // Found an image. |
| 75 var result = { |
| 76 src: element.src, |
| 77 referrerPolicy: getReferrerPolicy_() |
| 78 }; |
| 79 // Copy the title, if any. |
| 80 if (element.title) { |
| 81 result.title = element.title; |
| 82 } |
| 83 // Check if the image is also a link. |
| 84 var parent = element.parentNode; |
| 85 while (parent) { |
| 86 if (parent.tagName && |
| 87 parent.tagName.toLowerCase() === 'a' && |
| 88 parent.href) { |
| 89 // This regex identifies strings like void(0), |
| 90 // void(0) ;void(0);, ;;;; |
| 91 // which result in a NOP when executed as JavaScript. |
| 92 var regex = RegExp("^javascript:(?:(?:void\\(0\\)|;)\\s*)+$"); |
| 93 if (parent.href.match(regex)) { |
| 94 parent = parent.parentNode; |
| 95 continue; |
| 96 } |
| 97 result.href = parent.href; |
| 98 result.referrerPolicy = getReferrerPolicy_(parent); |
| 99 break; |
| 100 } |
| 101 parent = parent.parentNode; |
| 102 } |
| 103 return result; |
| 104 } |
| 105 element = element.parentNode; |
| 106 } |
| 107 } |
| 108 return {}; |
| 109 }; |
| 110 |
| 111 /** |
| 112 * Suppresses the next click such that they are not handled by JS click |
| 113 * event handlers. |
| 114 * @type {void} |
| 115 */ |
| 116 __gCrWeb['suppressNextClick'] = function() { |
| 117 var suppressNextClick = function(evt) { |
| 118 evt.preventDefault(); |
| 119 document.removeEventListener('click', suppressNextClick, false); |
| 120 }; |
| 121 document.addEventListener('click', suppressNextClick); |
| 122 }; |
| 123 |
| 124 /** |
| 125 * Returns the margin in points around touchable elements (e.g. links for |
| 126 * custom context menu). |
| 127 * @type {number} |
| 128 */ |
| 129 __gCrWeb['getPageWidth'] = function() { |
| 130 var documentElement = document.documentElement; |
| 131 var documentBody = document.body; |
| 132 return Math.max(documentElement.clientWidth, |
| 133 documentElement.scrollWidth, |
| 134 documentElement.offsetWidth, |
| 135 documentBody.scrollWidth, |
| 136 documentBody.offsetWidth); |
| 137 }; |
| 138 |
| 139 /** |
| 140 * Implementation of document.elementFromPoint that is working for iOS4 and |
| 141 * iOS5 and that also goes into frames and iframes. |
| 142 * @private |
| 143 */ |
| 144 var elementFromPoint_ = function(x, y) { |
| 145 var elementFromPointIsUsingViewPortCoordinates = function(win) { |
| 146 if (win.pageYOffset > 0) { // Page scrolled down. |
| 147 return (win.document.elementFromPoint( |
| 148 0, win.pageYOffset + win.innerHeight - 1) === null); |
| 149 } |
| 150 if (win.pageXOffset > 0) { // Page scrolled to the right. |
| 151 return (win.document.elementFromPoint( |
| 152 win.pageXOffset + win.innerWidth - 1, 0) === null); |
| 153 } |
| 154 return false; // No scrolling, don't care. |
| 155 }; |
| 156 |
| 157 var newCoordinate = function(x, y) { |
| 158 var coordinates = { |
| 159 x: x, y: y, |
| 160 viewPortX: x - window.pageXOffset, viewPortY: y - window.pageYOffset, |
| 161 useViewPortCoordinates: false, |
| 162 window: window |
| 163 }; |
| 164 return coordinates; |
| 165 }; |
| 166 |
| 167 // Returns the coordinates of the upper left corner of |obj| in the |
| 168 // coordinates of the window that |obj| is in. |
| 169 var getPositionInWindow = function(obj) { |
| 170 var coord = { x: 0, y: 0 }; |
| 171 while (obj.offsetParent) { |
| 172 coord.x += obj.offsetLeft; |
| 173 coord.y += obj.offsetTop; |
| 174 obj = obj.offsetParent; |
| 175 } |
| 176 return coord; |
| 177 }; |
| 178 |
| 179 var elementsFromCoordinates = function(coordinates) { |
| 180 coordinates.useViewPortCoordinates = coordinates.useViewPortCoordinates || |
| 181 elementFromPointIsUsingViewPortCoordinates(coordinates.window); |
| 182 |
| 183 var currentElement = null; |
| 184 if (coordinates.useViewPortCoordinates) { |
| 185 currentElement = coordinates.window.document.elementFromPoint( |
| 186 coordinates.viewPortX, coordinates.viewPortY); |
| 187 } else { |
| 188 currentElement = coordinates.window.document.elementFromPoint( |
| 189 coordinates.x, coordinates.y); |
| 190 } |
| 191 // We have to check for tagName, because if a selection is made by the |
| 192 // UIWebView, the element we will get won't have one. |
| 193 if (!currentElement || !currentElement.tagName) { |
| 194 return null; |
| 195 } |
| 196 if (currentElement.tagName.toLowerCase() === 'iframe' || |
| 197 currentElement.tagName.toLowerCase() === 'frame') { |
| 198 // The following condition is true if the iframe is in a different |
| 199 // domain; no further information is accessible. |
| 200 if (typeof(currentElement.contentWindow.document) == 'undefined') { |
| 201 return currentElement; |
| 202 } |
| 203 var framePosition = getPositionInWindow(currentElement); |
| 204 coordinates.viewPortX -= |
| 205 framePosition.x - coordinates.window.pageXOffset; |
| 206 coordinates.viewPortY -= |
| 207 framePosition.y - coordinates.window.pageYOffset; |
| 208 coordinates.window = currentElement.contentWindow; |
| 209 coordinates.x -= framePosition.x + coordinates.window.pageXOffset; |
| 210 coordinates.y -= framePosition.y + coordinates.window.pageYOffset; |
| 211 return elementsFromCoordinates(coordinates); |
| 212 } |
| 213 return currentElement; |
| 214 }; |
| 215 |
| 216 return elementsFromCoordinates(newCoordinate(x, y)); |
| 217 }; |
| 218 |
| 219 /** @private */ |
| 220 var spiralCoordinates_ = function(x, y) { |
| 221 var MAX_ANGLE = Math.PI * 2.0 * 3.0; |
| 222 var POINT_COUNT = 30; |
| 223 var ANGLE_STEP = MAX_ANGLE / POINT_COUNT; |
| 224 var TOUCH_MARGIN = 25; |
| 225 var SPEED = TOUCH_MARGIN / MAX_ANGLE; |
| 226 |
| 227 var coordinates = []; |
| 228 for (var index = 0; index < POINT_COUNT; index++) { |
| 229 var angle = ANGLE_STEP * index; |
| 230 var radius = angle * SPEED; |
| 231 |
| 232 coordinates.push({x: x + Math.round(Math.cos(angle) * radius), |
| 233 y: y + Math.round(Math.sin(angle) * radius)}); |
| 234 } |
| 235 |
| 236 return coordinates; |
| 237 }; |
| 238 |
| 239 /** @private */ |
| 240 var getComputedWebkitTouchCallout_ = function(element) { |
| 241 return window.getComputedStyle(element, null)['webkitTouchCallout']; |
| 242 }; |
| 243 |
| 244 /** |
| 245 * Gets the referrer policy to use for navigations away from the current page. |
| 246 * If a link element is passed, and it includes a rel=noreferrer tag, that |
| 247 * will override the page setting. |
| 248 * @param {HTMLElement=} opt_linkElement The link triggering the navigation. |
| 249 * @return {string} The policy string. |
| 250 * @private |
| 251 */ |
| 252 var getReferrerPolicy_ = function(opt_linkElement) { |
| 253 if (opt_linkElement) { |
| 254 var rel = opt_linkElement.getAttribute('rel'); |
| 255 if (rel && rel.toLowerCase() == 'noreferrer') { |
| 256 return 'never'; |
| 257 } |
| 258 } |
| 259 |
| 260 var metaTags = document.getElementsByTagName('meta'); |
| 261 for (var i = 0; i < metaTags.length; ++i) { |
| 262 if (metaTags[i].name.toLowerCase() == 'referrer') { |
| 263 return metaTags[i].content.toLowerCase(); |
| 264 } |
| 265 } |
| 266 return 'default'; |
| 267 }; |
| 268 |
| 269 }()); // End of anonymouse object |
OLD | NEW |