mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
305b3df92d
@ -268,7 +268,7 @@ xpcAccessibleTable::GetSelectedCells(nsIArray** aSelectedCells)
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
NS_IMETHODIMP rv = NS_OK;
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIMutableArray> selCells =
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
8
b2g/config/desktop/config.json
Normal file
8
b2g/config/desktop/config.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"gaia": {
|
||||
"l10n": {
|
||||
"vcs": "hgtool",
|
||||
"root": "https://hg.mozilla.org/gaia-l10n"
|
||||
}
|
||||
}
|
||||
}
|
@ -36,6 +36,8 @@ relativesrcdir toolkit/locales:
|
||||
#about:telemetry
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.dtd (%chrome/global/aboutTelemetry.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.properties (%chrome/global/aboutTelemetry.properties)
|
||||
#about:webrtc
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutWebrtc.properties (%chrome/global/aboutWebrtc.properties)
|
||||
|
||||
% override chrome://global/locale/about.dtd chrome://b2g-l10n/locale/overrides/about.dtd
|
||||
% override chrome://global/locale/aboutAbout.dtd chrome://b2g-l10n/locale/overrides/aboutAbout.dtd
|
||||
@ -54,6 +56,7 @@ relativesrcdir toolkit/locales:
|
||||
% override chrome://global/locale/mozilla.dtd chrome://b2g-l10n/locale/overrides/global/mozilla.dtd
|
||||
% override chrome://global/locale/aboutTelemetry.dtd chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.dtd
|
||||
% override chrome://global/locale/aboutTelemetry.properties chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.properties
|
||||
% override chrome://global/locale/aboutWebrtc.properties chrome://b2g-l10n/locale/overrides/global/aboutWebrtc.properties
|
||||
|
||||
# overrides for dom l10n, also for en-US
|
||||
relativesrcdir dom/locales:
|
||||
|
@ -342,6 +342,17 @@ nsBrowserContentHandler.prototype = {
|
||||
cmdLine.preventDefault = true;
|
||||
}
|
||||
|
||||
// In the past, when an instance was not already running, the -remote
|
||||
// option returned an error code. Any script or application invoking the
|
||||
// -remote option is expected to be handling this case, otherwise they
|
||||
// wouldn't be doing anything when there is no Firefox already running.
|
||||
// Making the -remote option always return an error code makes those
|
||||
// scripts or applications handle the situation as if Firefox was not
|
||||
// already running.
|
||||
if (cmdLine.handleFlag("remote", true)) {
|
||||
throw NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
var uriparam;
|
||||
try {
|
||||
while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
|
||||
|
@ -4651,8 +4651,7 @@ nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
|
||||
bool aCheckIfUnloadFired)
|
||||
{
|
||||
bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
|
||||
(!aCheckIfUnloadFired || !mFiredUnloadEvent) &&
|
||||
!mBlockNavigation;
|
||||
(!aCheckIfUnloadFired || !mFiredUnloadEvent);
|
||||
if (!isAllowed) {
|
||||
return false;
|
||||
}
|
||||
@ -9563,8 +9562,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
nsIDocShell** aDocShell,
|
||||
nsIRequest** aRequest)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mBlockNavigation);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
mOriginalUriString.Truncate();
|
||||
|
||||
@ -10020,19 +10017,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
GetCurScrollPos(ScrollOrientation_X, &cx);
|
||||
GetCurScrollPos(ScrollOrientation_Y, &cy);
|
||||
|
||||
{
|
||||
AutoRestore<bool> scrollingToAnchor(mBlockNavigation);
|
||||
mBlockNavigation = true;
|
||||
|
||||
// ScrollToAnchor doesn't necessarily cause us to scroll the window;
|
||||
// the function decides whether a scroll is appropriate based on the
|
||||
// arguments it receives. But even if we don't end up scrolling,
|
||||
// ScrollToAnchor performs other important tasks, such as informing
|
||||
// the presShell that we have a new hash. See bug 680257.
|
||||
rv = ScrollToAnchor(curHash, newHash, aLoadType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Reset mLoadType to its original value once we exit this block,
|
||||
// because this short-circuited load might have started after a
|
||||
// normal, network load, and we don't want to clobber its load type.
|
||||
@ -10122,16 +10106,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
}
|
||||
}
|
||||
|
||||
/* restore previous position of scroller(s), if we're moving
|
||||
* back in history (bug 59774)
|
||||
*/
|
||||
if (mOSHE && (aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_RELOAD_NORMAL)) {
|
||||
nscoord bx, by;
|
||||
mOSHE->GetScrollPosition(&bx, &by);
|
||||
SetCurScrollPosEx(bx, by);
|
||||
}
|
||||
|
||||
/* Restore the original LSHE if we were loading something
|
||||
* while short-circuited load was initiated.
|
||||
*/
|
||||
@ -10166,12 +10140,36 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
|
||||
SetDocCurrentStateObj(mOSHE);
|
||||
|
||||
// Inform the favicon service that the favicon for oldURI also
|
||||
// applies to aURI.
|
||||
CopyFavicon(currentURI, aURI, mInPrivateBrowsing);
|
||||
|
||||
nsRefPtr<nsGlobalWindow> win = mScriptGlobal ?
|
||||
mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
|
||||
|
||||
// ScrollToAnchor doesn't necessarily cause us to scroll the window;
|
||||
// the function decides whether a scroll is appropriate based on the
|
||||
// arguments it receives. But even if we don't end up scrolling,
|
||||
// ScrollToAnchor performs other important tasks, such as informing
|
||||
// the presShell that we have a new hash. See bug 680257.
|
||||
rv = ScrollToAnchor(curHash, newHash, aLoadType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
/* restore previous position of scroller(s), if we're moving
|
||||
* back in history (bug 59774)
|
||||
*/
|
||||
if (mOSHE && (aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_RELOAD_NORMAL)) {
|
||||
nscoord bx, by;
|
||||
mOSHE->GetScrollPosition(&bx, &by);
|
||||
SetCurScrollPosEx(bx, by);
|
||||
}
|
||||
|
||||
// Dispatch the popstate and hashchange events, as appropriate.
|
||||
//
|
||||
// The event dispatch below can cause us to re-enter script and
|
||||
// destroy the docshell, nulling out mScriptGlobal. Hold a stack
|
||||
// reference to avoid null derefs. See bug 914521.
|
||||
nsRefPtr<nsGlobalWindow> win = mScriptGlobal;
|
||||
if (win) {
|
||||
// Fire a hashchange event URIs differ, and only in their hashes.
|
||||
bool doHashchange = sameExceptHashes && !curHash.Equals(newHash);
|
||||
@ -10187,10 +10185,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
}
|
||||
}
|
||||
|
||||
// Inform the favicon service that the favicon for oldURI also
|
||||
// applies to aURI.
|
||||
CopyFavicon(currentURI, aURI, mInPrivateBrowsing);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
@ -13995,7 +13989,8 @@ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return swm->DispatchFetchEvent(doc, aChannel);
|
||||
bool isReload = mLoadType & LOAD_CMD_RELOAD;
|
||||
return swm->DispatchFetchEvent(doc, aChannel, isReload);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -898,7 +898,6 @@ protected:
|
||||
bool mUseRemoteTabs;
|
||||
bool mDeviceSizeIsPageSize;
|
||||
bool mWindowDraggingAllowed;
|
||||
bool mBlockNavigation;
|
||||
|
||||
// Because scriptability depends on the mAllowJavascript values of our
|
||||
// ancestors, we cache the effective scriptability and recompute it when
|
||||
|
@ -442,6 +442,9 @@ AnimationPlayer::DoPlay()
|
||||
// Clear ready promise. We'll create a new one lazily.
|
||||
mReady = nullptr;
|
||||
|
||||
// Clear the start time until we resolve a new one
|
||||
mStartTime.SetNull();
|
||||
|
||||
mIsPending = true;
|
||||
|
||||
nsIDocument* doc = GetRenderedDocument();
|
||||
|
@ -199,27 +199,6 @@ function EventWatcher(watchedNode, eventTypes)
|
||||
// The former is the start of the start delay. The latter is the start of the
|
||||
// active interval. (If there is no delay, they are the same.)
|
||||
|
||||
// Called when startTime is set to the time the start delay would ideally
|
||||
// start (not accounting for any delay to next paint tick).
|
||||
function checkStateOnSettingStartTimeToAnimationCreationTime(player)
|
||||
{
|
||||
// We don't test player.startTime since our caller just set it.
|
||||
|
||||
assert_equals(player.playState, 'running',
|
||||
'AnimationPlayer.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = player.source.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(player)
|
||||
{
|
||||
@ -294,41 +273,92 @@ function checkStateAtActiveIntervalEndTime(player)
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (play-pending) animation is unresolved');
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (pause-pending) animation is unresolved');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_true(player.startTime > 0,
|
||||
'startTime is resolved when running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is resolved when running');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_equals(player.startTime, null,
|
||||
'startTime is unresolved when paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is unresolved when paused');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
player.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
/* FIXME: Switch this on once deferred pausing is enabled
|
||||
assert_not_equals(player.startTime, null,
|
||||
'startTime is resolved when pause-pending');
|
||||
*/
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
assert_equals(player.startTime, null,
|
||||
'startTime is unresolved when play-pending');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while pause-pending and play-pending');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
// Seek to end to put us in the finished state
|
||||
// FIXME: Once we implement finish(), use that here.
|
||||
player.currentTime = 100 * 1000;
|
||||
player.ready.then(t.step_func(function() {
|
||||
// Call play() which puts us back in the running state
|
||||
player.play();
|
||||
// FIXME: Enable this once we implement finishing behavior (bug 1074630)
|
||||
/*
|
||||
assert_equals(player.startTime, null, 'startTime is unresolved');
|
||||
*/
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while play-pending from finished state');
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
// Animations shouldn't start until the next paint tick, so:
|
||||
assert_equals(player.startTime, null,
|
||||
'AnimationPlayer.startTime should be unresolved when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(player.playState, "pending",
|
||||
'AnimationPlayer.playState should be "pending" when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"running" when an animation is initially created');
|
||||
|
||||
// XXX Ideally we would have a test to check the ready Promise is initially
|
||||
// unresolved, but currently there is no Web API to do that. Waiting for the
|
||||
// ready Promise with a timeout doesn't work because the resolved callback
|
||||
// will be called (async) regardless of whether the Promise was resolved in
|
||||
// the past or is resolved in the future.
|
||||
|
||||
var currentTime = player.timeline.currentTime;
|
||||
player.startTime = currentTime;
|
||||
assert_approx_equals(player.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
|
||||
checkStateOnSettingStartTimeToAnimationCreationTime(player);
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
}, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
|
@ -190,23 +190,6 @@ function EventWatcher(watchedNode, eventTypes)
|
||||
// The former is the start of the start delay. The latter is the start of the
|
||||
// active interval. (If there is no delay, they are the same.)
|
||||
|
||||
// Called when startTime is set to the time the start delay would ideally
|
||||
// start (not accounting for any delay to next paint tick).
|
||||
function checkStateOnSettingStartTimeToAnimationCreationTime(player)
|
||||
{
|
||||
// We don't test player.startTime since our caller just set it.
|
||||
|
||||
assert_equals(player.playState, 'running',
|
||||
'AnimationPlayer.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
var div = player.source.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, INITIAL_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(player)
|
||||
{
|
||||
@ -268,37 +251,28 @@ function checkStateAtActiveIntervalEndTime(player)
|
||||
'value at the end of the active duration');
|
||||
}
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created transition is unresolved');
|
||||
|
||||
// Animations shouldn't start until the next paint tick, so:
|
||||
assert_equals(player.startTime, null,
|
||||
'AnimationPlayer.startTime should be unresolved when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(player.playState, "pending",
|
||||
'AnimationPlayer.playState should be "pending" when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
// XXX Ideally we would have a test to check the ready Promise is initially
|
||||
// unresolved, but currently there is no Web API to do that. Waiting for the
|
||||
// ready Promise with a timeout doesn't work because the resolved callback
|
||||
// will be called (async) regardless of whether the Promise was resolved in
|
||||
// the past or is resolved in the future.
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
var currentTime = player.timeline.currentTime;
|
||||
player.startTime = currentTime;
|
||||
assert_approx_equals(player.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
|
||||
checkStateOnSettingStartTimeToAnimationCreationTime(player);
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "mozilla/dom/PerformanceNavigationBinding.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "js/HeapAPI.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#define PERFLOG(msg, ...) __android_log_print(ANDROID_LOG_INFO, "PerformanceTiming", msg, ##__VA_ARGS__)
|
||||
@ -389,10 +390,27 @@ nsPerformanceNavigation::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenP
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsPerformance, DOMEventTargetHelper,
|
||||
mWindow, mTiming,
|
||||
mNavigation, mEntries,
|
||||
mParentPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsPerformance)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow, mTiming,
|
||||
mNavigation, mEntries,
|
||||
mParentPerformance)
|
||||
tmp->mMozMemory = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow, mTiming,
|
||||
mNavigation, mEntries,
|
||||
mParentPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
|
||||
@ -412,6 +430,7 @@ nsPerformance::nsPerformance(nsPIDOMWindow* aWindow,
|
||||
|
||||
nsPerformance::~nsPerformance()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
// QueryInterface implementation for nsPerformance
|
||||
@ -420,6 +439,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPerformance)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
void
|
||||
nsPerformance::GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj)
|
||||
{
|
||||
if (!mMozMemory) {
|
||||
mMozMemory = js::gc::NewMemoryInfoObject(aCx);
|
||||
if (mMozMemory) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
}
|
||||
|
||||
aObj.set(mMozMemory);
|
||||
}
|
||||
|
||||
nsPerformanceTiming*
|
||||
nsPerformance::Timing()
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
|
||||
@ -297,7 +298,7 @@ public:
|
||||
nsPerformance* aParentPerformance);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsPerformance, DOMEventTargetHelper)
|
||||
|
||||
nsDOMNavigationTiming* GetDOMTiming() const
|
||||
{
|
||||
@ -344,6 +345,8 @@ public:
|
||||
mozilla::ErrorResult& aRv);
|
||||
void ClearMeasures(const mozilla::dom::Optional<nsAString>& aName);
|
||||
|
||||
void GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj);
|
||||
|
||||
IMPL_EVENT_HANDLER(resourcetimingbufferfull)
|
||||
|
||||
private:
|
||||
@ -364,6 +367,7 @@ private:
|
||||
nsTArray<nsRefPtr<PerformanceEntry> > mEntries;
|
||||
nsRefPtr<nsPerformance> mParentPerformance;
|
||||
uint64_t mPrimaryBufferSize;
|
||||
JS::Heap<JSObject*> mMozMemory;
|
||||
|
||||
static const uint64_t kDefaultBufferSize = 150;
|
||||
|
||||
|
6
dom/cache/Action.cpp
vendored
6
dom/cache/Action.cpp
vendored
@ -10,13 +10,13 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
NS_IMPL_ISUPPORTS0(mozilla::dom::cache::Action::Resolver);
|
||||
|
||||
void
|
||||
Action::CancelOnInitiatingThread()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Action);
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
// It is possible for cancellation to be duplicated. For example, an
|
||||
// individual Cache could have its Actions canceled and then shutdown
|
||||
// could trigger a second action.
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
|
20
dom/cache/Action.h
vendored
20
dom/cache/Action.h
vendored
@ -18,21 +18,19 @@ namespace cache {
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
class Resolver : public nsISupports
|
||||
class Resolver
|
||||
{
|
||||
protected:
|
||||
// virtual because deleted through base class pointer
|
||||
virtual ~Resolver() { }
|
||||
|
||||
public:
|
||||
// Note: Action must drop Resolver ref after calling Resolve()!
|
||||
// Note: Must be called on the same thread used to execute
|
||||
// Action::RunOnTarget().
|
||||
virtual void Resolve(nsresult aRv) = 0;
|
||||
|
||||
// We must use ISUPPORTS for our refcounting here because sub-classes also
|
||||
// want to inherit interfaces like nsIRunnable.
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
AddRef(void) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
Release(void) = 0;
|
||||
};
|
||||
|
||||
// Execute operations on the target thread. Once complete call
|
||||
@ -51,6 +49,9 @@ public:
|
||||
// Cancellation is a best effort to stop processing as soon as possible, but
|
||||
// does not guarantee the Action will not run.
|
||||
//
|
||||
// CancelOnInitiatingThread() may be called more than once. Subsequent
|
||||
// calls should have no effect.
|
||||
//
|
||||
// Default implementation sets an internal cancellation flag that can be
|
||||
// queried with IsCanceled().
|
||||
virtual void CancelOnInitiatingThread();
|
||||
@ -63,8 +64,7 @@ public:
|
||||
// given cache ID then override this to return true.
|
||||
virtual bool MatchesCacheId(CacheId aCacheId) const { return false; }
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::Action)
|
||||
NS_DECL_OWNINGTHREAD
|
||||
NS_INLINE_DECL_REFCOUNTING(cache::Action)
|
||||
|
||||
protected:
|
||||
Action();
|
||||
|
429
dom/cache/Context.cpp
vendored
429
dom/cache/Context.cpp
vendored
@ -6,10 +6,12 @@
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/cache/Action.h"
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
#include "mozilla/dom/cache/ManagerId.h"
|
||||
#include "mozilla/dom/cache/OfflineStorage.h"
|
||||
#include "mozilla/dom/quota/OriginOrPatternString.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "nsIFile.h"
|
||||
@ -21,19 +23,18 @@ namespace {
|
||||
|
||||
using mozilla::dom::Nullable;
|
||||
using mozilla::dom::cache::QuotaInfo;
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::OriginOrPatternString;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
|
||||
// Executed when the context is destroyed to release our lock on the
|
||||
// QuotaManager.
|
||||
// Release our lock on the QuotaManager directory asynchronously.
|
||||
class QuotaReleaseRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo, const nsACString& aQuotaId)
|
||||
explicit QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo)
|
||||
: mQuotaInfo(aQuotaInfo)
|
||||
, mQuotaId(aQuotaId)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
@ -43,7 +44,7 @@ public:
|
||||
MOZ_ASSERT(qm);
|
||||
qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
|
||||
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
||||
mQuotaId);
|
||||
mQuotaInfo.mStorageId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -51,7 +52,6 @@ private:
|
||||
~QuotaReleaseRunnable() { }
|
||||
|
||||
const QuotaInfo mQuotaInfo;
|
||||
const nsCString mQuotaId;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
@ -70,20 +70,19 @@ using mozilla::dom::quota::PersistenceType;
|
||||
// the QuotaManager. This must be performed for each origin before any disk
|
||||
// IO occurrs.
|
||||
class Context::QuotaInitRunnable final : public nsIRunnable
|
||||
, public Action::Resolver
|
||||
{
|
||||
public:
|
||||
QuotaInitRunnable(Context* aContext,
|
||||
Manager* aManager,
|
||||
const nsACString& aQuotaId,
|
||||
Action* aQuotaIOThreadAction)
|
||||
: mContext(aContext)
|
||||
, mThreadsafeHandle(aContext->CreateThreadsafeHandle())
|
||||
, mManager(aManager)
|
||||
, mQuotaId(aQuotaId)
|
||||
, mQuotaIOThreadAction(aQuotaIOThreadAction)
|
||||
, mInitiatingThread(NS_GetCurrentThread())
|
||||
, mState(STATE_INIT)
|
||||
, mResult(NS_OK)
|
||||
, mState(STATE_INIT)
|
||||
, mNeedsQuotaRelease(false)
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mManager);
|
||||
@ -104,26 +103,35 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
virtual void Resolve(nsresult aRv) override
|
||||
{
|
||||
// Depending on the error or success path, this can run on either the
|
||||
// main thread or the QuotaManager IO thread. The IO thread is an
|
||||
// idle thread which may be destroyed and recreated, so its hard to
|
||||
// assert on.
|
||||
MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(aRv));
|
||||
|
||||
mResult = aRv;
|
||||
mState = STATE_COMPLETING;
|
||||
|
||||
nsresult rv = mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Crash for
|
||||
// this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch QuotaInitRunnable to initiating thread.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class SyncResolver final : public Action::Resolver
|
||||
{
|
||||
public:
|
||||
SyncResolver()
|
||||
: mResolved(false)
|
||||
, mResult(NS_OK)
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
Resolve(nsresult aRv) override
|
||||
{
|
||||
MOZ_ASSERT(!mResolved);
|
||||
mResolved = true;
|
||||
mResult = aRv;
|
||||
};
|
||||
|
||||
bool Resolved() const { return mResolved; }
|
||||
nsresult Result() const { return mResult; }
|
||||
|
||||
private:
|
||||
~SyncResolver() { }
|
||||
|
||||
bool mResolved;
|
||||
nsresult mResult;
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(Context::QuotaInitRunnable::SyncResolver, override)
|
||||
};
|
||||
|
||||
~QuotaInitRunnable()
|
||||
{
|
||||
MOZ_ASSERT(mState == STATE_COMPLETE);
|
||||
@ -152,21 +160,22 @@ private:
|
||||
}
|
||||
|
||||
nsRefPtr<Context> mContext;
|
||||
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
nsRefPtr<Manager> mManager;
|
||||
const nsCString mQuotaId;
|
||||
nsRefPtr<Action> mQuotaIOThreadAction;
|
||||
nsCOMPtr<nsIThread> mInitiatingThread;
|
||||
State mState;
|
||||
nsresult mResult;
|
||||
QuotaInfo mQuotaInfo;
|
||||
nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;
|
||||
State mState;
|
||||
bool mNeedsQuotaRelease;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::QuotaInitRunnable,
|
||||
Action::Resolver, nsIRunnable);
|
||||
NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
|
||||
|
||||
// The QuotaManager init state machine is represented in the following diagram:
|
||||
//
|
||||
@ -191,7 +200,7 @@ NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::QuotaInitRunnable,
|
||||
// +----------+------------+ |
|
||||
// | |
|
||||
// +---------v---------+ +------v------+
|
||||
// | Running | Resolve() | Completing |
|
||||
// | Running | | Completing |
|
||||
// | (Quota IO Thread) +------------>(Orig Thread)|
|
||||
// +-------------------+ +------+------+
|
||||
// |
|
||||
@ -200,14 +209,15 @@ NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::QuotaInitRunnable,
|
||||
// +----------+
|
||||
//
|
||||
// The initialization process proceeds through the main states. If an error
|
||||
// occurs, then we transition back to Completing state back on the original
|
||||
// thread.
|
||||
// occurs, then we transition to Completing state back on the original thread.
|
||||
NS_IMETHODIMP
|
||||
Context::QuotaInitRunnable::Run()
|
||||
{
|
||||
// May run on different threads depending on the state. See individual
|
||||
// state cases for thread assertions.
|
||||
|
||||
nsRefPtr<SyncResolver> resolver = new SyncResolver();
|
||||
|
||||
switch(mState) {
|
||||
// -----------------------------------
|
||||
case STATE_CALL_WAIT_FOR_OPEN_ALLOWED:
|
||||
@ -215,8 +225,8 @@ Context::QuotaInitRunnable::Run()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
QuotaManager* qm = QuotaManager::GetOrCreate();
|
||||
if (!qm) {
|
||||
Resolve(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
resolver->Resolve(NS_ERROR_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
nsRefPtr<ManagerId> managerId = mManager->GetManagerId();
|
||||
@ -226,20 +236,26 @@ Context::QuotaInitRunnable::Run()
|
||||
&mQuotaInfo.mOrigin,
|
||||
&mQuotaInfo.mIsApp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Resolve(rv);
|
||||
return NS_OK;
|
||||
resolver->Resolve(rv);
|
||||
break;
|
||||
}
|
||||
|
||||
QuotaManager::GetStorageId(PERSISTENCE_TYPE_DEFAULT,
|
||||
mQuotaInfo.mOrigin,
|
||||
Client::DOMCACHE,
|
||||
NS_LITERAL_STRING("cache"),
|
||||
mQuotaInfo.mStorageId);
|
||||
|
||||
// QuotaManager::WaitForOpenAllowed() will hold a reference to us as
|
||||
// a callback. We will then get executed again on the main thread when
|
||||
// it is safe to open the quota directory.
|
||||
mState = STATE_WAIT_FOR_OPEN_ALLOWED;
|
||||
rv = qm->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
|
||||
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
||||
mQuotaId, this);
|
||||
mQuotaInfo.mStorageId, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
Resolve(rv);
|
||||
return NS_OK;
|
||||
resolver->Resolve(rv);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -247,13 +263,21 @@ Context::QuotaInitRunnable::Run()
|
||||
case STATE_WAIT_FOR_OPEN_ALLOWED:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mNeedsQuotaRelease = true;
|
||||
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
MOZ_ASSERT(qm);
|
||||
|
||||
nsRefPtr<OfflineStorage> offlineStorage =
|
||||
OfflineStorage::Register(mThreadsafeHandle, mQuotaInfo);
|
||||
mOfflineStorage = new nsMainThreadPtrHolder<OfflineStorage>(offlineStorage);
|
||||
|
||||
mState = STATE_ENSURE_ORIGIN_INITIALIZED;
|
||||
nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Resolve(rv);
|
||||
return NS_OK;
|
||||
resolver->Resolve(rv);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -273,21 +297,21 @@ Context::QuotaInitRunnable::Run()
|
||||
mQuotaInfo.mIsApp,
|
||||
getter_AddRefs(mQuotaInfo.mDir));
|
||||
if (NS_FAILED(rv)) {
|
||||
Resolve(rv);
|
||||
return NS_OK;
|
||||
resolver->Resolve(rv);
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
|
||||
if (!mQuotaIOThreadAction) {
|
||||
Resolve(NS_OK);
|
||||
return NS_OK;
|
||||
resolver->Resolve(NS_OK);
|
||||
break;
|
||||
}
|
||||
|
||||
// Execute the provided initialization Action. We pass ourselves as the
|
||||
// Resolver. The Action must either call Resolve() immediately or hold
|
||||
// a ref to us and call Resolve() later.
|
||||
mQuotaIOThreadAction->RunOnTarget(this, mQuotaInfo);
|
||||
// Execute the provided initialization Action. The Action must Resolve()
|
||||
// before returning.
|
||||
mQuotaIOThreadAction->RunOnTarget(resolver, mQuotaInfo);
|
||||
MOZ_ASSERT(resolver->Resolved());
|
||||
|
||||
break;
|
||||
}
|
||||
@ -298,8 +322,15 @@ Context::QuotaInitRunnable::Run()
|
||||
if (mQuotaIOThreadAction) {
|
||||
mQuotaIOThreadAction->CompleteOnInitiatingThread(mResult);
|
||||
}
|
||||
mContext->OnQuotaInit(mResult, mQuotaInfo);
|
||||
mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage);
|
||||
mState = STATE_COMPLETE;
|
||||
|
||||
if (mNeedsQuotaRelease) {
|
||||
// Unlock the quota dir if we locked it previously
|
||||
nsCOMPtr<nsIRunnable> runnable = new QuotaReleaseRunnable(mQuotaInfo);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
|
||||
}
|
||||
|
||||
// Explicitly cleanup here as the destructor could fire on any of
|
||||
// the threads we have bounced through.
|
||||
Clear();
|
||||
@ -309,10 +340,20 @@ Context::QuotaInitRunnable::Run()
|
||||
default:
|
||||
{
|
||||
MOZ_CRASH("unexpected state in QuotaInitRunnable");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resolver->Resolved()) {
|
||||
MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(resolver->Result()));
|
||||
|
||||
MOZ_ASSERT(NS_SUCCEEDED(mResult));
|
||||
mResult = resolver->Result();
|
||||
|
||||
mState = STATE_COMPLETING;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -321,6 +362,7 @@ Context::QuotaInitRunnable::Run()
|
||||
// is initialized.
|
||||
class Context::ActionRunnable final : public nsIRunnable
|
||||
, public Action::Resolver
|
||||
, public Context::Activity
|
||||
{
|
||||
public:
|
||||
ActionRunnable(Context* aContext, nsIEventTarget* aTarget, Action* aAction,
|
||||
@ -332,6 +374,7 @@ public:
|
||||
, mInitiatingThread(NS_GetCurrentThread())
|
||||
, mState(STATE_INIT)
|
||||
, mResult(NS_OK)
|
||||
, mExecutingRunOnTarget(false)
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mTarget);
|
||||
@ -354,12 +397,15 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool MatchesCacheId(CacheId aCacheId) {
|
||||
virtual bool
|
||||
MatchesCacheId(CacheId aCacheId) const override
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
return mAction->MatchesCacheId(aCacheId);
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
virtual void
|
||||
Cancel() override
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
mAction->CancelOnInitiatingThread();
|
||||
@ -369,14 +415,26 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(mTarget == NS_GetCurrentThread());
|
||||
MOZ_ASSERT(mState == STATE_RUNNING);
|
||||
|
||||
mResult = aRv;
|
||||
mState = STATE_COMPLETING;
|
||||
nsresult rv = mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Crash
|
||||
// for this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch ActionRunnable to initiating thread.");
|
||||
|
||||
// We ultimately must complete on the initiating thread, but bounce through
|
||||
// the current thread again to ensure that we don't destroy objects and
|
||||
// state out from under the currently running action's stack.
|
||||
mState = STATE_RESOLVING;
|
||||
|
||||
// If we were resolved synchronously within Action::RunOnTarget() then we
|
||||
// can avoid a thread bounce and just resolve once RunOnTarget() returns.
|
||||
// The Run() method will handle this by looking at mState after
|
||||
// RunOnTarget() returns.
|
||||
if (mExecutingRunOnTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we are in an asynchronous resolve. And must perform a thread
|
||||
// bounce to run on the target thread again.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -392,7 +450,7 @@ private:
|
||||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mAction);
|
||||
mContext->OnActionRunnableComplete(this);
|
||||
mContext->RemoveActivity(this);
|
||||
mContext = nullptr;
|
||||
mAction = nullptr;
|
||||
}
|
||||
@ -402,6 +460,7 @@ private:
|
||||
STATE_INIT,
|
||||
STATE_RUN_ON_TARGET,
|
||||
STATE_RUNNING,
|
||||
STATE_RESOLVING,
|
||||
STATE_COMPLETING,
|
||||
STATE_COMPLETE
|
||||
};
|
||||
@ -414,13 +473,15 @@ private:
|
||||
State mState;
|
||||
nsresult mResult;
|
||||
|
||||
// Only accessible on target thread;
|
||||
bool mExecutingRunOnTarget;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::ActionRunnable,
|
||||
Action::Resolver, nsIRunnable);
|
||||
NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::ActionRunnable, nsIRunnable);
|
||||
|
||||
// The ActionRunnable has a simpler state machine. It basically needs to run
|
||||
// the action on the target thread and then complete on the original thread.
|
||||
@ -432,11 +493,17 @@ NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::ActionRunnable,
|
||||
// |
|
||||
// +-------v---------+
|
||||
// | RunOnTarget |
|
||||
// |Target IO Thread)+-------------------------------+
|
||||
// +-------+---------+ |
|
||||
// | |
|
||||
// +-------v----------+ Resolve() +-------v-----+
|
||||
// | Running | | Completing |
|
||||
// |Target IO Thread)+---+ Resolve()
|
||||
// +-------+---------+ |
|
||||
// | |
|
||||
// +-------v----------+ |
|
||||
// | Running | |
|
||||
// |(Target IO Thread)| |
|
||||
// +------------------+ |
|
||||
// | Resolve() |
|
||||
// +-------v----------+ |
|
||||
// | Resolving <--+ +-------------+
|
||||
// | | | Completing |
|
||||
// |(Target IO Thread)+---------------------->(Orig Thread)|
|
||||
// +------------------+ +-------+-----+
|
||||
// |
|
||||
@ -457,8 +524,39 @@ Context::ActionRunnable::Run()
|
||||
case STATE_RUN_ON_TARGET:
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
|
||||
MOZ_ASSERT(!mExecutingRunOnTarget);
|
||||
|
||||
// Note that we are calling RunOnTarget(). This lets us detect
|
||||
// if Resolve() is called synchronously.
|
||||
AutoRestore<bool> executingRunOnTarget(mExecutingRunOnTarget);
|
||||
mExecutingRunOnTarget = true;
|
||||
|
||||
mState = STATE_RUNNING;
|
||||
mAction->RunOnTarget(this, mQuotaInfo);
|
||||
|
||||
// Resolve was called synchronously from RunOnTarget(). We can
|
||||
// immediately move to completing now since we are sure RunOnTarget()
|
||||
// completed.
|
||||
if (mState == STATE_RESOLVING) {
|
||||
// Use recursion instead of switch case fall-through... Seems slightly
|
||||
// easier to understand.
|
||||
Run();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// -----------------
|
||||
case STATE_RESOLVING:
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
|
||||
// The call to Action::RunOnTarget() must have returned now if we
|
||||
// are running on the target thread again. We may now proceed
|
||||
// with completion.
|
||||
mState = STATE_COMPLETING;
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Crash
|
||||
// for this invariant violation.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
|
||||
break;
|
||||
}
|
||||
// -------------------
|
||||
@ -482,6 +580,99 @@ Context::ActionRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::AllowToClose()
|
||||
{
|
||||
if (mOwningThread == NS_GetCurrentThread()) {
|
||||
AllowToCloseOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::InvalidateAndAllowToClose()
|
||||
{
|
||||
if (mOwningThread == NS_GetCurrentThread()) {
|
||||
InvalidateAndAllowToCloseOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
Context::ThreadsafeHandle::ThreadsafeHandle(Context* aContext)
|
||||
: mStrongRef(aContext)
|
||||
, mWeakRef(aContext)
|
||||
, mOwningThread(NS_GetCurrentThread())
|
||||
{
|
||||
}
|
||||
|
||||
Context::ThreadsafeHandle::~ThreadsafeHandle()
|
||||
{
|
||||
// Normally we only touch mStrongRef on the owning thread. This is safe,
|
||||
// however, because when we do use mStrongRef on the owning thread we are
|
||||
// always holding a strong ref to the ThreadsafeHandle via the owning
|
||||
// runnable. So we cannot run the ThreadsafeHandle destructor simultaneously.
|
||||
if (!mStrongRef || mOwningThread == NS_GetCurrentThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewNonOwningRunnableMethod(mStrongRef.forget().take(), &Context::Release);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::AllowToCloseOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
// A Context "closes" when its ref count drops to zero. Dropping this
|
||||
// strong ref is necessary, but not sufficient for the close to occur.
|
||||
// Any outstanding IO will continue and keep the Context alive. Once
|
||||
// the Context is idle, it will be destroyed.
|
||||
mStrongRef = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
// Cancel the Context through the weak reference. This means we can
|
||||
// allow the Context to close by dropping the strong ref, but then
|
||||
// still cancel ongoing IO if necessary.
|
||||
if (mWeakRef) {
|
||||
mWeakRef->Invalidate();
|
||||
}
|
||||
// We should synchronously have AllowToCloseOnOwningThread called when
|
||||
// the Context is canceled.
|
||||
MOZ_ASSERT(!mStrongRef);
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
MOZ_ASSERT(!mStrongRef);
|
||||
MOZ_ASSERT(mWeakRef);
|
||||
MOZ_ASSERT(mWeakRef == aContext);
|
||||
mWeakRef = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Context>
|
||||
Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
|
||||
@ -489,8 +680,7 @@ Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
|
||||
nsRefPtr<Context> context = new Context(aManager);
|
||||
|
||||
nsRefPtr<QuotaInitRunnable> runnable =
|
||||
new QuotaInitRunnable(context, aManager, NS_LITERAL_CSTRING("Cache"),
|
||||
aQuotaIOThreadAction);
|
||||
new QuotaInitRunnable(context, aManager, aQuotaIOThreadAction);
|
||||
nsresult rv = runnable->Dispatch();
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Shutdown
|
||||
@ -535,9 +725,29 @@ Context::CancelAll()
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
mState = STATE_CONTEXT_CANCELED;
|
||||
mPendingActions.Clear();
|
||||
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
|
||||
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
|
||||
runnable->Cancel();
|
||||
{
|
||||
ActivityList::ForwardIterator iter(mActivityList);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->Cancel();
|
||||
}
|
||||
}
|
||||
AllowToClose();
|
||||
}
|
||||
|
||||
void
|
||||
Context::Invalidate()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
mManager->Invalidate();
|
||||
CancelAll();
|
||||
}
|
||||
|
||||
void
|
||||
Context::AllowToClose()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
if (mThreadsafeHandle) {
|
||||
mThreadsafeHandle->AllowToClose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,15 +755,20 @@ void
|
||||
Context::CancelForCacheId(CacheId aCacheId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
||||
|
||||
// Remove matching pending actions
|
||||
for (int32_t i = mPendingActions.Length() - 1; i >= 0; --i) {
|
||||
if (mPendingActions[i].mAction->MatchesCacheId(aCacheId)) {
|
||||
mPendingActions.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
|
||||
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
|
||||
if (runnable->MatchesCacheId(aCacheId)) {
|
||||
runnable->Cancel();
|
||||
|
||||
// Cancel activities and let them remove themselves
|
||||
ActivityList::ForwardIterator iter(mActivityList);
|
||||
while (iter.HasMore()) {
|
||||
Activity* activity = iter.GetNext();
|
||||
if (activity->MatchesCacheId(aCacheId)) {
|
||||
activity->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -563,14 +778,8 @@ Context::~Context()
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(mManager);
|
||||
|
||||
// Unlock the quota dir as we go out of scope.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new QuotaReleaseRunnable(mQuotaInfo, NS_LITERAL_CSTRING("Cache"));
|
||||
nsresult rv = NS_DispatchToMainThread(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Crash
|
||||
// for this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch QuotaReleaseRunnable to main thread.");
|
||||
if (mThreadsafeHandle) {
|
||||
mThreadsafeHandle->ContextDestroyed(this);
|
||||
}
|
||||
|
||||
mManager->RemoveContext(this);
|
||||
@ -589,21 +798,28 @@ Context::DispatchAction(nsIEventTarget* aTarget, Action* aAction)
|
||||
// for this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
|
||||
}
|
||||
mActionRunnables.AppendElement(runnable);
|
||||
AddActivity(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo)
|
||||
Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
||||
nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
|
||||
mQuotaInfo = aQuotaInfo;
|
||||
|
||||
// Always save the offline storage to ensure QuotaManager does not shutdown
|
||||
// before the Context has gone away.
|
||||
MOZ_ASSERT(!mOfflineStorage);
|
||||
mOfflineStorage = aOfflineStorage;
|
||||
|
||||
if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) {
|
||||
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
||||
mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
|
||||
}
|
||||
mPendingActions.Clear();
|
||||
mThreadsafeHandle->AllowToClose();
|
||||
// Context will destruct after return here and last ref is released.
|
||||
return;
|
||||
}
|
||||
@ -618,11 +834,32 @@ Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo)
|
||||
}
|
||||
|
||||
void
|
||||
Context::OnActionRunnableComplete(ActionRunnable* aActionRunnable)
|
||||
Context::AddActivity(Activity* aActivity)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(aActionRunnable);
|
||||
MOZ_ALWAYS_TRUE(mActionRunnables.RemoveElement(aActionRunnable));
|
||||
MOZ_ASSERT(aActivity);
|
||||
MOZ_ASSERT(!mActivityList.Contains(aActivity));
|
||||
mActivityList.AppendElement(aActivity);
|
||||
}
|
||||
|
||||
void
|
||||
Context::RemoveActivity(Activity* aActivity)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(aActivity);
|
||||
MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(aActivity));
|
||||
MOZ_ASSERT(!mActivityList.Contains(aActivity));
|
||||
}
|
||||
|
||||
already_AddRefed<Context::ThreadsafeHandle>
|
||||
Context::CreateThreadsafeHandle()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
if (!mThreadsafeHandle) {
|
||||
mThreadsafeHandle = new ThreadsafeHandle(this);
|
||||
}
|
||||
nsRefPtr<ThreadsafeHandle> ref = mThreadsafeHandle;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
106
dom/cache/Context.h
vendored
106
dom/cache/Context.h
vendored
@ -11,10 +11,13 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
class nsIEventTarget;
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -22,6 +25,7 @@ namespace cache {
|
||||
|
||||
class Action;
|
||||
class Manager;
|
||||
class OfflineStorage;
|
||||
|
||||
// The Context class is RAII-style class for managing IO operations within the
|
||||
// Cache.
|
||||
@ -31,8 +35,20 @@ class Manager;
|
||||
// delayed until this initialization is complete. They are then allow to
|
||||
// execute on any specified thread. Once all references to the Context are
|
||||
// gone, then the steps necessary to release the QuotaManager are performed.
|
||||
// Since pending Action objects reference the Context, this allows overlapping
|
||||
// IO to opportunistically run without re-initializing the QuotaManager again.
|
||||
// After initialization the Context holds a self reference, so it will stay
|
||||
// alive until one of three conditions occur:
|
||||
//
|
||||
// 1) The Manager will call Context::AllowToClose() when all of the actors
|
||||
// have removed themselves as listener. This means an idle context with
|
||||
// no active DOM objects will close gracefully.
|
||||
// 2) The QuotaManager invalidates the storage area so it can delete the
|
||||
// files. In this case the OfflineStorage calls Cache::Invalidate() which
|
||||
// in turn cancels all existing Action objects and then marks the Manager
|
||||
// as invalid.
|
||||
// 3) Browser shutdown occurs and the Manager calls Context::CancelAll().
|
||||
//
|
||||
// In either case, though, the Action objects must be destroyed first to
|
||||
// allow the Context to be destroyed.
|
||||
//
|
||||
// While the Context performs operations asynchronously on threads, all of
|
||||
// methods in its public interface must be called on the same thread
|
||||
@ -44,6 +60,56 @@ class Manager;
|
||||
class Context final
|
||||
{
|
||||
public:
|
||||
// Define a class allowing other threads to hold the Context alive. This also
|
||||
// allows these other threads to safely close or cancel the Context.
|
||||
class ThreadsafeHandle final
|
||||
{
|
||||
friend class Context;
|
||||
public:
|
||||
void AllowToClose();
|
||||
void InvalidateAndAllowToClose();
|
||||
private:
|
||||
explicit ThreadsafeHandle(Context* aContext);
|
||||
~ThreadsafeHandle();
|
||||
|
||||
// disallow copying
|
||||
ThreadsafeHandle(const ThreadsafeHandle&) = delete;
|
||||
ThreadsafeHandle& operator=(const ThreadsafeHandle&) = delete;
|
||||
|
||||
void AllowToCloseOnOwningThread();
|
||||
void InvalidateAndAllowToCloseOnOwningThread();
|
||||
|
||||
void ContextDestroyed(Context* aContext);
|
||||
|
||||
// Cleared to allow the Context to close. Only safe to access on
|
||||
// owning thread.
|
||||
nsRefPtr<Context> mStrongRef;
|
||||
|
||||
// Used to support cancelation even while the Context is already allowed
|
||||
// to close. Cleared by ~Context() calling ContextDestroyed(). Only
|
||||
// safe to access on owning thread.
|
||||
Context* mWeakRef;
|
||||
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::Context::ThreadsafeHandle)
|
||||
};
|
||||
|
||||
// Different objects hold references to the Context while some work is being
|
||||
// performed asynchronously. These objects must implement the Activity
|
||||
// interface and register themselves with the AddActivity(). When they are
|
||||
// destroyed they must call RemoveActivity(). This allows the Context to
|
||||
// cancel any outstanding Activity work when the Context is cancelled.
|
||||
class Activity
|
||||
{
|
||||
public:
|
||||
virtual void Cancel() = 0;
|
||||
virtual bool MatchesCacheId(CacheId aCacheId) const = 0;
|
||||
};
|
||||
|
||||
// Create a Context attached to the given Manager. The given Action
|
||||
// will run on the QuotaManager IO thread. Note, this Action must
|
||||
// be execute synchronously.
|
||||
static already_AddRefed<Context>
|
||||
Create(Manager* aManager, Action* aQuotaIOThreadAction);
|
||||
|
||||
@ -60,12 +126,28 @@ public:
|
||||
// Only callable from the thread that created the Context.
|
||||
void CancelAll();
|
||||
|
||||
// Like CancelAll(), but also marks the Manager as "invalid".
|
||||
void Invalidate();
|
||||
|
||||
// Remove any self references and allow the Context to be released when
|
||||
// there are no more Actions to process.
|
||||
void AllowToClose();
|
||||
|
||||
// Cancel any Actions running or waiting to run that operate on the given
|
||||
// cache ID.
|
||||
//
|
||||
// Only callable from the thread that created the Context.
|
||||
void CancelForCacheId(CacheId aCacheId);
|
||||
|
||||
void AddActivity(Activity* aActivity);
|
||||
void RemoveActivity(Activity* aActivity);
|
||||
|
||||
const QuotaInfo&
|
||||
GetQuotaInfo() const
|
||||
{
|
||||
return mQuotaInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
class QuotaInitRunnable;
|
||||
class ActionRunnable;
|
||||
@ -86,16 +168,28 @@ private:
|
||||
explicit Context(Manager* aManager);
|
||||
~Context();
|
||||
void DispatchAction(nsIEventTarget* aTarget, Action* aAction);
|
||||
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo);
|
||||
void OnActionRunnableComplete(ActionRunnable* const aAction);
|
||||
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
||||
nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage);
|
||||
|
||||
already_AddRefed<ThreadsafeHandle>
|
||||
CreateThreadsafeHandle();
|
||||
|
||||
nsRefPtr<Manager> mManager;
|
||||
State mState;
|
||||
QuotaInfo mQuotaInfo;
|
||||
nsTArray<PendingAction> mPendingActions;
|
||||
|
||||
// weak refs since ~ActionRunnable() removes itself from this list
|
||||
nsTArray<ActionRunnable*> mActionRunnables;
|
||||
// Weak refs since activites must remove themselves from this list before
|
||||
// being destroyed by calling RemoveActivity().
|
||||
typedef nsTObserverArray<Activity*> ActivityList;
|
||||
ActivityList mActivityList;
|
||||
|
||||
// The ThreadsafeHandle may have a strong ref back to us. This creates
|
||||
// a ref-cycle that keeps the Context alive. The ref-cycle is broken
|
||||
// when ThreadsafeHandle::AllowToClose() is called.
|
||||
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
|
||||
nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(cache::Context)
|
||||
|
41
dom/cache/DBSchema.cpp
vendored
41
dom/cache/DBSchema.cpp
vendored
@ -22,8 +22,8 @@ namespace dom {
|
||||
namespace cache {
|
||||
|
||||
|
||||
const int32_t DBSchema::kMaxWipeSchemaVersion = 3;
|
||||
const int32_t DBSchema::kLatestSchemaVersion = 3;
|
||||
const int32_t DBSchema::kMaxWipeSchemaVersion = 4;
|
||||
const int32_t DBSchema::kLatestSchemaVersion = 4;
|
||||
const int32_t DBSchema::kMaxEntriesPerStatement = 255;
|
||||
|
||||
using mozilla::void_t;
|
||||
@ -89,6 +89,7 @@ DBSchema::CreateSchema(mozIStorageConnection* aConn)
|
||||
"request_headers_guard INTEGER NOT NULL, "
|
||||
"request_mode INTEGER NOT NULL, "
|
||||
"request_credentials INTEGER NOT NULL, "
|
||||
"request_cache INTEGER NOT NULL, "
|
||||
"request_body_id TEXT NULL, "
|
||||
"response_type INTEGER NOT NULL, "
|
||||
"response_url TEXT NOT NULL, "
|
||||
@ -970,6 +971,7 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
|
||||
"request_headers_guard, "
|
||||
"request_mode, "
|
||||
"request_credentials, "
|
||||
"request_cache, "
|
||||
"request_body_id, "
|
||||
"response_type, "
|
||||
"response_url, "
|
||||
@ -979,7 +981,7 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
|
||||
"response_body_id, "
|
||||
"response_security_info, "
|
||||
"cache_id "
|
||||
") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)"
|
||||
") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17)"
|
||||
), getter_AddRefs(state));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
@ -1006,34 +1008,38 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
|
||||
static_cast<int32_t>(aRequest.credentials()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = BindId(state, 7, aRequestBodyId);
|
||||
rv = state->BindInt32Parameter(7,
|
||||
static_cast<int32_t>(aRequest.requestCache()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindInt32Parameter(8, static_cast<int32_t>(aResponse.type()));
|
||||
rv = BindId(state, 8, aRequestBodyId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindStringParameter(9, aResponse.url());
|
||||
rv = state->BindInt32Parameter(9, static_cast<int32_t>(aResponse.type()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindInt32Parameter(10, aResponse.status());
|
||||
rv = state->BindStringParameter(10, aResponse.url());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindUTF8StringParameter(11, aResponse.statusText());
|
||||
rv = state->BindInt32Parameter(11, aResponse.status());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindInt32Parameter(12,
|
||||
rv = state->BindUTF8StringParameter(12, aResponse.statusText());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindInt32Parameter(13,
|
||||
static_cast<int32_t>(aResponse.headersGuard()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = BindId(state, 13, aResponseBodyId);
|
||||
rv = BindId(state, 14, aResponseBodyId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindBlobParameter(14, reinterpret_cast<const uint8_t*>
|
||||
rv = state->BindBlobParameter(15, reinterpret_cast<const uint8_t*>
|
||||
(aResponse.securityInfo().get()),
|
||||
aResponse.securityInfo().Length());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->BindInt32Parameter(15, aCacheId);
|
||||
rv = state->BindInt32Parameter(16, aCacheId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = state->Execute();
|
||||
@ -1219,6 +1225,7 @@ DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
|
||||
"request_headers_guard, "
|
||||
"request_mode, "
|
||||
"request_credentials, "
|
||||
"request_cache, "
|
||||
"request_body_id "
|
||||
"FROM entries "
|
||||
"WHERE id=?1;"
|
||||
@ -1261,13 +1268,19 @@ DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
|
||||
aSavedRequestOut->mValue.credentials() =
|
||||
static_cast<RequestCredentials>(credentials);
|
||||
|
||||
int32_t requestCache;
|
||||
rv = state->GetInt32(7, &requestCache);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
aSavedRequestOut->mValue.requestCache() =
|
||||
static_cast<RequestCache>(requestCache);
|
||||
|
||||
bool nullBody = false;
|
||||
rv = state->GetIsNull(7, &nullBody);
|
||||
rv = state->GetIsNull(8, &nullBody);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
aSavedRequestOut->mHasBodyId = !nullBody;
|
||||
|
||||
if (aSavedRequestOut->mHasBodyId) {
|
||||
rv = ExtractId(state, 7, &aSavedRequestOut->mBodyId);
|
||||
rv = ExtractId(state, 8, &aSavedRequestOut->mBodyId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
}
|
||||
|
||||
|
85
dom/cache/Manager.cpp
vendored
85
dom/cache/Manager.cpp
vendored
@ -177,7 +177,10 @@ public:
|
||||
ManagerList::ForwardIterator iter(sFactory->mManagerList);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<Manager> manager = iter.GetNext();
|
||||
if (*manager->mManagerId == *aManagerId) {
|
||||
// If there is an invalid Manager finishing up and a new Manager
|
||||
// is created for the same origin, then the new Manager will
|
||||
// be blocked until QuotaManager finishes clearing the origin.
|
||||
if (manager->IsValid() && *manager->mManagerId == *aManagerId) {
|
||||
return manager.forget();
|
||||
}
|
||||
}
|
||||
@ -911,15 +914,19 @@ private:
|
||||
{
|
||||
// May be on any thread, including STS event target.
|
||||
MOZ_ASSERT(aClosure);
|
||||
nsRefPtr<CachePutAllAction> action = static_cast<CachePutAllAction*>(aClosure);
|
||||
// Weak ref as we are guaranteed to the action is alive until
|
||||
// CompleteOnInitiatingThread is called.
|
||||
CachePutAllAction* action = static_cast<CachePutAllAction*>(aClosure);
|
||||
action->CallOnAsyncCopyCompleteOnTargetThread(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
|
||||
{
|
||||
// May be on any thread, including STS event target.
|
||||
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArg<nsresult>(
|
||||
// May be on any thread, including STS event target. Non-owning runnable
|
||||
// here since we are guaranteed the Action will survive until
|
||||
// CompleteOnInitiatingThread is called.
|
||||
nsCOMPtr<nsIRunnable> runnable = NS_NewNonOwningRunnableMethodWithArgs<nsresult>(
|
||||
this, &CachePutAllAction::OnAsyncCopyComplete, aRv);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mTargetThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
@ -1404,6 +1411,9 @@ Manager::RemoveListener(Listener* aListener)
|
||||
mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
|
||||
MOZ_ASSERT(!mListeners.Contains(aListener,
|
||||
ListenerEntryListenerComparator()));
|
||||
if (mListeners.IsEmpty() && mContext) {
|
||||
mContext->AllowToClose();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1421,6 +1431,21 @@ Manager::RemoveContext(Context* aContext)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Manager::Invalidate()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
// QuotaManager can trigger this more than once.
|
||||
mValid = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Manager::IsValid() const
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
return mValid;
|
||||
}
|
||||
|
||||
void
|
||||
Manager::AddRefCacheId(CacheId aCacheId)
|
||||
{
|
||||
@ -1450,7 +1475,7 @@ Manager::ReleaseCacheId(CacheId aCacheId)
|
||||
bool orphaned = mCacheIdRefs[i].mOrphaned;
|
||||
mCacheIdRefs.RemoveElementAt(i);
|
||||
// TODO: note that we need to check this cache for staleness on startup (bug 1110446)
|
||||
if (orphaned && !mShuttingDown) {
|
||||
if (orphaned && !mShuttingDown && mValid) {
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
context->CancelForCacheId(aCacheId);
|
||||
nsRefPtr<Action> action = new DeleteOrphanedCacheAction(this,
|
||||
@ -1493,7 +1518,7 @@ Manager::ReleaseBodyId(const nsID& aBodyId)
|
||||
bool orphaned = mBodyIdRefs[i].mOrphaned;
|
||||
mBodyIdRefs.RemoveElementAt(i);
|
||||
// TODO: note that we need to check this body for staleness on startup (bug 1110446)
|
||||
if (orphaned && !mShuttingDown) {
|
||||
if (orphaned && !mShuttingDown && mValid) {
|
||||
nsRefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
context->Dispatch(mIOThread, action);
|
||||
@ -1535,9 +1560,8 @@ Manager::CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
nullptr, nullptr);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheMatch(aRequestId, NS_ERROR_FAILURE, nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
@ -1556,8 +1580,8 @@ Manager::CacheMatchAll(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<SavedResponse>(), nullptr);
|
||||
return;
|
||||
}
|
||||
@ -1578,8 +1602,8 @@ Manager::CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCachePutAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCachePutAll(aRequestId, NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
@ -1598,8 +1622,8 @@ Manager::CacheDelete(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, false);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheDelete(aRequestId, NS_ERROR_FAILURE, false);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
@ -1616,8 +1640,8 @@ Manager::CacheKeys(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheKeys(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<SavedRequest>(), nullptr);
|
||||
return;
|
||||
}
|
||||
@ -1637,8 +1661,8 @@ Manager::StorageMatch(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageMatch(aRequestId, NS_ERROR_FAILURE,
|
||||
nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
@ -1657,8 +1681,8 @@ Manager::StorageHas(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageHas(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageHas(aRequestId, NS_ERROR_FAILURE,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
@ -1675,8 +1699,8 @@ Manager::StorageOpen(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageOpen(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, 0);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageOpen(aRequestId, NS_ERROR_FAILURE, 0);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
@ -1692,8 +1716,8 @@ Manager::StorageDelete(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageDelete(aRequestId, NS_ERROR_FAILURE,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
@ -1710,8 +1734,8 @@ Manager::StorageKeys(Listener* aListener, RequestId aRequestId,
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageKeys(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<nsString>());
|
||||
return;
|
||||
}
|
||||
@ -1727,6 +1751,7 @@ Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
|
||||
, mIOThread(aIOThread)
|
||||
, mContext(nullptr)
|
||||
, mShuttingDown(false)
|
||||
, mValid(true)
|
||||
{
|
||||
MOZ_ASSERT(mManagerId);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
@ -1765,11 +1790,6 @@ Manager::Shutdown()
|
||||
// complete before shutdown proceeds.
|
||||
mShuttingDown = true;
|
||||
|
||||
for (uint32_t i = 0; i < mStreamLists.Length(); ++i) {
|
||||
nsRefPtr<StreamList> streamList = mStreamLists[i];
|
||||
streamList->CloseAll();
|
||||
}
|
||||
|
||||
// If there is a context, then we must wait for it to complete. Cancel and
|
||||
// only note that we are done after its cleaned up.
|
||||
if (mContext) {
|
||||
@ -1789,6 +1809,7 @@ Manager::CurrentContext()
|
||||
nsRefPtr<Context> ref = mContext;
|
||||
if (!ref) {
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
MOZ_ASSERT(mValid);
|
||||
nsRefPtr<Action> setupAction = new SetupAction();
|
||||
ref = Context::Create(this, setupAction);
|
||||
mContext = ref;
|
||||
|
10
dom/cache/Manager.h
vendored
10
dom/cache/Manager.h
vendored
@ -125,6 +125,11 @@ public:
|
||||
// Must be called by Context objects before they are destroyed.
|
||||
void RemoveContext(Context* aContext);
|
||||
|
||||
// Marks the Manager "invalid". Once the Context completes no new operations
|
||||
// will be permitted with this Manager. New actors will get a new Manager.
|
||||
void Invalidate();
|
||||
bool IsValid() const;
|
||||
|
||||
// If an actor represents a long term reference to a cache or body stream,
|
||||
// then they must call AddRefCacheId() or AddRefBodyId(). This will
|
||||
// cause the Manager to keep the backing data store alive for the given
|
||||
@ -214,8 +219,8 @@ private:
|
||||
struct ListenerEntry
|
||||
{
|
||||
ListenerEntry()
|
||||
: mId(UINT64_MAX),
|
||||
mListener(nullptr)
|
||||
: mId(UINT64_MAX)
|
||||
, mListener(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -255,6 +260,7 @@ private:
|
||||
nsTArray<StreamList*> mStreamLists;
|
||||
|
||||
bool mShuttingDown;
|
||||
bool mValid;
|
||||
|
||||
struct CacheIdRefCounter
|
||||
{
|
||||
|
135
dom/cache/OfflineStorage.cpp
vendored
Normal file
135
dom/cache/OfflineStorage.cpp
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
/* -*- 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 "mozilla/dom/cache/OfflineStorage.h"
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "mozilla/dom/cache/QuotaClient.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
|
||||
NS_IMPL_ISUPPORTS(OfflineStorage, nsIOfflineStorage);
|
||||
|
||||
// static
|
||||
already_AddRefed<OfflineStorage>
|
||||
OfflineStorage::Register(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
if (NS_WARN_IF(!qm)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Client> client = qm->GetClient(Client::DOMCACHE);
|
||||
|
||||
nsRefPtr<OfflineStorage> storage =
|
||||
new OfflineStorage(aContext, aQuotaInfo, client);
|
||||
|
||||
if (NS_WARN_IF(!qm->RegisterStorage(storage))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
void
|
||||
OfflineStorage::AddDestroyCallback(nsIRunnable* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(!mDestroyCallbacks.Contains(aCallback));
|
||||
mDestroyCallbacks.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
OfflineStorage::OfflineStorage(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo,
|
||||
Client* aClient)
|
||||
: mContext(aContext)
|
||||
, mQuotaInfo(aQuotaInfo)
|
||||
, mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mClient);
|
||||
|
||||
mPersistenceType = PERSISTENCE_TYPE_DEFAULT;
|
||||
mGroup = mQuotaInfo.mGroup;
|
||||
}
|
||||
|
||||
OfflineStorage::~OfflineStorage()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
MOZ_ASSERT(qm);
|
||||
qm->UnregisterStorage(this);
|
||||
for (uint32_t i = 0; i < mDestroyCallbacks.Length(); ++i) {
|
||||
mDestroyCallbacks[i]->Run();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(const nsACString&)
|
||||
OfflineStorage::Id()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mQuotaInfo.mStorageId;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(Client*)
|
||||
OfflineStorage::GetClient()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mClient;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
OfflineStorage::IsOwnedByProcess(ContentParent* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// The Cache and Context can be shared by multiple client processes. They
|
||||
// are not exclusively owned by a single process.
|
||||
//
|
||||
// As far as I can tell this is used by QuotaManager to shutdown storages
|
||||
// when a particular process goes away. We definitely don't want this
|
||||
// since we are shared. Also, the Cache actor code already properly
|
||||
// handles asynchronous actor destruction when the child process dies.
|
||||
//
|
||||
// Therefore, always return false here.
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(const nsACString&)
|
||||
OfflineStorage::Origin()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mQuotaInfo.mOrigin;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsresult)
|
||||
OfflineStorage::Close()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mContext->AllowToClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
OfflineStorage::Invalidate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mContext->InvalidateAndAllowToClose();
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
50
dom/cache/OfflineStorage.h
vendored
Normal file
50
dom/cache/OfflineStorage.h
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_cache_QuotaOfflineStorage_h
|
||||
#define mozilla_dom_cache_QuotaOfflineStorage_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "nsIOfflineStorage.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
class OfflineStorage final : public nsIOfflineStorage
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<OfflineStorage>
|
||||
Register(Context::ThreadsafeHandle* aContext, const QuotaInfo& aQuotaInfo);
|
||||
|
||||
void
|
||||
AddDestroyCallback(nsIRunnable* aCallback);
|
||||
|
||||
private:
|
||||
OfflineStorage(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo,
|
||||
Client* aClient);
|
||||
~OfflineStorage();
|
||||
|
||||
nsRefPtr<Context::ThreadsafeHandle> mContext;
|
||||
const QuotaInfo mQuotaInfo;
|
||||
nsRefPtr<Client> mClient;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mDestroyCallbacks;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOFFLINESTORAGE
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_cache_QuotaOfflineStorage_h
|
2
dom/cache/PCacheTypes.ipdlh
vendored
2
dom/cache/PCacheTypes.ipdlh
vendored
@ -10,6 +10,7 @@ include InputStreamParams;
|
||||
using HeadersGuardEnum from "mozilla/dom/FetchIPCUtils.h";
|
||||
using RequestCredentials from "mozilla/dom/FetchIPCUtils.h";
|
||||
using RequestMode from "mozilla/dom/FetchIPCUtils.h";
|
||||
using RequestCache from "mozilla/dom/FetchIPCUtils.h";
|
||||
using mozilla::dom::ResponseType from "mozilla/dom/FetchIPCUtils.h";
|
||||
using mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
using struct nsID from "nsID.h";
|
||||
@ -54,6 +55,7 @@ struct PCacheRequest
|
||||
RequestCredentials credentials;
|
||||
PCacheReadStreamOrVoid body;
|
||||
uint32_t context;
|
||||
RequestCache requestCache;
|
||||
};
|
||||
|
||||
union PCacheRequestOrVoid
|
||||
|
65
dom/cache/QuotaClient.cpp
vendored
65
dom/cache/QuotaClient.cpp
vendored
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
#include "mozilla/dom/cache/OfflineStorage.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/UsageInfo.h"
|
||||
#include "nsIFile.h"
|
||||
@ -18,6 +19,7 @@ namespace {
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::dom::cache::Manager;
|
||||
using mozilla::dom::cache::OfflineStorage;
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
@ -60,6 +62,41 @@ GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class StoragesDestroyedRunnable final : public nsRunnable
|
||||
{
|
||||
uint32_t mExpectedCalls;
|
||||
nsCOMPtr<nsIRunnable> mCallback;
|
||||
|
||||
public:
|
||||
StoragesDestroyedRunnable(uint32_t aExpectedCalls, nsIRunnable* aCallback)
|
||||
: mExpectedCalls(aExpectedCalls)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mExpectedCalls);
|
||||
MOZ_ASSERT(mCallback);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mExpectedCalls);
|
||||
mExpectedCalls -= 1;
|
||||
if (!mExpectedCalls) {
|
||||
mCallback->Run();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~StoragesDestroyedRunnable()
|
||||
{
|
||||
// This is a callback runnable and not used for thread dispatch. It should
|
||||
// always be destroyed on the main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
};
|
||||
|
||||
class CacheQuotaClient final : public Client
|
||||
{
|
||||
public:
|
||||
@ -151,13 +188,14 @@ public:
|
||||
OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const nsACString& aOrigin) override
|
||||
{
|
||||
// nothing to do
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
virtual void
|
||||
ReleaseIOThreadObjects() override
|
||||
{
|
||||
// nothing to do
|
||||
// Nothing to do here as the Context handles cleaning everything up
|
||||
// automatically.
|
||||
}
|
||||
|
||||
virtual bool
|
||||
@ -169,15 +207,26 @@ public:
|
||||
virtual bool
|
||||
IsTransactionServiceActivated() override
|
||||
{
|
||||
// TODO: implement nsIOfflineStorage interface (bug 1110487)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
|
||||
nsIRunnable* aCallback) override
|
||||
{
|
||||
// TODO: implement nsIOfflineStorage interface (bug 1110487)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aStorages.IsEmpty());
|
||||
|
||||
nsCOMPtr<nsIRunnable> callback =
|
||||
new StoragesDestroyedRunnable(aStorages.Length(), aCallback);
|
||||
|
||||
for (uint32_t i = 0; i < aStorages.Length(); ++i) {
|
||||
MOZ_ASSERT(aStorages[i]->GetClient());
|
||||
MOZ_ASSERT(aStorages[i]->GetClient()->GetType() == Client::DOMCACHE);
|
||||
nsRefPtr<OfflineStorage> storage =
|
||||
static_cast<OfflineStorage*>(aStorages[i]);
|
||||
storage->AddDestroyCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -191,9 +240,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
~CacheQuotaClient() { }
|
||||
~CacheQuotaClient()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient, override)
|
||||
};
|
||||
|
||||
|
3
dom/cache/QuotaClient.h
vendored
3
dom/cache/QuotaClient.h
vendored
@ -14,7 +14,8 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
already_AddRefed<quota::Client> CreateQuotaClient();
|
||||
already_AddRefed<quota::Client>
|
||||
CreateQuotaClient();
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
|
17
dom/cache/StreamList.cpp
vendored
17
dom/cache/StreamList.cpp
vendored
@ -23,7 +23,7 @@ StreamList::StreamList(Manager* aManager, Context* aContext)
|
||||
, mActivated(false)
|
||||
{
|
||||
MOZ_ASSERT(mManager);
|
||||
MOZ_ASSERT(mContext);
|
||||
mContext->AddActivity(this);
|
||||
}
|
||||
|
||||
void
|
||||
@ -142,6 +142,20 @@ StreamList::CloseAll()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamList::Cancel()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
CloseAll();
|
||||
}
|
||||
|
||||
bool
|
||||
StreamList::MatchesCacheId(CacheId aCacheId) const
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
return aCacheId == mCacheId;
|
||||
}
|
||||
|
||||
StreamList::~StreamList()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
@ -153,6 +167,7 @@ StreamList::~StreamList()
|
||||
}
|
||||
mManager->ReleaseCacheId(mCacheId);
|
||||
}
|
||||
mContext->RemoveActivity(this);
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
8
dom/cache/StreamList.h
vendored
8
dom/cache/StreamList.h
vendored
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_cache_StreamList_h
|
||||
#define mozilla_dom_cache_StreamList_h
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
@ -18,10 +19,9 @@ namespace dom {
|
||||
namespace cache {
|
||||
|
||||
class CacheStreamControlParent;
|
||||
class Context;
|
||||
class Manager;
|
||||
|
||||
class StreamList final
|
||||
class StreamList final : public Context::Activity
|
||||
{
|
||||
public:
|
||||
StreamList(Manager* aManager, Context* aContext);
|
||||
@ -39,6 +39,10 @@ public:
|
||||
void Close(const nsID& aId);
|
||||
void CloseAll();
|
||||
|
||||
// Context::Activity methods
|
||||
virtual void Cancel() override;
|
||||
virtual bool MatchesCacheId(CacheId aCacheId) const override;
|
||||
|
||||
private:
|
||||
~StreamList();
|
||||
struct Entry
|
||||
|
2
dom/cache/TypeUtils.cpp
vendored
2
dom/cache/TypeUtils.cpp
vendored
@ -225,6 +225,7 @@ TypeUtils::ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
|
||||
aOut.mode() = aIn->Mode();
|
||||
aOut.credentials() = aIn->GetCredentialsMode();
|
||||
aOut.context() = aIn->ContentPolicyType();
|
||||
aOut.requestCache() = aIn->GetCacheMode();
|
||||
|
||||
if (aBodyAction == IgnoreBody) {
|
||||
aOut.body() = void_t();
|
||||
@ -367,6 +368,7 @@ TypeUtils::ToInternalRequest(const PCacheRequest& aIn)
|
||||
internalRequest->SetMode(aIn.mode());
|
||||
internalRequest->SetCredentialsMode(aIn.credentials());
|
||||
internalRequest->SetContentPolicyType(aIn.context());
|
||||
internalRequest->SetCacheMode(aIn.requestCache());
|
||||
|
||||
nsRefPtr<InternalHeaders> internalHeaders =
|
||||
new InternalHeaders(aIn.headers(), aIn.headersGuard());
|
||||
|
1
dom/cache/Types.h
vendored
1
dom/cache/Types.h
vendored
@ -34,6 +34,7 @@ struct QuotaInfo
|
||||
nsCOMPtr<nsIFile> mDir;
|
||||
nsCString mGroup;
|
||||
nsCString mOrigin;
|
||||
nsCString mStorageId;
|
||||
bool mIsApp;
|
||||
};
|
||||
|
||||
|
2
dom/cache/moz.build
vendored
2
dom/cache/moz.build
vendored
@ -28,6 +28,7 @@ EXPORTS.mozilla.dom.cache += [
|
||||
'IPCUtils.h',
|
||||
'Manager.h',
|
||||
'ManagerId.h',
|
||||
'OfflineStorage.h',
|
||||
'PrincipalVerifier.h',
|
||||
'QuotaClient.h',
|
||||
'ReadStream.h',
|
||||
@ -61,6 +62,7 @@ UNIFIED_SOURCES += [
|
||||
'FileUtils.cpp',
|
||||
'Manager.cpp',
|
||||
'ManagerId.cpp',
|
||||
'OfflineStorage.cpp',
|
||||
'PrincipalVerifier.cpp',
|
||||
'QuotaClient.cpp',
|
||||
'ReadStream.cpp',
|
||||
|
20
dom/cache/test/mochitest/driver.js
vendored
20
dom/cache/test/mochitest/driver.js
vendored
@ -30,6 +30,22 @@ function runTests(testFile, order) {
|
||||
});
|
||||
}
|
||||
|
||||
// adapted from dom/indexedDB/test/helpers.js
|
||||
function clearStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
var appId, inBrowser;
|
||||
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
|
||||
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
|
||||
principal.appId != nsIPrincipal.NO_APP_ID) {
|
||||
appId = principal.appId;
|
||||
inBrowser = principal.isInBrowserElement;
|
||||
}
|
||||
SpecialPowers.clearStorageForURI(document.documentURI, resolve, appId,
|
||||
inBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
function loadScript(script) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var s = document.createElement("script");
|
||||
@ -100,8 +116,11 @@ function runTests(testFile, order) {
|
||||
return setupPrefs()
|
||||
.then(importDrivers)
|
||||
.then(runWorkerTest)
|
||||
.then(clearStorage)
|
||||
.then(runServiceWorkerTest)
|
||||
.then(clearStorage)
|
||||
.then(runFrameTest)
|
||||
.then(clearStorage)
|
||||
.catch(function(e) {
|
||||
ok(false, "A promise was rejected during test execution: " + e);
|
||||
});
|
||||
@ -109,6 +128,7 @@ function runTests(testFile, order) {
|
||||
return setupPrefs()
|
||||
.then(importDrivers)
|
||||
.then(() => Promise.all([runWorkerTest(), runServiceWorkerTest(), runFrameTest()]))
|
||||
.then(clearStorage)
|
||||
.catch(function(e) {
|
||||
ok(false, "A promise was rejected during test execution: " + e);
|
||||
});
|
||||
|
2
dom/cache/test/mochitest/mochitest.ini
vendored
2
dom/cache/test/mochitest/mochitest.ini
vendored
@ -17,6 +17,7 @@ support-files =
|
||||
test_caches.js
|
||||
test_cache_keys.js
|
||||
test_cache_put.js
|
||||
test_cache_requestCache.js
|
||||
|
||||
[test_cache.html]
|
||||
[test_cache_add.html]
|
||||
@ -27,3 +28,4 @@ support-files =
|
||||
[test_caches.html]
|
||||
[test_cache_keys.html]
|
||||
[test_cache_put.html]
|
||||
[test_cache_requestCache.html]
|
||||
|
2
dom/cache/test/mochitest/test_cache_keys.js
vendored
2
dom/cache/test/mochitest/test_cache_keys.js
vendored
@ -57,8 +57,6 @@ caches.open(name).then(function(cache) {
|
||||
}).then(function(keys) {
|
||||
is(keys.length, 1, "One match should be found");
|
||||
ok(keys[0].url.indexOf(tests[0]) >= 0, "Valid URL");
|
||||
// TODO: Add tests for ignoreVary
|
||||
|
||||
// Make sure cacheName is ignored.
|
||||
return c.keys(tests[0], {cacheName: "non-existing-cache"});
|
||||
}).then(function(keys) {
|
||||
|
@ -79,6 +79,33 @@ function testBasics() {
|
||||
});
|
||||
}
|
||||
|
||||
function testBasicKeys() {
|
||||
function checkRequest(reqs) {
|
||||
is(reqs.length, 1, "One request expected");
|
||||
ok(reqs[0].url.indexOf(requestURL) >= 0, "The correct request expected");
|
||||
ok(reqs[0].headers.get("WhatToVary"), "Cookie", "The correct request headers expected");
|
||||
}
|
||||
var test;
|
||||
return setupTest({"WhatToVary": "Cookie"})
|
||||
.then(function(t) {
|
||||
test = t;
|
||||
// Ensure that searching without specifying a Cookie header succeeds.
|
||||
return test.cache.keys(requestURL);
|
||||
}).then(function(r) {
|
||||
return checkRequest(r);
|
||||
}).then(function() {
|
||||
// Ensure that searching with a non-matching value for the Cookie header fails.
|
||||
return test.cache.keys(new Request(requestURL, {headers: {"Cookie": "foo=bar"}}));
|
||||
}).then(function(r) {
|
||||
is(r.length, 0, "Searching for a request with an unknown Vary header should not succeed");
|
||||
// Ensure that searching with a non-matching value for the Cookie header but with ignoreVary set succeeds.
|
||||
return test.cache.keys(new Request(requestURL, {headers: {"Cookie": "foo=bar"}}),
|
||||
{ignoreVary: true});
|
||||
}).then(function(r) {
|
||||
return checkRequest(r);
|
||||
});
|
||||
}
|
||||
|
||||
function testStar() {
|
||||
var test;
|
||||
return setupTest({"WhatToVary": "*", "Cookie": "foo=bar"})
|
||||
@ -262,6 +289,8 @@ function step(testPromise) {
|
||||
}
|
||||
|
||||
step(testBasics()).then(function() {
|
||||
return step(testBasicKeys());
|
||||
}).then(function() {
|
||||
return step(testStar());
|
||||
}).then(function() {
|
||||
return step(testMatch());
|
||||
|
20
dom/cache/test/mochitest/test_cache_requestCache.html
vendored
Normal file
20
dom/cache/test/mochitest/test_cache_requestCache.html
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Validate the Cache.keys() method</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="driver.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frame"></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
runTests("test_cache_requestCache.js")
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
27
dom/cache/test/mochitest/test_cache_requestCache.js
vendored
Normal file
27
dom/cache/test/mochitest/test_cache_requestCache.js
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
var name = "requestCache" + context;
|
||||
var c;
|
||||
|
||||
var reqWithoutCache = new Request("//mochi.test:8888/?noCache" + context);
|
||||
var reqWithCache = new Request("//mochi.test:8888/?withCache" + context,
|
||||
{cache: "force-cache"});
|
||||
|
||||
// Sanity check
|
||||
is(reqWithoutCache.cache, "default", "Correct default value");
|
||||
is(reqWithCache.cache, "force-cache", "Correct value set by the ctor");
|
||||
|
||||
caches.open(name).then(function(cache) {
|
||||
c = cache;
|
||||
return c.addAll([reqWithoutCache, reqWithCache]);
|
||||
}).then(function() {
|
||||
return c.keys();
|
||||
}).then(function(keys) {
|
||||
is(keys.length, 2, "Correct number of requests");
|
||||
is(keys[0].url, reqWithoutCache.url, "Correct URL");
|
||||
is(keys[0].cache, reqWithoutCache.cache, "Correct cache attribute");
|
||||
is(keys[1].url, reqWithCache.url, "Correct URL");
|
||||
is(keys[1].cache, reqWithCache.cache, "Correct cache attribute");
|
||||
return caches.delete(name);
|
||||
}).then(function(deleted) {
|
||||
ok(deleted, "The cache should be successfully deleted");
|
||||
testDone();
|
||||
});
|
@ -34,7 +34,8 @@ private:
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
|
||||
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
|
||||
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
||||
|
@ -50,3 +50,19 @@ WebGL1Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL1Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
|
||||
{
|
||||
switch (usage) {
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, usage);
|
||||
return false;
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ static const gl::GLFeature kRequiredFeatures[] = {
|
||||
gl::GLFeature::query_objects,
|
||||
gl::GLFeature::renderbuffer_color_float,
|
||||
gl::GLFeature::renderbuffer_color_half_float,
|
||||
gl::GLFeature::sRGB,
|
||||
gl::GLFeature::sRGB_framebuffer,
|
||||
gl::GLFeature::sRGB_texture,
|
||||
gl::GLFeature::sampler_objects,
|
||||
gl::GLFeature::standard_derivatives,
|
||||
gl::GLFeature::texture_3D,
|
||||
@ -120,12 +121,22 @@ WebGLContext::InitWebGL2()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<gl::GLFeature> missingList;
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
|
||||
if (!gl->IsSupported(kRequiredFeatures[i])) {
|
||||
GenerateWarning("WebGL 2 unavailable. Requires feature %s.",
|
||||
gl::GLContext::GetFeatureName(kRequiredFeatures[i]));
|
||||
return false;
|
||||
if (!gl->IsSupported(kRequiredFeatures[i]))
|
||||
missingList.push_back(kRequiredFeatures[i]);
|
||||
}
|
||||
|
||||
if (missingList.size()) {
|
||||
nsAutoCString exts;
|
||||
for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
|
||||
exts.AppendLiteral("\n ");
|
||||
exts.Append(gl::GLContext::GetFeatureName(*itr));
|
||||
}
|
||||
GenerateWarning("WebGL 2 unavailable. The following required features are"
|
||||
" unavailible: %s", exts.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ok WebGL 2 is compatible, we can enable natively supported extensions.
|
||||
|
@ -348,6 +348,7 @@ private:
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
|
||||
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
@ -75,6 +75,28 @@ WebGL2Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
|
||||
{
|
||||
switch (usage) {
|
||||
case LOCAL_GL_DYNAMIC_COPY:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_READ:
|
||||
case LOCAL_GL_STATIC_COPY:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_STATIC_READ:
|
||||
case LOCAL_GL_STREAM_COPY:
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STREAM_READ:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, usage);
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer objects
|
||||
|
||||
|
@ -932,7 +932,6 @@ protected:
|
||||
WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTarget(GLenum target);
|
||||
WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTargetIndexed(GLenum target,
|
||||
GLuint index);
|
||||
bool ValidateBufferUsageEnum(GLenum target, const char* info);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Queries (WebGL2ContextQueries.cpp)
|
||||
@ -1389,6 +1388,7 @@ private:
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
|
||||
virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) = 0;
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) = 0;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
|
||||
|
||||
protected:
|
||||
|
@ -890,6 +890,11 @@ WebGLContext::FramebufferTexture2D(GLenum target,
|
||||
if (!ValidateFramebufferTarget(target, "framebufferTexture2D"))
|
||||
return;
|
||||
|
||||
if (!IsWebGL2() && level != 0) {
|
||||
ErrorInvalidValue("framebufferTexture2D: level must be 0.");
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLFramebuffer* fb;
|
||||
switch (target) {
|
||||
case LOCAL_GL_FRAMEBUFFER:
|
||||
|
@ -34,7 +34,8 @@ WebGLExtensionSRGB::IsSupported(const WebGLContext* webgl)
|
||||
{
|
||||
gl::GLContext* gl = webgl->GL();
|
||||
|
||||
return gl->IsSupported(gl::GLFeature::sRGB);
|
||||
return gl->IsSupported(gl::GLFeature::sRGB_framebuffer) &&
|
||||
gl->IsSupported(gl::GLFeature::sRGB_texture);
|
||||
}
|
||||
|
||||
|
||||
|
@ -499,11 +499,8 @@ WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPoint,
|
||||
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
|
||||
mContext->mBoundReadFramebuffer == this);
|
||||
|
||||
if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
|
||||
tex))
|
||||
{
|
||||
if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", tex))
|
||||
return;
|
||||
}
|
||||
|
||||
if (tex) {
|
||||
bool isTexture2D = tex->Target() == LOCAL_GL_TEXTURE_2D;
|
||||
@ -515,11 +512,6 @@ WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPoint,
|
||||
}
|
||||
}
|
||||
|
||||
if (level != 0) {
|
||||
mContext->ErrorInvalidValue("framebufferTexture2D: Level must be 0.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the requested attachment. If result is NULL, attachment is invalid
|
||||
* and an error is generated.
|
||||
*
|
||||
|
@ -4646,7 +4646,7 @@ EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
|
||||
bool aAddState)
|
||||
{
|
||||
for (; aStartNode && aStartNode != aStopBefore;
|
||||
aStartNode = aStartNode->GetParent()) {
|
||||
aStartNode = aStartNode->GetParentElementCrossingShadowRoot()) {
|
||||
// We might be starting with a non-element (e.g. a text node) and
|
||||
// if someone is doing something weird might be ending with a
|
||||
// non-element too (e.g. a document fragment)
|
||||
@ -4674,7 +4674,7 @@ EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
|
||||
// still be in hover state. To handle this situation we need to
|
||||
// keep walking up the tree and any time we find a label mark its
|
||||
// corresponding node as still in our state.
|
||||
for ( ; aStartNode; aStartNode = aStartNode->GetParent()) {
|
||||
for ( ; aStartNode; aStartNode = aStartNode->GetParentElementCrossingShadowRoot()) {
|
||||
if (!aStartNode->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -141,6 +141,7 @@ support-files = bug1017086_inner.html
|
||||
[test_bug1017086_enable.html]
|
||||
support-files = bug1017086_inner.html
|
||||
[test_bug1079236.html]
|
||||
[test_bug1145910.html]
|
||||
[test_clickevent_on_input.html]
|
||||
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_continuous_wheel_events.html]
|
||||
|
48
dom/events/test/test_bug1145910.html
Normal file
48
dom/events/test/test_bug1145910.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1145910
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1145910</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
div:active {
|
||||
color: rgb(0, 255, 0);
|
||||
}
|
||||
</style>
|
||||
<div id="host">Foo</div>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1145910 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
var host = document.getElementById("host");
|
||||
var shadow = host.createShadowRoot();
|
||||
shadow.innerHTML = '<style>div:active { color: rgb(0, 255, 0); }</style><div id="inner">Bar</div>';
|
||||
var inner = shadow.getElementById("inner");
|
||||
|
||||
is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
|
||||
is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
|
||||
|
||||
synthesizeMouseAtCenter(host, { type: "mousedown" });
|
||||
|
||||
is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
|
||||
is(window.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
|
||||
|
||||
synthesizeMouseAtCenter(host, { type: "mouseup" });
|
||||
|
||||
is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
|
||||
is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -32,6 +32,11 @@ namespace IPC {
|
||||
mozilla::dom::RequestCredentials::Omit,
|
||||
mozilla::dom::RequestCredentials::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::RequestCache> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::RequestCache,
|
||||
mozilla::dom::RequestCache::Default,
|
||||
mozilla::dom::RequestCache::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::ResponseType> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::ResponseType,
|
||||
mozilla::dom::ResponseType::Basic,
|
||||
|
@ -208,6 +208,12 @@ public:
|
||||
return mCacheMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetCacheMode(RequestCache aCacheMode)
|
||||
{
|
||||
mCacheMode = aCacheMode;
|
||||
}
|
||||
|
||||
nsContentPolicyType
|
||||
ContentPolicyType() const
|
||||
{
|
||||
|
@ -82,6 +82,7 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
|
||||
RequestMode fallbackMode = RequestMode::EndGuard_;
|
||||
RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_;
|
||||
RequestCache fallbackCache = RequestCache::EndGuard_;
|
||||
if (aInput.IsUSVString()) {
|
||||
nsString input;
|
||||
input.Assign(aInput.GetAsUSVString());
|
||||
@ -127,6 +128,7 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
request->SetURL(NS_ConvertUTF16toUTF8(requestURL));
|
||||
fallbackMode = RequestMode::Cors;
|
||||
fallbackCredentials = RequestCredentials::Omit;
|
||||
fallbackCache = RequestCache::Default;
|
||||
}
|
||||
|
||||
// CORS-with-forced-preflight is not publicly exposed and should not be
|
||||
@ -152,6 +154,12 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
request->SetCredentialsMode(credentials);
|
||||
}
|
||||
|
||||
RequestCache cache = aInit.mCache.WasPassed() ?
|
||||
aInit.mCache.Value() : fallbackCache;
|
||||
if (cache != RequestCache::EndGuard_) {
|
||||
request->SetCacheMode(cache);
|
||||
}
|
||||
|
||||
// Request constructor step 14.
|
||||
if (aInit.mMethod.WasPassed()) {
|
||||
nsAutoCString method(aInit.mMethod.Value());
|
||||
|
@ -228,18 +228,5 @@ Response::Headers_()
|
||||
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
void
|
||||
Response::SetFinalURL(bool aFinalURL, ErrorResult& aRv)
|
||||
{
|
||||
nsCString url;
|
||||
mInternalResponse->GetUrl(url);
|
||||
if (url.IsEmpty()) {
|
||||
aRv.ThrowTypeError(MSG_RESPONSE_URL_IS_NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
mInternalResponse->SetFinalURL(aFinalURL);
|
||||
}
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -56,15 +56,6 @@ public:
|
||||
aUrl.AsAString() = NS_ConvertUTF8toUTF16(url);
|
||||
}
|
||||
|
||||
bool
|
||||
GetFinalURL(ErrorResult& aRv) const
|
||||
{
|
||||
return mInternalResponse->FinalURL();
|
||||
}
|
||||
|
||||
void
|
||||
SetFinalURL(bool aFinalURL, ErrorResult& aRv);
|
||||
|
||||
uint16_t
|
||||
Status() const
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ interface nsIServiceWorkerUnregisterCallback : nsISupports
|
||||
[noscript] void UnregisterFailed();
|
||||
};
|
||||
|
||||
[builtinclass, uuid(706c3e6b-c9d2-4857-893d-4b4845fec48f)]
|
||||
[builtinclass, uuid(e4c8baa5-237a-4bf6-82d4-ea06eb4b76ba)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -59,7 +59,8 @@ interface nsIServiceWorkerManager : nsISupports
|
||||
bool isControlled(in nsIDocument aDocument);
|
||||
|
||||
// Cause a fetch event to be dispatched to the worker global associated with the given document.
|
||||
void dispatchFetchEvent(in nsIDocument aDoc, in nsIInterceptedChannel aChannel);
|
||||
void dispatchFetchEvent(in nsIDocument aDoc, in nsIInterceptedChannel aChannel,
|
||||
in boolean aIsReload);
|
||||
|
||||
// aTarget MUST be a ServiceWorkerRegistration.
|
||||
[noscript] void AddRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
|
||||
|
@ -1431,7 +1431,6 @@ void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
|
||||
void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
|
||||
mMediaSeekable = aMediaSeekable;
|
||||
}
|
||||
|
||||
@ -1641,9 +1640,11 @@ MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
|
||||
void
|
||||
MediaDecoder::NotifyWaitingForResourcesStatusChanged()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (mDecoderStateMachine) {
|
||||
mDecoderStateMachine->NotifyWaitingForResourcesStatusChanged();
|
||||
RefPtr<nsRunnable> task =
|
||||
NS_NewRunnableMethod(mDecoderStateMachine,
|
||||
&MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged);
|
||||
mDecoderStateMachine->TaskQueue()->Dispatch(task.forget());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1741,7 +1742,6 @@ CDMProxy*
|
||||
MediaDecoder::GetCDMProxy()
|
||||
{
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
|
||||
return mProxy;
|
||||
}
|
||||
#endif
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "VideoUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
@ -27,6 +28,12 @@ extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(x, ...)
|
||||
#endif
|
||||
|
||||
// Same workaround as MediaDecoderStateMachine.cpp.
|
||||
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
|
||||
#define DECODER_WARN(x, ...) \
|
||||
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder, ##__VA_ARGS__).get()))
|
||||
|
||||
|
||||
PRLogModuleInfo* gMediaPromiseLog;
|
||||
|
||||
void
|
||||
@ -183,6 +190,43 @@ MediaDecoderReader::ComputeStartTime(const VideoData* aVideo, const AudioData* a
|
||||
return startTime;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaDecoderReader::CallReadMetadata()
|
||||
{
|
||||
typedef ReadMetadataFailureReason Reason;
|
||||
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
|
||||
DECODER_LOG("MediaDecoderReader::CallReadMetadata");
|
||||
|
||||
// PreReadMetadata causes us to try to allocate various hardware and OS
|
||||
// resources, which may not be available at the moment.
|
||||
PreReadMetadata();
|
||||
if (IsWaitingMediaResources()) {
|
||||
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
|
||||
}
|
||||
|
||||
// Attempt to read the metadata.
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
nsresult rv = ReadMetadata(&metadata->mInfo, getter_Transfers(metadata->mTags));
|
||||
|
||||
// Reading metadata can cause us to discover that we need resources (like
|
||||
// encryption keys).
|
||||
if (IsWaitingMediaResources()) {
|
||||
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
|
||||
}
|
||||
|
||||
// We're not waiting for anything. If we didn't get the metadata, that's an
|
||||
// error.
|
||||
if (NS_FAILED(rv) || !metadata->mInfo.HasValidMedia()) {
|
||||
DECODER_WARN("ReadMetadata failed, rv=%x HasValidMedia=%d", rv, metadata->mInfo.HasValidMedia());
|
||||
return MetadataPromise::CreateAndReject(Reason::METADATA_ERROR, __func__);
|
||||
}
|
||||
|
||||
// Success!
|
||||
return MetadataPromise::CreateAndResolve(metadata, __func__);
|
||||
}
|
||||
|
||||
class ReRequestVideoWithSkipTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
@ -357,3 +401,7 @@ MediaDecoderReader::Shutdown()
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#undef DECODER_LOG
|
||||
#undef DECODER_WARN
|
||||
#undef DECODER_WARN_HELPER
|
||||
|
@ -22,7 +22,8 @@ class TimeRanges;
|
||||
class MediaDecoderReader;
|
||||
class SharedDecoderManager;
|
||||
|
||||
struct WaitForDataRejectValue {
|
||||
struct WaitForDataRejectValue
|
||||
{
|
||||
enum Reason {
|
||||
SHUTDOWN,
|
||||
CANCELED
|
||||
@ -34,6 +35,23 @@ struct WaitForDataRejectValue {
|
||||
Reason mReason;
|
||||
};
|
||||
|
||||
class MetadataHolder
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataHolder)
|
||||
MediaInfo mInfo;
|
||||
nsAutoPtr<MetadataTags> mTags;
|
||||
|
||||
private:
|
||||
virtual ~MetadataHolder() {}
|
||||
};
|
||||
|
||||
enum class ReadMetadataFailureReason : int8_t
|
||||
{
|
||||
WAITING_FOR_RESOURCES,
|
||||
METADATA_ERROR
|
||||
};
|
||||
|
||||
// Encapsulates the decoding and reading of media data. Reading can either
|
||||
// synchronous and done on the calling "decode" thread, or asynchronous and
|
||||
// performed on a background thread, with the result being returned by
|
||||
@ -49,6 +67,7 @@ public:
|
||||
CANCELED
|
||||
};
|
||||
|
||||
typedef MediaPromise<nsRefPtr<MetadataHolder>, ReadMetadataFailureReason, /* IsExclusive = */ true> MetadataPromise;
|
||||
typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason, /* IsExclusive = */ true> AudioDataPromise;
|
||||
typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason, /* IsExclusive = */ true> VideoDataPromise;
|
||||
typedef MediaPromise<int64_t, nsresult, /* IsExclusive = */ true> SeekPromise;
|
||||
@ -148,6 +167,11 @@ public:
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
// The ReadMetadata API is unfortunately synchronous. We should fix that at
|
||||
// some point, but for now we can make things a bit better by using a
|
||||
// promise-y API on top of a synchronous call.
|
||||
nsRefPtr<MetadataPromise> CallReadMetadata();
|
||||
|
||||
// A function that is called before ReadMetadata() call.
|
||||
virtual void PreReadMetadata() {};
|
||||
|
||||
|
@ -1499,14 +1499,20 @@ void MediaDecoderStateMachine::SetDormant(bool aDormant)
|
||||
if (IsPlaying()) {
|
||||
StopPlayback();
|
||||
}
|
||||
StopAudioThread();
|
||||
FlushDecoding();
|
||||
// Now that those threads are stopped, there's no possibility of
|
||||
// mPendingWakeDecoder being needed again. Revoke it.
|
||||
mPendingWakeDecoder = nullptr;
|
||||
|
||||
Reset();
|
||||
|
||||
// Note that we do not wait for the decode task queue to go idle before
|
||||
// queuing the ReleaseMediaResources task - instead, we disconnect promises,
|
||||
// reset state, and put a ResetDecode in the decode task queue. Any tasks
|
||||
// that run after ResetDecode are supposed to run with a clean slate. We rely
|
||||
// on that in other places (i.e. seeking), so it seems reasonable to rely on
|
||||
// it here as well.
|
||||
DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
|
||||
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
// There's now no possibility of mPendingWakeDecoder being needed again. Revoke it.
|
||||
mPendingWakeDecoder = nullptr;
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
} else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
|
||||
mDecodingFrozenAtStateDecoding = true;
|
||||
@ -1560,43 +1566,21 @@ void MediaDecoderStateMachine::StartDecoding()
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::StartWaitForResources()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(OnDecodeThread(),
|
||||
"Should be on decode thread.");
|
||||
SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
|
||||
DECODER_LOG("StartWaitForResources");
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
|
||||
RefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethod(this,
|
||||
&MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged));
|
||||
DecodeTaskQueue()->Dispatch(task);
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
DECODER_LOG("DoNotifyWaitingForResourcesStatusChanged");
|
||||
DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
|
||||
|
||||
if (mState == DECODER_STATE_WAIT_FOR_RESOURCES) {
|
||||
// The reader is no longer waiting for resources (say a hardware decoder),
|
||||
// we can now proceed to decode metadata.
|
||||
// Try again.
|
||||
SetState(DECODER_STATE_DECODING_NONE);
|
||||
ScheduleStateMachine();
|
||||
} else if (mState == DECODER_STATE_WAIT_FOR_CDM &&
|
||||
!mReader->IsWaitingOnCDMResource()) {
|
||||
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
|
||||
EnqueueDecodeFirstFrameTask();
|
||||
}
|
||||
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::PlayInternal()
|
||||
@ -1641,38 +1625,6 @@ void MediaDecoderStateMachine::PlayInternal()
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::ResetPlayback()
|
||||
{
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
|
||||
// We should be reseting because we're seeking, shutting down, or
|
||||
// entering dormant state. We could also be in the process of going dormant,
|
||||
// and have just switched to exiting dormant before we finished entering
|
||||
// dormant, hence the DECODING_NONE case below.
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(mState == DECODER_STATE_SEEKING ||
|
||||
mState == DECODER_STATE_SHUTDOWN ||
|
||||
mState == DECODER_STATE_DORMANT ||
|
||||
mState == DECODER_STATE_DECODING_NONE);
|
||||
|
||||
// Audio thread should've been stopped at the moment. Otherwise, AudioSink
|
||||
// might be accessing AudioQueue outside of the decoder monitor while we
|
||||
// are clearing the queue and causes crash for no samples to be popped.
|
||||
MOZ_ASSERT(!mAudioSink);
|
||||
|
||||
mVideoFrameEndTime = -1;
|
||||
mDecodedVideoEndTime = -1;
|
||||
mAudioStartTime = -1;
|
||||
mAudioEndTime = -1;
|
||||
mDecodedAudioEndTime = -1;
|
||||
mAudioCompleted = false;
|
||||
AudioQueue().Reset();
|
||||
VideoQueue().Reset();
|
||||
mFirstVideoFrameAfterSeek = nullptr;
|
||||
mDropAudioUntilNextDiscontinuity = true;
|
||||
mDropVideoUntilNextDiscontinuity = true;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
@ -1771,19 +1723,6 @@ void MediaDecoderStateMachine::StopAudioThread()
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
|
||||
|
||||
RefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata));
|
||||
nsresult rv = DecodeTaskQueue()->Dispatch(task);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
|
||||
{
|
||||
@ -1926,14 +1865,8 @@ MediaDecoderStateMachine::InitiateSeek()
|
||||
mCurrentSeek.mTarget.mEventVisibility);
|
||||
NS_DispatchToMainThread(startEvent, NS_DISPATCH_NORMAL);
|
||||
|
||||
// The seek target is different than the current playback position,
|
||||
// we'll need to seek the playback position, so shutdown our decode
|
||||
// thread and audio sink.
|
||||
StopAudioThread();
|
||||
ResetPlayback();
|
||||
|
||||
// Put a reset in the pipe before seek.
|
||||
ResetDecode();
|
||||
// Reset our state machine and decoding pipeline before seeking.
|
||||
Reset();
|
||||
|
||||
// Do the seek.
|
||||
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
@ -2205,59 +2138,16 @@ MediaDecoderStateMachine::DecodeError()
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::CallDecodeMetadata()
|
||||
MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (mState != DECODER_STATE_DECODING_METADATA) {
|
||||
return;
|
||||
}
|
||||
if (NS_FAILED(DecodeMetadata())) {
|
||||
DECODER_WARN("Decode metadata failed, shutting down decoder");
|
||||
DecodeError();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult MediaDecoderStateMachine::DecodeMetadata()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
|
||||
DECODER_LOG("Decoding Media Headers");
|
||||
mMetadataRequest.Complete();
|
||||
|
||||
nsresult res;
|
||||
MediaInfo info;
|
||||
bool isAwaitingResources = false;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
mReader->PreReadMetadata();
|
||||
|
||||
if (mReader->IsWaitingMediaResources()) {
|
||||
StartWaitForResources();
|
||||
return NS_OK;
|
||||
}
|
||||
res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags));
|
||||
isAwaitingResources = mReader->IsWaitingMediaResources();
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(res) &&
|
||||
mState == DECODER_STATE_DECODING_METADATA &&
|
||||
isAwaitingResources) {
|
||||
// change state to DECODER_STATE_WAIT_FOR_RESOURCES
|
||||
StartWaitForResources();
|
||||
// affect values only if ReadMetadata succeeds
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(res) || (!info.HasValidMedia())) {
|
||||
DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
|
||||
}
|
||||
|
||||
mInfo = info;
|
||||
mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
|
||||
mInfo = aMetadata->mInfo;
|
||||
mMetadataTags = aMetadata->mTags.forget();
|
||||
|
||||
if (HasVideo()) {
|
||||
DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d",
|
||||
@ -2275,23 +2165,33 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
||||
EnqueueLoadedMetadataEvent();
|
||||
}
|
||||
|
||||
if (mState == DECODER_STATE_DECODING_METADATA) {
|
||||
if (mReader->IsWaitingOnCDMResource()) {
|
||||
// Metadata parsing was successful but we're still waiting for CDM caps
|
||||
// to become available so that we can build the correct decryptor/decoder.
|
||||
SetState(DECODER_STATE_WAIT_FOR_CDM);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
|
||||
res = EnqueueDecodeFirstFrameTask();
|
||||
if (NS_FAILED(res)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mReader->IsWaitingOnCDMResource()) {
|
||||
// Metadata parsing was successful but we're still waiting for CDM caps
|
||||
// to become available so that we can build the correct decryptor/decoder.
|
||||
SetState(DECODER_STATE_WAIT_FOR_CDM);
|
||||
return;
|
||||
}
|
||||
ScheduleStateMachine();
|
||||
|
||||
return NS_OK;
|
||||
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
|
||||
EnqueueDecodeFirstFrameTask();
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::OnMetadataNotRead(ReadMetadataFailureReason aReason)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
|
||||
mMetadataRequest.Complete();
|
||||
|
||||
if (aReason == ReadMetadataFailureReason::WAITING_FOR_RESOURCES) {
|
||||
SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
|
||||
} else {
|
||||
MOZ_ASSERT(aReason == ReadMetadataFailureReason::METADATA_ERROR);
|
||||
DECODER_WARN("Decode metadata failed, shutting down decoder");
|
||||
DecodeError();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -2674,8 +2574,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
StopPlayback();
|
||||
}
|
||||
|
||||
StopAudioThread();
|
||||
FlushDecoding();
|
||||
Reset();
|
||||
|
||||
// Put a task in the decode queue to shutdown the reader.
|
||||
// the queue to spin down.
|
||||
@ -2699,16 +2598,25 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
|
||||
case DECODER_STATE_DECODING_NONE: {
|
||||
SetState(DECODER_STATE_DECODING_METADATA);
|
||||
// Ensure we have a decode thread to decode metadata.
|
||||
return EnqueueDecodeMetadataTask();
|
||||
ScheduleStateMachine();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING_METADATA: {
|
||||
if (!mMetadataRequest.Exists()) {
|
||||
DECODER_LOG("Dispatching CallReadMetadata");
|
||||
mMetadataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::CallReadMetadata)
|
||||
->RefableThen(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMetadataRead,
|
||||
&MediaDecoderStateMachine::OnMetadataNotRead));
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING_FIRSTFRAME: {
|
||||
// DECODER_STATE_DECODING_FIRSTFRAME will be started by DecodeMetadata
|
||||
// DECODER_STATE_DECODING_FIRSTFRAME will be started by OnMetadataRead.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2838,48 +2746,46 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::FlushDecoding()
|
||||
MediaDecoderStateMachine::Reset()
|
||||
{
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
AssertCurrentThreadInMonitor();
|
||||
DECODER_LOG("MediaDecoderStateMachine::Reset");
|
||||
|
||||
// Put a task in the decode queue to abort any decoding operations.
|
||||
// The reader is not supposed to put any tasks to deliver samples into
|
||||
// the queue after this runs (unless we request another sample from it).
|
||||
ResetDecode();
|
||||
{
|
||||
// Wait for the ResetDecode to run and for the decoder to abort
|
||||
// decoding operations and run any pending callbacks. This is
|
||||
// important, as we don't want any pending tasks posted to the task
|
||||
// queue by the reader to deliver any samples after we've posted the
|
||||
// reader Shutdown() task below, as the sample-delivery tasks will
|
||||
// keep video frames alive until after we've called Reader::Shutdown(),
|
||||
// and shutdown on B2G will fail as there are outstanding video frames
|
||||
// alive.
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
DecodeTaskQueue()->AwaitIdle();
|
||||
}
|
||||
// We should be resetting because we're seeking, shutting down, or entering
|
||||
// dormant state. We could also be in the process of going dormant, and have
|
||||
// just switched to exiting dormant before we finished entering dormant,
|
||||
// hence the DECODING_NONE case below.
|
||||
MOZ_ASSERT(mState == DECODER_STATE_SEEKING ||
|
||||
mState == DECODER_STATE_SHUTDOWN ||
|
||||
mState == DECODER_STATE_DORMANT ||
|
||||
mState == DECODER_STATE_DECODING_NONE);
|
||||
|
||||
// We must reset playback so that all references to frames queued
|
||||
// in the state machine are dropped, else subsequent calls to Shutdown()
|
||||
// or ReleaseMediaResources() can fail on B2G.
|
||||
ResetPlayback();
|
||||
}
|
||||
// Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue
|
||||
// outside of the decoder monitor while we are clearing the queue and causes
|
||||
// crash for no samples to be popped.
|
||||
StopAudioThread();
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::ResetDecode()
|
||||
{
|
||||
MOZ_ASSERT(OnStateMachineThread());
|
||||
AssertCurrentThreadInMonitor();
|
||||
mVideoFrameEndTime = -1;
|
||||
mDecodedVideoEndTime = -1;
|
||||
mAudioStartTime = -1;
|
||||
mAudioEndTime = -1;
|
||||
mDecodedAudioEndTime = -1;
|
||||
mAudioCompleted = false;
|
||||
AudioQueue().Reset();
|
||||
VideoQueue().Reset();
|
||||
mFirstVideoFrameAfterSeek = nullptr;
|
||||
mDropAudioUntilNextDiscontinuity = true;
|
||||
mDropVideoUntilNextDiscontinuity = true;
|
||||
mDecodeToSeekTarget = false;
|
||||
|
||||
mMetadataRequest.DisconnectIfExists();
|
||||
mAudioDataRequest.DisconnectIfExists();
|
||||
mAudioWaitRequest.DisconnectIfExists();
|
||||
mVideoDataRequest.DisconnectIfExists();
|
||||
mVideoWaitRequest.DisconnectIfExists();
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
|
||||
mDecodeToSeekTarget = false;
|
||||
|
||||
RefPtr<nsRunnable> resetTask =
|
||||
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
|
||||
DecodeTaskQueue()->Dispatch(resetTask);
|
||||
|
@ -367,9 +367,8 @@ public:
|
||||
// be held.
|
||||
bool IsPlaying() const;
|
||||
|
||||
// Dispatch DoNotifyWaitingForResourcesStatusChanged task to the task queue.
|
||||
// Called when the reader may have acquired the hardware resources required
|
||||
// to begin decoding. The decoder monitor must be held while calling this.
|
||||
// to begin decoding.
|
||||
void NotifyWaitingForResourcesStatusChanged();
|
||||
|
||||
// Notifies the state machine that should minimize the number of samples
|
||||
@ -407,8 +406,9 @@ public:
|
||||
WaitRequestRef(aRejection.mType).Complete();
|
||||
}
|
||||
|
||||
// Resets all state related to decoding, emptying all buffers etc.
|
||||
void ResetDecode();
|
||||
// Resets all state related to decoding and playback, emptying all buffers
|
||||
// and aborting all pending operations on the decode task queue.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void AcquireMonitorAndInvokeDecodeError();
|
||||
@ -465,8 +465,6 @@ protected:
|
||||
|
||||
nsresult FinishDecodeFirstFrame();
|
||||
|
||||
nsAutoPtr<MetadataTags> mMetadataTags;
|
||||
|
||||
// True if our buffers of decoded audio are not full, and we should
|
||||
// decode more.
|
||||
bool NeedToDecodeAudio();
|
||||
@ -514,14 +512,6 @@ protected:
|
||||
// Dispatches an asynchronous event to update the media element's ready state.
|
||||
void UpdateReadyState();
|
||||
|
||||
// Resets playback timing data. Called when we seek, on the decode thread.
|
||||
void ResetPlayback();
|
||||
|
||||
// Orders the Reader to stop decoding, and blocks until the Reader
|
||||
// has stopped decoding and finished delivering samples, then calls
|
||||
// ResetPlayback() to discard all enqueued data.
|
||||
void FlushDecoding();
|
||||
|
||||
// Called when AudioSink reaches the end. |mPlayStartTime| and
|
||||
// |mPlayDuration| are updated to provide a good base for calculating video
|
||||
// stream time.
|
||||
@ -590,8 +580,6 @@ protected:
|
||||
// decode thread.
|
||||
void DecodeError();
|
||||
|
||||
void StartWaitForResources();
|
||||
|
||||
// Dispatches a task to the decode task queue to begin decoding metadata.
|
||||
// This is threadsafe and can be called on any thread.
|
||||
// The decoder monitor must be held.
|
||||
@ -659,12 +647,9 @@ protected:
|
||||
// must be held when calling this. Called on the decode thread.
|
||||
int64_t GetDecodedAudioDuration();
|
||||
|
||||
// Load metadata. Called on the decode thread. The decoder monitor
|
||||
// must be held with exactly one lock count.
|
||||
nsresult DecodeMetadata();
|
||||
|
||||
// Wraps the call to DecodeMetadata(), signals a DecodeError() on failure.
|
||||
void CallDecodeMetadata();
|
||||
// Promise callbacks for metadata reading.
|
||||
void OnMetadataRead(MetadataHolder* aMetadata);
|
||||
void OnMetadataNotRead(ReadMetadataFailureReason aReason);
|
||||
|
||||
// Initiate first content decoding. Called on the state machine thread.
|
||||
// The decoder monitor must be held with exactly one lock count.
|
||||
@ -733,10 +718,6 @@ protected:
|
||||
// Called by the AudioSink to signal errors.
|
||||
void OnAudioSinkError();
|
||||
|
||||
// The state machine may move into DECODING_METADATA if we are in
|
||||
// DECODER_STATE_WAIT_FOR_RESOURCES.
|
||||
void DoNotifyWaitingForResourcesStatusChanged();
|
||||
|
||||
// Return true if the video decoder's decode speed can not catch up the
|
||||
// play time.
|
||||
bool NeedToSkipToNextKeyframe();
|
||||
@ -1186,10 +1167,15 @@ protected:
|
||||
// we were at before the seek.
|
||||
int64_t mCurrentTimeBeforeSeek;
|
||||
|
||||
// Track our request for metadata from the reader.
|
||||
MediaPromiseConsumerHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
|
||||
|
||||
// Stores presentation info required for playback. The decoder monitor
|
||||
// must be held when accessing this.
|
||||
MediaInfo mInfo;
|
||||
|
||||
nsAutoPtr<MetadataTags> mMetadataTags;
|
||||
|
||||
mozilla::MediaMetadataManager mMetadataManager;
|
||||
|
||||
MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
|
||||
|
@ -274,8 +274,8 @@ AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aCo
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> decoder;
|
||||
|
||||
if ((strcmp(aConfig.mime_type, "video/avc") &&
|
||||
strcmp(aConfig.mime_type, "video/mp4")) ||
|
||||
if ((!aConfig.mime_type.EqualsLiteral("video/avc") &&
|
||||
!aConfig.mime_type.EqualsLiteral("video/mp4")) ||
|
||||
!mPDM->DecoderNeedsAVCC(aConfig)) {
|
||||
// There is no need for an AVCC wrapper for non-AVC content.
|
||||
decoder = mPDM->CreateVideoDecoder(aConfig,
|
||||
@ -305,13 +305,13 @@ AVCCDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aCo
|
||||
}
|
||||
|
||||
bool
|
||||
AVCCDecoderModule::SupportsAudioMimeType(const char* aMimeType)
|
||||
AVCCDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return mPDM->SupportsAudioMimeType(aMimeType);
|
||||
}
|
||||
|
||||
bool
|
||||
AVCCDecoderModule::SupportsVideoMimeType(const char* aMimeType)
|
||||
AVCCDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return mPDM->SupportsVideoMimeType(aMimeType);
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ public:
|
||||
FlushableMediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) override;
|
||||
|
||||
virtual bool SupportsAudioMimeType(const char* aMimeType) override;
|
||||
virtual bool SupportsVideoMimeType(const char* aMimeType) override;
|
||||
virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
|
||||
virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override;
|
||||
|
||||
private:
|
||||
nsRefPtr<PlatformDecoderModule> mPDM;
|
||||
|
@ -238,7 +238,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool
|
||||
SupportsAudioMimeType(const char* aMimeType) override
|
||||
SupportsAudioMimeType(const nsACString& aMimeType) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -311,19 +311,19 @@ MP4Reader::ExtractCryptoInitData(nsTArray<uint8_t>& aInitData)
|
||||
}
|
||||
|
||||
bool
|
||||
MP4Reader::IsSupportedAudioMimeType(const char* aMimeType)
|
||||
MP4Reader::IsSupportedAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return (!strcmp(aMimeType, "audio/mpeg") ||
|
||||
!strcmp(aMimeType, "audio/mp4a-latm")) &&
|
||||
return (aMimeType.EqualsLiteral("audio/mpeg") ||
|
||||
aMimeType.EqualsLiteral("audio/mp4a-latm")) &&
|
||||
mPlatform->SupportsAudioMimeType(aMimeType);
|
||||
}
|
||||
|
||||
bool
|
||||
MP4Reader::IsSupportedVideoMimeType(const char* aMimeType)
|
||||
MP4Reader::IsSupportedVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return (!strcmp(aMimeType, "video/mp4") ||
|
||||
!strcmp(aMimeType, "video/avc") ||
|
||||
!strcmp(aMimeType, "video/x-vnd.on2.vp6")) &&
|
||||
return (aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc") ||
|
||||
aMimeType.EqualsLiteral("video/x-vnd.on2.vp6")) &&
|
||||
mPlatform->SupportsVideoMimeType(aMimeType);
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,8 @@ private:
|
||||
void Flush(mp4_demuxer::TrackType aTrack);
|
||||
void DrainComplete(mp4_demuxer::TrackType aTrack);
|
||||
void UpdateIndex();
|
||||
bool IsSupportedAudioMimeType(const char* aMimeType);
|
||||
bool IsSupportedVideoMimeType(const char* aMimeType);
|
||||
bool IsSupportedAudioMimeType(const nsACString& aMimeType);
|
||||
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
|
||||
void NotifyResourcesStatusChanged();
|
||||
void RequestCodecResource();
|
||||
virtual bool IsWaitingOnCDMResource() override;
|
||||
|
@ -180,15 +180,15 @@ PlatformDecoderModule::CreatePDM()
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformDecoderModule::SupportsAudioMimeType(const char* aMimeType)
|
||||
PlatformDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return !strcmp(aMimeType, "audio/mp4a-latm");
|
||||
return aMimeType.EqualsLiteral("audio/mp4a-latm");
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformDecoderModule::SupportsVideoMimeType(const char* aMimeType)
|
||||
PlatformDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return !strcmp(aMimeType, "video/mp4") || !strcmp(aMimeType, "video/avc");
|
||||
return aMimeType.EqualsLiteral("video/mp4") || aMimeType.EqualsLiteral("video/avc");
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -122,8 +122,8 @@ public:
|
||||
// An audio decoder module must support AAC by default.
|
||||
// If more audio codec is to be supported, SupportsAudioMimeType will have
|
||||
// to be extended
|
||||
virtual bool SupportsAudioMimeType(const char* aMimeType);
|
||||
virtual bool SupportsVideoMimeType(const char* aMimeType);
|
||||
virtual bool SupportsAudioMimeType(const nsACString& aMimeType);
|
||||
virtual bool SupportsVideoMimeType(const nsACString& aMimeType);
|
||||
|
||||
// Indicates if the video decoder requires AVCC format.
|
||||
virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig);
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsPromiseFlatString.h"
|
||||
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
@ -28,14 +29,10 @@ using namespace mozilla::widget::sdk;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static MediaCodec::LocalRef CreateDecoder(const char* aMimeType)
|
||||
static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType)
|
||||
{
|
||||
if (!aMimeType) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MediaCodec::LocalRef codec;
|
||||
NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(aMimeType, &codec), nullptr);
|
||||
NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(PromiseFlatCString(aMimeType).get(), &codec), nullptr);
|
||||
return codec;
|
||||
}
|
||||
|
||||
@ -251,7 +248,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
bool AndroidDecoderModule::SupportsAudioMimeType(const char* aMimeType) {
|
||||
bool AndroidDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType) {
|
||||
return static_cast<bool>(CreateDecoder(aMimeType));
|
||||
}
|
||||
|
||||
@ -300,11 +297,11 @@ AndroidDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig&
|
||||
}
|
||||
|
||||
MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
|
||||
const char* aMimeType,
|
||||
const nsACString& aMimeType,
|
||||
MediaFormat::Param aFormat,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mType(aType)
|
||||
, mMimeType(strdup(aMimeType))
|
||||
, mMimeType(aMimeType)
|
||||
, mFormat(aFormat)
|
||||
, mCallback(aCallback)
|
||||
, mInputBuffers(nullptr)
|
||||
|
@ -35,14 +35,14 @@ public:
|
||||
AndroidDecoderModule() {}
|
||||
virtual ~AndroidDecoderModule() {}
|
||||
|
||||
virtual bool SupportsAudioMimeType(const char* aMimeType) override;
|
||||
virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
|
||||
};
|
||||
|
||||
class MediaCodecDataDecoder : public MediaDataDecoder {
|
||||
public:
|
||||
|
||||
MediaCodecDataDecoder(MediaData::Type aType,
|
||||
const char* aMimeType,
|
||||
const nsACString& aMimeType,
|
||||
widget::sdk::MediaFormat::Param aFormat,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
|
||||
@ -59,7 +59,7 @@ protected:
|
||||
|
||||
MediaData::Type mType;
|
||||
|
||||
nsAutoPtr<char> mMimeType;
|
||||
nsAutoCString mMimeType;
|
||||
widget::sdk::MediaFormat::GlobalRef mFormat;
|
||||
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
@ -35,14 +35,14 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MOZ_COUNT_CTOR(AppleATDecoder);
|
||||
LOG("Creating Apple AudioToolbox decoder");
|
||||
LOG("Audio Decoder configuration: %s %d Hz %d channels %d bits per channel",
|
||||
mConfig.mime_type,
|
||||
mConfig.mime_type.get(),
|
||||
mConfig.samples_per_second,
|
||||
mConfig.channel_count,
|
||||
mConfig.bits_per_sample);
|
||||
|
||||
if (!strcmp(mConfig.mime_type, "audio/mpeg")) {
|
||||
if (mConfig.mime_type.EqualsLiteral("audio/mpeg")) {
|
||||
mFormatID = kAudioFormatMPEGLayer3;
|
||||
} else if (!strcmp(mConfig.mime_type, "audio/mp4a-latm")) {
|
||||
} else if (mConfig.mime_type.EqualsLiteral("audio/mp4a-latm")) {
|
||||
mFormatID = kAudioFormatMPEG4AAC;
|
||||
} else {
|
||||
mFormatID = 0;
|
||||
|
@ -192,9 +192,9 @@ AppleDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aC
|
||||
}
|
||||
|
||||
bool
|
||||
AppleDecoderModule::SupportsAudioMimeType(const char* aMimeType)
|
||||
AppleDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return !strcmp(aMimeType, "audio/mp4a-latm") || !strcmp(aMimeType, "audio/mpeg");
|
||||
return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/mpeg");
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
FlushableMediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) override;
|
||||
|
||||
virtual bool SupportsAudioMimeType(const char* aMimeType) override;
|
||||
virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override;
|
||||
virtual bool
|
||||
DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
|
||||
|
||||
|
@ -170,13 +170,13 @@ FFmpegAudioDecoder<LIBAV_VER>::Drain()
|
||||
}
|
||||
|
||||
AVCodecID
|
||||
FFmpegAudioDecoder<LIBAV_VER>::GetCodecId(const char* aMimeType)
|
||||
FFmpegAudioDecoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
|
||||
{
|
||||
if (!strcmp(aMimeType, "audio/mpeg")) {
|
||||
if (aMimeType.EqualsLiteral("audio/mpeg")) {
|
||||
return AV_CODEC_ID_MP3;
|
||||
}
|
||||
|
||||
if (!strcmp(aMimeType, "audio/mp4a-latm")) {
|
||||
if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
|
||||
return AV_CODEC_ID_AAC;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#define __FFmpegAACDecoder_h__
|
||||
|
||||
#include "FFmpegDataDecoder.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
@ -29,7 +28,7 @@ public:
|
||||
virtual nsresult Init() override;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
|
||||
virtual nsresult Drain() override;
|
||||
static AVCodecID GetCodecId(const char* aMimeType);
|
||||
static AVCodecID GetCodecId(const nsACString& aMimeType);
|
||||
|
||||
private:
|
||||
void DecodePacket(mp4_demuxer::MP4Sample* aSample);
|
||||
|
@ -51,12 +51,12 @@ public:
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
virtual bool SupportsAudioMimeType(const char* aMimeType) override
|
||||
virtual bool SupportsAudioMimeType(const nsACString& aMimeType) override
|
||||
{
|
||||
return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
|
||||
}
|
||||
|
||||
virtual bool SupportsVideoMimeType(const char* aMimeType) override
|
||||
virtual bool SupportsVideoMimeType(const nsACString& aMimeType) override
|
||||
{
|
||||
return FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
|
||||
}
|
||||
|
@ -287,13 +287,13 @@ FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
|
||||
}
|
||||
|
||||
AVCodecID
|
||||
FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const char* aMimeType)
|
||||
FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
|
||||
{
|
||||
if (!strcmp(aMimeType, "video/avc") || !strcmp(aMimeType, "video/mp4")) {
|
||||
if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
|
||||
return AV_CODEC_ID_H264;
|
||||
}
|
||||
|
||||
if (!strcmp(aMimeType, "video/x-vnd.on2.vp6")) {
|
||||
if (aMimeType.EqualsLiteral("video/x-vnd.on2.vp6")) {
|
||||
return AV_CODEC_ID_VP6F;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Flush() override;
|
||||
static AVCodecID GetCodecId(const char* aMimeType);
|
||||
static AVCodecID GetCodecId(const nsACString& aMimeType);
|
||||
|
||||
private:
|
||||
void DecodeFrame(mp4_demuxer::MP4Sample* aSample);
|
||||
|
@ -46,7 +46,7 @@ GMPDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aCon
|
||||
FlushableMediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (strcmp(aConfig.mime_type, "video/avc") != 0) {
|
||||
if (!aConfig.mime_type.EqualsLiteral("video/avc")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ GMPDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aCon
|
||||
FlushableMediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (strcmp(aConfig.mime_type, "audio/mp4a-latm") != 0) {
|
||||
if (!aConfig.mime_type.EqualsLiteral("audio/mp4a-latm")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ GonkAudioDecoderManager::GonkAudioDecoderManager(
|
||||
mUserData.AppendElements(aConfig.audio_specific_config->Elements(),
|
||||
aConfig.audio_specific_config->Length());
|
||||
// Pass through mp3 without applying an ADTS header.
|
||||
if (strcmp(aConfig.mime_type, "audio/mp4a-latm") != 0) {
|
||||
if (!aConfig.mime_type.EqualsLiteral("audio/mp4a-latm")) {
|
||||
mUseAdts = false;
|
||||
}
|
||||
}
|
||||
|
@ -77,9 +77,9 @@ WMFAudioMFTManager::WMFAudioMFTManager(
|
||||
{
|
||||
MOZ_COUNT_CTOR(WMFAudioMFTManager);
|
||||
|
||||
if (!strcmp(aConfig.mime_type, "audio/mpeg")) {
|
||||
if (aConfig.mime_type.EqualsLiteral("audio/mpeg")) {
|
||||
mStreamType = MP3;
|
||||
} else if (!strcmp(aConfig.mime_type, "audio/mp4a-latm")) {
|
||||
} else if (aConfig.mime_type.EqualsLiteral("audio/mp4a-latm")) {
|
||||
mStreamType = AAC;
|
||||
AACAudioSpecificConfigToUserData(aConfig.aac_profile,
|
||||
aConfig.audio_specific_config->Elements(),
|
||||
|
@ -118,19 +118,19 @@ WMFDecoderModule::SupportsSharedDecoders(const mp4_demuxer::VideoDecoderConfig&
|
||||
}
|
||||
|
||||
bool
|
||||
WMFDecoderModule::SupportsVideoMimeType(const char* aMimeType)
|
||||
WMFDecoderModule::SupportsVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return !strcmp(aMimeType, "video/mp4") ||
|
||||
!strcmp(aMimeType, "video/avc") ||
|
||||
!strcmp(aMimeType, "video/webm; codecs=vp8") ||
|
||||
!strcmp(aMimeType, "video/webm; codecs=vp9");
|
||||
return aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc") ||
|
||||
aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
|
||||
aMimeType.EqualsLiteral("video/webm; codecs=vp9");
|
||||
}
|
||||
|
||||
bool
|
||||
WMFDecoderModule::SupportsAudioMimeType(const char* aMimeType)
|
||||
WMFDecoderModule::SupportsAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return !strcmp(aMimeType, "audio/mp4a-latm") ||
|
||||
!strcmp(aMimeType, "audio/mpeg");
|
||||
return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
||||
aMimeType.EqualsLiteral("audio/mpeg");
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -31,8 +31,8 @@ public:
|
||||
FlushableMediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) override;
|
||||
|
||||
bool SupportsVideoMimeType(const char* aMimeType) override;
|
||||
bool SupportsAudioMimeType(const char* aMimeType) override;
|
||||
bool SupportsVideoMimeType(const nsACString& aMimeType) override;
|
||||
bool SupportsAudioMimeType(const nsACString& aMimeType) override;
|
||||
|
||||
virtual void DisableHardwareAcceleration() override
|
||||
{
|
||||
|
@ -84,12 +84,12 @@ WMFVideoMFTManager::WMFVideoMFTManager(
|
||||
MOZ_COUNT_CTOR(WMFVideoMFTManager);
|
||||
|
||||
// Need additional checks/params to check vp8/vp9
|
||||
if (!strcmp(aConfig.mime_type, "video/mp4") ||
|
||||
!strcmp(aConfig.mime_type, "video/avc")) {
|
||||
if (aConfig.mime_type.EqualsLiteral("video/mp4") ||
|
||||
aConfig.mime_type.EqualsLiteral("video/avc")) {
|
||||
mStreamType = H264;
|
||||
} else if (!strcmp(aConfig.mime_type, "video/webm; codecs=vp8")) {
|
||||
} else if (aConfig.mime_type.EqualsLiteral("video/webm; codecs=vp8")) {
|
||||
mStreamType = VP8;
|
||||
} else if (!strcmp(aConfig.mime_type, "video/webm; codecs=vp9")) {
|
||||
} else if (aConfig.mime_type.EqualsLiteral("video/webm; codecs=vp9")) {
|
||||
mStreamType = VP9;
|
||||
} else {
|
||||
mStreamType = Unknown;
|
||||
|
@ -71,7 +71,9 @@ MediaSourceReader::PrepareInitialization()
|
||||
MSE_DEBUG("trackBuffers=%u", mTrackBuffers.Length());
|
||||
mEssentialTrackBuffers.AppendElements(mTrackBuffers);
|
||||
mHasEssentialTrackBuffers = true;
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
if (!IsWaitingMediaResources()) {
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@ -761,7 +763,10 @@ MediaSourceReader::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const Medi
|
||||
MSE_DEBUG("%p video", aTrackBuffer);
|
||||
mVideoTrack = aTrackBuffer;
|
||||
}
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
|
||||
if (!IsWaitingMediaResources()) {
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -112,10 +112,10 @@ IntelWebMVideoDecoder::Create(WebMReader* aReader)
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::IsSupportedVideoMimeType(const char* aMimeType)
|
||||
IntelWebMVideoDecoder::IsSupportedVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return (!strcmp(aMimeType, "video/webm; codecs=vp8") ||
|
||||
!strcmp(aMimeType, "video/webm; codecs=vp9")) &&
|
||||
return (aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
|
||||
aMimeType.EqualsLiteral("video/webm; codecs=vp9")) &&
|
||||
mPlatform->SupportsVideoMimeType(aMimeType);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ private:
|
||||
|
||||
bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed);
|
||||
|
||||
bool IsSupportedVideoMimeType(const char* aMimeType);
|
||||
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
|
||||
|
||||
VP8Sample* PopSample();
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "nsCORSListenerProxy.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsError.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
@ -819,6 +820,22 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
|
||||
}
|
||||
}
|
||||
|
||||
// Set CORS attributes on channel so that intercepted requests get correct
|
||||
// values. We have to do this here because the CheckMayLoad checks may lead
|
||||
// to early return. We can't be sure this is an http channel though, so we
|
||||
// can't return early on failure.
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
|
||||
if (internal) {
|
||||
if (mIsPreflight) {
|
||||
rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT);
|
||||
} else {
|
||||
rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = internal->SetCorsIncludeCredentials(mWithCredentials);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Check that the uri is ok to load
|
||||
rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
|
||||
|
@ -7,6 +7,7 @@ function testDefaultCtor() {
|
||||
is(req.referrer, "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
is(req.mode, "cors", "Request mode for string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
is(req.cache, "default", "Default Request cache is default");
|
||||
|
||||
var req = new Request(req);
|
||||
is(req.method, "GET", "Default Request method is GET");
|
||||
@ -16,6 +17,7 @@ function testDefaultCtor() {
|
||||
is(req.referrer, "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
is(req.mode, "cors", "Request mode string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
is(req.cache, "default", "Default Request cache is default");
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
@ -25,6 +27,7 @@ function testClone() {
|
||||
body: "Sample body",
|
||||
mode: "same-origin",
|
||||
credentials: "same-origin",
|
||||
cache: "no-store",
|
||||
});
|
||||
var clone = orig.clone();
|
||||
ok(clone.method === "POST", "Request method is POST");
|
||||
@ -39,6 +42,7 @@ function testClone() {
|
||||
ok(clone.referrer === "about:client", "Default referrer is `client` which serializes to about:client.");
|
||||
ok(clone.mode === "same-origin", "Request mode is same-origin");
|
||||
ok(clone.credentials === "same-origin", "Default credentials is same-origin");
|
||||
ok(clone.cache === "no-store", "Default cache is no-store");
|
||||
|
||||
ok(!orig.bodyUsed, "Original body is not consumed.");
|
||||
ok(!clone.bodyUsed, "Clone body is not consumed.");
|
||||
|
@ -121,25 +121,6 @@ function testOk() {
|
||||
ok(!r4.ok, "Response with status 302 should have ok false");
|
||||
}
|
||||
|
||||
// It is not possible to test setting finalURL until we have ServiceWorker
|
||||
// interception. This is because synthetic Responses do not have a url, the url
|
||||
// is set based on the request, so a SW could initiate a fetch() on behalf of
|
||||
// a client and set the resulting Response's finalURL before returning it to
|
||||
// the client, in which case the "set response's url to request's url" from the
|
||||
// client's point of view would not happen. A test for this will be added by
|
||||
// Bug 1134352.
|
||||
function testFinalURL() {
|
||||
var r1 = new Response();
|
||||
ok(!r1.finalURL, "Response.finalURL is false by default.");
|
||||
|
||||
try {
|
||||
r1.finalURL = true;
|
||||
ok(false, "Setting Response.finalURL of Response with null url should fail.");
|
||||
} catch(e) {
|
||||
ok(true, "Setting Response.finalURL of Response with null url should fail.");
|
||||
}
|
||||
}
|
||||
|
||||
function testBodyUsed() {
|
||||
var res = new Response("Sample body");
|
||||
ok(!res.bodyUsed, "bodyUsed is initially false.");
|
||||
@ -226,7 +207,6 @@ function runTest() {
|
||||
testError();
|
||||
testRedirect();
|
||||
testOk();
|
||||
testFinalURL();
|
||||
|
||||
return Promise.resolve()
|
||||
.then(testBodyCreation)
|
||||
|
@ -52,6 +52,13 @@ partial interface Performance {
|
||||
attribute EventHandler onresourcetimingbufferfull;
|
||||
};
|
||||
|
||||
// GC microbenchmarks, pref-guarded, not for general use (bug 1125412)
|
||||
[Exposed=Window]
|
||||
partial interface Performance {
|
||||
[Pref="dom.enable_memory_stats"]
|
||||
readonly attribute object mozMemory;
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/user-timing/
|
||||
[Exposed=Window]
|
||||
partial interface Performance {
|
||||
|
@ -17,8 +17,6 @@ interface Response {
|
||||
readonly attribute ResponseType type;
|
||||
|
||||
readonly attribute USVString url;
|
||||
[Throws]
|
||||
attribute boolean finalURL;
|
||||
readonly attribute unsigned short status;
|
||||
readonly attribute boolean ok;
|
||||
readonly attribute ByteString statusText;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ServiceWorkerEvents.h"
|
||||
#include "ServiceWorkerClient.h"
|
||||
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsContentUtils.h"
|
||||
@ -120,7 +121,7 @@ public:
|
||||
mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
|
||||
|
||||
nsAutoTArray<InternalHeaders::Entry, 5> entries;
|
||||
mInternalResponse->Headers()->GetEntries(entries);
|
||||
mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
|
||||
for (uint32_t i = 0; i < entries.Length(); ++i) {
|
||||
mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
|
||||
}
|
||||
@ -135,11 +136,14 @@ class RespondWithHandler final : public PromiseNativeHandler
|
||||
{
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
|
||||
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
|
||||
RequestMode mRequestMode;
|
||||
public:
|
||||
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
|
||||
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
|
||||
RequestMode aRequestMode)
|
||||
: mInterceptedChannel(aChannel)
|
||||
, mServiceWorker(aServiceWorker)
|
||||
, mRequestMode(aRequestMode)
|
||||
{
|
||||
}
|
||||
|
||||
@ -213,8 +217,10 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(nsm) Bug 1136200 deal with opaque and no-cors (fetch spec 4.2.2.2).
|
||||
if (response->Type() == ResponseType::Error) {
|
||||
// Section 4.2, step 2.2 "If either response's type is "opaque" and request's
|
||||
// mode is not "no-cors" or response's type is error, return a network error."
|
||||
if (((response->Type() == ResponseType::Opaque) && (mRequestMode != RequestMode::No_cors)) ||
|
||||
response->Type() == ResponseType::Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,7 +290,8 @@ FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
mWaitToRespond = true;
|
||||
nsRefPtr<RespondWithHandler> handler = new RespondWithHandler(mChannel, mServiceWorker);
|
||||
nsRefPtr<RespondWithHandler> handler =
|
||||
new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode());
|
||||
aPromise.AppendNativeHandler(handler);
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,15 @@ using namespace mozilla::ipc;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
|
||||
"RequestMode enumeration value should match Necko CORS mode value.");
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
|
||||
"RequestMode enumeration value should match Necko CORS mode value.");
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
|
||||
"RequestMode enumeration value should match Necko CORS mode value.");
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
|
||||
"RequestMode enumeration value should match Necko CORS mode value.");
|
||||
|
||||
struct ServiceWorkerManager::PendingOperation
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
@ -2131,15 +2140,23 @@ class FetchEventRunnable : public WorkerRunnable
|
||||
nsCString mSpec;
|
||||
nsCString mMethod;
|
||||
bool mIsReload;
|
||||
RequestMode mRequestMode;
|
||||
RequestCredentials mRequestCredentials;
|
||||
public:
|
||||
FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
|
||||
nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo)
|
||||
nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
|
||||
bool aIsReload)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
||||
, mInterceptedChannel(aChannel)
|
||||
, mServiceWorker(aServiceWorker)
|
||||
, mClientInfo(aClientInfo)
|
||||
, mIsReload(aIsReload)
|
||||
, mRequestMode(RequestMode::No_cors)
|
||||
// By default we set it to same-origin since normal HTTP fetches always
|
||||
// send credentials to same-origin websites unless explicitly forbidden.
|
||||
, mRequestCredentials(RequestCredentials::Same_origin)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
@ -2157,6 +2174,7 @@ public:
|
||||
nsresult
|
||||
Init()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -2178,8 +2196,35 @@ public:
|
||||
rv = channel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
//TODO(jdm): we should probably include reload-ness in the loadinfo or as a separate load flag
|
||||
mIsReload = false;
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
|
||||
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
uint32_t mode;
|
||||
internalChannel->GetCorsMode(&mode);
|
||||
switch (mode) {
|
||||
case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN:
|
||||
mRequestMode = RequestMode::Same_origin;
|
||||
break;
|
||||
case nsIHttpChannelInternal::CORS_MODE_NO_CORS:
|
||||
mRequestMode = RequestMode::No_cors;
|
||||
break;
|
||||
case nsIHttpChannelInternal::CORS_MODE_CORS:
|
||||
case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT:
|
||||
mRequestMode = RequestMode::Cors;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected CORS mode");
|
||||
}
|
||||
|
||||
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
|
||||
mRequestCredentials = RequestCredentials::Omit;
|
||||
} else {
|
||||
bool includeCrossOrigin;
|
||||
internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
|
||||
if (includeCrossOrigin) {
|
||||
mRequestCredentials = RequestCredentials::Include;
|
||||
}
|
||||
}
|
||||
|
||||
rv = httpChannel->VisitRequestHeaders(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -2244,7 +2289,9 @@ private:
|
||||
reqInit.mHeaders.Value().SetAsHeaders() = headers;
|
||||
|
||||
//TODO(jdm): set request body
|
||||
//TODO(jdm): set request same-origin mode and credentials
|
||||
|
||||
reqInit.mMode.Construct(mRequestMode);
|
||||
reqInit.mCredentials.Construct(mRequestCredentials);
|
||||
|
||||
ErrorResult rv;
|
||||
nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, rv);
|
||||
@ -2280,7 +2327,8 @@ private:
|
||||
NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel)
|
||||
ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel,
|
||||
bool aIsReload)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
nsCOMPtr<nsISupports> serviceWorker;
|
||||
@ -2330,7 +2378,7 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne
|
||||
|
||||
// clientInfo is null if we don't have a controlled document
|
||||
nsRefPtr<FetchEventRunnable> event =
|
||||
new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo);
|
||||
new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo, aIsReload);
|
||||
rv = event->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
function fetch(name, onload, onerror, headers) {
|
||||
function fetchXHR(name, onload, onerror, headers) {
|
||||
expectAsyncResult();
|
||||
|
||||
onload = onload || function() {
|
||||
@ -6,7 +6,7 @@ function fetch(name, onload, onerror, headers) {
|
||||
finish();
|
||||
};
|
||||
onerror = onerror || function() {
|
||||
my_ok(false, "XHR load should be intercepted successfully");
|
||||
my_ok(false, "XHR load for " + name + " should be intercepted successfully");
|
||||
finish();
|
||||
};
|
||||
|
||||
@ -21,52 +21,52 @@ function fetch(name, onload, onerror, headers) {
|
||||
x.send();
|
||||
}
|
||||
|
||||
fetch('synthesized.txt', function(xhr) {
|
||||
fetchXHR('synthesized.txt', function(xhr) {
|
||||
my_ok(xhr.status == 200, "load should be successful");
|
||||
my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('test-respondwith-response.txt', function(xhr) {
|
||||
fetchXHR('test-respondwith-response.txt', function(xhr) {
|
||||
my_ok(xhr.status == 200, "test-respondwith-response load should be successful");
|
||||
my_ok(xhr.responseText == "test-respondwith-response response body", "load should have response");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('synthesized-404.txt', function(xhr) {
|
||||
fetchXHR('synthesized-404.txt', function(xhr) {
|
||||
my_ok(xhr.status == 404, "load should 404");
|
||||
my_ok(xhr.responseText == "synthesized response body", "404 load should have synthesized response");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('synthesized-headers.txt', function(xhr) {
|
||||
fetchXHR('synthesized-headers.txt', function(xhr) {
|
||||
my_ok(xhr.status == 200, "load should be successful");
|
||||
my_ok(xhr.getResponseHeader("X-Custom-Greeting") === "Hello", "custom header should be set");
|
||||
my_ok(xhr.responseText == "synthesized response body", "custom header load should have synthesized response");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('ignored.txt', function(xhr) {
|
||||
fetchXHR('ignored.txt', function(xhr) {
|
||||
my_ok(xhr.status == 404, "load should be uninterrupted");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('rejected.txt', null, function(xhr) {
|
||||
fetchXHR('rejected.txt', null, function(xhr) {
|
||||
my_ok(xhr.status == 0, "load should not complete");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('nonresponse.txt', null, function(xhr) {
|
||||
fetchXHR('nonresponse.txt', null, function(xhr) {
|
||||
my_ok(xhr.status == 0, "load should not complete");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('nonresponse2.txt', null, function(xhr) {
|
||||
fetchXHR('nonresponse2.txt', null, function(xhr) {
|
||||
my_ok(xhr.status == 0, "load should not complete");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('headers.txt', function(xhr) {
|
||||
fetchXHR('headers.txt', function(xhr) {
|
||||
my_ok(xhr.status == 200, "load should be successful");
|
||||
my_ok(xhr.responseText == "1", "request header checks should have passed");
|
||||
finish();
|
||||
@ -80,7 +80,7 @@ expectedUncompressedResponse += "\n";
|
||||
|
||||
// ServiceWorker does not intercept, at which point the network request should
|
||||
// be correctly decoded.
|
||||
fetch('deliver-gzip.sjs', function(xhr) {
|
||||
fetchXHR('deliver-gzip.sjs', function(xhr) {
|
||||
my_ok(xhr.status == 200, "network gzip load should be successful");
|
||||
my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response.");
|
||||
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip.");
|
||||
@ -88,7 +88,7 @@ fetch('deliver-gzip.sjs', function(xhr) {
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('hello.gz', function(xhr) {
|
||||
fetchXHR('hello.gz', function(xhr) {
|
||||
my_ok(xhr.status == 200, "gzip load should be successful");
|
||||
my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
|
||||
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
|
||||
@ -96,10 +96,74 @@ fetch('hello.gz', function(xhr) {
|
||||
finish();
|
||||
});
|
||||
|
||||
fetch('hello-after-extracting.gz', function(xhr) {
|
||||
fetchXHR('hello-after-extracting.gz', function(xhr) {
|
||||
my_ok(xhr.status == 200, "gzip load should be successful");
|
||||
my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
|
||||
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
|
||||
my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetchXHR('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*', function(xhr) {
|
||||
my_ok(xhr.status == 200, "cross origin load with correct headers should be successful");
|
||||
my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out");
|
||||
finish();
|
||||
});
|
||||
|
||||
expectAsyncResult();
|
||||
fetch('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*')
|
||||
.then(function(res) {
|
||||
my_ok(res.ok, "Valid CORS request should receive valid response");
|
||||
my_ok(res.type == "cors", "Response type should be CORS");
|
||||
res.text().then(function(body) {
|
||||
my_ok(body === "<res>hello pass</res>\n", "cors response body should match");
|
||||
finish();
|
||||
});
|
||||
}, function(e) {
|
||||
my_ok(false, "CORS Fetch failed");
|
||||
finish();
|
||||
});
|
||||
|
||||
expectAsyncResult();
|
||||
fetch('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200', { mode: 'no-cors' })
|
||||
.then(function(res) {
|
||||
my_ok(res.type == "opaque", "Response type should be opaque");
|
||||
my_ok(res.status == 0, "Status should be 0");
|
||||
res.text().then(function(body) {
|
||||
my_ok(body === "", "opaque response body should be empty");
|
||||
finish();
|
||||
});
|
||||
}, function(e) {
|
||||
my_ok(false, "no-cors Fetch failed");
|
||||
finish();
|
||||
});
|
||||
|
||||
expectAsyncResult();
|
||||
fetch('opaque-on-same-origin')
|
||||
.then(function(res) {
|
||||
my_ok(false, "intercepted opaque response for non no-cors request should fail.");
|
||||
finish();
|
||||
}, function(e) {
|
||||
my_ok(true, "intercepted opaque response for non no-cors request should fail.");
|
||||
finish();
|
||||
});
|
||||
|
||||
expectAsyncResult();
|
||||
fetch('http://example.com/opaque-no-cors', { mode: "no-cors" })
|
||||
.then(function(res) {
|
||||
my_ok(res.type == "opaque", "intercepted opaque response for no-cors request should have type opaque.");
|
||||
finish();
|
||||
}, function(e) {
|
||||
my_ok(false, "intercepted opaque response for no-cors request should pass.");
|
||||
finish();
|
||||
});
|
||||
|
||||
expectAsyncResult();
|
||||
fetch('http://example.com/cors-for-no-cors', { mode: "no-cors" })
|
||||
.then(function(res) {
|
||||
my_ok(res.type == "opaque", "intercepted non-opaque response for no-cors request should resolve to opaque response.");
|
||||
finish();
|
||||
}, function(e) {
|
||||
my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail.");
|
||||
finish();
|
||||
});
|
||||
|
@ -24,7 +24,7 @@
|
||||
document.currentScript == document.getElementById('intercepted-script');
|
||||
}
|
||||
|
||||
function fetch(name, onload, onerror, headers) {
|
||||
function fetchXHR(name, onload, onerror, headers) {
|
||||
gExpected++;
|
||||
|
||||
onload = onload || function() {
|
||||
@ -135,7 +135,7 @@
|
||||
} else if (e.data == "expect") {
|
||||
gExpected++;
|
||||
} else if (e.data.type == "ok") {
|
||||
my_ok(e.data.value, e.data.msg);
|
||||
my_ok(e.data.value, "Fetch test on worker: " + e.data.msg);
|
||||
}
|
||||
};
|
||||
worker.onerror = function() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
var seenIndex = false;
|
||||
|
||||
onfetch = function(ev) {
|
||||
if (ev.request.url.contains("synthesized.txt")) {
|
||||
ev.respondWith(Promise.resolve(
|
||||
@ -115,4 +117,47 @@ onfetch = function(ev) {
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
else if (ev.request.url.contains('opaque-on-same-origin')) {
|
||||
var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200';
|
||||
ev.respondWith(fetch(url, { mode: 'no-cors' }));
|
||||
}
|
||||
|
||||
else if (ev.request.url.contains('opaque-no-cors')) {
|
||||
if (ev.request.mode != "no-cors") {
|
||||
ev.respondWith(Promise.reject());
|
||||
return;
|
||||
}
|
||||
|
||||
var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200';
|
||||
ev.respondWith(fetch(url, { mode: ev.request.mode }));
|
||||
}
|
||||
|
||||
else if (ev.request.url.contains('cors-for-no-cors')) {
|
||||
if (ev.request.mode != "no-cors") {
|
||||
ev.respondWith(Promise.reject());
|
||||
return;
|
||||
}
|
||||
|
||||
var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*';
|
||||
ev.respondWith(fetch(url));
|
||||
}
|
||||
|
||||
else if (ev.request.url.contains('example.com')) {
|
||||
ev.respondWith(fetch(ev.request));
|
||||
}
|
||||
|
||||
else if (ev.request.url.contains("index.html")) {
|
||||
if (seenIndex) {
|
||||
var body = "<script>" +
|
||||
"opener.postMessage({status: 'ok', result: " + ev.isReload + "," +
|
||||
"message: 'reload status should be indicated'}, '*');" +
|
||||
"opener.postMessage({status: 'done'}, '*');" +
|
||||
"</script>";
|
||||
ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}}));
|
||||
} else {
|
||||
seenIndex = true;
|
||||
ev.respondWith(fetch(ev.request.url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,13 +27,19 @@
|
||||
|
||||
function testController() {
|
||||
var p = new Promise(function(resolve, reject) {
|
||||
var reloaded = false;
|
||||
window.onmessage = function(e) {
|
||||
if (e.data.status == "ok") {
|
||||
ok(e.data.result, e.data.message);
|
||||
} else if (e.data.status == "done") {
|
||||
window.onmessage = null;
|
||||
w.close();
|
||||
resolve();
|
||||
if (reloaded) {
|
||||
window.onmessage = null;
|
||||
w.close();
|
||||
resolve();
|
||||
} else {
|
||||
w.location.reload();
|
||||
reloaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -46,7 +46,6 @@
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.fetch.enabled", true],
|
||||
["dom.caches.enabled", true]
|
||||
]}, runTest);
|
||||
};
|
||||
|
@ -46,7 +46,6 @@
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.fetch.enabled", true],
|
||||
["dom.caches.enabled", true]
|
||||
]}, runTest);
|
||||
};
|
||||
|
@ -119,6 +119,7 @@ static const char *sExtensionNames[] = {
|
||||
"GL_EXT_read_format_bgra",
|
||||
"GL_EXT_robustness",
|
||||
"GL_EXT_sRGB",
|
||||
"GL_EXT_sRGB_write_control",
|
||||
"GL_EXT_shader_texture_lod",
|
||||
"GL_EXT_texture3D",
|
||||
"GL_EXT_texture_compression_dxt1",
|
||||
|
@ -119,7 +119,8 @@ enum class GLFeature {
|
||||
renderbuffer_color_float,
|
||||
renderbuffer_color_half_float,
|
||||
robustness,
|
||||
sRGB,
|
||||
sRGB_framebuffer,
|
||||
sRGB_texture,
|
||||
sampler_objects,
|
||||
standard_derivatives,
|
||||
texture_3D,
|
||||
@ -415,6 +416,7 @@ public:
|
||||
EXT_read_format_bgra,
|
||||
EXT_robustness,
|
||||
EXT_sRGB,
|
||||
EXT_sRGB_write_control,
|
||||
EXT_shader_texture_lod,
|
||||
EXT_texture3D,
|
||||
EXT_texture_compression_dxt1,
|
||||
@ -1037,8 +1039,16 @@ public:
|
||||
}
|
||||
|
||||
BeforeGLReadCall();
|
||||
raw_fCopyTexImage2D(target, level, internalformat,
|
||||
x, y, width, height, border);
|
||||
bool didCopyTexImage2D = false;
|
||||
if (mScreen) {
|
||||
didCopyTexImage2D = mScreen->CopyTexImage2D(target, level, internalformat, x,
|
||||
y, width, height, border);
|
||||
}
|
||||
|
||||
if (!didCopyTexImage2D) {
|
||||
raw_fCopyTexImage2D(target, level, internalformat, x, y, width, height,
|
||||
border);
|
||||
}
|
||||
AfterGLReadCall();
|
||||
}
|
||||
|
||||
@ -1932,6 +1942,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
friend class SharedSurface_IOSurface;
|
||||
|
||||
void raw_fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
|
||||
{
|
||||
BEFORE_GL_CALL;
|
||||
|
@ -3,7 +3,6 @@
|
||||
* 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 "GLContext.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
@ -451,12 +450,24 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
||||
}
|
||||
},
|
||||
{
|
||||
"sRGB",
|
||||
"sRGB_framebuffer",
|
||||
GLVersion::GL3,
|
||||
GLESVersion::ES3,
|
||||
GLContext::ARB_framebuffer_sRGB,
|
||||
{
|
||||
GLContext::EXT_framebuffer_sRGB,
|
||||
GLContext::EXT_sRGB_write_control,
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"sRGB_texture",
|
||||
GLVersion::GL2_1,
|
||||
GLESVersion::ES3,
|
||||
GLContext::Extension_None,
|
||||
{
|
||||
GLContext::EXT_sRGB,
|
||||
GLContext::EXT_texture_sRGB,
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
@ -653,11 +664,10 @@ ProfileVersionForFeature(GLFeature feature, ContextProfile profile)
|
||||
|
||||
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
|
||||
|
||||
if (profile == ContextProfile::OpenGLES) {
|
||||
return (uint32_t) featureInfo.mOpenGLESVersion;
|
||||
}
|
||||
if (profile == ContextProfile::OpenGLES)
|
||||
return (uint32_t)featureInfo.mOpenGLESVersion;
|
||||
|
||||
return (uint32_t) featureInfo.mOpenGLVersion;
|
||||
return (uint32_t)featureInfo.mOpenGLVersion;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -691,72 +701,49 @@ GLContext::GetFeatureName(GLFeature feature)
|
||||
return GetFeatureInfo(feature).mName;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanReadSRGBFromFBOTexture(GLContext* gl)
|
||||
{
|
||||
if (!gl->WorkAroundDriverBugs())
|
||||
return true;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Bug 843668:
|
||||
// MacOSX 10.6 reports to support EXT_framebuffer_sRGB and
|
||||
// EXT_texture_sRGB but fails to convert from sRGB to linear
|
||||
// when writing to an sRGB texture attached to an FBO.
|
||||
if (!nsCocoaFeatures::OnLionOrLater()) {
|
||||
return false;
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GLContext::InitFeatures()
|
||||
{
|
||||
for (size_t feature_index = 0; feature_index < size_t(GLFeature::EnumMax); feature_index++)
|
||||
{
|
||||
GLFeature feature = GLFeature(feature_index);
|
||||
for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); featureId++) {
|
||||
GLFeature feature = GLFeature(featureId);
|
||||
|
||||
if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion)) {
|
||||
mAvailableFeatures[feature_index] = true;
|
||||
mAvailableFeatures[featureId] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
mAvailableFeatures[feature_index] = false;
|
||||
mAvailableFeatures[featureId] = false;
|
||||
|
||||
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
|
||||
|
||||
if (IsExtensionSupported(featureInfo.mARBExtensionWithoutARBSuffix))
|
||||
{
|
||||
mAvailableFeatures[feature_index] = true;
|
||||
if (IsExtensionSupported(featureInfo.mARBExtensionWithoutARBSuffix)) {
|
||||
mAvailableFeatures[featureId] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = 0; true; j++)
|
||||
{
|
||||
MOZ_ASSERT(j < kMAX_EXTENSION_GROUP_SIZE, "kMAX_EXTENSION_GROUP_SIZE too small");
|
||||
for (size_t j = 0; true; j++) {
|
||||
MOZ_ASSERT(j < kMAX_EXTENSION_GROUP_SIZE,
|
||||
"kMAX_EXTENSION_GROUP_SIZE too small");
|
||||
|
||||
if (featureInfo.mExtensions[j] == GLContext::Extensions_End) {
|
||||
if (featureInfo.mExtensions[j] == GLContext::Extensions_End)
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsExtensionSupported(featureInfo.mExtensions[j])) {
|
||||
mAvailableFeatures[feature_index] = true;
|
||||
mAvailableFeatures[featureId] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bug 843668: Work around limitation of the feature system.
|
||||
// For sRGB support under OpenGL to match OpenGL ES spec, check for both
|
||||
// EXT_texture_sRGB and EXT_framebuffer_sRGB is required.
|
||||
const bool aresRGBExtensionsAvailable =
|
||||
IsExtensionSupported(EXT_texture_sRGB) &&
|
||||
(IsExtensionSupported(ARB_framebuffer_sRGB) ||
|
||||
IsExtensionSupported(EXT_framebuffer_sRGB));
|
||||
|
||||
mAvailableFeatures[size_t(GLFeature::sRGB)] =
|
||||
aresRGBExtensionsAvailable &&
|
||||
CanReadSRGBFromFBOTexture(this);
|
||||
if (WorkAroundDriverBugs()) {
|
||||
#ifdef XP_MACOSX
|
||||
// MacOSX 10.6 reports to support EXT_framebuffer_sRGB and EXT_texture_sRGB but
|
||||
// fails to convert from sRGB to linear when reading from an sRGB texture attached
|
||||
// to an FBO. (bug 843668)
|
||||
if (!nsCocoaFeatures::OnLionOrLater())
|
||||
MarkUnsupported(GLFeature::sRGB_framebuffer);
|
||||
#endif // XP_MACOSX
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -766,20 +753,19 @@ GLContext::MarkUnsupported(GLFeature feature)
|
||||
|
||||
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
|
||||
|
||||
for (size_t i = 0; true; i++)
|
||||
{
|
||||
for (size_t i = 0; true; i++) {
|
||||
MOZ_ASSERT(i < kMAX_EXTENSION_GROUP_SIZE, "kMAX_EXTENSION_GROUP_SIZE too small");
|
||||
|
||||
if (featureInfo.mExtensions[i] == GLContext::Extensions_End) {
|
||||
if (featureInfo.mExtensions[i] == GLContext::Extensions_End)
|
||||
break;
|
||||
}
|
||||
|
||||
MarkExtensionUnsupported(featureInfo.mExtensions[i]);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!IsSupported(feature), "GLContext::MarkUnsupported has failed!");
|
||||
|
||||
NS_WARNING(nsPrintfCString("%s marked as unsupported", GetFeatureName(feature)).get());
|
||||
NS_WARNING(nsPrintfCString("%s marked as unsupported",
|
||||
GetFeatureName(feature)).get());
|
||||
}
|
||||
|
||||
} /* namespace gl */
|
||||
|
@ -313,6 +313,23 @@ GLScreenBuffer::BeforeReadCall()
|
||||
AssureBlitted();
|
||||
}
|
||||
|
||||
bool
|
||||
GLScreenBuffer::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
|
||||
GLint y, GLsizei width, GLsizei height, GLint border)
|
||||
{
|
||||
SharedSurface* surf;
|
||||
if (GetReadFB() == 0) {
|
||||
surf = SharedSurf();
|
||||
} else {
|
||||
surf = mGL->mFBOMapping[GetReadFB()];
|
||||
}
|
||||
if (surf) {
|
||||
return surf->CopyTexImage2D(target, level, internalformat, x, y, width, height, border);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GLScreenBuffer::ReadPixels(GLint x, GLint y,
|
||||
GLsizei width, GLsizei height,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user