Bug 691610 - e10s support for useDefaultIcon. r=felipe sr=smaug
authorTom Schuster <evilpies@gmail.com>
Mon, 29 Jul 2013 11:03:41 -0400
changeset 140449 4c3a590491308991db5bacae3970c61b4c704dd9
parent 140448 ebc3baa2b9cba97c036f55c409f32fb864116643
child 140450 3f5f81744326f9fd1437aefd04946e48ef785b6f
push id25030
push userryanvm@gmail.com
push dateTue, 30 Jul 2013 17:07:39 +0000
treeherdermozilla-central@129ce98f4cb2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, smaug
bugs691610
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 691610 - e10s support for useDefaultIcon. r=felipe sr=smaug
browser/base/content/tabbrowser.xml
content/html/document/src/ImageDocument.cpp
image/test/mochitest/Makefile.in
image/test/mochitest/test_ImageContentLoaded.html
toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
toolkit/content/browser-child.js
toolkit/content/widgets/browser.xml
toolkit/content/widgets/remote-browser.xml
toolkit/modules/RemoteWebProgress.jsm
uriloader/base/nsDocLoader.cpp
uriloader/base/nsIWebProgress.idl
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -581,20 +581,18 @@
                 // cancelled a pending load which would have cleared
                 // its anchor scroll detection temporary increment.
                 if (aWebProgress.isTopLevel)
                   this.mBrowser.userTypedClear += 2;
 
                 if (this._shouldShowProgress(aRequest)) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
-                    if (!gMultiProcessBrowser) {
-                      if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
-                        this.mTabBrowser.setTabTitleLoading(this.mTab);
-                    }
+                    if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
+                      this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
 
                   if (this.mTab.selected)
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
@@ -696,20 +694,19 @@
                     findBar.close();
 
                   // fix bug 253793 - turn off highlight when page changes
                   findBar.getElement("highlight").checked = false;
                 }
 
                 // Don't clear the favicon if this onLocationChange was
                 // triggered by a pushState or a replaceState.  See bug 550565.
-                if (!gMultiProcessBrowser) {
-                  if (aWebProgress.isLoadingDocument &&
-                      !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
-                    this.mBrowser.mIconURL = null;
+                if (aWebProgress.isLoadingDocument &&
+                    !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
+                  this.mBrowser.mIconURL = null;
                 }
 
                 let autocomplete = this.mTabBrowser._placesAutocomplete;
                 if (this.mBrowser.registeredOpenURI) {
                   autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
                   delete this.mBrowser.registeredOpenURI;
                 }
                 // Tabs in private windows aren't registered as "Open" so
@@ -825,40 +822,33 @@
           ]]>
         </body>
       </method>
 
       <method name="useDefaultIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
-            // Bug 691610 - e10s support for useDefaultIcon
-            if (gMultiProcessBrowser)
-              return;
-
             var browser = this.getBrowserForTab(aTab);
-            var docURIObject = browser.contentDocument.documentURIObject;
+            var documentURI = browser.documentURI;
             var icon = null;
-            if (browser.contentDocument instanceof ImageDocument) {
+
+            if (browser.imageDocument) {
               if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
                 let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
-                try {
-                  let req = browser.contentDocument.imageRequest;
-                  if (req &&
-                      req.image &&
-                      req.image.width <= sz &&
-                      req.image.height <= sz)
-                    icon = browser.currentURI;
-                } catch (e) { }
+                if (browser.imageDocument.width <= sz &&
+                    browser.imageDocument.height <= sz) {
+                  icon = browser.currentURI;
+                }
               }
             }
             // Use documentURIObject in the check for shouldLoadFavIcon so that we
             // do the right thing with about:-style error pages.  Bug 453442
-            else if (this.shouldLoadFavIcon(docURIObject)) {
-              let url = docURIObject.prePath + "/favicon.ico";
+            else if (this.shouldLoadFavIcon(documentURI)) {
+              let url = documentURI.prePath + "/favicon.ico";
               if (!this.isFailedIcon(url))
                 icon = url;
             }
             this.setIcon(aTab, icon);
           ]]>
         </body>
       </method>
 
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -49,21 +49,20 @@
 #define SITE_SPECIFIC_ZOOM "browser.zoom.siteSpecific"
 
 namespace mozilla {
 namespace dom {
  
 class ImageListener : public MediaDocumentStreamListener
 {
 public:
+  NS_DECL_NSIREQUESTOBSERVER
+
   ImageListener(ImageDocument* aDocument);
   virtual ~ImageListener();
-
-  /* nsIRequestObserver */
-  NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt);
 };
 
 ImageListener::ImageListener(ImageDocument* aDocument)
   : MediaDocumentStreamListener(aDocument)
 {
 }
 
 ImageListener::~ImageListener()
@@ -91,43 +90,53 @@ ImageListener::OnStartRequest(nsIRequest
   nsAutoCString mimeType;
   channel->GetContentType(mimeType);
 
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> channelPrincipal;
   if (secMan) {
     secMan->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
   }
-  
+
   int16_t decision = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE,
                                              channelURI,
                                              channelPrincipal,
                                              domWindow->GetFrameElementInternal(),
                                              mimeType,
                                              nullptr,
                                              &decision,
                                              nsContentUtils::GetContentPolicy(),
                                              secMan);
-                                               
+
   if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) {
     request->Cancel(NS_ERROR_CONTENT_BLOCKED);
     return NS_OK;
   }
 
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
 
   imageLoader->AddObserver(imgDoc);
   imgDoc->mObservingImageLoader = true;
   imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
 
   return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
 }
 
+NS_IMETHODIMP
+ImageListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, nsresult aStatus)
+{
+  ImageDocument* imgDoc = static_cast<ImageDocument*>(mDocument.get());
+  nsContentUtils::DispatchChromeEvent(imgDoc, static_cast<nsIDocument*>(imgDoc),
+                                      NS_LITERAL_STRING("ImageContentLoaded"),
+                                      true, true);
+  return MediaDocumentStreamListener::OnStopRequest(aRequest, aCtxt, aStatus);
+}
+
 ImageDocument::ImageDocument()
   : MediaDocument(),
     mOriginalZoomLevel(1.0)
 {
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 }
 
--- a/image/test/mochitest/Makefile.in
+++ b/image/test/mochitest/Makefile.in
@@ -64,16 +64,17 @@ MOCHITEST_FILES =   imgutils.js \
                 test_bug767779.html \
                 bug767779.sjs \
                 animated-gif_trailing-garbage.gif \
 		test_error_events.html \
 		error-early.png \
 		test_drawDiscardedImage.html \
 		short_header.gif \
 		test_short_gif_header.html \
+                test_ImageContentLoaded.html \
                 $(NULL)
 
 # Tests disabled due to intermittent orange
 # test_bug435296.html disabled - See bug 578591
 # test_bug478398.html disabled - See bug 579139
 
 MOCHITEST_CHROME_FILES = imgutils.js \
                 animationPolling.js \
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_ImageContentLoaded.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=691610
+-->
+<head>
+<title>Test for Bug 691610</title>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+    SimpleTest.waitForExplicitFinish()
+
+    SpecialPowers.addChromeEventListener("ImageContentLoaded", function () {
+       ok(true, "chrome listener was invoked");
+       SimpleTest.finish();
+    }, true);
+
+    var iframe = document.createElement("iframe");
+    iframe.src = "damon.jpg"
+    document.body.appendChild(iframe);
+    iframe.contentDocument.defaultView.addEventListener("ImageContentLoaded", function () {
+        ok(false, "should not invoke event");
+    }, true);
+</script>
+</body>
+</html>
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -92,16 +92,23 @@ nsBrowserStatusFilter::GetIsTopLevel(boo
 
 NS_IMETHODIMP
 nsBrowserStatusFilter::GetIsLoadingDocument(bool *aIsLoadingDocument)
 {
     NS_NOTREACHED("nsBrowserStatusFilter::GetIsLoadingDocument");
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+nsBrowserStatusFilter::GetLoadType(uint32_t *aLoadType)
+{
+    *aLoadType = 0;
+    NS_NOTREACHED("nsBrowserStatusFilter::GetLoadType");
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
 
 //-----------------------------------------------------------------------------
 // nsBrowserStatusFilter::nsIWebProgressListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnStateChange(nsIWebProgress *aWebProgress,
                                      nsIRequest *aRequest,
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -20,17 +20,19 @@ let WebProgressListener = {
     if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
       return null;
     return aRequest.QueryInterface(Ci.nsIChannel).URI.spec;
   },
 
   _setupJSON: function setupJSON(aWebProgress, aRequest) {
     return {
       isTopLevel: aWebProgress.isTopLevel,
-      requestURI: this._requestSpec(aRequest)
+      isLoadingDocument: aWebProgress.isLoadingDocument,
+      requestURI: this._requestSpec(aRequest),
+      loadType: aWebProgress.loadType
     };
   },
 
   _setupObjects: function setupObjects(aWebProgress) {
     let win = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindow);
     return {
       contentWindow: win,
@@ -190,8 +192,18 @@ addEventListener("DOMTitleChanged", func
   case "DOMTitleChanged":
     if (!aEvent.isTrusted || aEvent.target.defaultView != content)
       return;
 
     sendAsyncMessage("DOMTitleChanged", { title: document.title });
     break;
   }
 }, false);
+
+addEventListener("ImageContentLoaded", function (aEvent) {
+  if (content.document instanceof Ci.nsIImageDocument) {
+    let req = content.document.imageRequest;
+    if (!req.image)
+      return;
+    sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width,
+                                              height: req.image.height });
+  }
+}, false)
\ No newline at end of file
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -209,16 +209,20 @@
           ]]>
         </body>
       </method>
 
       <property name="currentURI"
                 onget="return this.webNavigation.currentURI;"
                 readonly="true"/>
 
+      <property name="documentURI"
+                onget="return this.contentDocument.documentURIObject;"
+                readonly="true"/>
+
       <property name="preferences"
                 onget="return this.mPrefs.QueryInterface(Components.interfaces.nsIPrefService);"
                 readonly="true"/>
 
       <field name="_docShell">null</field>
 
       <property name="docShell"
                 onget="return this._docShell || (this._docShell = this.boxObject.QueryInterface(Components.interfaces.nsIContainerBoxObject).docShell);"
@@ -234,16 +238,32 @@
           <![CDATA[
             if (this.docShell)
               return this.docShell.isActive = val;
             return false;
           ]]>
         </setter>
       </property>
 
+      <property name="imageDocument"
+                readonly="true">
+        <getter>
+          <![CDATA[
+            var document = this.contentDocument;
+            if (!document || !(document instanceof Ci.nsIImageDocument))
+              return null;
+
+            try {
+                return {width: document.imageRequest.image.width, height: document.imageRequest.image.height };
+            } catch (e) {}
+            return null;
+          ]]>
+        </getter>
+      </property>
+
       <property name="isRemoteBrowser"
                 onget="return (this.getAttribute('remote') == 'true');"
                 readonly="true"/>
 
       <property name="messageManager"
                 onget="return this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;"
                 readonly="true"/>
 
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -45,16 +45,22 @@
               let RemoteWebProgress = Components.utils.import(jsm, {}).RemoteWebProgress;
               this._remoteWebProgress = new RemoteWebProgress(this);
             }
             return this._remoteWebProgress;
       	  ]]>
       	</getter>
       </property>
 
+      <field name="_documentURI">null</field>
+
+      <property name="documentURI"
+                onget="return this._documentURI;"
+                readonly="true"/>
+
       <field name="_contentTitle">""</field>
 
       <property name="contentTitle"
                 onget="return this._contentTitle"
                 readonly="true"/>
 
       <field name="_characterSet">null</field>
 
@@ -67,19 +73,26 @@
       <property name="contentWindow"
                 onget="return this._contentWindow"
                 readonly="true"/>
 
       <property name="contentDocument"
                 onget="return this.contentWindow ? this.contentWindow.document : null"
                 readonly="true"/>
 
+      <field name="_imageDocument">null</field>
+
+      <property name="imageDocument"
+                onget="return this._imageDocument"
+                readonly="true"/>
+
       <constructor>
         <![CDATA[
           this.messageManager.addMessageListener("DOMTitleChanged", this);
+          this.messageManager.addMessageListener("ImageDocumentLoaded", this);
           this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true);
           this.webProgress._init();
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           this.webProgress._destroy();
@@ -89,16 +102,22 @@
       <method name="receiveMessage">
         <parameter name="aMessage"/>
         <body><![CDATA[
           let json = aMessage.json;
           switch (aMessage.name) {
             case "DOMTitleChanged":
               this._contentTitle = json.title;
               break;
+            case "ImageDocumentLoaded":
+              this._imageDocument = {
+                width: json.width,
+                height: json.height
+              };
+              break;
           }
         ]]></body>
       </method>
 
     </implementation>
 
   </binding>
 
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -6,34 +6,41 @@
 this.EXPORTED_SYMBOLS = ["RemoteWebProgress"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+function newURI(spec)
+{
+    return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
+                                                    .newURI(spec, null, null);
+}
+
 function RemoteWebProgressRequest(spec)
 {
   this.uri = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
                                                     .newURI(spec, null, null);
 }
 
 RemoteWebProgressRequest.prototype = {
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]),
 
   get URI() { return this.uri.clone(); }
 };
 
 function RemoteWebProgress(browser)
 {
   this._browser = browser;
-  this._isDocumentLoading = false;
+  this._isLoadingDocument = false;
   this._DOMWindow = null;
   this._isTopLevel = null;
+  this._loadType = 0;
   this._progressListeners = [];
 }
 
 RemoteWebProgress.prototype = {
   NOTIFY_STATE_REQUEST:  0x00000001,
   NOTIFY_STATE_DOCUMENT: 0x00000002,
   NOTIFY_STATE_NETWORK:  0x00000004,
   NOTIFY_STATE_WINDOW:   0x00000008,
@@ -51,28 +58,29 @@ RemoteWebProgress.prototype = {
     this._browser.messageManager.addMessageListener("Content:SecurityChange", this);
     this._browser.messageManager.addMessageListener("Content:StatusChange", this);
   },
 
   _destroy: function WP_Destroy() {
     this._browser = null;
   },
 
-  get isLoadingDocument() { return this._isDocumentLoading },
+  get isLoadingDocument() { return this._isLoadingDocument },
   get DOMWindow() { return this._DOMWindow; },
   get DOMWindowID() { return 0; },
   get isTopLevel() {
     // When this object is accessed directly, it's usually obtained
     // through browser.webProgress and thus represents the top-level
     // document.
     // However, during message handling it temporarily represents
     // the webProgress that generated the notification, which may or
     // may not be a toplevel frame.
     return this._isTopLevel === null ? true : this._isTopLevel;
   },
+  get loadType() { return this._loadType; },
 
   addProgressListener: function WP_AddProgressListener (aListener) {
     let listener = aListener.QueryInterface(Ci.nsIWebProgressListener);
     this._progressListeners.push(listener);
   },
 
   removeProgressListener: function WP_RemoveProgressListener (aListener) {
     this._progressListeners =
@@ -81,36 +89,40 @@ RemoteWebProgress.prototype = {
 
   _uriSpec: function (spec) {
     if (!spec)
       return null;
     return new RemoteWebProgressRequest(spec);
   },
 
   receiveMessage: function WP_ReceiveMessage(aMessage) {
+    this._isLoadingDocument = aMessage.json.isLoadingDocument;
     this._DOMWindow = aMessage.objects.DOMWindow;
     this._isTopLevel = aMessage.json.isTopLevel;
+    this._loadType = aMessage.json.loadType;
+
     this._browser._contentWindow = aMessage.objects.contentWindow;
 
     let req = this._uriSpec(aMessage.json.requestURI);
     switch (aMessage.name) {
     case "Content:StateChange":
       for each (let p in this._progressListeners) {
         p.onStateChange(this, req, aMessage.json.stateFlags, aMessage.json.status);
       }
       break;
 
     case "Content:LocationChange":
-      let loc = Cc["@mozilla.org/network/io-service;1"]
-                .getService(Ci.nsIIOService)
-                .newURI(aMessage.json.location, null, null);
+      let loc = newURI(aMessage.json.location);
+
       this._browser.webNavigation._currentURI = loc;
       this._browser.webNavigation.canGoBack = aMessage.json.canGoBack;
       this._browser.webNavigation.canGoForward = aMessage.json.canGoForward;
       this._browser._characterSet = aMessage.json.charset;
+      this._browser._documentURI = newURI(aMessage.json.documentURI);
+      this._browser._imageDocument = null;
 
       for each (let p in this._progressListeners) {
         p.onLocationChange(this, req, loc);
       }
       break;
 
     case "Content:SecurityChange":
       // Invoking this getter triggers the generation of the underlying object,
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -970,16 +970,24 @@ nsDocLoader::GetIsTopLevel(bool *aResult
 NS_IMETHODIMP
 nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
 {
   *aIsLoadingDocument = mIsLoadingDocument;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocLoader::GetLoadType(uint32_t *aLoadType)
+{
+  *aLoadType = 0;
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 int64_t nsDocLoader::GetMaxTotalProgress()
 {
   int64_t newMaxTotal = 0;
 
   uint32_t count = mChildList.Length();
   nsCOMPtr<nsIWebProgress> webProgress;
   for (uint32_t i=0; i < count; i++) 
   {
--- a/uriloader/base/nsIWebProgress.idl
+++ b/uriloader/base/nsIWebProgress.idl
@@ -21,33 +21,33 @@ interface nsIWebProgressListener;
  * instances is not made explicit by this interface, but the relationship may
  * exist in some implementations.
  *
  * A nsIWebProgressListener instance receives notifications for the
  * nsIWebProgress instance to which it added itself, and it may also receive
  * notifications from any nsIWebProgress instances that are children of that
  * nsIWebProgress instance.
  */
-[scriptable, uuid(1c3437b0-9e2c-11e2-9e96-0800200c9a66)]
+[scriptable, uuid(bd0efb3b-1c81-4fb0-b16d-576a2be48a95)]
 interface nsIWebProgress : nsISupports
 {
   /**
    * The following flags may be combined to form the aNotifyMask parameter for
    * the addProgressListener method.  They limit the set of events that are
    * delivered to an nsIWebProgressListener instance.
-   */ 
+   */
 
   /**
    * These flags indicate the state transistions to observe, corresponding to
    * nsIWebProgressListener::onStateChange.
    *
    * NOTIFY_STATE_REQUEST
    *   Only receive the onStateChange event if the aStateFlags parameter
    *   includes nsIWebProgressListener::STATE_IS_REQUEST.
-   *   
+   *
    * NOTIFY_STATE_DOCUMENT
    *   Only receive the onStateChange event if the aStateFlags parameter
    *   includes nsIWebProgressListener::STATE_IS_DOCUMENT.
    *
    * NOTIFY_STATE_NETWORK
    *   Only receive the onStateChange event if the aStateFlags parameter
    *   includes nsIWebProgressListener::STATE_IS_NETWORK.
    *
@@ -133,15 +133,21 @@ interface nsIWebProgress : nsISupports
    */
   readonly attribute nsIDOMWindow DOMWindow;
   readonly attribute uint64_t DOMWindowID;
 
   /**
    * Indicates whether DOMWindow.top == DOMWindow.
    */
   readonly attribute boolean isTopLevel;
-  
+
   /**
    * Indicates whether or not a document is currently being loaded
    * in the context of this nsIWebProgress instance.
    */
   readonly attribute boolean isLoadingDocument;
+
+  /**
+   * Contains a load type as specified by the load* constants in
+   * nsIDocShellLoadInfo.idl.
+   */
+  readonly attribute unsigned long loadType;
 };