merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-06-18 15:13:19 +02:00
commit b8a0bf4eb3
213 changed files with 4015 additions and 2224 deletions

View File

@ -516,7 +516,7 @@ GetClosestInterestingAccessible(id anObject)
struct RoleDescrMap
{
NSString* role;
const nsString& description;
const nsString description;
};
static const RoleDescrMap sRoleDescrMap[] = {

View File

@ -350,6 +350,11 @@ pref("dom.w3c_touch_events.enabled", 1);
pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
// W3C draft pointer events
pref("dom.w3c_pointer_events.enabled", false);
// W3C touch-action css property (related to touch and pointer events)
pref("layout.css.touch_action.enabled", false);
#ifdef MOZ_SAFE_BROWSING
// Safe browsing does nothing unless this pref is set
pref("browser.safebrowsing.enabled", false);

View File

@ -18,3 +18,8 @@ pref("devtools.toolbox.sidebar.width", 800);
pref("browser.tabs.remote.autostart", false);
pref("browser.tabs.remote.autostart.1", false);
pref("browser.tabs.remote.autostart.2", false);
// W3C draft pointer events
pref("dom.w3c_pointer_events.enabled", false);
// W3C touch-action css property (related to touch and pointer events)
pref("layout.css.touch_action.enabled", false);

View File

@ -505,10 +505,17 @@ pref("dom.disable_window_move_resize", false);
// prevent JS from monkeying with window focus, etc
pref("dom.disable_window_flip", true);
// Disable touch events on Desktop Firefox by default until they are properly
// supported (bug 736048)
// Disable touch events on Desktop Firefox by default
// until they are properly supported (bug 736048)
pref("dom.w3c_touch_events.enabled", 0);
#ifdef NIGHTLY_BUILD
// W3C draft pointer events
pref("dom.w3c_pointer_events.enabled", true);
// W3C touch-action css property (related to touch and pointer events)
pref("layout.css.touch_action.enabled", true);
#endif
// popups.policy 1=allow,2=reject
pref("privacy.popups.policy", 1);
pref("privacy.popups.usecustom", true);

View File

@ -44,6 +44,9 @@ let tests = [
});
let loopDoc = document.getElementById("loop-notification-panel").children[0].contentDocument;
yield waitForConditionPromise(() => {
return loopDoc.readyState == 'complete';
}, "Loop notification panel document should be fully loaded.");
let gettingStartedButton = loopDoc.getElementById("fte-button");
ok(gettingStartedButton, "Getting Started button should be found");

23
dom/base/ChromeUtils.cpp Normal file
View File

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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 "ChromeUtils.h"
#include "mozilla/BasePrincipal.h"
namespace mozilla {
namespace dom {
/* static */ void
ChromeUtils::OriginAttributesToCookieJar(GlobalObject& aGlobal,
const OriginAttributesDictionary& aAttrs,
nsCString& aCookieJar)
{
OriginAttributes attrs(aAttrs);
attrs.CookieJar(aCookieJar);
}
} // namespace dom
} // namespace mozilla

52
dom/base/ChromeUtils.h Normal file
View File

@ -0,0 +1,52 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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_ChromeUtils__
#define mozilla_dom_ChromeUtils__
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ChromeUtilsBinding.h"
#include "mozilla/dom/ThreadSafeChromeUtilsBinding.h"
#include "mozilla/ErrorResult.h"
namespace mozilla {
namespace devtools {
class HeapSnapshot;
}
namespace dom {
class ThreadSafeChromeUtils
{
public:
// Implemented in toolkit/devtools/server/HeapSnapshot.cpp
static void SaveHeapSnapshot(GlobalObject& global,
JSContext* cx,
const nsAString& filePath,
const HeapSnapshotBoundaries& boundaries,
ErrorResult& rv);
// Implemented in toolkit/devtools/server/HeapSnapshot.cpp
static already_AddRefed<devtools::HeapSnapshot> ReadHeapSnapshot(GlobalObject& global,
JSContext* cx,
const nsAString& filePath,
ErrorResult& rv);
};
class ChromeUtils : public ThreadSafeChromeUtils
{
public:
static void
OriginAttributesToCookieJar(dom::GlobalObject& aGlobal,
const dom::OriginAttributesDictionary& aAttrs,
nsCString& aCookieJar);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ChromeUtils__

View File

@ -14,6 +14,7 @@
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/FileBinding.h"
#include <algorithm>
#include "nsPIDOMWindow.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -154,6 +154,7 @@ EXPORTS.mozilla.dom += [
'BarProps.h',
'BlobSet.h',
'ChildIterator.h',
'ChromeUtils.h',
'Comment.h',
'Console.h',
'DirectionalityUtils.h',
@ -209,6 +210,7 @@ UNIFIED_SOURCES += [
'Attr.cpp',
'BarProps.cpp',
'ChildIterator.cpp',
'ChromeUtils.cpp',
'Comment.cpp',
'Console.cpp',
'Crypto.cpp',

View File

@ -4,7 +4,7 @@
* 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/. */
/*
/*
* Implementation of the "@mozilla.org/layout/content-policy;1" contract.
*/
@ -15,11 +15,15 @@
#include "nsContentPolicyUtils.h"
#include "nsContentPolicy.h"
#include "nsIURI.h"
#include "nsIDocShell.h"
#include "nsIDOMNode.h"
#include "nsIDOMWindow.h"
#include "nsIContent.h"
#include "nsILoadContext.h"
#include "nsCOMArray.h"
using mozilla::LogLevel;
NS_IMPL_ISUPPORTS(nsContentPolicy, nsIContentPolicy)
static PRLogModuleInfo* gConPolLog;

View File

@ -7575,7 +7575,7 @@ nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext,
}
nsresult
nsContentUtils::SendKeyEvent(nsCOMPtr<nsIWidget> aWidget,
nsContentUtils::SendKeyEvent(nsIWidget* aWidget,
const nsAString& aType,
int32_t aKeyCode,
int32_t aCharCode,

View File

@ -2363,7 +2363,7 @@ public:
* Synthesize a key event to the given widget
* (see nsIDOMWindowUtils.sendKeyEvent).
*/
static nsresult SendKeyEvent(nsCOMPtr<nsIWidget> aWidget,
static nsresult SendKeyEvent(nsIWidget* aWidget,
const nsAString& aType,
int32_t aKeyCode,
int32_t aCharCode,

View File

@ -933,6 +933,9 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
ourFrameFrame->EndSwapDocShells(otherFrame);
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourDoc->FlushPendingNotifications(Flush_Layout);
otherDoc->FlushPendingNotifications(Flush_Layout);

View File

@ -1785,8 +1785,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
}
} else {
// We're going to run these against some non-global scope.
options.setHasPollutedScope(true);
if (!JS::Compile(cx, options, srcBuf, &script)) {
if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
return;
}
}

View File

@ -59,16 +59,16 @@ class MOZ_STACK_CLASS nsViewportInfo
ConstrainViewportValues();
}
mozilla::CSSToScreenScale GetDefaultZoom() { return mDefaultZoom; }
mozilla::CSSToScreenScale GetDefaultZoom() const { return mDefaultZoom; }
void SetDefaultZoom(const mozilla::CSSToScreenScale& aDefaultZoom);
mozilla::CSSToScreenScale GetMinZoom() { return mMinZoom; }
mozilla::CSSToScreenScale GetMaxZoom() { return mMaxZoom; }
mozilla::CSSToScreenScale GetMinZoom() const { return mMinZoom; }
mozilla::CSSToScreenScale GetMaxZoom() const { return mMaxZoom; }
mozilla::CSSSize GetSize() { return mSize; }
mozilla::CSSSize GetSize() const { return mSize; }
bool IsAutoSizeEnabled() { return mAutoSize; }
bool IsZoomAllowed() { return mAllowZoom; }
bool IsDoubleTapZoomAllowed() { return mAllowDoubleTapZoom; }
bool IsAutoSizeEnabled() const { return mAutoSize; }
bool IsZoomAllowed() const { return mAllowZoom; }
bool IsDoubleTapZoomAllowed() const { return mAllowDoubleTapZoom; }
void SetAllowDoubleTapZoom(bool aAllowDoubleTapZoom) { mAllowDoubleTapZoom = aAllowDoubleTapZoom; }

View File

@ -270,8 +270,6 @@ DOMInterfaces = {
# The codegen is dumb, and doesn't understand that this interface is only a
# collection of static methods, so we have this `concrete: False` hack.
'concrete': False,
'nativeType': 'mozilla::devtools::ChromeUtils',
'implicitJSContext': ['readHeapSnapshot', 'saveHeapSnapshot']
},
'ChromeWindow': {
@ -1273,6 +1271,14 @@ DOMInterfaces = {
'wrapperCache': False
},
'ThreadSafeChromeUtils': {
# The codegen is dumb, and doesn't understand that this interface is only a
# collection of static methods, so we have this `concrete: False` hack.
'concrete': False,
'headerFile': 'mozilla/dom/ChromeUtils.h',
'implicitJSContext': ['readHeapSnapshot', 'saveHeapSnapshot']
},
'TimeRanges': {
'wrapperCache': False
},

View File

@ -2732,9 +2732,14 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
if needInterfacePrototypeObject:
protoClass = "&PrototypeClass.mBase"
protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
parentProto = "parentProto"
getParentProto = CGGeneric(getParentProto)
else:
protoClass = "nullptr"
protoCache = "nullptr"
parentProto = "nullptr"
getParentProto = None
if needInterfaceObject:
interfaceClass = "&InterfaceObjectClass.mBase"
interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
@ -2762,7 +2767,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
"""
JS::Heap<JSObject*>* protoCache = ${protoCache};
JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,
dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
${protoClass}, protoCache,
constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
interfaceCache,
@ -2771,6 +2776,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
${name}, aDefineOnGlobal);
""",
protoClass=protoClass,
parentProto=parentProto,
protoCache=protoCache,
interfaceClass=interfaceClass,
constructHookHolder=constructHookHolder,
@ -2849,7 +2855,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
defineAliases = None
return CGList(
[CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
[getParentProto, CGGeneric(getConstructorProto), initIds,
prefCache, CGGeneric(call), defineAliases, createUnforgeableHolder, setUnforgeableHolder],
"\n").define()

View File

@ -686,14 +686,18 @@ class Descriptor(DescriptorProvider):
def needsHeaderInclude(self):
"""
An interface doesn't need a header file if it is not concrete,
not pref-controlled, has no prototype object, and has no
static methods or attributes.
An interface doesn't need a header file if it is not concrete, not
pref-controlled, has no prototype object, has no static methods or
attributes and has no parent. The parent matters because we assert
things about refcounting that depend on the actual underlying type if we
have a parent.
"""
return (self.interface.isExternal() or self.concrete or
self.interface.hasInterfacePrototypeObject() or
any((m.isAttr() or m.isMethod()) and m.isStatic() for m
in self.interface.members))
in self.interface.members) or
self.interface.parent)
def hasThreadChecks(self):
return ((self.isExposedConditionally() and

View File

@ -32,6 +32,7 @@ using ScreenIntSize from "Units.h";
using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
using FrameMetrics::ViewID from "FrameMetrics.h";
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
@ -446,8 +447,8 @@ parent:
* The zoom controller code lives on the parent side and so this allows it to
* have up-to-date zoom constraints.
*/
UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId, bool aIsRoot,
ZoomConstraints aConstraints);
UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId,
MaybeZoomConstraints aConstraints);
/**
* Brings up the auth prompt dialog.

View File

@ -311,19 +311,8 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, GetInnerSize());
uint32_t presShellId = 0;
mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
APZCCallbackHelper::GetOrCreateScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId);
if (scrollIdentifiersValid) {
ZoomConstraints constraints(
viewportInfo.IsZoomAllowed(),
viewportInfo.IsDoubleTapZoomAllowed(),
ConvertScaleForRoot(viewportInfo.GetMinZoom()),
ConvertScaleForRoot(viewportInfo.GetMaxZoom()));
DoUpdateZoomConstraints(presShellId,
viewId,
/* isRoot = */ true,
constraints);
}
float screenW = GetInnerSize().width;
float screenH = GetInnerSize().height;
@ -470,25 +459,6 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
// displayport, so we start with async painting.
mLastRootMetrics = ProcessUpdateFrame(metrics);
if (viewportInfo.IsZoomAllowed() && scrollIdentifiersValid) {
// If the CSS viewport is narrower than the screen (i.e. width <= device-width)
// then we disable double-tap-to-zoom behaviour.
bool allowDoubleTapZoom = (viewport.width > screenW / metrics.GetDevPixelsPerCSSPixel().scale);
if (allowDoubleTapZoom != viewportInfo.IsDoubleTapZoomAllowed()) {
viewportInfo.SetAllowDoubleTapZoom(allowDoubleTapZoom);
ZoomConstraints constraints(
viewportInfo.IsZoomAllowed(),
viewportInfo.IsDoubleTapZoomAllowed(),
ConvertScaleForRoot(viewportInfo.GetMinZoom()),
ConvertScaleForRoot(viewportInfo.GetMaxZoom()));
DoUpdateZoomConstraints(presShellId,
viewId,
/* isRoot = */ true,
constraints);
}
}
return true;
}
@ -1077,12 +1047,17 @@ TabChild::OnSecurityChange(nsIWebProgress* aWebProgress,
bool
TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const bool& aIsRoot,
const ZoomConstraints& aConstraints)
const Maybe<ZoomConstraints>& aConstraints)
{
if (sPreallocatedTab == this) {
// If we're the preallocated tab, bail out because doing IPC will crash.
// Once we get used for something we'll get another zoom constraints update
// and all will be well.
return true;
}
return SendUpdateZoomConstraints(aPresShellId,
aViewId,
aIsRoot,
aConstraints);
}
@ -2133,8 +2108,7 @@ TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifier
// Note: there is nothing to do with the modifiers here, as we are not
// synthesizing any sort of mouse event.
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid,
GetPresShellResolution());
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
nsString data;
data.AppendLiteral("{ \"x\" : ");
data.AppendFloat(point.x);
@ -2151,7 +2125,7 @@ bool
TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
{
if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid, GetPresShellResolution());
mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
}
return true;
}
@ -2161,7 +2135,7 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
{
if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
aInputBlockId, GetPresShellResolution());
aInputBlockId);
}
return true;
}
@ -2406,17 +2380,6 @@ TabChild::CancelTapTracking()
mTapHoldTimer = nullptr;
}
float
TabChild::GetPresShellResolution() const
{
nsCOMPtr<nsIDocument> document(GetDocument());
nsIPresShell* shell = document->GetShell();
if (!shell) {
return 1.0f;
}
return shell->GetResolution();
}
bool
TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -2429,7 +2392,7 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
localEvent.widget = mPuppetWidget;
APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
mPuppetWidget->GetDefaultScale(), GetPresShellResolution());
mPuppetWidget->GetDefaultScale());
if (localEvent.message == NS_TOUCH_START && AsyncPanZoomEnabled()) {
if (gfxPrefs::TouchActionEnabled()) {
@ -2576,10 +2539,7 @@ TabChild::RecvKeyEvent(const nsString& aType,
const bool& aPreventDefault)
{
bool ignored = false;
// XXX Why nsContentUtils::SendKeyEvent takes nsCOMPtr<nsIWidget> for the
// first argument? Why not just nsIWidget*?
nsCOMPtr<nsIWidget> widget(mPuppetWidget);
nsContentUtils::SendKeyEvent(widget, aType, aKeyCode, aCharCode,
nsContentUtils::SendKeyEvent(mPuppetWidget, aType, aKeyCode, aCharCode,
aModifiers, aPreventDefault, &ignored);
return true;
}

View File

@ -190,8 +190,7 @@ public:
bool HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize);
virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
const mozilla::layers::FrameMetrics::ViewID& aViewId,
const bool& aIsRoot,
const mozilla::layers::ZoomConstraints& aConstraints) = 0;
const Maybe<mozilla::layers::ZoomConstraints>& aConstraints) = 0;
virtual ScreenIntSize GetInnerSize() = 0;
@ -311,8 +310,7 @@ public:
nsIPrincipal* aPrincipal) override;
virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const bool& aIsRoot,
const ZoomConstraints& aConstraints) override;
const Maybe<ZoomConstraints>& aConstraints) override;
virtual bool RecvLoadURL(const nsCString& aURI,
const BrowserConfiguration& aConfiguration) override;
virtual bool RecvCacheFileDescriptor(const nsString& aPath,
@ -601,9 +599,6 @@ private:
bool HasValidInnerSize();
// Get the pres shell resolution of the document in this tab.
float GetPresShellResolution() const;
void SetTabId(const TabId& aTabId);
ScreenIntRect GetOuterRect();

View File

@ -2740,11 +2740,10 @@ TabParent::RecvZoomToRect(const uint32_t& aPresShellId,
bool
TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const bool& aIsRoot,
const ZoomConstraints& aConstraints)
const MaybeZoomConstraints& aConstraints)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->UpdateZoomConstraints(aPresShellId, aViewId, aIsRoot, aConstraints);
rfp->UpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
}
return true;
}

View File

@ -222,8 +222,7 @@ public:
const CSSRect& aRect) override;
virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
const ViewID& aViewId,
const bool& aIsRoot,
const ZoomConstraints& aConstraints) override;
const MaybeZoomConstraints& aConstraints) override;
virtual bool RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const bool& aPreventDefault) override;

View File

@ -8,6 +8,7 @@
#define AbstractMediaDecoder_h_
#include "mozilla/Attributes.h"
#include "StateMirroring.h"
#include "MediaInfo.h"
#include "nsISupports.h"
#include "nsDataHashtable.h"
@ -71,8 +72,7 @@ public:
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) = 0;
// Return the duration of the media in microseconds.
virtual int64_t GetMediaDuration() = 0;
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() { return nullptr; };
// Sets the duration of the media in microseconds. The MediaDecoder
// fires a durationchange event to its owner (e.g., an HTML audio

View File

@ -304,25 +304,21 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
double MediaDecoder::GetDuration()
{
MOZ_ASSERT(NS_IsMainThread());
if (mInfiniteStream) {
return std::numeric_limits<double>::infinity();
}
if (mDuration >= 0) {
return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S);
}
return std::numeric_limits<double>::quiet_NaN();
return mDuration;
}
int64_t MediaDecoder::GetMediaDuration()
AbstractCanonical<media::NullableTimeUnit>*
MediaDecoder::CanonicalDurationOrNull()
{
NS_ENSURE_TRUE(GetStateMachine(), -1);
return GetStateMachine()->GetDuration();
MOZ_ASSERT(mDecoderStateMachine);
return mDecoderStateMachine->CanonicalDuration();
}
void MediaDecoder::SetInfinite(bool aInfinite)
{
MOZ_ASSERT(NS_IsMainThread());
mInfiniteStream = aInfinite;
DurationChanged();
}
bool MediaDecoder::IsInfinite()
@ -343,7 +339,8 @@ MediaDecoder::MediaDecoder() :
mVolume(AbstractThread::MainThread(), 0.0, "MediaDecoder::mVolume (Canonical)"),
mPlaybackRate(AbstractThread::MainThread(), 1.0, "MediaDecoder::mPlaybackRate (Canonical)"),
mPreservesPitch(AbstractThread::MainThread(), true, "MediaDecoder::mPreservesPitch (Canonical)"),
mDuration(-1),
mDuration(std::numeric_limits<double>::quiet_NaN()),
mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(), "MediaDecoder::mStateMachineDuration (Mirror)"),
mMediaSeekable(true),
mSameOriginMedia(false),
mReentrantMonitor("media.decoder"),
@ -366,6 +363,7 @@ MediaDecoder::MediaDecoder() :
mPausedForPlaybackRateNull(false),
mMinimizePreroll(false),
mMediaTracksConstructed(false),
mFiredMetadataLoaded(false),
mIsDormant(false),
mWasEndedWhenEnteredDormant(false),
mIsHeuristicDormantSupported(
@ -385,6 +383,9 @@ MediaDecoder::MediaDecoder() :
// Initialize watchers.
//
// mDuration
mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
// readyState
mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
mWatchManager.Watch(mNextFrameStatus, &MediaDecoder::UpdateReadyState);
@ -649,17 +650,6 @@ void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
aInfo->HasAudio(), aInfo->HasVideo());
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
// Duration has changed so we should recompute playback rate
UpdatePlaybackRate();
}
if (mDuration == -1) {
SetInfinite(true);
}
mInfo = aInfo.forget();
ConstructMediaTracks();
@ -668,6 +658,7 @@ void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
// our new size.
Invalidate();
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
mFiredMetadataLoaded = true;
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
}
}
@ -868,9 +859,9 @@ double MediaDecoder::ComputePlaybackRate(bool* aReliable)
MOZ_ASSERT(NS_IsMainThread() || OnStateMachineTaskQueue() || OnDecodeTaskQueue());
int64_t length = mResource ? mResource->GetLength() : -1;
if (mDuration >= 0 && length >= 0) {
if (!IsNaN(mDuration) && !mozilla::IsInfinite<double>(mDuration) && length >= 0) {
*aReliable = true;
return length * static_cast<double>(USECS_PER_S) / mDuration;
return length * mDuration;
}
return mPlaybackStatistics->GetRateAtLastStop(aReliable);
}
@ -1077,24 +1068,38 @@ void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisib
}
}
void MediaDecoder::DurationChanged(TimeUnit aNewDuration)
void MediaDecoder::DurationChanged()
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
int64_t oldDuration = mDuration;
mDuration = aNewDuration.ToMicroseconds();
double oldDuration = mDuration;
if (IsInfinite()) {
mDuration = std::numeric_limits<double>::infinity();
} else if (mExplicitDuration.Ref().isSome()) {
mDuration = mExplicitDuration.Ref().ref();
} else if (mStateMachineDuration.Ref().isSome()) {
mDuration = mStateMachineDuration.Ref().ref().ToSeconds();
}
if (mDuration == oldDuration || IsNaN(mDuration)) {
return;
}
DECODER_LOG("Duration changed to %f", mDuration);
// Duration has changed so we should recompute playback rate
UpdatePlaybackRate();
SetInfinite(mDuration == -1);
if (mOwner && oldDuration != mDuration && !IsInfinite()) {
DECODER_LOG("Duration changed to %lld", mDuration);
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
// of whether we should fire durationchange on explicit infinity.
if (mOwner && mFiredMetadataLoaded &&
(!mozilla::IsInfinite<double>(mDuration) || mExplicitDuration.Ref().isSome())) {
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
}
if (CurrentPosition() > aNewDuration.ToMicroseconds()) {
Seek(aNewDuration.ToSeconds(), SeekTarget::Accurate);
if (CurrentPosition() > TimeUnit::FromSeconds(mDuration).ToMicroseconds()) {
Seek(mDuration, SeekTarget::Accurate);
}
}
@ -1256,9 +1261,11 @@ MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
mDecoderStateMachine = aStateMachine;
if (mDecoderStateMachine) {
mStateMachineDuration.Connect(mDecoderStateMachine->CanonicalDuration());
mNextFrameStatus.Connect(mDecoderStateMachine->CanonicalNextFrameStatus());
mCurrentPosition.Connect(mDecoderStateMachine->CanonicalCurrentPosition());
} else {
mStateMachineDuration.DisconnectIfConnected();
mNextFrameStatus.DisconnectIfConnected();
mCurrentPosition.DisconnectIfConnected();
}

View File

@ -402,8 +402,7 @@ public:
// Return the duration of the video in seconds.
virtual double GetDuration();
// Return the duration of the video in seconds.
int64_t GetMediaDuration() final override;
AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
// A media stream is assumed to be infinite if the metadata doesn't
// contain the duration, and range requests are not supported, and
@ -511,10 +510,6 @@ public:
// Returns a weak reference to the media decoder owner.
MediaDecoderOwner* GetMediaOwner() const;
// Called by the state machine to notify the decoder that the duration
// has changed.
void DurationChanged(media::TimeUnit aNewDuration);
bool OnStateMachineTaskQueue() const override;
bool OnDecodeTaskQueue() const override;
@ -888,6 +883,10 @@ protected:
// Return true if the decoder has reached the end of playback
bool IsEnded() const;
// Called by the state machine to notify the decoder that the duration
// has changed.
void DurationChanged();
// State-watching manager.
WatchManager<MediaDecoder> mWatchManager;
@ -943,10 +942,11 @@ public:
AbstractCanonical<bool>* CanonicalPreservesPitch() { return &mPreservesPitch; }
protected:
// Duration of the media resource. Set to -1 if unknown.
// Set when the metadata is loaded. Accessed on the main thread
// only.
int64_t mDuration;
// Official duration of the media resource as observed by script.
double mDuration;
// Duration of the media resource according to the state machine.
Mirror<media::NullableTimeUnit> mStateMachineDuration;
// True if the media is seekable (i.e. supports random access).
bool mMediaSeekable;
@ -996,7 +996,15 @@ protected:
// for MSE.
Canonical<Maybe<double>> mExplicitDuration;
double ExplicitDuration() { return mExplicitDuration.Ref().ref(); }
void SetExplicitDuration(double aValue) { mExplicitDuration.Set(Some(aValue)); }
void SetExplicitDuration(double aValue)
{
mExplicitDuration.Set(Some(aValue));
// We Invoke DurationChanged explicitly, rather than using a watcher, so
// that it takes effect immediately, rather than at the end of the current task.
DurationChanged();
}
public:
AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() { return &mExplicitDuration; }
protected:
@ -1088,6 +1096,9 @@ protected:
// track list, false if all tracks are removed from the track list.
bool mMediaTracksConstructed;
// True if we've already fired metadataloaded.
bool mFiredMetadataLoaded;
// Stores media info, including info of audio tracks and video tracks, should
// only be accessed from main thread.
nsAutoPtr<MediaInfo> mInfo;

View File

@ -15,6 +15,8 @@
#include <stdint.h>
#include <algorithm>
using namespace mozilla::media;
namespace mozilla {
// Un-comment to enable logging of seek bisections.
@ -67,6 +69,7 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
: new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
, mIgnoreAudioOutputFormat(false)
, mStartTime(-1)
, mHitAudioDecodeError(false)
@ -76,6 +79,19 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
, mVideoDiscontinuity(false)
{
MOZ_COUNT_CTOR(MediaDecoderReader);
MOZ_ASSERT(NS_IsMainThread());
// Dispatch initialization that needs to happen on that task queue.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderReader::InitializationTask);
mTaskQueue->Dispatch(r.forget());
}
void
MediaDecoderReader::InitializationTask()
{
if (mDecoder->CanonicalDurationOrNull()) {
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
}
}
MediaDecoderReader::~MediaDecoderReader()
@ -157,12 +173,12 @@ MediaDecoderReader::GetBuffered()
{
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
AutoPinned<MediaResource> stream(mDecoder->GetResource());
int64_t durationUs = 0;
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
durationUs = mDecoder->GetMediaDuration();
if (!mDuration.ReadOnWrongThread().isSome()) {
return TimeIntervals();
}
return GetEstimatedBufferedTimeRanges(stream, durationUs);
return GetEstimatedBufferedTimeRanges(stream, mDuration.ReadOnWrongThread().ref().ToMicroseconds());
}
nsRefPtr<MediaDecoderReader::MetadataPromise>
@ -341,6 +357,8 @@ MediaDecoderReader::Shutdown()
mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
ReleaseMediaResources();
mDuration.DisconnectIfConnected();
nsRefPtr<ShutdownPromise> p;
// Spin down the task queue if necessary. We wait until BreakCycles to null

View File

@ -81,6 +81,12 @@ public:
// destroyed.
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue = nullptr);
// Does any spinup that needs to happen on this task queue. This runs on a
// different thread than Init, and there should not be ordering dependencies
// between the two (even though in practice, Init will always run first right
// now thanks to the tail dispatcher).
void InitializationTask();
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
// on failure.
virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
@ -318,6 +324,9 @@ protected:
// Stores presentation info required for playback.
MediaInfo mInfo;
// Duration, mirrored from the state machine task queue.
Mirror<media::NullableTimeUnit> mDuration;
// Whether we should accept media that we know we can't play
// directly, because they have a number of channel higher than
// what we support.

View File

@ -187,10 +187,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mDelayedScheduler(this),
mState(DECODER_STATE_DECODING_NONE, "MediaDecoderStateMachine::mState"),
mPlayDuration(0),
mEndTime(-1),
mDurationSet(false),
mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"),
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
"MediaDecoderStateMachine::EstimatedDuration (Mirror)"),
"MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
mExplicitDuration(mTaskQueue, Maybe<double>(),
"MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"),
@ -443,9 +442,11 @@ void MediaDecoderStateMachine::SendStreamData()
bool finished =
(!mInfo.HasAudio() || AudioQueue().IsFinished()) &&
(!mInfo.HasVideo() || VideoQueue().IsFinished());
if (mDecoder->IsSameOriginMedia()) {
{
SourceMediaStream* mediaStream = stream->mStream;
StreamTime endPosition = 0;
const bool isSameOrigin = mDecoder->IsSameOriginMedia();
if (!stream->mStreamInitialized) {
if (mInfo.HasAudio()) {
@ -477,6 +478,9 @@ void MediaDecoderStateMachine::SendStreamData()
for (uint32_t i = 0; i < audio.Length(); ++i) {
SendStreamAudio(audio[i], stream, &output);
}
if (!isSameOrigin) {
output.ReplaceWithDisabled();
}
// |mNextAudioTime| is updated as we process each audio sample in
// SendStreamAudio(). This is consistent with how |mNextVideoTime|
// is updated for video samples.
@ -543,6 +547,9 @@ void MediaDecoderStateMachine::SendStreamData()
if (output.GetLastFrame()) {
stream->mEOSVideoCompensation = ZeroDurationAtLastChunk(output);
}
if (!isSameOrigin) {
output.ReplaceWithDisabled();
}
if (output.GetDuration() > 0) {
mediaStream->AppendToTrack(videoTrackId, &output);
}
@ -556,6 +563,9 @@ void MediaDecoderStateMachine::SendStreamData()
stream->mLastVideoImageDisplaySize, &endSegment);
stream->mNextVideoTime += deviation_usec;
MOZ_ASSERT(endSegment.GetDuration() > 0);
if (!isSameOrigin) {
endSegment.ReplaceWithDisabled();
}
mediaStream->AppendToTrack(videoTrackId, &endSegment);
}
mediaStream->EndTrack(videoTrackId);
@ -1381,33 +1391,11 @@ bool MediaDecoderStateMachine::IsRealTime() const
return mRealTime;
}
int64_t MediaDecoderStateMachine::GetDuration()
{
AssertCurrentThreadInMonitor();
if (mEndTime == -1)
return -1;
return mEndTime;
}
int64_t MediaDecoderStateMachine::GetEndTime()
{
if (mEndTime == -1 && mDurationSet) {
return INT64_MAX;
}
return mEndTime;
}
void MediaDecoderStateMachine::RecomputeDuration()
{
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
// We dispatch DurationChanged to the MediaDecoder when the duration changes
// sometime after initialization, unless it has already been fired by the code
// that set the new duration.
bool fireDurationChanged = false;
TimeUnit duration;
if (mExplicitDuration.Ref().isSome()) {
double d = mExplicitDuration.Ref().ref();
@ -1421,7 +1409,6 @@ void MediaDecoderStateMachine::RecomputeDuration()
duration = TimeUnit::FromSeconds(d);
} else if (mEstimatedDuration.Ref().isSome()) {
duration = mEstimatedDuration.Ref().ref();
fireDurationChanged = true;
} else if (mInfo.mMetadataDuration.isSome()) {
duration = mInfo.mMetadataDuration.ref();
} else {
@ -1430,19 +1417,10 @@ void MediaDecoderStateMachine::RecomputeDuration()
if (duration < mObservedDuration.Ref()) {
duration = mObservedDuration;
fireDurationChanged = true;
}
fireDurationChanged = fireDurationChanged && duration.ToMicroseconds() != GetDuration();
MOZ_ASSERT(duration.ToMicroseconds() >= 0);
mEndTime = duration.IsInfinite() ? -1 : duration.ToMicroseconds();
mDurationSet = true;
if (fireDurationChanged) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethodWithArg<TimeUnit>(mDecoder, &MediaDecoder::DurationChanged, duration);
AbstractThread::MainThread()->Dispatch(event.forget());
}
mDuration = Some(duration);
}
void MediaDecoderStateMachine::SetFragmentEndTime(int64_t aEndTime)
@ -1668,11 +1646,11 @@ void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
// While playing an unseekable stream of unknown duration, mEndTime is
// While playing an unseekable stream of unknown duration, mDuration is
// updated (in AdvanceFrame()) as we play. But if data is being downloaded
// faster than played, mEndTime won't reflect the end of playable data
// faster than played, mDuration won't reflect the end of playable data
// since we haven't played the frame at the end of buffered data. So update
// mEndTime here as new data is downloaded to prevent such a lag.
// mDuration here as new data is downloaded to prevent such a lag.
//
// Make sure to only do this if we have a start time, otherwise the reader
// doesn't know how to compute GetBuffered.
@ -1687,7 +1665,7 @@ void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
media::TimeUnit end{buffered.GetEnd(&exists)};
if (exists) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mEndTime = std::max<int64_t>(mEndTime, end.ToMicroseconds());
mDuration = Some(std::max<TimeUnit>(Duration(), end));
}
}
}
@ -1835,7 +1813,7 @@ MediaDecoderStateMachine::InitiateSeek()
mCurrentSeek.Steal(mPendingSeek);
// Bound the seek time to be inside the media range.
int64_t end = GetEndTime();
int64_t end = Duration().ToMicroseconds();
NS_ASSERTION(end != -1, "Should know end time by now");
int64_t seekTime = mCurrentSeek.mTarget.mTime;
seekTime = std::min(seekTime, end);
@ -1876,7 +1854,7 @@ MediaDecoderStateMachine::InitiateSeek()
nsRefPtr<MediaDecoderStateMachine> self = this;
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
GetEndTime())
Duration().ToMicroseconds())
->Then(TaskQueue(), __func__,
[self] (int64_t) -> void {
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
@ -2087,7 +2065,7 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
// If we don't have a duration, GetBuffered is probably not going to produce
// a useful buffered range. Return false here so that we don't get stuck in
// buffering mode for live streams.
if (GetDuration() < 0) {
if (Duration().IsInfinite()) {
return false;
}
@ -2108,12 +2086,12 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
endOfDecodedAudioData = mDecodedAudioEndTime;
}
int64_t endOfDecodedData = std::min(endOfDecodedVideoData, endOfDecodedAudioData);
if (GetDuration() < endOfDecodedData) {
if (Duration().ToMicroseconds() < endOfDecodedData) {
// Our duration is not up to date. No point buffering.
return false;
}
media::TimeInterval interval(media::TimeUnit::FromMicroseconds(endOfDecodedData),
media::TimeUnit::FromMicroseconds(std::min(endOfDecodedData + aUsecs, GetDuration())));
media::TimeUnit::FromMicroseconds(std::min(endOfDecodedData + aUsecs, Duration().ToMicroseconds())));
return endOfDecodedData != INT64_MAX && !buffered.Contains(interval);
}
@ -2202,7 +2180,7 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
// feeding in the CDM, which we need to decode the first frame (and
// thus get the metadata). We could fix this if we could compute the start
// time by demuxing without necessaring decoding.
mNotifyMetadataBeforeFirstFrame = mDurationSet || mReader->IsWaitingOnCDMResource();
mNotifyMetadataBeforeFirstFrame = mDuration.Ref().isSome() || mReader->IsWaitingOnCDMResource();
if (mNotifyMetadataBeforeFirstFrame) {
EnqueueLoadedMetadataEvent();
}
@ -2347,13 +2325,14 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
}
}
MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
(GetDuration() != -1) || mDurationSet,
"Seekable media should have duration");
DECODER_LOG("Media goes from %lld to %lld (duration %lld) "
// If we don't know the duration by this point, we assume infinity, per spec.
if (mDuration.Ref().isNothing()) {
mDuration = Some(TimeUnit::FromInfinity());
}
DECODER_LOG("Media duration %lld, "
"transportSeekable=%d, mediaSeekable=%d",
0, mEndTime, GetDuration(),
mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
Duration().ToMicroseconds(), mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
if (HasAudio() && !HasVideo()) {
// We're playing audio only. We don't need to worry about slow video
@ -2409,7 +2388,7 @@ MediaDecoderStateMachine::SeekCompleted()
// Setup timestamp state.
nsRefPtr<VideoData> video = VideoQueue().PeekFront();
if (seekTime == mEndTime) {
if (seekTime == Duration().ToMicroseconds()) {
newCurrentTime = mAudioStartTime = seekTime;
} else if (HasAudio()) {
AudioData* audio = AudioQueue().PeekFront();
@ -2440,7 +2419,7 @@ MediaDecoderStateMachine::SeekCompleted()
// for the seeking.
DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING");
SetState(DECODER_STATE_SEEKING);
} else if (GetMediaTime() == mEndTime && !isLiveStream) {
} else if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) {
// Seeked to end of media, move to COMPLETED state. Note we don't do
// this if we're playing a live stream, since the end of media will advance
// once we download more data!
@ -2522,6 +2501,7 @@ MediaDecoderStateMachine::FinishShutdown()
mVolume.DisconnectIfConnected();
mLogicalPlaybackRate.DisconnectIfConnected();
mPreservesPitch.DisconnectIfConnected();
mDuration.DisconnectAll();
mNextFrameStatus.DisconnectAll();
mCurrentPosition.DisconnectAll();
@ -2704,7 +2684,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
!mSentPlaybackEndedEvent)
{
int64_t clockTime = std::max(mAudioEndTime, mVideoFrameEndTime);
clockTime = std::max(int64_t(0), std::max(clockTime, mEndTime));
clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
UpdatePlaybackPosition(clockTime);
nsCOMPtr<nsIRunnable> event =

View File

@ -186,16 +186,6 @@ public:
bool IsRealTime() const;
// Called from the main thread to get the duration. The decoder monitor
// must be obtained before calling this. It is in units of microseconds.
int64_t GetDuration();
// Time of the last frame in the media, in microseconds or INT64_MAX if
// media has an infinite duration.
// Accessed on state machine, decode, and main threads.
// Access controlled by decoder monitor.
int64_t GetEndTime();
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
bool OnDecodeTaskQueue() const;
@ -694,6 +684,7 @@ public:
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioSinkComplete);
TaskQueue()->Dispatch(runnable.forget());
}
private:
// Called by the AudioSink to signal errors.
void OnAudioSinkError();
@ -947,20 +938,17 @@ public:
// buffering.
TimeStamp mBufferingStart;
// Time of the last frame in the media, in microseconds. This is the
// end time of the last frame in the media. Accessed on state
// machine, decode, and main threads. Access controlled by decoder monitor.
// It will be set to -1 if the duration is infinite
int64_t mEndTime;
// Duration of the media. This is guaranteed to be non-null after we finish
// decoding the first frame.
Canonical<media::NullableTimeUnit> mDuration;
media::TimeUnit Duration() const { MOZ_ASSERT(OnTaskQueue()); return mDuration.Ref().ref(); }
public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration() { return &mDuration; }
protected:
// Recomputes the canonical duration from various sources.
void RecomputeDuration();
// Will be set when SetDuration has been called with a value != -1
// mDurationSet false doesn't indicate that we do not have a valid duration
// as mStartTime and mEndTime could have been set separately.
bool mDurationSet;
// The duration according to the demuxer's current estimate, mirrored from the main thread.
Mirror<media::NullableTimeUnit> mEstimatedDuration;

View File

@ -283,14 +283,11 @@ public:
~Mirror()
{
if (mImpl->OwnerThread()->IsCurrentThreadIn()) {
mImpl->DisconnectIfConnected();
} else {
// If holder destruction happens on a thread other than the mirror's
// owner thread, manual disconnection is mandatory. We should make this
// more automatic by hooking it up to task queue shutdown.
MOZ_DIAGNOSTIC_ASSERT(!mImpl->IsConnected());
}
// As a member of complex objects, a Mirror<T> may be destroyed on a
// different thread than its owner, or late in shutdown during CC. Given
// that, we require manual disconnection so that callers can put things in
// the right place.
MOZ_DIAGNOSTIC_ASSERT(!mImpl->IsConnected());
}
private:
@ -312,6 +309,9 @@ private:
return mValue;
}
// Temporary workaround for naughty code.
const T& ReadOnWrongThread() { return mValue; }
virtual void UpdateValue(const T& aNewValue) override
{
MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
@ -377,6 +377,7 @@ public:
// Access to the T.
const T& Ref() const { return *mImpl; }
const T& ReadOnWrongThread() const { return mImpl->ReadOnWrongThread(); }
operator const T&() const { return Ref(); }
private:

View File

@ -8,6 +8,7 @@
#define StateWatching_h_
#include "AbstractThread.h"
#include "TaskDispatcher.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/unused.h"

View File

@ -16,6 +16,8 @@
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include <queue>
namespace mozilla {
/*

View File

@ -587,6 +587,7 @@ GMPChild::ShutdownComplete()
{
LOGD("%s", __FUNCTION__);
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
mAsyncShutdown = nullptr;
SendAsyncShutdownComplete();
}

View File

@ -34,6 +34,10 @@
#include "nsHashKeys.h"
#include "nsIFile.h"
#include "nsISimpleEnumerator.h"
#if defined(MOZ_CRASHREPORTER)
#include "nsExceptionHandler.h"
#endif
#include <limits>
namespace mozilla {
@ -79,7 +83,7 @@ static bool sHaveSetTimeoutPrefCache = false;
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
: mShuttingDown(false)
, mScannedPluginOnDisk(false)
, mWaitingForPluginsAsyncShutdown(false)
, mWaitingForPluginsSyncShutdown(false)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sHaveSetTimeoutPrefCache) {
@ -210,10 +214,11 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
// back to the GMPParent, which then registers the GMPParent by calling
// GMPService::AsyncShutdownNeeded().
//
// On shutdown, we set mWaitingForPluginsAsyncShutdown to true, and then
// On shutdown, we set mWaitingForPluginsSyncShutdown to true, and then
// call UnloadPlugins on the GMPThread, and process events on the main
// thread until an event sets mWaitingForPluginsAsyncShutdown=false on
// the main thread.
// thread until 1. An event sets mWaitingForPluginsSyncShutdown=false on
// the main thread; then 2. All async-shutdown plugins have indicated
// they have completed shutdown.
//
// UnloadPlugins() sends close messages for all plugins' API objects to
// the GMP interfaces in the child process, and then sends the async
@ -221,14 +226,14 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
// shutdown, it calls GMPAsyncShutdownHost::ShutdownComplete(), which
// sends a message back to the parent, which calls
// GMPService::AsyncShutdownComplete(). If all plugins requiring async
// shutdown have called AsyncShutdownComplete() we stick an event on the
// main thread to set mWaitingForPluginsAsyncShutdown=false. We must use
// an event to do this, as we must ensure the main thread processes an
// shutdown have called AsyncShutdownComplete() we stick a dummy event on
// the main thread, where the list of pending plugins is checked. We must
// use an event to do this, as we must ensure the main thread processes an
// event to run its loop. This will unblock the main thread, and shutdown
// of other components will proceed.
//
// We set a timer in UnloadPlugins(), and abort waiting for async
// shutdown if the GMPs are taking too long to shutdown.
// During shutdown, each GMPParent starts a timer, and pretends shutdown
// is complete if it is taking too long.
//
// We shutdown in "profile-change-teardown", as the profile dir is
// still writable then, and it's required for GMPStorage. We block the
@ -238,7 +243,7 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
// GMPStorage needs to work up until the shutdown-complete notification
// arrives from the GMP process.
mWaitingForPluginsAsyncShutdown = true;
mWaitingForPluginsSyncShutdown = true;
nsCOMPtr<nsIThread> gmpThread;
{
@ -249,18 +254,56 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
}
if (gmpThread) {
LOGD(("%s::%s Starting to unload plugins, waiting for first sync shutdown..."
, __CLASS__, __FUNCTION__));
gmpThread->Dispatch(
NS_NewRunnableMethod(this,
&GeckoMediaPluginServiceParent::UnloadPlugins),
NS_DISPATCH_NORMAL);
} else {
MOZ_ASSERT(mPlugins.IsEmpty());
mWaitingForPluginsAsyncShutdown = false;
}
// Wait for plugins to do async shutdown...
while (mWaitingForPluginsAsyncShutdown) {
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
// Wait for UnloadPlugins() to do initial sync shutdown...
while (mWaitingForPluginsSyncShutdown) {
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
}
// Wait for other plugins (if any) to do async shutdown...
auto syncShutdownPluginsRemaining =
std::numeric_limits<decltype(mAsyncShutdownPlugins.Length())>::max();
for (;;) {
{
MutexAutoLock lock(mMutex);
if (mAsyncShutdownPlugins.IsEmpty()) {
LOGD(("%s::%s Finished unloading all plugins"
, __CLASS__, __FUNCTION__));
#if defined(MOZ_CRASHREPORTER)
CrashReporter::RemoveCrashReportAnnotation(
NS_LITERAL_CSTRING("AsyncPluginShutdown"));
#endif
break;
} else if (mAsyncShutdownPlugins.Length() < syncShutdownPluginsRemaining) {
// First time here, or number of pending plugins has decreased.
// -> Update list of pending plugins in crash report.
syncShutdownPluginsRemaining = mAsyncShutdownPlugins.Length();
LOGD(("%s::%s Still waiting for %d plugins to shutdown..."
, __CLASS__, __FUNCTION__, (int)syncShutdownPluginsRemaining));
#if defined(MOZ_CRASHREPORTER)
nsAutoCString names;
for (const auto& plugin : mAsyncShutdownPlugins) {
if (!names.IsEmpty()) { names.Append(NS_LITERAL_CSTRING(", ")); }
names.Append(plugin->GetDisplayName());
}
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("AsyncPluginShutdown"),
names);
#endif
}
}
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
}
} else {
// GMP thread has already shutdown.
MOZ_ASSERT(mPlugins.IsEmpty());
mWaitingForPluginsSyncShutdown = false;
}
} else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
@ -326,6 +369,7 @@ GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
mAsyncShutdownPlugins.AppendElement(aParent);
}
@ -333,39 +377,60 @@ GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
void
GeckoMediaPluginServiceParent::AsyncShutdownComplete(GMPParent* aParent)
{
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
LOGD(("%s::%s %p '%s'", __CLASS__, __FUNCTION__,
aParent, aParent->GetDisplayName().get()));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
mAsyncShutdownPlugins.RemoveElement(aParent);
if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
{
MutexAutoLock lock(mMutex);
mAsyncShutdownPlugins.RemoveElement(aParent);
}
if (mShuttingDownOnGMPThread) {
// The main thread may be waiting for async shutdown of plugins,
// which has completed. Break the main thread out of its waiting loop.
// one of which has completed. Wake up the main thread by sending a task.
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
this, &GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
}
void
GeckoMediaPluginServiceParent::SetAsyncShutdownComplete()
GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
mWaitingForPluginsAsyncShutdown = false;
// Nothing to do, this task is just used to wake up the event loop in Observe().
}
void
GeckoMediaPluginServiceParent::NotifySyncShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
mWaitingForPluginsSyncShutdown = false;
}
void
GeckoMediaPluginServiceParent::UnloadPlugins()
{
LOGD(("%s::%s async_shutdown=%d", __CLASS__, __FUNCTION__,
mAsyncShutdownPlugins.Length()));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MOZ_ASSERT(!mShuttingDownOnGMPThread);
mShuttingDownOnGMPThread = true;
{
MutexAutoLock lock(mMutex);
// Note: CloseActive is async; it will actually finish
LOGD(("%s::%s plugins:%u including async:%u", __CLASS__, __FUNCTION__,
mPlugins.Length(), mAsyncShutdownPlugins.Length()));
#ifdef DEBUG
for (const auto& plugin : mPlugins) {
LOGD(("%s::%s plugin: '%s'", __CLASS__, __FUNCTION__,
plugin->GetDisplayName().get()));
}
for (const auto& plugin : mAsyncShutdownPlugins) {
LOGD(("%s::%s async plugin: '%s'", __CLASS__, __FUNCTION__,
plugin->GetDisplayName().get()));
}
#endif
// Note: CloseActive may be async; it could actually finish
// shutting down when all the plugins have unloaded.
for (size_t i = 0; i < mPlugins.Length(); i++) {
mPlugins[i]->CloseActive(true);
@ -373,11 +438,9 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
mPlugins.Clear();
}
if (mAsyncShutdownPlugins.IsEmpty()) {
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete));
NS_DispatchToMainThread(task);
}
void

View File

@ -73,7 +73,8 @@ private:
void UnloadPlugins();
void CrashPlugins();
void SetAsyncShutdownComplete();
void NotifySyncShutdownComplete();
void NotifyAsyncShutdownComplete();
void LoadFromEnvironment();
void ProcessPossiblePlugin(nsIFile* aDir);
@ -138,6 +139,7 @@ private:
// Protected by mMutex from the base class.
nsTArray<nsRefPtr<GMPParent>> mPlugins;
bool mShuttingDown;
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins;
// True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
// plugins found there into mPlugins.
@ -158,9 +160,7 @@ private:
T mValue;
};
MainThreadOnly<bool> mWaitingForPluginsAsyncShutdown;
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
MainThreadOnly<bool> mWaitingForPluginsSyncShutdown;
nsTArray<nsString> mPluginsWaitingForDeletion;

View File

@ -887,15 +887,9 @@ media::TimeIntervals GStreamerReader::GetBuffered()
nsTArray<MediaByteRange> ranges;
resource->GetCachedRanges(ranges);
if (resource->IsDataCachedToEndOfResource(0)) {
if (resource->IsDataCachedToEndOfResource(0) && mDuration.ReadOnWrongThread().isSome()) {
/* fast path for local or completely cached files */
gint64 duration = 0;
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
duration = mDecoder->GetMediaDuration();
}
gint64 duration = mDuration.ReadOnWrongThread().ref().ToMicroseconds();
LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]",
(double) duration / GST_MSECOND, GetDataLength());
buffered +=

View File

@ -221,7 +221,6 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio
GetReader()->SetMediaSourceDuration(ExplicitDuration());
}
MediaDecoder::DurationChanged(TimeUnit::FromSeconds(ExplicitDuration()));
if (mMediaSource && aAction != MSRangeRemovalAction::SKIP) {
mMediaSource->DurationChange(oldDuration, aDuration);
}

View File

@ -36,7 +36,6 @@ public:
virtual bool IsTransportSeekable() final override;
virtual bool OnDecodeTaskQueue() const final override;
virtual bool OnStateMachineTaskQueue() const final override;
virtual int64_t GetMediaDuration() final override { MOZ_ASSERT_UNREACHABLE(""); return -1; };
virtual layers::ImageContainer* GetImageContainer() final override;
virtual MediaDecoderOwner* GetOwner() final override;
virtual SourceBufferResource* GetResource() const final override;

View File

@ -75,7 +75,7 @@ AppleVDADecoder::~AppleVDADecoder()
nsresult
AppleVDADecoder::Init()
{
if (!gfxPlatform::CanUseHardwareVideoDecoding()) {
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
// This GPU is blacklisted for hardware decoding.
return NS_ERROR_FAILURE;
}

View File

@ -380,7 +380,7 @@ AppleVTDecoder::CreateDecoderSpecification()
const void* specKeys[] = { AppleVTLinker::skPropEnableHWAccel };
const void* specValues[1];
if (gfxPlatform::CanUseHardwareVideoDecoding()) {
if (gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
specValues[0] = kCFBooleanTrue;
} else {
// This GPU is blacklisted for hardware decoding.

View File

@ -49,8 +49,7 @@ WMFDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
sIsWMFEnabled = Preferences::GetBool("media.windows-media-foundation.enabled", false);
sDXVAEnabled = !gfxWindowsPlatform::GetPlatform()->IsWARP() &&
gfxPlatform::CanUseHardwareVideoDecoding();
sDXVAEnabled = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
}
nsresult

View File

@ -85,13 +85,6 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
// ignore
}
int64_t
BufferDecoder::GetMediaDuration()
{
// unknown
return -1;
}
void
BufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
{

View File

@ -45,8 +45,6 @@ public:
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
uint32_t aDropped) final override;
virtual int64_t GetMediaDuration() final override;
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) final override;
virtual void SetMediaSeekable(bool aMediaSeekable) final override;

View File

@ -887,7 +887,7 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"PluginArray",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PointerEvent", disabled: true},
{name: "PointerEvent", nightly: true, desktop: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PopStateEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -5,30 +5,11 @@
*/
/**
* A collection of static utility methods that are only exposed to Chrome.
* A collection of static utility methods that are only exposed to Chrome. This
* interface is not exposed in workers, while ThreadSafeChromeUtils is.
*/
[ChromeOnly, Exposed=(Window,System)]
interface ChromeUtils {
/**
* Serialize a snapshot of the heap graph, as seen by |JS::ubi::Node| and
* restricted by |boundaries|, and write it to the provided file path.
*
* @param filePath The file path to write the heap snapshot to.
*
* @param boundaries The portion of the heap graph to write.
*/
[Throws]
static void saveHeapSnapshot(DOMString filePath,
optional HeapSnapshotBoundaries boundaries);
/**
* Deserialize a core dump into a HeapSnapshot.
*
* @param filePath The file path to read the core dump from.
*/
[Throws, NewObject]
static HeapSnapshot readHeapSnapshot(DOMString filePath);
interface ChromeUtils : ThreadSafeChromeUtils {
/**
* A helper that converts OriginAttributesDictionary to cookie jar opaque
* identfier.
@ -39,38 +20,6 @@ interface ChromeUtils {
originAttributesToCookieJar(optional OriginAttributesDictionary originAttrs);
};
/**
* A JS object whose properties specify what portion of the heap graph to
* write. The recognized properties are:
*
* * globals: [ global, ... ]
* Dump only nodes that either:
* - belong in the compartment of one of the given globals;
* - belong to no compartment, but do belong to a Zone that contains one of
* the given globals;
* - are referred to directly by one of the last two kinds of nodes; or
* - is the fictional root node, described below.
*
* * debugger: Debugger object
* Like "globals", but use the Debugger's debuggees as the globals.
*
* * runtime: true
* Dump the entire heap graph, starting with the JSRuntime's roots.
*
* One, and only one, of these properties must exist on the boundaries object.
*
* The root of the dumped graph is a fictional node whose ubi::Node type name is
* "CoreDumpRoot". If we are dumping the entire ubi::Node graph, this root node
* has an edge for each of the JSRuntime's roots. If we are dumping a selected
* set of globals, the root has an edge to each global, and an edge for each
* incoming JS reference to the selected Zones.
*/
dictionary HeapSnapshotBoundaries {
sequence<object> globals;
object debugger;
boolean runtime;
};
/**
* Used by principals and the script security manager to represent origin
* attributes.

View File

@ -7,6 +7,6 @@
/**
* A HeapSnapshot represents a snapshot of the heap graph
*/
[ChromeOnly, Exposed=(Window,System)]
[ChromeOnly, Exposed=(Window,System,Worker)]
interface HeapSnapshot {
};

View File

@ -0,0 +1,64 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
/**
* A collection of **thread-safe** static utility methods that are only exposed
* to Chrome. This interface is exposed in workers, while ChromeUtils is not.
*/
[ChromeOnly, Exposed=(Window,System,Worker)]
interface ThreadSafeChromeUtils {
/**
* Serialize a snapshot of the heap graph, as seen by |JS::ubi::Node| and
* restricted by |boundaries|, and write it to the provided file path.
*
* @param filePath The file path to write the heap snapshot to.
*
* @param boundaries The portion of the heap graph to write.
*/
[Throws]
static void saveHeapSnapshot(DOMString filePath,
optional HeapSnapshotBoundaries boundaries);
/**
* Deserialize a core dump into a HeapSnapshot.
*
* @param filePath The file path to read the heap snapshot from.
*/
[Throws, NewObject]
static HeapSnapshot readHeapSnapshot(DOMString filePath);
};
/**
* A JS object whose properties specify what portion of the heap graph to
* write. The recognized properties are:
*
* * globals: [ global, ... ]
* Dump only nodes that either:
* - belong in the compartment of one of the given globals;
* - belong to no compartment, but do belong to a Zone that contains one of
* the given globals;
* - are referred to directly by one of the last two kinds of nodes; or
* - is the fictional root node, described below.
*
* * debugger: Debugger object
* Like "globals", but use the Debugger's debuggees as the globals.
*
* * runtime: true
* Dump the entire heap graph, starting with the JSRuntime's roots.
*
* One, and only one, of these properties must exist on the boundaries object.
*
* The root of the dumped graph is a fictional node whose ubi::Node type name is
* "CoreDumpRoot". If we are dumping the entire ubi::Node graph, this root node
* has an edge for each of the JSRuntime's roots. If we are dumping a selected
* set of globals, the root has an edge to each global, and an edge for each
* incoming JS reference to the selected Zones.
*/
dictionary HeapSnapshotBoundaries {
sequence<object> globals;
object debugger;
boolean runtime;
};

View File

@ -522,6 +522,7 @@ WEBIDL_FILES = [
'TextTrack.webidl',
'TextTrackCueList.webidl',
'TextTrackList.webidl',
'ThreadSafeChromeUtils.webidl',
'TimeEvent.webidl',
'TimeRanges.webidl',
'Touch.webidl',

View File

@ -3580,6 +3580,24 @@ ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
info.mIndexedDBAllowed =
indexedDB::IDBFactory::AllowedForPrincipal(aPrincipal);
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = aPrincipal->GetCsp(getter_AddRefs(csp));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
info.mCSP = csp;
if (info.mCSP) {
rv = info.mCSP->GetAllowsEval(&info.mReportCSPViolations,
&info.mEvalAllowed);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
info.mEvalAllowed = true;
info.mReportCSPViolations = false;
}
// NOTE: this defaults the SW load context to:
// - private browsing = false
// - content = true

View File

@ -0,0 +1 @@
eval('1+1');

View File

@ -137,6 +137,8 @@ support-files =
register_https.html
gzip_redirect_worker.js
sw_clients/navigator.html
eval_worker.js
test_eval_not_allowed.html^headers^
[test_unregister.html]
[test_installation_simple.html]
@ -190,3 +192,5 @@ support-files =
[test_gzip_redirect.html]
[test_register_base.html]
[test_register_https_in_http.html]
[test_eval_allowed.html]
[test_eval_not_allowed.html]

View File

@ -0,0 +1,42 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1160458 - CSP activated by default in Service Workers</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
function register() {
return navigator.serviceWorker.register("eval_worker.js");
}
function runTest() {
register()
.then(function(swr) {
ok(true, "eval in service worker script didn't cause an issue");
swr.unregister();
SimpleTest.finish();
}).catch(function(e) {
ok(false, "Some test failed with error " + e);
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,45 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1160458 - CSP activated by default in Service Workers</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
function register() {
return navigator.serviceWorker.register("eval_worker.js");
}
function runTest() {
register()
.then(function(swr) {
ok(false, "eval in service worker should not be allowed when the policy prevents it");
swr.unregister();
SimpleTest.finish();
}).catch(function() {
ok(true, "eval in service worker is not allowed when the policy prevents eval");
SimpleTest.finish();
}).catch(function(e) {
ok(false, "Some test failed with error " + e);
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'"

View File

@ -866,6 +866,8 @@ struct ZoomConstraints {
}
};
typedef Maybe<ZoomConstraints> MaybeZoomConstraints;
}
}

View File

@ -217,6 +217,15 @@ AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
<< sfx;
}
void
AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
const char* pfx, const char* sfx)
{
aStream << pfx
<< nsPrintfCString("{ z=%d dt=%d min=%f max=%f }", z.mAllowZoom, z.mAllowDoubleTapZoom, z.mMinZoom.scale, z.mMaxZoom.scale).get()
<< sfx;
}
void
AppendToString(std::stringstream& aStream, const Matrix& m,
const char* pfx, const char* sfx)

View File

@ -130,6 +130,10 @@ void
AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
const char* pfx="", const char* sfx="");
void
AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
const char* pfx="", const char* sfx="");
template<class T>
void
AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,

View File

@ -90,16 +90,6 @@ public:
*/
virtual void PostDelayedTask(Task* aTask, int aDelayMs) = 0;
/**
* Retrieves the last known zoom constraints for the root scrollable layer
* for this layers tree. This function should return false if there are no
* last known zoom constraints.
*/
virtual bool GetRootZoomConstraints(ZoomConstraints* aOutConstraints)
{
return false;
}
/**
* APZ uses |FrameMetrics::mCompositionBounds| for hit testing. Sometimes,
* widget code has knowledge of a touch-sensitive region that should

View File

@ -322,6 +322,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
HitTestingTreeNode* aNextSibling,
TreeBuildingState& aState)
{
mTreeLock.AssertCurrentThreadOwns();
bool needsApzc = true;
if (!aMetrics.IsScrollable()) {
needsApzc = false;
@ -464,22 +466,18 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
}
if (newApzc) {
if (apzc->IsRootContent()) {
// If we just created a new root-content apzc, then we need to update
// its zoom constraints which might have arrived before it was created.
ZoomConstraints constraints;
if (state->mController->GetRootZoomConstraints(&constraints)) {
apzc->UpdateZoomConstraints(constraints);
}
auto it = mZoomConstraints.find(guid);
if (it != mZoomConstraints.end()) {
// We have a zoomconstraints for this guid, apply it.
apzc->UpdateZoomConstraints(it->second);
} else if (!apzc->HasNoParentWithSameLayersId()) {
// Otherwise, an APZC that has a parent in the same layer tree gets
// the same zoom constraints as its parent. This ensures that if e.g.
// user-scalable=no was specified on the root, none of the APZCs allow
// double-tap to zoom.
// This is a sub-APZC, so inherit the zoom constraints from its parent.
// This ensures that if e.g. user-scalable=no was specified, none of the
// APZCs for that subtree allow double-tap to zoom.
apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
}
// Otherwise, if the APZC has no parent in the same layer tree, leave
// it with the existing zoom constraints.
// Otherwise, this is the root of a layers id, but we didn't have a saved
// zoom constraints. Leave it empty for now.
}
// Add a guid -> APZC mapping for the newly created APZC.
@ -1028,16 +1026,24 @@ APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid
void
APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
const ZoomConstraints& aConstraints)
const Maybe<ZoomConstraints>& aConstraints)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
// For a given layers id, non-{root content} APZCs inherit the zoom constraints
// of their root.
if (node && node->GetApzc()->IsRootContent()) {
UpdateZoomConstraintsRecursively(node.get(), aConstraints);
// Propagate the zoom constraints down to the subtree, stopping at APZCs
// which have their own zoom constraints or are in a different layers id.
if (aConstraints) {
APZCTM_LOG("Recording constraints %s for guid %s\n",
Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
mZoomConstraints[aGuid] = aConstraints.ref();
} else {
APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
mZoomConstraints.erase(aGuid);
}
if (node && aConstraints) {
UpdateZoomConstraintsRecursively(node.get(), aConstraints.ref());
}
}
@ -1052,9 +1058,13 @@ APZCTreeManager::UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode,
aNode->GetApzc()->UpdateZoomConstraints(aConstraints);
}
for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) {
// We can have subtrees with their own layers id - leave those alone.
if (child->GetApzc() && child->GetApzc()->HasNoParentWithSameLayersId()) {
continue;
if (AsyncPanZoomController* childApzc = child->GetApzc()) {
// We can have subtrees with their own zoom constraints or separate layers
// id - leave those alone.
if (childApzc->HasNoParentWithSameLayersId() ||
mZoomConstraints.find(childApzc->GetGuid()) != mZoomConstraints.end()) {
continue;
}
}
UpdateZoomConstraintsRecursively(child, aConstraints);
}

View File

@ -236,9 +236,11 @@ public:
/**
* Updates any zoom constraints contained in the <meta name="viewport"> tag.
* If the |aConstraints| is Nothing() then previously-provided constraints for
* the given |aGuid| are cleared.
*/
void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
const ZoomConstraints& aConstraints);
const Maybe<ZoomConstraints>& aConstraints);
/**
* Cancels any currently running animation. Note that all this does is set the
@ -487,10 +489,14 @@ private:
* isolation (that is, if its tree pointers are not being accessed or mutated). The
* lock also needs to be held when accessing the mRootNode instance variable, as that
* is considered part of the APZC tree management state.
* Finally, the lock needs to be held when accessing mOverscrollHandoffChain.
* Finally, the lock needs to be held when accessing mZoomConstraints.
* IMPORTANT: See the note about lock ordering at the top of this file. */
mutable mozilla::Monitor mTreeLock;
nsRefPtr<HitTestingTreeNode> mRootNode;
/* Holds the zoom constraints for scrollable layers, as determined by the
* the main-thread gecko code. */
std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints;
/* This tracks the APZC that should receive all inputs for the current input event block.
* This allows touch points to move outside the thing they started on, but still have the
* touch events delivered to the same initial APZC. This will only ever be touched on the

View File

@ -351,17 +351,49 @@ APZCCallbackHelper::AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollI
}
}
static nsIPresShell*
GetRootContentDocumentPresShellForContent(nsIContent* aContent)
{
nsIDocument* doc = aContent->GetComposedDoc();
if (!doc) {
return nullptr;
}
nsIPresShell* shell = doc->GetShell();
if (!shell) {
return nullptr;
}
nsPresContext* context = shell->GetPresContext();
if (!context) {
return nullptr;
}
context = context->GetToplevelContentDocumentPresContext();
if (!context) {
return nullptr;
}
return context->PresShell();
}
CSSPoint
APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
const ScrollableLayerGuid& aGuid,
float aPresShellResolution)
const ScrollableLayerGuid& aGuid)
{
// First, scale inversely by the pres shell resolution to cancel the
// scale-to-resolution transform that the compositor adds to the layer with
// the pres shell resolution. The points sent to Gecko by APZ don't have
// this transform unapplied (unlike other compositor-side transforms)
// because APZ doesn't know about it.
CSSPoint input = aInput / aPresShellResolution;
CSSPoint input = aInput;
if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) {
return input;
}
nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
if (!content) {
return input;
}
// First, scale inversely by the root content document's pres shell
// resolution to cancel the scale-to-resolution transform that the
// compositor adds to the layer with the pres shell resolution. The points
// sent to Gecko by APZ don't have this transform unapplied (unlike other
// compositor-side transforms) because APZ doesn't know about it.
if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
input = input / shell->GetResolution();
}
// Now apply the callback-transform.
// XXX: technically we need to walk all the way up the layer tree from the layer
@ -374,16 +406,10 @@ APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
// some things transformed improperly. In practice we should rarely hit scenarios
// where any of this matters, so I'm skipping it for now and just doing the single
// transform for the layer that the input hit.
if (aGuid.mScrollId != FrameMetrics::NULL_SCROLL_ID) {
nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
if (content) {
void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
if (property) {
CSSPoint delta = (*static_cast<CSSPoint*>(property));
return input + delta;
}
}
void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
if (property) {
CSSPoint delta = (*static_cast<CSSPoint*>(property));
input += delta;
}
return input;
}
@ -391,23 +417,21 @@ APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
LayoutDeviceIntPoint
APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale,
float aPresShellResolution)
const CSSToLayoutDeviceScale& aScale)
{
LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
point = ApplyCallbackTransform(point / aScale, aGuid, aPresShellResolution) * aScale;
point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
return gfx::RoundedToInt(point);
}
void
APZCCallbackHelper::ApplyCallbackTransform(WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale,
float aPresShellResolution)
const CSSToLayoutDeviceScale& aScale)
{
for (size_t i = 0; i < aEvent.touches.Length(); i++) {
aEvent.touches[i]->mRefPoint = ApplyCallbackTransform(
aEvent.touches[i]->mRefPoint, aGuid, aScale, aPresShellResolution);
aEvent.touches[i]->mRefPoint, aGuid, aScale);
}
}

View File

@ -86,8 +86,7 @@ public:
pres shell resolution, to cancel out a compositor-side transform (added in
bug 1076241) that APZ doesn't unapply. */
static CSSPoint ApplyCallbackTransform(const CSSPoint& aInput,
const ScrollableLayerGuid& aGuid,
float aPresShellResolution);
const ScrollableLayerGuid& aGuid);
/* Same as above, but operates on LayoutDeviceIntPoint.
Requires an additonal |aScale| parameter to convert between CSS and
@ -95,15 +94,13 @@ public:
static mozilla::LayoutDeviceIntPoint
ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale,
float aPresShellResolution);
const CSSToLayoutDeviceScale& aScale);
/* Convenience function for applying a callback transform to all touch
* points of a touch event. */
static void ApplyCallbackTransform(WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const CSSToLayoutDeviceScale& aScale,
float aPresShellResolution);
const CSSToLayoutDeviceScale& aScale);
/* Dispatch a widget event via the widget stored in the event, if any.
* In a child process, allows the TabParent event-capture mechanism to

View File

@ -149,8 +149,7 @@ NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
void
APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
float aPresShellResolution)
const ScrollableLayerGuid& aGuid)
{
APZES_LOG("Handling single tap at %s on %s with %d\n",
Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
@ -165,7 +164,7 @@ APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
}
LayoutDevicePoint currentPoint =
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid)
* widget->GetDefaultScale();;
if (!mActiveElementManager->ActiveElementUsesStyle()) {
// If the active element isn't visually affected by the :active style, we
@ -194,8 +193,7 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
float aPresShellResolution)
uint64_t aInputBlockId)
{
APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
@ -212,7 +210,7 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
// including in JS code, so it's not trivial to change.
bool eventHandled =
APZCCallbackHelper::DispatchMouseEvent(aPresShell, NS_LITERAL_STRING("contextmenu"),
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution),
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid),
2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
@ -221,7 +219,7 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
// If no one handle context menu, fire MOZLONGTAP event
if (!eventHandled) {
LayoutDevicePoint currentPoint =
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid)
* widget->GetDefaultScale();
int time = 0;
nsEventStatus status =

View File

@ -51,14 +51,12 @@ public:
void ProcessSingleTap(const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
float aPresShellResolution);
const ScrollableLayerGuid& aGuid);
void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
const CSSPoint& aPoint,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
float aPresShellResolution);
uint64_t aInputBlockId);
void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,

View File

@ -113,14 +113,6 @@ ChromeProcessController::Destroy()
mWidget = nullptr;
}
float
ChromeProcessController::GetPresShellResolution() const
{
// The document in the chrome process cannot be zoomed, so its pres shell
// resolution is 1.
return 1.0f;
}
nsIPresShell*
ChromeProcessController::GetPresShell() const
{
@ -162,7 +154,7 @@ ChromeProcessController::HandleSingleTap(const CSSPoint& aPoint,
return;
}
mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid, GetPresShellResolution());
mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
}
void
@ -179,7 +171,7 @@ ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, Modifier
}
mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
aInputBlockId, GetPresShellResolution());
aInputBlockId);
}
void

View File

@ -62,7 +62,6 @@ private:
MessageLoop* mUILoop;
void InitializeRoot();
float GetPresShellResolution() const;
nsIPresShell* GetPresShell() const;
nsIDocument* GetDocument() const;
already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils() const;

View File

@ -671,7 +671,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
}
if (UseVsyncComposition()) {
NS_WARNING("Enabling vsync compositor");
gfxDebugOnce() << "Enabling vsync compositor";
mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
} else {
mCompositorScheduler = new CompositorSoftwareTimerScheduler(this);
@ -1550,7 +1550,10 @@ CompositorParent::DeallocateLayerTreeId(uint64_t aId)
// Here main thread notifies compositor to remove an element from
// sIndirectLayerTrees. This removed element might be queried soon.
// Checking the elements of sIndirectLayerTrees exist or not before using.
MOZ_ASSERT(CompositorLoop());
if (!CompositorLoop()) {
gfxCriticalError() << "Attempting to post to a invalid Compositor Loop";
return;
}
CompositorLoop()->PostTask(FROM_HERE,
NewRunnableFunction(&EraseLayerState, aId));
}

View File

@ -505,7 +505,7 @@ public:
static bool CanUseDirect3D9();
static bool CanUseDirect3D11();
static bool CanUseHardwareVideoDecoding();
virtual bool CanUseHardwareVideoDecoding();
static bool CanUseDirect3D11ANGLE();
/**

View File

@ -409,6 +409,7 @@ gfxWindowsPlatform::gfxWindowsPlatform()
: mD3D11DeviceInitialized(false)
, mIsWARP(false)
, mHasDeviceReset(false)
, mDoesD3D11TextureSharingWork(false)
{
mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
mUseClearTypeAlways = UNINITIALIZED_VALUE;
@ -470,6 +471,15 @@ gfxWindowsPlatform::GetDPIScale()
return WinUtils::LogToPhysFactor();
}
bool
gfxWindowsPlatform::CanUseHardwareVideoDecoding()
{
if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) {
return false;
}
return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
}
void
gfxWindowsPlatform::UpdateRenderMode()
{
@ -495,6 +505,7 @@ gfxWindowsPlatform::UpdateRenderMode()
}
mRenderMode = RENDER_GDI;
mDoesD3D11TextureSharingWork = true;
bool isVistaOrHigher = IsVistaOrLater();
@ -534,8 +545,11 @@ gfxWindowsPlatform::UpdateRenderMode()
}
ID3D11Device *device = GetD3D11Device();
if (device) {
mDoesD3D11TextureSharingWork = DoesD3D11TextureSharingWork(device);
}
if (isVistaOrHigher && !InSafeMode() && tryD2D && device &&
DoesD3D11TextureSharingWork(device)) {
mDoesD3D11TextureSharingWork) {
VerifyD2DDevice(d2dForceEnabled);
if (mD2DDevice && GetD3D11Device()) {

View File

@ -203,6 +203,8 @@ public:
const uint8_t* aFontData,
uint32_t aLength);
virtual bool CanUseHardwareVideoDecoding() override;
/**
* Check whether format is supported on a platform or not (if unclear, returns true)
*/
@ -300,6 +302,7 @@ private:
mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
bool mIsWARP;
bool mHasDeviceReset;
bool mDoesD3D11TextureSharingWork;
DeviceResetReason mDeviceResetReason;
virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);

View File

@ -686,9 +686,11 @@ RasterImage::CopyFrame(uint32_t aWhichFrame, uint32_t aFlags)
}
DataSourceSurface::MappedSurface mapping;
DebugOnly<bool> success =
surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
NS_ASSERTION(success, "Failed to map surface");
if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
gfxCriticalError() << "RasterImage::CopyFrame failed to map surface";
return nullptr;
}
RefPtr<DrawTarget> target =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
mapping.mData,

View File

@ -14,6 +14,7 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include "jsapi.h"
#include "jspubtd.h"
#include "js/GCAPI.h"

View File

@ -108,6 +108,7 @@ namespace JS {
_(CantInlineNoBaseline) \
_(CantInlineLazy) \
_(CantInlineNotConstructor) \
_(CantInlineClassConstructor) \
_(CantInlineDisabledIon) \
_(CantInlineTooManyArgs) \
_(CantInlineRecursive) \

View File

@ -212,18 +212,6 @@ TryEvalJSON(JSContext* cx, JSLinearString* str, MutableHandleValue rval)
: ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
}
static bool
HasPollutedScopeChain(JSObject* scopeChain)
{
while (scopeChain) {
if (scopeChain->is<DynamicWithObject>())
return true;
scopeChain = scopeChain->enclosingScope();
}
return false;
}
// Define subset of ExecuteType so that casting performs the injection.
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
@ -326,13 +314,8 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
if (!staticScope)
return false;
bool hasPollutedGlobalScope =
HasPollutedScopeChain(scopeobj) ||
(evalType == DIRECT_EVAL && callerScript->hasPollutedGlobalScope());
CompileOptions options(cx);
options.setFileAndLine(filename, 1)
.setHasPollutedScope(hasPollutedGlobalScope)
.setIsRunOnce(true)
.setForEval(true)
.setNoScriptRval(false)
@ -350,7 +333,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope,
scopeobj, staticScope, callerScript,
options, srcBuf, linearStr, staticLevel);
if (!compiled)
return false;
@ -404,7 +387,7 @@ js::DirectEvalStringFromIon(JSContext* cx,
bool mutedErrors;
uint32_t pcOffset;
DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
&mutedErrors, CALLED_FROM_JSOP_EVAL);
&mutedErrors, CALLED_FROM_JSOP_EVAL);
const char* introducerFilename = filename;
if (maybeScript && maybeScript->scriptSource()->introducerFilename())
@ -417,8 +400,6 @@ js::DirectEvalStringFromIon(JSContext* cx,
CompileOptions options(cx);
options.setFileAndLine(filename, 1)
.setHasPollutedScope(HasPollutedScopeChain(scopeobj) ||
callerScript->hasPollutedGlobalScope())
.setIsRunOnce(true)
.setForEval(true)
.setNoScriptRval(false)
@ -436,7 +417,7 @@ js::DirectEvalStringFromIon(JSContext* cx,
: SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope,
scopeobj, staticScope, callerScript,
options, srcBuf, linearStr, staticLevel);
if (!compiled)
return false;
@ -507,35 +488,31 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
CHECK_REQUEST(cx);
assertSameCompartment(cx, global);
MOZ_ASSERT(global->is<GlobalObject>());
MOZ_RELEASE_ASSERT(scriptArg->hasPollutedGlobalScope());
MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
RootedScript script(cx, scriptArg);
if (script->compartment() != cx->compartment()) {
script = CloneScript(cx, nullptr, nullptr, script);
Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
if (!staticScope)
return false;
script = CloneGlobalScript(cx, staticScope, script);
if (!script)
return false;
Debugger::onNewScript(cx, script);
}
RootedObject scope(cx, JS_NewPlainObject(cx));
Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalRoot));
if (!scope)
return false;
if (!scope->setQualifiedVarObj(cx))
return false;
if (!scope->setUnqualifiedVarObj(cx))
return false;
JSObject* thisobj = GetThisObject(cx, global);
if (!thisobj)
return false;
RootedValue thisv(cx, ObjectValue(*thisobj));
RootedValue rval(cx);
// XXXbz when this is fixed to pass in an actual ScopeObject, fix
// up the assert in js::CloneFunctionObject accordingly.
if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
NullFramePtr() /* evalInFrame */, rval.address()))
{

View File

@ -2272,11 +2272,10 @@ EvalReturningScope(JSContext* cx, unsigned argc, jsval* vp)
JS::CompileOptions options(cx);
options.setFileAndLine(filename.get(), lineno);
options.setNoScriptRval(true);
options.setHasPollutedScope(true);
JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
RootedScript script(cx);
if (!JS::Compile(cx, options, srcBuf, &script))
if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script))
return false;
if (global) {

View File

@ -144,10 +144,11 @@ MaybeCheckEvalFreeVariables(ExclusiveContext* cxArg, HandleScript evalCaller, Ha
}
static inline bool
CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
CanLazilyParse(ExclusiveContext* cx, HandleObject staticScope,
const ReadOnlyCompileOptions& options)
{
return options.canLazilyParse &&
!options.hasPollutedGlobalScope &&
!HasNonSyntacticStaticScopeChain(staticScope) &&
!cx->compartment()->options().disableLazyParsing() &&
!cx->compartment()->options().discardSource() &&
!options.sourceIsLazy;
@ -210,8 +211,8 @@ frontend::CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOp
JSScript*
frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain,
Handle<ScopeObject*> enclosingStaticScope,
HandleScript evalCaller,
Handle<StaticEvalObject*> evalStaticScope,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
JSString* source_ /* = nullptr */,
@ -260,7 +261,7 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
return nullptr;
}
bool canLazilyParse = CanLazilyParse(cx, options);
bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options);
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
if (canLazilyParse) {
@ -284,22 +285,22 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
Directives directives(options.strictOption);
GlobalSharedContext globalsc(cx, directives, evalStaticScope, options.extraWarningsOption);
GlobalSharedContext globalsc(cx, directives, enclosingStaticScope, options.extraWarningsOption);
Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingStaticScope, savedCallerFun,
options, staticLevel, sourceObject, 0,
srcBuf.length()));
if (!script)
return nullptr;
bool insideNonGlobalEval =
evalStaticScope && evalStaticScope->enclosingScopeForStaticScopeIter();
enclosingStaticScope && enclosingStaticScope->is<StaticEvalObject>() &&
enclosingStaticScope->as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
BytecodeEmitter::EmitterMode emitterMode =
options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script,
/* lazyScript = */ nullptr, options.forEval,
evalCaller, insideNonGlobalEval,
options.lineno, emitterMode);
evalCaller, insideNonGlobalEval, options.lineno, emitterMode);
if (!bce.init())
return nullptr;
@ -561,7 +562,7 @@ CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyComp
return false;
}
bool canLazilyParse = CanLazilyParse(cx, options);
bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options);
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
if (canLazilyParse) {

View File

@ -17,18 +17,17 @@ class AutoNameVector;
class LazyScript;
class LifoAlloc;
class ScriptSourceObject;
class StaticEvalObject;
class ScopeObject;
struct SourceCompressionTask;
namespace frontend {
JSScript*
CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
HandleObject scopeChain, HandleScript evalCaller,
Handle<StaticEvalObject*> evalStaticScope,
const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf,
JSString* source_ = nullptr, unsigned staticLevel = 0,
SourceCompressionTask* extraSct = nullptr);
HandleObject scopeChain, Handle<ScopeObject*> enclosingStaticScope,
HandleScript evalCaller, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
unsigned staticLevel = 0, SourceCompressionTask* extraSct = nullptr);
bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);

View File

@ -769,7 +769,7 @@ BytecodeEmitter::enclosingStaticScope()
// Top-level eval scripts have a placeholder static scope so that
// StaticScopeIter may iterate through evals.
return sc->asGlobalSharedContext()->evalStaticScope();
return sc->asGlobalSharedContext()->topStaticScope();
}
return sc->asFunctionBox()->function();
@ -1548,14 +1548,14 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn)
// Use generic ops if a catch block is encountered.
return false;
}
if (ssi.hasDynamicScopeObject())
if (ssi.hasSyntacticDynamicScopeObject())
hops++;
continue;
}
RootedScript script(cx, ssi.funScript());
if (script->functionNonDelazifying()->atom() == pn->pn_atom)
return false;
if (ssi.hasDynamicScopeObject()) {
if (ssi.hasSyntacticDynamicScopeObject()) {
uint32_t slot;
if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) {
JSOp op;
@ -1587,9 +1587,9 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn)
if (insideNonGlobalEval)
return false;
// Skip trying to use GNAME ops if we know our script has a polluted
// global scope, since they'll just get treated as NAME ops anyway.
if (script->hasPollutedGlobalScope())
// Skip trying to use GNAME ops if we know our script has a non-syntactic
// scope, since they'll just get treated as NAME ops anyway.
if (script->hasNonSyntacticScope())
return false;
// Deoptimized names also aren't necessarily globals.
@ -2356,13 +2356,14 @@ BytecodeEmitter::checkRunOnceContext()
bool
BytecodeEmitter::needsImplicitThis()
{
if (sc->isFunctionBox() && sc->asFunctionBox()->inWith)
if (sc->inWith())
return true;
for (StmtInfoBCE* stmt = topStmt; stmt; stmt = stmt->down) {
if (stmt->type == STMT_WITH)
return true;
}
return false;
}
@ -3406,6 +3407,19 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
*/
FunctionBox* funbox = sc->asFunctionBox();
// Link the function and the script to each other, so that StaticScopeIter
// may walk the scope chain of currently compiling scripts.
RootedFunction fun(cx, funbox->function());
MOZ_ASSERT(fun->isInterpreted());
script->setFunction(fun);
if (fun->isInterpretedLazy())
fun->setUnlazifiedScript(script);
else
fun->setScript(script);
if (funbox->argumentsHasLocalBinding()) {
MOZ_ASSERT(offset() == 0); /* See JSScript::argumentsBytecode. */
switchToPrologue();
@ -3509,15 +3523,6 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
MOZ_ASSERT(!script->hasRunOnce());
}
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
RootedFunction fun(cx, script->functionNonDelazifying());
MOZ_ASSERT(fun->isInterpreted());
if (fun->isInterpretedLazy())
fun->setUnlazifiedScript(script);
else
fun->setScript(script);
tellDebuggerAboutCompiledScript(cx);
return true;
@ -5765,8 +5770,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
Rooted<JSScript*> parent(cx, script);
CompileOptions options(cx, parser->options());
options.setMutedErrors(parent->mutedErrors())
.setHasPollutedScope(parent->hasPollutedGlobalScope())
.setSelfHostingMode(parent->selfHosted())
.setNoScriptRval(false)
.setForEval(false)
.setVersion(parent->getVersion());

View File

@ -22,7 +22,7 @@
namespace js {
class StaticEvalObject;
class ScopeObject;
namespace frontend {

View File

@ -596,7 +596,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct
bufEnd(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
inWith(false), // initialized below
inWith_(false), // initialized below
inGenexpLambda(false),
hasDestructuringArgs(false),
useAsm(false),
@ -612,7 +612,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct
MOZ_ASSERT(fun->isTenured());
if (!outerpc) {
inWith = false;
inWith_ = false;
} else if (outerpc->parsingWith) {
// This covers cases that don't involve eval(). For example:
@ -621,7 +621,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct
//
// In this case, |outerpc| corresponds to global code, and
// outerpc->parsingWith is true.
inWith = true;
inWith_ = true;
} else if (outerpc->sc->isFunctionBox()) {
// This is like the above case, but for more deeply nested functions.
@ -632,8 +632,17 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct
// In this case, the inner anonymous function needs to inherit the
// setting of |inWith| from the outer one.
FunctionBox* parent = outerpc->sc->asFunctionBox();
if (parent && parent->inWith)
inWith = true;
if (parent && parent->inWith())
inWith_ = true;
} else {
// This is like the above case, but when inside eval.
//
// For example:
//
// with(o) { eval("(function() { g(); })();"); }
//
// In this case, the static scope chain tells us the presence of with.
inWith_ = outerpc->sc->inWith();
}
}
@ -2215,7 +2224,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox* funbo
// while its ParseContext and associated lexdeps and inner functions are
// still available.
if (funbox->inWith)
if (funbox->inWith())
return abortIfSyntaxParser();
size_t numFreeVariables = pc->lexdeps->count();

View File

@ -235,6 +235,7 @@ class SharedContext
SuperProperty
};
virtual bool allowSyntax(AllowedSyntax allowed) const = 0;
virtual bool inWith() const = 0;
protected:
static bool FunctionAllowsSyntax(JSFunction* func, AllowedSyntax allowed)
@ -256,21 +257,13 @@ class SharedContext
class GlobalSharedContext : public SharedContext
{
private:
Handle<StaticEvalObject*> staticEvalScope_;
Handle<ScopeObject*> topStaticScope_;
bool allowNewTarget_;
bool allowSuperProperty_;
bool inWith_;
public:
GlobalSharedContext(ExclusiveContext* cx,
Directives directives, Handle<StaticEvalObject*> staticEvalScope,
bool extraWarnings)
: SharedContext(cx, directives, extraWarnings),
staticEvalScope_(staticEvalScope)
{}
ObjectBox* toObjectBox() { return nullptr; }
HandleObject evalStaticScope() const { return staticEvalScope_; }
bool allowSyntax(AllowedSyntax allowed) const {
StaticScopeIter<CanGC> it(context, staticEvalScope_);
bool computeAllowSyntax(AllowedSyntax allowed) const {
StaticScopeIter<CanGC> it(context, topStaticScope_);
for (; !it.done(); it++) {
if (it.type() == StaticScopeIter<CanGC>::Function &&
!it.fun().isArrow())
@ -280,6 +273,40 @@ class GlobalSharedContext : public SharedContext
}
return false;
}
bool computeInWith() const {
for (StaticScopeIter<CanGC> it(context, topStaticScope_); !it.done(); it++) {
if (it.type() == StaticScopeIter<CanGC>::With)
return true;
}
return false;
}
public:
GlobalSharedContext(ExclusiveContext* cx,
Directives directives, Handle<ScopeObject*> topStaticScope,
bool extraWarnings)
: SharedContext(cx, directives, extraWarnings),
topStaticScope_(topStaticScope),
allowNewTarget_(computeAllowSyntax(AllowedSyntax::NewTarget)),
allowSuperProperty_(computeAllowSyntax(AllowedSyntax::SuperProperty)),
inWith_(computeInWith())
{}
ObjectBox* toObjectBox() override { return nullptr; }
HandleObject topStaticScope() const { return topStaticScope_; }
bool allowSyntax(AllowedSyntax allowSyntax) const override {
switch (allowSyntax) {
case AllowedSyntax::NewTarget:
// Any function supports new.target
return allowNewTarget_;
case AllowedSyntax::SuperProperty:
return allowSuperProperty_;
default:;
}
MOZ_CRASH("Unknown AllowedSyntax query");
}
bool inWith() const override { return inWith_; }
};
class FunctionBox : public ObjectBox, public SharedContext
@ -293,7 +320,7 @@ class FunctionBox : public ObjectBox, public SharedContext
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
bool inWith:1; /* some enclosing scope is a with-statement */
bool inWith_:1; /* some enclosing scope is a with-statement */
bool inGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
bool useAsm:1; /* see useAsmOrInsideUseAsm */
@ -311,7 +338,7 @@ class FunctionBox : public ObjectBox, public SharedContext
ParseContext<ParseHandler>* pc, Directives directives,
bool extraWarnings, GeneratorKind generatorKind);
ObjectBox* toObjectBox() { return this; }
ObjectBox* toObjectBox() override { return this; }
JSFunction* function() const { return &object->as<JSFunction>(); }
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
@ -372,9 +399,13 @@ class FunctionBox : public ObjectBox, public SharedContext
isGenerator();
}
bool allowSyntax(AllowedSyntax allowed) const {
bool allowSyntax(AllowedSyntax allowed) const override {
return FunctionAllowsSyntax(function(), allowed);
}
bool inWith() const override {
return inWith_;
}
};
inline FunctionBox*

View File

@ -211,18 +211,6 @@ AutoGCRooter::trace(JSTracer* trc)
return;
}
case OBJOBJHASHMAP: {
AutoObjectObjectHashMap::HashMapImpl& map = static_cast<AutoObjectObjectHashMap*>(this)->map;
for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) {
TraceRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value");
JSObject* key = e.front().key();
TraceRoot(trc, &key, "AutoObjectObjectHashMap key");
if (key != e.front().key())
e.rekeyFront(key);
}
return;
}
case OBJU32HASHMAP: {
AutoObjectUnsigned32HashMap* self = static_cast<AutoObjectUnsigned32HashMap*>(this);
AutoObjectUnsigned32HashMap::HashMapImpl& map = self->map;
@ -235,18 +223,6 @@ AutoGCRooter::trace(JSTracer* trc)
return;
}
case OBJHASHSET: {
AutoObjectHashSet* self = static_cast<AutoObjectHashSet*>(this);
AutoObjectHashSet::HashSetImpl& set = self->set;
for (AutoObjectHashSet::Enum e(set); !e.empty(); e.popFront()) {
JSObject* obj = e.front();
TraceRoot(trc, &obj, "AutoObjectHashSet value");
if (obj != e.front())
e.rekeyFront(obj);
}
return;
}
case HASHABLEVALUE: {
AutoHashableValueRooter* rooter = static_cast<AutoHashableValueRooter*>(this);
rooter->trace(trc);

View File

@ -0,0 +1,30 @@
load(libdir + "class.js");
var test = `
function test(fun) {
fun();
}
// Generate a CallAnyScripted stub in test()
for (let i = 0; i < 20; i++) {
test(function() { /* wheeee */ });
}
class foo {
constructor() { }
}
// Compile foo()
for (let i = 0; i < 11; i++)
new foo();
try {
test(foo);
throw new Error("Invoking a class constructor without new must throw");
} catch (e if e instanceof TypeError) { }
`;
if (classesEnabled())
eval(test);

View File

@ -4,6 +4,7 @@ var g = newGlobal();
var g2 = newGlobal();
var dbg = new Debugger(g, g2);
var log = '';
var canary = 42;
dbg.onNewScript = function (evalScript) {
log += 'e';
@ -24,5 +25,8 @@ dbg.onDebuggerStatement = function (frame) {
};
assertEq(log, '');
var evalScope = g.evalReturningScope("debugger; // nee", g2);
var evalScope = g.evalReturningScope("canary = 'dead'; debugger; // nee", g2);
assertEq(log, 'ecbd');
assertEq(canary, 42);
assertEq(evalScope.canary, 'dead');

View File

@ -0,0 +1,45 @@
// |jit-test| test-join=--no-unboxed-objects
//
// Unboxed object optimization might not trigger in all cases, thus we ensure
// that Scalar Replacement optimization is working well independently of the
// object representation.
// Ion eager fails the test below because we have not yet created any
// template object in baseline before running the content of the top-level
// function.
if (getJitCompilerOptions()["ion.warmup.trigger"] <= 30)
setJitCompilerOption("ion.warmup.trigger", 30);
// This test checks that we are able to remove the getelem & setelem with scalar
// replacement, so we should not force inline caches, as this would skip the
// generation of getelem & setelem instructions.
if (getJitCompilerOptions()["ion.forceinlineCaches"])
setJitCompilerOption("ion.forceinlineCaches", 0);
var uceFault = function (j) {
if (j >= 31)
uceFault = function (j) { return true; };
return false;
}
function f(j) {
var i = Math.pow(2, j) | 0;
var obj = {
i: i,
v: i + i
};
assertRecoveredOnBailout(obj, true);
assertRecoveredOnBailout(obj.v, true);
if (uceFault(j) || uceFault(j)) {
// MObjectState::recover should neither fail,
// nor coerce its result to an int32.
assertEq(obj.v, 2 * i);
}
return 2 * obj.i;
}
var min = -100;
for (var j = min; j <= 31; ++j) {
with({}){};
f(j);
}

View File

@ -704,13 +704,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
scopeChain = fun->environment();
}
} else {
// For global scripts without a polluted global scope the scope
// For global scripts without a non-syntactic scope the scope
// chain is the script's global (Ion does not compile scripts
// with a polluted global scope). Also note that it's invalid to
// resume into the prologue in this case because the prologue
// expects the scope chain in R1 for eval and global scripts.
// with a non-syntactic global scope). Also note that it's
// invalid to resume into the prologue in this case because
// the prologue expects the scope chain in R1 for eval and
// global scripts.
MOZ_ASSERT(!script->isForEval());
MOZ_ASSERT(!script->hasPollutedGlobalScope());
MOZ_ASSERT(!script->hasNonSyntacticScope());
scopeChain = &(script->global());
}
}
@ -1591,6 +1592,27 @@ jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIter
return BAILOUT_RETURN_OK;
}
static bool
InvalidateAfterBailout(JSContext* cx, HandleScript outerScript, const char* reason)
{
// In some cases, the computation of recover instruction can invalidate the
// Ion script before we reach the end of the bailout. Thus, if the outer
// script no longer have any Ion script attached, then we just skip the
// invalidation.
//
// For example, such case can happen if the template object for an unboxed
// objects no longer match the content of its properties (see Bug 1174547)
if (!outerScript->hasIonScript()) {
JitSpew(JitSpew_BaselineBailouts, "Ion script is already invalidated");
return true;
}
MOZ_ASSERT(!outerScript->ionScript()->invalidated());
JitSpew(JitSpew_BaselineBailouts, "Invalidating due to %s", reason);
return Invalidate(cx, outerScript);
}
static bool
HandleBoundsCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
{
@ -1598,13 +1620,10 @@ HandleBoundsCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript i
innerScript->filename(), innerScript->lineno(),
outerScript->filename(), outerScript->lineno());
MOZ_ASSERT(!outerScript->ionScript()->invalidated());
if (!innerScript->failedBoundsCheck())
innerScript->setFailedBoundsCheck();
JitSpew(JitSpew_BaselineBailouts, "Invalidating due to bounds check failure");
if (!Invalidate(cx, outerScript))
if (!InvalidateAfterBailout(cx, outerScript, "bounds check failure"))
return false;
if (innerScript->hasIonScript() && !Invalidate(cx, innerScript))
@ -1620,27 +1639,22 @@ HandleShapeGuardFailure(JSContext* cx, HandleScript outerScript, HandleScript in
innerScript->filename(), innerScript->lineno(),
outerScript->filename(), outerScript->lineno());
MOZ_ASSERT(!outerScript->ionScript()->invalidated());
// TODO: Currently this mimic's Ion's handling of this case. Investigate setting
// the flag on innerScript as opposed to outerScript, and maybe invalidating both
// inner and outer scripts, instead of just the outer one.
outerScript->setFailedShapeGuard();
JitSpew(JitSpew_BaselineBailouts, "Invalidating due to shape guard failure");
return Invalidate(cx, outerScript);
return InvalidateAfterBailout(cx, outerScript, "shape guard failure");
}
static bool
HandleBaselineInfoBailout(JSContext* cx, JSScript* outerScript, JSScript* innerScript)
HandleBaselineInfoBailout(JSContext* cx, HandleScript outerScript, HandleScript innerScript)
{
JitSpew(JitSpew_IonBailouts, "Baseline info failure %s:%d, inlined into %s:%d",
innerScript->filename(), innerScript->lineno(),
outerScript->filename(), outerScript->lineno());
MOZ_ASSERT(!outerScript->ionScript()->invalidated());
JitSpew(JitSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
return Invalidate(cx, outerScript);
return InvalidateAfterBailout(cx, outerScript, "invalid baseline info");
}
static bool
@ -1650,13 +1664,10 @@ HandleLexicalCheckFailure(JSContext* cx, HandleScript outerScript, HandleScript
innerScript->filename(), innerScript->lineno(),
outerScript->filename(), outerScript->lineno());
MOZ_ASSERT(!outerScript->ionScript()->invalidated());
if (!innerScript->failedLexicalCheck())
innerScript->setFailedLexicalCheck();
JitSpew(JitSpew_BaselineBailouts, "Invalidating due to lexical check failure");
if (!Invalidate(cx, outerScript))
if (!InvalidateAfterBailout(cx, outerScript, "lexical check failure"))
return false;
if (innerScript->hasIonScript() && !Invalidate(cx, innerScript))

View File

@ -20,6 +20,7 @@
#endif
#include "jit/SharedICHelpers.h"
#include "jit/VMFunctions.h"
#include "vm/ScopeObject.h"
#include "vm/TraceLogging.h"
#include "jsscriptinlines.h"
@ -128,7 +129,7 @@ BaselineCompiler::compile()
return Method_Error;
if (fun->isNamedLambda()) {
RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, TenuredObject));
if (!declEnvObject)
return Method_Error;
templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject);
@ -2062,7 +2063,7 @@ BaselineCompiler::emit_JSOP_IN()
bool
BaselineCompiler::emit_JSOP_GETGNAME()
{
if (script->hasPollutedGlobalScope())
if (script->hasNonSyntacticScope())
return emit_JSOP_GETNAME();
RootedPropertyName name(cx, script->getName(pc));
@ -2097,7 +2098,7 @@ BaselineCompiler::emit_JSOP_GETGNAME()
bool
BaselineCompiler::emit_JSOP_BINDGNAME()
{
if (!script->hasPollutedGlobalScope()) {
if (!script->hasNonSyntacticScope()) {
frame.push(ObjectValue(script->global()));
return true;
}
@ -2920,7 +2921,7 @@ BaselineCompiler::emit_JSOP_IMPLICITTHIS()
bool
BaselineCompiler::emit_JSOP_GIMPLICITTHIS()
{
if (!script->hasPollutedGlobalScope()) {
if (!script->hasNonSyntacticScope()) {
frame.push(UndefinedValue());
return true;
}

View File

@ -6133,7 +6133,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
attached = true;
}
if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) {
if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub, scopeChain.as<GlobalObject>(),
name, &attached, &isTemporarilyUnoptimizable))
{
@ -6163,7 +6163,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
if (attached)
return true;
if (IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) {
if (IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
Handle<GlobalObject*> global = scopeChain.as<GlobalObject>();
if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, global, name, &attached))
return false;
@ -9560,6 +9560,10 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
if (constructing && !fun->isConstructor())
return true;
// Likewise, if the callee is a class constructor, we have to throw.
if (!constructing && fun->isClassConstructor())
return true;
if (!fun->hasJITCode()) {
// Don't treat this as an unoptimizable case, as we'll add a stub
// when the callee becomes hot.
@ -10418,10 +10422,13 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
// Ensure the object is a function.
masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
&failure);
if (isConstructing_)
if (isConstructing_) {
masm.branchIfNotInterpretedConstructor(callee, regs.getAny(), &failure);
else
} else {
masm.branchIfFunctionHasNoScript(callee, &failure);
masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee,
regs.getAny(), &failure);
}
}
// Load the JSScript.

View File

@ -170,7 +170,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
case JSOP_GETGNAME:
case JSOP_SETGNAME:
case JSOP_STRICTSETGNAME:
if (script_->hasPollutedGlobalScope())
if (script_->hasNonSyntacticScope())
usesScopeChain_ = true;
break;

View File

@ -3028,10 +3028,12 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
// Guard that calleereg is an interpreted function with a JSScript.
// If we are constructing, also ensure the callee is a constructor.
if (call->mir()->isConstructing())
if (call->mir()->isConstructing()) {
masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
else
} else {
masm.branchIfFunctionHasNoScript(calleereg, &invoke);
masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, calleereg, objreg, &invoke);
}
// Knowing that calleereg is a non-native function, load the JSScript.
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
@ -3125,6 +3127,7 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
// Native single targets are handled by LCallNative.
MOZ_ASSERT(!target->isNative());
MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing());
// Missing arguments must have been explicitly appended by the IonBuilder.
DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack);
@ -4683,7 +4686,7 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox* lir)
bailoutFrom(&bail, lir->snapshot());
}
typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, gc::InitialHeap);
typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, NewObjectKind);
static const VMFunction NewDeclEnvObjectInfo =
FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
@ -4697,7 +4700,7 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject* lir)
// If we have a template object, we can inline call object creation.
OutOfLineCode* ool = oolCallVM(NewDeclEnvObjectInfo, lir,
ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(gc::DefaultHeap)),
ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(GenericObject)),
StoreRegisterTo(objReg));
bool initContents = ShouldInitFixedSlots(lir, templateObj);

View File

@ -1410,7 +1410,50 @@ OptimizeMIR(MIRGenerator* mir)
if (mir->shouldCancel("Alias analysis"))
return false;
if (!mir->compilingAsmJS()) {
// We only eliminate dead resume point operands in the first pass
// because it is currently unsound to do so after GVN.
//
// Consider the following example, where def1 dominates, and is
// congruent with def4, and use3 dominates, and is congruent with,
// use6.
//
// def1
// nop2
// resumepoint def1
// use3 def1
// def4
// nop5
// resumepoint def4
// use6 def4
// use7 use3 use6
//
// Assume that use3, use6, and use7 can cause OSI and are
// non-effectful. That is, use3 will resume at nop2, and use6 and use7
// will resume at nop5.
//
// After GVN, since def1 =~ def4, we have:
//
// def4 - replaced with def1 and pruned
// use6 - replaced with use3 and pruned
// use7 - renumbered to use5
//
// def1
// nop2
// resumepoint def1
// use3 def1
// nop4
// resumepoint def1
// use5 use3 use3
//
// nop4's resumepoint's operand of def1 is considered dead, because it
// is dominated by the last use of def1, use3.
//
// However, if use5 causes OSI, we will resume at nop4's resume
// point. The baseline frame at that point expects the now-pruned def4
// to exist. However, since it was replaced with def1 by GVN, and def1
// is dead at the point of nop4, the baseline frame incorrectly gets
// an optimized out value.
if (!mir->compilingAsmJS() && doRepeatOptimizations == 1) {
// Eliminating dead resume point operands requires basic block
// instructions to be numbered. Reuse the numbering computed during
// alias analysis.
@ -2130,12 +2173,12 @@ CheckScript(JSContext* cx, JSScript* script, bool osr)
return false;
}
if (script->hasPollutedGlobalScope() && !script->functionNonDelazifying()) {
// Support functions with a polluted global scope but not other
if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) {
// Support functions with a non-syntactic global scope but not other
// scripts. For global scripts, IonBuilder currently uses the global
// object as scope chain, this is not valid when the script has a
// polluted global scope.
TrackAndSpewIonAbort(cx, script, "has polluted global scope");
// non-syntactic global scope.
TrackAndSpewIonAbort(cx, script, "has non-syntactic global scope");
return false;
}

View File

@ -438,6 +438,11 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
return DontInline(inlineScript, "Callee is not a constructor");
}
if (!callInfo.constructing() && target->isClassConstructor()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
return DontInline(inlineScript, "Not constructing class constructor");
}
AnalysisMode analysisMode = info().analysisMode();
if (!CanIonCompile(inlineScript, analysisMode)) {
trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
@ -613,7 +618,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
type = MIRType_Undefined;
break;
case JSOP_GIMPLICITTHIS:
if (!script()->hasPollutedGlobalScope())
if (!script()->hasNonSyntacticScope())
type = MIRType_Undefined;
break;
case JSOP_NULL:
@ -1177,10 +1182,10 @@ IonBuilder::initScopeChain(MDefinition* callee)
return false;
}
} else {
// For global scripts without a polluted global scope, the scope chain
// is the global object.
// For global scripts without a non-syntactic global scope, the scope
// chain is the global object.
MOZ_ASSERT(!script()->isForEval());
MOZ_ASSERT(!script()->hasPollutedGlobalScope());
MOZ_ASSERT(!script()->hasNonSyntacticScope());
scope = constant(ObjectValue(script()->global()));
}
@ -1795,7 +1800,7 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_GETGNAME:
{
PropertyName* name = info().getAtom(pc)->asPropertyName();
if (!script()->hasPollutedGlobalScope())
if (!script()->hasNonSyntacticScope())
return jsop_getgname(name);
return jsop_getname(name);
}
@ -1804,7 +1809,7 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_STRICTSETGNAME:
{
PropertyName* name = info().getAtom(pc)->asPropertyName();
if (script()->hasPollutedGlobalScope())
if (script()->hasNonSyntacticScope())
return jsop_setprop(name);
JSObject* obj = &script()->global();
return setStaticName(obj, name);
@ -1823,7 +1828,7 @@ IonBuilder::inspectOpcode(JSOp op)
}
case JSOP_BINDGNAME:
if (!script()->hasPollutedGlobalScope())
if (!script()->hasNonSyntacticScope())
return pushConstant(ObjectValue(script()->global()));
// Fall through to JSOP_BINDNAME
case JSOP_BINDNAME:
@ -1970,7 +1975,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_debugger();
case JSOP_GIMPLICITTHIS:
if (!script()->hasPollutedGlobalScope())
if (!script()->hasNonSyntacticScope())
return pushConstant(UndefinedValue());
// Just fall through to the unsupported bytecode case.
@ -7608,7 +7613,7 @@ bool
IonBuilder::jsop_getname(PropertyName* name)
{
MDefinition* object;
if (IsGlobalOp(JSOp(*pc)) && !script()->hasPollutedGlobalScope()) {
if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope()) {
MInstruction* global = constant(ObjectValue(script()->global()));
object = global;
} else {

View File

@ -2553,7 +2553,7 @@ InlineFrameIterator::computeScopeChain(Value scopeChainValue, MaybeReadFallback&
// Ion does not handle non-function scripts that have anything other than
// the global on their scope chain.
MOZ_ASSERT(!script()->isForEval());
MOZ_ASSERT(!script()->hasPollutedGlobalScope());
MOZ_ASSERT(!script()->hasNonSyntacticScope());
return &script()->global();
}

View File

@ -456,7 +456,7 @@ LIRGenerator::visitCall(MCall* call)
MOZ_ASSERT(ok, "How can we not have four temp registers?");
lir = new(alloc()) LCallDOMNative(tempFixed(cxReg), tempFixed(objReg),
tempFixed(privReg), tempFixed(argsReg));
} else if (target) {
} else if (target && !(target->isClassConstructor() && !call->isConstructing())) {
// Call known functions.
if (target->isNative()) {
Register cxReg, numReg, vpReg, tmpReg;

View File

@ -941,6 +941,15 @@ class MacroAssembler : public MacroAssemblerSpecific
branchTestClassIsProxy(proxy, scratch, label);
}
void branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun,
Register scratch, Label* label)
{
Address flags(fun, JSFunction::offsetOfFlags());
load32(flags, scratch);
and32(Imm32(JSFunction::FUNCTION_KIND_MASK), scratch);
branch32(cond, scratch, Imm32(kind << JSFunction::FUNCTION_KIND_SHIFT), label);
}
public:
#ifndef JS_CODEGEN_ARM64
// StackPointer manipulation functions.

View File

@ -24,6 +24,7 @@
#include "jit/MIRGraph.h"
#include "jit/VMFunctions.h"
#include "vm/Interpreter.h"
#include "vm/String.h"
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
@ -1375,6 +1376,8 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const
if (object->is<UnboxedPlainObject>()) {
const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout();
RootedId id(cx);
RootedValue receiver(cx, ObjectValue(*object));
const UnboxedLayout::PropertyVector& properties = layout.properties();
for (size_t i = 0; i < properties.length(); i++) {
val = iter.read();
@ -1392,7 +1395,14 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const
if (properties[i].type == JSVAL_TYPE_BOOLEAN)
val.setBoolean(val.toInt32() != 0);
MOZ_ALWAYS_TRUE(object->as<UnboxedPlainObject>().setValue(cx, properties[i], val));
id = NameToId(properties[i].name);
ObjectOpResult result;
// SetProperty can only fail due to OOM.
if (!SetProperty(cx, object, id, val, receiver, result))
return false;
if (!result)
return result.reportError(cx, object, id);
}
} else {
RootedNativeObject nativeObject(cx, &object->as<NativeObject>());

View File

@ -102,6 +102,7 @@ MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: i
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
// JSON
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")

View File

@ -24,6 +24,7 @@ UNIFIED_SOURCES += [
'testDefineGetterSetterNonEnumerable.cpp',
'testDefineProperty.cpp',
'testDefinePropertyIgnoredAttributes.cpp',
'testDifferentNewTargetInvokeConstructor.cpp',
'testEnclosingFunction.cpp',
'testErrorCopying.cpp',
'testException.cpp',

View File

@ -0,0 +1,37 @@
/* 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 "jsapi.h"
#include "jsapi-tests/tests.h"
BEGIN_TEST(testDifferentNewTargetInvokeConstructor)
{
JS::RootedValue func(cx);
JS::RootedValue otherFunc(cx);
EVAL("(function() { /* This is a different new.target function */ })", &otherFunc);
EVAL("(function(expected) { if (expected !== new.target) throw new Error('whoops'); })",
&func);
JS::AutoValueArray<1> args(cx);
args[0].set(otherFunc);
JS::RootedValue rval(cx);
JS::RootedObject newTarget(cx, &otherFunc.toObject());
CHECK(JS::Construct(cx, func, newTarget, args, &rval));
// It should fail, though, if newTarget is not a constructor
JS::RootedValue plain(cx);
EVAL("({})", &plain);
args[0].set(plain);
newTarget = &plain.toObject();
CHECK(!JS::Construct(cx, func, newTarget, args, &rval));
return true;
}
END_TEST(testDifferentNewTargetInvokeConstructor)

Some files were not shown because too many files have changed in this diff Show More