OrientationChangeHandler.jsm 6.24 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* 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/. */

"use strict";

this.EXPORTED_SYMBOLS = ["OrientationChangeHandler"];

const Ci = Components.interfaces;
const Cu = Components.utils;

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

XPCOMUtils.defineLazyServiceGetter(Services, "embedlite",
                                    "@mozilla.org/embedlite-app-service;1",
                                    "nsIEmbedAppService");

this.OrientationChangeHandler = function OrientationChangeHandler(window) {
20 21 22 23
  this.docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIWebNavigation)
                    .QueryInterface(Ci.nsIDocShell);

24 25 26 27 28 29 30 31
  this.webProgress = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIWebProgress);
  this.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_PROGRESS |
                                             Ci.nsIWebProgress.NOTIFY_STATE_ALL);

  this._winID = Services.embedlite.getIDByWindow(window);
  this._targetWindow = Services.embedlite.getContentWindowByID(this._winID);
  // "DomContentLoaded" event listener registered in EmbedLiteOrientationChangeHandler.js
32 33 34 35 36
}

OrientationChangeHandler.prototype = {
  _targetWindow: null,
  _winID: -1,
37
  webProgress: null,
38
  docShell: null,
39

40
  lastOrientation: "unknown",
41
  orientationChangeSent: false,
42 43 44 45 46 47

  handleOrientationChange: function(evt) {
    let that = this;
    let newOrientation = that._targetWindow.screen.mozOrientation;
    let fullSwitch = (newOrientation.split("-")[0] ==
                      that.lastOrientation.split("-")[0]);
48
    that.orientationChangeSent = false;
49 50 51
    that.lastOrientation = newOrientation;

    function sendOrientationChanged() {
52
      if (that.orientationChangeSent) {
53 54 55 56 57
        return;
      }
      try {
        Services.embedlite.sendAsyncMessage(that._winID, "embed:contentOrientationChanged",
                                            JSON.stringify({
58
                                                             "orientation": that.lastOrientation
59
                                                           }));
60
        that.orientationChangeSent = true;
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
      } catch (e) {
        dump("EmbedLiteOrientationChangeHandler: Failed to report orientation change " + e + "\n")
      }
    }

    // 180deg rotation, no resize
    if (fullSwitch) {
      that._targetWindow.setTimeout(sendOrientationChanged);
      return;
    }

    that._targetWindow.addEventListener("resize", resizeThrottler, false);
    let resizeTimeout;
    function resizeThrottler() {
      // ignore resize events as long as an actualResizeHandler execution is in the queue
      if (!resizeTimeout) {
        resizeTimeout = that._targetWindow.setTimeout(function() {
          resizeTimeout = null;
          that._targetWindow.removeEventListener("resize", resizeThrottler, false);
          sendOrientationChanged();

          // The sendOrientationChanged will execute at a rate of 15fps
          // Noise should be small as we're only listening resizing after
          // orientation has changed.
        }, 66);
      }
    }

    // Fallback timeout 200ms.
    // When fullscreen video playback is running, noticed that
    // resizing doesn't take always place. This guarantees that
    // we will send the message back to chrome after 200ms. Normally
    // we go always through the resizeThrottler.
    that._targetWindow.setTimeout(sendOrientationChanged, 200);
  },

97 98 99 100 101 102
  registerOrientationChangeListener: function() {
    let window = this._targetWindow;
    window.screen.addEventListener("mozorientationchange", this, true);

    // Confirm initial orientation
    try {
103
      let updateInitialOrientation = (this._targetWindow.screen.mozOrientation != this.lastOrientation);
104
      this.lastOrientation = this._targetWindow.screen.mozOrientation;
105 106 107 108 109 110
      if (updateInitialOrientation) {
        Services.embedlite.sendAsyncMessage(this._winID, "embed:contentOrientationChanged",
                                            JSON.stringify({
                                                             "orientation": this.lastOrientation
                                                           }));
      }
111 112 113 114 115
    } catch (e) {
      dump("EmbedLiteOrientationChangeHandler: Report initial orientation " + e + "\n")
    }
  },

116 117 118 119 120 121 122 123 124
  handleEvent: function(evt) {
    let window = this._targetWindow;
    switch (evt.type) {
    case "DOMContentLoaded":
      let target = evt.originalTarget;
      // ignore on frames and other documents
      if (target != window.document) {
        return;
      }
125
      this.registerOrientationChangeListener();
126 127 128 129 130 131
      break;
    case "mozorientationchange":
      this.handleOrientationChange(evt);
      break;
    }
  },
132 133 134 135

  // nsIWebProgressListener
  onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
    if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
136
        (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) && aWebProgress.isTopLevel) {
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
      // This is a fallback registration in case content loading
      // is stopped before dom is loaded. Could happen in slow mobile networks.
      this.registerOrientationChangeListener();
    }
  },
  onLocationChange: function() {},
  onSecurityChange: function() {},
  onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress,
                             aMaxSelfProgress, aCurTotalProgress,
                             aMaxTotalProgress) {
    // Filter optimization: We're only interested about top level and we don't
    // want garbage.
    // Needed e.g. when loading raw image urls as then we don't get
    // DomContentLoaded message.
    if (aWebProgress.isTopLevel &&
        aCurTotalProgress <= aMaxTotalProgress && aMaxTotalProgress > 0 &&
        (aCurTotalProgress / aMaxTotalProgress) >= 1.0) {
        this.registerOrientationChangeListener();
    }
  },
  onStatusChange: function() { },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                            Ci.nsIDOMEventListener,
                                            Ci.nsISupportsWeakReference])
162
};