1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/content/contenthandlers/Content.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,590 @@ 1.4 +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// This stays here because otherwise it's hard to tell if there's a parsing error 1.10 +dump("### Content.js loaded\n"); 1.11 + 1.12 +let Cc = Components.classes; 1.13 +let Ci = Components.interfaces; 1.14 +let Cu = Components.utils; 1.15 +let Cr = Components.results; 1.16 + 1.17 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyGetter(this, "Services", function() { 1.20 + Cu.import("resource://gre/modules/Services.jsm"); 1.21 + return Services; 1.22 +}); 1.23 + 1.24 +XPCOMUtils.defineLazyGetter(this, "Rect", function() { 1.25 + Cu.import("resource://gre/modules/Geometry.jsm"); 1.26 + return Rect; 1.27 +}); 1.28 + 1.29 +XPCOMUtils.defineLazyGetter(this, "Point", function() { 1.30 + Cu.import("resource://gre/modules/Geometry.jsm"); 1.31 + return Point; 1.32 +}); 1.33 + 1.34 +XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent", 1.35 + "resource://gre/modules/LoginManagerContent.jsm"); 1.36 + 1.37 +XPCOMUtils.defineLazyServiceGetter(this, "gFocusManager", 1.38 + "@mozilla.org/focus-manager;1", "nsIFocusManager"); 1.39 + 1.40 +XPCOMUtils.defineLazyServiceGetter(this, "gDOMUtils", 1.41 + "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils"); 1.42 + 1.43 +this.XULDocument = Ci.nsIDOMXULDocument; 1.44 +this.HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement; 1.45 +this.HTMLIFrameElement = Ci.nsIDOMHTMLIFrameElement; 1.46 +this.HTMLFrameElement = Ci.nsIDOMHTMLFrameElement; 1.47 +this.HTMLFrameSetElement = Ci.nsIDOMHTMLFrameSetElement; 1.48 +this.HTMLSelectElement = Ci.nsIDOMHTMLSelectElement; 1.49 +this.HTMLOptionElement = Ci.nsIDOMHTMLOptionElement; 1.50 + 1.51 +const kReferenceDpi = 240; // standard "pixel" size used in some preferences 1.52 + 1.53 +const kStateActive = 0x00000001; // :active pseudoclass for elements 1.54 + 1.55 +const kZoomToElementMargin = 16; // in px 1.56 + 1.57 +/* 1.58 + * getBoundingContentRect 1.59 + * 1.60 + * @param aElement 1.61 + * @return Bounding content rect adjusted for scroll and frame offsets. 1.62 + */ 1.63 +function getBoundingContentRect(aElement) { 1.64 + if (!aElement) 1.65 + return new Rect(0, 0, 0, 0); 1.66 + 1.67 + let document = aElement.ownerDocument; 1.68 + while(document.defaultView.frameElement) 1.69 + document = document.defaultView.frameElement.ownerDocument; 1.70 + 1.71 + let offset = ContentScroll.getScrollOffset(content); 1.72 + offset = new Point(offset.x, offset.y); 1.73 + 1.74 + let r = aElement.getBoundingClientRect(); 1.75 + 1.76 + // step out of iframes and frames, offsetting scroll values 1.77 + let view = aElement.ownerDocument.defaultView; 1.78 + for (let frame = view; frame != content; frame = frame.parent) { 1.79 + // adjust client coordinates' origin to be top left of iframe viewport 1.80 + let rect = frame.frameElement.getBoundingClientRect(); 1.81 + let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth; 1.82 + let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth; 1.83 + offset.add(rect.left + parseInt(left), rect.top + parseInt(top)); 1.84 + } 1.85 + 1.86 + return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height); 1.87 +} 1.88 +this.getBoundingContentRect = getBoundingContentRect; 1.89 + 1.90 +/* 1.91 + * getOverflowContentBoundingRect 1.92 + * 1.93 + * @param aElement 1.94 + * @return Bounding content rect adjusted for scroll and frame offsets. 1.95 + */ 1.96 + 1.97 +function getOverflowContentBoundingRect(aElement) { 1.98 + let r = getBoundingContentRect(aElement); 1.99 + 1.100 + // If the overflow is hidden don't bother calculating it 1.101 + let computedStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement); 1.102 + let blockDisplays = ["block", "inline-block", "list-item"]; 1.103 + if ((blockDisplays.indexOf(computedStyle.getPropertyValue("display")) != -1 && 1.104 + computedStyle.getPropertyValue("overflow") == "hidden") || 1.105 + aElement instanceof HTMLSelectElement) { 1.106 + return r; 1.107 + } 1.108 + 1.109 + for (let i = 0; i < aElement.childElementCount; i++) { 1.110 + r = r.union(getBoundingContentRect(aElement.children[i])); 1.111 + } 1.112 + 1.113 + return r; 1.114 +} 1.115 +this.getOverflowContentBoundingRect = getOverflowContentBoundingRect; 1.116 + 1.117 +/* 1.118 + * Content 1.119 + * 1.120 + * Browser event receiver for content. 1.121 + */ 1.122 +let Content = { 1.123 + _debugEvents: false, 1.124 + 1.125 + get formAssistant() { 1.126 + delete this.formAssistant; 1.127 + return this.formAssistant = new FormAssistant(); 1.128 + }, 1.129 + 1.130 + init: function init() { 1.131 + // Asyncronous messages sent from the browser 1.132 + addMessageListener("Browser:Blur", this); 1.133 + addMessageListener("Browser:SaveAs", this); 1.134 + addMessageListener("Browser:MozApplicationCache:Fetch", this); 1.135 + addMessageListener("Browser:SetCharset", this); 1.136 + addMessageListener("Browser:CanUnload", this); 1.137 + addMessageListener("Browser:PanBegin", this); 1.138 + addMessageListener("Gesture:SingleTap", this); 1.139 + addMessageListener("Gesture:DoubleTap", this); 1.140 + 1.141 + addEventListener("touchstart", this, false); 1.142 + addEventListener("click", this, true); 1.143 + addEventListener("keydown", this); 1.144 + addEventListener("keyup", this); 1.145 + 1.146 + // Synchronous events caught during the bubbling phase 1.147 + addEventListener("MozApplicationManifest", this, false); 1.148 + addEventListener("DOMContentLoaded", this, false); 1.149 + addEventListener("DOMAutoComplete", this, false); 1.150 + addEventListener("DOMFormHasPassword", this, false); 1.151 + addEventListener("blur", this, false); 1.152 + // Attach a listener to watch for "click" events bubbling up from error 1.153 + // pages and other similar page. This lets us fix bugs like 401575 which 1.154 + // require error page UI to do privileged things, without letting error 1.155 + // pages have any privilege themselves. 1.156 + addEventListener("click", this, false); 1.157 + 1.158 + docShell.useGlobalHistory = true; 1.159 + }, 1.160 + 1.161 + /******************************************* 1.162 + * Events 1.163 + */ 1.164 + 1.165 + handleEvent: function handleEvent(aEvent) { 1.166 + if (this._debugEvents) Util.dumpLn("Content:", aEvent.type); 1.167 + switch (aEvent.type) { 1.168 + case "MozApplicationManifest": { 1.169 + let doc = aEvent.originalTarget; 1.170 + sendAsyncMessage("Browser:MozApplicationManifest", { 1.171 + location: doc.documentURIObject.spec, 1.172 + manifest: doc.documentElement.getAttribute("manifest"), 1.173 + charset: doc.characterSet 1.174 + }); 1.175 + break; 1.176 + } 1.177 + 1.178 + case "keyup": 1.179 + // If after a key is pressed we still have no input, then close 1.180 + // the autocomplete. Perhaps the user used backspace or delete. 1.181 + // Allow down arrow to trigger autofill popup on empty input. 1.182 + if ((!aEvent.target.value && aEvent.keyCode != aEvent.DOM_VK_DOWN) 1.183 + || aEvent.keyCode == aEvent.DOM_VK_ESCAPE) 1.184 + this.formAssistant.close(); 1.185 + else 1.186 + this.formAssistant.open(aEvent.target, aEvent); 1.187 + break; 1.188 + 1.189 + case "click": 1.190 + // Workaround for bug 925457: we sometimes don't recognize the 1.191 + // correct tap target or are unable to identify if it's editable. 1.192 + // Instead always save tap co-ordinates for the keyboard to look for 1.193 + // when it is up. 1.194 + SelectionHandler.onClickCoords(aEvent.clientX, aEvent.clientY); 1.195 + 1.196 + if (aEvent.eventPhase == aEvent.BUBBLING_PHASE) 1.197 + this._onClickBubble(aEvent); 1.198 + else 1.199 + this._onClickCapture(aEvent); 1.200 + break; 1.201 + 1.202 + case "DOMFormHasPassword": 1.203 + LoginManagerContent.onFormPassword(aEvent); 1.204 + break; 1.205 + 1.206 + case "DOMContentLoaded": 1.207 + this._maybeNotifyErrorPage(); 1.208 + break; 1.209 + 1.210 + case "DOMAutoComplete": 1.211 + case "blur": 1.212 + LoginManagerContent.onUsernameInput(aEvent); 1.213 + break; 1.214 + 1.215 + case "touchstart": 1.216 + this._onTouchStart(aEvent); 1.217 + break; 1.218 + } 1.219 + }, 1.220 + 1.221 + receiveMessage: function receiveMessage(aMessage) { 1.222 + if (this._debugEvents) Util.dumpLn("Content:", aMessage.name); 1.223 + let json = aMessage.json; 1.224 + let x = json.x; 1.225 + let y = json.y; 1.226 + 1.227 + switch (aMessage.name) { 1.228 + case "Browser:Blur": 1.229 + gFocusManager.clearFocus(content); 1.230 + break; 1.231 + 1.232 + case "Browser:CanUnload": 1.233 + let canUnload = docShell.contentViewer.permitUnload(); 1.234 + sendSyncMessage("Browser:CanUnload:Return", { permit: canUnload }); 1.235 + break; 1.236 + 1.237 + case "Browser:SaveAs": 1.238 + break; 1.239 + 1.240 + case "Browser:MozApplicationCache:Fetch": { 1.241 + let currentURI = Services.io.newURI(json.location, json.charset, null); 1.242 + let manifestURI = Services.io.newURI(json.manifest, json.charset, currentURI); 1.243 + let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"] 1.244 + .getService(Ci.nsIOfflineCacheUpdateService); 1.245 + updateService.scheduleUpdate(manifestURI, currentURI, content); 1.246 + break; 1.247 + } 1.248 + 1.249 + case "Browser:SetCharset": { 1.250 + docShell.gatherCharsetMenuTelemetry(); 1.251 + docShell.charset = json.charset; 1.252 + 1.253 + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.254 + webNav.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); 1.255 + break; 1.256 + } 1.257 + 1.258 + case "Browser:PanBegin": 1.259 + this._cancelTapHighlight(); 1.260 + break; 1.261 + 1.262 + case "Gesture:SingleTap": 1.263 + this._onSingleTap(json.x, json.y, json.modifiers); 1.264 + break; 1.265 + 1.266 + case "Gesture:DoubleTap": 1.267 + this._onDoubleTap(json.x, json.y); 1.268 + break; 1.269 + } 1.270 + }, 1.271 + 1.272 + /****************************************************** 1.273 + * Event handlers 1.274 + */ 1.275 + 1.276 + _onTouchStart: function _onTouchStart(aEvent) { 1.277 + let element = aEvent.target; 1.278 + 1.279 + // There is no need to have a feedback for disabled element 1.280 + let isDisabled = element instanceof HTMLOptionElement ? 1.281 + (element.disabled || element.parentNode.disabled) : element.disabled; 1.282 + if (isDisabled) 1.283 + return; 1.284 + 1.285 + // Set the target element to active 1.286 + this._doTapHighlight(element); 1.287 + }, 1.288 + 1.289 + _onClickCapture: function _onClickCapture(aEvent) { 1.290 + let element = aEvent.target; 1.291 + 1.292 + ContextMenuHandler.reset(); 1.293 + 1.294 + // Only show autocomplete after the item is clicked 1.295 + if (!this.lastClickElement || this.lastClickElement != element) { 1.296 + this.lastClickElement = element; 1.297 + if (aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE && 1.298 + !(element instanceof HTMLSelectElement)) { 1.299 + return; 1.300 + } 1.301 + } 1.302 + 1.303 + this.formAssistant.focusSync = true; 1.304 + this.formAssistant.open(element, aEvent); 1.305 + this._cancelTapHighlight(); 1.306 + this.formAssistant.focusSync = false; 1.307 + 1.308 + // A tap on a form input triggers touch input caret selection 1.309 + if (Util.isEditable(element) && 1.310 + aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) { 1.311 + let { offsetX, offsetY } = Util.translateToTopLevelWindow(element); 1.312 + sendAsyncMessage("Content:SelectionCaret", { 1.313 + xPos: aEvent.clientX + offsetX, 1.314 + yPos: aEvent.clientY + offsetY 1.315 + }); 1.316 + } else { 1.317 + SelectionHandler.closeSelection(); 1.318 + } 1.319 + }, 1.320 + 1.321 + // Checks clicks we care about - events bubbling up from about pages. 1.322 + _onClickBubble: function _onClickBubble(aEvent) { 1.323 + // Don't trust synthetic events 1.324 + if (!aEvent.isTrusted) 1.325 + return; 1.326 + 1.327 + let ot = aEvent.originalTarget; 1.328 + let errorDoc = ot.ownerDocument; 1.329 + if (!errorDoc) 1.330 + return; 1.331 + 1.332 + // If the event came from an ssl error page, it is probably either 1.333 + // "Add Exception…" or "Get me out of here!" button. 1.334 + if (/^about:certerror\?e=nssBadCert/.test(errorDoc.documentURI)) { 1.335 + let perm = errorDoc.getElementById("permanentExceptionButton"); 1.336 + let temp = errorDoc.getElementById("temporaryExceptionButton"); 1.337 + if (ot == temp || ot == perm) { 1.338 + let action = (ot == perm ? "permanent" : "temporary"); 1.339 + sendAsyncMessage("Browser:CertException", 1.340 + { url: errorDoc.location.href, action: action }); 1.341 + } else if (ot == errorDoc.getElementById("getMeOutOfHereButton")) { 1.342 + sendAsyncMessage("Browser:CertException", 1.343 + { url: errorDoc.location.href, action: "leave" }); 1.344 + } 1.345 + } else if (/^about:blocked/.test(errorDoc.documentURI)) { 1.346 + // The event came from a button on a malware/phishing block page 1.347 + // First check whether it's malware or phishing, so that we can 1.348 + // use the right strings/links. 1.349 + let isMalware = /e=malwareBlocked/.test(errorDoc.documentURI); 1.350 + 1.351 + if (ot == errorDoc.getElementById("getMeOutButton")) { 1.352 + sendAsyncMessage("Browser:BlockedSite", 1.353 + { url: errorDoc.location.href, action: "leave" }); 1.354 + } else if (ot == errorDoc.getElementById("reportButton")) { 1.355 + // This is the "Why is this site blocked" button. For malware, 1.356 + // we can fetch a site-specific report, for phishing, we redirect 1.357 + // to the generic page describing phishing protection. 1.358 + let action = isMalware ? "report-malware" : "report-phishing"; 1.359 + sendAsyncMessage("Browser:BlockedSite", 1.360 + { url: errorDoc.location.href, action: action }); 1.361 + } else if (ot == errorDoc.getElementById("ignoreWarningButton")) { 1.362 + // Allow users to override and continue through to the site, 1.363 + // but add a notify bar as a reminder, so that they don't lose 1.364 + // track after, e.g., tab switching. 1.365 + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.366 + webNav.loadURI(content.location, 1.367 + Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, 1.368 + null, null, null); 1.369 + } 1.370 + } 1.371 + }, 1.372 + 1.373 + _onSingleTap: function (aX, aY, aModifiers) { 1.374 + let utils = Util.getWindowUtils(content); 1.375 + for (let type of ["mousemove", "mousedown", "mouseup"]) { 1.376 + utils.sendMouseEventToWindow(type, aX, aY, 0, 1, aModifiers, true, 1.0, 1.377 + Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH); 1.378 + } 1.379 + }, 1.380 + 1.381 + _onDoubleTap: function (aX, aY) { 1.382 + let { element } = Content.getCurrentWindowAndOffset(aX, aY); 1.383 + while (element && !this._shouldZoomToElement(element)) { 1.384 + element = element.parentNode; 1.385 + } 1.386 + 1.387 + if (!element) { 1.388 + this._zoomOut(); 1.389 + } else { 1.390 + this._zoomToElement(element); 1.391 + } 1.392 + }, 1.393 + 1.394 + /****************************************************** 1.395 + * Zoom utilities 1.396 + */ 1.397 + _zoomOut: function() { 1.398 + let rect = new Rect(0,0,0,0); 1.399 + this._zoomToRect(rect); 1.400 + }, 1.401 + 1.402 + _zoomToElement: function(aElement) { 1.403 + let rect = getBoundingContentRect(aElement); 1.404 + this._inflateRect(rect, kZoomToElementMargin); 1.405 + this._zoomToRect(rect); 1.406 + }, 1.407 + 1.408 + _inflateRect: function(aRect, aMargin) { 1.409 + aRect.left -= aMargin; 1.410 + aRect.top -= aMargin; 1.411 + aRect.bottom += aMargin; 1.412 + aRect.right += aMargin; 1.413 + }, 1.414 + 1.415 + _zoomToRect: function (aRect) { 1.416 + let utils = Util.getWindowUtils(content); 1.417 + let viewId = utils.getViewId(content.document.documentElement); 1.418 + let presShellId = {}; 1.419 + utils.getPresShellId(presShellId); 1.420 + sendAsyncMessage("Content:ZoomToRect", { 1.421 + rect: aRect, 1.422 + presShellId: presShellId.value, 1.423 + viewId: viewId, 1.424 + }); 1.425 + }, 1.426 + 1.427 + _shouldZoomToElement: function(aElement) { 1.428 + let win = aElement.ownerDocument.defaultView; 1.429 + if (win.getComputedStyle(aElement, null).display == "inline") { 1.430 + return false; 1.431 + } 1.432 + else if (aElement instanceof Ci.nsIDOMHTMLLIElement) { 1.433 + return false; 1.434 + } 1.435 + else if (aElement instanceof Ci.nsIDOMHTMLQuoteElement) { 1.436 + return false; 1.437 + } 1.438 + else { 1.439 + return true; 1.440 + } 1.441 + }, 1.442 + 1.443 + 1.444 + /****************************************************** 1.445 + * General utilities 1.446 + */ 1.447 + 1.448 + /* 1.449 + * Retrieve the total offset from the window's origin to the sub frame 1.450 + * element including frame and scroll offsets. The resulting offset is 1.451 + * such that: 1.452 + * sub frame coords + offset = root frame position 1.453 + */ 1.454 + getCurrentWindowAndOffset: function(x, y) { 1.455 + // If the element at the given point belongs to another document (such 1.456 + // as an iframe's subdocument), the element in the calling document's 1.457 + // DOM (e.g. the iframe) is returned. 1.458 + let utils = Util.getWindowUtils(content); 1.459 + let element = utils.elementFromPoint(x, y, true, false); 1.460 + let offset = { x:0, y:0 }; 1.461 + 1.462 + while (element && (element instanceof HTMLIFrameElement || 1.463 + element instanceof HTMLFrameElement)) { 1.464 + // get the child frame position in client coordinates 1.465 + let rect = element.getBoundingClientRect(); 1.466 + 1.467 + // calculate offsets for digging down into sub frames 1.468 + // using elementFromPoint: 1.469 + 1.470 + // Get the content scroll offset in the child frame 1.471 + scrollOffset = ContentScroll.getScrollOffset(element.contentDocument.defaultView); 1.472 + // subtract frame and scroll offset from our elementFromPoint coordinates 1.473 + x -= rect.left + scrollOffset.x; 1.474 + y -= rect.top + scrollOffset.y; 1.475 + 1.476 + // calculate offsets we'll use to translate to client coords: 1.477 + 1.478 + // add frame client offset to our total offset result 1.479 + offset.x += rect.left; 1.480 + offset.y += rect.top; 1.481 + 1.482 + // get the frame's nsIDOMWindowUtils 1.483 + utils = element.contentDocument 1.484 + .defaultView 1.485 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.486 + .getInterface(Ci.nsIDOMWindowUtils); 1.487 + 1.488 + // retrieve the target element in the sub frame at x, y 1.489 + element = utils.elementFromPoint(x, y, true, false); 1.490 + } 1.491 + 1.492 + if (!element) 1.493 + return {}; 1.494 + 1.495 + return { 1.496 + element: element, 1.497 + contentWindow: element.ownerDocument.defaultView, 1.498 + offset: offset, 1.499 + utils: utils 1.500 + }; 1.501 + }, 1.502 + 1.503 + 1.504 + _maybeNotifyErrorPage: function _maybeNotifyErrorPage() { 1.505 + // Notify browser that an error page is being shown instead 1.506 + // of the target location. Necessary to get proper thumbnail 1.507 + // updates on chrome for error pages. 1.508 + if (content.location.href !== content.document.documentURI) 1.509 + sendAsyncMessage("Browser:ErrorPage", null); 1.510 + }, 1.511 + 1.512 + _highlightElement: null, 1.513 + 1.514 + _doTapHighlight: function _doTapHighlight(aElement) { 1.515 + gDOMUtils.setContentState(aElement, kStateActive); 1.516 + this._highlightElement = aElement; 1.517 + }, 1.518 + 1.519 + _cancelTapHighlight: function _cancelTapHighlight(aElement) { 1.520 + gDOMUtils.setContentState(content.document.documentElement, kStateActive); 1.521 + this._highlightElement = null; 1.522 + }, 1.523 +}; 1.524 + 1.525 +Content.init(); 1.526 + 1.527 +var FormSubmitObserver = { 1.528 + init: function init(){ 1.529 + addMessageListener("Browser:TabOpen", this); 1.530 + addMessageListener("Browser:TabClose", this); 1.531 + 1.532 + addEventListener("pageshow", this, false); 1.533 + 1.534 + Services.obs.addObserver(this, "invalidformsubmit", false); 1.535 + }, 1.536 + 1.537 + handleEvent: function handleEvent(aEvent) { 1.538 + let target = aEvent.originalTarget; 1.539 + let isRootDocument = (target == content.document || target.ownerDocument == content.document); 1.540 + if (!isRootDocument) 1.541 + return; 1.542 + 1.543 + // Reset invalid submit state on each pageshow 1.544 + if (aEvent.type == "pageshow") 1.545 + Content.formAssistant.invalidSubmit = false; 1.546 + }, 1.547 + 1.548 + receiveMessage: function receiveMessage(aMessage) { 1.549 + let json = aMessage.json; 1.550 + switch (aMessage.name) { 1.551 + case "Browser:TabOpen": 1.552 + Services.obs.addObserver(this, "formsubmit", false); 1.553 + break; 1.554 + case "Browser:TabClose": 1.555 + Services.obs.removeObserver(this, "formsubmit"); 1.556 + break; 1.557 + } 1.558 + }, 1.559 + 1.560 + notify: function notify(aFormElement, aWindow, aActionURI, aCancelSubmit) { 1.561 + // Do not notify unless this is the window where the submit occurred 1.562 + if (aWindow == content) 1.563 + // We don't need to send any data along 1.564 + sendAsyncMessage("Browser:FormSubmit", {}); 1.565 + }, 1.566 + 1.567 + notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) { 1.568 + if (!aInvalidElements.length) 1.569 + return; 1.570 + 1.571 + let element = aInvalidElements.queryElementAt(0, Ci.nsISupports); 1.572 + if (!(element instanceof HTMLInputElement || 1.573 + element instanceof HTMLTextAreaElement || 1.574 + element instanceof HTMLSelectElement || 1.575 + element instanceof HTMLButtonElement)) { 1.576 + return; 1.577 + } 1.578 + 1.579 + Content.formAssistant.invalidSubmit = true; 1.580 + Content.formAssistant.open(element); 1.581 + }, 1.582 + 1.583 + QueryInterface : function(aIID) { 1.584 + if (!aIID.equals(Ci.nsIFormSubmitObserver) && 1.585 + !aIID.equals(Ci.nsISupportsWeakReference) && 1.586 + !aIID.equals(Ci.nsISupports)) 1.587 + throw Cr.NS_ERROR_NO_INTERFACE; 1.588 + return this; 1.589 + } 1.590 +}; 1.591 +this.Content = Content; 1.592 + 1.593 +FormSubmitObserver.init();