browser/modules/FormValidationHandler.jsm
author Gregory Szorc <gps@mozilla.com>
Mon, 08 May 2017 17:19:05 -0700
changeset 360045 a0e257e346ccf3c1db332ec5903241f4eeb9a7ee
parent 356336 d7b4d6fa41ed8accca24641ef513b6636eb31bb6
child 400794 d4a7c018420e408fbe0a13ffddd2861623fda5a7
permissions -rw-r--r--
Bug 1359965 - Support and generate tar.gz WPT archive; r=glandium Several years ago there was a single zip file for all test files. Clients would only extract the files they needed. Thus, zip was a reasonable archive format because it allowed direct access to members without having to decompress the entirety of the stream. We have since split up that monolithic archive into separate, domain-specific archives. e.g. 1 archive for mochitests and one for xpcshell tests. This drastically cut down on network I/O required on testers because they only fetched archives/data that was relevant. It also enabled parallel generation of test archives, we shaved dozens of seconds off builds due to compression being a long pole. Despite the architectural changes to test archive management, we still used zip files. This is not ideal because we no longer access specific files in test archives and thus don't care about single/partial member access performance. This commit implements support for generating tar.gz test archives. And it switches the web-platform archive to a tar.gz file. The performance implications for archive generation are significant: before: 48,321,250 bytes; 6.05s after: 31,844,267 bytes; 4.57s The size is reduced because we have a single compression context so data from 1 file can benefit compression in a subsequent file. CPU usage is reduced because the compressor has to work less with 1 context than it does with N. While I didn't measure it, decompression performance should also be improved for the same reasons. And of course network I/O will be reduced. mozharness consumers use a generic method for handling unarchiving. This method automagically handles multiple file extensions. So as long as downstream consumers aren't hard coding ".zip" this change should "just work." MozReview-Commit-ID: LQa5MIHLsms

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Chrome side handling of form validation popup.
 */

"use strict";

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

this.EXPORTED_SYMBOLS = [ "FormValidationHandler" ];

Cu.import("resource://gre/modules/Services.jsm");

var FormValidationHandler =
{
  _panel: null,
  _anchor: null,

  /*
   * Public apis
   */

  uninit() {
    this._panel = null;
    this._anchor = null;
  },

  hidePopup() {
    this._hidePopup();
  },

  /*
   * Events
   */

  // Listeners are added in nsBrowserGlue.js
  receiveMessage(aMessage) {
    let window = aMessage.target.ownerGlobal;
    let json = aMessage.json;
    let tabBrowser = window.gBrowser;
    switch (aMessage.name) {
      case "FormValidation:ShowPopup":
        // target is the <browser>, make sure we're receiving a message
        // from the foreground tab.
        if (tabBrowser && aMessage.target != tabBrowser.selectedBrowser) {
          return;
        }
        this._showPopup(window, json);
        break;
      case "FormValidation:HidePopup":
        this._hidePopup();
        break;
    }
  },

  observe(aSubject, aTopic, aData) {
    this._hidePopup();
  },

  handleEvent(aEvent) {
    switch (aEvent.type) {
      case "FullZoomChange":
      case "TextZoomChange":
      case "ZoomChangeUsingMouseWheel":
      case "scroll":
        this._hidePopup();
        break;
      case "popuphiding":
        this._onPopupHiding(aEvent);
        break;
    }
  },

  /*
   * Internal
   */

  _onPopupHiding(aEvent) {
    aEvent.originalTarget.removeEventListener("popuphiding", this, true);
    let tabBrowser = aEvent.originalTarget.ownerDocument.getElementById("content");
    tabBrowser.selectedBrowser.removeEventListener("scroll", this, true);
    tabBrowser.selectedBrowser.removeEventListener("FullZoomChange", this);
    tabBrowser.selectedBrowser.removeEventListener("TextZoomChange", this);
    tabBrowser.selectedBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);

    this._panel.hidden = true;
    this._panel = null;
    this._anchor.hidden = true;
    this._anchor = null;
  },

  /*
   * Shows the form validation popup at a specified position or updates the
   * messaging and position if the popup is already displayed.
   *
   * @aWindow - the chrome window
   * @aPanelData - Object that contains popup information
   *  aPanelData stucture detail:
   *   contentRect - the bounding client rect of the target element. If
   *    content is remote, this is relative to the browser, otherwise its
   *    relative to the window.
   *   position - popup positional string constants.
   *   message - the form element validation message text.
   */
  _showPopup(aWindow, aPanelData) {
    let previouslyShown = !!this._panel;
    this._panel = aWindow.document.getElementById("invalid-form-popup");
    this._panel.firstChild.textContent = aPanelData.message;
    this._panel.hidden = false;

    let tabBrowser = aWindow.gBrowser;
    this._anchor = tabBrowser.popupAnchor;
    this._anchor.left = aPanelData.contentRect.left;
    this._anchor.top = aPanelData.contentRect.top;
    this._anchor.width = aPanelData.contentRect.width;
    this._anchor.height = aPanelData.contentRect.height;
    this._anchor.hidden = false;

    // Display the panel if it isn't already visible.
    if (!previouslyShown) {
      // Cleanup after the popup is hidden
      this._panel.addEventListener("popuphiding", this, true);

      // Hide if the user scrolls the page
      tabBrowser.selectedBrowser.addEventListener("scroll", this, true);
      tabBrowser.selectedBrowser.addEventListener("FullZoomChange", this);
      tabBrowser.selectedBrowser.addEventListener("TextZoomChange", this);
      tabBrowser.selectedBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);

      // Open the popup
      this._panel.openPopup(this._anchor, aPanelData.position, 0, 0, false);
    }
  },

  /*
   * Hide the popup if currently displayed. Will fire an event to onPopupHiding
   * above if visible.
   */
  _hidePopup() {
    if (this._panel) {
      this._panel.hidePopup();
    }
  }
};