implement navigator.pendingOfflineLoads. b=372969, r=biesi, r+sr=jst
authordcamp@mozilla.com
Sun, 08 Jul 2007 23:19:27 -0700
changeset 3254 7064e8c25060b5cffe08d6191840d238fbdc569e
parent 3253 550b7530ee6362927d372d1354e7f405be4f5b2d
child 3255 20e298fb0d4aafade4538e144b964d3fa105f96e
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, r
bugs372969
milestone1.9a7pre
implement navigator.pendingOfflineLoads. b=372969, r=biesi, r+sr=jst
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
content/html/document/src/nsHTMLContentSink.cpp
dom/public/idl/base/nsIDOMClientInformation.idl
dom/public/idl/offline/Makefile.in
dom/public/idl/offline/nsIDOMLoadStatus.idl
dom/public/idl/offline/nsIDOMLoadStatusEvent.idl
dom/public/idl/offline/nsIDOMLoadStatusList.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsDOMClassInfo.h
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsGlobalWindow.h
dom/src/offline/Makefile.in
dom/src/offline/nsDOMOfflineLoadStatusList.cpp
dom/src/offline/nsDOMOfflineLoadStatusList.h
dom/src/offline/nsDOMOfflineResourceList.cpp
dom/tests/mochitest/ajax/offline/Makefile.in
dom/tests/mochitest/ajax/offline/test_pendingOfflineLoads.html
uriloader/prefetch/nsIPrefetchService.idl
uriloader/prefetch/nsPrefetchService.cpp
uriloader/prefetch/nsPrefetchService.h
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -668,24 +668,24 @@ nsContentSink::ProcessLink(nsIContent* a
 {
   // XXX seems overkill to generate this string array
   nsStringArray linkTypes;
   nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
 
   PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
   // prefetch href if relation is "next" or "prefetch"
   if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
-    PrefetchHref(aHref, hasPrefetch, PR_FALSE);
+    PrefetchHref(aHref, aElement, hasPrefetch, PR_FALSE);
   }
 
   // fetch href into the offline cache if relation is "offline-resource"
   if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
     AddOfflineResource(aHref);
     if (mSaveOfflineResources)
-      PrefetchHref(aHref, PR_TRUE, PR_TRUE);
+      PrefetchHref(aHref, aElement, PR_TRUE, PR_TRUE);
   }
 
   // is it a stylesheet link?
   if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) {
     return NS_OK;
   }
 
   PRBool isAlternate = linkTypes.IndexOf(NS_LITERAL_STRING("alternate")) != -1;
@@ -759,16 +759,17 @@ nsContentSink::ProcessMETATag(nsIContent
   }
 
   return rv;
 }
 
 
 void
 nsContentSink::PrefetchHref(const nsAString &aHref,
+                            nsIContent *aSource,
                             PRBool aExplicit,
                             PRBool aOffline)
 {
   //
   // SECURITY CHECK: disable prefetching from mailnews!
   //
   // walk up the docshell tree to see if any containing
   // docshell are of type MAIL.
@@ -803,20 +804,24 @@ nsContentSink::PrefetchHref(const nsAStr
   if (prefetchService) {
     // construct URI using document charset
     const nsACString &charset = mDocument->GetDocumentCharacterSet();
     nsCOMPtr<nsIURI> uri;
     NS_NewURI(getter_AddRefs(uri), aHref,
               charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
               mDocumentBaseURI);
     if (uri) {
+      nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
       if (aOffline)
-        prefetchService->PrefetchURIForOfflineUse(uri, mDocumentURI, aExplicit);
+        prefetchService->PrefetchURIForOfflineUse(uri,
+                                                  mDocumentURI,
+                                                  domNode,
+                                                  aExplicit);
       else
-        prefetchService->PrefetchURI(uri, mDocumentURI, aExplicit);
+        prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
     }
   }
 }
 
 nsresult
 nsContentSink::GetOfflineCacheSession(nsIOfflineCacheSession **aSession)
 {
   if (!mOfflineCacheSession) {
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -162,17 +162,18 @@ protected:
 
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsSubstring& aHref,
                                     PRBool aAlternate,
                                     const nsSubstring& aTitle,
                                     const nsSubstring& aType,
                                     const nsSubstring& aMedia);
 
-  void PrefetchHref(const nsAString &aHref, PRBool aExplicit, PRBool aOffline);
+  void PrefetchHref(const nsAString &aHref, nsIContent *aSource,
+                    PRBool aExplicit, PRBool aOffline);
   nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
   nsresult AddOfflineResource(const nsAString &aHref);
 
   void ScrollToRef();
   nsresult RefreshIfEnabled(nsIViewManager* vm);
 
   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -2999,26 +2999,26 @@ HTMLContentSink::ProcessLINKTag(const ns
         // XXX seems overkill to generate this string array
         nsStringArray linkTypes;
         nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
         PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
         if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
           nsAutoString hrefVal;
           element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
           if (!hrefVal.IsEmpty()) {
-            PrefetchHref(hrefVal, hasPrefetch, PR_FALSE);
+            PrefetchHref(hrefVal, element, hasPrefetch, PR_FALSE);
           }
         }
         if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
           nsAutoString hrefVal;
           element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
           if (!hrefVal.IsEmpty()) {
             AddOfflineResource(hrefVal);
             if (mSaveOfflineResources)
-              PrefetchHref(hrefVal, PR_TRUE, PR_TRUE);
+              PrefetchHref(hrefVal, element, PR_TRUE, PR_TRUE);
           }
         }
       }
     }
   }
 
   return result;
 }
--- a/dom/public/idl/base/nsIDOMClientInformation.idl
+++ b/dom/public/idl/base/nsIDOMClientInformation.idl
@@ -33,27 +33,29 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIDOMOfflineResourceList;
+interface nsIDOMLoadStatusList;
 
-[scriptable, uuid(21c6561e-4778-47ed-96d0-eadc46d6a3ec)]
+[scriptable, uuid(02bb1271-05dd-4bde-a9ca-68571bf8c702)]
 interface nsIDOMClientInformation : nsISupports
 {
   /**
    * Web Applications 1.0 Browser State: registerContentHandler
    * Allows web services to register themselves as handlers for certain content
    * types.
    * http://whatwg.org/specs/web-apps/current-work/
    */
   void registerContentHandler(in DOMString mimeType, in DOMString uri, in DOMString title);
   void registerProtocolHandler(in DOMString protocol, in DOMString uri, in DOMString title);
 
   boolean isLocallyAvailable(in DOMString uri, in boolean whenOffline);
 
   readonly attribute nsIDOMOfflineResourceList offlineResources;
+  readonly attribute nsIDOMLoadStatusList pendingOfflineLoads;
 };
 
 
--- a/dom/public/idl/offline/Makefile.in
+++ b/dom/public/idl/offline/Makefile.in
@@ -43,11 +43,14 @@ VPATH          = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 XPIDL_MODULE   = dom_offline
 GRE_MODULE     = 1
 
 SDK_XPIDLSRCS =                       \
         nsIDOMOfflineResourceList.idl \
-       $(NULL)
+        nsIDOMLoadStatusList.idl      \
+        nsIDOMLoadStatus.idl          \
+        nsIDOMLoadStatusEvent.idl     \
+        $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/offline/nsIDOMLoadStatus.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+
+[scriptable, uuid(2cb53a8a-d2f4-4ddf-874f-3bc2d595c41a)]
+interface nsIDOMLoadStatus : nsISupports
+{
+  readonly attribute nsIDOMNode source;
+  readonly attribute DOMString uri;
+  readonly attribute long totalSize;
+  readonly attribute long loadedSize;
+  readonly attribute unsigned short readyState;
+  readonly attribute unsigned short status;
+
+  const unsigned short UNINITIALIZED = 0;
+  const unsigned short REQUESTED     = 1;
+  const unsigned short RECEIVING     = 2;
+  const unsigned short LOADED        = 3;
+};
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/offline/nsIDOMLoadStatusEvent.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+#include "nsIDOMEvent.idl"
+
+interface nsIDOMLoadStatus;
+
+[scriptable, uuid(f14431b1-efb6-436c-a272-312f087b1459)]
+interface nsIDOMLoadStatusEvent : nsIDOMEvent
+{
+  readonly attribute nsIDOMLoadStatus status;
+
+  void initLoadStatusEvent(in DOMString typeArg,
+                           in boolean canBubbleArg,
+                           in boolean cancellableArg,
+                           in nsIDOMLoadStatus statusArg);
+  void initLoadStatusEventNS(in DOMString namespaceURIArg,
+                             in DOMString typeArg,
+                             in boolean canBubbleArg,
+                             in boolean cancellableArg,
+                             in nsIDOMLoadStatus statusArg);
+};
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/offline/nsIDOMLoadStatusList.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+
+interface nsIDOMLoadStatus;
+
+[scriptable, uuid(d58bc0cf-e35c-4d22-9e21-9ada8fc4203a)]
+interface nsIDOMLoadStatusList : nsISupports
+{
+  readonly attribute unsigned long length;
+  nsIDOMLoadStatus item(in unsigned long index);
+};
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -393,16 +393,19 @@ enum nsDOMClassInfoID {
 #if defined(MOZ_SVG) && defined(MOZ_SVG_FOREIGNOBJECT)
   eDOMClassInfo_SVGForeignObjectElement_id,
 #endif
 
   eDOMClassInfo_XULCommandEvent_id,
   eDOMClassInfo_CommandEvent_id,
 
   eDOMClassInfo_OfflineResourceList_id,
+  eDOMClassInfo_LoadStatusList_id,
+  eDOMClassInfo_LoadStatus_id,
+  eDOMClassInfo_LoadStatusEvent_id,
 
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
 
 /**
  * nsIClassInfo helper macros
  */
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -426,16 +426,21 @@
 // Storage includes
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDOMStorageList.h"
 #include "nsIDOMStorageItem.h"
 #include "nsIDOMStorageEvent.h"
 #include "nsIDOMToString.h"
 
+// Offline includes
+#include "nsIDOMLoadStatusList.h"
+#include "nsIDOMLoadStatus.h"
+#include "nsIDOMLoadStatusEvent.h"
+
 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
@@ -1174,16 +1179,23 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CommandEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(OfflineResourceList, nsOfflineResourceListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(LoadStatusList, nsLoadStatusListSH,
+                           ARRAY_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(LoadStatus, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(LoadStatusEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that shuld be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -3191,16 +3203,30 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCommandEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(OfflineResourceList, nsIDOMOfflineResourceList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMOfflineResourceList)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(LoadStatusList, nsIDOMLoadStatusList)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMLoadStatusList)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(LoadStatus, nsIDOMLoadStatus)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMLoadStatus)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(LoadStatusEvent, nsIDOMLoadStatusEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMLoadStatusEvent)
+    DOM_CLASSINFO_EVENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(TextRectangle, nsIDOMTextRectangle)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMTextRectangle)
    DOM_CLASSINFO_MAP_END
  
   DOM_CLASSINFO_MAP_BEGIN(TextRectangleList, nsIDOMTextRectangleList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMTextRectangleList)
   DOM_CLASSINFO_MAP_END
 
@@ -10064,8 +10090,24 @@ nsresult
 nsOfflineResourceListSH::GetStringAt(nsISupports *aNative, PRInt32 aIndex,
                                      nsAString& aResult)
 {
   nsCOMPtr<nsIDOMOfflineResourceList> list(do_QueryInterface(aNative));
   NS_ENSURE_TRUE(list, NS_ERROR_UNEXPECTED);
 
   return list->Item(aIndex, aResult);
 }
+
+// nsLoadStatusListSH
+nsresult
+nsLoadStatusListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+                              nsISupports **aResult)
+{
+  nsCOMPtr<nsIDOMLoadStatusList> list(do_QueryInterface(aNative));
+  NS_ENSURE_TRUE(list, NS_ERROR_UNEXPECTED);
+
+  nsIDOMLoadStatus *status = nsnull; // Weak, transfer the ownership over to aResult
+  nsresult rv = list->Item(aIndex, &status);
+
+  *aResult = status;
+
+  return rv;
+}
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -1618,14 +1618,34 @@ protected:
 
 public:
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsOfflineResourceListSH(aData);
   }
 };
 
+class nsLoadStatusListSH : public nsArraySH
+{
+protected:
+  nsLoadStatusListSH(nsDOMClassInfoData *aData) : nsArraySH(aData)
+  {
+  }
+
+  virtual ~nsLoadStatusListSH()
+  {
+  }
+
+  virtual nsresult GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+                             nsISupports **aResult);
+
+public:
+  static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
+  {
+    return new nsLoadStatusListSH(aData);
+  }
+};
 
 
 void InvalidateContextAndWrapperCache();
 
 
 #endif /* nsDOMClassInfo_h___ */
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -46,16 +46,17 @@
 
 // Local Includes
 #include "nsGlobalWindow.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsBarProps.h"
 #include "nsDOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
+#include "nsDOMOfflineLoadStatusList.h"
 #include "nsDOMError.h"
 
 // Helper Classes
 #include "nsXPIDLString.h"
 #include "nsJSUtils.h"
 #include "prmem.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "jsdbgapi.h"           // for JS_ClearWatchPointsForObject
@@ -8450,16 +8451,17 @@ void
 nsNavigator::LoadingNewDocument()
 {
   // Release these so that they will be recreated for the
   // new document (if requested).  The plugins or mime types
   // arrays may have changed.  See bug 150087.
   mMimeTypes = nsnull;
   mPlugins = nsnull;
   mOfflineResources = nsnull;
+  mPendingOfflineLoads = nsnull;
 }
 
 nsresult
 nsNavigator::RefreshMIMEArray()
 {
   nsresult rv = NS_OK;
   if (mMimeTypes)
     rv = mMimeTypes->Refresh();
@@ -8594,8 +8596,32 @@ nsNavigator::GetOfflineResources(nsIDOMO
     if (!mOfflineResources) return NS_ERROR_OUT_OF_MEMORY;
   }
 
   NS_IF_ADDREF(*aList = mOfflineResources);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNavigator::GetPendingOfflineLoads(nsIDOMLoadStatusList **aList)
+{
+  NS_ENSURE_ARG_POINTER(aList);
+
+  if (!mPendingOfflineLoads) {
+    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
+    if (!webNav) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = webNav->GetCurrentURI(getter_AddRefs(uri));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mPendingOfflineLoads = new nsDOMOfflineLoadStatusList(uri);
+    if (!mPendingOfflineLoads) return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  NS_IF_ADDREF(*aList = mPendingOfflineLoads);
+
+  return NS_OK;
+}
+
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -114,16 +114,17 @@ class nsNavigator;
 class nsScreen;
 class nsHistory;
 class nsIDocShellLoadInfo;
 class WindowStateHolder;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 
 class nsDOMOfflineResourceList;
+class nsDOMOfflineLoadStatusList;
 
 // permissible values for CheckOpenAllow
 enum OpenAllowValue {
   allowNot = 0,     // the window opening is denied
   allowNoAbuse,     // allowed: not a popup
   allowWhitelisted  // allowed: it's whitelisted or popup blocking is disabled
 };
 
@@ -753,16 +754,17 @@ public:
 
   void LoadingNewDocument();
   nsresult RefreshMIMEArray();
 
 protected:
   nsRefPtr<nsMimeTypeArray> mMimeTypes;
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<nsDOMOfflineResourceList> mOfflineResources;
+  nsRefPtr<nsDOMOfflineLoadStatusList> mPendingOfflineLoads;
   nsIDocShell* mDocShell; // weak reference
 
   static jsval       sPrefInternal_id;
 };
 
 class nsIURI;
 
 //*****************************************************************************
--- a/dom/src/offline/Makefile.in
+++ b/dom/src/offline/Makefile.in
@@ -45,28 +45,31 @@ include $(DEPTH)/config/autoconf.mk
 MODULE         = dom
 LIBRARY_NAME   = jsdomoffline_s
 LIBXUL_LIBRARY = 1
 
 REQUIRES       = xpcom         \
 		 string        \
 		 content       \
 		 caps          \
+		 gfx           \
 		 js            \
 		 layout        \
+		 locale        \
 		 necko         \
 		 nkcache       \
 		 pref          \
 		 prefetch      \
 		 widget        \
 		 xpconnect     \
 		 $(NULL)
 
-CPPSRCS =                            \
-	nsDOMOfflineResourceList.cpp  \
+CPPSRCS =                              \
+	nsDOMOfflineResourceList.cpp   \
+	nsDOMOfflineLoadStatusList.cpp \
 	$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 LOCAL_INCLUDES = \
 		-I$(srcdir)/../base \
 		-I$(topsrcdir)/content/events/src
new file mode 100644
--- /dev/null
+++ b/dom/src/offline/nsDOMOfflineLoadStatusList.cpp
@@ -0,0 +1,611 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMOfflineLoadStatusList.h"
+#include "nsDOMClassInfo.h"
+#include "nsIMutableArray.h"
+#include "nsCPrefetchService.h"
+#include "nsIObserverService.h"
+#include "nsIJSContextStack.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIContent.h"
+#include "nsNetCID.h"
+#include "nsICacheService.h"
+#include "nsICacheSession.h"
+#include "nsContentUtils.h"
+#include "nsDOMError.h"
+#include "nsNetUtil.h"
+#include "nsAutoPtr.h"
+
+#define LOADREQUESTED_STR "loadrequested"
+#define LOADCOMPLETED_STR "loadcompleted"
+
+//
+// nsDOMOfflineLoadStatus
+//
+
+// XXX
+//
+// nsDOMOfflineLoadStatusList has an array wrapper in its classinfo struct
+// (LoadStatusList) that exposes nsDOMOfflineLoadStatusList::Item() as
+// array items.
+//
+// For scripts to recognize these as nsIDOMLoadStatus objects, I needed
+// to add a LoadStatus classinfo.
+//
+// Because the prefetch service is outside the dom module, it can't
+// actually use the LoadStatus classinfo.
+//
+// nsDOMOfflineLoadStatus is just a simple wrapper around the
+// nsIDOMLoadStatus objects returned by the prefetch service that adds the
+// LoadStatus classinfo implementation.
+//
+// Is there a better way around this?
+
+class nsDOMOfflineLoadStatus : public nsIDOMLoadStatus
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMLOADSTATUS
+
+  nsDOMOfflineLoadStatus(nsIDOMLoadStatus *status);
+  virtual ~nsDOMOfflineLoadStatus();
+
+  const nsIDOMLoadStatus *Implementation() { return mStatus; }
+private:
+  nsCOMPtr<nsIDOMLoadStatus> mStatus;
+};
+
+NS_INTERFACE_MAP_BEGIN(nsDOMOfflineLoadStatus)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLoadStatus)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMLoadStatus)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LoadStatus)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsDOMOfflineLoadStatus)
+NS_IMPL_RELEASE(nsDOMOfflineLoadStatus)
+
+nsDOMOfflineLoadStatus::nsDOMOfflineLoadStatus(nsIDOMLoadStatus *aStatus)
+  : mStatus(aStatus)
+{
+}
+
+nsDOMOfflineLoadStatus::~nsDOMOfflineLoadStatus()
+{
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetSource(nsIDOMNode **aSource)
+{
+  return mStatus->GetSource(aSource);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetUri(nsAString &aURI)
+{
+  return mStatus->GetUri(aURI);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetTotalSize(PRInt32 *aTotalSize)
+{
+  return mStatus->GetTotalSize(aTotalSize);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetLoadedSize(PRInt32 *aLoadedSize)
+{
+  return mStatus->GetLoadedSize(aLoadedSize);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetReadyState(PRUint16 *aReadyState)
+{
+  return mStatus->GetReadyState(aReadyState);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatus::GetStatus(PRUint16 *aStatus)
+{
+  return mStatus->GetStatus(aStatus);
+}
+
+//
+// nsDOMOfflineLoadStatusList
+//
+
+NS_INTERFACE_MAP_BEGIN(nsDOMOfflineLoadStatusList)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLoadStatusList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMLoadStatusList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LoadStatusList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsDOMOfflineLoadStatusList)
+NS_IMPL_RELEASE(nsDOMOfflineLoadStatusList)
+
+nsDOMOfflineLoadStatusList::nsDOMOfflineLoadStatusList(nsIURI *aURI)
+  : mInitialized(PR_FALSE)
+  , mURI(aURI)
+{
+}
+
+nsDOMOfflineLoadStatusList::~nsDOMOfflineLoadStatusList()
+{
+  mLoadRequestedEventListeners.Clear();
+  mLoadCompletedEventListeners.Clear();
+}
+
+nsresult
+nsDOMOfflineLoadStatusList::Init()
+{
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  mInitialized = PR_TRUE;
+
+  nsresult rv = mURI->GetHostPort(mHostPort);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsICacheService> serv =
+    do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsICacheSession> session;
+  rv = serv->CreateSession("HTTP-offline",
+                           nsICache::STORE_OFFLINE,
+                           nsICache::STREAM_BASED,
+                           getter_AddRefs(session));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mCacheSession = do_QueryInterface(session, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get the current list of loads from the prefetch queue
+  nsCOMPtr<nsIPrefetchService> prefetchService =
+    do_GetService(NS_PREFETCHSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISimpleEnumerator> e;
+  rv = prefetchService->EnumerateQueue(PR_FALSE, PR_TRUE, getter_AddRefs(e));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool more;
+  while (NS_SUCCEEDED(rv = e->HasMoreElements(&more)) && more) {
+    nsCOMPtr<nsIDOMLoadStatus> status;
+    rv = e->GetNext(getter_AddRefs(status));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRBool shouldInclude;
+    rv = ShouldInclude(status, &shouldInclude);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!shouldInclude) {
+      continue;
+    }
+
+    nsDOMOfflineLoadStatus *wrapper = new nsDOMOfflineLoadStatus(status);
+    if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
+
+    mItems.AppendObject(wrapper);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // watch for changes in the prefetch queue
+  nsCOMPtr<nsIObserverService> observerServ =
+    do_GetService("@mozilla.org/observer-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = observerServ->AddObserver(this, "offline-load-requested", PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = observerServ->AddObserver(this, "offline-load-completed", PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMOfflineLoadStatusList::ShouldInclude(nsIDOMLoadStatus *aStatus,
+                                          PRBool *aShouldInclude)
+{
+  *aShouldInclude = PR_FALSE;
+
+  nsAutoString uriStr;
+  nsresult rv = aStatus->GetUri(uriStr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), uriStr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString hostport;
+  rv = uri->GetHostPort(hostport);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (hostport != mHostPort)
+    return NS_OK;
+
+  // Check that the URL is owned by this domain
+  nsCAutoString spec;
+  rv = uri->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> source;
+  rv = aStatus->GetSource(getter_AddRefs(source));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString ownerURI;
+  if (source) {
+    // Came from a <link> element, check that it's owned by this URI
+    rv = mURI->GetSpec(ownerURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // Didn't come from a <link> element, check that it's owned by
+    // resource list (no owner URI)
+    ownerURI.Truncate();
+  }
+
+  PRBool owned;
+  rv = mCacheSession->KeyIsOwned(mHostPort, ownerURI, spec, &owned);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!owned) {
+    return NS_OK;
+  }
+
+  *aShouldInclude = PR_TRUE;
+
+  return NS_OK;
+}
+
+nsIDOMLoadStatus *
+nsDOMOfflineLoadStatusList::FindWrapper(nsIDOMLoadStatus *aStatus,
+                                        PRUint32 *index)
+{
+  for (int i = 0; i < mItems.Count(); i++) {
+    nsDOMOfflineLoadStatus *item = NS_STATIC_CAST(nsDOMOfflineLoadStatus*,
+                                                  mItems[i]);
+    if (item->Implementation() == aStatus) {
+      *index = i;
+      return mItems[i];
+    }
+  }
+
+  return nsnull;
+}
+
+//
+// nsDOMOfflineLoadStatusList::nsIDOMLoadStatusList
+//
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::GetLength(PRUint32 *aLength)
+{
+  nsresult rv = Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aLength = mItems.Count();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::Item(PRUint32 aItem, nsIDOMLoadStatus **aStatus)
+{
+  nsresult rv = Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if ((PRInt32)aItem > mItems.Count()) return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+  NS_ADDREF(*aStatus = mItems[aItem]);
+
+  return NS_OK;
+}
+
+//
+// nsDOMOfflineLoadStatusList::nsIDOMEventTarget
+//
+
+static nsIScriptContext *
+GetCurrentContext()
+{
+  // Get JSContext from stack.
+  nsCOMPtr<nsIJSContextStack> stack =
+    do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+
+  if (!stack) {
+    return nsnull;
+  }
+
+  JSContext *cx;
+
+  if (NS_FAILED(stack->Peek(&cx)) || !cx) {
+    return nsnull;
+  }
+
+  return GetScriptContextFromJSContext(cx);
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::AddEventListener(const nsAString& aType,
+                                             nsIDOMEventListener *aListener,
+                                             PRBool aUseCapture)
+{
+  nsresult rv = Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ENSURE_ARG(aListener);
+
+  nsCOMArray<nsIDOMEventListener> *array;
+
+#define IMPL_ADD_LISTENER(_type, _member)    \
+  if (aType.EqualsLiteral(_type)) {           \
+    array = &(_member);                      \
+  } else
+
+  IMPL_ADD_LISTENER(LOADREQUESTED_STR, mLoadRequestedEventListeners)
+  IMPL_ADD_LISTENER(LOADCOMPLETED_STR, mLoadCompletedEventListeners)
+  {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  array->AppendObject(aListener);
+  mScriptContext = GetCurrentContext();
+#undef IMPL_ADD_LISTENER
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::RemoveEventListener(const nsAString &aType,
+                                                nsIDOMEventListener *aListener,
+                                                PRBool aUseCapture)
+{
+  nsresult rv = Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ENSURE_ARG(aListener);
+
+  nsCOMArray<nsIDOMEventListener> *array;
+
+#define IMPL_REMOVE_LISTENER(_type, _member)  \
+  if (aType.EqualsLiteral(_type)) {            \
+    array = &(_member);                       \
+  } else
+
+  IMPL_REMOVE_LISTENER(LOADREQUESTED_STR, mLoadRequestedEventListeners)
+  IMPL_REMOVE_LISTENER(LOADCOMPLETED_STR, mLoadCompletedEventListeners)
+  {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // Allow a caller to remove O(N^2) behavior by removing end-to-start.
+  for (PRUint32 i = array->Count() - 1; i != PRUint32(-1); --i) {
+    if (array->ObjectAt(i) == aListener) {
+      array->RemoveObjectAt(i);
+      break;
+    }
+  }
+
+#undef IMPL_ADD_LISTENER
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::DispatchEvent(nsIDOMEvent *evt, PRBool *_retval)
+{
+  // Ignored
+
+  return NS_OK;
+}
+
+void
+nsDOMOfflineLoadStatusList::NotifyEventListeners(const nsCOMArray<nsIDOMEventListener>& aListeners,
+                                                 nsIDOMEvent* aEvent)
+{
+  // XXXbz wouldn't it be easier to just have an actual nsEventListenerManager
+  // to work with or something?  I feel like we're duplicating code here...
+  //
+  // (and this was duplicated from XMLHttpRequest)
+  if (!aEvent)
+    return;
+
+  nsCOMPtr<nsIJSContextStack> stack;
+  JSContext *cx = nsnull;
+
+  if (mScriptContext) {
+    stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+
+    if (stack) {
+      cx = (JSContext *)mScriptContext->GetNativeContext();
+
+      if (cx) {
+        stack->Push(cx);
+      }
+    }
+  }
+
+  PRInt32 count = aListeners.Count();
+  for (PRInt32 index = 0; index < count; ++index) {
+    nsIDOMEventListener* listener = aListeners[index];
+
+    if (listener) {
+      listener->HandleEvent(aEvent);
+    }
+  }
+
+  if (cx) {
+    stack->Pop(&cx);
+  }
+}
+
+nsresult
+nsDOMOfflineLoadStatusList::SendLoadEvent(const nsAString &aEventName,
+                                          const nsCOMArray<nsIDOMEventListener> &aListeners,
+                                          nsIDOMLoadStatus *aStatus)
+{
+  NS_ENSURE_ARG(aStatus);
+
+  if (aListeners.Count() == 0) return NS_OK;
+
+  nsRefPtr<nsDOMLoadStatusEvent> event = new nsDOMLoadStatusEvent(aEventName,
+                                                                  aStatus);
+  if (!event) return NS_ERROR_OUT_OF_MEMORY;
+
+  nsresult rv = event->Init();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NotifyEventListeners(aListeners,
+                       NS_STATIC_CAST(nsIDOMLoadStatusEvent*, event));
+
+  return NS_OK;
+}
+
+//
+// nsDOMLoadStatusList::nsIObserver
+//
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::Observe(nsISupports *aSubject,
+                                    const char *aTopic,
+                                    const PRUnichar *aData)
+{
+  nsresult rv;
+  if (!strcmp(aTopic, "offline-load-requested")) {
+    nsCOMPtr<nsIDOMLoadStatus> status = do_QueryInterface(aSubject);
+    if (status) {
+      PRBool shouldInclude;
+      rv = ShouldInclude(status, &shouldInclude);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!shouldInclude) return NS_OK;
+
+      nsDOMOfflineLoadStatus *wrapper = new nsDOMOfflineLoadStatus(status);
+      if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
+
+      mItems.AppendObject(wrapper);
+
+      rv = SendLoadEvent(NS_LITERAL_STRING(LOADREQUESTED_STR),
+                         mLoadRequestedEventListeners,
+                         wrapper);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  } else if (!strcmp(aTopic, "offline-load-completed")) {
+    nsCOMPtr<nsIDOMLoadStatus> status = do_QueryInterface(aSubject);
+    if (status) {
+      PRUint32 index;
+      nsCOMPtr<nsIDOMLoadStatus> wrapper = FindWrapper(status, &index);
+      if (wrapper) {
+        mItems.RemoveObjectAt(index);
+
+        rv = SendLoadEvent(NS_LITERAL_STRING(LOADCOMPLETED_STR),
+                           mLoadCompletedEventListeners,
+                           wrapper);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+//
+// nsDOMLoadStatusEvent
+//
+
+NS_INTERFACE_MAP_BEGIN(nsDOMLoadStatusEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMLoadStatusEvent)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LoadStatusEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMPL_ADDREF_INHERITED(nsDOMLoadStatusEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMLoadStatusEvent, nsDOMEvent)
+
+nsresult
+nsDOMLoadStatusEvent::Init()
+{
+  nsresult rv = InitEvent(mEventName, PR_TRUE, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // This init method is only called by native code, so set the
+  // trusted flag there.
+  SetTrusted(PR_TRUE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMLoadStatusEvent::InitLoadStatusEvent(const nsAString& aTypeArg,
+                                          PRBool aCanBubbleArg,
+                                          PRBool aCancelableArg,
+                                          nsIDOMLoadStatus* aStatusArg)
+{
+  nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mStatus = aStatusArg;
+
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsDOMLoadStatusEvent::InitLoadStatusEventNS(const nsAString& aNamespaceURIArg,
+                                            const nsAString& aTypeArg,
+                                            PRBool aCanBubbleArg,
+                                            PRBool aCancelableArg,
+                                            nsIDOMLoadStatus* aStatusArg)
+{
+  // XXX: Figure out what to do with aNamespaceURIArg here!
+  nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mStatus = aStatusArg;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMLoadStatusEvent::GetStatus(nsIDOMLoadStatus **aStatus)
+{
+  NS_ADDREF(*aStatus = mStatus);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/offline/nsDOMOfflineLoadStatusList.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMOfflineLoadStatusList_h___
+#define nsDOMOfflineLoadStatusList_h___
+
+#include "nscore.h"
+#include "nsIDOMLoadStatus.h"
+#include "nsIDOMLoadStatusEvent.h"
+#include "nsIDOMLoadStatusList.h"
+#include "nsIOfflineCacheSession.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIURI.h"
+#include "nsDOMEvent.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMEventListener.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+#include "nsIScriptContext.h"
+
+class nsDOMOfflineLoadStatus;
+
+class nsDOMOfflineLoadStatusList : public nsIDOMLoadStatusList,
+                                   public nsIDOMEventTarget,
+                                   public nsIObserver,
+                                   public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMLOADSTATUSLIST
+  NS_DECL_NSIDOMEVENTTARGET
+  NS_DECL_NSIOBSERVER
+
+  nsDOMOfflineLoadStatusList(nsIURI *aURI);
+  virtual ~nsDOMOfflineLoadStatusList();
+
+  nsresult Init();
+
+private :
+  nsresult          ShouldInclude       (nsIDOMLoadStatus *aStatus,
+                                         PRBool *aInclude);
+  nsIDOMLoadStatus *FindWrapper         (nsIDOMLoadStatus *aStatus,
+                                         PRUint32 *aIndex);
+  void              NotifyEventListeners(const nsCOMArray<nsIDOMEventListener>& aListeners,
+                                         nsIDOMEvent* aEvent);
+
+  nsresult          SendLoadEvent       (const nsAString& aEventName,
+                                         const nsCOMArray<nsIDOMEventListener>& aListeners,
+                                         nsIDOMLoadStatus *aStatus);
+
+  PRBool mInitialized;
+
+  nsCOMPtr<nsIURI> mURI;
+  nsCOMArray<nsIDOMLoadStatus> mItems;
+  nsCString mHostPort;
+  nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
+
+  nsCOMPtr<nsIScriptContext> mScriptContext;
+
+  nsCOMArray<nsIDOMEventListener> mLoadRequestedEventListeners;
+  nsCOMArray<nsIDOMEventListener> mLoadCompletedEventListeners;
+};
+
+class nsDOMLoadStatusEvent : public nsDOMEvent,
+                             public nsIDOMLoadStatusEvent
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMLOADSTATUSEVENT
+  NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
+
+  nsDOMLoadStatusEvent(const nsAString& aEventName, nsIDOMLoadStatus *aStatus)
+    : nsDOMEvent(nsnull, nsnull), mEventName(aEventName), mStatus(aStatus)
+  {
+  }
+
+  virtual ~nsDOMLoadStatusEvent() { }
+
+  nsresult Init();
+
+private:
+  nsAutoString mEventName;
+  nsCOMPtr<nsIDOMLoadStatus> mStatus;
+};
+
+#endif
+
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -195,16 +195,17 @@ nsDOMOfflineResourceList::Add(const nsAS
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrefetchService> prefetchService =
     do_GetService(NS_PREFETCHSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return prefetchService->PrefetchURIForOfflineUse(requestedURI,
                                                    mURI,
+                                                   nsnull,
                                                    PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::Remove(const nsAString& aURI)
 {
   nsresult rv = Init();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -264,16 +265,17 @@ nsDOMOfflineResourceList::Refresh()
   for (PRUint32 i = 0; i < gCachedKeysCount; i++) {
     // this will fail if the URI is not absolute
     nsCOMPtr<nsIURI> requestedURI;
     nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), gCachedKeys[i]);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = prefetchService->PrefetchURIForOfflineUse(requestedURI,
                                                    mURI,
+                                                   nsnull,
                                                    PR_TRUE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/tests/mochitest/ajax/offline/Makefile.in
+++ b/dom/tests/mochitest/ajax/offline/Makefile.in
@@ -43,12 +43,13 @@ relativesrcdir	= dom/tests/mochitest/aja
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES	= \
         test_offlineResources.html \
         test_isLocallyAvailable.html \
+        test_pendingOfflineLoads.html \
         $(NULL)
 
 libs::	$(_TEST_FILES)
 	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/ajax/offline/test_pendingOfflineLoads.html
@@ -0,0 +1,131 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>navigator.pendingOfflineLoads Test</title>
+
+<link id="load1" rel="offline-resource" href="http://localhost:8888/unknown1">
+<link id="load2" rel="offline-resource" href="http://localhost:8888/MochiKit/packed.js">
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script type="text/javascript">
+
+var base = window.location.protocol + "//" + window.location.host;
+var thisURL = base + window.location.pathname;
+
+var expectedCompletions = {};
+
+expectedCompletions["http://localhost:8888/unknown1"] = {
+  "source": document.getElementById("load1"),
+  "status": 404
+};
+expectedCompletions["http://localhost:8888/unknown2"] = {
+  "source": null,
+  "status": 404
+};
+expectedCompletions["http://localhost:8888/MochiKit/packed.js"] = {
+  "source": document.getElementById("load2"),
+  "status": 200
+};
+expectedCompletions[thisURL] = {
+  "source": null,
+  "status": 200
+};
+
+var expectedAdditions = { };
+for (var uri in expectedCompletions) {
+  expectedAdditions[uri] = expectedCompletions[uri];
+}
+
+function check_load_added(load)
+{
+  var source = load.source;
+  var uri = load.uri;
+
+  ok(expectedAdditions[uri], uri + " should be in the expected addition list");
+  ok(expectedAdditions[uri].source == source,
+     uri + " should come from the expected source");
+  ok(load.status == 0, "status should be 0");
+  delete expectedAdditions[uri];
+}
+
+
+function load_requested(e)
+{
+  check_load_added(e.status);
+}
+
+function load_completed(e)
+{
+  var load = e.status;
+  var uri = load.uri;
+
+  ok(expectedCompletions[uri],
+     uri + " should be in the expected completion list");
+  ok(expectedCompletions[uri].source == load.source,
+     uri + " should come from the expected source");
+  ok(expectedCompletions[uri].status == load.status,
+     uri + " should have a load status of " + expectedCompletions[uri].status);
+
+  delete expectedCompletions[uri];
+
+  if (navigator.pendingOfflineLoads.length == 0) {
+    for (var name in expectedAdditions) {
+      ok(false, name + " did not receive a request event");
+    }
+
+    for (var name in expectedCompletions) {
+      ok(false, name + " did not receive a completed event");
+    }
+
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+    // Clear the ownership list
+    var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
+      .getService(Components.interfaces.nsICacheService);
+    var cacheSession = cacheService.createSession("HTTP-offline",
+                                                  Components.interfaces.nsICache.STORE_OFFLINE,
+                                                  true)
+      .QueryInterface(Components.interfaces.nsIOfflineCacheSession);
+    cacheSession.setOwnedKeys(window.location.host, thisURL, 0, []);
+    cacheSession.setOwnedKeys(window.location.host, "", 0, []);
+    cacheSession.evictUnownedEntries();
+
+    SimpleTest.finish();
+  }
+}
+
+function run_test()
+{
+  navigator.pendingOfflineLoads.addEventListener("loadrequested",
+                                                 load_requested,
+                                                 false);
+  navigator.pendingOfflineLoads.addEventListener("loadcompleted",
+                                                 load_completed,
+                                                 false);
+
+  // The <link rel="offline-resource"> should already be in the queue
+  ok(navigator.pendingOfflineLoads.length == 2,
+     "<link rel=\"offline-resource\"> loads should already be in the queue");
+
+  for (var i = 0; i < navigator.pendingOfflineLoads.length; i++) {
+    var load = navigator.pendingOfflineLoads[i];
+    check_load_added(navigator.pendingOfflineLoads[i]);
+  }
+
+  // Add the correct resources
+  navigator.offlineResources.add("http://localhost:8888/unknown2");
+  navigator.offlineResources.add(thisURL);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+<body onload="run_test()">
+
+
+
+</body>
+</html>
--- a/uriloader/prefetch/nsIPrefetchService.idl
+++ b/uriloader/prefetch/nsIPrefetchService.idl
@@ -33,35 +33,56 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
+interface nsIDOMNode;
+interface nsISimpleEnumerator;
 
-[scriptable, uuid(ae241be6-3cb6-47ec-9e38-2d89bc22042a)]
+[scriptable, uuid(cba513eb-c457-4b93-832c-1a979e66edd1)]
 interface nsIPrefetchService : nsISupports
 {
     /**
      * Enqueue a request to prefetch the specified URI.
      *
      * @param aURI the URI of the document to prefetch
      * @param aReferrerURI the URI of the referring page
+     * @param aSource the DOM node (such as a <link> tag) that requested this
+     *        fetch, or null if the prefetch was not requested by a DOM node.
      * @param aExplicit the link element has an explicit prefetch link type
      */
-    void prefetchURI(in nsIURI aURI, in nsIURI aReferrerURI, in boolean aExplicit);
+    void prefetchURI(in nsIURI aURI,
+                     in nsIURI aReferrerURI,
+                     in nsIDOMNode aSource,
+                     in boolean aExplicit);
 
     /**
      * Enqueue a request to prefetch the specified URI, making sure it's in the
      * offline cache.
      *
      * @param aURI the URI of the document to prefetch
      * @param aReferrerURI the URI of the referring page
+     * @param aSource the DOM node (such as a <link> tag) that requested this
+     *        fetch, or null if the prefetch was not requested by a DOM node.
      * @param aExplicit the link element has an explicit offline link type
      */
     void prefetchURIForOfflineUse(in nsIURI aURI,
                                   in nsIURI aReferrerURI,
+                                  in nsIDOMNode aSource,
                                   in boolean aExplicit);
 
+    /**
+     * Enumerate the items in the prefetch queue.  Each element in the
+     * enumeration is an nsIDOMLoadStatus.
+     *
+     * @param aIncludeNormalItems include normal prefetch items in the list.
+     * @param aIncludeOfflineItems include items being fetched for offline
+     *        use.
+     */
+    nsISimpleEnumerator enumerateQueue(in boolean aIncludeNormalItems,
+                                       in boolean aIncludeOfflineItems);
+
     // XXX do we need a way to cancel prefetch requests?
 };
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -46,20 +46,22 @@
 #include "nsIPrefBranch2.h"
 #include "nsIDocCharset.h"
 #include "nsIWebProgress.h"
 #include "nsCURILoader.h"
 #include "nsICachingChannel.h"
 #include "nsICacheVisitor.h"
 #include "nsIHttpChannel.h"
 #include "nsIURL.h"
+#include "nsISimpleEnumerator.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
+#include "nsStreamUtils.h"
 #include "nsAutoPtr.h"
 #include "prtime.h"
 #include "prlog.h"
 #include "plstr.h"
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
@@ -90,62 +92,206 @@ PRTimeToSeconds(PRTime t_usec)
     LL_DIV(t_usec, t_usec, usec_per_sec);
     LL_L2I(t_sec, t_usec);
     return t_sec;
 }
 
 #define NowInSeconds() PRTimeToSeconds(PR_Now())
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener <public>
+// nsPrefetchQueueEnumerator
 //-----------------------------------------------------------------------------
+class nsPrefetchQueueEnumerator : public nsISimpleEnumerator
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISIMPLEENUMERATOR
+    nsPrefetchQueueEnumerator(nsPrefetchService *aService,
+                              PRBool aIncludeNormal,
+                              PRBool aIncludeOffline);
+    ~nsPrefetchQueueEnumerator();
+
+private:
+    void Increment();
 
-nsPrefetchListener::nsPrefetchListener(nsPrefetchService *aService)
+    nsRefPtr<nsPrefetchService> mService;
+    nsRefPtr<nsPrefetchNode> mCurrent;
+    PRBool mIncludeNormal;
+    PRBool mIncludeOffline;
+    PRBool mStarted;
+};
+
+//-----------------------------------------------------------------------------
+// nsPrefetchQueueEnumerator <public>
+//-----------------------------------------------------------------------------
+nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService,
+                                                     PRBool aIncludeNormal,
+                                                     PRBool aIncludeOffline)
+    : mService(aService)
+    , mIncludeNormal(aIncludeNormal)
+    , mIncludeOffline(aIncludeOffline)
+    , mStarted(PR_FALSE)
 {
-    NS_ADDREF(mService = aService);
+    Increment();
 }
 
-nsPrefetchListener::~nsPrefetchListener()
+nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator()
 {
-    NS_RELEASE(mService);
 }
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener <private>
+// nsPrefetchQueueEnumerator::nsISimpleEnumerator
 //-----------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrefetchQueueEnumerator::HasMoreElements(PRBool *aHasMore)
+{
+    *aHasMore = (mCurrent != nsnull);
+    return NS_OK;
+}
 
-NS_METHOD
-nsPrefetchListener::ConsumeSegments(nsIInputStream *aInputStream,
-                                    void *aClosure,
-                                    const char *aFromSegment,
-                                    PRUint32 aOffset,
-                                    PRUint32 aCount,
-                                    PRUint32 *aBytesConsumed)
+NS_IMETHODIMP
+nsPrefetchQueueEnumerator::GetNext(nsISupports **aItem)
 {
-    *aBytesConsumed = aCount;
+    if (!mCurrent) return NS_ERROR_FAILURE;
+
+    NS_ADDREF(*aItem = static_cast<nsIDOMLoadStatus*>(mCurrent.get()));
+
+    Increment();
+
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener::nsISupports
+// nsPrefetchQueueEnumerator <private>
+//-----------------------------------------------------------------------------
+
+void
+nsPrefetchQueueEnumerator::Increment()
+{
+    do {
+        if (!mStarted) {
+            // If the service is currently serving a request, it won't be
+            // in the pending queue, so we return it first.  If it isn't,
+            // we'll just start with the pending queue.
+            mStarted = PR_TRUE;
+            mCurrent = mService->GetCurrentNode();
+            if (!mCurrent)
+                mCurrent = mService->GetQueueHead();
+        }
+        else if (mCurrent) {
+            if (mCurrent == mService->GetCurrentNode()) {
+                // If we just returned the node being processed by the service,
+                // start with the pending queue
+                mCurrent = mService->GetQueueHead();
+            }
+            else {
+                // Otherwise just advance to the next item in the queue
+                mCurrent = mCurrent->mNext;
+            }
+        }
+    } while (mCurrent && mIncludeOffline != mCurrent->mOffline &&
+             mIncludeNormal == mCurrent->mOffline);
+}
+
+//-----------------------------------------------------------------------------
+// nsPrefetchQueueEnumerator::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS1(nsPrefetchQueueEnumerator, nsISimpleEnumerator)
+
+//-----------------------------------------------------------------------------
+// nsPrefetchNode <public>
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS4(nsPrefetchListener,
+nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
+                               nsIURI *aURI,
+                               nsIURI *aReferrerURI,
+                               nsIDOMNode *aSource,
+                               PRBool aOffline)
+    : mNext(nsnull)
+    , mURI(aURI)
+    , mReferrerURI(aReferrerURI)
+    , mOffline(aOffline)
+    , mService(aService)
+    , mChannel(nsnull)
+    , mState(nsIDOMLoadStatus::UNINITIALIZED)
+    , mBytesRead(0)
+{
+    mSource = do_GetWeakReference(aSource);
+}
+
+nsresult
+nsPrefetchNode::OpenChannel()
+{
+    nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
+                                mURI,
+                                nsnull, nsnull, this,
+                                nsIRequest::LOAD_BACKGROUND |
+                                nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // configure HTTP specific stuff
+    nsCOMPtr<nsIHttpChannel> httpChannel =
+        do_QueryInterface(mChannel);
+    if (httpChannel) {
+        httpChannel->SetReferrer(mReferrerURI);
+        httpChannel->SetRequestHeader(
+            NS_LITERAL_CSTRING("X-Moz"),
+            mOffline ?
+            NS_LITERAL_CSTRING("offline-resource") :
+            NS_LITERAL_CSTRING("prefetch"),
+            PR_FALSE);
+    }
+
+    if (mOffline) {
+        nsCOMPtr<nsICachingChannel> cachingChannel =
+            do_QueryInterface(mChannel);
+        if (cachingChannel) {
+            rv = cachingChannel->SetCacheForOfflineUse(PR_TRUE);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+    }
+
+    rv = mChannel->AsyncOpen(this, nsnull);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mState = nsIDOMLoadStatus::REQUESTED;
+
+    return NS_OK;
+}
+
+nsresult
+nsPrefetchNode::CancelChannel(nsresult error)
+{
+    mChannel->Cancel(error);
+    mChannel = nsnull;
+
+    mState = nsIDOMLoadStatus::UNINITIALIZED;
+
+    return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsPrefetchNode::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS5(nsPrefetchNode,
+                   nsIDOMLoadStatus,
                    nsIRequestObserver,
                    nsIStreamListener,
                    nsIInterfaceRequestor,
                    nsIChannelEventSink)
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener::nsIStreamListener
+// nsPrefetchNode::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsPrefetchListener::OnStartRequest(nsIRequest *aRequest,
-                                   nsISupports *aContext)
+nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
+                               nsISupports *aContext)
 {
     nsresult rv;
 
     nsCOMPtr<nsICachingChannel> cachingChannel =
         do_QueryInterface(aRequest, &rv);
     if (NS_FAILED(rv)) return rv;
 
     PRBool cacheForOfflineUse;
@@ -178,67 +324,82 @@ nsPrefetchListener::OnStartRequest(nsIRe
         if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
             if (NowInSeconds() >= expTime) {
                 LOG(("document cannot be reused from cache; "
                      "canceling prefetch\n"));
                 return NS_BINDING_ABORTED;
             }
         }
     }
+
+    mState = nsIDOMLoadStatus::RECEIVING;
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPrefetchListener::OnDataAvailable(nsIRequest *aRequest,
-                                    nsISupports *aContext,
-                                    nsIInputStream *aStream,
-                                    PRUint32 aOffset,
-                                    PRUint32 aCount)
+nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest,
+                                nsISupports *aContext,
+                                nsIInputStream *aStream,
+                                PRUint32 aOffset,
+                                PRUint32 aCount)
 {
     PRUint32 bytesRead = 0;
-    aStream->ReadSegments(ConsumeSegments, nsnull, aCount, &bytesRead);
+    aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
+    mBytesRead += bytesRead;
     LOG(("prefetched %u bytes [offset=%u]\n", bytesRead, aOffset));
     return NS_OK;
 }
 
+
 NS_IMETHODIMP
-nsPrefetchListener::OnStopRequest(nsIRequest *aRequest,
-                                  nsISupports *aContext,
-                                  nsresult aStatus)
+nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
+                              nsISupports *aContext,
+                              nsresult aStatus)
 {
     LOG(("done prefetching [status=%x]\n", aStatus));
 
+    mState = nsIDOMLoadStatus::LOADED;
+
+    if (mBytesRead == 0 && aStatus == NS_OK) {
+        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
+        // specified), but the object should report loadedSize as if it
+        // did.
+        mChannel->GetContentLength(&mBytesRead);
+    }
+
+    mService->NotifyLoadCompleted(this);
     mService->ProcessNextURI();
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener::nsIInterfaceRequestor
+// nsPrefetchNode::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsPrefetchListener::GetInterface(const nsIID &aIID, void **aResult)
+nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult)
 {
     if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
         NS_ADDREF_THIS();
         *aResult = static_cast<nsIChannelEventSink *>(this);
         return NS_OK;
     }
 
     return NS_ERROR_NO_INTERFACE;
 }
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener::nsIChannelEventSink
+// nsPrefetchNode::nsIChannelEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsPrefetchListener::OnChannelRedirect(nsIChannel *aOldChannel,
-                                      nsIChannel *aNewChannel,
-                                      PRUint32 aFlags)
+nsPrefetchNode::OnChannelRedirect(nsIChannel *aOldChannel,
+                                  nsIChannel *aNewChannel,
+                                  PRUint32 aFlags)
 {
     nsCOMPtr<nsIURI> newURI;
     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
     if (NS_FAILED(rv))
         return rv;
 
     PRBool offline;
     nsCOMPtr<nsICachingChannel> oldCachingChannel =
@@ -267,20 +428,22 @@ nsPrefetchListener::OnChannelRedirect(ns
     NS_ENSURE_STATE(httpChannel);
 
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                   offline ?
                                       NS_LITERAL_CSTRING("offline-resource") :
                                       NS_LITERAL_CSTRING("prefetch"),
                                   PR_FALSE);
 
-    mService->UpdateCurrentChannel(aNewChannel);
+    mChannel = aNewChannel;
+
     return NS_OK;
 }
 
+
 //-----------------------------------------------------------------------------
 // nsPrefetchService <public>
 //-----------------------------------------------------------------------------
 
 nsPrefetchService::nsPrefetchService()
     : mQueueHead(nsnull)
     , mQueueTail(nsnull)
     , mStopCount(0)
@@ -333,25 +496,20 @@ nsPrefetchService::Init()
 }
 
 void
 nsPrefetchService::ProcessNextURI()
 {
     nsresult rv;
     nsCOMPtr<nsIURI> uri, referrer;
 
-    mCurrentChannel = nsnull;
-
-    nsRefPtr<nsPrefetchListener> listener(new nsPrefetchListener(this));
-    if (!listener) return;
+    mCurrentNode = nsnull;
 
     do {
-        PRBool offline;
-        rv = DequeueURI(getter_AddRefs(uri), getter_AddRefs(referrer),
-                        &offline);
+        rv = DequeueNode(getter_AddRefs(mCurrentNode));
         if (rv == NS_ERROR_NOT_AVAILABLE && mFetchedOffline) {
             // done loading stuff, go ahead and evict unowned entries from
             // the offline cache
             mFetchedOffline = PR_FALSE;
 
             nsCOMPtr<nsIOfflineCacheSession> session;
             rv = GetOfflineCacheSession(getter_AddRefs(session));
             if (NS_FAILED(rv)) break;
@@ -360,60 +518,63 @@ nsPrefetchService::ProcessNextURI()
             break;
         }
 
         if (NS_FAILED(rv)) break;
 
 #if defined(PR_LOGGING)
         if (LOG_ENABLED()) {
             nsCAutoString spec;
-            uri->GetSpec(spec);
+            mCurrentNode->mURI->GetSpec(spec);
             LOG(("ProcessNextURI [%s]\n", spec.get()));
         }
 #endif
 
         //
         // if opening the channel fails, then just skip to the next uri
         //
-        rv = NS_NewChannel(getter_AddRefs(mCurrentChannel), uri,
-                           nsnull, nsnull, listener,
-                           nsIRequest::LOAD_BACKGROUND |
-                           nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
-        if (NS_FAILED(rv)) continue;
-
-        // configure HTTP specific stuff
-        nsCOMPtr<nsIHttpChannel> httpChannel =
-            do_QueryInterface(mCurrentChannel);
-        if (httpChannel) {
-            httpChannel->SetReferrer(referrer);
-            httpChannel->SetRequestHeader(
-                NS_LITERAL_CSTRING("X-Moz"),
-                offline ?
-                    NS_LITERAL_CSTRING("offline-resource") :
-                    NS_LITERAL_CSTRING("prefetch"),
-                PR_FALSE);
-        }
-
-        if (offline) {
-            nsCOMPtr<nsICachingChannel> cachingChannel =
-                do_QueryInterface(mCurrentChannel);
-            if (cachingChannel) {
-                if (NS_FAILED(cachingChannel->SetCacheForOfflineUse(PR_TRUE))) {
-                    continue;
-                }
-            }
-
+        rv = mCurrentNode->OpenChannel();
+        if (NS_SUCCEEDED(rv) && mCurrentNode->mOffline)
             mFetchedOffline = PR_TRUE;
-        }
-
-        rv = mCurrentChannel->AsyncOpen(listener, nsnull);
     }
     while (NS_FAILED(rv));
 }
 
+void
+nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIObserverService> observerService =
+        do_GetService("@mozilla.org/observer-service;1", &rv);
+    if (NS_FAILED(rv)) return;
+
+    const char *topic = node->mOffline ? "offline-load-requested" :
+                                         "prefetch-load-requested";
+
+    observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
+                                     topic, nsnull);
+}
+
+void
+nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIObserverService> observerService =
+        do_GetService("@mozilla.org/observer-service;1", &rv);
+    if (NS_FAILED(rv)) return;
+
+    const char *topic = node->mOffline ? "offline-load-completed" :
+                                         "prefetch-load-completed";
+
+    observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
+                                     topic, nsnull);
+}
+
 //-----------------------------------------------------------------------------
 // nsPrefetchService <private>
 //-----------------------------------------------------------------------------
 
 void
 nsPrefetchService::AddProgressListener()
 {
     // Register as an observer for the document loader  
@@ -429,52 +590,59 @@ nsPrefetchService::RemoveProgressListene
     // Register as an observer for the document loader  
     nsCOMPtr<nsIWebProgress> progress =
         do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     if (progress)
         progress->RemoveProgressListener(this);
 }
 
 nsresult
-nsPrefetchService::EnqueueURI(nsIURI *aURI,
-                              nsIURI *aReferrerURI,
-                              PRBool aOffline)
+nsPrefetchService::EnqueueNode(nsPrefetchNode *aNode)
 {
-    nsPrefetchNode *node = new nsPrefetchNode(aURI, aReferrerURI, aOffline);
-    if (!node)
-        return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(aNode);
 
     if (!mQueueTail) {
-        mQueueHead = node;
-        mQueueTail = node;
+        mQueueHead = aNode;
+        mQueueTail = aNode;
     }
     else {
-        mQueueTail->mNext = node;
-        mQueueTail = node;
+        mQueueTail->mNext = aNode;
+        mQueueTail = aNode;
     }
 
     return NS_OK;
 }
 
 nsresult
-nsPrefetchService::DequeueURI(nsIURI **aURI,
-                              nsIURI **aReferrerURI,
-                              PRBool *aOffline)
+nsPrefetchService::EnqueueURI(nsIURI *aURI,
+                              nsIURI *aReferrerURI,
+                              nsIDOMNode *aSource,
+                              PRBool aOffline,
+                              nsPrefetchNode **aNode)
+{
+    nsPrefetchNode *node = new nsPrefetchNode(this, aURI, aReferrerURI,
+                                              aSource, aOffline);
+    if (!node)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aNode = node);
+
+    return EnqueueNode(node);
+}
+
+nsresult
+nsPrefetchService::DequeueNode(nsPrefetchNode **node)
 {
     if (!mQueueHead)
         return NS_ERROR_NOT_AVAILABLE;
 
-    // remove from the head
-    NS_ADDREF(*aURI = mQueueHead->mURI);
-    NS_ADDREF(*aReferrerURI = mQueueHead->mReferrerURI);
-    *aOffline = mQueueHead->mOffline;
-
-    nsPrefetchNode *node = mQueueHead;
+    // give the ref to the caller
+    *node = mQueueHead;
     mQueueHead = mQueueHead->mNext;
-    delete node;
+    (*node)->mNext = nsnull;
 
     if (!mQueueHead)
         mQueueTail = nsnull;
 
     return NS_OK;
 }
 
 void
@@ -485,17 +653,17 @@ nsPrefetchService::EmptyQueue(PRBool inc
 
     while (node) {
         nsPrefetchNode *next = node->mNext;
         if (includeOffline || !node->mOffline) {
             if (prev)
                 prev->mNext = next;
             else
                 mQueueHead = next;
-            delete node;
+            NS_RELEASE(node);
         }
         else
             prev = node;
 
         node = next;
     }
 }
 
@@ -535,53 +703,40 @@ nsPrefetchService::StartPrefetching()
     if (mStopCount > 0)
         mStopCount--;
 
     LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
 
     // only start prefetching after we've received enough DOCUMENT
     // STOP notifications.  we do this inorder to defer prefetching
     // until after all sub-frames have finished loading.
-    if (mStopCount == 0 && !mCurrentChannel) {
+    if (mStopCount == 0 && !mCurrentNode) {
         mHaveProcessed = PR_TRUE;
         ProcessNextURI();
     }
 }
 
 void
 nsPrefetchService::StopPrefetching()
 {
     mStopCount++;
 
     LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
 
     // only kill the prefetch queue if we've actually started prefetching.
-    if (!mCurrentChannel)
+    if (!mCurrentNode)
         return;
 
     // if it's an offline prefetch, requeue it for when prefetching starts
     // again
-    nsCOMPtr<nsICachingChannel> cachingChannel =
-        do_QueryInterface(mCurrentChannel);
-    PRBool offline;
-    if (cachingChannel &&
-        NS_SUCCEEDED(cachingChannel->GetCacheForOfflineUse(&offline)) &&
-        offline) {
-        nsCOMPtr<nsIHttpChannel> httpChannel =
-            do_QueryInterface(mCurrentChannel);
-        nsCOMPtr<nsIURI> uri;
-        nsCOMPtr<nsIURI> referrerURI;
-        if (NS_SUCCEEDED(mCurrentChannel->GetURI(getter_AddRefs(uri))) &&
-            NS_SUCCEEDED(httpChannel->GetReferrer(getter_AddRefs(referrerURI)))) {
-            EnqueueURI(uri, referrerURI, PR_TRUE);
-        }
-    }
+    if (mCurrentNode->mOffline)
+        EnqueueNode(mCurrentNode);
 
-    mCurrentChannel->Cancel(NS_BINDING_ABORTED);
-    mCurrentChannel = nsnull;
+    mCurrentNode->CancelChannel(NS_BINDING_ABORTED);
+    mCurrentNode = nsnull;
     EmptyQueue(PR_FALSE);
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS4(nsPrefetchService,
@@ -592,16 +747,17 @@ NS_IMPL_ISUPPORTS4(nsPrefetchService,
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsIPrefetchService
 //-----------------------------------------------------------------------------
 
 nsresult
 nsPrefetchService::Prefetch(nsIURI *aURI,
                             nsIURI *aReferrerURI,
+                            nsIDOMNode *aSource,
                             PRBool aExplicit,
                             PRBool aOffline)
 {
     nsresult rv;
 
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aReferrerURI);
 
@@ -667,43 +823,27 @@ nsPrefetchService::Prefetch(nsIURI *aURI
         nsCAutoString query;
         rv = url->GetQuery(query);
         if (NS_FAILED(rv) || !query.IsEmpty()) {
             LOG(("rejected: URL has a query string\n"));
             return NS_ERROR_ABORT;
         }
     }
 
-    // 
+    //
     // cancel if being prefetched
     //
-    if (mCurrentChannel) {
-        nsCOMPtr<nsIURI> currentURI;
-        mCurrentChannel->GetURI(getter_AddRefs(currentURI));
-        if (currentURI) {
-            PRBool equals;
-            if (NS_SUCCEEDED(currentURI->Equals(aURI, &equals)) && equals) {
-                if (aOffline) {
-                    // We may still need to put it on the queue if the channel
-                    // isn't fetching to the offline cache
-                    nsCOMPtr<nsICachingChannel> cachingChannel =
-                        do_QueryInterface(mCurrentChannel, &rv);
-                    if (NS_SUCCEEDED(rv)) {
-                        PRBool offline;
-                        rv = cachingChannel->GetCacheForOfflineUse(&offline);
-                        if (NS_SUCCEEDED(rv) && offline) {
-                            LOG(("rejected: URL is already being prefetched\n"));
-                            return NS_ERROR_ABORT;
-                        }
-                    }
-                }
-                else {
-                    LOG(("rejected: URL is already being prefetched\n"));
-                    return NS_ERROR_ABORT;
-                }
+    if (mCurrentNode) {
+        PRBool equals;
+        if (NS_SUCCEEDED(mCurrentNode->mURI->Equals(aURI, &equals)) && equals) {
+            // We may still need to put it on the queue if the channel
+            // isn't fetching to the offline cache
+            if (!aOffline || mCurrentNode->mOffline) {
+                LOG(("rejected: URL is already being prefetched\n"));
+                return NS_ERROR_ABORT;
             }
         }
     }
 
     //
     // cancel if already on the prefetch queue
     //
     nsPrefetchNode *node = mQueueHead;
@@ -714,40 +854,144 @@ nsPrefetchService::Prefetch(nsIURI *aURI
                 // make sure the node is placed in the offline cache
                 node->mOffline = PR_TRUE;
             }
             LOG(("rejected: URL is already on prefetch queue\n"));
             return NS_ERROR_ABORT;
         }
     }
 
-    rv = EnqueueURI(aURI, aReferrerURI, aOffline);
+    nsRefPtr<nsPrefetchNode> enqueuedNode;
+    rv = EnqueueURI(aURI, aReferrerURI, aSource, aOffline,
+                    getter_AddRefs(enqueuedNode));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    NotifyLoadRequested(enqueuedNode);
+
     // if there are no pages loading, kick off the request immediately
     if (mStopCount == 0 && mHaveProcessed)
         ProcessNextURI();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrefetchService::PrefetchURI(nsIURI *aURI,
                                nsIURI *aReferrerURI,
+                               nsIDOMNode *aSource,
                                PRBool aExplicit)
 {
-    return Prefetch(aURI, aReferrerURI, aExplicit, PR_FALSE);
+    return Prefetch(aURI, aReferrerURI, aSource, aExplicit, PR_FALSE);
 }
 
 NS_IMETHODIMP
 nsPrefetchService::PrefetchURIForOfflineUse(nsIURI *aURI,
                                             nsIURI *aReferrerURI,
+                                            nsIDOMNode *aSource,
                                             PRBool aExplicit)
 {
-    return Prefetch(aURI, aReferrerURI, aExplicit, PR_TRUE);
+    return Prefetch(aURI, aReferrerURI, aSource, aExplicit, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsPrefetchService::EnumerateQueue(PRBool aIncludeNormalItems,
+                                  PRBool aIncludeOfflineItems,
+                                  nsISimpleEnumerator **aEnumerator)
+{
+    *aEnumerator = new nsPrefetchQueueEnumerator(this,
+                                                 aIncludeNormalItems,
+                                                 aIncludeOfflineItems);
+    if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*aEnumerator);
+
+    return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsPrefetchNode::nsIDOMLoadStatus
+//-----------------------------------------------------------------------------
+NS_IMETHODIMP
+nsPrefetchNode::GetSource(nsIDOMNode **aSource)
+{
+    *aSource = nsnull;
+    nsCOMPtr<nsIDOMNode> source = do_QueryReferent(mSource);
+    if (source)
+        source.swap(*aSource);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchNode::GetUri(nsAString &aURI)
+{
+    nsCAutoString spec;
+    nsresult rv = mURI->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    CopyUTF8toUTF16(spec, aURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchNode::GetTotalSize(PRInt32 *aTotalSize)
+{
+    if (mChannel) {
+        return mChannel->GetContentLength(aTotalSize);
+    }
+
+    *aTotalSize = -1;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchNode::GetLoadedSize(PRInt32 *aLoadedSize)
+{
+    *aLoadedSize = mBytesRead;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchNode::GetReadyState(PRUint16 *aReadyState)
+{
+    *aReadyState = mState;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrefetchNode::GetStatus(PRUint16 *aStatus)
+{
+    if (!mChannel) {
+        *aStatus = 0;
+        return NS_OK;
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 httpStatus;
+    rv = httpChannel->GetResponseStatus(&httpStatus);
+    if (rv == NS_ERROR_NOT_AVAILABLE) {
+        // Someone's calling this before we got a response... Check our
+        // ReadyState.  If we're at RECEIVING or LOADED, then this means the
+        // connection errored before we got any data; return a somewhat
+        // sensible error code in that case.
+        if (mState >= nsIDOMLoadStatus::RECEIVING) {
+            *aStatus = NS_ERROR_NOT_AVAILABLE;
+            return NS_OK;
+        }
+
+        *aStatus = 0;
+        return NS_OK;
+    }
+
+    NS_ENSURE_SUCCESS(rv, rv);
+    *aStatus = PRUint16(httpStatus);
+    return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsIWebProgressListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress,
--- a/uriloader/prefetch/nsPrefetchService.h
+++ b/uriloader/prefetch/nsPrefetchService.h
@@ -43,18 +43,20 @@
 #include "nsIObserver.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
 #include "nsIWebProgressListener.h"
 #include "nsIStreamListener.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOMLoadStatus.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
 
 class nsPrefetchService;
 class nsPrefetchListener;
 class nsPrefetchNode;
 class nsIOfflineCacheSession;
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService
@@ -70,91 +72,93 @@ public:
     NS_DECL_NSIPREFETCHSERVICE
     NS_DECL_NSIWEBPROGRESSLISTENER
     NS_DECL_NSIOBSERVER
 
     nsPrefetchService();
 
     nsresult Init();
     void     ProcessNextURI();
-    void     UpdateCurrentChannel(nsIChannel *c) { mCurrentChannel = c; }
+
+    nsPrefetchNode *GetCurrentNode() { return mCurrentNode.get(); }
+    nsPrefetchNode *GetQueueHead() { return mQueueHead; }
+
+    void NotifyLoadRequested(nsPrefetchNode *node);
+    void NotifyLoadCompleted(nsPrefetchNode *node);
 
 private:
     ~nsPrefetchService();
 
     nsresult Prefetch(nsIURI *aURI,
                       nsIURI *aReferrerURI,
+                      nsIDOMNode *aSource,
                       PRBool aExplicit,
                       PRBool aOffline);
 
     void     AddProgressListener();
     void     RemoveProgressListener();
-    nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aOffline);
-    nsresult DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI, PRBool *aOffline);
+    nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI,
+                        nsIDOMNode *aSource, PRBool aOffline,
+                        nsPrefetchNode **node);
+    nsresult EnqueueNode(nsPrefetchNode *node);
+    nsresult DequeueNode(nsPrefetchNode **node);
     void     EmptyQueue(PRBool includeOffline);
     nsresult SaveOfflineList(nsIURI *aDocumentUri,
                              nsIDOMDocument *aDoc);
     nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
 
     void     StartPrefetching();
     void     StopPrefetching();
 
     nsCOMPtr<nsIOfflineCacheSession>  mOfflineCacheSession;
     nsPrefetchNode                   *mQueueHead;
     nsPrefetchNode                   *mQueueTail;
-    nsCOMPtr<nsIChannel>              mCurrentChannel;
+    nsRefPtr<nsPrefetchNode>          mCurrentNode;
     PRInt32                           mStopCount;
     // true if pending document loads have ever reached zero.
     PRInt32                           mHaveProcessed;
     PRBool                            mDisabled;
     PRBool                            mFetchedOffline;
 
 };
 
 //-----------------------------------------------------------------------------
-// nsPrefetchListener
+// nsPrefetchNode
 //-----------------------------------------------------------------------------
 
-class nsPrefetchListener : public nsIStreamListener
-                         , public nsIInterfaceRequestor
-                         , public nsIChannelEventSink
+class nsPrefetchNode : public nsIDOMLoadStatus
+                     , public nsIStreamListener
+                     , public nsIInterfaceRequestor
+                     , public nsIChannelEventSink
 {
 public:
     NS_DECL_ISUPPORTS
+    NS_DECL_NSIDOMLOADSTATUS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NSICHANNELEVENTSINK
 
-    nsPrefetchListener(nsPrefetchService *aPrefetchService);
+    nsPrefetchNode(nsPrefetchService *aPrefetchService,
+                   nsIURI *aURI,
+                   nsIURI *aReferrerURI,
+                   nsIDOMNode *aSource,
+                   PRBool aOffline);
+
+    ~nsPrefetchNode() {}
+
+    nsresult OpenChannel();
+    nsresult CancelChannel(nsresult error);
+
+    nsPrefetchNode             *mNext;
+    nsCOMPtr<nsIURI>            mURI;
+    nsCOMPtr<nsIURI>            mReferrerURI;
+    nsCOMPtr<nsIWeakReference>  mSource;
+    PRBool                      mOffline;
 
 private:
-    ~nsPrefetchListener();
-
-    static NS_METHOD ConsumeSegments(nsIInputStream *, void *, const char *,
-                                     PRUint32, PRUint32, PRUint32 *);
-
-    nsPrefetchService *mService;
-};
-
-//-----------------------------------------------------------------------------
-// nsPrefetchNode
-//-----------------------------------------------------------------------------
-
-class nsPrefetchNode
-{
-public:
-    nsPrefetchNode(nsIURI *aURI,
-                   nsIURI *aReferrerURI,
-                   PRBool aOffline)
-        : mNext(nsnull)
-        , mURI(aURI)
-        , mReferrerURI(aReferrerURI)
-        , mOffline(aOffline)
-        { }
-
-    nsPrefetchNode  *mNext;
-    nsCOMPtr<nsIURI> mURI;
-    nsCOMPtr<nsIURI> mReferrerURI;
-    PRBool           mOffline;
+    nsRefPtr<nsPrefetchService> mService;
+    nsCOMPtr<nsIChannel>        mChannel;
+    PRUint16                    mState;
+    PRInt32                     mBytesRead;
 };
 
 #endif // !nsPrefetchService_h__