browser/metro/base/content/bindings/browser.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 let Cc = Components.classes;
     7 let Ci = Components.interfaces;
     8 let Cu = Components.utils;
    10 Cu.import("resource://gre/modules/Services.jsm");
    11 Cu.import("resource://gre/modules/FormData.jsm");
    12 Cu.import("resource://gre/modules/ScrollPosition.jsm");
    13 Cu.import("resource://gre/modules/Timer.jsm", this);
    15 let WebProgressListener = {
    16   _lastLocation: null,
    17   _firstPaint: false,
    19   init: function() {
    20     let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
    21                 Ci.nsIWebProgress.NOTIFY_SECURITY |
    22                 Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
    23                 Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
    24                 Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
    26     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
    27     webProgress.addProgressListener(this, flags);
    28   },
    30   onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
    31     if (content != aWebProgress.DOMWindow)
    32       return;
    34     sendAsyncMessage("Content:StateChange", {
    35       contentWindowId: this.contentWindowId,
    36       stateFlags: aStateFlags,
    37       status: aStatus
    38     });
    39   },
    41   onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
    42     if (content != aWebProgress.DOMWindow)
    43       return;
    45     let spec = aLocationURI ? aLocationURI.spec : "";
    46     let location = spec.split("#")[0];
    48     let charset = content.document.characterSet;
    50     sendAsyncMessage("Content:LocationChange", {
    51       contentWindowId: this.contentWindowId,
    52       documentURI:     aWebProgress.DOMWindow.document.documentURIObject.spec,
    53       location:        spec,
    54       canGoBack:       docShell.canGoBack,
    55       canGoForward:    docShell.canGoForward,
    56       charset:         charset.toString()
    57     });
    59     this._firstPaint = false;
    60     let self = this;
    62     // Keep track of hash changes
    63     this.hashChanged = (location == this._lastLocation);
    64     this._lastLocation = location;
    66     // When a new page is loaded fire a message for the first paint
    67     addEventListener("MozAfterPaint", function(aEvent) {
    68       removeEventListener("MozAfterPaint", arguments.callee, true);
    70       self._firstPaint = true;
    71       let scrollOffset = ContentScroll.getScrollOffset(content);
    72       sendAsyncMessage("Browser:FirstPaint", scrollOffset);
    73     }, true);
    74   },
    76   onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
    77   },
    79   onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
    80     if (content != aWebProgress.DOMWindow)
    81       return;
    83     let serialization = SecurityUI.getSSLStatusAsString();
    85     sendAsyncMessage("Content:SecurityChange", {
    86       contentWindowId: this.contentWindowId,
    87       SSLStatusAsString: serialization,
    88       state: aState
    89     });
    90   },
    92   get contentWindowId() {
    93     return content.QueryInterface(Ci.nsIInterfaceRequestor)
    94                   .getInterface(Ci.nsIDOMWindowUtils)
    95                   .currentInnerWindowID;
    96   },
    98   QueryInterface: function QueryInterface(aIID) {
    99     if (aIID.equals(Ci.nsIWebProgressListener) ||
   100         aIID.equals(Ci.nsISupportsWeakReference) ||
   101         aIID.equals(Ci.nsISupports)) {
   102         return this;
   103     }
   105     throw Components.results.NS_ERROR_NO_INTERFACE;
   106   }
   107 };
   109 WebProgressListener.init();
   112 let SecurityUI = {
   113   getSSLStatusAsString: function() {
   114     let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
   116     if (status) {
   117       let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
   118                       .getService(Ci.nsISerializationHelper);
   120       status.QueryInterface(Ci.nsISerializable);
   121       return serhelper.serializeToString(status);
   122     }
   124     return null;
   125   }
   126 };
   128 let WebNavigation =  {
   129   _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation),
   130   _timer: null,
   132   init: function() {
   133     addMessageListener("WebNavigation:GoBack", this);
   134     addMessageListener("WebNavigation:GoForward", this);
   135     addMessageListener("WebNavigation:GotoIndex", this);
   136     addMessageListener("WebNavigation:LoadURI", this);
   137     addMessageListener("WebNavigation:Reload", this);
   138     addMessageListener("WebNavigation:Stop", this);
   139   },
   141   receiveMessage: function(message) {
   142     switch (message.name) {
   143       case "WebNavigation:GoBack":
   144         this.goBack();
   145         break;
   146       case "WebNavigation:GoForward":
   147         this.goForward();
   148         break;
   149       case "WebNavigation:GotoIndex":
   150         this.gotoIndex(message);
   151         break;
   152       case "WebNavigation:LoadURI":
   153         this.loadURI(message);
   154         break;
   155       case "WebNavigation:Reload":
   156         this.reload(message);
   157         break;
   158       case "WebNavigation:Stop":
   159         this.stop(message);
   160         break;
   161     }
   162   },
   164   goBack: function() {
   165     if (this._webNavigation.canGoBack)
   166       this._webNavigation.goBack();
   167   },
   169   goForward: function() {
   170     if (this._webNavigation.canGoForward)
   171       this._webNavigation.goForward();
   172   },
   174   gotoIndex: function(message) {
   175     this._webNavigation.gotoIndex(message.index);
   176   },
   178   loadURI: function(message) {
   179     let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
   180     this._webNavigation.loadURI(message.json.uri, flags, null, null, null);
   182     let tabData = message.json;
   183     if (tabData.entries) {
   184       // We are going to load from history so kill the current load. We do not
   185       // want the load added to the history anyway. We reload after resetting history
   186       this._webNavigation.stop(this._webNavigation.STOP_ALL);
   187       this._restoreHistory(tabData, 0);
   188     }
   189   },
   191   reload: function(message) {
   192     let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
   193     this._webNavigation.reload(flags);
   194   },
   196   stop: function(message) {
   197     let flags = message.json.flags || this._webNavigation.STOP_ALL;
   198     this._webNavigation.stop(flags);
   199   },
   201   _restoreHistory: function _restoreHistory(aTabData, aCount) {
   202     // We need to wait for the sessionHistory to be initialized and there
   203     // is no good way to do this. We'll try a wait loop like desktop
   204     try {
   205       if (!this._webNavigation.sessionHistory)
   206         throw new Error();
   207     } catch (ex) {
   208       if (aCount < 10) {
   209         let self = this;
   210         this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   211         this._timer.initWithCallback(function(aTimer) {
   212           self._timer = null;
   213           self._restoreHistory(aTabData, aCount + 1);
   214         }, 100, Ci.nsITimer.TYPE_ONE_SHOT);
   215         return;
   216       }
   217     }
   219     let history = this._webNavigation.sessionHistory;
   220     if (history.count > 0)
   221       history.PurgeHistory(history.count);
   222     history.QueryInterface(Ci.nsISHistoryInternal);
   224     // helper hashes for ensuring unique frame IDs and unique document
   225     // identifiers.
   226     let idMap = { used: {} };
   227     let docIdentMap = {};
   229     for (let i = 0; i < aTabData.entries.length; i++) {
   230       if (!aTabData.entries[i].url)
   231         continue;
   232       history.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true);
   233     }
   235     // We need to force set the active history item and cause it to reload since
   236     // we stop the load above
   237     let activeIndex = (aTabData.index || aTabData.entries.length) - 1;
   238     history.getEntryAtIndex(activeIndex, true);
   239     history.QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
   240   },
   242   _deserializeHistoryEntry: function _deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) {
   243     let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry);
   245     shEntry.setURI(Services.io.newURI(aEntry.url, null, null));
   246     shEntry.setTitle(aEntry.title || aEntry.url);
   247     if (aEntry.subframe)
   248       shEntry.setIsSubFrame(aEntry.subframe || false);
   249     shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
   250     if (aEntry.contentType)
   251       shEntry.contentType = aEntry.contentType;
   252     if (aEntry.referrer)
   253       shEntry.referrerURI = Services.io.newURI(aEntry.referrer, null, null);
   255     if (aEntry.cacheKey) {
   256       let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32);
   257       cacheKey.data = aEntry.cacheKey;
   258       shEntry.cacheKey = cacheKey;
   259     }
   261     if (aEntry.ID) {
   262       // get a new unique ID for this frame (since the one from the last
   263       // start might already be in use)
   264       let id = aIdMap[aEntry.ID] || 0;
   265       if (!id) {
   266         for (id = Date.now(); id in aIdMap.used; id++);
   267         aIdMap[aEntry.ID] = id;
   268         aIdMap.used[id] = true;
   269       }
   270       shEntry.ID = id;
   271     }
   273     if (aEntry.docshellID)
   274       shEntry.docshellID = aEntry.docshellID;
   276     if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
   277       shEntry.stateData =
   278         Cc["@mozilla.org/docshell/structured-clone-container;1"].
   279         createInstance(Ci.nsIStructuredCloneContainer);
   281       shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
   282     }
   284     if (aEntry.scroll) {
   285       let scrollPos = aEntry.scroll.split(",");
   286       scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
   287       shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
   288     }
   290     let childDocIdents = {};
   291     if (aEntry.docIdentifier) {
   292       // If we have a serialized document identifier, try to find an SHEntry
   293       // which matches that doc identifier and adopt that SHEntry's
   294       // BFCacheEntry.  If we don't find a match, insert shEntry as the match
   295       // for the document identifier.
   296       let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
   297       if (!matchingEntry) {
   298         matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
   299         aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
   300       } else {
   301         shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
   302         childDocIdents = matchingEntry.childDocIdents;
   303       }
   304     }
   306     if (aEntry.owner_b64) {
   307       let ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
   308       let binaryData = atob(aEntry.owner_b64);
   309       ownerInput.setData(binaryData, binaryData.length);
   310       let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIObjectInputStream);
   311       binaryStream.setInputStream(ownerInput);
   312       try { // Catch possible deserialization exceptions
   313         shEntry.owner = binaryStream.readObject(true);
   314       } catch (ex) { dump(ex); }
   315     }
   317     if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
   318       for (let i = 0; i < aEntry.children.length; i++) {
   319         if (!aEntry.children[i].url)
   320           continue;
   322         // We're getting sessionrestore.js files with a cycle in the
   323         // doc-identifier graph, likely due to bug 698656.  (That is, we have
   324         // an entry where doc identifier A is an ancestor of doc identifier B,
   325         // and another entry where doc identifier B is an ancestor of A.)
   326         //
   327         // If we were to respect these doc identifiers, we'd create a cycle in
   328         // the SHEntries themselves, which causes the docshell to loop forever
   329         // when it looks for the root SHEntry.
   330         //
   331         // So as a hack to fix this, we restrict the scope of a doc identifier
   332         // to be a node's siblings and cousins, and pass childDocIdents, not
   333         // aDocIdents, to _deserializeHistoryEntry.  That is, we say that two
   334         // SHEntries with the same doc identifier have the same document iff
   335         // they have the same parent or their parents have the same document.
   337         shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, childDocIdents), i);
   338       }
   339     }
   341     return shEntry;
   342   },
   344   sendHistory: function sendHistory() {
   345     // We need to package up the session history and send it to the sessionstore
   346     let entries = [];
   347     let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
   348     for (let i = 0; i < history.count; i++) {
   349       let entry = this._serializeHistoryEntry(history.getEntryAtIndex(i, false));
   351       // If someone directly navigates to one of these URLs and they switch to Desktop,
   352       // we need to make the page load-able.
   353       if (entry.url == "about:home" || entry.url == "about:start") {
   354         entry.url = "about:newtab";
   355       }
   356       entries.push(entry);
   357     }
   358     let index = history.index + 1;
   359     sendAsyncMessage("Content:SessionHistory", { entries: entries, index: index });
   360   },
   362   _serializeHistoryEntry: function _serializeHistoryEntry(aEntry) {
   363     let entry = { url: aEntry.URI.spec };
   365     if (Util.isURLEmpty(entry.url)) {
   366       entry.title = Util.getEmptyURLTabTitle();
   367     } else {
   368       entry.title = aEntry.title;
   369     }
   371     if (!(aEntry instanceof Ci.nsISHEntry))
   372       return entry;
   374     let cacheKey = aEntry.cacheKey;
   375     if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && cacheKey.data != 0)
   376       entry.cacheKey = cacheKey.data;
   378     entry.ID = aEntry.ID;
   379     entry.docshellID = aEntry.docshellID;
   381     if (aEntry.referrerURI)
   382       entry.referrer = aEntry.referrerURI.spec;
   384     if (aEntry.contentType)
   385       entry.contentType = aEntry.contentType;
   387     let x = {}, y = {};
   388     aEntry.getScrollPosition(x, y);
   389     if (x.value != 0 || y.value != 0)
   390       entry.scroll = x.value + "," + y.value;
   392     if (aEntry.owner) {
   393       try {
   394         let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIObjectOutputStream);
   395         let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
   396         pipe.init(false, false, 0, 0xffffffff, null);
   397         binaryStream.setOutputStream(pipe.outputStream);
   398         binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
   399         binaryStream.close();
   401         // Now we want to read the data from the pipe's input end and encode it.
   402         let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
   403         scriptableStream.setInputStream(pipe.inputStream);
   404         let ownerBytes = scriptableStream.readByteArray(scriptableStream.available());
   405         // We can stop doing base64 encoding once our serialization into JSON
   406         // is guaranteed to handle all chars in strings, including embedded
   407         // nulls.
   408         entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
   409       } catch (e) { dump(e); }
   410     }
   412     entry.docIdentifier = aEntry.BFCacheEntry.ID;
   414     if (aEntry.stateData != null) {
   415       entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
   416       entry.structuredCloneVersion = aEntry.stateData.formatVersion;
   417     }
   419     if (!(aEntry instanceof Ci.nsISHContainer))
   420       return entry;
   422     if (aEntry.childCount > 0) {
   423       entry.children = [];
   424       for (let i = 0; i < aEntry.childCount; i++) {
   425         let child = aEntry.GetChildAt(i);
   426         if (child)
   427           entry.children.push(this._serializeHistoryEntry(child));
   428         else // to maintain the correct frame order, insert a dummy entry 
   429           entry.children.push({ url: "about:blank" });
   431         // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
   432         if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
   433           delete entry.children;
   434           break;
   435         }
   436       }
   437     }
   439     return entry;
   440   }
   441 };
   443 WebNavigation.init();
   446 let DOMEvents =  {
   447   _timeout: null,
   448   _sessionEvents: new Set(),
   449   _sessionEventMap: {"SessionStore:collectFormdata" : FormData.collect,
   450                      "SessionStore:collectScrollPosition" : ScrollPosition.collect},
   452   init: function() {
   453     addEventListener("DOMContentLoaded", this, false);
   454     addEventListener("DOMTitleChanged", this, false);
   455     addEventListener("DOMLinkAdded", this, false);
   456     addEventListener("DOMWillOpenModalDialog", this, false);
   457     addEventListener("DOMModalDialogClosed", this, true);
   458     addEventListener("DOMWindowClose", this, false);
   459     addEventListener("DOMPopupBlocked", this, false);
   460     addEventListener("pageshow", this, false);
   461     addEventListener("pagehide", this, false);
   463     addEventListener("input", this, true);
   464     addEventListener("change", this, true);
   465     addEventListener("scroll", this, true);
   466     addMessageListener("SessionStore:restoreSessionTabData", this);
   467   },
   469   receiveMessage: function(message) {
   470     switch (message.name) {
   471       case "SessionStore:restoreSessionTabData":
   472         if (message.json.formdata)
   473           FormData.restore(content, message.json.formdata);
   474         if (message.json.scroll)
   475           ScrollPosition.restore(content, message.json.scroll.scroll);
   476         break;
   477     }
   478   },
   480   handleEvent: function(aEvent) {
   481     let document = content.document;
   482     switch (aEvent.type) {
   483       case "DOMContentLoaded":
   484         if (document.documentURIObject.spec == "about:blank")
   485           return;
   487         sendAsyncMessage("DOMContentLoaded", { });
   489         // Send the session history now too
   490         WebNavigation.sendHistory();
   491         break;
   493       case "pageshow":
   494       case "pagehide": {
   495         if (aEvent.target.defaultView != content)
   496           break;
   498         let util = aEvent.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
   499                                             .getInterface(Ci.nsIDOMWindowUtils);
   501         let json = {
   502           contentWindowWidth: content.innerWidth,
   503           contentWindowHeight: content.innerHeight,
   504           windowId: util.outerWindowID,
   505           persisted: aEvent.persisted
   506         };
   508         // Clear onload focus to prevent the VKB to be shown unexpectingly
   509         // but only if the location has really changed and not only the
   510         // fragment identifier
   511         let contentWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
   512         if (!WebProgressListener.hashChanged && contentWindowID == util.currentInnerWindowID) {
   513           let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   514           focusManager.clearFocus(content);
   515         }
   517         sendAsyncMessage(aEvent.type, json);
   518         break;
   519       }
   521       case "DOMPopupBlocked": {
   522         let util = aEvent.requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor)
   523                                           .getInterface(Ci.nsIDOMWindowUtils);
   524         let json = {
   525           windowId: util.outerWindowID,
   526           popupWindowURI: {
   527             spec: aEvent.popupWindowURI.spec,
   528             charset: aEvent.popupWindowURI.originCharset
   529           },
   530           popupWindowFeatures: aEvent.popupWindowFeatures,
   531           popupWindowName: aEvent.popupWindowName
   532         };
   534         sendAsyncMessage("DOMPopupBlocked", json);
   535         break;
   536       }
   538       case "DOMTitleChanged":
   539         sendAsyncMessage("DOMTitleChanged", { title: document.title });
   540         break;
   542       case "DOMLinkAdded":
   543         let target = aEvent.originalTarget;
   544         if (!target.href || target.disabled)
   545           return;
   547         let json = {
   548           windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
   549           href: target.href,
   550           charset: document.characterSet,
   551           title: target.title,
   552           rel: target.rel,
   553           type: target.type
   554         };
   556         // rel=icon can also have a sizes attribute
   557         if (target.hasAttribute("sizes"))
   558           json.sizes = target.getAttribute("sizes");
   560         sendAsyncMessage("DOMLinkAdded", json);
   561         break;
   563       case "DOMWillOpenModalDialog":
   564       case "DOMModalDialogClosed":
   565       case "DOMWindowClose":
   566         let retvals = sendSyncMessage(aEvent.type, { });
   567         for (let i in retvals) {
   568           if (retvals[i].preventDefault) {
   569             aEvent.preventDefault();
   570             break;
   571           }
   572         }
   573         break;
   574       case "input":
   575       case "change":
   576         this._sessionEvents.add("SessionStore:collectFormdata");
   577         this._sendUpdates();
   578         break;
   579       case "scroll":
   580         this._sessionEvents.add("SessionStore:collectScrollPosition");
   581         this._sendUpdates();
   582         break;
   583     }
   584   },
   586   _sendUpdates: function() {
   587     if (!this._timeout) {
   588       // Wait a little before sending the message to batch multiple changes.
   589       this._timeout = setTimeout(function() {
   590         for (let eventType of this._sessionEvents) {
   591           sendAsyncMessage(eventType, {
   592             data: this._sessionEventMap[eventType](content)
   593           });
   594         }
   595         this._sessionEvents.clear();
   596         clearTimeout(this._timeout);
   597         this._timeout = null;
   598       }.bind(this), 1000);
   599     }
   600   }
   601 };
   603 DOMEvents.init();
   605 let ContentScroll =  {
   606   // The most recent offset set by APZC for the root scroll frame
   607   _scrollOffset: { x: 0, y: 0 },
   609   init: function() {
   610     addMessageListener("Content:SetWindowSize", this);
   612     addEventListener("pagehide", this, false);
   613     addEventListener("MozScrolledAreaChanged", this, false);
   614   },
   616   getScrollOffset: function(aWindow) {
   617     let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   618     let scrollX = {}, scrollY = {};
   619     cwu.getScrollXY(false, scrollX, scrollY);
   620     return { x: scrollX.value, y: scrollY.value };
   621   },
   623   getScrollOffsetForElement: function(aElement) {
   624     if (aElement.parentNode == aElement.ownerDocument)
   625       return this.getScrollOffset(aElement.ownerDocument.defaultView);
   626     return { x: aElement.scrollLeft, y: aElement.scrollTop };
   627   },
   629   receiveMessage: function(aMessage) {
   630     let json = aMessage.json;
   631     switch (aMessage.name) {
   632       case "Content:SetWindowSize": {
   633         let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   634         cwu.setCSSViewport(json.width, json.height);
   635         sendAsyncMessage("Content:SetWindowSize:Complete", {});
   636         break;
   637       }
   638     }
   639   },
   641   handleEvent: function(aEvent) {
   642     switch (aEvent.type) {
   643       case "pagehide":
   644         this._scrollOffset = { x: 0, y: 0 };
   645         break;
   647       case "MozScrolledAreaChanged": {
   648         let doc = aEvent.originalTarget;
   649         if (content != doc.defaultView) // We are only interested in root scroll pane changes
   650           return;
   652         sendAsyncMessage("MozScrolledAreaChanged", {
   653           width: aEvent.width,
   654           height: aEvent.height,
   655           left: aEvent.x + content.scrollX
   656         });
   658         // Send event only after painting to make sure content views in the parent process have
   659         // been updated.
   660         addEventListener("MozAfterPaint", function afterPaint() {
   661           removeEventListener("MozAfterPaint", afterPaint, false);
   662           sendAsyncMessage("Content:UpdateDisplayPort");
   663         }, false);
   665         break;
   666       }
   667     }
   668   }
   669 };
   670 this.ContentScroll = ContentScroll;
   672 ContentScroll.init();
   674 let ContentActive =  {
   675   init: function() {
   676     addMessageListener("Content:Activate", this);
   677     addMessageListener("Content:Deactivate", this);
   678   },
   680   receiveMessage: function(aMessage) {
   681     let json = aMessage.json;
   682     switch (aMessage.name) {
   683       case "Content:Deactivate":
   684         docShell.isActive = false;
   685         let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   686         if (json.keepviewport)
   687           break;
   688         cwu.setDisplayPortForElement(0, 0, 0, 0, content.document.documentElement, 0);
   689         break;
   691       case "Content:Activate":
   692         docShell.isActive = true;
   693         break;
   694     }
   695   }
   696 };
   698 ContentActive.init();
   700 /**
   701  * Helper class for IndexedDB, child part. Listens using
   702  * the observer service for events regarding IndexedDB
   703  * prompts, and sends messages to the parent to actually
   704  * show the prompts.
   705  */
   706 let IndexedDB = {
   707   _permissionsPrompt: "indexedDB-permissions-prompt",
   708   _permissionsResponse: "indexedDB-permissions-response",
   710   _quotaPrompt: "indexedDB-quota-prompt",
   711   _quotaResponse: "indexedDB-quota-response",
   712   _quotaCancel: "indexedDB-quota-cancel",
   714   waitingObservers: [],
   716   init: function IndexedDBPromptHelper_init() {
   717     let os = Services.obs;
   718     os.addObserver(this, this._permissionsPrompt, false);
   719     os.addObserver(this, this._quotaPrompt, false);
   720     os.addObserver(this, this._quotaCancel, false);
   721     addMessageListener("IndexedDB:Response", this);
   722   },
   724   observe: function IndexedDBPromptHelper_observe(aSubject, aTopic, aData) {
   725     if (aTopic != this._permissionsPrompt && aTopic != this._quotaPrompt && aTopic != this._quotaCancel) {
   726       throw new Error("Unexpected topic!");
   727     }
   729     let requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
   730     let observer = requestor.getInterface(Ci.nsIObserver);
   732     let contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
   733     let contentDocument = contentWindow.document;
   735     if (aTopic == this._quotaCancel) {
   736       observer.observe(null, this._quotaResponse, Ci.nsIPermissionManager.UNKNOWN_ACTION);
   737       return;
   738     }
   740     // Remote to parent
   741     sendAsyncMessage("IndexedDB:Prompt", {
   742       topic: aTopic,
   743       host: contentDocument.documentURIObject.asciiHost,
   744       location: contentDocument.location.toString(),
   745       data: aData,
   746       observerId: this.addWaitingObserver(observer)
   747     });
   748   },
   750   receiveMessage: function(aMessage) {
   751     let payload = aMessage.json;
   752     switch (aMessage.name) {
   753       case "IndexedDB:Response":
   754         let observer = this.getAndRemoveWaitingObserver(payload.observerId);
   755         observer.observe(null, payload.responseTopic, payload.permission);
   756     }
   757   },
   759   addWaitingObserver: function(aObserver) {
   760     let observerId = 0;
   761     while (observerId in this.waitingObservers)
   762       observerId++;
   763     this.waitingObservers[observerId] = aObserver;
   764     return observerId;
   765   },
   767   getAndRemoveWaitingObserver: function(aObserverId) {
   768     let observer = this.waitingObservers[aObserverId];
   769     delete this.waitingObservers[aObserverId];
   770     return observer;
   771   }
   772 };
   774 IndexedDB.init();

mercurial