--- 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__