зеркало из https://github.com/mozilla/pjs.git
Bug 402272: Replace <link rel="offline-resource"> with manifests, and navigator.offlineResources/pendingOfflineLoads with window.applicationCache. p=dcamp/Honza Bombas, r=biesi, r+sr=jst, blocking1.9=jst
This commit is contained in:
Родитель
a355d3804f
Коммит
3494bf7df2
|
@ -1152,6 +1152,21 @@ public:
|
|||
nsNativeKeyEvent* aNativeEvent,
|
||||
PRBool aGetCharCode);
|
||||
|
||||
/**
|
||||
* Get the application manifest URI for this context. The manifest URI
|
||||
* is specified in the manifest= attribute of the root element of the
|
||||
* toplevel window.
|
||||
*
|
||||
* @param aWindow The context to check.
|
||||
* @param aURI The manifest URI.
|
||||
*/
|
||||
static void GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI);
|
||||
|
||||
/**
|
||||
* Check whether an application should be allowed to use offline APIs.
|
||||
*/
|
||||
static PRBool OfflineAppAllowed(nsIURI *aURI);
|
||||
|
||||
private:
|
||||
|
||||
static PRBool InitializeEventTable();
|
||||
|
@ -1223,7 +1238,6 @@ private:
|
|||
static PRBool sInitialized;
|
||||
};
|
||||
|
||||
|
||||
#define NS_HOLD_JS_OBJECTS(obj, clazz) \
|
||||
nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz), \
|
||||
&NS_CYCLE_COLLECTION_NAME(clazz))
|
||||
|
|
|
@ -70,10 +70,8 @@
|
|||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsICache.h"
|
||||
#include "nsICacheService.h"
|
||||
#include "nsICacheSession.h"
|
||||
#include "nsIOfflineCacheUpdate.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIDOMLoadStatus.h"
|
||||
#include "nsICookieService.h"
|
||||
#include "nsIPrompt.h"
|
||||
|
@ -94,6 +92,7 @@
|
|||
#include "nsIDOMNode.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsPresShellIterator.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
PRLogModuleInfo* gContentSinkLogModuleInfo;
|
||||
|
||||
|
@ -684,11 +683,6 @@ nsContentSink::ProcessLink(nsIContent* aElement,
|
|||
PrefetchHref(aHref, aElement, hasPrefetch);
|
||||
}
|
||||
|
||||
// fetch href into the offline cache if relation is "offline-resource"
|
||||
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
|
||||
AddOfflineResource(aHref, aElement);
|
||||
}
|
||||
|
||||
// is it a stylesheet link?
|
||||
if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) {
|
||||
return NS_OK;
|
||||
|
@ -820,64 +814,62 @@ nsContentSink::PrefetchHref(const nsAString &aHref,
|
|||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentSink::AddOfflineResource(const nsAString &aHref, nsIContent *aSource)
|
||||
void
|
||||
nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
|
||||
{
|
||||
PRBool match;
|
||||
nsresult rv;
|
||||
// Check for a manifest= attribute.
|
||||
nsAutoString manifestSpec;
|
||||
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
|
||||
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
|
||||
if (!innerURI)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (!mHaveOfflineResources) {
|
||||
mHaveOfflineResources = PR_TRUE;
|
||||
|
||||
// only let http and https urls add offline resources
|
||||
nsresult rv = innerURI->SchemeIs("http", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!match) {
|
||||
rv = innerURI->SchemeIs("https", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!match)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// create updater
|
||||
mOfflineCacheUpdate =
|
||||
do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString ownerDomain;
|
||||
rv = innerURI->GetHostPort(ownerDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString ownerSpec;
|
||||
rv = mDocumentURI->GetSpec(ownerSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mOfflineCacheUpdate->Init(PR_FALSE, ownerDomain,
|
||||
ownerSpec, mDocumentURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Kick off this update when the document is done loading
|
||||
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
|
||||
mOfflineCacheUpdate->ScheduleOnDocumentStop(doc);
|
||||
if (manifestSpec.IsEmpty() ||
|
||||
manifestSpec.FindChar('#') != kNotFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mOfflineCacheUpdate) return NS_OK;
|
||||
// We only care about manifests in toplevel windows.
|
||||
nsCOMPtr<nsPIDOMWindow> pwindow =
|
||||
do_QueryInterface(mDocument->GetScriptGlobalObject());
|
||||
if (!pwindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nsACString &charset = mDocument->GetDocumentCharacterSet();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), aHref,
|
||||
charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
|
||||
mDocumentBaseURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(pwindow->GetOuterWindow());
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
|
||||
nsCOMPtr<nsIDOMWindow> parent;
|
||||
window->GetParent(getter_AddRefs(parent));
|
||||
if (parent.get() != window.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return mOfflineCacheUpdate->AddURI(uri, domNode);
|
||||
// Only update if the document has permission to use offline APIs.
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> manifestURI;
|
||||
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
|
||||
manifestSpec, mDocument,
|
||||
mDocumentURI);
|
||||
if (!manifestURI) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Documents must list a manifest from the same origin
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckSameOriginURI(manifestURI, mDocumentURI, PR_TRUE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the update
|
||||
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
|
||||
nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
|
||||
do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
|
||||
updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -75,7 +75,6 @@ class nsIContent;
|
|||
class nsIViewManager;
|
||||
class nsNodeInfoManager;
|
||||
class nsScriptLoader;
|
||||
class nsIOfflineCacheUpdate;
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
|
||||
|
@ -169,7 +168,7 @@ protected:
|
|||
|
||||
void PrefetchHref(const nsAString &aHref, nsIContent *aSource,
|
||||
PRBool aExplicit);
|
||||
nsresult AddOfflineResource(const nsAString &aHref, nsIContent *aSource);
|
||||
void ProcessOfflineManifest(nsIContent *aElement);
|
||||
|
||||
void ScrollToRef();
|
||||
nsresult RefreshIfEnabled(nsIViewManager* vm);
|
||||
|
@ -260,9 +259,6 @@ protected:
|
|||
// Do we notify based on time?
|
||||
PRPackedBool mNotifyOnTimer;
|
||||
|
||||
// For saving <link rel="offline-resource"> links
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> mOfflineCacheUpdate;
|
||||
|
||||
// Have we already called BeginUpdate for this set of content changes?
|
||||
PRUint8 mBeganUpdate : 1;
|
||||
PRUint8 mLayoutStarted : 1;
|
||||
|
@ -275,8 +271,6 @@ protected:
|
|||
PRUint8 mChangeScrollPosWhenScrollingToRef : 1;
|
||||
// If true, we deferred starting layout until sheets load
|
||||
PRUint8 mDeferredLayoutStart : 1;
|
||||
// true if an <link rel="offline-resource"> nodes have been encountered.
|
||||
PRUint8 mHaveOfflineResources : 1;
|
||||
// If true, we deferred notifications until sheets load
|
||||
PRUint8 mDeferredFlushTags : 1;
|
||||
|
||||
|
|
|
@ -145,6 +145,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
|
|||
#include "nsIDOMNSUIEvent.h"
|
||||
#include "nsIDOMNSEvent.h"
|
||||
#include "nsIPrivateDOMEvent.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
#ifdef IBMBIDI
|
||||
#include "nsIBidiKeyboard.h"
|
||||
|
@ -675,6 +676,82 @@ nsContentUtils::IsPunctuationMark(PRUnichar aChar)
|
|||
return CCMAP_HAS_CHAR(gPuncCharsCCMap, aChar);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI)
|
||||
{
|
||||
nsCOMPtr<nsIDOMWindow> top;
|
||||
aWindow->GetTop(getter_AddRefs(top));
|
||||
if (!top) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> topDOMDocument;
|
||||
top->GetDocument(getter_AddRefs(topDOMDocument));
|
||||
nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(topDOMDocument);
|
||||
if (!topDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> docElement = topDoc->GetRootContent();
|
||||
if (!docElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString manifestSpec;
|
||||
docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
|
||||
|
||||
// Manifest URIs can't have fragment identifiers.
|
||||
if (manifestSpec.IsEmpty() ||
|
||||
manifestSpec.FindChar('#') != kNotFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec,
|
||||
topDoc, topDoc->GetBaseURI());
|
||||
}
|
||||
|
||||
/* static */
|
||||
PRBool
|
||||
nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
|
||||
if (!innerURI)
|
||||
return PR_FALSE;
|
||||
|
||||
// only http and https applications can use offline APIs.
|
||||
PRBool match;
|
||||
nsresult rv = innerURI->SchemeIs("http", &match);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
if (!match) {
|
||||
rv = innerURI->SchemeIs("https", &match);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
if (!match) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
if (!permissionManager) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRUint32 perm;
|
||||
permissionManager->TestExactPermission(innerURI, "offline-app", &perm);
|
||||
|
||||
if (perm == nsIPermissionManager::UNKNOWN_ACTION) {
|
||||
return GetBoolPref("offline-apps.allow_by_default");
|
||||
}
|
||||
|
||||
if (perm == nsIPermissionManager::DENY_ACTION) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsContentUtils::Shutdown()
|
||||
|
|
|
@ -476,6 +476,7 @@ GK_ATOM(lowest, "lowest")
|
|||
GK_ATOM(lowsrc, "lowsrc")
|
||||
GK_ATOM(ltr, "ltr")
|
||||
GK_ATOM(map, "map")
|
||||
GK_ATOM(manifest, "manifest")
|
||||
GK_ATOM(marginheight, "marginheight")
|
||||
GK_ATOM(marginwidth, "marginwidth")
|
||||
GK_ATOM(marquee, "marquee")
|
||||
|
|
|
@ -2346,6 +2346,7 @@ HTMLContentSink::OpenContainer(const nsIParserNode& aNode)
|
|||
if (!mNotifiedRootInsertion) {
|
||||
NotifyRootInsertion();
|
||||
}
|
||||
ProcessOfflineManifest(mRoot);
|
||||
}
|
||||
break;
|
||||
case eHTMLTag_form:
|
||||
|
@ -2992,13 +2993,6 @@ HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
|
|||
PrefetchHref(hrefVal, element, hasPrefetch);
|
||||
}
|
||||
}
|
||||
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
|
||||
nsAutoString hrefVal;
|
||||
element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
|
||||
if (!hrefVal.IsEmpty()) {
|
||||
AddOfflineResource(hrefVal, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,8 +72,8 @@ class nsPresContext;
|
|||
struct nsTimeout;
|
||||
|
||||
#define NS_PIDOMWINDOW_IID \
|
||||
{ 0x51316cbc, 0x595e, 0x40b1, \
|
||||
{ 0xab, 0x07, 0x21, 0xbf, 0xe0, 0x92, 0x62, 0xa9 } }
|
||||
{ 0x909852b5, 0xb9e6, 0x4d94, \
|
||||
{ 0x8d, 0xe3, 0x05, 0x16, 0x34, 0x80, 0x0b, 0x73 } }
|
||||
|
||||
class nsPIDOMWindow : public nsIDOMWindowInternal
|
||||
{
|
||||
|
@ -272,6 +272,8 @@ public:
|
|||
// the window was frozen.
|
||||
virtual nsresult FireDelayedDOMEvents() = 0;
|
||||
|
||||
virtual PRBool IsFrozen() const = 0;
|
||||
|
||||
// Add a timeout to this window.
|
||||
virtual nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
||||
PRInt32 interval,
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
interface nsIDOMOfflineResourceList;
|
||||
interface nsIDOMLoadStatusList;
|
||||
|
||||
[scriptable, uuid(02bb1271-05dd-4bde-a9ca-68571bf8c702)]
|
||||
[scriptable, uuid(4de9dbd5-0eff-47aa-8520-c062c6933d6a)]
|
||||
interface nsIDOMClientInformation : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -53,9 +53,6 @@ interface nsIDOMClientInformation : nsISupports
|
|||
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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
|
||||
#include "nsIDOMWindow.idl"
|
||||
|
||||
[scriptable, uuid(65455132-b96a-40ec-adea-52fa22b1028c)]
|
||||
interface nsIDOMOfflineResourceList;
|
||||
|
||||
[scriptable, uuid(73c5fa35-3add-4c87-a303-a850ccf4d65a)]
|
||||
interface nsIDOMWindow2 : nsIDOMWindow
|
||||
{
|
||||
/**
|
||||
|
@ -46,4 +48,9 @@ interface nsIDOMWindow2 : nsIDOMWindow
|
|||
* in the window root.
|
||||
*/
|
||||
[noscript] readonly attribute nsIDOMEventTarget windowRoot;
|
||||
|
||||
/**
|
||||
* Get the application cache object for this window.
|
||||
*/
|
||||
readonly attribute nsIDOMOfflineResourceList applicationCache;
|
||||
};
|
||||
|
|
|
@ -41,13 +41,76 @@
|
|||
[scriptable, uuid(8449bce2-0d8c-4c74-ab79-b41b8d81f1c4)]
|
||||
interface nsIDOMOfflineResourceList : nsISupports
|
||||
{
|
||||
/**
|
||||
* Enumerate the list of dynamically-managed entries.
|
||||
*/
|
||||
readonly attribute unsigned long length;
|
||||
DOMString item(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Add an item to the list of dynamically-managed entries. The resource
|
||||
* will be fetched into the application cache.
|
||||
*
|
||||
* @param uri
|
||||
* The resource to add.
|
||||
*/
|
||||
void add(in DOMString uri);
|
||||
void remove(in DOMString uri);
|
||||
boolean has(in DOMString uri);
|
||||
void clear();
|
||||
|
||||
void refresh();
|
||||
/**
|
||||
* Remove an item from the list of dynamically-managed entries. If this
|
||||
* was the last reference to a URI in the application cache, the cache
|
||||
* entry will be removed.
|
||||
*
|
||||
* @param uri
|
||||
* The resource to remove.
|
||||
*/
|
||||
void remove(in DOMString uri);
|
||||
|
||||
/**
|
||||
* State of the application cache this object is associated with.
|
||||
*/
|
||||
|
||||
/* This object is not associated with an application cache. */
|
||||
const unsigned short UNCACHED = 0;
|
||||
|
||||
/* The application cache is not being updated. */
|
||||
const unsigned short IDLE = 1;
|
||||
|
||||
/* The manifest is being fetched and checked for updates */
|
||||
const unsigned short CHECKING = 2;
|
||||
|
||||
/* Resources are being downloaded to be added to the cache */
|
||||
const unsigned short DOWNLOADING = 3;
|
||||
|
||||
/**
|
||||
* There is a new version of the application cache available
|
||||
*
|
||||
* Versioned application caches are not currently implemented, so this
|
||||
* value will not yet be returned
|
||||
*/
|
||||
const unsigned short UPDATEREADY = 4;
|
||||
|
||||
readonly attribute unsigned short status;
|
||||
|
||||
/**
|
||||
* Begin the application update process on the associated application cache.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Swap in the newest version of the application cache.
|
||||
*
|
||||
* Versioned application caches are not currently implemented, so this
|
||||
* method will throw an exception.
|
||||
*/
|
||||
void swapCache();
|
||||
|
||||
/* Events */
|
||||
attribute nsIDOMEventListener onchecking;
|
||||
attribute nsIDOMEventListener onerror;
|
||||
attribute nsIDOMEventListener onnoupdate;
|
||||
attribute nsIDOMEventListener ondownloading;
|
||||
attribute nsIDOMEventListener onprogress;
|
||||
attribute nsIDOMEventListener onupdateready;
|
||||
attribute nsIDOMEventListener oncached;
|
||||
};
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "nsBarProps.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsDOMOfflineResourceList.h"
|
||||
#include "nsDOMOfflineLoadStatusList.h"
|
||||
#include "nsDOMError.h"
|
||||
|
||||
// Helper Classes
|
||||
|
@ -764,6 +763,7 @@ nsGlobalWindow::CleanUp()
|
|||
mScrollbars = nsnull;
|
||||
mLocation = nsnull;
|
||||
mFrames = nsnull;
|
||||
mApplicationCache = nsnull;
|
||||
|
||||
ClearControllers();
|
||||
|
||||
|
@ -849,6 +849,11 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope)
|
|||
mDocument = nsnull;
|
||||
mDoc = nsnull;
|
||||
|
||||
if (mApplicationCache) {
|
||||
static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
|
||||
mApplicationCache = nsnull;
|
||||
}
|
||||
|
||||
if (aClearScope) {
|
||||
PRUint32 lang_id;
|
||||
NS_STID_FOR_ID(lang_id) {
|
||||
|
@ -940,6 +945,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpenerScriptPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSessionStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplicationCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc)
|
||||
|
||||
|
@ -983,6 +989,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpenerScriptPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSessionStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplicationCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocumentPrincipal)
|
||||
|
||||
// Unlink any associated preserved wrapper.
|
||||
|
@ -2689,6 +2696,46 @@ nsGlobalWindow::GetFrames(nsIDOMWindowCollection** aFrames)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache)
|
||||
{
|
||||
FORWARD_TO_INNER(GetApplicationCache, (aApplicationCache), NS_ERROR_UNEXPECTED);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aApplicationCache);
|
||||
|
||||
if (!mApplicationCache) {
|
||||
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);
|
||||
|
||||
nsCOMPtr<nsIURI> manifestURI;
|
||||
nsContentUtils::GetOfflineAppManifest(GetOuterWindowInternal(),
|
||||
getter_AddRefs(manifestURI));
|
||||
|
||||
PRBool isToplevel;
|
||||
nsCOMPtr<nsIDOMWindow> parentWindow;
|
||||
GetParent(getter_AddRefs(parentWindow));
|
||||
isToplevel = (parentWindow.get() == static_cast<nsIDOMWindow*>(GetOuterWindowInternal()));
|
||||
|
||||
nsDOMOfflineResourceList* applicationCache =
|
||||
new nsDOMOfflineResourceList(isToplevel, manifestURI, uri, this);
|
||||
|
||||
if (!applicationCache)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mApplicationCache = applicationCache;
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
|
||||
{
|
||||
|
@ -6653,6 +6700,10 @@ nsGlobalWindow::FireDelayedDOMEvents()
|
|||
mPendingStorageEvents = nsnull;
|
||||
}
|
||||
|
||||
if (mApplicationCache) {
|
||||
static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
|
||||
}
|
||||
|
||||
if (mFireOfflineStatusChangeEventOnThaw) {
|
||||
mFireOfflineStatusChangeEventOnThaw = PR_FALSE;
|
||||
FireOfflineStatusEvent();
|
||||
|
@ -8996,8 +9047,6 @@ nsNavigator::LoadingNewDocument()
|
|||
// arrays may have changed. See bug 150087.
|
||||
mMimeTypes = nsnull;
|
||||
mPlugins = nsnull;
|
||||
mOfflineResources = nsnull;
|
||||
mPendingOfflineLoads = nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -9118,51 +9167,3 @@ nsNavigator::IsLocallyAvailable(const nsAString &aURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavigator::GetOfflineResources(nsIDOMOfflineResourceList **aList)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aList);
|
||||
|
||||
if (!mOfflineResources) {
|
||||
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);
|
||||
|
||||
mOfflineResources = new nsDOMOfflineResourceList(uri);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -301,6 +301,10 @@ public:
|
|||
virtual NS_HIDDEN_(nsresult) RestoreWindowState(nsISupports *aState);
|
||||
virtual NS_HIDDEN_(nsresult) ResumeTimeouts();
|
||||
virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents();
|
||||
virtual NS_HIDDEN_(PRBool) IsFrozen() const
|
||||
{
|
||||
return mIsFrozen;
|
||||
}
|
||||
|
||||
virtual NS_HIDDEN_(PRBool) WouldReuseInnerWindow(nsIDocument *aNewDocument);
|
||||
|
||||
|
@ -392,11 +396,6 @@ public:
|
|||
return static_cast<nsGlobalWindow *>(EnsureInnerWindow());
|
||||
}
|
||||
|
||||
PRBool IsFrozen() const
|
||||
{
|
||||
return mIsFrozen;
|
||||
}
|
||||
|
||||
PRBool IsCreatingInnerWindow() const
|
||||
{
|
||||
return mCreatingInnerWindow;
|
||||
|
@ -718,6 +717,8 @@ protected:
|
|||
PRBool mSetOpenerWindowCalled;
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
|
||||
|
||||
friend class nsDOMScriptableHelper;
|
||||
friend class nsDOMWindowUtils;
|
||||
static nsIFactory *sComputedDOMStyleFactory;
|
||||
|
@ -804,8 +805,6 @@ public:
|
|||
protected:
|
||||
nsRefPtr<nsMimeTypeArray> mMimeTypes;
|
||||
nsRefPtr<nsPluginArray> mPlugins;
|
||||
nsRefPtr<nsDOMOfflineResourceList> mOfflineResources;
|
||||
nsRefPtr<nsDOMOfflineLoadStatusList> mPendingOfflineLoads;
|
||||
nsIDocShell* mDocShell; // weak reference
|
||||
|
||||
static jsval sPrefInternal_id;
|
||||
|
|
|
@ -65,7 +65,6 @@ REQUIRES = xpcom \
|
|||
|
||||
CPPSRCS = \
|
||||
nsDOMOfflineResourceList.cpp \
|
||||
nsDOMOfflineLoadStatusList.cpp \
|
||||
$(NULL)
|
||||
|
||||
# we don't want the shared lib, but we want to force the creation of a static lib.
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
/* -*- 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 "nsIOfflineCacheUpdate.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 UpdateAdded (nsIOfflineCacheUpdate *aUpdate);
|
||||
nsresult UpdateCompleted (nsIOfflineCacheUpdate *aUpdate);
|
||||
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<nsIScriptContext> mScriptContext;
|
||||
|
||||
nsCOMArray<nsIDOMEventListener> mLoadRequestedEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mLoadCompletedEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mUpdateCompletedEventListeners;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
@ -50,6 +50,21 @@
|
|||
#include "nsIDOMLoadStatus.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsIPrivateDOMEvent.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
|
||||
// Event names
|
||||
|
||||
#define CHECKING_STR "checking"
|
||||
#define ERROR_STR "error"
|
||||
#define NOUPDATE_STR "noupdate"
|
||||
#define DOWNLOADING_STR "downloading"
|
||||
#define PROGRESS_STR "progress"
|
||||
#define CACHED_STR "cached"
|
||||
#define UPDATEREADY_STR "updateready"
|
||||
|
||||
// To prevent abuse of the resource list for data storage, the number
|
||||
// of offline urls and their length are limited.
|
||||
|
@ -66,19 +81,89 @@ static PRUint32 gCachedKeysCount = 0;
|
|||
// nsDOMOfflineResourceList
|
||||
//
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMOfflineResourceList)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMOfflineResourceList)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMOfflineResourceList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheSession)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheUpdate)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCheckingListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mErrorListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mNoUpdateListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mDownloadingListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mProgressListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCachedListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mUpdateReadyListeners)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCheckingListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnNoUpdateListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnDownloadingListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCachedListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUpdateReadyListener)
|
||||
|
||||
for (PRUint32 i = 0; i < tmp->mPendingEvents.Length(); i++) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPendingEvents[i].event);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPendingEvents[i].listener);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingEvents[i].listeners);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMOfflineResourceList)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheSession)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheUpdate)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCheckingListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mErrorListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mNoUpdateListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mDownloadingListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mProgressListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mCachedListeners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mUpdateReadyListeners)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCheckingListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnNoUpdateListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnDownloadingListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCachedListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUpdateReadyListener)
|
||||
|
||||
for (PRUint32 i = 0; i < tmp->mPendingEvents.Length(); i++) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPendingEvents[i].event);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPendingEvents[i].listener);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingEvents[i].listeners);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMOfflineResourceList)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMOfflineResourceList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMOfflineResourceList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(OfflineResourceList)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsDOMOfflineResourceList)
|
||||
NS_IMPL_RELEASE(nsDOMOfflineResourceList)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMOfflineResourceList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMOfflineResourceList)
|
||||
|
||||
nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aURI)
|
||||
nsDOMOfflineResourceList::nsDOMOfflineResourceList(PRBool aToplevel,
|
||||
nsIURI *aManifestURI,
|
||||
nsIURI *aDocumentURI,
|
||||
nsIDOMWindow *aWindow)
|
||||
: mInitialized(PR_FALSE)
|
||||
, mURI(aURI)
|
||||
, mToplevel(aToplevel)
|
||||
, mManifestURI(aManifestURI)
|
||||
, mDocumentURI(aDocumentURI)
|
||||
{
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
}
|
||||
|
||||
nsDOMOfflineResourceList::~nsDOMOfflineResourceList()
|
||||
|
@ -92,11 +177,26 @@ nsDOMOfflineResourceList::Init()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mURI);
|
||||
if (!mManifestURI) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckSameOriginURI(mManifestURI, mDocumentURI, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Dynamically-managed resources are stored as a separate ownership list
|
||||
// from the manifest.
|
||||
rv = mManifestURI->GetSpec(mDynamicOwnerSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDynamicOwnerSpec.Append("#dynamic");
|
||||
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
|
||||
if (!innerURI)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv = innerURI->GetHostPort(mHostPort);
|
||||
rv = innerURI->GetHostPort(mHostPort);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsICacheService> serv = do_GetService(NS_CACHESERVICE_CONTRACTID,
|
||||
|
@ -113,11 +213,61 @@ nsDOMOfflineResourceList::Init()
|
|||
mCacheSession = do_QueryInterface(session, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check for in-progress cache updates
|
||||
nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
|
||||
do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 numUpdates;
|
||||
rv = cacheUpdateService->GetNumUpdates(&numUpdates);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRUint32 i = 0; i < numUpdates; i++) {
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
|
||||
rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateAdded(cacheUpdate);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// watch for new offline cache updates
|
||||
nsCOMPtr<nsIObserverService> observerServ =
|
||||
do_GetService("@mozilla.org/observer-service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = observerServ->AddObserver(this, "offline-cache-update-added", PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = observerServ->AddObserver(this, "offline-cache-update-completed", PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mInitialized = PR_TRUE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMOfflineResourceList::Disconnect()
|
||||
{
|
||||
mCheckingListeners.Clear();
|
||||
mErrorListeners.Clear();
|
||||
mNoUpdateListeners.Clear();
|
||||
mDownloadingListeners.Clear();
|
||||
mProgressListeners.Clear();
|
||||
mCachedListeners.Clear();
|
||||
mUpdateReadyListeners.Clear();
|
||||
|
||||
mOnCheckingListener = nsnull;
|
||||
mOnErrorListener = nsnull;
|
||||
mOnNoUpdateListener = nsnull;
|
||||
mOnDownloadingListener = nsnull;
|
||||
mOnProgressListener = nsnull;
|
||||
mOnCachedListener = nsnull;
|
||||
mOnUpdateReadyListener = nsnull;
|
||||
|
||||
mPendingEvents.Clear();
|
||||
}
|
||||
|
||||
//
|
||||
// nsDOMOfflineResourceList::nsIDOMOfflineResourceList
|
||||
//
|
||||
|
@ -128,6 +278,10 @@ nsDOMOfflineResourceList::GetLength(PRUint32 *aLength)
|
|||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
rv = CacheKeys();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -141,6 +295,10 @@ nsDOMOfflineResourceList::Item(PRUint32 aIndex, nsAString& aURI)
|
|||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
SetDOMStringToNull(aURI);
|
||||
|
||||
rv = CacheKeys();
|
||||
|
@ -160,6 +318,10 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
|
|||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI;
|
||||
|
||||
// this will fail if the URI is not absolute
|
||||
|
@ -167,6 +329,18 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
|
|||
rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString scheme;
|
||||
rv = requestedURI->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool match;
|
||||
rv = mManifestURI->SchemeIs(scheme.get(), &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!match) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
PRUint32 length;
|
||||
rv = GetLength(&length);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -181,10 +355,10 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
|
|||
do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->Init(PR_TRUE, mHostPort, NS_LITERAL_CSTRING(""), mURI);
|
||||
rv = update->Init(PR_TRUE, mManifestURI, mDocumentURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->AddURI(requestedURI, nsnull);
|
||||
rv = update->AddDynamicURI(requestedURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->Schedule();
|
||||
|
@ -199,61 +373,522 @@ nsDOMOfflineResourceList::Remove(const nsAString& aURI)
|
|||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCAutoString key;
|
||||
rv = GetCacheKey(aURI, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ClearCachedKeys();
|
||||
|
||||
return mCacheSession->RemoveOwnedKey(mHostPort,
|
||||
NS_LITERAL_CSTRING(""),
|
||||
key);
|
||||
rv = mCacheSession->RemoveOwnedKey(mHostPort, mDynamicOwnerSpec, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mCacheSession->EvictUnownedEntries();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Has(const nsAString& aURI, PRBool *aExists)
|
||||
nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
|
||||
// It is OK to check the status without a manifest attribute (you'll
|
||||
// just get "uncached").
|
||||
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR && !mManifestURI) {
|
||||
*aStatus = nsIDOMOfflineResourceList::UNCACHED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mCacheUpdate) {
|
||||
return mCacheUpdate->GetStatus(aStatus);
|
||||
}
|
||||
|
||||
// XXX: the spec allows either UNCACHED or IDLE, depending on whether
|
||||
// the application is associated with a specific offline cache. Until
|
||||
// we have versioned application caches, the best approximation is
|
||||
// probably IDLE if the offline-app permission is set, and UNCACHED
|
||||
// otherwise.
|
||||
|
||||
if (nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
*aStatus = nsIDOMOfflineResourceList::IDLE;
|
||||
} else {
|
||||
*aStatus = nsIDOMOfflineResourceList::UNCACHED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Update()
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString key;
|
||||
rv = GetCacheKey(aURI, key);
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
|
||||
do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mCacheSession->KeyIsOwned(mHostPort, NS_LITERAL_CSTRING(""),
|
||||
key, aExists);
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> update;
|
||||
rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI,
|
||||
getter_AddRefs(update));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Clear()
|
||||
nsDOMOfflineResourceList::SwapCache()
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ClearCachedKeys();
|
||||
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
return mCacheSession->SetOwnedKeys(mHostPort,
|
||||
NS_LITERAL_CSTRING(""),
|
||||
0, nsnull);
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//
|
||||
// nsDOMOfflineResourceList::nsIDOMEventTarget
|
||||
//
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Refresh()
|
||||
nsDOMOfflineResourceList::GetOnchecking(nsIDOMEventListener **aOnchecking)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> update =
|
||||
do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
|
||||
NS_ENSURE_ARG_POINTER(aOnchecking);
|
||||
NS_IF_ADDREF(*aOnchecking = mOnCheckingListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOnchecking(nsIDOMEventListener *aOnchecking)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->Init(PR_FALSE, mHostPort, NS_LITERAL_CSTRING(""), mURI);
|
||||
mOnCheckingListener = aOnchecking;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOnerror(nsIDOMEventListener **aOnerror)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->Schedule();
|
||||
NS_ENSURE_ARG_POINTER(aOnerror);
|
||||
NS_IF_ADDREF(*aOnerror = mOnErrorListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOnerror(nsIDOMEventListener *aOnerror)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
mOnErrorListener = aOnerror;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOnnoupdate(nsIDOMEventListener **aOnnoupdate)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOnnoupdate);
|
||||
NS_IF_ADDREF(*aOnnoupdate = mOnNoUpdateListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOnnoupdate(nsIDOMEventListener *aOnnoupdate)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOnNoUpdateListener = aOnnoupdate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOndownloading(nsIDOMEventListener **aOndownloading)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOndownloading);
|
||||
NS_IF_ADDREF(*aOndownloading = mOnDownloadingListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOndownloading(nsIDOMEventListener *aOndownloading)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOnDownloadingListener = aOndownloading;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOnprogress(nsIDOMEventListener **aOnprogress)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOnprogress);
|
||||
NS_IF_ADDREF(*aOnprogress = mOnProgressListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOnprogress(nsIDOMEventListener *aOnprogress)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOnProgressListener = aOnprogress;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOnupdateready(nsIDOMEventListener **aOnupdateready)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOnupdateready);
|
||||
NS_IF_ADDREF(*aOnupdateready = mOnUpdateReadyListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOncached(nsIDOMEventListener *aOncached)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOnCachedListener = aOncached;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::GetOncached(nsIDOMEventListener **aOncached)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOncached);
|
||||
NS_IF_ADDREF(*aOncached = mOnCachedListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::SetOnupdateready(nsIDOMEventListener *aOnupdateready)
|
||||
{
|
||||
nsresult rv = Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOnUpdateReadyListener = aOnupdateready;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::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(CHECKING_STR, mCheckingListeners)
|
||||
IMPL_ADD_LISTENER(ERROR_STR, mErrorListeners)
|
||||
IMPL_ADD_LISTENER(NOUPDATE_STR, mNoUpdateListeners)
|
||||
IMPL_ADD_LISTENER(DOWNLOADING_STR, mDownloadingListeners)
|
||||
IMPL_ADD_LISTENER(PROGRESS_STR, mProgressListeners)
|
||||
IMPL_ADD_LISTENER(CACHED_STR, mCachedListeners)
|
||||
IMPL_ADD_LISTENER(UPDATEREADY_STR, mUpdateReadyListeners)
|
||||
{
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
array->AppendObject(aListener);
|
||||
#undef IMPL_ADD_LISTENER
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::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(CHECKING_STR, mCheckingListeners)
|
||||
IMPL_REMOVE_LISTENER(ERROR_STR, mErrorListeners)
|
||||
IMPL_REMOVE_LISTENER(NOUPDATE_STR, mNoUpdateListeners)
|
||||
IMPL_REMOVE_LISTENER(DOWNLOADING_STR, mDownloadingListeners)
|
||||
IMPL_REMOVE_LISTENER(PROGRESS_STR, mProgressListeners)
|
||||
IMPL_REMOVE_LISTENER(CACHED_STR, mCachedListeners)
|
||||
IMPL_REMOVE_LISTENER(UPDATEREADY_STR, mUpdateReadyListeners)
|
||||
{
|
||||
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_REMOVE_LISTENER
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::DispatchEvent(nsIDOMEvent *evt, PRBool *_retval)
|
||||
{
|
||||
// Ignored
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMOfflineResourceList::NotifyEventListeners(nsIDOMEventListener *aListener,
|
||||
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;
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = do_QueryReferent(mWindow);
|
||||
if (!scriptGlobal)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIScriptContext> context = scriptGlobal->GetContext();
|
||||
if (context) {
|
||||
stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
|
||||
|
||||
if (stack) {
|
||||
cx = (JSContext *)context->GetNativeContext();
|
||||
|
||||
if (cx) {
|
||||
stack->Push(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aListener) {
|
||||
aListener->HandleEvent(aEvent);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMOfflineResourceList::FirePendingEvents()
|
||||
{
|
||||
for (PRUint32 i = 0; i < mPendingEvents.Length(); i++) {
|
||||
const PendingEvent &pending = mPendingEvents[i];
|
||||
NotifyEventListeners(pending.listener, pending.listeners, pending.event);
|
||||
}
|
||||
mPendingEvents.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMOfflineResourceList::SendEvent(const nsAString &aEventName,
|
||||
nsIDOMEventListener *aListener,
|
||||
const nsCOMArray<nsIDOMEventListener> &aListeners)
|
||||
{
|
||||
// Only toplevel windows get application cache events.
|
||||
if (!mToplevel) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aListener && aListeners.Count() == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Don't send events to closed windows
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
if (!window) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!window->GetDocShell()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
|
||||
NS_LITERAL_STRING("Events"),
|
||||
getter_AddRefs(event));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privevent = do_QueryInterface(event);
|
||||
if (!privevent) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
event->InitEvent(aEventName, PR_FALSE, PR_TRUE);
|
||||
|
||||
privevent->SetTarget(this);
|
||||
privevent->SetCurrentTarget(this);
|
||||
privevent->SetOriginalTarget(this);
|
||||
|
||||
// We assume anyone that managed to call SendEvent is trusted
|
||||
privevent->SetTrusted(PR_TRUE);
|
||||
|
||||
// If the window is frozen or we're still catching up on events that were
|
||||
// queued while frozen, save the event for later.
|
||||
if (window->IsFrozen() || mPendingEvents.Length() > 0) {
|
||||
PendingEvent *pending = mPendingEvents.AppendElement();
|
||||
pending->event = event;
|
||||
pending->listener = aListener;
|
||||
pending->listeners.SetCapacity(aListeners.Count());
|
||||
pending->listeners.AppendObjects(aListeners);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NotifyEventListeners(aListener, aListeners, event);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// nsDOMOfflineResourceList::nsIObserver
|
||||
//
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
if (!strcmp(aTopic, "offline-cache-update-added")) {
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
|
||||
if (update) {
|
||||
UpdateAdded(update);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "offline-cache-update-completed")) {
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
|
||||
if (update) {
|
||||
UpdateCompleted(update);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver
|
||||
//
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Error(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING(ERROR_STR), mOnErrorListener, mErrorListeners);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Checking(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING(CHECKING_STR),
|
||||
mOnCheckingListener, mCheckingListeners);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::NoUpdate(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING(NOUPDATE_STR),
|
||||
mOnNoUpdateListener, mNoUpdateListeners);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::Downloading(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR),
|
||||
mOnDownloadingListener, mDownloadingListeners);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::ItemStarted(nsIOfflineCacheUpdate *aUpdate,
|
||||
nsIDOMLoadStatus *aItem)
|
||||
{
|
||||
SendEvent(NS_LITERAL_STRING(PROGRESS_STR),
|
||||
mOnProgressListener, mProgressListeners);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMOfflineResourceList::ItemCompleted(nsIOfflineCacheUpdate *aUpdate,
|
||||
nsIDOMLoadStatus *aItem)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -266,6 +901,71 @@ nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
|
|||
return GetCacheKey(requestedURI, aKey);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
// Ignore partial updates.
|
||||
PRBool partial;
|
||||
nsresult rv = aUpdate->GetPartial(&partial);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (partial) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> updateURI;
|
||||
rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool equals;
|
||||
rv = updateURI->Equals(mManifestURI, &equals);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!equals) {
|
||||
// This update doesn't belong to us
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(!mCacheUpdate, NS_ERROR_FAILURE);
|
||||
|
||||
// We don't need to emit signals here. Updates are either added
|
||||
// when they are scheduled (in which case they are always IDLE) or
|
||||
// they are added when the applicationCache object is initialized, so there
|
||||
// are no listeners to accept signals anyway.
|
||||
|
||||
mCacheUpdate = aUpdate;
|
||||
mCacheUpdate->AddObserver(this, PR_TRUE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
|
||||
{
|
||||
if (aUpdate != mCacheUpdate) {
|
||||
// This isn't the update we're watching.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool succeeded;
|
||||
nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
|
||||
|
||||
mCacheUpdate->RemoveObserver(this);
|
||||
mCacheUpdate = nsnull;
|
||||
|
||||
if (NS_SUCCEEDED(rv) && succeeded) {
|
||||
// XXX: the spec requires a "cached" event to be sent if this is a
|
||||
// first-time cache attempt, and "updateready" if this page was loaded
|
||||
// from an existing application cache. Since we don't have versioned
|
||||
// application caches yet, basically each update acts like a first-time
|
||||
// update, so we'll always fire "cached" for now.
|
||||
SendEvent(NS_LITERAL_STRING(CACHED_STR),
|
||||
mOnCachedListener, mCachedListeners);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
|
||||
{
|
||||
|
@ -292,7 +992,7 @@ nsDOMOfflineResourceList::CacheKeys()
|
|||
|
||||
ClearCachedKeys();
|
||||
|
||||
nsresult rv = mCacheSession->GetOwnedKeys(mHostPort, NS_LITERAL_CSTRING(""),
|
||||
nsresult rv = mCacheSession->GetOwnedKeys(mHostPort, mDynamicOwnerSpec,
|
||||
&gCachedKeysCount, &gCachedKeys);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
|
|
|
@ -42,23 +42,61 @@
|
|||
#include "nscore.h"
|
||||
#include "nsIDOMOfflineResourceList.h"
|
||||
#include "nsIOfflineCacheSession.h"
|
||||
#include "nsIOfflineCacheUpdate.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsDOMOfflineResourceList : public nsIDOMOfflineResourceList
|
||||
class nsIDOMWindow;
|
||||
|
||||
class nsDOMOfflineResourceList : public nsIDOMOfflineResourceList,
|
||||
public nsIObserver,
|
||||
public nsIOfflineCacheUpdateObserver,
|
||||
public nsIDOMEventTarget,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIDOMOFFLINERESOURCELIST
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
|
||||
NS_DECL_NSIDOMEVENTTARGET
|
||||
|
||||
nsDOMOfflineResourceList(nsIURI* aURI);
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMOfflineResourceList,
|
||||
nsIDOMOfflineResourceList)
|
||||
|
||||
nsDOMOfflineResourceList(PRBool aToplevel,
|
||||
nsIURI* aManifestURI,
|
||||
nsIURI* aDocumentURI,
|
||||
nsIDOMWindow* aWindow);
|
||||
virtual ~nsDOMOfflineResourceList();
|
||||
|
||||
void FirePendingEvents();
|
||||
void Disconnect();
|
||||
|
||||
private:
|
||||
nsresult Init();
|
||||
|
||||
void NotifyEventListeners(nsIDOMEventListener *aListener,
|
||||
const nsCOMArray<nsIDOMEventListener>& aListeners,
|
||||
nsIDOMEvent* aEvent);
|
||||
|
||||
nsresult SendEvent(const nsAString &aEventName,
|
||||
nsIDOMEventListener *aListener,
|
||||
const nsCOMArray<nsIDOMEventListener> &aListeners);
|
||||
|
||||
nsresult UpdateAdded(nsIOfflineCacheUpdate *aUpdate);
|
||||
nsresult UpdateCompleted(nsIOfflineCacheUpdate *aUpdate);
|
||||
|
||||
nsresult GetCacheKey(const nsAString &aURI, nsCString &aKey);
|
||||
nsresult GetCacheKey(nsIURI *aURI, nsCString &aKey);
|
||||
|
||||
|
@ -66,9 +104,38 @@ private:
|
|||
void ClearCachedKeys();
|
||||
|
||||
PRBool mInitialized;
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
PRBool mToplevel;
|
||||
nsCOMPtr<nsIURI> mManifestURI;
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
nsCOMPtr<nsIWeakReference> mWindow;
|
||||
nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> mCacheUpdate;
|
||||
nsCAutoString mHostPort;
|
||||
nsCAutoString mDynamicOwnerSpec;
|
||||
|
||||
nsCOMArray<nsIDOMEventListener> mCheckingListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mErrorListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mNoUpdateListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mDownloadingListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mProgressListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mCachedListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mUpdateReadyListeners;
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> mOnCheckingListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnErrorListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnNoUpdateListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnDownloadingListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnProgressListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnCachedListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnUpdateReadyListener;
|
||||
|
||||
struct PendingEvent {
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
nsCOMPtr<nsIDOMEventListener> listener;
|
||||
nsCOMArray<nsIDOMEventListener> listeners;
|
||||
};
|
||||
|
||||
nsTArray<PendingEvent> mPendingEvents;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -46,13 +46,21 @@ include $(DEPTH)/config/autoconf.mk
|
|||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
test_pendingOfflineLoads.html \
|
||||
offlineTests.js \
|
||||
test_badManifestMagic.html \
|
||||
test_badManifestMime.html \
|
||||
test_missingFile.html \
|
||||
test_simpleManifest.html \
|
||||
test_offlineIFrame.html \
|
||||
badManifestMagic.cacheManifest \
|
||||
badManifestMagic.cacheManifest^headers^ \
|
||||
missingFile.cacheManifest \
|
||||
missingFile.cacheManifest^headers^ \
|
||||
simpleManifest.cacheManifest \
|
||||
simpleManifest.cacheManifest^headers^ \
|
||||
simpleManifest.notmanifest \
|
||||
offlineChild.html \
|
||||
$(NULL)
|
||||
|
||||
# XXX: disabled pending fixes
|
||||
# test_isLocallyAvailable.html \
|
||||
# test_offlineResources.html
|
||||
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# This doesn't start with the magic cache manifest line.
|
||||
http://localhost:8888/tests/SimpleTest/SimpleTest.js
|
||||
http://localhost:8888/MochiKit/packed.js
|
||||
http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js
|
|
@ -0,0 +1,2 @@
|
|||
Content-Type: text/cache-manifest
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
CACHE MANIFEST
|
||||
http://localhost:8888/tests/SimpleTest/SimpleTest.js
|
||||
http://localhost:8888/MochiKit/packed.js
|
||||
http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js
|
||||
|
||||
# The following item doesn't exist, and will cause an update error.
|
||||
http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/doesntExist.html
|
|
@ -0,0 +1,2 @@
|
|||
Content-Type: text/cache-manifest
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// This is called by the parent when the offline update is complete
|
||||
// (since we don't get those events)
|
||||
function doneLoading()
|
||||
{
|
||||
window.top.childFinished();
|
||||
}
|
||||
|
||||
if (OfflineTest.setupChild()) {
|
||||
// Child frames shouldn't be receiving cache events.
|
||||
applicationCache.onerror = OfflineTest.failEvent;
|
||||
applicationCache.onnoupdate = OfflineTest.failEvent;
|
||||
applicationCache.ondownloading = OfflineTest.failEvent;
|
||||
applicationCache.onprogress = OfflineTest.failEvent;
|
||||
applicationCache.onupdateready = OfflineTest.failEvent;
|
||||
applicationCache.oncached = OfflineTest.failEvent;
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body> </html>
|
|
@ -0,0 +1,211 @@
|
|||
// Utility functions for offline tests.
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var OfflineTest = {
|
||||
|
||||
_slaveWindow: null,
|
||||
|
||||
// The window where test results should be sent.
|
||||
_masterWindow: null,
|
||||
|
||||
setupChild: function()
|
||||
{
|
||||
if (window.parent.OfflineTest.hasSlave()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._slaveWindow = null;
|
||||
this._masterWindow = window.top;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Setup the tests. This will reload the current page in a new window
|
||||
// if necessary.
|
||||
setup: function()
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
if (!window.opener || !window.opener.OfflineTest ||
|
||||
!window.opener.OfflineTest._isMaster) {
|
||||
// Offline applications must be toplevel windows and have the
|
||||
// offline-app permission. Because we were loaded without the
|
||||
// offline-app permission and (probably) in an iframe, we need to
|
||||
// enable the pref and spawn a new window to perform the actual
|
||||
// tests. It will use this window to report successes and
|
||||
// failures.
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
var uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(window.location.href, null, null);
|
||||
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
// Tests must run as toplevel windows. Open a slave window to run
|
||||
// the test.
|
||||
this._isMaster = true;
|
||||
this._slaveWindow = window.open(window.location, "offlinetest");
|
||||
|
||||
this._slaveWindow._OfflineSlaveWindow = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._masterWindow = window.opener;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
teardown: function()
|
||||
{
|
||||
// Remove the offline-app permission we gave ourselves.
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
var uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(window.location.href, null, null);
|
||||
pm.remove(uri.host, "offline-app");
|
||||
|
||||
this.clear();
|
||||
},
|
||||
|
||||
finish: function()
|
||||
{
|
||||
SimpleTest.finish();
|
||||
|
||||
if (this._masterWindow) {
|
||||
this._masterWindow.OfflineTest.finish();
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
|
||||
hasSlave: function()
|
||||
{
|
||||
return (this._slaveWindow != null);
|
||||
},
|
||||
|
||||
//
|
||||
// Mochitest wrappers - These forward tests to the proper mochitest window.
|
||||
//
|
||||
ok: function(condition, name, diag)
|
||||
{
|
||||
return this._masterWindow.SimpleTest.ok(condition, name, diag);
|
||||
},
|
||||
|
||||
is: function(a, b, name)
|
||||
{
|
||||
return this._masterWindow.SimpleTest.is(a, b, name);
|
||||
},
|
||||
|
||||
clear: function()
|
||||
{
|
||||
// Clear the ownership list
|
||||
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Ci.nsICacheService);
|
||||
var cacheSession = cacheService.createSession("HTTP-offline",
|
||||
Ci.nsICache.STORE_OFFLINE,
|
||||
true)
|
||||
.QueryInterface(Ci.nsIOfflineCacheSession);
|
||||
|
||||
// Clear manifest-owned urls
|
||||
cacheSession.setOwnedKeys(window.location.host,
|
||||
this.getManifestUrl() + "#manifest", 0, []);
|
||||
|
||||
// Clear dynamically-owned urls
|
||||
cacheSession.setOwnedKeys(window.location.host,
|
||||
this.getManifestUrl() + "#dynamic", 0, []);
|
||||
|
||||
cacheSession.evictUnownedEntries();
|
||||
},
|
||||
|
||||
failEvent: function(e)
|
||||
{
|
||||
OfflineTest.ok(false, "Unexpected event: " + e.type);
|
||||
},
|
||||
|
||||
// The offline API as specified has no way to watch the load of a resource
|
||||
// added with applicationCache.add().
|
||||
waitForAdd: function(url, onFinished) {
|
||||
// Check every half second for ten seconds.
|
||||
var numChecks = 20;
|
||||
var waitFunc = function() {
|
||||
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Ci.nsICacheService);
|
||||
var cacheSession = cacheService.createSession("HTTP-offline",
|
||||
Ci.nsICache.STORE_OFFLINE,
|
||||
true);
|
||||
var entry;
|
||||
try {
|
||||
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, true);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
entry.close();
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (--numChecks == 0) {
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(OfflineTest.priv(waitFunc), 500);
|
||||
}
|
||||
|
||||
setTimeout(this.priv(waitFunc), 500);
|
||||
},
|
||||
|
||||
getManifestUrl: function()
|
||||
{
|
||||
return window.top.document.documentElement.getAttribute("manifest");
|
||||
},
|
||||
|
||||
priv: function(func)
|
||||
{
|
||||
var self = this;
|
||||
return function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
func();
|
||||
}
|
||||
},
|
||||
|
||||
checkCache: function(url, expectEntry)
|
||||
{
|
||||
var cacheService = Cc["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Ci.nsICacheService);
|
||||
var cacheSession = cacheService.createSession("HTTP-offline",
|
||||
Ci.nsICache.STORE_OFFLINE,
|
||||
true);
|
||||
try {
|
||||
var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, true);
|
||||
if (expectEntry) {
|
||||
this.ok(true, url + " should exist in the offline cache");
|
||||
} else {
|
||||
this.ok(false, url + " should not exist in the offline cache");
|
||||
}
|
||||
entry.close();
|
||||
} catch (e) {
|
||||
// this constant isn't in Components.results
|
||||
const kNetBase = 2152398848; // 0x804B0000
|
||||
var NS_ERROR_CACHE_KEY_NOT_FOUND = kNetBase + 61
|
||||
if (e.result == NS_ERROR_CACHE_KEY_NOT_FOUND) {
|
||||
if (expectEntry) {
|
||||
this.ok(false, url + " should exist in the offline cache");
|
||||
} else {
|
||||
this.ok(true, url + " should not exist in the offline cache");
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
CACHE MANIFEST
|
||||
http://localhost:8888/tests/SimpleTest/SimpleTest.js
|
||||
http://localhost:8888/MochiKit/packed.js
|
||||
http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js
|
||||
|
||||
# The following item doesn't have the same scheme as the manifest, and
|
||||
# will be ignored.
|
||||
https://localhost:8888/MochiKit/packed.js
|
||||
|
||||
# The following item is not a valid URI and will be ignored
|
||||
bad:/uri/invalid
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Content-Type: text/cache-manifest
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
CACHE MANIFEST
|
||||
|
||||
http://localhost:8888/tests/SimpleTest/SimpleTest.js
|
||||
http://localhost:8888/MochiKit/packed.js
|
||||
http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js
|
||||
|
||||
# The following item doesn't have the same scheme as the manifest, and
|
||||
# will be ignored.
|
||||
https://localhost:8888/MochiKit/packed.js
|
||||
|
||||
# The following item is not a valid URI and will be ignored
|
||||
bad:/uri/invalid
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/badManifestMagic.cacheManifest">
|
||||
<head>
|
||||
<title>bad manifest magic</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var gGotChecking = false;
|
||||
|
||||
function handleError() {
|
||||
OfflineTest.ok(gGotChecking, "Expected checking event");
|
||||
OfflineTest.ok(true, "Expected error event");
|
||||
|
||||
// These items are listed in the manifest, but the error should have
|
||||
// prevented them from being committed to the cache.
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
|
||||
|
||||
OfflineTest.teardown();
|
||||
OfflineTest.finish();
|
||||
}
|
||||
|
||||
|
||||
if (OfflineTest.setup()) {
|
||||
// Don't expect a bunch of events.
|
||||
applicationCache.ondownloading = OfflineTest.failEvent;
|
||||
applicationCache.onupdateready = OfflineTest.failEvent;
|
||||
applicationCache.oncached = OfflineTest.failEvent;
|
||||
applicationCache.onnoupdate = OfflineTest.failEvent;
|
||||
applicationCache.onprogress = OfflineTest.failEvent;
|
||||
|
||||
// ... but expect 'checking' and 'error'
|
||||
applicationCache.onchecking = function() { gGotChecking = true; };
|
||||
applicationCache.onerror = OfflineTest.priv(handleError);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.notmanifest">
|
||||
<head>
|
||||
<title>bad manifest content type</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var gGotChecking = false;
|
||||
|
||||
function handleError() {
|
||||
OfflineTest.ok(gGotChecking, "Expected checking event");
|
||||
OfflineTest.ok(true, "Expected error event");
|
||||
|
||||
// These items are listed in the manifest, but the error should have
|
||||
// prevented them from being committed to the cache.
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
|
||||
|
||||
OfflineTest.teardown();
|
||||
OfflineTest.finish();
|
||||
}
|
||||
|
||||
if (OfflineTest.setup()) {
|
||||
// Don't expect a bunch of events.
|
||||
applicationCache.ondownloading = OfflineTest.failEvent;
|
||||
applicationCache.onupdateready = OfflineTest.failEvent;
|
||||
applicationCache.oncached = OfflineTest.failEvent;
|
||||
applicationCache.onnoupdate = OfflineTest.failEvent;
|
||||
applicationCache.onprogress = OfflineTest.failEvent;
|
||||
|
||||
// ... but expect 'checking' and 'error'
|
||||
applicationCache.onchecking = function() { gGotChecking = true; };
|
||||
applicationCache.onerror = OfflineTest.priv(handleError);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,94 +0,0 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>navigator.isLocallyAvailable Test</title>
|
||||
|
||||
<link rel="offline-resource" href="http://localhost:8888/tests/SimpleTest/SimpleTest.js">
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function run_test()
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
// check invalid urls
|
||||
try {
|
||||
navigator.isLocallyAvailable("http://different.origin.com", false);
|
||||
ok(false, "can't check from a different origin");
|
||||
} catch(e) {
|
||||
ok(true, "can't check from a different origin");
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.isLocallyAvailable("ftp://localhost/blah/blah", false);
|
||||
ok(false, "urls must be http or https");
|
||||
} catch(e) {
|
||||
ok(true, "urls must be http or https");
|
||||
}
|
||||
|
||||
// check an URL that should definitely not be there
|
||||
ok(navigator.isLocallyAvailable("http://localhost:8888/blah/blah/blah",
|
||||
false) == false,
|
||||
"unknown item shouldn't be available online");
|
||||
ok(navigator.isLocallyAvailable("http://localhost:8888/blah/blah/blah",
|
||||
true) == false,
|
||||
"unknown item shouldn't be available offline");
|
||||
|
||||
// check a URL that should be available on and offline
|
||||
var url = "http://localhost:8888/tests/SimpleTest/SimpleTest.js";
|
||||
ok(navigator.isLocallyAvailable(url, false) == true,
|
||||
url + " should be available online");
|
||||
ok(navigator.isLocallyAvailable(url, true) == true,
|
||||
url + " should be available offline");
|
||||
|
||||
// Pull it out of the disk cache
|
||||
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Components.interfaces.nsICacheService);
|
||||
var session = cacheService.createSession
|
||||
("HTTP",
|
||||
Components.interfaces.nsICache.STORE_ON_DISK,
|
||||
Components.interfaces.nsICache.STREAM_BASED);
|
||||
var entry = session.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_WRITE, false);
|
||||
entry.doom();
|
||||
entry.close();
|
||||
|
||||
// Now it should be available offline but not online
|
||||
ok(navigator.isLocallyAvailable(url, false) == false,
|
||||
url + " should not be available online");
|
||||
|
||||
ok(navigator.isLocallyAvailable(url, true) == true,
|
||||
url + " should be available offline");
|
||||
|
||||
// Clear the offline cache/ownership on the way out
|
||||
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,
|
||||
window.location.protocol + "//" + window.location.host + window.location.pathname,
|
||||
0, []);
|
||||
cacheSession.setOwnedKeys(window.location.host, "", 0, []);
|
||||
cacheSession.evictUnownedEntries();
|
||||
|
||||
cacheService.evictEntries(Components.interfaces.nsICache.STORE_OFFLINE);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Give the offline resources some time to load.
|
||||
setTimeout("run_test()", 1000);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,50 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/missingFile.cacheManifest">
|
||||
<head>
|
||||
<title>missing manifest file test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var gGotChecking = false;
|
||||
var gGotDownloading = false;
|
||||
|
||||
function handleError() {
|
||||
OfflineTest.ok(gGotChecking, "Expected checking event");
|
||||
OfflineTest.ok(gGotDownloading, "Expected downloading event");
|
||||
OfflineTest.ok(true, "Expected error event");
|
||||
|
||||
// These items are listed in the manifest, but the error should have
|
||||
// prevented them from being committed to the cache.
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
|
||||
|
||||
OfflineTest.teardown();
|
||||
OfflineTest.finish();
|
||||
}
|
||||
|
||||
if (OfflineTest.setup()) {
|
||||
// Don't expect any "success" events.
|
||||
applicationCache.onupdateready = function() { OfflineTest.failEvent("updateready"); }
|
||||
applicationCache.oncached = function() { OfflineTest.failEvent("cached"); }
|
||||
applicationCache.onnoupdate = function() { OfflineTest.failEvent("noupdate"); }
|
||||
|
||||
applicationCache.ondownloading = function() { gGotDownloading = true; };
|
||||
applicationCache.onchecking = function() { gGotChecking = true; };
|
||||
applicationCache.onerror = OfflineTest.priv(handleError);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest">
|
||||
<head>
|
||||
<title>offline iframe test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function childFinished()
|
||||
{
|
||||
OfflineTest.teardown();
|
||||
OfflineTest.finish();
|
||||
}
|
||||
|
||||
function manifestUpdated()
|
||||
{
|
||||
// The manifest itself should be in the cache
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true);
|
||||
|
||||
// The entries from the manifest should be in the cache
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
|
||||
|
||||
window.frames["offlineChild"].doneLoading();
|
||||
}
|
||||
|
||||
if (OfflineTest.setup()) {
|
||||
applicationCache.onerror = OfflineTest.failEvent;
|
||||
|
||||
applicationCache.oncached = OfflineTest.priv(manifestUpdated);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<iframe name="offlineChild" src="offlineChild.html"></iframe>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,150 +0,0 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>navigator.offlineResources Test</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var nsICache = Components.interfaces.nsICache;
|
||||
var base = window.location.protocol + "//" + window.location.host;
|
||||
var packedURL = base + "/MochiKit/packed.js";
|
||||
var simpleTestURL = base + "/tests/SimpleTest/SimpleTest.js";
|
||||
var thisURL = base + window.location.pathname;
|
||||
|
||||
function check_cache(url, expectEntry)
|
||||
{
|
||||
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Components.interfaces.nsICacheService);
|
||||
var cacheSession = cacheService.createSession("HTTP-offline",
|
||||
nsICache.STORE_OFFLINE,
|
||||
true);
|
||||
try {
|
||||
var entry = cacheSession.openCacheEntry(url, nsICache.ACCESS_READ, true);
|
||||
ok(expectEntry == true, url + " should exist in the offline cache");
|
||||
entry.close();
|
||||
} catch (e) {
|
||||
// this constant isn't in Components.results
|
||||
const kNetBase = 2152398848; // 0x804B0000
|
||||
var NS_ERROR_CACHE_KEY_NOT_FOUND = kNetBase + 61
|
||||
if (e.result == NS_ERROR_CACHE_KEY_NOT_FOUND) {
|
||||
ok(expectEntry == false, url + " should not exist in the offline cache");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function check_list(array)
|
||||
{
|
||||
var checkObj = {};
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
checkObj[array[i]] = true;
|
||||
}
|
||||
for (var i = 0; i < navigator.offlineResources.length; i++) {
|
||||
ok (navigator.offlineResources[i] in checkObj,
|
||||
navigator.offlineResources[i] + " should not be in offlineResources");
|
||||
delete checkObj[navigator.offlineResources[i]];
|
||||
}
|
||||
|
||||
for (var key in checkObj) {
|
||||
ok(false, key + " was not in navigator.offlineResources");
|
||||
}
|
||||
}
|
||||
|
||||
function run_eviction()
|
||||
{
|
||||
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Components.interfaces.nsICacheService);
|
||||
var cacheSession = cacheService.createSession("HTTP-offline",
|
||||
nsICache.STORE_OFFLINE,
|
||||
true)
|
||||
.QueryInterface(Components.interfaces.nsIOfflineCacheSession);
|
||||
cacheSession.evictUnownedEntries();
|
||||
}
|
||||
|
||||
function clear_cache()
|
||||
{
|
||||
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
|
||||
.getService(Components.interfaces.nsICacheService);
|
||||
|
||||
cacheService.evictEntries(nsICache.STORE_OFFLINE);
|
||||
}
|
||||
|
||||
function load_done() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
check_list([packedURL, simpleTestURL, thisURL]);
|
||||
|
||||
check_cache(packedURL, true);
|
||||
check_cache(simpleTestURL, true);
|
||||
check_cache(thisURL, true);
|
||||
|
||||
navigator.offlineResources.remove(packedURL);
|
||||
run_eviction();
|
||||
|
||||
check_list([simpleTestURL, thisURL]);
|
||||
|
||||
// Make sure the removed entry was evicted but the others weren't
|
||||
check_cache(packedURL, false);
|
||||
check_cache(simpleTestURL, true);
|
||||
check_cache(thisURL, true);
|
||||
|
||||
navigator.offlineResources.clear();
|
||||
run_eviction();
|
||||
|
||||
check_list([]);
|
||||
|
||||
check_cache(packedURL, false);
|
||||
check_cache(simpleTestURL, false);
|
||||
check_cache(thisURL, false);
|
||||
|
||||
// Clear out the offline cache on the way out
|
||||
clear_cache();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
// Clear the offline cache to make sure we're the one adding to it
|
||||
clear_cache();
|
||||
|
||||
// Check invalid urls
|
||||
try {
|
||||
navigator.offlineResources.add("/blah/blah");
|
||||
ok(false, "relative URLs cannot be added");
|
||||
} catch(e) {
|
||||
ok(true, "relative URLs should throw an error");
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.offlineResources.add("ftp://localhost/blah/blah");
|
||||
ok(false, "urls must be http or https");
|
||||
} catch(e) {
|
||||
ok(true, "urls must be http or https");
|
||||
}
|
||||
|
||||
// Add the correct resources
|
||||
navigator.offlineResources.add(packedURL);
|
||||
navigator.offlineResources.add(simpleTestURL);
|
||||
navigator.offlineResources.add(thisURL);
|
||||
|
||||
// Give the offline resources some time to load; Once pendingOfflineLoads
|
||||
// is implemented, we can use that instead.
|
||||
setTimeout("load_done()", 1000);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
|
||||
run_test();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,127 +0,0 @@
|
|||
<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);
|
||||
|
||||
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>
|
|
@ -0,0 +1,116 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest">
|
||||
<head>
|
||||
<title>simple manifest test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var gGotChecking = false;
|
||||
var gGotDownloading = false;
|
||||
|
||||
function addFinished()
|
||||
{
|
||||
// Check that the entry was added successfully
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js",
|
||||
true);
|
||||
|
||||
OfflineTest.ok(applicationCache.length == 1, "applicationCache should have one dynamic entry");
|
||||
OfflineTest.ok(applicationCache.item(0) == "http://localhost:8888/tests/SimpleTest/EventUtils.js",
|
||||
"applicationCache's dynamic entry should be the one we expect");
|
||||
|
||||
// Now test that removes work
|
||||
applicationCache.remove("http://localhost:8888/tests/SimpleTest/EventUtils.js");
|
||||
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js",
|
||||
false);
|
||||
OfflineTest.ok(applicationCache.length == 0,
|
||||
"applicationCache should have no dynamic entries");
|
||||
|
||||
// We're done
|
||||
|
||||
OfflineTest.teardown();
|
||||
|
||||
OfflineTest.finish();
|
||||
}
|
||||
|
||||
function secondUpdate()
|
||||
{
|
||||
OfflineTest.ok(gGotChecking, "Should get a checking event");
|
||||
OfflineTest.ok(gGotDownloading, "Should get a downloading event");
|
||||
|
||||
// The document that requested the manifest should be in the cache
|
||||
OfflineTest.checkCache(window.location.href, true);
|
||||
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
|
||||
|
||||
// Now add a file using the applicationCache API
|
||||
applicationCache.add("http://localhost:8888/tests/SimpleTest/EventUtils.js");
|
||||
|
||||
// Wait for the add() to be downloaded
|
||||
OfflineTest.waitForAdd("http://localhost:8888/tests/SimpleTest/EventUtils.js",
|
||||
OfflineTest.priv(addFinished));
|
||||
}
|
||||
|
||||
function manifestUpdated()
|
||||
{
|
||||
OfflineTest.ok(gGotChecking, "Should get a checking event");
|
||||
OfflineTest.ok(gGotDownloading, "Should get a downloading event");
|
||||
|
||||
// The manifest itself should be in the cache
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true);
|
||||
|
||||
// The document that requested the manifest should be in the cache
|
||||
OfflineTest.checkCache(window.location.href, true);
|
||||
|
||||
// The entries from the manifest should be in the cache
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", true);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
|
||||
|
||||
// The bad entries from the manifest should not be in the cache
|
||||
OfflineTest.checkCache("https://localhost:8888/MochiKit/packed.js", false);
|
||||
OfflineTest.checkCache("bad:/uri/invalid", false);
|
||||
|
||||
// Remove items from the cache.
|
||||
OfflineTest.clear();
|
||||
|
||||
// Make sure OfflineTest.clear() properly removed the items
|
||||
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/SimpleTest.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/MochiKit/packed.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
|
||||
OfflineTest.checkCache("http://localhost:8888/tests/SimpleTest/EventUtils.js",
|
||||
false);
|
||||
|
||||
// Now make sure applicationCache.update() does what we expect.
|
||||
applicationCache.oncached = OfflineTest.priv(secondUpdate);
|
||||
|
||||
gGotChecking = false;
|
||||
gGotDownloading = false;
|
||||
applicationCache.update();
|
||||
}
|
||||
|
||||
|
||||
if (OfflineTest.setup()) {
|
||||
applicationCache.onerror = OfflineTest.failEvent;
|
||||
|
||||
applicationCache.onchecking = function() { gGotChecking = true; };
|
||||
applicationCache.ondownloading = function() { gGotDownloading = true; };
|
||||
applicationCache.oncached = OfflineTest.priv(manifestUpdated);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -42,19 +42,69 @@ interface nsIURI;
|
|||
interface nsIDOMNode;
|
||||
interface nsIDOMDocument;
|
||||
interface nsIDOMLoadStatus;
|
||||
interface nsIOfflineCacheUpdate;
|
||||
|
||||
[scriptable, uuid(e0785ebb-b3a1-426a-a70e-be2b923e973e)]
|
||||
[scriptable, uuid(0aa38757-999c-44d6-bdb4-7dd32634fa83)]
|
||||
interface nsIOfflineCacheUpdateObserver : nsISupports {
|
||||
/**
|
||||
* There was an error updating the cache.
|
||||
*
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
*/
|
||||
void error(in nsIOfflineCacheUpdate aUpdate);
|
||||
|
||||
/**
|
||||
* The manifest is being checked for updates
|
||||
*
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
*/
|
||||
void checking(in nsIOfflineCacheUpdate aUpdate);
|
||||
|
||||
/**
|
||||
* No update was necessary.
|
||||
*
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
*/
|
||||
void noUpdate(in nsIOfflineCacheUpdate aUpdate);
|
||||
|
||||
/**
|
||||
* Starting to download resources
|
||||
*
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
*/
|
||||
void downloading(in nsIOfflineCacheUpdate aUpdate);
|
||||
|
||||
/**
|
||||
* An item has started downloading.
|
||||
*
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
* @param aItem
|
||||
* load status for the item that is being downloaded.
|
||||
*/
|
||||
void itemStarted(in nsIOfflineCacheUpdate aUpdate,
|
||||
in nsIDOMLoadStatus aItem);
|
||||
|
||||
/**
|
||||
* An item has finished loading.
|
||||
*
|
||||
* @param aItem load status for the item that completed.
|
||||
* @param aUpdate
|
||||
* The nsIOfflineCacheUpdate being processed.
|
||||
* @param aItem
|
||||
* load status for the item that completed.
|
||||
*/
|
||||
void itemCompleted(in nsIDOMLoadStatus aItem);
|
||||
void itemCompleted(in nsIOfflineCacheUpdate aUpdate,
|
||||
in nsIDOMLoadStatus aItem);
|
||||
};
|
||||
|
||||
/**
|
||||
* An nsIOfflineCacheUpdate is used to update a domain's offline resources.
|
||||
* An nsIOfflineCacheUpdate is used to update an application's offline
|
||||
* resources.
|
||||
*
|
||||
* It can be used to perform partial or complete updates.
|
||||
*
|
||||
* Each update object maintains a list of nsIDOMLoadStatus items for the
|
||||
|
@ -67,6 +117,18 @@ interface nsIOfflineCacheUpdateObserver : nsISupports {
|
|||
*/
|
||||
[scriptable, uuid(7dc06ede-1098-4384-b95e-65525ab254c9)]
|
||||
interface nsIOfflineCacheUpdate : nsISupports {
|
||||
/**
|
||||
* Fetch the status of the running update. This will return a value
|
||||
* defined in nsIDOMOfflineResourceList.
|
||||
*/
|
||||
readonly attribute unsigned short status;
|
||||
|
||||
/**
|
||||
* TRUE if the update is being used to add specific resources.
|
||||
* FALSE if the complete cache update process is happening.
|
||||
*/
|
||||
readonly attribute boolean partial;
|
||||
|
||||
/**
|
||||
* The domain being updated, and the domain that will own any URIs added
|
||||
* with this update.
|
||||
|
@ -74,9 +136,14 @@ interface nsIOfflineCacheUpdate : nsISupports {
|
|||
readonly attribute ACString updateDomain;
|
||||
|
||||
/**
|
||||
* The URI that will own any URIs added by this update
|
||||
* The manifest for the offline application being updated.
|
||||
*/
|
||||
readonly attribute ACString ownerURI;
|
||||
readonly attribute nsIURI manifestURI;
|
||||
|
||||
/**
|
||||
* TRUE if the cache update completed successfully.
|
||||
*/
|
||||
readonly attribute boolean succeeded;
|
||||
|
||||
/**
|
||||
* Initialize the update.
|
||||
|
@ -84,29 +151,23 @@ interface nsIOfflineCacheUpdate : nsISupports {
|
|||
* @param aPartialUpdate
|
||||
* TRUE if the update should just update the URIs given to it,
|
||||
* FALSE if all URLs for the owner domain should be added.
|
||||
* @param aUpdateDomain
|
||||
* The domain which is being updated, and which will own any
|
||||
* URIs added.
|
||||
* @param aOwnerURI
|
||||
* The owner URI for any URIs added.
|
||||
* @param aReferrerURI
|
||||
* @param aManifestURI
|
||||
* The manifest URI to be checked, or for partial updates the
|
||||
* manifest that should own resources that are added.
|
||||
* @param aDocumentURI
|
||||
* The page that is requesting the update.
|
||||
*/
|
||||
void init(in boolean aPartialUpdate,
|
||||
in ACString aUpdateDomain,
|
||||
in ACString aOwnerURI,
|
||||
in nsIURI aReferrerURI);
|
||||
in nsIURI aManifestURI,
|
||||
in nsIURI aDocumentURI);
|
||||
|
||||
/**
|
||||
* Add a URI to the offline cache as part of the update.
|
||||
*
|
||||
* @param aURI
|
||||
* The URI to add.
|
||||
* @param aSource
|
||||
* The DOM node (<link> tag) associated with this node (for
|
||||
* implementing nsIDOMLoadStatus).
|
||||
*/
|
||||
void addURI(in nsIURI aURI, in nsIDOMNode aSource);
|
||||
void addDynamicURI(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Add the update to the offline update queue. An offline-cache-update-added
|
||||
|
@ -114,14 +175,6 @@ interface nsIOfflineCacheUpdate : nsISupports {
|
|||
*/
|
||||
void schedule();
|
||||
|
||||
/**
|
||||
* Request that the update be scheduled when a document finishes loading.
|
||||
*
|
||||
* @param aDocument
|
||||
* When this document finishes loading, the update will be scheduled.
|
||||
*/
|
||||
void scheduleOnDocumentStop(in nsIDOMDocument aDocument);
|
||||
|
||||
/**
|
||||
* Access to the list of items in the update.
|
||||
*/
|
||||
|
@ -149,11 +202,27 @@ interface nsIOfflineCacheUpdate : nsISupports {
|
|||
void removeObserver(in nsIOfflineCacheUpdateObserver aObserver);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f99ca10f-5cde-4966-b845-433f2921a201)]
|
||||
[scriptable, uuid(3abee04b-5bbb-4405-b659-35f780e38da0)]
|
||||
interface nsIOfflineCacheUpdateService : nsISupports {
|
||||
/**
|
||||
* Access to the list of cache updates that have been scheduled.
|
||||
*/
|
||||
readonly attribute unsigned long numUpdates;
|
||||
nsIOfflineCacheUpdate getUpdate(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Schedule a cache update for a given offline manifest. If an
|
||||
* existing update is scheduled or running, that update will be returned.
|
||||
* Otherwise a new update will be scheduled.
|
||||
*/
|
||||
nsIOfflineCacheUpdate scheduleUpdate(in nsIURI aManifestURI,
|
||||
in nsIURI aDocumentURI);
|
||||
|
||||
/**
|
||||
* Schedule a cache update for a manifest when the document finishes
|
||||
* loading.
|
||||
*/
|
||||
void scheduleOnDocumentStop(in nsIURI aManifestURI,
|
||||
in nsIURI aDocumentURI,
|
||||
in nsIDOMDocument aDocument);
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "nsICacheSession.h"
|
||||
#include "nsICachingChannel.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIDOMOfflineResourceList.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIOfflineCacheSession.h"
|
||||
#include "nsIWebProgress.h"
|
||||
|
@ -101,7 +102,6 @@ NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
|
|||
nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
|
||||
nsIURI *aURI,
|
||||
nsIURI *aReferrerURI,
|
||||
nsIDOMNode *aSource,
|
||||
const nsACString &aClientID)
|
||||
: mURI(aURI)
|
||||
, mReferrerURI(aReferrerURI)
|
||||
|
@ -111,7 +111,6 @@ nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate
|
|||
, mState(nsIDOMLoadStatus::UNINITIALIZED)
|
||||
, mBytesRead(0)
|
||||
{
|
||||
mSource = do_GetWeakReference(aSource);
|
||||
}
|
||||
|
||||
nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
|
||||
|
@ -277,15 +276,13 @@ nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel,
|
|||
}
|
||||
}
|
||||
|
||||
nsCAutoString oldScheme;
|
||||
mURI->GetScheme(oldScheme);
|
||||
|
||||
PRBool match;
|
||||
rv = newURI->SchemeIs("http", &match);
|
||||
if (NS_FAILED(rv) || !match) {
|
||||
if (NS_FAILED(newURI->SchemeIs("https", &match)) ||
|
||||
!match) {
|
||||
LOG(("rejected: URL is not of type http\n"));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
|
||||
LOG(("rejected: redirected to a different scheme\n"));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// HTTP request headers are not automatically forwarded to the new channel.
|
||||
|
@ -308,12 +305,8 @@ nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel,
|
|||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdateItem::GetSource(nsIDOMNode **aSource)
|
||||
{
|
||||
if (mSource) {
|
||||
return CallQueryReferent(mSource.get(), aSource);
|
||||
} else {
|
||||
*aSource = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
*aSource = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -385,6 +378,258 @@ nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineManifestItem
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineManifestItem <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsOfflineManifestItem::nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
|
||||
nsIURI *aURI,
|
||||
nsIURI *aReferrerURI,
|
||||
const nsACString &aClientID)
|
||||
: nsOfflineCacheUpdateItem(aUpdate, aURI, aReferrerURI, aClientID)
|
||||
, mParserState(PARSE_INIT)
|
||||
, mNeedsUpdate(PR_TRUE)
|
||||
{
|
||||
}
|
||||
|
||||
nsOfflineManifestItem::~nsOfflineManifestItem()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineManifestItem <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* static */
|
||||
NS_METHOD
|
||||
nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aBytesConsumed)
|
||||
{
|
||||
nsOfflineManifestItem *manifest =
|
||||
static_cast<nsOfflineManifestItem*>(aClosure);
|
||||
|
||||
*aBytesConsumed = aCount;
|
||||
|
||||
if (manifest->mParserState == PARSE_ERROR) {
|
||||
// parse already failed, ignore this
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
manifest->mReadBuf.Append(aFromSegment, aCount);
|
||||
|
||||
nsCString::const_iterator begin, iter, end;
|
||||
manifest->mReadBuf.BeginReading(begin);
|
||||
manifest->mReadBuf.EndReading(end);
|
||||
|
||||
for (iter = begin; iter != end; iter++) {
|
||||
if (*iter == '\r' || *iter == '\n') {
|
||||
nsresult rv = manifest->HandleManifestLine(begin, iter);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("HandleManifestLine failed with 0x%08x", rv));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
begin = iter;
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
|
||||
// any leftovers are saved for next time
|
||||
manifest->mReadBuf = Substring(begin, end);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
|
||||
const nsCString::const_iterator &aEnd)
|
||||
{
|
||||
nsCString::const_iterator begin = aBegin;
|
||||
nsCString::const_iterator end = aEnd;
|
||||
|
||||
// all lines ignore trailing spaces and tabs
|
||||
nsCString::const_iterator last = end;
|
||||
--last;
|
||||
while (end != begin && (*last == ' ' || *last == '\t')) {
|
||||
--end;
|
||||
--last;
|
||||
}
|
||||
|
||||
if (mParserState == PARSE_INIT) {
|
||||
// Allow a UTF-8 BOM
|
||||
if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
|
||||
if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
|
||||
++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
|
||||
mParserState = PARSE_ERROR;
|
||||
return NS_OK;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
|
||||
const nsCSubstring &magic = Substring(begin, end);
|
||||
|
||||
if (!magic.EqualsLiteral("CACHE MANIFEST")) {
|
||||
mParserState = PARSE_ERROR;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mParserState = PARSE_CACHE_ENTRIES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// lines other than the first ignore leading spaces and tabs
|
||||
while (begin != end && (*begin == ' ' || *begin == '\t'))
|
||||
begin++;
|
||||
|
||||
// ignore blank lines and comments
|
||||
if (begin == end || *begin == '#')
|
||||
return NS_OK;
|
||||
|
||||
const nsCSubstring &line = Substring(begin, end);
|
||||
|
||||
if (line.EqualsLiteral("CACHE:")) {
|
||||
mParserState = PARSE_CACHE_ENTRIES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (line.EqualsLiteral("FALLBACK:")) {
|
||||
mParserState = PARSE_FALLBACK_ENTRIES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (line.EqualsLiteral("NETWORK:")) {
|
||||
mParserState = PARSE_NETWORK_ENTRIES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
switch(mParserState) {
|
||||
case PARSE_INIT:
|
||||
case PARSE_ERROR: {
|
||||
// this should have been dealt with earlier
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
case PARSE_CACHE_ENTRIES: {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), line, nsnull, mURI);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
|
||||
nsCAutoString scheme;
|
||||
uri->GetScheme(scheme);
|
||||
|
||||
// Manifest URIs must have the same scheme as the manifest.
|
||||
PRBool match;
|
||||
if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
|
||||
break;
|
||||
|
||||
mExplicitURIs.AppendObject(uri);
|
||||
break;
|
||||
}
|
||||
case PARSE_FALLBACK_ENTRIES:
|
||||
case PARSE_NETWORK_ENTRIES: {
|
||||
// we don't currently implement fallbacks or whitelists,
|
||||
// ignore these for now.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool succeeded;
|
||||
rv = channel->GetRequestSucceeded(&succeeded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!succeeded) {
|
||||
LOG(("HTTP request failed"));
|
||||
mParserState = PARSE_ERROR;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
nsCAutoString contentType;
|
||||
rv = channel->GetContentType(contentType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!contentType.EqualsLiteral("text/cache-manifest")) {
|
||||
LOG(("Rejected cache manifest with Content-Type %s (expecting text/cache-manifest)",
|
||||
contentType.get()));
|
||||
mParserState = PARSE_ERROR;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aStream,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aCount)
|
||||
{
|
||||
PRUint32 bytesRead = 0;
|
||||
aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
|
||||
mBytesRead += bytesRead;
|
||||
|
||||
if (mParserState == PARSE_ERROR) {
|
||||
LOG(("OnDataAvailable is canceling the request due a parse error\n"));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
LOG(("loaded %u bytes into offline cache [offset=%u]\n",
|
||||
bytesRead, aOffset));
|
||||
|
||||
// All the parent method does is read and discard, don't bother
|
||||
// chaining up.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
// handle any leftover manifest data
|
||||
nsCString::const_iterator begin, end;
|
||||
mReadBuf.BeginReading(begin);
|
||||
mReadBuf.EndReading(end);
|
||||
nsresult rv = HandleManifestLine(begin, end);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mBytesRead == 0) {
|
||||
// we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
|
||||
// specified.)
|
||||
mNeedsUpdate = PR_FALSE;
|
||||
|
||||
// XXX: The spec calls for a byte-by-byte comparison of the manifest,
|
||||
// with an exact match skipping the update. Need to implement this
|
||||
// or we'll be fetching way too often.
|
||||
}
|
||||
|
||||
return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineCacheUpdate::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -412,9 +657,8 @@ nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
|
|||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
|
||||
const nsACString &aUpdateDomain,
|
||||
const nsACString &aOwnerURI,
|
||||
nsIURI *aReferrerURI)
|
||||
nsIURI *aManifestURI,
|
||||
nsIURI *aDocumentURI)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
@ -427,9 +671,40 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
|
|||
LOG(("nsOfflineCacheUpdate::Init [%p]", this));
|
||||
|
||||
mPartialUpdate = aPartialUpdate;
|
||||
mUpdateDomain = aUpdateDomain;
|
||||
mOwnerURI = aOwnerURI;
|
||||
mReferrerURI = aReferrerURI;
|
||||
|
||||
// Only http and https applications are supported.
|
||||
PRBool match;
|
||||
rv = aManifestURI->SchemeIs("http", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!match) {
|
||||
rv = aManifestURI->SchemeIs("https", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!match)
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
mManifestURI = aManifestURI;
|
||||
|
||||
rv = mManifestURI->GetHostPort(mUpdateDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString manifestSpec;
|
||||
|
||||
rv = mManifestURI->GetSpec(manifestSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 ref = manifestSpec.FindChar('#');
|
||||
if (ref != kNotFound)
|
||||
manifestSpec.Truncate(ref);
|
||||
|
||||
mManifestOwnerSpec = manifestSpec;
|
||||
mManifestOwnerSpec.AppendLiteral("#manifest");
|
||||
|
||||
mDynamicOwnerSpec = manifestSpec;
|
||||
mDynamicOwnerSpec.AppendLiteral("#dynamic");
|
||||
|
||||
mDocumentURI = aDocumentURI;
|
||||
|
||||
nsCOMPtr<nsICacheService> cacheService =
|
||||
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
|
||||
|
@ -461,6 +736,11 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
|
|||
|
||||
mCacheSession = do_QueryInterface(session, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// The manifest implicitly owns itself.
|
||||
rv = mCacheSession->AddOwnedKey(mUpdateDomain, mManifestOwnerSpec,
|
||||
manifestSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mState = STATE_INITIALIZED;
|
||||
|
@ -468,6 +748,45 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::HandleManifest(PRBool *aDoUpdate)
|
||||
{
|
||||
// Be pessimistic
|
||||
*aDoUpdate = PR_FALSE;
|
||||
|
||||
PRUint16 status;
|
||||
nsresult rv = mManifestItem->GetStatus(&status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (status == 0 || status >= 400 || !mManifestItem->ParseSucceeded()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mManifestItem->NeedsUpdate()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Add items requested by the manifest.
|
||||
const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
|
||||
for (PRInt32 i = 0; i < manifestURIs.Count(); i++) {
|
||||
rv = AddURI(manifestURIs[i], mManifestOwnerSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// The document that requested the manifest is implicitly included
|
||||
// as part of that manifest update.
|
||||
rv = AddURI(mDocumentURI, mManifestOwnerSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add items requested by the script API
|
||||
rv = AddOwnedItems(mDynamicOwnerSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aDoUpdate = PR_TRUE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsOfflineCacheUpdate::LoadCompleted()
|
||||
{
|
||||
|
@ -475,22 +794,55 @@ nsOfflineCacheUpdate::LoadCompleted()
|
|||
|
||||
LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
|
||||
|
||||
if (mState == STATE_CANCELLED) {
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState == STATE_CHECKING) {
|
||||
// Manifest load finished.
|
||||
|
||||
NS_ASSERTION(mManifestItem,
|
||||
"Must have a manifest item in STATE_CHECKING.");
|
||||
|
||||
PRBool doUpdate;
|
||||
if (NS_FAILED(HandleManifest(&doUpdate))) {
|
||||
mSucceeded = PR_FALSE;
|
||||
NotifyError();
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doUpdate) {
|
||||
mSucceeded = PR_FALSE;
|
||||
NotifyNoUpdate();
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mState = STATE_DOWNLOADING;
|
||||
NotifyDownloading();
|
||||
|
||||
// Start fetching resources.
|
||||
ProcessNextURI();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal load finished.
|
||||
|
||||
nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[mCurrentItem];
|
||||
mCurrentItem++;
|
||||
|
||||
PRUint16 status;
|
||||
rv = item->GetStatus(&status);
|
||||
|
||||
// Check for failures. Only connection or server errors (5XX) will cause
|
||||
// the update to fail.
|
||||
if (NS_FAILED(rv) || status == 0 || status >= 500) {
|
||||
// Only fail updates from this domain. Outside-of-domain updates
|
||||
// are not guaranteeed to be updated.
|
||||
nsCAutoString domain;
|
||||
item->mURI->GetHostPort(domain);
|
||||
if (domain == mUpdateDomain) {
|
||||
mSucceeded = PR_FALSE;
|
||||
}
|
||||
// Check for failures. 4XX and 5XX errors will cause the update to fail.
|
||||
if (NS_FAILED(rv) || status == 0 || status >= 400) {
|
||||
mSucceeded = PR_FALSE;
|
||||
NotifyError();
|
||||
Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
rv = NotifyCompleted(item);
|
||||
|
@ -504,17 +856,31 @@ nsOfflineCacheUpdate::Begin()
|
|||
{
|
||||
LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
|
||||
|
||||
if (!mPartialUpdate) {
|
||||
// All offline items for a domain should be updated as a group; add
|
||||
// the other offline items requested for this domain
|
||||
nsresult rv = AddDomainItems();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mCurrentItem = 0;
|
||||
|
||||
if (mPartialUpdate) {
|
||||
mState = STATE_DOWNLOADING;
|
||||
NotifyDownloading();
|
||||
ProcessNextURI();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
// Start checking the manifest.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
|
||||
mCurrentItem = 0;
|
||||
ProcessNextURI();
|
||||
mManifestItem = new nsOfflineManifestItem(this, mManifestURI,
|
||||
mDocumentURI, mClientID);
|
||||
if (!mManifestItem) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mState = STATE_CHECKING;
|
||||
NotifyChecking();
|
||||
|
||||
nsresult rv = mManifestItem->OpenChannel();
|
||||
if (NS_FAILED(rv)) {
|
||||
LoadCompleted();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -555,8 +921,8 @@ nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
|
|||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
|
||||
nsRefPtr<nsOfflineCacheUpdateItem> item =
|
||||
new nsOfflineCacheUpdateItem(this, uri, mReferrerURI,
|
||||
nsnull, mClientID);
|
||||
new nsOfflineCacheUpdateItem(this, uri, mDocumentURI,
|
||||
mClientID);
|
||||
if (!item) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mItems.AppendElement(item);
|
||||
|
@ -566,38 +932,16 @@ nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Add all URIs needed by this domain to the update
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::AddDomainItems()
|
||||
{
|
||||
PRUint32 count;
|
||||
char **uris;
|
||||
nsresult rv = mMainCacheSession->GetOwnerURIs(mUpdateDomain, &count, &uris);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
AutoFreeArray autoFree(count, uris);
|
||||
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
const char *ownerURI = uris[i];
|
||||
// if this update includes changes to this owner URI, ignore the
|
||||
// set in the database.
|
||||
if (!mAddedItems || !mOwnerURI.Equals(ownerURI)) {
|
||||
rv = AddOwnedItems(nsDependentCString(ownerURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::ProcessNextURI()
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, current=%d, numItems=%d]",
|
||||
this, mCurrentItem, mItems.Length()));
|
||||
|
||||
if (mState == STATE_CANCELLED ||
|
||||
mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
|
||||
NS_ASSERTION(mState == STATE_DOWNLOADING,
|
||||
"ProcessNextURI should only be called from the DOWNLOADING state");
|
||||
|
||||
if (mCurrentItem >= static_cast<PRInt32>(mItems.Length())) {
|
||||
return Finish();
|
||||
}
|
||||
|
||||
|
@ -609,6 +953,8 @@ nsOfflineCacheUpdate::ProcessNextURI()
|
|||
}
|
||||
#endif
|
||||
|
||||
NotifyStarted(mItems[mCurrentItem]);
|
||||
|
||||
nsresult rv = mItems[mCurrentItem]->OpenChannel();
|
||||
if (NS_FAILED(rv)) {
|
||||
LoadCompleted();
|
||||
|
@ -619,25 +965,115 @@ nsOfflineCacheUpdate::ProcessNextURI()
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
|
||||
nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
|
||||
{
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
|
||||
for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
|
||||
nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
|
||||
do_QueryReferent(mWeakObservers[i]);
|
||||
if (observer)
|
||||
observers.AppendObject(observer);
|
||||
aObservers.AppendObject(observer);
|
||||
else
|
||||
mWeakObservers.RemoveObjectAt(i--);
|
||||
}
|
||||
|
||||
for (PRInt32 i = 0; i < mObservers.Count(); i++) {
|
||||
observers.AppendObject(mObservers[i]);
|
||||
aObservers.AppendObject(mObservers[i]);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyError()
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyError [%p]", this));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->ItemCompleted(aItem);
|
||||
observers[i]->Error(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyChecking()
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyChecking [%p]", this));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->Checking(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyNoUpdate()
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%p]", this));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->NoUpdate(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyDownloading()
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyDownloading [%p]", this));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->Downloading(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyStarted(nsOfflineCacheUpdateItem *aItem)
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyStarted [%p, %p]", this, aItem));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->ItemStarted(this, aItem);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::NotifyCompleted [%p, %p]", this, aItem));
|
||||
|
||||
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
||||
nsresult rv = GatherObservers(observers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < observers.Count(); i++) {
|
||||
observers[i]->ItemCompleted(this, aItem);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -653,11 +1089,16 @@ nsOfflineCacheUpdate::Finish()
|
|||
nsOfflineCacheUpdateService* service =
|
||||
nsOfflineCacheUpdateService::EnsureService();
|
||||
|
||||
if (!service)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (!mPartialUpdate) {
|
||||
if (mSucceeded) {
|
||||
nsresult rv = mMainCacheSession->MergeTemporaryClientID(mClientID);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv)) {
|
||||
NotifyError();
|
||||
mSucceeded = PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mSucceeded) {
|
||||
|
@ -668,9 +1109,6 @@ nsOfflineCacheUpdate::Finish()
|
|||
}
|
||||
}
|
||||
|
||||
if (!service)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return service->UpdateFinished(this);
|
||||
}
|
||||
|
||||
|
@ -688,37 +1126,65 @@ nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::GetOwnerURI(nsACString &aOwnerURI)
|
||||
nsOfflineCacheUpdate::GetStatus(PRUint16 *aStatus)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
||||
switch (mState) {
|
||||
case STATE_CHECKING :
|
||||
return nsIDOMOfflineResourceList::CHECKING;
|
||||
case STATE_DOWNLOADING :
|
||||
return nsIDOMOfflineResourceList::DOWNLOADING;
|
||||
default :
|
||||
return nsIDOMOfflineResourceList::IDLE;
|
||||
}
|
||||
|
||||
aOwnerURI = mOwnerURI;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::GetPartial(PRBool *aPartial)
|
||||
{
|
||||
*aPartial = mPartialUpdate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::AddURI(nsIURI *aURI, nsIDOMNode *aSource)
|
||||
nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (mState >= STATE_RUNNING)
|
||||
NS_IF_ADDREF(*aManifestURI = mManifestURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::GetSucceeded(PRBool *aSucceeded)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
*aSucceeded = mSucceeded;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdate::AddURI(nsIURI *aURI, const nsACString &aOwnerSpec)
|
||||
{
|
||||
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (mState >= STATE_DOWNLOADING)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// only http and https urls can be put in the offline cache
|
||||
PRBool match;
|
||||
nsresult rv = aURI->SchemeIs("http", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Manifest URIs must have the same scheme as the manifest.
|
||||
nsCAutoString scheme;
|
||||
aURI->GetScheme(scheme);
|
||||
|
||||
if (!match) {
|
||||
rv = aURI->SchemeIs("https", &match);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!match)
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
PRBool match;
|
||||
if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Save the cache key as an owned URI
|
||||
nsCAutoString spec;
|
||||
rv = aURI->GetSpec(spec);
|
||||
nsresult rv = aURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// url fragments aren't used in cache keys
|
||||
|
@ -727,17 +1193,24 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, nsIDOMNode *aSource)
|
|||
spec.EndReading(specEnd);
|
||||
if (FindCharInReadable('#', specStart, specEnd)) {
|
||||
spec.BeginReading(specEnd);
|
||||
rv = mCacheSession->AddOwnedKey(mUpdateDomain, mOwnerURI,
|
||||
rv = mCacheSession->AddOwnedKey(mUpdateDomain, aOwnerSpec,
|
||||
Substring(specEnd, specStart));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
rv = mCacheSession->AddOwnedKey(mUpdateDomain, mOwnerURI, spec);
|
||||
rv = mCacheSession->AddOwnedKey(mUpdateDomain, aOwnerSpec, spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Don't fetch the same URI twice.
|
||||
for (PRUint32 i = 0; i < mItems.Length(); i++) {
|
||||
PRBool equals;
|
||||
if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<nsOfflineCacheUpdateItem> item =
|
||||
new nsOfflineCacheUpdateItem(this, aURI, mReferrerURI,
|
||||
aSource, mClientID);
|
||||
new nsOfflineCacheUpdateItem(this, aURI, mDocumentURI, mClientID);
|
||||
if (!item) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mItems.AppendElement(item);
|
||||
|
@ -746,6 +1219,12 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, nsIDOMNode *aSource)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
|
||||
{
|
||||
return AddURI(aURI, mDynamicOwnerSpec);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::GetCount(PRUint32 *aNumItems)
|
||||
{
|
||||
|
@ -833,21 +1312,6 @@ nsOfflineCacheUpdate::Schedule()
|
|||
return service->Schedule(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdate::ScheduleOnDocumentStop(nsIDOMDocument *aDocument)
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdate::ScheduleOnDocumentStop [%p]", this));
|
||||
|
||||
nsOfflineCacheUpdateService* service =
|
||||
nsOfflineCacheUpdateService::EnsureService();
|
||||
|
||||
if (!service) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return service->ScheduleOnDocumentStop(this, aDocument);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineCacheUpdateService::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -910,6 +1374,7 @@ nsOfflineCacheUpdateService::Init()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsOfflineCacheUpdateService *
|
||||
nsOfflineCacheUpdateService::GetInstance()
|
||||
{
|
||||
|
@ -931,6 +1396,7 @@ nsOfflineCacheUpdateService::GetInstance()
|
|||
return gOfflineCacheUpdateService;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsOfflineCacheUpdateService *
|
||||
nsOfflineCacheUpdateService::EnsureService()
|
||||
{
|
||||
|
@ -965,14 +1431,18 @@ nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsOfflineCacheUpdate *aUpdate,
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
|
||||
nsIURI *aDocumentURI,
|
||||
nsIDOMDocument *aDocument)
|
||||
{
|
||||
LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, update=%p, doc=%p]",
|
||||
this, aUpdate, aDocument));
|
||||
LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
|
||||
this, aManifestURI, aDocumentURI, aDocument));
|
||||
|
||||
if (!mDocUpdates.Put(aDocument, aUpdate))
|
||||
PendingUpdate *update = new PendingUpdate();
|
||||
update->mManifestURI = aManifestURI;
|
||||
update->mDocumentURI = aDocumentURI;
|
||||
if (!mDocUpdates.Put(aDocument, update))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1058,6 +1528,55 @@ nsOfflineCacheUpdateService::GetUpdate(PRUint32 aIndex,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
|
||||
nsIURI *aDocumentURI,
|
||||
nsIOfflineCacheUpdate **aUpdate)
|
||||
{
|
||||
// Check for existing updates
|
||||
nsresult rv;
|
||||
for (PRUint32 i = 0; i < mUpdates.Length(); i++) {
|
||||
nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[i];
|
||||
|
||||
PRBool partial;
|
||||
rv = update->GetPartial(&partial);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (partial) {
|
||||
// Partial updates aren't considered
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> manifestURI;
|
||||
update->GetManifestURI(getter_AddRefs(manifestURI));
|
||||
if (manifestURI) {
|
||||
PRBool equals;
|
||||
rv = manifestURI->Equals(aManifestURI, &equals);
|
||||
if (equals) {
|
||||
NS_ADDREF(*aUpdate = update);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is no existing update, start one.
|
||||
|
||||
nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
|
||||
if (!update)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
rv = update->Init(PR_FALSE, aManifestURI, aDocumentURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = update->Schedule();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ADDREF(*aUpdate = update);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsOfflineCacheUpdateService::nsIObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1114,9 +1633,16 @@ nsOfflineCacheUpdateService::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
LOG(("nsOfflineCacheUpdateService::OnStateChange [%p, doc=%p]",
|
||||
this, doc.get()));
|
||||
|
||||
nsRefPtr<nsOfflineCacheUpdate> update;
|
||||
if (mDocUpdates.Get(doc, getter_AddRefs(update))) {
|
||||
Schedule(update);
|
||||
|
||||
PendingUpdate *pendingUpdate;
|
||||
if (mDocUpdates.Get(doc, &pendingUpdate)) {
|
||||
// Only schedule the update if the document loaded successfull
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
nsCOMPtr<nsIOfflineCacheUpdate> update;
|
||||
ScheduleUpdate(pendingUpdate->mManifestURI,
|
||||
pendingUpdate->mDocumentURI,
|
||||
getter_AddRefs(update));
|
||||
}
|
||||
mDocUpdates.Remove(doc);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
#include "nsIStreamListener.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
@ -84,13 +84,11 @@ public:
|
|||
nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
|
||||
nsIURI *aURI,
|
||||
nsIURI *aReferrerURI,
|
||||
nsIDOMNode *aSource,
|
||||
const nsACString &aClientID);
|
||||
~nsOfflineCacheUpdateItem();
|
||||
virtual ~nsOfflineCacheUpdateItem();
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIURI> mReferrerURI;
|
||||
nsCOMPtr<nsIWeakReference> mSource;
|
||||
nsCString mClientID;
|
||||
|
||||
nsresult OpenChannel();
|
||||
|
@ -100,9 +98,54 @@ private:
|
|||
nsOfflineCacheUpdate* mUpdate;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
PRUint16 mState;
|
||||
|
||||
protected:
|
||||
PRInt32 mBytesRead;
|
||||
};
|
||||
|
||||
|
||||
class nsOfflineManifestItem : public nsOfflineCacheUpdateItem
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
nsOfflineManifestItem(nsOfflineCacheUpdate *aUpdate,
|
||||
nsIURI *aURI,
|
||||
nsIURI *aReferrerURI,
|
||||
const nsACString &aClientID);
|
||||
virtual ~nsOfflineManifestItem();
|
||||
|
||||
nsCOMArray<nsIURI> &GetExplicitURIs() { return mExplicitURIs; }
|
||||
|
||||
PRBool ParseSucceeded()
|
||||
{ return (mParserState != PARSE_INIT && mParserState != PARSE_ERROR); }
|
||||
PRBool NeedsUpdate() { return mParserState != PARSE_INIT && mNeedsUpdate; }
|
||||
|
||||
private:
|
||||
static NS_METHOD ReadManifest(nsIInputStream *aInputStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aBytesConsumed);
|
||||
|
||||
nsresult HandleManifestLine(const nsCString::const_iterator &aBegin,
|
||||
const nsCString::const_iterator &aEnd);
|
||||
|
||||
enum {
|
||||
PARSE_INIT,
|
||||
PARSE_CACHE_ENTRIES,
|
||||
PARSE_FALLBACK_ENTRIES,
|
||||
PARSE_NETWORK_ENTRIES,
|
||||
PARSE_ERROR
|
||||
} mParserState;
|
||||
|
||||
nsCString mReadBuf;
|
||||
nsCOMArray<nsIURI> mExplicitURIs;
|
||||
PRBool mNeedsUpdate;
|
||||
};
|
||||
|
||||
class nsOfflineCacheUpdate : public nsIOfflineCacheUpdate
|
||||
{
|
||||
public:
|
||||
|
@ -119,17 +162,27 @@ public:
|
|||
|
||||
void LoadCompleted();
|
||||
private:
|
||||
nsresult HandleManifest(PRBool *aDoUpdate);
|
||||
nsresult AddURI(nsIURI *aURI, const nsACString &aOwnerSpec);
|
||||
|
||||
nsresult ProcessNextURI();
|
||||
|
||||
nsresult AddOwnedItems(const nsACString &aOwnerURI);
|
||||
nsresult AddDomainItems();
|
||||
nsresult NotifyAdded(nsOfflineCacheUpdateItem *aItem);
|
||||
|
||||
nsresult GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers);
|
||||
nsresult NotifyError();
|
||||
nsresult NotifyChecking();
|
||||
nsresult NotifyNoUpdate();
|
||||
nsresult NotifyDownloading();
|
||||
nsresult NotifyStarted(nsOfflineCacheUpdateItem *aItem);
|
||||
nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem);
|
||||
nsresult Finish();
|
||||
|
||||
enum {
|
||||
STATE_UNINITIALIZED,
|
||||
STATE_INITIALIZED,
|
||||
STATE_RUNNING,
|
||||
STATE_CHECKING,
|
||||
STATE_DOWNLOADING,
|
||||
STATE_CANCELLED,
|
||||
STATE_FINISHED
|
||||
} mState;
|
||||
|
@ -138,8 +191,11 @@ private:
|
|||
PRBool mPartialUpdate;
|
||||
PRBool mSucceeded;
|
||||
nsCString mUpdateDomain;
|
||||
nsCString mOwnerURI;
|
||||
nsCOMPtr<nsIURI> mReferrerURI;
|
||||
nsCOMPtr<nsIURI> mManifestURI;
|
||||
nsCString mManifestOwnerSpec;
|
||||
nsCString mDynamicOwnerSpec;
|
||||
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
|
||||
nsCString mClientID;
|
||||
nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
|
||||
|
@ -147,6 +203,8 @@ private:
|
|||
|
||||
nsCOMPtr<nsIObserverService> mObserverService;
|
||||
|
||||
nsRefPtr<nsOfflineManifestItem> mManifestItem;
|
||||
|
||||
/* Items being updated */
|
||||
PRInt32 mCurrentItem;
|
||||
nsTArray<nsRefPtr<nsOfflineCacheUpdateItem> > mItems;
|
||||
|
@ -173,8 +231,6 @@ public:
|
|||
nsresult Init();
|
||||
|
||||
nsresult Schedule(nsOfflineCacheUpdate *aUpdate);
|
||||
nsresult ScheduleOnDocumentStop(nsOfflineCacheUpdate *aUpdate,
|
||||
nsIDOMDocument *aDocument);
|
||||
nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
|
||||
|
||||
/**
|
||||
|
@ -190,7 +246,12 @@ private:
|
|||
nsresult ProcessNextUpdate();
|
||||
|
||||
nsTArray<nsRefPtr<nsOfflineCacheUpdate> > mUpdates;
|
||||
nsRefPtrHashtable<nsVoidPtrHashKey, nsOfflineCacheUpdate> mDocUpdates;
|
||||
|
||||
struct PendingUpdate {
|
||||
nsCOMPtr<nsIURI> mManifestURI;
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
};
|
||||
nsClassHashtable<nsVoidPtrHashKey, PendingUpdate> mDocUpdates;
|
||||
|
||||
PRBool mDisabled;
|
||||
PRBool mUpdateRunning;
|
||||
|
|
Загрузка…
Ссылка в новой задаче