1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/content/Util.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,430 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +let Cc = Components.classes; 1.9 +let Ci = Components.interfaces; 1.10 + 1.11 +Components.utils.import("resource:///modules/ContentUtil.jsm"); 1.12 + 1.13 +let Util = { 1.14 + /* 1.15 + * General purpose utilities 1.16 + */ 1.17 + 1.18 + getWindowUtils: function getWindowUtils(aWindow) { 1.19 + return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); 1.20 + }, 1.21 + 1.22 + // Put the Mozilla networking code into a state that will kick the 1.23 + // auto-connection process. 1.24 + forceOnline: function forceOnline() { 1.25 + Services.io.offline = false; 1.26 + }, 1.27 + 1.28 + /* 1.29 + * Timing utilties 1.30 + */ 1.31 + 1.32 + // Executes aFunc after other events have been processed. 1.33 + executeSoon: function executeSoon(aFunc) { 1.34 + Services.tm.mainThread.dispatch({ 1.35 + run: function() { 1.36 + aFunc(); 1.37 + } 1.38 + }, Ci.nsIThread.DISPATCH_NORMAL); 1.39 + }, 1.40 + 1.41 + /* 1.42 + * Console printing utilities 1.43 + */ 1.44 + 1.45 + // Like dump, but each arg is handled and there's an automatic newline 1.46 + dumpLn: function dumpLn() { 1.47 + for (let i = 0; i < arguments.length; i++) 1.48 + dump(arguments[i] + " "); 1.49 + dump("\n"); 1.50 + }, 1.51 + 1.52 + /* 1.53 + * Element utilities 1.54 + */ 1.55 + 1.56 + transitionElementVisibility: function(aNodes, aVisible) { 1.57 + // accept single node or a collection of nodes 1.58 + aNodes = aNodes.length ? aNodes : [aNodes]; 1.59 + let defd = Promise.defer(); 1.60 + let pending = 0; 1.61 + Array.forEach(aNodes, function(aNode) { 1.62 + if (aVisible) { 1.63 + aNode.hidden = false; 1.64 + aNode.removeAttribute("fade"); // trigger transition to full opacity 1.65 + } else { 1.66 + aNode.setAttribute("fade", true); // trigger transition to 0 opacity 1.67 + } 1.68 + aNode.addEventListener("transitionend", function onTransitionEnd(aEvent){ 1.69 + aNode.removeEventListener("transitionend", onTransitionEnd); 1.70 + if (!aVisible) { 1.71 + aNode.hidden = true; 1.72 + } 1.73 + pending--; 1.74 + if (!pending){ 1.75 + defd.resolve(true); 1.76 + } 1.77 + }, false); 1.78 + pending++; 1.79 + }); 1.80 + return defd.promise; 1.81 + }, 1.82 + 1.83 + isTextInput: function isTextInput(aElement) { 1.84 + return ((aElement instanceof Ci.nsIDOMHTMLInputElement && 1.85 + aElement.mozIsTextField(false)) || 1.86 + aElement instanceof Ci.nsIDOMHTMLTextAreaElement); 1.87 + }, 1.88 + 1.89 + /** 1.90 + * Checks whether aElement's content can be edited either if it(or any of its 1.91 + * parents) has "contenteditable" attribute set to "true" or aElement's 1.92 + * ownerDocument is in design mode. 1.93 + */ 1.94 + isEditableContent: function isEditableContent(aElement) { 1.95 + return !!aElement && (aElement.isContentEditable || 1.96 + this.isOwnerDocumentInDesignMode(aElement)); 1.97 + 1.98 + }, 1.99 + 1.100 + isEditable: function isEditable(aElement) { 1.101 + if (!aElement) { 1.102 + return false; 1.103 + } 1.104 + 1.105 + if (this.isTextInput(aElement) || this.isEditableContent(aElement)) { 1.106 + return true; 1.107 + } 1.108 + 1.109 + // If a body element is editable and the body is the child of an 1.110 + // iframe or div we can assume this is an advanced HTML editor 1.111 + if ((aElement instanceof Ci.nsIDOMHTMLIFrameElement || 1.112 + aElement instanceof Ci.nsIDOMHTMLDivElement) && 1.113 + aElement.contentDocument && 1.114 + this.isEditableContent(aElement.contentDocument.body)) { 1.115 + return true; 1.116 + } 1.117 + 1.118 + return false; 1.119 + }, 1.120 + 1.121 + /** 1.122 + * Checks whether aElement's owner document has design mode turned on. 1.123 + */ 1.124 + isOwnerDocumentInDesignMode: function(aElement) { 1.125 + return !!aElement && !!aElement.ownerDocument && 1.126 + aElement.ownerDocument.designMode == "on"; 1.127 + }, 1.128 + 1.129 + isMultilineInput: function isMultilineInput(aElement) { 1.130 + return (aElement instanceof Ci.nsIDOMHTMLTextAreaElement); 1.131 + }, 1.132 + 1.133 + isLink: function isLink(aElement) { 1.134 + return ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) || 1.135 + (aElement instanceof Ci.nsIDOMHTMLAreaElement && aElement.href) || 1.136 + aElement instanceof Ci.nsIDOMHTMLLinkElement || 1.137 + aElement.getAttributeNS(kXLinkNamespace, "type") == "simple"); 1.138 + }, 1.139 + 1.140 + isText: function isText(aElement) { 1.141 + return (aElement instanceof Ci.nsIDOMHTMLParagraphElement || 1.142 + aElement instanceof Ci.nsIDOMHTMLDivElement || 1.143 + aElement instanceof Ci.nsIDOMHTMLLIElement || 1.144 + aElement instanceof Ci.nsIDOMHTMLPreElement || 1.145 + aElement instanceof Ci.nsIDOMHTMLHeadingElement || 1.146 + aElement instanceof Ci.nsIDOMHTMLTableCellElement || 1.147 + aElement instanceof Ci.nsIDOMHTMLBodyElement); 1.148 + }, 1.149 + 1.150 + /* 1.151 + * Rect and nsIDOMRect utilities 1.152 + */ 1.153 + 1.154 + getCleanRect: function getCleanRect() { 1.155 + return { 1.156 + left: 0, top: 0, right: 0, bottom: 0 1.157 + }; 1.158 + }, 1.159 + 1.160 + pointWithinRect: function pointWithinRect(aX, aY, aRect) { 1.161 + return (aRect.left < aX && aRect.top < aY && 1.162 + aRect.right > aX && aRect.bottom > aY); 1.163 + }, 1.164 + 1.165 + pointWithinDOMRect: function pointWithinDOMRect(aX, aY, aRect) { 1.166 + if (!aRect.width || !aRect.height) 1.167 + return false; 1.168 + return this.pointWithinRect(aX, aY, aRect); 1.169 + }, 1.170 + 1.171 + isEmptyDOMRect: function isEmptyDOMRect(aRect) { 1.172 + if ((aRect.bottom - aRect.top) <= 0 && 1.173 + (aRect.right - aRect.left) <= 0) 1.174 + return true; 1.175 + return false; 1.176 + }, 1.177 + 1.178 + // Dumps the details of a dom rect to the console 1.179 + dumpDOMRect: function dumpDOMRect(aMsg, aRect) { 1.180 + try { 1.181 + Util.dumpLn(aMsg, 1.182 + "left:" + Math.round(aRect.left) + ",", 1.183 + "top:" + Math.round(aRect.top) + ",", 1.184 + "right:" + Math.round(aRect.right) + ",", 1.185 + "bottom:" + Math.round(aRect.bottom) + ",", 1.186 + "width:" + Math.round(aRect.right - aRect.left) + ",", 1.187 + "height:" + Math.round(aRect.bottom - aRect.top) ); 1.188 + } catch (ex) { 1.189 + Util.dumpLn("dumpDOMRect:", ex.message); 1.190 + } 1.191 + }, 1.192 + 1.193 + /* 1.194 + * DownloadUtils.convertByteUnits returns [size, localized-unit-string] 1.195 + * so they are joined for a single download size string. 1.196 + */ 1.197 + getDownloadSize: function dv__getDownloadSize (aSize) { 1.198 + let [size, units] = DownloadUtils.convertByteUnits(aSize); 1.199 + if (aSize > 0) 1.200 + return size + units; 1.201 + else 1.202 + return Strings.browser.GetStringFromName("downloadsUnknownSize"); 1.203 + }, 1.204 + 1.205 + /* 1.206 + * URIs and schemes 1.207 + */ 1.208 + 1.209 + makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) { 1.210 + return Services.io.newURI(aURL, aOriginCharset, aBaseURI); 1.211 + }, 1.212 + 1.213 + makeURLAbsolute: function makeURLAbsolute(base, url) { 1.214 + // Note: makeURI() will throw if url is not a valid URI 1.215 + return this.makeURI(url, null, this.makeURI(base)).spec; 1.216 + }, 1.217 + 1.218 + isLocalScheme: function isLocalScheme(aURL) { 1.219 + return ((aURL.indexOf("about:") == 0 && 1.220 + aURL != "about:blank" && 1.221 + aURL != "about:empty" && 1.222 + aURL != "about:start") || 1.223 + aURL.indexOf("chrome:") == 0); 1.224 + }, 1.225 + 1.226 + // Don't display anything in the urlbar for these special URIs. 1.227 + isURLEmpty: function isURLEmpty(aURL) { 1.228 + return (!aURL || 1.229 + aURL == "about:blank" || 1.230 + aURL == "about:empty" || 1.231 + aURL == "about:home" || 1.232 + aURL == "about:newtab" || 1.233 + aURL.startsWith("about:newtab")); 1.234 + }, 1.235 + 1.236 + // Title to use for emptyURL tabs. 1.237 + getEmptyURLTabTitle: function getEmptyURLTabTitle() { 1.238 + let browserStrings = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.239 + 1.240 + return browserStrings.GetStringFromName("tabs.emptyTabTitle"); 1.241 + }, 1.242 + 1.243 + // Don't remember these pages in the session store. 1.244 + isURLMemorable: function isURLMemorable(aURL) { 1.245 + return !(aURL == "about:blank" || 1.246 + aURL == "about:empty" || 1.247 + aURL == "about:start"); 1.248 + }, 1.249 + 1.250 + /* 1.251 + * Math utilities 1.252 + */ 1.253 + 1.254 + clamp: function(num, min, max) { 1.255 + return Math.max(min, Math.min(max, num)); 1.256 + }, 1.257 + 1.258 + /* 1.259 + * Screen and layout utilities 1.260 + */ 1.261 + 1.262 + /* 1.263 + * translateToTopLevelWindow - Given an element potentially within 1.264 + * a subframe, calculate the offsets up to the top level browser. 1.265 + */ 1.266 + translateToTopLevelWindow: function translateToTopLevelWindow(aElement) { 1.267 + let offsetX = 0; 1.268 + let offsetY = 0; 1.269 + let element = aElement; 1.270 + while (element && 1.271 + element.ownerDocument && 1.272 + element.ownerDocument.defaultView != content) { 1.273 + element = element.ownerDocument.defaultView.frameElement; 1.274 + let rect = element.getBoundingClientRect(); 1.275 + offsetX += rect.left; 1.276 + offsetY += rect.top; 1.277 + } 1.278 + let win = null; 1.279 + if (element == aElement) 1.280 + win = content; 1.281 + else 1.282 + win = element.contentDocument.defaultView; 1.283 + return { targetWindow: win, offsetX: offsetX, offsetY: offsetY }; 1.284 + }, 1.285 + 1.286 + get displayDPI() { 1.287 + delete this.displayDPI; 1.288 + return this.displayDPI = this.getWindowUtils(window).displayDPI; 1.289 + }, 1.290 + 1.291 + /* 1.292 + * aViewHeight - the height of the viewable area in the browser 1.293 + * aRect - a bounding rectangle of a selection or element. 1.294 + * 1.295 + * return - number of pixels for the browser to be shifted up by such 1.296 + * that aRect is centered vertically within aViewHeight. 1.297 + */ 1.298 + centerElementInView: function centerElementInView(aViewHeight, aRect) { 1.299 + // If the bottom of the target bounds is higher than the new height, 1.300 + // there's no need to adjust. It will be above the keyboard. 1.301 + if (aRect.bottom <= aViewHeight) { 1.302 + return 0; 1.303 + } 1.304 + 1.305 + // height of the target element 1.306 + let targetHeight = aRect.bottom - aRect.top; 1.307 + // height of the browser view. 1.308 + let viewBottom = content.innerHeight; 1.309 + 1.310 + // If the target is shorter than the new content height, we can go ahead 1.311 + // and center it. 1.312 + if (targetHeight <= aViewHeight) { 1.313 + // Try to center the element vertically in the new content area, but 1.314 + // don't position such that the bottom of the browser view moves above 1.315 + // the top of the chrome. We purposely do not resize the browser window 1.316 + // by making it taller when trying to center elements that are near the 1.317 + // lower bounds. This would trigger reflow which can cause content to 1.318 + // shift around. 1.319 + let splitMargin = Math.round((aViewHeight - targetHeight) * .5); 1.320 + let distanceToPageBounds = viewBottom - aRect.bottom; 1.321 + let distanceFromChromeTop = aRect.bottom - aViewHeight; 1.322 + let distanceToCenter = 1.323 + distanceFromChromeTop + Math.min(distanceToPageBounds, splitMargin); 1.324 + return distanceToCenter; 1.325 + } 1.326 + }, 1.327 + 1.328 + /* 1.329 + * Local system utilities 1.330 + */ 1.331 + 1.332 + copyImageToClipboard: function Util_copyImageToClipboard(aImageLoadingContent) { 1.333 + let image = aImageLoadingContent.QueryInterface(Ci.nsIImageLoadingContent); 1.334 + if (!image) { 1.335 + Util.dumpLn("copyImageToClipboard error: image is not an nsIImageLoadingContent"); 1.336 + return; 1.337 + } 1.338 + try { 1.339 + let xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable); 1.340 + xferable.init(null); 1.341 + let imgRequest = aImageLoadingContent.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); 1.342 + let mimeType = imgRequest.mimeType; 1.343 + let imgContainer = imgRequest.image; 1.344 + let imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"].createInstance(Ci.nsISupportsInterfacePointer); 1.345 + imgPtr.data = imgContainer; 1.346 + xferable.setTransferData(mimeType, imgPtr, null); 1.347 + let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); 1.348 + clip.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard); 1.349 + } catch (e) { 1.350 + Util.dumpLn(e.message); 1.351 + } 1.352 + }, 1.353 +}; 1.354 + 1.355 + 1.356 +/* 1.357 + * Timeout 1.358 + * 1.359 + * Helper class to nsITimer that adds a little more pizazz. Callback can be an 1.360 + * object with a notify method or a function. 1.361 + */ 1.362 +Util.Timeout = function(aCallback) { 1.363 + this._callback = aCallback; 1.364 + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.365 + this._type = null; 1.366 +}; 1.367 + 1.368 +Util.Timeout.prototype = { 1.369 + // Timer callback. Don't call this manually. 1.370 + notify: function notify() { 1.371 + if (this._type == this._timer.TYPE_ONE_SHOT) 1.372 + this._type = null; 1.373 + 1.374 + if (this._callback.notify) 1.375 + this._callback.notify(); 1.376 + else 1.377 + this._callback.apply(null); 1.378 + }, 1.379 + 1.380 + // Helper function for once and interval. 1.381 + _start: function _start(aDelay, aType, aCallback) { 1.382 + if (aCallback) 1.383 + this._callback = aCallback; 1.384 + this.clear(); 1.385 + this._timer.initWithCallback(this, aDelay, aType); 1.386 + this._type = aType; 1.387 + return this; 1.388 + }, 1.389 + 1.390 + // Do the callback once. Cancels other timeouts on this object. 1.391 + once: function once(aDelay, aCallback) { 1.392 + return this._start(aDelay, this._timer.TYPE_ONE_SHOT, aCallback); 1.393 + }, 1.394 + 1.395 + // Do the callback every aDelay msecs. Cancels other timeouts on this object. 1.396 + interval: function interval(aDelay, aCallback) { 1.397 + return this._start(aDelay, this._timer.TYPE_REPEATING_SLACK, aCallback); 1.398 + }, 1.399 + 1.400 + // Clear any pending timeouts. 1.401 + clear: function clear() { 1.402 + if (this.isPending()) { 1.403 + this._timer.cancel(); 1.404 + this._type = null; 1.405 + } 1.406 + return this; 1.407 + }, 1.408 + 1.409 + // If there is a pending timeout, call it and cancel the timeout. 1.410 + flush: function flush() { 1.411 + if (this.isPending()) { 1.412 + this.notify(); 1.413 + this.clear(); 1.414 + } 1.415 + return this; 1.416 + }, 1.417 + 1.418 + // Return true if we are waiting for a callback. 1.419 + isPending: function isPending() { 1.420 + return this._type !== null; 1.421 + } 1.422 +}; 1.423 + 1.424 +// Mixin the ContentUtil module exports 1.425 +{ 1.426 + for (let name in ContentUtil) { 1.427 + let copy = ContentUtil[name]; 1.428 + if (copy !== undefined) 1.429 + Util[name] = copy; 1.430 + } 1.431 +} 1.432 + 1.433 +this.Util = Util;