mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
b8a0bf4eb3
@ -516,7 +516,7 @@ GetClosestInterestingAccessible(id anObject)
|
||||
struct RoleDescrMap
|
||||
{
|
||||
NSString* role;
|
||||
const nsString& description;
|
||||
const nsString description;
|
||||
};
|
||||
|
||||
static const RoleDescrMap sRoleDescrMap[] = {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
23
dom/base/ChromeUtils.cpp
Normal 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
52
dom/base/ChromeUtils.h
Normal 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__
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -933,6 +933,9 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
|
||||
|
||||
ourFrameFrame->EndSwapDocShells(otherFrame);
|
||||
|
||||
ourShell->BackingScaleFactorChanged();
|
||||
otherShell->BackingScaleFactorChanged();
|
||||
|
||||
ourDoc->FlushPendingNotifications(Flush_Layout);
|
||||
otherDoc->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
||||
|
@ -283,15 +283,12 @@ 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.
|
||||
// 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:
|
||||
class Impl : public AbstractMirror<T>, public WatchTarget
|
||||
@ -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:
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define StateWatching_h_
|
||||
|
||||
#include "AbstractThread.h"
|
||||
#include "TaskDispatcher.h"
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
|
@ -587,6 +587,7 @@ GMPChild::ShutdownComplete()
|
||||
{
|
||||
LOGD("%s", __FUNCTION__);
|
||||
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
|
||||
mAsyncShutdown = nullptr;
|
||||
SendAsyncShutdownComplete();
|
||||
}
|
||||
|
||||
|
@ -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,19 +254,57 @@ 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 UnloadPlugins() to do initial sync shutdown...
|
||||
while (mWaitingForPluginsSyncShutdown) {
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
|
||||
// Wait for plugins to do async shutdown...
|
||||
while (mWaitingForPluginsAsyncShutdown) {
|
||||
// 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)) {
|
||||
MOZ_ASSERT(mShuttingDown);
|
||||
@ -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);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mAsyncShutdownPlugins.RemoveElement(aParent);
|
||||
if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
|
||||
}
|
||||
|
||||
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,12 +438,10 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
|
||||
mPlugins.Clear();
|
||||
}
|
||||
|
||||
if (mAsyncShutdownPlugins.IsEmpty()) {
|
||||
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
|
||||
this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::CrashPlugins()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 +=
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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!
|
||||
|
@ -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.
|
||||
|
@ -7,6 +7,6 @@
|
||||
/**
|
||||
* A HeapSnapshot represents a snapshot of the heap graph
|
||||
*/
|
||||
[ChromeOnly, Exposed=(Window,System)]
|
||||
[ChromeOnly, Exposed=(Window,System,Worker)]
|
||||
interface HeapSnapshot {
|
||||
};
|
||||
|
64
dom/webidl/ThreadSafeChromeUtils.webidl
Normal file
64
dom/webidl/ThreadSafeChromeUtils.webidl
Normal 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;
|
||||
};
|
@ -522,6 +522,7 @@ WEBIDL_FILES = [
|
||||
'TextTrack.webidl',
|
||||
'TextTrackCueList.webidl',
|
||||
'TextTrackList.webidl',
|
||||
'ThreadSafeChromeUtils.webidl',
|
||||
'TimeEvent.webidl',
|
||||
'TimeRanges.webidl',
|
||||
'Touch.webidl',
|
||||
|
@ -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
|
||||
|
1
dom/workers/test/serviceworkers/eval_worker.js
Normal file
1
dom/workers/test/serviceworkers/eval_worker.js
Normal file
@ -0,0 +1 @@
|
||||
eval('1+1');
|
@ -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]
|
||||
|
42
dom/workers/test/serviceworkers/test_eval_allowed.html
Normal file
42
dom/workers/test/serviceworkers/test_eval_allowed.html
Normal 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>
|
45
dom/workers/test/serviceworkers/test_eval_not_allowed.html
Normal file
45
dom/workers/test/serviceworkers/test_eval_not_allowed.html
Normal 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>
|
@ -0,0 +1 @@
|
||||
Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'"
|
@ -866,6 +866,8 @@ struct ZoomConstraints {
|
||||
}
|
||||
};
|
||||
|
||||
typedef Maybe<ZoomConstraints> MaybeZoomConstraints;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,10 +1058,14 @@ 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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -62,7 +62,6 @@ private:
|
||||
MessageLoop* mUILoop;
|
||||
|
||||
void InitializeRoot();
|
||||
float GetPresShellResolution() const;
|
||||
nsIPresShell* GetPresShell() const;
|
||||
nsIDocument* GetDocument() const;
|
||||
already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils() const;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ public:
|
||||
|
||||
static bool CanUseDirect3D9();
|
||||
static bool CanUseDirect3D11();
|
||||
static bool CanUseHardwareVideoDecoding();
|
||||
virtual bool CanUseHardwareVideoDecoding();
|
||||
static bool CanUseDirect3D11ANGLE();
|
||||
|
||||
/**
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
|
@ -108,6 +108,7 @@ namespace JS {
|
||||
_(CantInlineNoBaseline) \
|
||||
_(CantInlineLazy) \
|
||||
_(CantInlineNotConstructor) \
|
||||
_(CantInlineClassConstructor) \
|
||||
_(CantInlineDisabledIon) \
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineRecursive) \
|
||||
|
@ -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;
|
||||
@ -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()))
|
||||
{
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
class StaticEvalObject;
|
||||
class ScopeObject;
|
||||
|
||||
namespace frontend {
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
@ -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');
|
||||
|
||||
|
45
js/src/jit-test/tests/ion/recover-object-bug1175233.js
Normal file
45
js/src/jit-test/tests/ion/recover-object-bug1175233.js
Normal 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);
|
||||
}
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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>());
|
||||
|
@ -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")
|
||||
|
@ -24,6 +24,7 @@ UNIFIED_SOURCES += [
|
||||
'testDefineGetterSetterNonEnumerable.cpp',
|
||||
'testDefineProperty.cpp',
|
||||
'testDefinePropertyIgnoredAttributes.cpp',
|
||||
'testDifferentNewTargetInvokeConstructor.cpp',
|
||||
'testEnclosingFunction.cpp',
|
||||
'testErrorCopying.cpp',
|
||||
'testException.cpp',
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user