mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
04d44bae12
Given that we are going to add ContentBlockingAllowList in CookieSettings, so CookieSettings will be responsible for more stuff than the cookie behavior and cookie permission. We should use a proper name to reflect the purpose of it. The name 'CookieSettings' is misleading that this is only for cookie related stuff. So, we decide to rename 'CookieSettins' to 'CookieJarSettings' which serves better meaning here. Differential Revision: https://phabricator.services.mozilla.com/D63935 --HG-- rename : netwerk/cookie/CookieSettings.cpp => netwerk/cookie/CookieJarSettings.cpp rename : netwerk/cookie/nsICookieSettings.idl => netwerk/cookie/nsICookieJarSettings.idl extra : moz-landing-system : lando
852 lines
22 KiB
C++
852 lines
22 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsDOMOfflineResourceList.h"
|
|
#include "nsError.h"
|
|
#include "mozilla/Components.h"
|
|
#include "mozilla/dom/DOMStringList.h"
|
|
#include "nsMemory.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsIOfflineCacheUpdate.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsILoadContext.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIScriptGlobalObject.h"
|
|
#include "nsIWebNavigation.h"
|
|
#include "nsOfflineCacheUpdate.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "mozilla/dom/OfflineResourceListBinding.h"
|
|
#include "mozilla/EventDispatcher.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
#define IS_CHILD_PROCESS() (GeckoProcessType_Default != XRE_GetProcessType())
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
// 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"
|
|
#define OBSOLETE_STR "obsolete"
|
|
|
|
// To prevent abuse of the resource list for data storage, the number
|
|
// of offline urls and their length are limited.
|
|
|
|
static const char kMaxEntriesPref[] = "offline.max_site_resources";
|
|
#define DEFAULT_MAX_ENTRIES 100
|
|
#define MAX_URI_LENGTH 2048
|
|
|
|
//
|
|
// nsDOMOfflineResourceList
|
|
//
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WEAK_INHERITED(nsDOMOfflineResourceList,
|
|
DOMEventTargetHelper, mCacheUpdate,
|
|
mPendingEvents)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMOfflineResourceList)
|
|
NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
|
|
|
|
nsDOMOfflineResourceList::nsDOMOfflineResourceList(
|
|
nsIURI* aManifestURI, nsIURI* aDocumentURI, nsIPrincipal* aLoadingPrincipal,
|
|
nsPIDOMWindowInner* aWindow)
|
|
: DOMEventTargetHelper(aWindow),
|
|
mInitialized(false),
|
|
mManifestURI(aManifestURI),
|
|
mDocumentURI(aDocumentURI),
|
|
mLoadingPrincipal(aLoadingPrincipal),
|
|
mExposeCacheUpdateStatus(true),
|
|
mStatus(OfflineResourceList_Binding::IDLE) {}
|
|
|
|
nsDOMOfflineResourceList::~nsDOMOfflineResourceList() { ClearCachedKeys(); }
|
|
|
|
JSObject* nsDOMOfflineResourceList::WrapObject(
|
|
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
|
return OfflineResourceList_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
nsresult nsDOMOfflineResourceList::Init() {
|
|
if (mInitialized) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!mManifestURI) {
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
}
|
|
|
|
mManifestURI->GetAsciiSpec(mManifestSpec);
|
|
bool isPrivateWin =
|
|
mLoadingPrincipal
|
|
? mLoadingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0
|
|
: false;
|
|
|
|
nsresult rv = nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
|
|
mManifestURI, mDocumentURI, true, isPrivateWin);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Dynamically-managed resources are stored as a separate ownership list
|
|
// from the manifest.
|
|
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
|
|
if (!innerURI) return NS_ERROR_FAILURE;
|
|
|
|
if (!IS_CHILD_PROCESS()) {
|
|
mApplicationCacheService =
|
|
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Check for in-progress cache updates
|
|
nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
|
|
components::OfflineCacheUpdate::Service();
|
|
NS_ENSURE_TRUE(cacheUpdateService, NS_ERROR_UNEXPECTED);
|
|
|
|
uint32_t numUpdates;
|
|
rv = cacheUpdateService->GetNumUpdates(&numUpdates);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for (uint32_t i = 0; i < numUpdates; i++) {
|
|
nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
|
|
rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
UpdateAdded(cacheUpdate);
|
|
}
|
|
}
|
|
|
|
// watch for new offline cache updates
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (!observerService) return NS_ERROR_FAILURE;
|
|
|
|
rv = observerService->AddObserver(this, "offline-cache-update-added", true);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = observerService->AddObserver(this, "offline-cache-update-completed",
|
|
true);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mInitialized = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::Disconnect() {
|
|
mPendingEvents.Clear();
|
|
|
|
if (mListenerManager) {
|
|
mListenerManager->Disconnect();
|
|
mListenerManager = nullptr;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<DOMStringList> nsDOMOfflineResourceList::GetMozItems(
|
|
ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<DOMStringList> items = new DOMStringList();
|
|
|
|
// If we are not associated with an application cache, return an
|
|
// empty list.
|
|
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
|
|
if (!appCache) {
|
|
return items.forget();
|
|
}
|
|
|
|
aRv = Init();
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsTArray<nsCString> keys;
|
|
aRv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, keys);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (auto& key : keys) {
|
|
items->Add(NS_ConvertUTF8toUTF16(key));
|
|
}
|
|
|
|
return items.forget();
|
|
}
|
|
|
|
bool nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI,
|
|
ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return false;
|
|
}
|
|
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
|
|
if (!appCache) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return false;
|
|
}
|
|
|
|
nsAutoCString key;
|
|
rv = GetCacheKey(aURI, key);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
|
|
uint32_t types;
|
|
rv = appCache->GetTypes(key, &types);
|
|
if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
|
|
return false;
|
|
}
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
|
|
return types & nsIApplicationCache::ITEM_DYNAMIC;
|
|
}
|
|
|
|
uint32_t nsDOMOfflineResourceList::GetMozLength(ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return 0;
|
|
}
|
|
|
|
if (!mManifestURI) {
|
|
return 0;
|
|
}
|
|
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return 0;
|
|
}
|
|
|
|
rv = CacheKeys();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return 0;
|
|
}
|
|
|
|
return mCachedKeys->Length();
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::MozItem(uint32_t aIndex, nsAString& aURI,
|
|
ErrorResult& aRv) {
|
|
bool found;
|
|
IndexedGetter(aIndex, found, aURI, aRv);
|
|
if (!aRv.Failed() && !found) {
|
|
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
|
}
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::IndexedGetter(uint32_t aIndex, bool& aFound,
|
|
nsAString& aURI,
|
|
ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return;
|
|
}
|
|
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
rv = CacheKeys();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (aIndex >= mCachedKeys->Length()) {
|
|
aFound = false;
|
|
return;
|
|
}
|
|
|
|
aFound = true;
|
|
CopyUTF8toUTF16(mCachedKeys->ElementAt(aIndex), aURI);
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::MozAdd(const nsAString& aURI, ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return;
|
|
}
|
|
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
|
|
if (!appCache) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
if (aURI.Length() > MAX_URI_LENGTH) {
|
|
aRv.Throw(NS_ERROR_DOM_BAD_URI);
|
|
return;
|
|
}
|
|
|
|
// this will fail if the URI is not absolute
|
|
nsCOMPtr<nsIURI> requestedURI;
|
|
rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
nsAutoCString scheme;
|
|
rv = requestedURI->GetScheme(scheme);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
bool match;
|
|
rv = mManifestURI->SchemeIs(scheme.get(), &match);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!match) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
uint32_t length = GetMozLength(aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
uint32_t maxEntries =
|
|
Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES);
|
|
|
|
if (length > maxEntries) {
|
|
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
|
return;
|
|
}
|
|
|
|
ClearCachedKeys();
|
|
|
|
nsCOMPtr<nsIOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
|
|
|
|
nsAutoCString clientID;
|
|
rv = appCache->GetClientID(clientID);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
RefPtr<Document> doc = GetOwner()->GetExtantDoc();
|
|
nsCOMPtr<nsICookieJarSettings> cjs = doc ? doc->CookieJarSettings() : nullptr;
|
|
|
|
rv = update->InitPartial(mManifestURI, clientID, mDocumentURI,
|
|
mLoadingPrincipal, cjs);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
rv = update->AddDynamicURI(requestedURI);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
rv = update->Schedule();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::MozRemove(const nsAString& aURI,
|
|
ErrorResult& aRv) {
|
|
if (IS_CHILD_PROCESS()) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return;
|
|
}
|
|
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
|
|
if (!appCache) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
nsAutoCString key;
|
|
rv = GetCacheKey(aURI, key);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
ClearCachedKeys();
|
|
|
|
// XXX: This is a race condition. remove() is specced to remove
|
|
// from the currently associated application cache, but if this
|
|
// happens during an update (or after an update, if we haven't
|
|
// swapped yet), that remove() will be lost when the next update is
|
|
// finished. Need to bring this issue up.
|
|
|
|
rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint16_t nsDOMOfflineResourceList::GetStatus(ErrorResult& aRv) {
|
|
nsresult rv = Init();
|
|
|
|
// Init may fail with INVALID_STATE_ERR if there is no manifest URI.
|
|
// The status attribute should not throw that exception, convert it
|
|
// to an UNCACHED.
|
|
if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
|
|
!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
|
return OfflineResourceList_Binding::UNCACHED;
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return 0;
|
|
}
|
|
|
|
// If this object is not associated with a cache, return UNCACHED
|
|
nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
|
|
if (!appCache) {
|
|
return OfflineResourceList_Binding::UNCACHED;
|
|
}
|
|
|
|
// If there is an update in process, use its status.
|
|
if (mCacheUpdate && mExposeCacheUpdateStatus) {
|
|
uint16_t status;
|
|
rv = mCacheUpdate->GetStatus(&status);
|
|
if (NS_SUCCEEDED(rv) && status != OfflineResourceList_Binding::IDLE) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
if (mAvailableApplicationCache) {
|
|
return OfflineResourceList_Binding::UPDATEREADY;
|
|
}
|
|
|
|
return mStatus;
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::Update(ErrorResult& aRv) {
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
|
|
components::OfflineCacheUpdate::Service();
|
|
if (NS_WARN_IF(!updateService)) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
|
|
|
nsCOMPtr<nsIOfflineCacheUpdate> update;
|
|
rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI,
|
|
mLoadingPrincipal, window,
|
|
getter_AddRefs(update));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::SwapCache(ErrorResult& aRv) {
|
|
nsresult rv = Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
|
|
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
|
|
if (!currentAppCache) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
// Check the current and potentially newly available cache are not identical.
|
|
if (mAvailableApplicationCache == currentAppCache) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
if (mAvailableApplicationCache) {
|
|
nsCString currClientId, availClientId;
|
|
currentAppCache->GetClientID(currClientId);
|
|
mAvailableApplicationCache->GetClientID(availClientId);
|
|
if (availClientId == currClientId) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
} else if (mStatus != OfflineResourceList_Binding::OBSOLETE) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
ClearCachedKeys();
|
|
|
|
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
|
|
GetDocumentAppCacheContainer();
|
|
|
|
// In the case of an obsolete cache group, newAppCache might be null.
|
|
// We will disassociate from the cache in that case.
|
|
if (appCacheContainer) {
|
|
rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mAvailableApplicationCache = nullptr;
|
|
mStatus = OfflineResourceList_Binding::IDLE;
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::FirePendingEvents() {
|
|
for (int32_t i = 0; i < mPendingEvents.Count(); ++i) {
|
|
RefPtr<Event> event = mPendingEvents[i];
|
|
DispatchEvent(*event);
|
|
}
|
|
mPendingEvents.Clear();
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::SendEvent(const nsAString& aEventName) {
|
|
// Don't send events to closed windows
|
|
if (!GetOwner()) {
|
|
return;
|
|
}
|
|
|
|
if (!GetOwner()->GetDocShell()) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
|
event->InitEvent(aEventName, false, true);
|
|
|
|
// We assume anyone that managed to call SendEvent is trusted
|
|
event->SetTrusted(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 (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) {
|
|
mPendingEvents.AppendObject(event);
|
|
return;
|
|
}
|
|
|
|
DispatchEvent(*event);
|
|
}
|
|
|
|
//
|
|
// nsDOMOfflineResourceList::nsIObserver
|
|
//
|
|
NS_IMETHODIMP
|
|
nsDOMOfflineResourceList::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* 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::UpdateStateChanged(nsIOfflineCacheUpdate* aUpdate,
|
|
uint32_t event) {
|
|
mExposeCacheUpdateStatus =
|
|
(event == STATE_CHECKING) || (event == STATE_DOWNLOADING) ||
|
|
(event == STATE_ITEMSTARTED) || (event == STATE_ITEMCOMPLETED) ||
|
|
// During notification of "obsolete" we must expose state of the update
|
|
(event == STATE_OBSOLETE);
|
|
|
|
switch (event) {
|
|
case STATE_ERROR:
|
|
SendEvent(NS_LITERAL_STRING(ERROR_STR));
|
|
break;
|
|
case STATE_CHECKING:
|
|
SendEvent(NS_LITERAL_STRING(CHECKING_STR));
|
|
break;
|
|
case STATE_NOUPDATE:
|
|
SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
|
|
break;
|
|
case STATE_OBSOLETE:
|
|
mStatus = OfflineResourceList_Binding::OBSOLETE;
|
|
mAvailableApplicationCache = nullptr;
|
|
SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
|
|
break;
|
|
case STATE_DOWNLOADING:
|
|
SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
|
|
break;
|
|
case STATE_ITEMSTARTED:
|
|
SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
|
|
break;
|
|
case STATE_ITEMCOMPLETED:
|
|
// Nothing to do here...
|
|
break;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsDOMOfflineResourceList::ApplicationCacheAvailable(
|
|
nsIApplicationCache* aApplicationCache) {
|
|
nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
|
|
if (currentAppCache) {
|
|
// Document already has a cache, we cannot override it. swapCache is
|
|
// here to do it on demand.
|
|
|
|
// If the newly available cache is identical to the current cache, then
|
|
// just ignore this event.
|
|
if (aApplicationCache == currentAppCache) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCString currClientId, availClientId;
|
|
currentAppCache->GetClientID(currClientId);
|
|
aApplicationCache->GetClientID(availClientId);
|
|
if (availClientId == currClientId) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mAvailableApplicationCache = aApplicationCache;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
|
|
GetDocumentAppCacheContainer();
|
|
|
|
if (appCacheContainer) {
|
|
appCacheContainer->SetApplicationCache(aApplicationCache);
|
|
}
|
|
|
|
mAvailableApplicationCache = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDOMOfflineResourceList::GetCacheKey(const nsAString& aURI,
|
|
nsCString& aKey) {
|
|
nsCOMPtr<nsIURI> requestedURI;
|
|
nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return GetCacheKey(requestedURI, aKey);
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate* aUpdate) {
|
|
// Ignore partial updates.
|
|
bool partial;
|
|
nsresult rv = aUpdate->GetPartial(&partial);
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
if (partial) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> updateURI;
|
|
rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI));
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
bool equals;
|
|
rv = updateURI->Equals(mManifestURI, &equals);
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
if (!equals) {
|
|
// This update doesn't belong to us
|
|
return;
|
|
}
|
|
|
|
if (mCacheUpdate) {
|
|
return;
|
|
}
|
|
|
|
// 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, true);
|
|
}
|
|
|
|
already_AddRefed<nsIApplicationCacheContainer>
|
|
nsDOMOfflineResourceList::GetDocumentAppCacheContainer() {
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner());
|
|
if (!webnav) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
|
|
do_GetInterface(webnav);
|
|
return appCacheContainer.forget();
|
|
}
|
|
|
|
already_AddRefed<nsIApplicationCache>
|
|
nsDOMOfflineResourceList::GetDocumentAppCache() {
|
|
nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
|
|
GetDocumentAppCacheContainer();
|
|
|
|
if (appCacheContainer) {
|
|
nsCOMPtr<nsIApplicationCache> applicationCache;
|
|
appCacheContainer->GetApplicationCache(getter_AddRefs(applicationCache));
|
|
return applicationCache.forget();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate* aUpdate) {
|
|
if (aUpdate != mCacheUpdate) {
|
|
// This isn't the update we're watching.
|
|
return;
|
|
}
|
|
|
|
bool partial;
|
|
mCacheUpdate->GetPartial(&partial);
|
|
bool isUpgrade;
|
|
mCacheUpdate->GetIsUpgrade(&isUpgrade);
|
|
|
|
bool succeeded;
|
|
nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
|
|
|
|
mCacheUpdate->RemoveObserver(this);
|
|
mCacheUpdate = nullptr;
|
|
|
|
if (NS_SUCCEEDED(rv) && succeeded && !partial) {
|
|
mStatus = OfflineResourceList_Binding::IDLE;
|
|
if (isUpgrade) {
|
|
SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR));
|
|
} else {
|
|
SendEvent(NS_LITERAL_STRING(CACHED_STR));
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult nsDOMOfflineResourceList::GetCacheKey(nsIURI* aURI, nsCString& aKey) {
|
|
nsresult rv = aURI->GetAsciiSpec(aKey);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// url fragments aren't used in cache keys
|
|
nsAutoCString::const_iterator specStart, specEnd;
|
|
aKey.BeginReading(specStart);
|
|
aKey.EndReading(specEnd);
|
|
if (FindCharInReadable('#', specStart, specEnd)) {
|
|
aKey.BeginReading(specEnd);
|
|
aKey = Substring(specEnd, specStart);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDOMOfflineResourceList::CacheKeys() {
|
|
if (IS_CHILD_PROCESS()) return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
if (mCachedKeys) return NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
|
|
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
|
|
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
|
|
|
|
nsAutoCString originSuffix;
|
|
if (loadContext) {
|
|
mozilla::OriginAttributes oa;
|
|
loadContext->GetOriginAttributes(oa);
|
|
|
|
oa.CreateSuffix(originSuffix);
|
|
}
|
|
|
|
nsAutoCString groupID;
|
|
mApplicationCacheService->BuildGroupIDForSuffix(mManifestURI, originSuffix,
|
|
groupID);
|
|
|
|
nsCOMPtr<nsIApplicationCache> appCache;
|
|
mApplicationCacheService->GetActiveCache(groupID, getter_AddRefs(appCache));
|
|
|
|
if (!appCache) {
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
}
|
|
|
|
mCachedKeys.emplace();
|
|
nsresult rv =
|
|
appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, *mCachedKeys);
|
|
if (NS_FAILED(rv)) {
|
|
mCachedKeys.reset();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void nsDOMOfflineResourceList::ClearCachedKeys() { mCachedKeys.reset(); }
|