mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1233497 - Fix infrastructure for disallowing unsafe CPOWs in browser code. r=mrbkap (d8c6be9ef0) - Bug 1235615 - Split JS::CompartmentOptions into JS::CompartmentCreationOptions that are immutable characteristics of a compartment, and JS::CompartmentBehaviors that may be changed after the compartment's been created. r=terrence (d664435b2c) - Bug1245801- Disable non-standard flag argument of String.prototype. {search,match,replace} in non-release channel. r=jandem (2e41d087e4) - Bug 1207922 - Part 1: Self-host RegExp.prototype.{exec,test}. r=till,h4writer (21dddac0e3) - Bug 1207922 - Part 2: Propagate OOM thrown from stub generation. r=till (507c3fc5e0) - Bug 1207922 - Part 3: Add masm.branchLatin1String and masm.branchTwoByteString. r=h4writer (59f56d60d8) - Bug 1207922 - Clean-up RRegExp{Match,Test}er function in Recover.cpp. r=arai (fd4f0e4744) - Bug 1226904 - Fix boundary checking for leaves collecting. r=roc (0736fec30c) - Bug 1222880 - Followup to handle the case when aStopAtAnimatedGeometryRoot isn't an ancestor of aAnimatedGeometryRoot. CLOSED TREE (648d12bbcb) - Bug 1222880. Followup to the followup to handle the case when aStopAtAnimatedGeometryRoot isn't an ancestor of aAnimatedGeometryRoot the same way we did before the patches of this bug. r=mattwoordrow (more or less) (e4ce4414ef) - Bug 1200611 - Size ImageLayers correctly for <img>s using object-fit. r=dholbert (cc3b82a126) - Bug 1232852 (part 1) - Set |aSnap| in two GetOpaqueRegion() overloadings that are missing it. r=roc. (114660bbbe) - Bug 1232852 (part 2) - Remove some dead member functions from nsILayoutDebugger. r=roc. (b9eb5ca6b4) - Bug 1233605 - Cull some uses of gfxContext. r=dholbert. (2ae4a1d390) - Bug 1059519. Relax assertion. r=mstange (79a1f08992) - Bug 1176395 - When an element is both position:sticky and transformed, apply the position:sticky outside the transform. r=roc (b2d86a714b) - Bug 1147673 - Use ancestor clip for root scrollable framemetrics clips. r=tn (6fd10a0ea3) - Bug 1187804 - Reftests for async scrolling with position:fixed in an iframe. r=kats (2e30186596) - Bug 1223944 - Reftest bg-fixed-transformed-image-ref.html fails for Fennec when C++APZ enabled. r=kats (99bfa3344b) - Bug 1208829 - Reftest. r=mstange (e35a18d12a) - Bug 1224209. Add reftest. (acfd2fba9d) - Bug 1201889 - Reftest. r=mstange (ec4b26fbd2) - Bug 1208829 - Another reftest. r=botond (558ca3cdbc) - Bug 1147673 - Make display items know about their scroll clips. r=tn, r=roc (3485c3fada) - Bug 1152049 - Rename GetClippedBoundsUpTo into GetScrollClippedBoundsUpTo. r=tn (aeeaf3bcdb) - Bug 1232852 (part 3) - Remove unused parameters from some accessibility code. r=tbsaunde. (97041bf561) - Bug 1232852 (part 4) - Remove some unused parameters in and around layout/base/. r=heycam. (2dcf169efa) - Bug 1232852 (part 5) - Remove some unused parameters in and around layout/base/. r=heycam. (9a302428f1) - Bug 1232852 (part 6) - Remove unused parameters from some layout sort functions. r=tn. (26faa2c71f) - Bug 1232852 (part 7) - Remove some unused parameters in and around layout/base/. r=tn. (6f9417aa0b) - Bug 1232852 (part 8) - Remove some unused parameters in and around layout/base/. r=roc. (30315134c4) - Bug 1186774 - Scroll position (scrollX/scrollY) should be restored after popstate, not before, r=bz (3d8cd617ce) - Bug 1155730, implement History.scrollRestoration r=jst (4e0ffb69a9) - Bug 1237075 - Navigating from 'manual' to 'auto' session history entry should scroll the page, r=jst (d8eb9296bf) - Bug 1228229 part 2 - Add a helper to get the appropriate (pseudo-)element for a frame; r=dbaron (2a8b5bdc95) - Bug 1228229 part 3 - Factor out a method to get compositor-animatable overridden properties; r=dbaron (0e5fef1fc9) - Bug 1228229 part 4 - Add a flag to EffectSet to mark when the cascade needs to be updated; r=dbaron (206e42236e) - Bug 1228229 part 5 - Separate target element registration in NotifyAnimationTimingUpdated; r=dbaron (fe4b799d14) - Bug 1228229 part 6 - Mark the animation cascade results as dirty when an effect goes in or out of being "in effect"; r=dbaron (6be413b655) - Bug 1228229 part 7 - Add a method to Animation to indicate if it applies to the transitions level of the cascade; r=dbaron (d1845e299b) - Bug 1228229 part 8 - Add EffectCompositor::(Maybe)UpdateCascadeResults; r=dbaron (dfdd0b9822) - Bug 1228229 part 9 - Use EffectCompositor::UpdateCascadeResults; r=dbaron (917ec2023c) - Bug 1228229 part 10 - Remove no-longer-used cascade functions; r=dbaron (3dc6662f3a) - Bug 1228229 part 11 - Avoid calling nsRuleNode::ComputePropertiesOverridingAnimation when there are no compositor-animatable properties; r=dbaron (9b90a1d9a6) - Bug 1229662 (part 1) - Remove AzureState::clipWasReset. r=jrmuizel. (ce48b700f7) - Bug 1229662 (part 2) - Remove AzureState::parentTarget. r=jrmuizel. (95713803b1) - Bug 1229662 (part 3) - Remove AzureState::fillRule. r=jrmuizel. (e1f936af7d) - part of Bug 1232576 (part 1) - Move the reference |cairo_t*| from gfxContext (e81dd09541) - part of Bug 1232576 (part 2) - Rename gfxContext::GetCairo() as GetRefCairo() (82538c1451) - Bug 1232822 (part 1) - Moz2Dify SetupCairoFont(). r=jfkthame. (ef6b1e99b3) - Bug 1232822 (part 2) - Moz2Dify SetupGlyphExtents(). r=jfkthame. (9ed9a03559) - Bug 1232822 (part 3) - Moz2Dify gfxFont::CalcXScale() and gfxFont::PostShapingFixup(). r=jfkthame. (60f5f49df8) - Bug 1232822 (part 4) - Remove unused argument from SetPotentialLineBreaks(). r=jfkthame. (7fb087a26b) - Bug 1232822 (part 5) - Moz2Dify GetRoundOffsetsToPixels(). r=jfkthame. (4055a07cba) - Bug 1232822 (part 6) - Move RefCairo() from gfxContext to gfxFont. r=jfkthame. (c5d2db8eab) - Bug 1235185 - Fix clang -Wclass-varargs warnings in js/. r=bhackett (43fc9c0b1c) - Bug 1232772 - suppress numerous clang-style warnings when using clang-cl; r=glandium (c26dab4483) - Bug1204752- Disable thread-safe statics on VS2015 to fix WinXP startup crash. r=glandium (59c67ca7ba) - Bug 1235743 - Move compiler flags used for dependency generation to a separate variable. r=gps (649853408e) - Bug 1232159 followup, test for the existence of TypedObject so the test doesn't fail when it hits mozilla-aurora, r=efaust (c87a681a64)
This commit is contained in:
@@ -104,8 +104,8 @@ AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccHideEvent::
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode),
|
||||
AccHideEvent(Accessible* aTarget, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget),
|
||||
mNeedsShutdown(aNeedsShutdown)
|
||||
{
|
||||
mNextSibling = mAccessible->NextSibling();
|
||||
@@ -118,8 +118,8 @@ AccHideEvent::
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccShowEvent::
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
|
||||
AccShowEvent(Accessible* aTarget) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -212,8 +212,7 @@ private:
|
||||
class AccMutationEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget,
|
||||
nsINode* aTargetNode) :
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceMutationTextChange)
|
||||
{
|
||||
// Don't coalesce these since they are coalesced by reorder event. Coalesce
|
||||
@@ -250,8 +249,7 @@ protected:
|
||||
class AccHideEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode,
|
||||
bool aNeedsShutdown = true);
|
||||
explicit AccHideEvent(Accessible* aTarget, bool aNeedsShutdown = true);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eHideEvent;
|
||||
@@ -281,7 +279,7 @@ protected:
|
||||
class AccShowEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode);
|
||||
explicit AccShowEvent(Accessible* aTarget);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eShowEvent;
|
||||
|
||||
@@ -1515,7 +1515,7 @@ DocAccessible::DoInitialUpdate()
|
||||
uint32_t childCount = ChildCount();
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
Accessible* child = GetChildAt(i);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child);
|
||||
FireDelayedEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -1896,7 +1896,6 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
// this node already then it will be suppressed by this one.
|
||||
Accessible* focusedAcc = nullptr;
|
||||
|
||||
nsINode* node = aChild->GetNode();
|
||||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(aChild, &focusedAcc);
|
||||
@@ -1918,9 +1917,9 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
// Fire show/hide event.
|
||||
RefPtr<AccMutationEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(aChild, node);
|
||||
event = new AccShowEvent(aChild);
|
||||
else
|
||||
event = new AccHideEvent(aChild, node);
|
||||
event = new AccHideEvent(aChild);
|
||||
|
||||
FireDelayedEvent(event);
|
||||
aReorderEvent->AddSubMutationEvent(event);
|
||||
@@ -2093,8 +2092,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
@@ -2125,8 +2123,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
reorderEvent = new AccReorderEvent(aNewParent);
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
|
||||
FireDelayedEvent(showEvent);
|
||||
@@ -2144,8 +2141,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
|
||||
Accessible* parent = aChild->Parent();
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation mut(parent);
|
||||
@@ -2156,8 +2152,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
@@ -2181,8 +2176,7 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||
continue;
|
||||
}
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
||||
}
|
||||
|
||||
if (aDoFireEvents) {
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area, areaContent);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
|
||||
document->BindToDocument(mBullet, nullptr);
|
||||
InsertChildAt(0, mBullet);
|
||||
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet, mBullet->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
} else {
|
||||
|
||||
+4
-2
@@ -353,6 +353,8 @@ endif # MOZ_OPTIMIZE == 1
|
||||
LDFLAGS += $(MOZ_OPTIMIZE_LDFLAGS)
|
||||
endif # MOZ_OPTIMIZE
|
||||
|
||||
HOST_CFLAGS += $(_DEPEND_CFLAGS)
|
||||
HOST_CXXFLAGS += $(_DEPEND_CFLAGS)
|
||||
ifdef CROSS_COMPILE
|
||||
HOST_CFLAGS += $(HOST_OPTIMIZE_FLAGS)
|
||||
else
|
||||
@@ -427,8 +429,8 @@ OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
|
||||
endif
|
||||
endif
|
||||
|
||||
COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS)
|
||||
COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS)
|
||||
COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) $(_DEPEND_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS)
|
||||
COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) $(_DEPEND_CFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS)
|
||||
COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
|
||||
COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
|
||||
ASFLAGS += $(MOZBUILD_ASFLAGS)
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
# preprocessing
|
||||
# - INSTALL_MANIFESTS, which defines the list of base directories handled
|
||||
# by install manifests, see further below
|
||||
# - MANIFEST_TARGETS, which defines the file paths of chrome manifests, see
|
||||
# further below
|
||||
#
|
||||
# A convention used between this file and the Makefile including it is that
|
||||
# global Make variables names are uppercase, while "local" Make variables
|
||||
@@ -42,7 +40,6 @@
|
||||
default: $(addprefix install-,$(INSTALL_MANIFESTS))
|
||||
|
||||
# Explicit files to be built for a default build
|
||||
default: $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS))
|
||||
default: $(TOPOBJDIR)/dist/bin/platform.ini
|
||||
|
||||
# Targets from the recursive make backend to be built for a default build
|
||||
@@ -106,20 +103,6 @@ $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/config/build
|
||||
$(MOZ_DEBUG_DEFINES) \
|
||||
install_$(subst /,_,$*)
|
||||
|
||||
# Create some chrome manifests
|
||||
# This rule is forced to run every time because it may be updating files that
|
||||
# already exit.
|
||||
#
|
||||
# The list of chrome manifests is given in MANIFEST_TARGETS, relative to the
|
||||
# top object directory. The content for those manifests is given in the
|
||||
# `content` variable associated with the target. For example:
|
||||
# MANIFEST_TARGETS = foo
|
||||
# $(TOPOBJDIR)/foo: content = "manifest foo.manifest" "manifest bar.manifest"
|
||||
$(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)): FORCE
|
||||
$(PYTHON) -m mozbuild.action.buildlist \
|
||||
$@ \
|
||||
$(content)
|
||||
|
||||
# ============================================================================
|
||||
# Below is a set of additional dependencies and variables used to build things
|
||||
# that are not supported by data in moz.build.
|
||||
|
||||
@@ -57,6 +57,7 @@ ifneq (,$(filter $(PROGRAM) $(HOST_PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $
|
||||
endif
|
||||
$(LOOP_OVER_DIRS)
|
||||
|
||||
showbuild showhost: _DEPEND_CFLAGS=
|
||||
showbuild:
|
||||
$(call print_vars,\
|
||||
MOZ_BUILD_ROOT \
|
||||
|
||||
@@ -467,6 +467,19 @@ EXTRA_DEPS += $(LD_VERSION_SCRIPT)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef SYMBOLS_FILE
|
||||
ifdef GCC_USE_GNU_LD
|
||||
EXTRA_DSO_LDOPTS += -Wl,--version-script,$(SYMBOLS_FILE)
|
||||
else
|
||||
ifeq ($(OS_TARGET),Darwin)
|
||||
EXTRA_DSO_LDOPTS += -Wl,-exported_symbols_list,$(SYMBOLS_FILE)
|
||||
endif
|
||||
ifeq ($(OS_TARGET),WINNT)
|
||||
EXTRA_DSO_LDOPTS += -DEF:$(call normalizepath,$(SYMBOLS_FILE))
|
||||
endif
|
||||
endif
|
||||
EXTRA_DEPS += $(SYMBOLS_FILE)
|
||||
endif
|
||||
#
|
||||
# GNU doesn't have path length limitation
|
||||
#
|
||||
@@ -1489,6 +1502,8 @@ $(PP_TARGETS_ALL_RESULTS):
|
||||
$(RM) '$@'
|
||||
$(call py_action,preprocessor,--depend $(MDDEPDIR)/$(@F).pp $(PP_TARGET_FLAGS) $(DEFINES) $(ACDEFINES) $(MOZ_DEBUG_DEFINES) '$<' -o '$@')
|
||||
|
||||
$(filter %.css,$(PP_TARGETS_ALL_RESULTS)): PP_TARGET_FLAGS+=--marker %
|
||||
|
||||
# The depfile is based on the filename, and we don't want conflicts. So check
|
||||
# there's only one occurrence of any given filename in PP_TARGETS_ALL_RESULTS.
|
||||
PP_TARGETS_ALL_RESULT_NAMES := $(notdir $(PP_TARGETS_ALL_RESULTS))
|
||||
|
||||
+60
-6
@@ -549,6 +549,10 @@ case "$target" in
|
||||
# -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146)
|
||||
CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-"
|
||||
|
||||
# Disable C++11 thread-safe statics due to crashes on XP (bug 1204752)
|
||||
# See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
|
||||
CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-"
|
||||
|
||||
# https://connect.microsoft.com/VisualStudio/feedback/details/888527/warnings-on-dbghelp-h
|
||||
# for dbghelp.h, imagehlp.h, and shobj.h
|
||||
# C4091: 'typedef ': ignored on left of '' when no variable is declared
|
||||
@@ -2190,8 +2194,61 @@ ia64*-hpux*)
|
||||
CFLAGS="$CFLAGS -wd4244 -wd4267 -wd4819"
|
||||
CXXFLAGS="$CXXFLAGS -wd4251 -wd4244 -wd4267 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
|
||||
if test -n "$CLANG_CL"; then
|
||||
# XXX We should combine some of these with our generic GCC-style
|
||||
# warning checks.
|
||||
#
|
||||
# Suppress the clang-cl warning for the inline 'new' and 'delete' in mozalloc
|
||||
CXXFLAGS="$CXXFLAGS -Wno-inline-new-delete"
|
||||
# We use offsetof on non-POD objects all the time.
|
||||
# We also suppress this warning on other platforms.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-offsetof"
|
||||
# MFBT thinks clang-cl supports constexpr, which it does, but
|
||||
# not everything in Windows C++ headers supports constexpr
|
||||
# as we might expect until MSVC 2015, so turn off this warning
|
||||
# for now.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-constexpr"
|
||||
# This warns for reasonable things like:
|
||||
# enum { X = 0xffffffffU };
|
||||
# which is annoying for IDL headers.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-microsoft-enum-value"
|
||||
# This warns for cases that would be reached by the Microsoft
|
||||
# #include rules, but also currently warns on cases that would
|
||||
# *also* be reached by standard C++ include rules. That
|
||||
# behavior doesn't seem useful, so we turn it off.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-microsoft-include"
|
||||
# We normally error out on unknown pragmas, but since clang-cl
|
||||
# claims to be MSVC, it would be difficult to add
|
||||
# #if defined(_MSC_VER) && !defined(__clang__) everywhere we
|
||||
# use such pragmas, so just ignore them.
|
||||
CFLAGS="$CFLAGS -Wno-unknown-pragmas"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas"
|
||||
# clang-cl's Intrin.h marks things like _ReadWriteBarrier
|
||||
# as __attribute((__deprecated__)). This is nice to know,
|
||||
# but since we don't get the equivalent warning from MSVC,
|
||||
# let's just ignore it.
|
||||
CFLAGS="$CFLAGS -Wno-deprecated-declarations"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-deprecated-declarations"
|
||||
# We use a function like:
|
||||
# __declspec(noreturn) __inline void f() {}
|
||||
# which -Winvalid-noreturn complains about. Again, MSVC seems
|
||||
# OK with it, so let's silence the warning.
|
||||
CFLAGS="$CFLAGS -Wno-invalid-noreturn"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-noreturn"
|
||||
# Missing |override| on virtual function declarations isn't
|
||||
# something that MSVC currently warns about.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-inconsistent-missing-override"
|
||||
# We use -DHAS_EXCEPTIONS=0, which removes the |throw()|
|
||||
# declaration on |operator delete(void*)|. However, clang-cl
|
||||
# must internally declare |operator delete(void*)| differently,
|
||||
# which causes this warning for virtually every file in the
|
||||
# tree. clang-cl doesn't support -fno-exceptions or equivalent,
|
||||
# so there doesn't seem to be any way to convince clang-cl to
|
||||
# declare |delete| differently. Therefore, suppress this
|
||||
# warning.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-implicit-exception-spec-mismatch"
|
||||
# At least one MSVC header and several headers in-tree have
|
||||
# unused typedefs, so turn this on.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"
|
||||
fi
|
||||
# make 'foo == bar;' error out
|
||||
CFLAGS="$CFLAGS -we4553"
|
||||
@@ -8493,22 +8550,19 @@ CXXFLAGS=`echo \
|
||||
|
||||
COMPILE_CFLAGS=`echo \
|
||||
$_DEFINES_CFLAGS \
|
||||
$_DEPEND_CFLAGS \
|
||||
$COMPILE_CFLAGS`
|
||||
|
||||
COMPILE_CXXFLAGS=`echo \
|
||||
$_DEFINES_CXXFLAGS \
|
||||
$_DEPEND_CFLAGS \
|
||||
$COMPILE_CXXFLAGS`
|
||||
|
||||
HOST_CFLAGS=`echo \
|
||||
$HOST_CFLAGS \
|
||||
$_DEPEND_CFLAGS`
|
||||
$HOST_CFLAGS`
|
||||
|
||||
HOST_CXXFLAGS=`echo \
|
||||
$HOST_CXXFLAGS \
|
||||
$_DEPEND_CFLAGS`
|
||||
$HOST_CXXFLAGS`
|
||||
|
||||
AC_SUBST(_DEPEND_CFLAGS)
|
||||
AC_SUBST(MOZ_NATIVE_JPEG)
|
||||
AC_SUBST(MOZ_NATIVE_PNG)
|
||||
AC_SUBST(MOZ_NATIVE_BZ2)
|
||||
|
||||
@@ -10075,9 +10075,11 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
nsCOMPtr<nsIInputStream> postData;
|
||||
nsCOMPtr<nsISupports> cacheKey;
|
||||
|
||||
bool scrollRestorationIsManual = false;
|
||||
if (mOSHE) {
|
||||
/* save current position of scroller(s) (bug 59774) */
|
||||
mOSHE->SetScrollPosition(cx, cy);
|
||||
mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
|
||||
// Get the postdata and page ident from the current page, if
|
||||
// the new load is being done via normal means. Note that
|
||||
// "normal means" can be checked for just by checking for
|
||||
@@ -10092,11 +10094,21 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
// cache data, since the two SHEntries correspond to the
|
||||
// same document.
|
||||
if (mLSHE) {
|
||||
if (!aSHEntry) {
|
||||
// If we're not doing a history load, scroll restoration
|
||||
// should be inherited from the previous session history entry.
|
||||
mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
|
||||
}
|
||||
mLSHE->AdoptBFCacheEntry(mOSHE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're doing a history load, use its scroll restoration state.
|
||||
if (aSHEntry) {
|
||||
aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
|
||||
}
|
||||
|
||||
/* Assign mOSHE to mLSHE. This will either be a new entry created
|
||||
* by OnNewURI() for normal loads or aSHEntry for history loads.
|
||||
*/
|
||||
@@ -10164,11 +10176,14 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
/* restore previous position of scroller(s), if we're moving
|
||||
* back in history (bug 59774)
|
||||
*/
|
||||
nscoord bx = 0;
|
||||
nscoord by = 0;
|
||||
bool needsScrollPosUpdate = false;
|
||||
if (mOSHE && (aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_RELOAD_NORMAL)) {
|
||||
nscoord bx, by;
|
||||
aLoadType == LOAD_RELOAD_NORMAL) &&
|
||||
!scrollRestorationIsManual) {
|
||||
needsScrollPosUpdate = true;
|
||||
mOSHE->GetScrollPosition(&bx, &by);
|
||||
SetCurScrollPosEx(bx, by);
|
||||
}
|
||||
|
||||
// Dispatch the popstate and hashchange events, as appropriate.
|
||||
@@ -10184,6 +10199,10 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
win->DispatchSyncPopState();
|
||||
}
|
||||
|
||||
if (needsScrollPosUpdate && win->HasActiveDocument()) {
|
||||
SetCurScrollPosEx(bx, by);
|
||||
}
|
||||
|
||||
if (doHashchange) {
|
||||
// Note that currentURI hasn't changed because it's on the
|
||||
// stack, so we can just use it directly as the old URI.
|
||||
@@ -11654,6 +11673,9 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||
GetCurScrollPos(ScrollOrientation_Y, &cy);
|
||||
mOSHE->SetScrollPosition(cx, cy);
|
||||
|
||||
bool scrollRestorationIsManual = false;
|
||||
mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
|
||||
|
||||
// Since we're not changing which page we have loaded, pass
|
||||
// true for aCloneChildren.
|
||||
rv = AddToSessionHistory(newURI, nullptr, nullptr, true,
|
||||
@@ -11662,6 +11684,10 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||
|
||||
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
||||
|
||||
// Session history entries created by pushState inherit scroll restoration
|
||||
// mode from the current entry.
|
||||
newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
|
||||
|
||||
// Link the new SHEntry to the old SHEntry's BFCache entry, since the
|
||||
// two entries correspond to the same document.
|
||||
NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
|
||||
@@ -11773,6 +11799,27 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual)
|
||||
{
|
||||
*aIsManual = false;
|
||||
if (mOSHE) {
|
||||
mOSHE->GetScrollRestorationIsManual(aIsManual);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual)
|
||||
{
|
||||
if (mOSHE) {
|
||||
mOSHE->SetScrollRestorationIsManual(aIsManual);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
|
||||
{
|
||||
@@ -12183,10 +12230,21 @@ nsDocShell::PersistLayoutHistoryState()
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mOSHE) {
|
||||
bool scrollRestorationIsManual = false;
|
||||
mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = GetPresShell();
|
||||
nsCOMPtr<nsILayoutHistoryState> layoutState;
|
||||
if (shell) {
|
||||
nsCOMPtr<nsILayoutHistoryState> layoutState;
|
||||
rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
|
||||
} else if (scrollRestorationIsManual) {
|
||||
// Even if we don't have layout anymore, we may want to reset the current
|
||||
// scroll state in layout history.
|
||||
GetLayoutHistoryState(getter_AddRefs(layoutState));
|
||||
}
|
||||
|
||||
if (scrollRestorationIsManual && layoutState) {
|
||||
layoutState->ResetScrollState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1073,6 +1073,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
* top level chrome docshell.
|
||||
*/
|
||||
attribute boolean windowDraggingAllowed;
|
||||
|
||||
/**
|
||||
* Sets/gets the current scroll restoration mode.
|
||||
* @see https://html.spec.whatwg.org/#dom-history-scroll-restoration
|
||||
*/
|
||||
attribute boolean currentScrollRestorationIsManual;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(395c6a79-80ae-4ede-9e05-93bee6c3d9a1)]
|
||||
|
||||
@@ -30,7 +30,7 @@ class nsSHEntryShared;
|
||||
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
|
||||
[ptr] native nsSHEntryShared(nsSHEntryShared);
|
||||
|
||||
[scriptable, uuid(d5fbeb10-f373-4677-b69a-2694aa706cac)]
|
||||
[scriptable, uuid(0dad26b8-a259-42c7-93f1-2fa7fc076e45)]
|
||||
interface nsISHEntry : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -289,6 +289,12 @@ interface nsISHEntry : nsISupports
|
||||
* for example with view-source.
|
||||
*/
|
||||
attribute nsIURI baseURI;
|
||||
|
||||
/**
|
||||
* Sets/gets the current scroll restoration state,
|
||||
* if true == "manual", false == "auto".
|
||||
*/
|
||||
attribute boolean scrollRestorationIsManual;
|
||||
};
|
||||
|
||||
[scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]
|
||||
@@ -324,6 +330,7 @@ interface nsISHEntryInternal : nsISupports
|
||||
|
||||
%}
|
||||
|
||||
|
||||
[scriptable, uuid(c93d6146-9cfa-4da9-b652-f64d958f3ce4)]
|
||||
interface nsISHEntry_ESR38 : nsISHEntry
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@ nsSHEntry::nsSHEntry()
|
||||
, mParent(nullptr)
|
||||
, mURIWasModified(false)
|
||||
, mIsSrcdocEntry(false)
|
||||
, mScrollRestorationIsManual(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,6 +54,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
|
||||
, mURIWasModified(aOther.mURIWasModified)
|
||||
, mStateData(aOther.mStateData)
|
||||
, mIsSrcdocEntry(aOther.mIsSrcdocEntry)
|
||||
, mScrollRestorationIsManual(false)
|
||||
, mSrcdocData(aOther.mSrcdocData)
|
||||
, mBaseURI(aOther.mBaseURI)
|
||||
{
|
||||
@@ -598,6 +600,20 @@ nsSHEntry::SetBaseURI(nsIURI* aBaseURI)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::GetScrollRestorationIsManual(bool* aIsManual)
|
||||
{
|
||||
*aIsManual = mScrollRestorationIsManual;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::SetScrollRestorationIsManual(bool aIsManual)
|
||||
{
|
||||
mScrollRestorationIsManual = aIsManual;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::GetChildCount(int32_t* aCount)
|
||||
{
|
||||
|
||||
@@ -65,6 +65,7 @@ private:
|
||||
bool mURIWasModified;
|
||||
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
|
||||
bool mIsSrcdocEntry;
|
||||
bool mScrollRestorationIsManual;
|
||||
nsString mSrcdocData;
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
};
|
||||
|
||||
@@ -100,6 +100,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
[test_bug797909.html]
|
||||
[test_bug1045096.html]
|
||||
[test_bug1186774.html]
|
||||
[test_framedhistoryframes.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage, and also bug 784321
|
||||
support-files = file_framedhistoryframes.html
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
var oldHistoryObject = null;
|
||||
|
||||
function test(event) {
|
||||
if (!opener.scrollRestorationTest) {
|
||||
opener.scrollRestorationTest = 0;
|
||||
}
|
||||
++opener.scrollRestorationTest;
|
||||
|
||||
switch (opener.scrollRestorationTest) {
|
||||
case 1: {
|
||||
opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
|
||||
opener.ok(history.scrollRestoration, "History object has scrollRestoration property.");
|
||||
opener.ok(history.scrollRestoration, "auto", "history.scrollRestoration's default value should be 'auto'.");
|
||||
history.scrollRestoration = "foobar";
|
||||
opener.ok(history.scrollRestoration, "auto", "Invalid enum value should not change the value of an attribute.");
|
||||
history.scrollRestoration = "manual";
|
||||
opener.ok(history.scrollRestoration, "manual", "Valid enum value should change the value of an attribute.");
|
||||
history.scrollRestoration = "auto";
|
||||
opener.ok(history.scrollRestoration, "auto", "Valid enum value should change the value of an attribute.");
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
window.location.reload(false);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
|
||||
opener.isnot(window.scrollY, 0, "Should have restored scrolling.");
|
||||
opener.is(history.scrollRestoration, "auto", "Should have the same scrollRestoration as before reload.");
|
||||
history.scrollRestoration = "manual";
|
||||
window.onunload = function() {} // Disable bfcache.
|
||||
window.location.reload(false);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
|
||||
opener.is(window.scrollY, 0, "Should not have restored scrolling.");
|
||||
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload.");
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
window.onunload = null; // Should get bfcache behavior.
|
||||
opener.setTimeout("testWindow.history.back();", 250);
|
||||
window.location.href = 'data:text/html,';
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
opener.is(event.persisted, true, "Should have persisted session history entry.");
|
||||
opener.isnot(window.scrollY, 0, "Should have kept the old scroll position.");
|
||||
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload.");
|
||||
window.scrollTo(0, 0);
|
||||
window.location.hash = "hash";
|
||||
requestAnimationFrame(test);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled to #hash.");
|
||||
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation.");
|
||||
window.onunload = function() {} // Disable bfcache.
|
||||
opener.setTimeout("is(testWindow.history.scrollRestoration, 'auto'); testWindow.history.back();", 250);
|
||||
window.location.href = 'data:text/html,';
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
opener.is(event.persisted, false, "Shouldn't have persisted session history entry.");
|
||||
opener.is(window.scrollY, 0, "Shouldn't have kept the old scroll position.");
|
||||
opener.is(history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation.");
|
||||
history.scrollRestoration = "auto";
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
history.pushState({ state: "state1" }, "state1");
|
||||
history.pushState({ state: "state2" }, "state2");
|
||||
window.scrollTo(0, 0);
|
||||
history.back();
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled back to the state1's position");
|
||||
opener.is(history.state.state, "state1", "Unexpected state.");
|
||||
|
||||
history.scrollRestoration = "manual";
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
history.pushState({ state: "state3" }, "state3");
|
||||
history.pushState({ state: "state4" }, "state4");
|
||||
window.scrollTo(0, 0);
|
||||
history.back();
|
||||
opener.is(window.scrollY, 0, "Shouldn't have scrolled back to the state3's position");
|
||||
opener.is(history.state.state, "state3", "Unexpected state.");
|
||||
|
||||
history.pushState({ state: "state5" }, "state5");
|
||||
history.scrollRestoration = "auto";
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled to 'bottom'.");
|
||||
history.back();
|
||||
window.scrollTo(0, 0);
|
||||
history.forward();
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled back to the state5's position");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.src = "data:text/html,";
|
||||
document.body.appendChild(ifr);
|
||||
ifr.onload = test;
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
oldHistoryObject = event.target.contentWindow.history;
|
||||
event.target.src = "about:blank";
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
try {
|
||||
var sr = oldHistoryObject.scrollRestoration;
|
||||
opener.ok(false, "Should have thrown an exception.");
|
||||
} catch(ex) {
|
||||
opener.isnot(ex, null, "Did get an exception");
|
||||
}
|
||||
try {
|
||||
oldHistoryObject.scrollRestoration = "auto";
|
||||
opener.ok(false, "Should have thrown an exception.");
|
||||
} catch(ex) {
|
||||
opener.isnot(ex, null, "Did get an exception");
|
||||
}
|
||||
opener.nextTest();
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("pageshow",
|
||||
function(e) {
|
||||
setTimeout(test, 0, e);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="border: 1px solid black; height: 5000px;">
|
||||
</div>
|
||||
<div id="bottom">Hello world</div>
|
||||
<a href="#hash" name="hash">hash</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,6 +10,7 @@ support-files =
|
||||
file_document_write_1.html
|
||||
file_fragment_handling_during_load.html
|
||||
file_nested_frames.html
|
||||
file_scrollRestoration.html
|
||||
file_shiftReload_and_pushState.html
|
||||
file_static_and_dynamic_1.html
|
||||
frame0.html
|
||||
|
||||
@@ -29,7 +29,8 @@ var testFiles =
|
||||
"file_bug534178.html", // Session history transaction clean-up.
|
||||
"file_fragment_handling_during_load.html",
|
||||
"file_nested_frames.html",
|
||||
"file_shiftReload_and_pushState.html"
|
||||
"file_shiftReload_and_pushState.html",
|
||||
"file_scrollRestoration.html"
|
||||
];
|
||||
var testCount = 0; // Used by the test files.
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1186774
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1186774</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1186774 **/
|
||||
|
||||
var child;
|
||||
|
||||
function runTest() {
|
||||
child = window.open("data:text/html,<div style='height: 9000px;'></div>", "", "width=100,height=100");
|
||||
child.onload = function() {
|
||||
setTimeout(function() {
|
||||
child.scrollTo(0, 0);
|
||||
child.history.pushState({}, "initial");
|
||||
child.scrollTo(0, 3000);
|
||||
child.history.pushState({}, "scrolled");
|
||||
child.scrollTo(0, 6000);
|
||||
child.history.back();
|
||||
});
|
||||
}
|
||||
|
||||
child.onpopstate = function() {
|
||||
is(child.scrollY, 6000, "Shouldn't have scrolled before popstate");
|
||||
child.close();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(runTest);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186774">Mozilla Bug 1186774</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -285,6 +285,18 @@ public:
|
||||
*/
|
||||
virtual bool HasLowerCompositeOrderThan(const Animation& aOther) const;
|
||||
|
||||
/**
|
||||
* Returns true if effect(s) associated with this Animation are applied to
|
||||
* the transitions level of the cascade. Otherwise, it is assumed that
|
||||
* animated values are applies to the animations level of the cascade.
|
||||
*
|
||||
* This is used in determining which animations are sent to the compositor
|
||||
* since animations applied to the transitions level of the cascade are not
|
||||
* overridden by higher-priority style values in the same way that
|
||||
* regular animations that apply to the animations level of the cascade are.
|
||||
*/
|
||||
virtual bool AppliesToTransitionsLevel() const { return false; }
|
||||
|
||||
/**
|
||||
* Returns true if this animation does not currently need to update
|
||||
* style on the main thread (e.g. because it is empty, or is
|
||||
|
||||
@@ -7,12 +7,24 @@
|
||||
#include "EffectCompositor.h"
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h" // For KeyframeEffectReadOnly
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/LayerAnimationInfo.h"
|
||||
#include "AnimationCommon.h" // For AnimationCollection
|
||||
#include "nsAnimationManager.h"
|
||||
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
|
||||
#include "nsCSSPropertySet.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
|
||||
#include "nsTArray.h"
|
||||
#include "nsTransitionManager.h"
|
||||
|
||||
using mozilla::dom::Animation;
|
||||
using mozilla::dom::Element;
|
||||
using mozilla::dom::KeyframeEffectReadOnly;
|
||||
|
||||
namespace mozilla {
|
||||
@@ -40,6 +52,21 @@ FindAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
return false;
|
||||
}
|
||||
|
||||
// The animation cascade will almost always be up-to-date by this point
|
||||
// but there are some cases such as when we are restoring the refresh driver
|
||||
// from test control after seeking where it might not be the case.
|
||||
//
|
||||
// Those cases are probably not important but just to be safe, let's make
|
||||
// sure the cascade is up to date since if it *is* up to date, this is
|
||||
// basically a no-op.
|
||||
Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>> pseudoElement =
|
||||
EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
|
||||
if (pseudoElement) {
|
||||
EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->first(),
|
||||
pseudoElement->second(),
|
||||
aFrame->StyleContext());
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
nsCString message;
|
||||
@@ -104,4 +131,244 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
|
||||
nsCSSPseudoElements::Type
|
||||
aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects || !effects->CascadeNeedsUpdate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
||||
|
||||
MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
|
||||
}
|
||||
|
||||
namespace {
|
||||
class EffectCompositeOrderComparator {
|
||||
public:
|
||||
bool Equals(const KeyframeEffectReadOnly* a,
|
||||
const KeyframeEffectReadOnly* b) const
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
bool LessThan(const KeyframeEffectReadOnly* a,
|
||||
const KeyframeEffectReadOnly* b) const
|
||||
{
|
||||
MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
|
||||
MOZ_ASSERT(
|
||||
Equals(a, b) ||
|
||||
a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
|
||||
b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
|
||||
return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::UpdateCascadeResults(Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
||||
}
|
||||
|
||||
/* static */ Maybe<Pair<Element*, nsCSSPseudoElements::Type>>
|
||||
EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
|
||||
{
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
Maybe<Pair<Element*, nsCSSPseudoElements::Type>> result;
|
||||
|
||||
nsIContent* content = aFrame->GetContent();
|
||||
if (!content) {
|
||||
return result;
|
||||
}
|
||||
|
||||
nsCSSPseudoElements::Type pseudoType =
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement;
|
||||
|
||||
if (aFrame->IsGeneratedContentFrame()) {
|
||||
nsIFrame* parent = aFrame->GetParent();
|
||||
if (parent->IsGeneratedContentFrame()) {
|
||||
return result;
|
||||
}
|
||||
nsIAtom* name = content->NodeInfo()->NameAtom();
|
||||
if (name == nsGkAtoms::mozgeneratedcontentbefore) {
|
||||
pseudoType = nsCSSPseudoElements::ePseudo_before;
|
||||
} else if (name == nsGkAtoms::mozgeneratedcontentafter) {
|
||||
pseudoType = nsCSSPseudoElements::ePseudo_after;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
content = content->GetParent();
|
||||
if (!content) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!content->IsElement()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Some(MakePair(content->AsElement(), pseudoType));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::GetOverriddenProperties(nsStyleContext* aStyleContext,
|
||||
EffectSet& aEffectSet,
|
||||
nsCSSPropertySet&
|
||||
aPropertiesOverridden)
|
||||
{
|
||||
nsAutoTArray<nsCSSProperty, LayerAnimationInfo::kRecords> propertiesToTrack;
|
||||
{
|
||||
nsCSSPropertySet propertiesToTrackAsSet;
|
||||
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
||||
for (const AnimationProperty& property : effect->Properties()) {
|
||||
if (nsCSSProps::PropHasFlags(property.mProperty,
|
||||
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
|
||||
!propertiesToTrackAsSet.HasProperty(property.mProperty)) {
|
||||
propertiesToTrackAsSet.AddProperty(property.mProperty);
|
||||
propertiesToTrack.AppendElement(property.mProperty);
|
||||
}
|
||||
}
|
||||
// Skip iterating over the rest of the effects if we've already
|
||||
// found all the compositor-animatable properties.
|
||||
if (propertiesToTrack.Length() == LayerAnimationInfo::kRecords) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesToTrack.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
|
||||
aStyleContext,
|
||||
aPropertiesOverridden);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
|
||||
Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
|
||||
"Effect set should correspond to the specified (pseudo-)element");
|
||||
if (aEffectSet.IsEmpty()) {
|
||||
aEffectSet.MarkCascadeUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a list of effects sorted by composite order.
|
||||
nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
|
||||
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
// Get properties that override the *animations* level of the cascade.
|
||||
//
|
||||
// We only do this for properties that we can animate on the compositor
|
||||
// since we will apply other properties on the main thread where the usual
|
||||
// cascade applies.
|
||||
nsCSSPropertySet overriddenProperties;
|
||||
if (aStyleContext) {
|
||||
GetOverriddenProperties(aStyleContext, aEffectSet, overriddenProperties);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
nsCSSPropertySet animatedProperties;
|
||||
|
||||
// Iterate from highest to lowest composite order.
|
||||
for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
|
||||
MOZ_ASSERT(effect->GetAnimation(),
|
||||
"Effects on a target element should have an Animation");
|
||||
bool inEffect = effect->IsInEffect();
|
||||
for (AnimationProperty& prop : effect->Properties()) {
|
||||
|
||||
bool winsInCascade = !animatedProperties.HasProperty(prop.mProperty) &&
|
||||
inEffect;
|
||||
|
||||
// If this property wins in the cascade, add it to the set of animated
|
||||
// properties. We need to do this even if the property is overridden
|
||||
// (in which case we set winsInCascade to false below) since we don't
|
||||
// want to fire transitions on these properties.
|
||||
if (winsInCascade) {
|
||||
animatedProperties.AddProperty(prop.mProperty);
|
||||
}
|
||||
|
||||
// For effects that will be applied to the animations level of the
|
||||
// cascade, we need to check that the property isn't being set by
|
||||
// something with higher priority in the cascade.
|
||||
//
|
||||
// We only do this, however, for properties that can be animated on
|
||||
// the compositor. For properties animated on the main thread the usual
|
||||
// cascade ensures these animations will be correctly overridden.
|
||||
if (winsInCascade &&
|
||||
!effect->GetAnimation()->AppliesToTransitionsLevel() &&
|
||||
overriddenProperties.HasProperty(prop.mProperty)) {
|
||||
winsInCascade = false;
|
||||
}
|
||||
|
||||
if (winsInCascade != prop.mWinsInCascade) {
|
||||
changed = true;
|
||||
}
|
||||
prop.mWinsInCascade = winsInCascade;
|
||||
}
|
||||
}
|
||||
|
||||
aEffectSet.MarkCascadeUpdated();
|
||||
|
||||
// If there is any change in the cascade result, update animations on
|
||||
// layers with the winning animations.
|
||||
nsPresContext* presContext = GetPresContext(aElement);
|
||||
if (changed && presContext) {
|
||||
// We currently unconditionally update both animations and transitions
|
||||
// even if we could, for example, get away with only updating animations.
|
||||
// This is a temporary measure until we unify all animation style updating
|
||||
// under EffectCompositor.
|
||||
AnimationCollection* animations =
|
||||
presContext->AnimationManager()->GetAnimationCollection(aElement,
|
||||
aPseudoType,
|
||||
false);
|
||||
/* don't create */
|
||||
if (animations) {
|
||||
animations->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
||||
}
|
||||
|
||||
AnimationCollection* transitions =
|
||||
presContext->TransitionManager()->GetAnimationCollection(aElement,
|
||||
aPseudoType,
|
||||
false);
|
||||
/* don't create */
|
||||
if (transitions) {
|
||||
transitions->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ nsPresContext*
|
||||
EffectCompositor::GetPresContext(Element* aElement)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
nsIPresShell* shell = nsComputedDOMStyle::GetPresShellForContent(aElement);
|
||||
if (!shell) {
|
||||
return nullptr;
|
||||
}
|
||||
return shell->GetPresContext();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -7,13 +7,23 @@
|
||||
#ifndef mozilla_EffectCompositor_h
|
||||
#define mozilla_EffectCompositor_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Pair.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsCSSPropertySet;
|
||||
class nsPresContext;
|
||||
class nsStyleContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class EffectSet;
|
||||
|
||||
namespace dom {
|
||||
class Animation;
|
||||
class Element;
|
||||
}
|
||||
|
||||
class EffectCompositor
|
||||
@@ -25,6 +35,59 @@ public:
|
||||
static nsTArray<RefPtr<dom::Animation>>
|
||||
GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty);
|
||||
|
||||
|
||||
// Update animation cascade results for the specified (pseudo-)element
|
||||
// but only if we have marked the cascade as needing an update due a
|
||||
// the change in the set of effects or a change in one of the effects'
|
||||
// "in effect" state.
|
||||
//
|
||||
// This method does NOT detect if other styles that apply above the
|
||||
// animation level of the cascade have changed.
|
||||
static void
|
||||
MaybeUpdateCascadeResults(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
// Update the mWinsInCascade member for each property in effects targetting
|
||||
// the specified (pseudo-)element.
|
||||
//
|
||||
// This can be expensive so we should only call it if styles that apply
|
||||
// above the animation level of the cascade might have changed. For all
|
||||
// other cases we should call MaybeUpdateCascadeResults.
|
||||
static void
|
||||
UpdateCascadeResults(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
// Helper to fetch the corresponding element and pseudo-type from a frame.
|
||||
//
|
||||
// For frames corresponding to pseudo-elements, the returned element is the
|
||||
// element on which we store the animations (i.e. the EffectSet and/or
|
||||
// AnimationCollection), *not* the generated content.
|
||||
//
|
||||
// Returns an empty result when a suitable element cannot be found including
|
||||
// when the frame represents a pseudo-element on which we do not support
|
||||
// animations.
|
||||
static Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>>
|
||||
GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);
|
||||
|
||||
private:
|
||||
// Get the properties in |aEffectSet| that we are able to animate on the
|
||||
// compositor but which are also specified at a higher level in the cascade
|
||||
// than the animations level in |aStyleContext|.
|
||||
static void
|
||||
GetOverriddenProperties(nsStyleContext* aStyleContext,
|
||||
EffectSet& aEffectSet,
|
||||
nsCSSPropertySet& aPropertiesOverridden);
|
||||
|
||||
static void
|
||||
UpdateCascadeResults(EffectSet& aEffectSet,
|
||||
dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
static nsPresContext* GetPresContext(dom::Element* aElement);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -143,13 +143,23 @@ EffectSet::GetEffectSetPropertyAtom(nsCSSPseudoElements::Type aPseudoType)
|
||||
void
|
||||
EffectSet::AddEffect(dom::KeyframeEffectReadOnly& aEffect)
|
||||
{
|
||||
if (mEffects.Contains(&aEffect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEffects.PutEntry(&aEffect);
|
||||
MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
EffectSet::RemoveEffect(dom::KeyframeEffectReadOnly& aEffect)
|
||||
{
|
||||
if (!mEffects.Contains(&aEffect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEffects.RemoveEntry(&aEffect);
|
||||
MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -24,8 +24,9 @@ class EffectSet
|
||||
{
|
||||
public:
|
||||
EffectSet()
|
||||
: mCascadeNeedsUpdate(false)
|
||||
#ifdef DEBUG
|
||||
: mCalledPropertyDtor(false)
|
||||
, mCalledPropertyDtor(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(EffectSet);
|
||||
@@ -120,6 +121,10 @@ public:
|
||||
}
|
||||
bool IsEmpty() const { return mEffects.IsEmpty(); }
|
||||
|
||||
bool CascadeNeedsUpdate() const { return mCascadeNeedsUpdate; }
|
||||
void MarkCascadeNeedsUpdate() { mCascadeNeedsUpdate = true; }
|
||||
void MarkCascadeUpdated() { mCascadeNeedsUpdate = false; }
|
||||
|
||||
static nsIAtom** GetEffectSetPropertyAtoms();
|
||||
|
||||
private:
|
||||
@@ -128,6 +133,13 @@ private:
|
||||
|
||||
OwningEffectSet mEffects;
|
||||
|
||||
// Dirty flag to represent when the mWinsInCascade flag on effects in
|
||||
// this set might need to be updated.
|
||||
//
|
||||
// Set to true any time the set of effects is changed or when
|
||||
// one the effects goes in or out of the "in effect" state.
|
||||
bool mCascadeNeedsUpdate;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mCalledPropertyDtor;
|
||||
#endif
|
||||
|
||||
@@ -94,6 +94,7 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
, mTarget(aTarget)
|
||||
, mTiming(aTiming)
|
||||
, mPseudoType(aPseudoType)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
{
|
||||
MOZ_ASSERT(aTarget, "null animation target is not yet supported");
|
||||
}
|
||||
@@ -132,6 +133,36 @@ KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming)
|
||||
// update our registration with the target element.
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
{
|
||||
UpdateTargetRegistration();
|
||||
|
||||
// If the effect is not relevant it will be removed from the target
|
||||
// element's effect set. However, effects not in the effect set
|
||||
// will not be included in the set of candidate effects for running on
|
||||
// the compositor and hence they won't have their compositor status
|
||||
// updated. As a result, we need to make sure we clear their compositor
|
||||
// status here.
|
||||
bool isRelevant = mAnimation && mAnimation->IsRelevant();
|
||||
if (!isRelevant) {
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
|
||||
// Detect changes to "in effect" status since we need to recalculate the
|
||||
// animation cascade for this element whenever that changes.
|
||||
bool inEffect = IsInEffect();
|
||||
if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
}
|
||||
mInEffectOnLastAnimationTimingUpdate = inEffect;
|
||||
}
|
||||
}
|
||||
|
||||
Nullable<TimeDuration>
|
||||
KeyframeEffectReadOnly::GetLocalTime() const
|
||||
{
|
||||
@@ -533,10 +564,6 @@ KeyframeEffectReadOnly::UpdateTargetRegistration()
|
||||
if (effectSet) {
|
||||
effectSet->RemoveEffect(*this);
|
||||
}
|
||||
// Any effects not in the effect set will not be included in the set of
|
||||
// candidate effects for running on the compositor and hence they won't
|
||||
// have their compositor status updated so we should do that now.
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,10 +131,7 @@ struct AnimationProperty
|
||||
// For CSS Animations, which are overridden by !important rules in the
|
||||
// cascade, we actually determine this from the CSS cascade
|
||||
// computations, and then use it for OMTA.
|
||||
// **NOTE**: For CSS animations, we only bother setting mWinsInCascade
|
||||
// accurately for properties that we can animate on the compositor.
|
||||
// For other properties, we make it always be true.
|
||||
// **NOTE 2**: This member is not included when comparing AnimationProperty
|
||||
// **NOTE**: This member is not included when comparing AnimationProperty
|
||||
// objects for equality.
|
||||
bool mWinsInCascade = true;
|
||||
|
||||
@@ -229,7 +226,7 @@ public:
|
||||
const AnimationTiming& Timing() const { return mTiming; }
|
||||
AnimationTiming& Timing() { return mTiming; }
|
||||
void SetTiming(const AnimationTiming& aTiming);
|
||||
void NotifyAnimationTimingUpdated() { UpdateTargetRegistration(); }
|
||||
void NotifyAnimationTimingUpdated();
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const;
|
||||
|
||||
@@ -349,6 +346,10 @@ protected:
|
||||
|
||||
InfallibleTArray<AnimationProperty> mProperties;
|
||||
|
||||
// We need to track when we go to or from being "in effect" since
|
||||
// we need to re-evaluate the cascade of animations when that changes.
|
||||
bool mInEffectOnLastAnimationTimingUpdate;
|
||||
|
||||
private:
|
||||
nsIFrame* GetAnimationFrame() const;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ UNIFIED_SOURCES += [
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/layout/style',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
+11
-20
@@ -749,8 +749,7 @@ nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
nsDOMStyleSheetList::StyleSheetAdded(nsIStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet)
|
||||
{
|
||||
if (aDocumentSheet && -1 != mLength) {
|
||||
@@ -762,8 +761,7 @@ nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
nsDOMStyleSheetList::StyleSheetRemoved(nsIStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet)
|
||||
{
|
||||
if (aDocumentSheet && -1 != mLength) {
|
||||
@@ -3739,14 +3737,12 @@ nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
|
||||
// Don't add anything here. Add it to |doCreateShell| instead.
|
||||
// This exists so that subclasses can pass other values for the 4th
|
||||
// parameter some of the time.
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet,
|
||||
eCompatibility_FullStandards);
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPresShell>
|
||||
nsDocument::doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode)
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet)
|
||||
{
|
||||
NS_ASSERTION(!mPresShell, "We have a presshell already!");
|
||||
|
||||
@@ -3755,7 +3751,7 @@ nsDocument::doCreateShell(nsPresContext* aContext,
|
||||
FillStyleSet(aStyleSet);
|
||||
|
||||
RefPtr<PresShell> shell = new PresShell;
|
||||
shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
|
||||
shell->Init(this, aContext, aViewManager, aStyleSet);
|
||||
|
||||
// Note: we don't hold a ref to the shell (it holds a ref to us)
|
||||
mPresShell = shell;
|
||||
@@ -4153,7 +4149,7 @@ nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
|
||||
void
|
||||
nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (aSheet, aDocumentSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
|
||||
@@ -4166,7 +4162,7 @@ nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
|
||||
void
|
||||
nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (aSheet, aDocumentSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
|
||||
@@ -4293,8 +4289,7 @@ nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
|
||||
// that are children of sheets in our style set, as well as some
|
||||
// sheets for nsHTMLEditor.
|
||||
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
|
||||
(this, aSheet, aApplicable));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
|
||||
@@ -5199,9 +5194,7 @@ void
|
||||
nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
|
||||
(this, aSheet,
|
||||
aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
@@ -5215,8 +5208,7 @@ void
|
||||
nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
|
||||
(this, aSheet, aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
@@ -5231,8 +5223,7 @@ void
|
||||
nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
|
||||
(this, aSheet, aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
|
||||
@@ -1476,8 +1476,7 @@ public:
|
||||
protected:
|
||||
already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode);
|
||||
nsStyleSet* aStyleSet);
|
||||
|
||||
void RemoveDocStyleSheetsFromStyleSets();
|
||||
void RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets,
|
||||
|
||||
@@ -1813,8 +1813,8 @@ nsMessageManagerScriptExecutor::InitChildGlobalInternal(
|
||||
const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
options.setZone(JS::SystemZone)
|
||||
.setVersion(JSVERSION_LATEST);
|
||||
options.creationOptions().setZone(JS::SystemZone);
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
|
||||
nsresult rv =
|
||||
xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
|
||||
|
||||
@@ -2331,13 +2331,11 @@ CreateNativeGlobalForInner(JSContext* aCx,
|
||||
// windows or inside a browser element. In such cases we want to tag the
|
||||
// window's compartment with the add-on ID. See bug 1092156.
|
||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
options.setAddonId(MapURIToAddonID(aURI));
|
||||
options.creationOptions().setAddonId(MapURIToAddonID(aURI));
|
||||
}
|
||||
|
||||
if (top) {
|
||||
if (top->GetGlobalJSObject()) {
|
||||
options.setSameZoneAs(top->GetGlobalJSObject());
|
||||
}
|
||||
if (top && top->GetGlobalJSObject()) {
|
||||
options.creationOptions().setSameZoneAs(top->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
// Determine if we need the Components object.
|
||||
|
||||
+32
-1
@@ -7,7 +7,6 @@
|
||||
#include "nsHistory.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/dom/HistoryBinding.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
@@ -96,6 +95,38 @@ nsHistory::GetLength(ErrorResult& aRv) const
|
||||
return len >= 0 ? len : 0;
|
||||
}
|
||||
|
||||
ScrollRestoration
|
||||
nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mInnerWindow));
|
||||
if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return mozilla::dom::ScrollRestoration::Auto;
|
||||
}
|
||||
|
||||
bool currentScrollRestorationIsManual = false;
|
||||
win->GetDocShell()->
|
||||
GetCurrentScrollRestorationIsManual(¤tScrollRestorationIsManual);
|
||||
return currentScrollRestorationIsManual ?
|
||||
mozilla::dom::ScrollRestoration::Manual :
|
||||
mozilla::dom::ScrollRestoration::Auto;
|
||||
}
|
||||
|
||||
void
|
||||
nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mInnerWindow));
|
||||
if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
win->GetDocShell()->
|
||||
SetCurrentScrollRestorationIsManual(
|
||||
aMode == mozilla::dom::ScrollRestoration::Manual);
|
||||
}
|
||||
|
||||
void
|
||||
nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
||||
ErrorResult& aRv) const
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/HistoryBinding.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDOMHistory.h"
|
||||
@@ -36,6 +37,9 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint32_t GetLength(mozilla::ErrorResult& aRv) const;
|
||||
mozilla::dom::ScrollRestoration GetScrollRestoration(mozilla::ErrorResult& aRv);
|
||||
void SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv) const;
|
||||
void Go(int32_t aDelta, mozilla::ErrorResult& aRv);
|
||||
|
||||
@@ -94,15 +94,13 @@ public:
|
||||
* A StyleSheet has just been added to the document. This method is
|
||||
* called automatically when a StyleSheet gets added to the
|
||||
* document, even if the stylesheet is not applicable. The
|
||||
* notification is passed on to all of the document observers.
|
||||
* notification is passed on to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been added
|
||||
* @param aDocumentSheet True if sheet is in document's style sheet list,
|
||||
* false if sheet is not (i.e., UA or user sheet)
|
||||
*/
|
||||
virtual void StyleSheetAdded(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
virtual void StyleSheetAdded(nsIStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet) = 0;
|
||||
|
||||
/**
|
||||
@@ -111,13 +109,11 @@ public:
|
||||
* from the document, even if the stylesheet is not applicable. The
|
||||
* notification is passed on to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been removed
|
||||
* @param aDocumentSheet True if sheet is in document's style sheet list,
|
||||
* false if sheet is not (i.e., UA or user sheet)
|
||||
*/
|
||||
virtual void StyleSheetRemoved(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
virtual void StyleSheetRemoved(nsIStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet) = 0;
|
||||
|
||||
/**
|
||||
@@ -127,14 +123,9 @@ public:
|
||||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has changed state
|
||||
* @param aApplicable true if the sheet is applicable, false if
|
||||
* it is not applicable
|
||||
*/
|
||||
virtual void StyleSheetApplicableStateChanged(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
bool aApplicable) = 0;
|
||||
virtual void StyleSheetApplicableStateChanged(nsIStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been modified within a style sheet.
|
||||
@@ -143,13 +134,9 @@ public:
|
||||
* the document. The notification is passed on to all of
|
||||
* the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that contians the rule
|
||||
* @param aStyleRule The rule being changed.
|
||||
*/
|
||||
virtual void StyleRuleChanged(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been added to a style sheet.
|
||||
@@ -158,13 +145,9 @@ public:
|
||||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been modified
|
||||
* @param aStyleRule the rule that was added
|
||||
*/
|
||||
virtual void StyleRuleAdded(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been removed from a style sheet.
|
||||
@@ -173,13 +156,9 @@ public:
|
||||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been modified
|
||||
* @param aStyleRule the rule that was removed
|
||||
*/
|
||||
virtual void StyleRuleRemoved(nsIDocument *aDocument,
|
||||
nsIStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
||||
@@ -207,34 +186,24 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
||||
mozilla::EventStates aStateMask) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED \
|
||||
virtual void StyleSheetAdded(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
virtual void StyleSheetAdded(nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED \
|
||||
virtual void StyleSheetRemoved(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
virtual void StyleSheetRemoved(nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED \
|
||||
virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet,\
|
||||
bool aApplicable) override;
|
||||
virtual void StyleSheetApplicableStateChanged(nsIStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED \
|
||||
virtual void StyleRuleChanged(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED \
|
||||
virtual void StyleRuleAdded(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED \
|
||||
virtual void StyleRuleRemoved(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE \
|
||||
@@ -292,39 +261,29 @@ NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)
|
||||
|
||||
#define NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(_class) \
|
||||
void \
|
||||
_class::StyleSheetAdded(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
_class::StyleSheetAdded(nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleSheetRemoved(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
_class::StyleSheetRemoved(nsIStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleSheetApplicableStateChanged(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
bool aApplicable) \
|
||||
_class::StyleSheetApplicableStateChanged(nsIStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleChanged(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleChanged(nsIStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleAdded(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleAdded(nsIStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleRemoved(nsIDocument* aDocument, \
|
||||
nsIStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleRemoved(nsIStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
|
||||
SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, false);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
|
||||
});
|
||||
|
||||
function done() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@@ -3051,7 +3051,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
|
||||
JSPrincipals* aPrincipal, bool aInitStandardClasses,
|
||||
JS::MutableHandle<JSObject*> aGlobal)
|
||||
{
|
||||
aOptions.setTrace(CreateGlobalOptions<T>::TraceGlobal);
|
||||
aOptions.creationOptions().setTrace(CreateGlobalOptions<T>::TraceGlobal);
|
||||
|
||||
aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
|
||||
JS::DontFireOnNewGlobalHook, aOptions));
|
||||
|
||||
@@ -269,7 +269,7 @@ nsHTMLDocument::CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet)
|
||||
{
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -11,10 +11,14 @@
|
||||
* and create derivative works of this document.
|
||||
*/
|
||||
|
||||
enum ScrollRestoration { "auto", "manual" };
|
||||
|
||||
interface History {
|
||||
[Throws]
|
||||
readonly attribute unsigned long length;
|
||||
[Throws]
|
||||
attribute ScrollRestoration scrollRestoration;
|
||||
[Throws]
|
||||
readonly attribute any state;
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void go(optional long delta = 0);
|
||||
|
||||
@@ -1843,7 +1843,7 @@ RuntimeService::Init()
|
||||
if (!sDefaultJSSettings.gcSettings[0].IsSet()) {
|
||||
sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions();
|
||||
sDefaultJSSettings.chrome.maxScriptRuntime = -1;
|
||||
sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST);
|
||||
sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_LATEST);
|
||||
sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
|
||||
#ifdef JS_GC_ZEAL
|
||||
sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
|
||||
@@ -2620,7 +2620,7 @@ RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure
|
||||
|
||||
bool useLatest = Preferences::GetBool("dom.workers.latestJSVersion", false);
|
||||
JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions;
|
||||
options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
|
||||
options.behaviors().setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable)
|
||||
|
||||
@@ -432,8 +432,9 @@ DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
const bool extraWarnings = usesSystemPrincipal &&
|
||||
xpc::ExtraWarningsForSystemJS();
|
||||
|
||||
options.setDiscardSource(discardSource)
|
||||
.extraWarningsOverride().set(extraWarnings);
|
||||
JS::CompartmentBehaviors& behaviors = options.behaviors();
|
||||
behaviors.setDiscardSource(discardSource)
|
||||
.extraWarningsOverride().set(extraWarnings);
|
||||
|
||||
return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this,
|
||||
options,
|
||||
@@ -796,7 +797,7 @@ WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
options.setInvisibleToDebugger(true);
|
||||
options.creationOptions().setInvisibleToDebugger(true);
|
||||
|
||||
JS::Rooted<JSObject*> sandbox(aCx,
|
||||
JS_NewGlobalObject(aCx, js::Jsvalify(&workerdebuggersandbox_class), nullptr,
|
||||
|
||||
@@ -190,7 +190,6 @@ ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
||||
mPhase = PHASE_CONSTRUCTION;
|
||||
|
||||
MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
|
||||
RefPtr<gfxContext> targetContext = aTarget;
|
||||
|
||||
// If the last transaction was incomplete (a failed DoEmptyTransaction),
|
||||
// don't signal a new transaction to ShadowLayerForwarder. Carry on adding
|
||||
|
||||
@@ -55,7 +55,8 @@ static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int wid
|
||||
ss << x << ", " << y;
|
||||
|
||||
// Draw text using cairo toy text API
|
||||
cairo_t* cr = c.GetCairo();
|
||||
// XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
|
||||
cairo_t* cr = gfxFont::RefCairo(dt);
|
||||
cairo_set_font_size(cr, 25);
|
||||
cairo_text_extents_t extents;
|
||||
cairo_text_extents(cr, ss.str().c_str(), &extents);
|
||||
@@ -1327,8 +1328,6 @@ ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
|
||||
RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget();
|
||||
drawTarget->SetTransform(Matrix());
|
||||
|
||||
RefPtr<gfxContext> ctxt = new gfxContext(drawTarget);
|
||||
|
||||
// XXX Perhaps we should just copy the bounding rectangle here?
|
||||
RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot();
|
||||
nsIntRegionRectIterator it(aDirtyRegion);
|
||||
@@ -1362,7 +1361,6 @@ ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
|
||||
aTileOrigin.y * GetPresShellResolution(), GetTileLength(), GetTileLength());
|
||||
#endif
|
||||
|
||||
ctxt = nullptr;
|
||||
drawTarget = nullptr;
|
||||
|
||||
nsIntRegion tileRegion =
|
||||
|
||||
+17
-191
@@ -1,5 +1,6 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@@ -102,10 +103,6 @@ gfxContext::~gfxContext()
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
mDT->PopClip();
|
||||
}
|
||||
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mDT->Flush();
|
||||
MOZ_COUNT_DTOR(gfxContext);
|
||||
@@ -135,46 +132,11 @@ gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
DestroyRefCairo(void* aData)
|
||||
{
|
||||
cairo_t* refCairo = static_cast<cairo_t*>(aData);
|
||||
MOZ_ASSERT(refCairo);
|
||||
cairo_destroy(refCairo);
|
||||
}
|
||||
|
||||
/* static */ cairo_t *
|
||||
gfxContext::RefCairo(DrawTarget* aDT)
|
||||
{
|
||||
// DrawTargets that don't use a Cairo backend can be given a 1x1 "reference"
|
||||
// |cairo_t*|, stored in the DrawTarget's user data, for doing font-related
|
||||
// operations.
|
||||
static UserDataKey sRefCairo;
|
||||
|
||||
cairo_t* refCairo = nullptr;
|
||||
if (aDT->GetBackendType() == BackendType::CAIRO) {
|
||||
refCairo = static_cast<cairo_t*>
|
||||
(aDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
|
||||
if (refCairo) {
|
||||
return refCairo;
|
||||
}
|
||||
}
|
||||
|
||||
refCairo = static_cast<cairo_t*>(aDT->GetUserData(&sRefCairo));
|
||||
if (!refCairo) {
|
||||
refCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
|
||||
aDT->AddUserData(&sRefCairo, refCairo, DestroyRefCairo);
|
||||
}
|
||||
|
||||
return refCairo;
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::Save()
|
||||
{
|
||||
CurrentState().transform = mTransform;
|
||||
mStateStack.AppendElement(AzureState(CurrentState()));
|
||||
CurrentState().clipWasReset = false;
|
||||
CurrentState().pushedClips.Clear();
|
||||
}
|
||||
|
||||
@@ -185,11 +147,6 @@ gfxContext::Restore()
|
||||
mDT->PopClip();
|
||||
}
|
||||
|
||||
if (CurrentState().clipWasReset &&
|
||||
CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
|
||||
PushClipsToDT(mDT);
|
||||
}
|
||||
|
||||
mStateStack.RemoveElementAt(mStateStack.Length() - 1);
|
||||
|
||||
mDT = CurrentState().drawTarget;
|
||||
@@ -555,18 +512,6 @@ gfxContext::CurrentMiterLimit() const
|
||||
return CurrentState().strokeOptions.mMiterLimit;
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::SetFillRule(FillRule rule)
|
||||
{
|
||||
CurrentState().fillRule = rule;
|
||||
}
|
||||
|
||||
FillRule
|
||||
gfxContext::CurrentFillRule() const
|
||||
{
|
||||
return CurrentState().fillRule;
|
||||
}
|
||||
|
||||
// clipping
|
||||
void
|
||||
gfxContext::Clip(const Rect& rect)
|
||||
@@ -643,9 +588,6 @@ gfxContext::HasComplexClip() const
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -653,15 +595,7 @@ gfxContext::HasComplexClip() const
|
||||
bool
|
||||
gfxContext::ExportClip(ClipExporter& aExporter)
|
||||
{
|
||||
unsigned int lastReset = 0;
|
||||
for (int i = mStateStack.Length() - 1; i > 0; i--) {
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
lastReset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int i = 0; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
|
||||
gfx::Matrix transform = clip.transform;
|
||||
@@ -687,20 +621,12 @@ gfxContext::ExportClip(ClipExporter& aExporter)
|
||||
bool
|
||||
gfxContext::ClipContainsRect(const gfxRect& aRect)
|
||||
{
|
||||
unsigned int lastReset = 0;
|
||||
for (int i = mStateStack.Length() - 2; i > 0; i--) {
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
lastReset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we always return false when the clip list contains a
|
||||
// non-rectangular clip or a non-rectilinear transform, our 'total' clip
|
||||
// is always a rectangle if we hit the end of this function.
|
||||
Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
|
||||
|
||||
for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int i = 0; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
|
||||
if (clip.path || !clip.transform.IsRectilinear()) {
|
||||
@@ -1079,18 +1005,18 @@ gfxContext::EnsurePath()
|
||||
Matrix mat = mTransform;
|
||||
mat.Invert();
|
||||
mat = mPathTransform * mat;
|
||||
mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
|
||||
mPathBuilder = mPath->TransformedCopyToBuilder(mat);
|
||||
mPath = mPathBuilder->Finish();
|
||||
mPathBuilder = nullptr;
|
||||
|
||||
mTransformChanged = false;
|
||||
}
|
||||
|
||||
if (CurrentState().fillRule == mPath->GetFillRule()) {
|
||||
if (FillRule::FILL_WINDING == mPath->GetFillRule()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
|
||||
mPathBuilder = mPath->CopyToBuilder();
|
||||
|
||||
mPath = mPathBuilder->Finish();
|
||||
mPathBuilder = nullptr;
|
||||
@@ -1111,13 +1037,13 @@ gfxContext::EnsurePathBuilder()
|
||||
|
||||
if (mPath) {
|
||||
if (!mTransformChanged) {
|
||||
mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
|
||||
mPathBuilder = mPath->CopyToBuilder();
|
||||
mPath = nullptr;
|
||||
} else {
|
||||
Matrix invTransform = mTransform;
|
||||
invTransform.Invert();
|
||||
Matrix toNewUS = mPathTransform * invTransform;
|
||||
mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
|
||||
mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1125,7 +1051,7 @@ gfxContext::EnsurePathBuilder()
|
||||
DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
|
||||
|
||||
if (!mPathBuilder) {
|
||||
mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
|
||||
mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
|
||||
|
||||
if (mPathIsRect) {
|
||||
mPathBuilder->MoveTo(mRect.TopLeft());
|
||||
@@ -1148,7 +1074,7 @@ gfxContext::EnsurePathBuilder()
|
||||
Matrix toNewUS = mPathTransform * invTransform;
|
||||
|
||||
RefPtr<Path> path = mPathBuilder->Finish();
|
||||
mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
|
||||
mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
|
||||
}
|
||||
|
||||
mPathIsRect = false;
|
||||
@@ -1180,22 +1106,10 @@ gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity)
|
||||
void
|
||||
gfxContext::PushClipsToDT(DrawTarget *aDT)
|
||||
{
|
||||
// Tricky, we have to restore all clips -since the last time- the clip
|
||||
// was reset. If we didn't reset the clip, just popping the clips we
|
||||
// added was fine.
|
||||
unsigned int lastReset = 0;
|
||||
for (int i = mStateStack.Length() - 2; i > 0; i--) {
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
lastReset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't need to save the old transform, we'll be setting a new one soon!
|
||||
|
||||
// Push all clips from the last state on the stack where the clip was
|
||||
// reset to the clip before ours.
|
||||
for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
|
||||
// Push all clips from the bottom of the stack to the clip before ours.
|
||||
for (unsigned int i = 0; i < mStateStack.Length() - 1; i++) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
|
||||
if (mStateStack[i].pushedClips[c].path) {
|
||||
@@ -1269,8 +1183,8 @@ gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransfo
|
||||
mRect = toNewUS.TransformBounds(mRect);
|
||||
mRect.NudgeToIntegers();
|
||||
} else {
|
||||
mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
|
||||
|
||||
mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
|
||||
|
||||
mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
|
||||
mPathBuilder->LineTo(toNewUS * mRect.TopRight());
|
||||
mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
|
||||
@@ -1295,17 +1209,9 @@ gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransfo
|
||||
Rect
|
||||
gfxContext::GetAzureDeviceSpaceClipBounds()
|
||||
{
|
||||
unsigned int lastReset = 0;
|
||||
for (int i = mStateStack.Length() - 1; i > 0; i--) {
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
lastReset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
|
||||
Float(mDT->GetSize().width), Float(mDT->GetSize().height));
|
||||
for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int i = 0; i < mStateStack.Length(); i++) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
|
||||
if (clip.path) {
|
||||
@@ -1382,83 +1288,3 @@ gfxContext::PushNewDT(gfxContentType content)
|
||||
mDT = newDT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work out whether cairo will snap inter-glyph spacing to pixels.
|
||||
*
|
||||
* Layout does not align text to pixel boundaries, so, with font drawing
|
||||
* backends that snap glyph positions to pixels, it is important that
|
||||
* inter-glyph spacing within words is always an integer number of pixels.
|
||||
* This ensures that the drawing backend snaps all of the word's glyphs in the
|
||||
* same direction and so inter-glyph spacing remains the same.
|
||||
*/
|
||||
void
|
||||
gfxContext::GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY)
|
||||
{
|
||||
*aRoundX = false;
|
||||
// Could do something fancy here for ScaleFactors of
|
||||
// AxisAlignedTransforms, but we leave things simple.
|
||||
// Not much point rounding if a matrix will mess things up anyway.
|
||||
// Also return false for non-cairo contexts.
|
||||
if (CurrentMatrix().HasNonTranslation()) {
|
||||
*aRoundY = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// All raster backends snap glyphs to pixels vertically.
|
||||
// Print backends set CAIRO_HINT_METRICS_OFF.
|
||||
*aRoundY = true;
|
||||
|
||||
cairo_t *cr = gfxContext::RefCairo(GetDrawTarget());
|
||||
cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
|
||||
|
||||
// bug 1198921 - this sometimes fails under Windows for whatver reason
|
||||
NS_ASSERTION(scaled_font, "null cairo scaled font should never be returned "
|
||||
"by cairo_get_scaled_font");
|
||||
if (!scaled_font) {
|
||||
*aRoundX = true; // default to the same as the fallback path below
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes hint metrics gets set for us, most notably for printing.
|
||||
cairo_hint_metrics_t hint_metrics =
|
||||
cairo_scaled_font_get_hint_metrics(scaled_font);
|
||||
|
||||
switch (hint_metrics) {
|
||||
case CAIRO_HINT_METRICS_OFF:
|
||||
*aRoundY = false;
|
||||
return;
|
||||
case CAIRO_HINT_METRICS_DEFAULT:
|
||||
// Here we mimic what cairo surface/font backends do. Printing
|
||||
// surfaces have already been handled by hint_metrics. The
|
||||
// fallback show_glyphs implementation composites pixel-aligned
|
||||
// glyph surfaces, so we just pick surface/font combinations that
|
||||
// override this.
|
||||
switch (cairo_scaled_font_get_type(scaled_font)) {
|
||||
#if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
|
||||
case CAIRO_FONT_TYPE_DWRITE:
|
||||
// show_glyphs is implemented on the font and so is used for
|
||||
// all surface types; however, it may pixel-snap depending on
|
||||
// the dwrite rendering mode
|
||||
if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
|
||||
gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
|
||||
DWRITE_MEASURING_MODE_NATURAL) {
|
||||
return;
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
#endif
|
||||
case CAIRO_FONT_TYPE_QUARTZ:
|
||||
// Quartz surfaces implement show_glyphs for Quartz fonts
|
||||
if (cairo_surface_get_type(cairo_get_target(cr)) ==
|
||||
CAIRO_SURFACE_TYPE_QUARTZ) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CAIRO_HINT_METRICS_ON:
|
||||
break;
|
||||
}
|
||||
*aRoundX = true;
|
||||
}
|
||||
|
||||
@@ -84,12 +84,6 @@ public:
|
||||
return CurrentSurface(nullptr, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the reference cairo_t object from aDT.
|
||||
* XXX this should be moved into gfxFont at some point.
|
||||
*/
|
||||
static cairo_t* RefCairo(mozilla::gfx::DrawTarget* aDT);
|
||||
|
||||
mozilla::gfx::DrawTarget *GetDrawTarget() { return mDT; }
|
||||
|
||||
/**
|
||||
@@ -365,13 +359,6 @@ public:
|
||||
void SetMiterLimit(gfxFloat limit);
|
||||
gfxFloat CurrentMiterLimit() const;
|
||||
|
||||
/**
|
||||
** Fill Properties
|
||||
**/
|
||||
|
||||
void SetFillRule(FillRule rule);
|
||||
FillRule CurrentFillRule() const;
|
||||
|
||||
/**
|
||||
* Sets the operator used for all further drawing. The operator affects
|
||||
* how drawing something will modify the destination. For example, the
|
||||
@@ -451,9 +438,6 @@ public:
|
||||
|
||||
mozilla::gfx::Point GetDeviceOffset() const;
|
||||
|
||||
// Work out whether cairo will snap inter-glyph spacing to pixels.
|
||||
void GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY);
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
/**
|
||||
* Debug functions to encode the current surface as a PNG and export it.
|
||||
@@ -495,8 +479,6 @@ private:
|
||||
AzureState()
|
||||
: op(mozilla::gfx::CompositionOp::OP_OVER)
|
||||
, color(0, 0, 0, 1.0f)
|
||||
, clipWasReset(false)
|
||||
, fillRule(mozilla::gfx::FillRule::FILL_WINDING)
|
||||
, aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL)
|
||||
, patternTransformChanged(false)
|
||||
{}
|
||||
@@ -516,11 +498,8 @@ private:
|
||||
};
|
||||
nsTArray<PushedClip> pushedClips;
|
||||
nsTArray<Float> dashPattern;
|
||||
bool clipWasReset;
|
||||
mozilla::gfx::FillRule fillRule;
|
||||
StrokeOptions strokeOptions;
|
||||
RefPtr<DrawTarget> drawTarget;
|
||||
RefPtr<DrawTarget> parentTarget;
|
||||
mozilla::gfx::AntialiasMode aaMode;
|
||||
bool patternTransformChanged;
|
||||
Matrix patternTransform;
|
||||
@@ -559,8 +538,6 @@ private:
|
||||
AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
|
||||
const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
|
||||
|
||||
cairo_t *mRefCairo;
|
||||
|
||||
RefPtr<DrawTarget> mDT;
|
||||
RefPtr<DrawTarget> mOriginalDT;
|
||||
};
|
||||
|
||||
@@ -469,7 +469,7 @@ gfxDWriteFont::GetSpaceGlyph()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxDWriteFont::SetupCairoFont(gfxContext *aContext)
|
||||
gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
|
||||
{
|
||||
cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
|
||||
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
|
||||
@@ -477,8 +477,7 @@ gfxDWriteFont::SetupCairoFont(gfxContext *aContext)
|
||||
// the cairo_t, precluding any further drawing.
|
||||
return false;
|
||||
}
|
||||
cairo_set_scaled_font(gfxContext::RefCairo(aContext->GetDrawTarget()),
|
||||
scaledFont);
|
||||
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), scaledFont);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
|
||||
virtual uint32_t GetSpaceGlyph() override;
|
||||
|
||||
virtual bool SetupCairoFont(gfxContext *aContext) override;
|
||||
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
|
||||
|
||||
virtual bool AllowSubpixelAA() override
|
||||
{ return mAllowManualShowGlyphs; }
|
||||
|
||||
@@ -176,7 +176,7 @@ gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFT2FontBase::SetupCairoFont(gfxContext *aContext)
|
||||
gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
|
||||
{
|
||||
// The scaled font ctm is not relevant right here because
|
||||
// cairo_set_scaled_font does not record the scaled font itself, but
|
||||
@@ -210,7 +210,6 @@ gfxFT2FontBase::SetupCairoFont(gfxContext *aContext)
|
||||
// what is set here. It's too late to change things here as measuring has
|
||||
// already taken place. We should really be measuring with a different
|
||||
// font for pdf and ps surfaces (bug 403513).
|
||||
cairo_set_scaled_font(gfxContext::RefCairo(aContext->GetDrawTarget()),
|
||||
cairoFont);
|
||||
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
uint16_t aGID) override;
|
||||
|
||||
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
|
||||
virtual bool SetupCairoFont(gfxContext *aContext) override;
|
||||
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
|
||||
|
||||
virtual FontType GetType() const override { return FONT_TYPE_FT2; }
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
|
||||
aVertical, aShapedText)) {
|
||||
// harfbuzz must have failed(?!), just render raw glyphs
|
||||
AddRange(aText, aOffset, aLength, aShapedText);
|
||||
PostShapingFixup(aContext, aText, aOffset, aLength, aVertical,
|
||||
aShapedText);
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength,
|
||||
aVertical, aShapedText);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
+142
-29
@@ -548,6 +548,90 @@ gfxFontShaper::MergeFontFeatures(
|
||||
}
|
||||
}
|
||||
|
||||
// Work out whether cairo will snap inter-glyph spacing to pixels.
|
||||
//
|
||||
// Layout does not align text to pixel boundaries, so, with font drawing
|
||||
// backends that snap glyph positions to pixels, it is important that
|
||||
// inter-glyph spacing within words is always an integer number of pixels.
|
||||
// This ensures that the drawing backend snaps all of the word's glyphs in the
|
||||
// same direction and so inter-glyph spacing remains the same.
|
||||
//
|
||||
/* static */ void
|
||||
gfxFontShaper::GetRoundOffsetsToPixels(DrawTarget* aDrawTarget,
|
||||
bool* aRoundX, bool* aRoundY)
|
||||
{
|
||||
*aRoundX = false;
|
||||
// Could do something fancy here for ScaleFactors of
|
||||
// AxisAlignedTransforms, but we leave things simple.
|
||||
// Not much point rounding if a matrix will mess things up anyway.
|
||||
// Also return false for non-cairo contexts.
|
||||
if (aDrawTarget->GetTransform().HasNonTranslation()) {
|
||||
*aRoundY = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// All raster backends snap glyphs to pixels vertically.
|
||||
// Print backends set CAIRO_HINT_METRICS_OFF.
|
||||
*aRoundY = true;
|
||||
|
||||
cairo_t* cr = gfxFont::RefCairo(aDrawTarget);
|
||||
cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
|
||||
|
||||
// bug 1198921 - this sometimes fails under Windows for whatver reason
|
||||
NS_ASSERTION(scaled_font, "null cairo scaled font should never be returned "
|
||||
"by cairo_get_scaled_font");
|
||||
if (!scaled_font) {
|
||||
*aRoundX = true; // default to the same as the fallback path below
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes hint metrics gets set for us, most notably for printing.
|
||||
cairo_font_options_t *font_options = cairo_font_options_create();
|
||||
cairo_scaled_font_get_font_options(scaled_font, font_options);
|
||||
cairo_hint_metrics_t hint_metrics =
|
||||
cairo_font_options_get_hint_metrics(font_options);
|
||||
cairo_font_options_destroy(font_options);
|
||||
|
||||
switch (hint_metrics) {
|
||||
case CAIRO_HINT_METRICS_OFF:
|
||||
*aRoundY = false;
|
||||
return;
|
||||
case CAIRO_HINT_METRICS_DEFAULT:
|
||||
// Here we mimic what cairo surface/font backends do. Printing
|
||||
// surfaces have already been handled by hint_metrics. The
|
||||
// fallback show_glyphs implementation composites pixel-aligned
|
||||
// glyph surfaces, so we just pick surface/font combinations that
|
||||
// override this.
|
||||
switch (cairo_scaled_font_get_type(scaled_font)) {
|
||||
#if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
|
||||
case CAIRO_FONT_TYPE_DWRITE:
|
||||
// show_glyphs is implemented on the font and so is used for
|
||||
// all surface types; however, it may pixel-snap depending on
|
||||
// the dwrite rendering mode
|
||||
if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
|
||||
gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
|
||||
DWRITE_MEASURING_MODE_NATURAL) {
|
||||
return;
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
#endif
|
||||
case CAIRO_FONT_TYPE_QUARTZ:
|
||||
// Quartz surfaces implement show_glyphs for Quartz fonts
|
||||
if (cairo_surface_get_type(cairo_get_target(cr)) ==
|
||||
CAIRO_SURFACE_TYPE_QUARTZ) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CAIRO_HINT_METRICS_ON:
|
||||
break;
|
||||
}
|
||||
*aRoundX = true;
|
||||
}
|
||||
|
||||
void
|
||||
gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
|
||||
const char16_t *aString,
|
||||
@@ -780,7 +864,7 @@ gfxFont::~gfxFont()
|
||||
gfxFloat
|
||||
gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
|
||||
{
|
||||
if (!SetupCairoFont(aCtx)) {
|
||||
if (!SetupCairoFont(aCtx->GetDrawTarget())) {
|
||||
return 0;
|
||||
}
|
||||
if (ProvidesGlyphWidths()) {
|
||||
@@ -1641,10 +1725,10 @@ private:
|
||||
// the second draw occurs at a constant offset in device pixels.
|
||||
|
||||
double
|
||||
gfxFont::CalcXScale(gfxContext *aContext)
|
||||
gfxFont::CalcXScale(DrawTarget* aDrawTarget)
|
||||
{
|
||||
// determine magnitude of a 1px x offset in device space
|
||||
gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
|
||||
Size t = aDrawTarget->GetTransform() * Size(1.0, 0.0);
|
||||
if (t.width == 1.0 && t.height == 0.0) {
|
||||
// short-circuit the most common case to avoid sqrt() and division
|
||||
return 1.0;
|
||||
@@ -1715,8 +1799,9 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
|
||||
}
|
||||
|
||||
if (fontParams.haveColorGlyphs &&
|
||||
RenderColorGlyph(runParams.context, fontParams.scaledFont,
|
||||
fontParams.renderingOptions, fontParams.drawOptions,
|
||||
RenderColorGlyph(runParams.dt,
|
||||
fontParams.scaledFont, fontParams.renderingOptions,
|
||||
fontParams.drawOptions,
|
||||
fontParams.matInv * gfx::Point(devPt.x, devPt.y),
|
||||
aGlyphID)) {
|
||||
return;
|
||||
@@ -1968,7 +2053,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
|
||||
// Synthetic-bold strikes are each offset one device pixel in run direction.
|
||||
// (these values are only needed if IsSyntheticBold() is true)
|
||||
if (IsSyntheticBold()) {
|
||||
double xscale = CalcXScale(aRunParams.context);
|
||||
double xscale = CalcXScale(aRunParams.context->GetDrawTarget());
|
||||
fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
|
||||
if (xscale != 0.0) {
|
||||
// use as many strikes as needed for the the increased advance
|
||||
@@ -2097,7 +2182,7 @@ gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMod
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFont::RenderColorGlyph(gfxContext* aContext,
|
||||
gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget,
|
||||
mozilla::gfx::ScaledFont* scaledFont,
|
||||
GlyphRenderingOptions* aRenderingOptions,
|
||||
mozilla::gfx::DrawOptions aDrawOptions,
|
||||
@@ -2111,7 +2196,6 @@ gfxFont::RenderColorGlyph(gfxContext* aContext,
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
|
||||
for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
|
||||
layerIndex++) {
|
||||
Glyph glyph;
|
||||
@@ -2122,9 +2206,9 @@ gfxFont::RenderColorGlyph(gfxContext* aContext,
|
||||
buffer.mGlyphs = &glyph;
|
||||
buffer.mNumGlyphs = 1;
|
||||
|
||||
dt->FillGlyphs(scaledFont, buffer,
|
||||
ColorPattern(layerColors[layerIndex]),
|
||||
aDrawOptions, aRenderingOptions);
|
||||
aDrawTarget->FillGlyphs(scaledFont, buffer,
|
||||
ColorPattern(layerColors[layerIndex]),
|
||||
aDrawOptions, aRenderingOptions);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2587,26 +2671,26 @@ gfxFont::ShapeText(gfxContext *aContext,
|
||||
|
||||
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
|
||||
|
||||
PostShapingFixup(aContext, aText, aOffset, aLength, aVertical,
|
||||
aShapedText);
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength,
|
||||
aVertical, aShapedText);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFont::PostShapingFixup(gfxContext *aContext,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
bool aVertical,
|
||||
gfxShapedText *aShapedText)
|
||||
gfxFont::PostShapingFixup(DrawTarget* aDrawTarget,
|
||||
const char16_t* aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
bool aVertical,
|
||||
gfxShapedText* aShapedText)
|
||||
{
|
||||
if (IsSyntheticBold()) {
|
||||
const Metrics& metrics =
|
||||
GetMetrics(aVertical ? eVertical : eHorizontal);
|
||||
if (metrics.maxAdvance > metrics.aveCharWidth) {
|
||||
float synBoldOffset =
|
||||
GetSyntheticBoldOffset() * CalcXScale(aContext);
|
||||
GetSyntheticBoldOffset() * CalcXScale(aDrawTarget);
|
||||
aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
|
||||
aOffset, aLength);
|
||||
}
|
||||
@@ -3182,6 +3266,40 @@ gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
|
||||
return fe->FindOrMakeFont(&style, needsBold, mUnicodeRangeMap);
|
||||
}
|
||||
|
||||
static void
|
||||
DestroyRefCairo(void* aData)
|
||||
{
|
||||
cairo_t* refCairo = static_cast<cairo_t*>(aData);
|
||||
MOZ_ASSERT(refCairo);
|
||||
cairo_destroy(refCairo);
|
||||
}
|
||||
|
||||
/* static */ cairo_t *
|
||||
gfxFont::RefCairo(DrawTarget* aDT)
|
||||
{
|
||||
// DrawTargets that don't use a Cairo backend can be given a 1x1 "reference"
|
||||
// |cairo_t*|, stored in the DrawTarget's user data, for doing font-related
|
||||
// operations.
|
||||
static UserDataKey sRefCairo;
|
||||
|
||||
cairo_t* refCairo = nullptr;
|
||||
if (aDT->GetBackendType() == BackendType::CAIRO) {
|
||||
refCairo = static_cast<cairo_t*>
|
||||
(aDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
|
||||
if (refCairo) {
|
||||
return refCairo;
|
||||
}
|
||||
}
|
||||
|
||||
refCairo = static_cast<cairo_t*>(aDT->GetUserData(&sRefCairo));
|
||||
if (!refCairo) {
|
||||
refCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
|
||||
aDT->AddUserData(&sRefCairo, refCairo, DestroyRefCairo);
|
||||
}
|
||||
|
||||
return refCairo;
|
||||
}
|
||||
|
||||
gfxGlyphExtents *
|
||||
gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
|
||||
uint32_t i, count = mGlyphExtentsArray.Length();
|
||||
@@ -3200,16 +3318,12 @@ gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
|
||||
}
|
||||
|
||||
void
|
||||
gfxFont::SetupGlyphExtents(gfxContext *aContext,
|
||||
uint32_t aGlyphID, bool aNeedTight,
|
||||
gfxGlyphExtents *aExtents)
|
||||
gfxFont::SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
|
||||
bool aNeedTight, gfxGlyphExtents *aExtents)
|
||||
{
|
||||
gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
|
||||
aContext->SetMatrix(gfxMatrix());
|
||||
|
||||
gfxRect svgBounds;
|
||||
if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
|
||||
mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
|
||||
mFontEntry->GetSVGGlyphExtents(aDrawTarget, aGlyphID, &svgBounds)) {
|
||||
gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
|
||||
aExtents->SetTightGlyphExtents(aGlyphID,
|
||||
gfxRect(svgBounds.x * d2a,
|
||||
@@ -3224,8 +3338,7 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext,
|
||||
glyph.x = 0;
|
||||
glyph.y = 0;
|
||||
cairo_text_extents_t extents;
|
||||
cairo_glyph_extents(gfxContext::RefCairo(aContext->GetDrawTarget()),
|
||||
&glyph, 1, &extents);
|
||||
cairo_glyph_extents(gfxFont::RefCairo(aDrawTarget), &glyph, 1, &extents);
|
||||
|
||||
const Metrics& fontMetrics = GetMetrics(eHorizontal);
|
||||
int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
|
||||
|
||||
+23
-11
@@ -29,6 +29,7 @@
|
||||
#include "harfbuzz/hb.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
typedef struct _cairo cairo_t;
|
||||
typedef struct _cairo_scaled_font cairo_scaled_font_t;
|
||||
//typedef struct gr_face gr_face;
|
||||
|
||||
@@ -613,6 +614,8 @@ protected:
|
||||
|
||||
class gfxFontShaper {
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
explicit gfxFontShaper(gfxFont *aFont)
|
||||
: mFont(aFont)
|
||||
{
|
||||
@@ -645,6 +648,10 @@ public:
|
||||
void* aHandleFeatureData);
|
||||
|
||||
protected:
|
||||
// Work out whether cairo will snap inter-glyph spacing to pixels.
|
||||
static void GetRoundOffsetsToPixels(DrawTarget* aDrawTarget,
|
||||
bool* aRoundX, bool* aRoundY);
|
||||
|
||||
// the font this shaper is working with. The font owns a nsAutoPtr reference
|
||||
// to this object, and will destroy it before it dies. Thus, mFont will always
|
||||
// be valid.
|
||||
@@ -1656,12 +1663,12 @@ public:
|
||||
|
||||
gfxGlyphExtents *GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit);
|
||||
|
||||
// You need to call SetupCairoFont on the aCR just before calling this
|
||||
virtual void SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID,
|
||||
// You need to call SetupCairoFont on aDrawTarget just before calling this.
|
||||
virtual void SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
|
||||
bool aNeedTight, gfxGlyphExtents *aExtents);
|
||||
|
||||
// This is called by the default Draw() implementation above.
|
||||
virtual bool SetupCairoFont(gfxContext *aContext) = 0;
|
||||
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) = 0;
|
||||
|
||||
virtual bool AllowSubpixelAA() { return true; }
|
||||
|
||||
@@ -1843,6 +1850,11 @@ public:
|
||||
virtual already_AddRefed<gfxFont>
|
||||
GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
|
||||
|
||||
/**
|
||||
* Return the reference cairo_t object from aDT.
|
||||
*/
|
||||
static cairo_t* RefCairo(mozilla::gfx::DrawTarget* aDT);
|
||||
|
||||
protected:
|
||||
virtual const Metrics& GetHorizontalMetrics() = 0;
|
||||
|
||||
@@ -1927,12 +1939,12 @@ protected:
|
||||
// Helper to adjust for synthetic bold and set character-type flags
|
||||
// in the shaped text; implementations of ShapeText should call this
|
||||
// after glyph shaping has been completed.
|
||||
void PostShapingFixup(gfxContext *aContext,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset, // position within aShapedText
|
||||
uint32_t aLength,
|
||||
bool aVertical,
|
||||
gfxShapedText *aShapedText);
|
||||
void PostShapingFixup(DrawTarget* aContext,
|
||||
const char16_t* aText,
|
||||
uint32_t aOffset, // position within aShapedText
|
||||
uint32_t aLength,
|
||||
bool aVertical,
|
||||
gfxShapedText* aShapedText);
|
||||
|
||||
// Shape text directly into a range within a textrun, without using the
|
||||
// font's word cache. Intended for use when the font has layout features
|
||||
@@ -2126,7 +2138,7 @@ protected:
|
||||
gfxTextRunDrawCallbacks *aCallbacks,
|
||||
bool& aEmittedGlyphs) const;
|
||||
|
||||
bool RenderColorGlyph(gfxContext* aContext,
|
||||
bool RenderColorGlyph(DrawTarget* aDrawTarget,
|
||||
mozilla::gfx::ScaledFont* scaledFont,
|
||||
mozilla::gfx::GlyphRenderingOptions* renderingOptions,
|
||||
mozilla::gfx::DrawOptions drawOptions,
|
||||
@@ -2140,7 +2152,7 @@ protected:
|
||||
// the second draw occurs at a constant offset in device pixels.
|
||||
// This helper calculates the scale factor we need to apply to the
|
||||
// synthetic-bold offset.
|
||||
static double CalcXScale(gfxContext *aContext);
|
||||
static double CalcXScale(DrawTarget* aDrawTarget);
|
||||
};
|
||||
|
||||
// proportion of ascent used for x-height, if unable to read value from font
|
||||
|
||||
@@ -333,7 +333,7 @@ gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
|
||||
gfxFontEntry::GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
|
||||
gfxRect *aResult)
|
||||
{
|
||||
MOZ_ASSERT(mSVGInitialized,
|
||||
@@ -342,8 +342,7 @@ gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
|
||||
"font has invalid unitsPerEm");
|
||||
|
||||
cairo_matrix_t fontMatrix;
|
||||
cairo_get_font_matrix(gfxContext::RefCairo(aContext->GetDrawTarget()),
|
||||
&fontMatrix);
|
||||
cairo_get_font_matrix(gfxFont::RefCairo(aDrawTarget), &fontMatrix);
|
||||
|
||||
gfxMatrix svgToAppSpace(fontMatrix.xx, fontMatrix.yx,
|
||||
fontMatrix.xy, fontMatrix.yy,
|
||||
|
||||
@@ -97,6 +97,8 @@ private:
|
||||
|
||||
class gfxFontEntry {
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
|
||||
|
||||
explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
|
||||
@@ -180,7 +182,7 @@ public:
|
||||
|
||||
bool TryGetSVGData(gfxFont* aFont);
|
||||
bool HasSVGGlyph(uint32_t aGlyphId);
|
||||
bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
|
||||
bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
|
||||
gfxRect *aResult);
|
||||
bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
|
||||
gfxTextContextPaint *aContextPaint);
|
||||
|
||||
@@ -98,7 +98,7 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
|
||||
// creating a "toy" font internally (see bug 544617).
|
||||
// We must check that this succeeded, otherwise we risk cairo creating the
|
||||
// wrong kind of font internally as a fallback (bug 744480).
|
||||
if (!SetupCairoFont(aContext)) {
|
||||
if (!SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ gfxGDIFont::GetSpaceGlyph()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxGDIFont::SetupCairoFont(gfxContext *aContext)
|
||||
gfxGDIFont::SetupCairoFont(DrawTarget* aDrawTarget)
|
||||
{
|
||||
if (!mMetrics) {
|
||||
Initialize();
|
||||
@@ -136,7 +136,7 @@ gfxGDIFont::SetupCairoFont(gfxContext *aContext)
|
||||
// the cairo_t, precluding any further drawing.
|
||||
return false;
|
||||
}
|
||||
cairo_set_scaled_font(gfxContext::RefCairo(aContext->GetDrawTarget()), mScaledFont);
|
||||
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
/* overrides for the pure virtual methods in gfxFont */
|
||||
virtual uint32_t GetSpaceGlyph() override;
|
||||
|
||||
virtual bool SetupCairoFont(gfxContext *aContext) override;
|
||||
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
|
||||
|
||||
/* override Measure to add padding for antialiasing */
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
|
||||
@@ -45,11 +45,11 @@ gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aFont->SetupCairoFont(aContext)) {
|
||||
if (aFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupLazyTight;
|
||||
#endif
|
||||
aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
|
||||
aFont->SetupGlyphExtents(aContext->GetDrawTarget(), aGlyphID, true, this);
|
||||
entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
||||
}
|
||||
if (!entry) {
|
||||
|
||||
@@ -91,7 +91,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext,
|
||||
gfxShapedText *aShapedText)
|
||||
{
|
||||
// some font back-ends require this in order to get proper hinted metrics
|
||||
if (!mFont->SetupCairoFont(aContext)) {
|
||||
if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -268,9 +268,8 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext,
|
||||
}
|
||||
}
|
||||
|
||||
bool roundX;
|
||||
bool roundY;
|
||||
aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
|
||||
bool roundX, roundY;
|
||||
GetRoundOffsetsToPixels(aContext->GetDrawTarget(), &roundX, &roundY);
|
||||
|
||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
||||
aShapedText->GetCharacterGlyphs() + aOffset;
|
||||
|
||||
@@ -1471,7 +1471,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
gfxShapedText *aShapedText)
|
||||
{
|
||||
// some font back-ends require this in order to get proper hinted metrics
|
||||
if (!mFont->SetupCairoFont(aContext)) {
|
||||
if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1617,12 +1617,12 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
int32_t glyphStart = 0; // looking for a clump that starts at this glyph
|
||||
int32_t charStart = 0; // and this char index within the range of the run
|
||||
|
||||
bool roundI;
|
||||
bool roundB;
|
||||
bool roundI, roundB;
|
||||
DrawTarget* drawTarget = aContext->GetDrawTarget();
|
||||
if (aVertical) {
|
||||
aContext->GetRoundOffsetsToPixels(&roundB, &roundI);
|
||||
GetRoundOffsetsToPixels(drawTarget, &roundB, &roundI);
|
||||
} else {
|
||||
aContext->GetRoundOffsetsToPixels(&roundI, &roundB);
|
||||
GetRoundOffsetsToPixels(drawTarget, &roundI, &roundB);
|
||||
}
|
||||
|
||||
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
||||
|
||||
@@ -142,8 +142,8 @@ gfxMacFont::ShapeText(gfxContext *aContext,
|
||||
}
|
||||
if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
aScript, aVertical, aShapedText)) {
|
||||
PostShapingFixup(aContext, aText, aOffset, aLength, aVertical,
|
||||
aShapedText);
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset,
|
||||
aLength, aVertical, aShapedText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -153,15 +153,14 @@ gfxMacFont::ShapeText(gfxContext *aContext,
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMacFont::SetupCairoFont(gfxContext *aContext)
|
||||
gfxMacFont::SetupCairoFont(DrawTarget* aDrawTarget)
|
||||
{
|
||||
if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
|
||||
// Don't cairo_set_scaled_font as that would propagate the error to
|
||||
// the cairo_t, precluding any further drawing.
|
||||
return false;
|
||||
}
|
||||
cairo_set_scaled_font(gfxContext::RefCairo(aContext->GetDrawTarget()),
|
||||
mScaledFont);
|
||||
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
return mSpaceGlyph;
|
||||
}
|
||||
|
||||
virtual bool SetupCairoFont(gfxContext *aContext) override;
|
||||
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
|
||||
|
||||
/* override Measure to add padding for antialiasing */
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
|
||||
@@ -211,8 +211,7 @@ gfxTextRun::ReleaseFontGroup()
|
||||
|
||||
bool
|
||||
gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
|
||||
uint8_t *aBreakBefore,
|
||||
gfxContext *aRefContext)
|
||||
uint8_t *aBreakBefore)
|
||||
{
|
||||
NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
|
||||
|
||||
@@ -1411,7 +1410,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
uint32_t glyphIndex = glyphData->GetSimpleGlyph();
|
||||
if (!extents->IsGlyphKnown(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
if (!font->SetupCairoFont(aRefContext)) {
|
||||
if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) {
|
||||
NS_WARNING("failed to set up font for glyph extents");
|
||||
break;
|
||||
}
|
||||
@@ -1420,7 +1419,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupEagerSimple;
|
||||
#endif
|
||||
font->SetupGlyphExtents(aRefContext,
|
||||
font->SetupGlyphExtents(aRefContext->GetDrawTarget(),
|
||||
glyphIndex, false, extents);
|
||||
}
|
||||
}
|
||||
@@ -1437,7 +1436,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
uint32_t glyphIndex = details->mGlyphID;
|
||||
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
if (!font->SetupCairoFont(aRefContext)) {
|
||||
if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) {
|
||||
NS_WARNING("failed to set up font for glyph extents");
|
||||
break;
|
||||
}
|
||||
@@ -1446,7 +1445,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupEagerTight;
|
||||
#endif
|
||||
font->SetupGlyphExtents(aRefContext,
|
||||
font->SetupGlyphExtents(aRefContext->GetDrawTarget(),
|
||||
glyphIndex, true, extents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +153,7 @@ public:
|
||||
* breaks are the same as the old
|
||||
*/
|
||||
virtual bool SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
|
||||
uint8_t *aBreakBefore,
|
||||
gfxContext *aRefContext);
|
||||
uint8_t *aBreakBefore);
|
||||
|
||||
/**
|
||||
* Layout provides PropertyProvider objects. These allow detection of
|
||||
|
||||
@@ -513,8 +513,9 @@ XPCShellEnvironment::Init()
|
||||
}
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
options.setZone(JS::SystemZone)
|
||||
.setVersion(JSVERSION_LATEST);
|
||||
options.creationOptions().setZone(JS::SystemZone);
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = xpc->InitClassesWithNewWrappedGlobal(cx,
|
||||
static_cast<nsIGlobalObject *>(backstagePass),
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "JavaScriptParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jswrapper.h"
|
||||
@@ -68,8 +69,11 @@ JavaScriptParent::allowMessage(JSContext* cx)
|
||||
return true;
|
||||
|
||||
if (ForbidUnsafeBrowserCPOWs()) {
|
||||
if (JSObject* global = JS::CurrentGlobalOrNull(cx)) {
|
||||
if (!JS::AddonIdOfObject(global)) {
|
||||
nsIGlobalObject* global = dom::GetIncumbentGlobal();
|
||||
JSObject* jsGlobal = global ? global->GetGlobalJSObject() : nullptr;
|
||||
if (jsGlobal) {
|
||||
JSAutoCompartment ac(cx, jsGlobal);
|
||||
if (!JS::AddonIdOfObject(jsGlobal) && !xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) {
|
||||
JS_ReportError(cx, "unsafe CPOW usage forbidden");
|
||||
return false;
|
||||
}
|
||||
|
||||
+127
-147
@@ -26,6 +26,7 @@ using namespace js::unicode;
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::Maybe;
|
||||
|
||||
/* ES6 21.2.5.2.2 steps 19-29. */
|
||||
bool
|
||||
js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
|
||||
MutableHandleValue rval)
|
||||
@@ -50,11 +51,13 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
||||
size_t numPairs = matches.length();
|
||||
MOZ_ASSERT(numPairs > 0);
|
||||
|
||||
/* Step 19. */
|
||||
RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
|
||||
if (!arr)
|
||||
return false;
|
||||
|
||||
/* Store a Value for each pair. */
|
||||
/* Steps 27-28
|
||||
* Store a Value for each pair. */
|
||||
for (size_t i = 0; i < numPairs; i++) {
|
||||
const MatchPair& pair = matches[i];
|
||||
|
||||
@@ -71,10 +74,12 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the |index| property. (TemplateObject positions it in slot 0) */
|
||||
/* Step 24 (reordered)
|
||||
* Set the |index| property. (TemplateObject positions it in slot 0) */
|
||||
arr->setSlot(0, Int32Value(matches[0].start));
|
||||
|
||||
/* Set the |input| property. (TemplateObject positions it in slot 1) */
|
||||
/* Step 25 (reordered)
|
||||
* Set the |input| property. (TemplateObject positions it in slot 1) */
|
||||
arr->setSlot(1, StringValue(input));
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -89,21 +94,25 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
||||
MOZ_ASSERT(test == arr->getSlot(1));
|
||||
#endif
|
||||
|
||||
/* Step 29. */
|
||||
rval.setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES6 21.2.5.2.2 steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
|
||||
size_t searchIndex, MatchPairs* matches)
|
||||
size_t searchIndex, bool sticky, MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
RegExpRunStatus status = re.execute(cx, input, searchIndex, matches);
|
||||
RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, matches, endIndex);
|
||||
|
||||
/* Out of spec: Update RegExpStatics. */
|
||||
if (status == RegExpRunStatus_Success && res) {
|
||||
if (matches) {
|
||||
if (!res->updateFromMatchPairs(cx, input, *matches))
|
||||
return RegExpRunStatus_Error;
|
||||
} else {
|
||||
res->updateLazily(cx, input, &re, searchIndex);
|
||||
res->updateLazily(cx, input, &re, searchIndex, sticky);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
@@ -121,7 +130,8 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
|
||||
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, &matches);
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, reobj.sticky(),
|
||||
&matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
@@ -597,8 +607,8 @@ const JSFunctionSpec js::regexp_methods[] = {
|
||||
#endif
|
||||
JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
|
||||
JS_FN("compile", regexp_compile, 2,0),
|
||||
JS_INLINABLE_FN("exec", regexp_exec, 1,0, RegExpExec),
|
||||
JS_INLINABLE_FN("test", regexp_test, 1,0, RegExpTest),
|
||||
JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
|
||||
JS_SELF_HOSTED_FN("test", "RegExpTest" , 1,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@@ -767,23 +777,6 @@ js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
|
||||
return proto;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportLastIndexNonwritable(JSContext* cx)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_READ_ONLY, "\"lastIndex\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetLastIndex(JSContext* cx, Handle<RegExpObject*> reobj, double lastIndex)
|
||||
{
|
||||
if (!reobj->lookup(cx, cx->names().lastIndex)->writable())
|
||||
return ReportLastIndexNonwritable(cx);
|
||||
|
||||
reobj->setLastIndex(lastIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static bool
|
||||
IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
|
||||
@@ -807,10 +800,11 @@ IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32
|
||||
: IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
|
||||
}
|
||||
|
||||
/* ES6 final draft 21.2.5.2.2. */
|
||||
RegExpRunStatus
|
||||
js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
MatchPairs* matches, RegExpStaticsUpdate staticsUpdate)
|
||||
/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, bool sticky,
|
||||
MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
|
||||
{
|
||||
/*
|
||||
* WARNING: Despite the presence of spec step comment numbers, this
|
||||
@@ -838,56 +832,10 @@ js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
if (!input)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/* Step 3. */
|
||||
size_t length = input->length();
|
||||
/* Handled by caller */
|
||||
MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
|
||||
|
||||
/* Steps 4-5. */
|
||||
RootedValue lastIndex(cx, reobj->getLastIndex());
|
||||
int searchIndex;
|
||||
if (lastIndex.isInt32()) {
|
||||
/* Aggressively avoid doubles. */
|
||||
searchIndex = lastIndex.toInt32();
|
||||
} else {
|
||||
double d;
|
||||
if (!ToInteger(cx, lastIndex, &d))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/* Inlined steps 6-10, 15.a with doubles to detect failure case. */
|
||||
if (reobj->needUpdateLastIndex() && (d < 0 || d > length)) {
|
||||
/* Steps 15.a.i-ii. */
|
||||
if (!SetLastIndex(cx, reobj, 0))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/* Step 15.a.iii. */
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
}
|
||||
|
||||
searchIndex = int(d);
|
||||
}
|
||||
|
||||
/*
|
||||
* Steps 6-10.
|
||||
*
|
||||
* Also make sure that we have a MatchPairs for regexps which update their
|
||||
* last index, as we won't compute the last index otherwise.
|
||||
*/
|
||||
Maybe<ScopedMatchPairs> alternateMatches;
|
||||
if (!reobj->needUpdateLastIndex()) {
|
||||
searchIndex = 0;
|
||||
} else if (!matches) {
|
||||
alternateMatches.emplace(&cx->tempLifoAlloc());
|
||||
matches = &alternateMatches.ref();
|
||||
}
|
||||
|
||||
/* Step 15.a. */
|
||||
if (searchIndex < 0 || size_t(searchIndex) > length) {
|
||||
/* Steps 15.a.i-ii. */
|
||||
if (!SetLastIndex(cx, reobj, 0))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/* Step 15.a.iii. */
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
}
|
||||
/* Steps 4-10 performed by the caller. */
|
||||
|
||||
/* Steps 12-13. */
|
||||
if (reobj->unicode()) {
|
||||
@@ -898,7 +846,7 @@ js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
*
|
||||
* In the spec, pattern match is performed with decoded Unicode code
|
||||
* points, but our implementation performs it with UTF-16 encoded
|
||||
* string. In step 2, we should decrement searchIndex (index) if it
|
||||
* string. In step 2, we should decrement lastIndex (index) if it
|
||||
* points the trail surrogate that has corresponding lead surrogate.
|
||||
*
|
||||
* var r = /\uD83D\uDC38/ug;
|
||||
@@ -912,92 +860,84 @@ js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
* However, the spec will change to match our implementation's
|
||||
* behavior. See https://github.com/tc39/ecma262/issues/128.
|
||||
*/
|
||||
if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex))
|
||||
searchIndex--;
|
||||
if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
|
||||
lastIndex--;
|
||||
}
|
||||
|
||||
/* Step 14-29. */
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches);
|
||||
/* Steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, sticky, matches, endIndex);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
if (status == RegExpRunStatus_Success_NotFound) {
|
||||
/* Steps 15.a.i-ii. */
|
||||
if (!SetLastIndex(cx, reobj, 0))
|
||||
return RegExpRunStatus_Error;
|
||||
} else if (reobj->needUpdateLastIndex()) {
|
||||
/* Steps 18.a-b. */
|
||||
MOZ_ASSERT(matches && !matches->empty());
|
||||
if (!SetLastIndex(cx, reobj, (*matches)[0].limit))
|
||||
return RegExpRunStatus_Error;
|
||||
}
|
||||
/* Steps 15.a.i-ii, 18 are done by Self-hosted function. */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExp(JSContext* cx, const CallArgs& args, MatchPairs* matches)
|
||||
{
|
||||
/* Step 1 (a) was performed by CallNonGenericMethod. */
|
||||
RootedObject regexp(cx, &args.thisv().toObject());
|
||||
|
||||
/* Step 2. */
|
||||
RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
|
||||
if (!string)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
|
||||
}
|
||||
|
||||
/* ES5 15.10.6.2. */
|
||||
/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
|
||||
static bool
|
||||
regexp_exec_impl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
|
||||
RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, bool sticky,
|
||||
RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
|
||||
{
|
||||
/* Execute regular expression and gather matches. */
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, &matches, staticsUpdate);
|
||||
/* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, sticky,
|
||||
&matches, nullptr, staticsUpdate);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
/* Steps 15.a, 15.c. */
|
||||
if (status == RegExpRunStatus_Success_NotFound) {
|
||||
rval.setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Steps 19-29 */
|
||||
return CreateRegExpMatchResult(cx, string, matches, rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
regexp_exec_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
RootedObject regexp(cx, &args.thisv().toObject());
|
||||
RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
|
||||
if (!string)
|
||||
return false;
|
||||
|
||||
return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval());
|
||||
}
|
||||
|
||||
/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
|
||||
bool
|
||||
js::regexp_exec(JSContext* cx, unsigned argc, Value* vp)
|
||||
js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, IsRegExpObject, regexp_exec_impl, args);
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
MOZ_ASSERT(args[2].isNumber());
|
||||
MOZ_ASSERT(args[3].isBoolean());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
RootedValue lastIndexVal(cx, args[2]);
|
||||
bool sticky = ToBoolean(args[3]);
|
||||
|
||||
int32_t lastIndex = 0;
|
||||
if (!ToInt32(cx, lastIndexVal, &lastIndex))
|
||||
return false;
|
||||
|
||||
/* Steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
|
||||
return RegExpMatcherImpl(cx, regexp, string, lastIndex, sticky,
|
||||
UpdateRegExpStatics, args.rval());
|
||||
}
|
||||
|
||||
/* Separate interface for use by IonMonkey. */
|
||||
bool
|
||||
js::regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
MatchPairs* maybeMatches, MutableHandleValue output)
|
||||
js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky,
|
||||
MatchPairs* maybeMatches, MutableHandleValue output)
|
||||
{
|
||||
MOZ_ASSERT(lastIndex <= INT32_MAX);
|
||||
|
||||
// The MatchPairs will always be passed in, but RegExp execution was
|
||||
// successful only if the pairs have actually been filled in.
|
||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
|
||||
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
|
||||
return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output);
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex, sticky,
|
||||
UpdateRegExpStatics, output);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1011,32 +951,70 @@ js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
|
||||
return RegExpMatcherImpl(cx, regexp, string, 0, false,
|
||||
DontUpdateRegExpStatics, args.rval());
|
||||
}
|
||||
|
||||
/* ES5 15.10.6.3. */
|
||||
static bool
|
||||
regexp_test_impl(JSContext* cx, const CallArgs& args)
|
||||
/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
bool
|
||||
js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, args, nullptr);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
MOZ_ASSERT(args[2].isNumber());
|
||||
MOZ_ASSERT(args[3].isBoolean());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
RootedValue lastIndexVal(cx, args[2]);
|
||||
bool sticky = ToBoolean(args[3]);
|
||||
|
||||
int32_t lastIndex = 0;
|
||||
if (!ToInt32(cx, lastIndexVal, &lastIndex))
|
||||
return false;
|
||||
|
||||
/* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
|
||||
size_t endIndex = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string,
|
||||
lastIndex, sticky,
|
||||
nullptr, &endIndex, UpdateRegExpStatics);
|
||||
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
if (status == RegExpRunStatus_Success) {
|
||||
MOZ_ASSERT(endIndex <= INT32_MAX);
|
||||
args.rval().setInt32(int32_t(endIndex));
|
||||
} else {
|
||||
args.rval().setInt32(-1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Separate interface for use by IonMonkey. */
|
||||
bool
|
||||
js::regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result)
|
||||
js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky, int32_t* endIndex)
|
||||
{
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, nullptr, UpdateRegExpStatics);
|
||||
*result = (status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
}
|
||||
MOZ_ASSERT(lastIndex <= INT32_MAX);
|
||||
|
||||
bool
|
||||
js::regexp_test(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, IsRegExpObject, regexp_test_impl, args);
|
||||
size_t endIndexTmp = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, sticky,
|
||||
nullptr, &endIndexTmp, UpdateRegExpStatics);
|
||||
|
||||
if (status == RegExpRunStatus_Success) {
|
||||
MOZ_ASSERT(endIndexTmp <= INT32_MAX);
|
||||
*endIndex = int32_t(endIndexTmp);
|
||||
return true;
|
||||
}
|
||||
if (status == RegExpRunStatus_Success_NotFound) {
|
||||
*endIndex = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1050,7 +1028,9 @@ js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, nullptr, DontUpdateRegExpStatics);
|
||||
size_t ignored = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0, false,
|
||||
nullptr, &ignored, DontUpdateRegExpStatics);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
}
|
||||
|
||||
+9
-11
@@ -26,10 +26,6 @@ enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
|
||||
// Whether RegExp statics should be used to create a RegExp instance.
|
||||
enum RegExpStaticsUse { UseRegExpStatics, DontUseRegExpStatics };
|
||||
|
||||
RegExpRunStatus
|
||||
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
MatchPairs* matches, RegExpStaticsUpdate staticsUpdate);
|
||||
|
||||
/*
|
||||
* Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
|
||||
*
|
||||
@@ -48,17 +44,19 @@ CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& mat
|
||||
MutableHandleValue rval);
|
||||
|
||||
extern bool
|
||||
regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input, MatchPairs* maybeMatches,
|
||||
MutableHandleValue output);
|
||||
RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
regexp_exec(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
bool
|
||||
regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result);
|
||||
RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky,
|
||||
MatchPairs* maybeMatches, MutableHandleValue output);
|
||||
|
||||
extern bool
|
||||
regexp_test(JSContext* cx, unsigned argc, Value* vp);
|
||||
RegExpTester(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky, int32_t* endIndex);
|
||||
|
||||
/*
|
||||
* The following functions are for use by self-hosted code.
|
||||
|
||||
@@ -54,4 +54,121 @@ function RegExpToString()
|
||||
// Step 7.
|
||||
return '/' + pattern + '/' + flags;
|
||||
}
|
||||
|
||||
// ES6 21.2.5.2.
|
||||
// NOTE: This is not RegExpExec (21.2.5.2.1).
|
||||
function RegExp_prototype_Exec(string) {
|
||||
// Steps 1-3.
|
||||
var R = this;
|
||||
if (!IsObject(R) || !IsRegExpObject(R))
|
||||
return callFunction(CallRegExpMethodIfWrapped, R, string, "RegExp_prototype_Exec");
|
||||
|
||||
// Steps 4-5.
|
||||
var S = ToString(string);
|
||||
|
||||
// Step 6.
|
||||
return RegExpBuiltinExec(R, S, false);
|
||||
}
|
||||
|
||||
// ES6 21.2.5.2.1.
|
||||
function RegExpExec(R, S, forTest) {
|
||||
// Steps 1-2 (skipped).
|
||||
|
||||
// Steps 3-4.
|
||||
var exec = R.exec;
|
||||
|
||||
// Step 5.
|
||||
// If exec is the original RegExp.prototype.exec, use the same, faster,
|
||||
// path as for the case where exec isn't callable.
|
||||
if (exec === RegExp_prototype_Exec || !IsCallable(exec)) {
|
||||
// ES6 21.2.5.2 steps 1-2, 4-5 (skipped) for optimized case.
|
||||
|
||||
// Steps 6-7 or ES6 21.2.5.2 steps 3, 6 for optimized case.
|
||||
return RegExpBuiltinExec(R, S, forTest);
|
||||
}
|
||||
|
||||
// Steps 5.a-b.
|
||||
var result = callContentFunction(exec, R, S);
|
||||
|
||||
// Step 5.c.
|
||||
if (typeof result !== "object")
|
||||
ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL);
|
||||
|
||||
// Step 5.d.
|
||||
return forTest ? result !== null : result;
|
||||
}
|
||||
|
||||
// ES6 21.2.5.2.2.
|
||||
function RegExpBuiltinExec(R, S, forTest) {
|
||||
// ES6 21.2.5.2.1 step 6.
|
||||
// This check is here for RegExpTest. RegExp_prototype_Exec does same
|
||||
// thing already.
|
||||
if (!IsRegExpObject(R))
|
||||
return callFunction(CallRegExpMethodIfWrapped, R, R, S, forTest, "RegExpBuiltinExec");
|
||||
|
||||
// Step 1-2 (skipped).
|
||||
|
||||
// Steps 4-5.
|
||||
var lastIndex = ToLength(R.lastIndex);
|
||||
|
||||
// Steps 6-7.
|
||||
var global = !!R.global;
|
||||
|
||||
// Steps 8-9.
|
||||
var sticky = !!R.sticky;
|
||||
|
||||
// Step 10.
|
||||
if (!global && !sticky) {
|
||||
lastIndex = 0;
|
||||
} else {
|
||||
if (lastIndex < 0 || lastIndex > S.length) {
|
||||
// Steps 15.a.i-ii, 15.c.i.1-2.
|
||||
R.lastIndex = 0;
|
||||
return forTest ? false : null;
|
||||
}
|
||||
}
|
||||
|
||||
if (forTest) {
|
||||
// Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
|
||||
var endIndex = RegExpTester(R, S, lastIndex, sticky);
|
||||
if (endIndex == -1) {
|
||||
// Steps 15.a.i-ii, 15.c.i.1-2.
|
||||
R.lastIndex = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 18.
|
||||
if (global || sticky)
|
||||
R.lastIndex = endIndex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
|
||||
var result = RegExpMatcher(R, S, lastIndex, sticky);
|
||||
if (result === null) {
|
||||
// Steps 15.a.i-ii, 15.c.i.1-2.
|
||||
R.lastIndex = 0;
|
||||
} else {
|
||||
// Step 18.
|
||||
if (global || sticky)
|
||||
R.lastIndex = result.index + result[0].length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ES6 21.2.5.13.
|
||||
function RegExpTest(string) {
|
||||
// Steps 1-2.
|
||||
var R = this;
|
||||
if (!IsObject(R))
|
||||
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R);
|
||||
|
||||
// Steps 3-4.
|
||||
var S = ToString(string);
|
||||
|
||||
// Steps 5-6.
|
||||
return RegExpExec(R, S, true);
|
||||
}
|
||||
_SetCanonicalName(RegExpToString, "toString");
|
||||
|
||||
@@ -2733,7 +2733,7 @@ SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
|
||||
JS::CompartmentOptionsRef(cx->compartment()).setDisableLazyParsing(disable);
|
||||
cx->compartment()->behaviors().setDisableLazyParsing(disable);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@@ -2745,7 +2745,7 @@ SetDiscardSource(JSContext* cx, unsigned argc, Value* vp)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
|
||||
JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(discard);
|
||||
cx->compartment()->behaviors().setDiscardSource(discard);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@@ -3128,6 +3128,15 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
|
||||
return GetProperty(cx, env, env, id, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
EnableMatchFlagArgument(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
cx->runtime()->options().setMatchFlagArgument(true);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment' [, 'shrinking'])",
|
||||
@@ -3615,6 +3624,11 @@ gc::ZealModeHelpText),
|
||||
"getModuleEnvironmentValue(module, name)",
|
||||
" Get the value of a bound name in a module environment.\n"),
|
||||
|
||||
JS_FN_HELP("enableMatchFlagArgument", EnableMatchFlagArgument, 0, 0,
|
||||
"enableMatchFlagArgument()",
|
||||
" Enables the deprecated, non-standard flag argument of\n"
|
||||
" String.prototype.{match,search,replace}.\n"),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
||||
+62
-6
@@ -398,6 +398,10 @@ case "$target" in
|
||||
|
||||
# -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146)
|
||||
CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-"
|
||||
|
||||
# Disable C++11 thread-safe statics due to crashes on XP (bug 1204752)
|
||||
# See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
|
||||
CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-"
|
||||
else
|
||||
AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
|
||||
fi
|
||||
@@ -1349,6 +1353,8 @@ if test "$GNU_CXX"; then
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wsign-compare"
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits"
|
||||
|
||||
MOZ_CXX_SUPPORTS_WARNING(-W, class-varargs, ac_cxx_has_wclass_varargs)
|
||||
|
||||
# Treat some warnings as errors if --enable-warnings-as-errors:
|
||||
if test "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
|
||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=char-subscripts"
|
||||
@@ -1783,8 +1789,61 @@ ia64*-hpux*)
|
||||
CFLAGS="$CFLAGS -wd4244 -wd4267"
|
||||
CXXFLAGS="$CXXFLAGS -wd4244 -wd4267 -wd4251"
|
||||
if test -n "$CLANG_CL"; then
|
||||
# XXX We should combine some of these with our generic GCC-style
|
||||
# warning checks.
|
||||
#
|
||||
# Suppress the clang-cl warning for the inline 'new' and 'delete' in mozalloc
|
||||
CXXFLAGS="$CXXFLAGS -Wno-inline-new-delete"
|
||||
# We use offsetof on non-POD objects all the time.
|
||||
# We also suppress this warning on other platforms.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-offsetof"
|
||||
# MFBT thinks clang-cl supports constexpr, which it does, but
|
||||
# not everything in Windows C++ headers supports constexpr
|
||||
# as we might expect until MSVC 2015, so turn off this warning
|
||||
# for now.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-constexpr"
|
||||
# This warns for reasonable things like:
|
||||
# enum { X = 0xffffffffU };
|
||||
# which is annoying for IDL headers.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-microsoft-enum-value"
|
||||
# This warns for cases that would be reached by the Microsoft
|
||||
# #include rules, but also currently warns on cases that would
|
||||
# *also* be reached by standard C++ include rules. That
|
||||
# behavior doesn't seem useful, so we turn it off.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-microsoft-include"
|
||||
# We normally error out on unknown pragmas, but since clang-cl
|
||||
# claims to be MSVC, it would be difficult to add
|
||||
# #if defined(_MSC_VER) && !defined(__clang__) everywhere we
|
||||
# use such pragmas, so just ignore them.
|
||||
CFLAGS="$CFLAGS -Wno-unknown-pragmas"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unknown-pragmas"
|
||||
# clang-cl's Intrin.h marks things like _ReadWriteBarrier
|
||||
# as __attribute((__deprecated__)). This is nice to know,
|
||||
# but since we don't get the equivalent warning from MSVC,
|
||||
# let's just ignore it.
|
||||
CFLAGS="$CFLAGS -Wno-deprecated-declarations"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-deprecated-declarations"
|
||||
# We use a function like:
|
||||
# __declspec(noreturn) __inline void f() {}
|
||||
# which -Winvalid-noreturn complains about. Again, MSVC seems
|
||||
# OK with it, so let's silence the warning.
|
||||
CFLAGS="$CFLAGS -Wno-invalid-noreturn"
|
||||
CXXFLAGS="$CXXFLAGS -Wno-invalid-noreturn"
|
||||
# Missing |override| on virtual function declarations isn't
|
||||
# something that MSVC currently warns about.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-inconsistent-missing-override"
|
||||
# We use -DHAS_EXCEPTIONS=0, which removes the |throw()|
|
||||
# declaration on |operator delete(void*)|. However, clang-cl
|
||||
# must internally declare |operator delete(void*)| differently,
|
||||
# which causes this warning for virtually every file in the
|
||||
# tree. clang-cl doesn't support -fno-exceptions or equivalent,
|
||||
# so there doesn't seem to be any way to convince clang-cl to
|
||||
# declare |delete| differently. Therefore, suppress this
|
||||
# warning.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-implicit-exception-spec-mismatch"
|
||||
# At least one MSVC header and several headers in-tree have
|
||||
# unused typedefs, so turn this on.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"
|
||||
fi
|
||||
# make 'foo == bar;' error out
|
||||
CFLAGS="$CFLAGS -we4553"
|
||||
@@ -3672,24 +3731,21 @@ CXXFLAGS=`echo \
|
||||
|
||||
COMPILE_CFLAGS=`echo \
|
||||
$_DEFINES_CFLAGS \
|
||||
$_DEPEND_CFLAGS \
|
||||
$COMPILE_CFLAGS`
|
||||
|
||||
COMPILE_CXXFLAGS=`echo \
|
||||
$_DEFINES_CXXFLAGS \
|
||||
$_DEPEND_CFLAGS \
|
||||
$COMPILE_CXXFLAGS`
|
||||
|
||||
HOST_CFLAGS=`echo \
|
||||
$HOST_CFLAGS \
|
||||
$_DEPEND_CFLAGS`
|
||||
$HOST_CFLAGS`
|
||||
|
||||
HOST_CXXFLAGS=`echo \
|
||||
$HOST_CXXFLAGS \
|
||||
$_DEPEND_CFLAGS`
|
||||
$HOST_CXXFLAGS`
|
||||
|
||||
AC_SUBST(NSPR_CFLAGS)
|
||||
AC_SUBST_LIST(NSPR_LIBS)
|
||||
AC_SUBST(_DEPEND_CFLAGS)
|
||||
AC_SUBST(MOZ_NATIVE_NSPR)
|
||||
|
||||
if test -n "$MOZ_NUWA_PROCESS"; then
|
||||
|
||||
@@ -21,5 +21,6 @@ parallel/alloc-too-many-objs.js
|
||||
self-test/assertDeepEq.js
|
||||
v8-v5/check-earley-boyer.js
|
||||
v8-v5/check-raytrace.js
|
||||
v8-v5/check-regexp.js
|
||||
v8-v5/check-splay.js
|
||||
SIMD/nursery-overflow.js
|
||||
|
||||
@@ -199,7 +199,7 @@ BytecodeCompiler::maybeCompressSource()
|
||||
sourceCompressor = maybeSourceCompressor.ptr();
|
||||
}
|
||||
|
||||
if (!cx->compartment()->options().discardSource()) {
|
||||
if (!cx->compartment()->behaviors().discardSource()) {
|
||||
if (options.sourceIsLazy) {
|
||||
scriptSource->setSourceRetrievable();
|
||||
} else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceArgumentsNotIncluded,
|
||||
@@ -217,8 +217,8 @@ BytecodeCompiler::canLazilyParse()
|
||||
{
|
||||
return options.canLazilyParse &&
|
||||
!HasNonSyntacticStaticScopeChain(enclosingStaticScope) &&
|
||||
!cx->compartment()->options().disableLazyParsing() &&
|
||||
!cx->compartment()->options().discardSource() &&
|
||||
!cx->compartment()->behaviors().disableLazyParsing() &&
|
||||
!cx->compartment()->behaviors().discardSource() &&
|
||||
!options.sourceIsLazy &&
|
||||
!cx->lcovEnabled();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ void breakpoint() {
|
||||
GDBFragment* GDBFragment::allFragments = nullptr;
|
||||
|
||||
int
|
||||
main (int argc, const char** argv)
|
||||
main(int argc, const char** argv)
|
||||
{
|
||||
if (!JS_Init()) return 1;
|
||||
JSRuntime* runtime = checkPtr(JS_NewRuntime(1024 * 1024));
|
||||
@@ -74,7 +74,8 @@ main (int argc, const char** argv)
|
||||
|
||||
/* Create the global object. */
|
||||
JS::CompartmentOptions options;
|
||||
options.setVersion(JSVERSION_LATEST);
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
|
||||
RootedObject global(cx, checkPtr(JS_NewGlobalObject(cx, &global_class,
|
||||
nullptr, JS::FireOnNewGlobalHook, options)));
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
@@ -204,6 +204,10 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only)
|
||||
masm.assumeUnreachable("Not enough output registers for RegExp");
|
||||
masm.bind(&enoughRegisters);
|
||||
#endif
|
||||
} else {
|
||||
Register endIndexRegister = input_end_pointer;
|
||||
masm.loadPtr(Address(temp0, offsetof(InputOutputData, endIndex)), endIndexRegister);
|
||||
masm.storePtr(endIndexRegister, Address(masm.getStackPointer(), offsetof(FrameData, endIndex)));
|
||||
}
|
||||
|
||||
// Load string end pointer.
|
||||
@@ -354,6 +358,30 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only)
|
||||
|
||||
masm.jump(&load_char_start_regexp);
|
||||
} else {
|
||||
if (match_only) {
|
||||
// Store endIndex.
|
||||
|
||||
Register endIndexRegister = temp1;
|
||||
Register inputByteLength = backtrack_stack_pointer;
|
||||
|
||||
masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, endIndex)), endIndexRegister);
|
||||
|
||||
masm.loadPtr(inputOutputAddress, temp0);
|
||||
masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength);
|
||||
masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength);
|
||||
|
||||
masm.loadPtr(register_location(1), temp0);
|
||||
|
||||
// Convert to index from start of string, not end.
|
||||
masm.addPtr(inputByteLength, temp0);
|
||||
|
||||
// Convert byte index to character index.
|
||||
if (mode_ == CHAR16)
|
||||
masm.rshiftPtrArithmetic(Imm32(1), temp0);
|
||||
|
||||
masm.store32(temp0, Address(endIndexRegister, 0));
|
||||
}
|
||||
|
||||
masm.movePtr(ImmWord(RegExpRunStatus_Success), temp0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ struct InputOutputData
|
||||
|
||||
// Index into inputStart (in chars) at which to begin matching.
|
||||
size_t startIndex;
|
||||
size_t* endIndex;
|
||||
|
||||
MatchPairs* matches;
|
||||
|
||||
@@ -52,10 +53,11 @@ struct InputOutputData
|
||||
|
||||
template <typename CharT>
|
||||
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
|
||||
size_t startIndex, MatchPairs* matches)
|
||||
size_t startIndex, MatchPairs* matches, size_t* endIndex)
|
||||
: inputStart(inputStart),
|
||||
inputEnd(inputEnd),
|
||||
startIndex(startIndex),
|
||||
endIndex(endIndex),
|
||||
matches(matches),
|
||||
result(0)
|
||||
{}
|
||||
@@ -66,6 +68,7 @@ struct FrameData
|
||||
// Copy of the input/output data's data.
|
||||
char16_t* inputStart;
|
||||
size_t startIndex;
|
||||
size_t* endIndex;
|
||||
|
||||
// Pointer to the character before the input start.
|
||||
char16_t* inputStartMinusOne;
|
||||
|
||||
@@ -1888,11 +1888,11 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData*
|
||||
template <typename CharT>
|
||||
RegExpRunStatus
|
||||
irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
|
||||
size_t length, MatchPairs* matches)
|
||||
size_t length, MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
typedef void (*RegExpCodeSignature)(InputOutputData*);
|
||||
|
||||
InputOutputData data(chars, chars + length, start, matches);
|
||||
InputOutputData data(chars, chars + length, start, matches, endIndex);
|
||||
|
||||
RegExpCodeSignature function = reinterpret_cast<RegExpCodeSignature>(codeBlock->raw());
|
||||
|
||||
@@ -1906,11 +1906,11 @@ irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars
|
||||
|
||||
template RegExpRunStatus
|
||||
irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const Latin1Char* chars, size_t start,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
template RegExpRunStatus
|
||||
irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const char16_t* chars, size_t start,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Tree to graph conversion
|
||||
|
||||
@@ -96,12 +96,12 @@ CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
|
||||
template <typename CharT>
|
||||
RegExpRunStatus
|
||||
ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
template <typename CharT>
|
||||
RegExpRunStatus
|
||||
InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t start,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
#define FOR_EACH_NODE_TYPE(VISIT) \
|
||||
VISIT(End) \
|
||||
|
||||
@@ -117,7 +117,7 @@ Load16Aligned(const uint8_t* pc)
|
||||
template <typename CharT>
|
||||
RegExpRunStatus
|
||||
irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t current,
|
||||
size_t length, MatchPairs* matches)
|
||||
size_t length, MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
const uint8_t* pc = byteCode;
|
||||
|
||||
@@ -199,6 +199,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
||||
BYTECODE(SUCCEED)
|
||||
if (matches)
|
||||
memcpy(matches->pairsRaw(), registers.begin(), matches->length() * 2 * sizeof(int32_t));
|
||||
else if (endIndex)
|
||||
*endIndex = registers[1];
|
||||
return RegExpRunStatus_Success;
|
||||
BYTECODE(ADVANCE_CP)
|
||||
current += insn >> BYTECODE_SHIFT;
|
||||
@@ -492,8 +494,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
||||
|
||||
template RegExpRunStatus
|
||||
irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const Latin1Char* chars, size_t current,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
template RegExpRunStatus
|
||||
irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const char16_t* chars, size_t current,
|
||||
size_t length, MatchPairs* matches);
|
||||
size_t length, MatchPairs* matches, size_t* endIndex);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
Function.prototype.prototype = function() {}
|
||||
|
||||
var type = TypedObject.uint8.array(4).array(4);
|
||||
|
||||
@@ -12,6 +12,8 @@ function isPatternSyntaxError(pattern) {
|
||||
}
|
||||
}
|
||||
|
||||
enableMatchFlagArgument();
|
||||
|
||||
// Bug example.
|
||||
assertEq("1+2".replace("1+2", "$&+3", "g"), "1+2+3");
|
||||
assertEq("1112".replace("1+2", "", "g"), "1112");
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Bug 1207922 - lastIndex should be reset to 0 when match fails.
|
||||
|
||||
var pattern = /abc/;
|
||||
var string = 'aaaaaaaa';
|
||||
|
||||
function test() {
|
||||
pattern.lastIndex = 3;
|
||||
var result = pattern.exec(string);
|
||||
assertEq(result, null);
|
||||
assertEq(pattern.lastIndex, 0);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
test();
|
||||
}
|
||||
|
||||
function test2() {
|
||||
pattern.lastIndex = 3;
|
||||
var result = pattern.test(string);
|
||||
assertEq(result, false);
|
||||
assertEq(pattern.lastIndex, 0);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
test2();
|
||||
}
|
||||
@@ -3,3 +3,4 @@ if (!('oomTest' in this))
|
||||
|
||||
oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
|
||||
oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
|
||||
oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true));
|
||||
|
||||
@@ -83,6 +83,8 @@ function split_join_pattern(i) {
|
||||
// Check that, as opposed to String.replace, we are doing a global replacement
|
||||
// as String.split does.
|
||||
function split_join_multiple(i) {
|
||||
enableMatchFlagArgument();
|
||||
|
||||
var s1 = i + "-\n-" + i + "-\n-" + i;
|
||||
assertEq(s1.split("-\n-").join("-") , i + "-" + i + "-" + i);
|
||||
assertEq(s1.replace("-\n-", "-") , i + "-" + i + "-\n-" + i);
|
||||
|
||||
@@ -1035,7 +1035,7 @@ BacktrackingAllocator::tryMergeReusedRegister(VirtualRegister& def, VirtualRegis
|
||||
MOZ_ASSERT(!inputRange->hasUses());
|
||||
|
||||
JitSpew(JitSpew_RegAlloc, " splitting reused input at %u to try to help grouping",
|
||||
inputOf(def.ins()));
|
||||
inputOf(def.ins()).bits());
|
||||
|
||||
LiveBundle* firstBundle = inputRange->bundle();
|
||||
input.removeRange(inputRange);
|
||||
|
||||
@@ -1491,7 +1491,8 @@ static const VMFunction DeepCloneObjectLiteralInfo =
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_OBJECT()
|
||||
{
|
||||
if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
|
||||
JSCompartment* comp = cx->compartment();
|
||||
if (comp->creationOptions().cloneSingletons()) {
|
||||
RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
|
||||
if (!obj)
|
||||
return false;
|
||||
@@ -1510,7 +1511,7 @@ BaselineCompiler::emit_JSOP_OBJECT()
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
|
||||
comp->behaviors().setSingletonsAsValues();
|
||||
frame.push(ObjectValue(*script->getObject(pc)));
|
||||
return true;
|
||||
}
|
||||
|
||||
+274
-90
@@ -37,6 +37,7 @@
|
||||
#include "vm/MatchPairs.h"
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
#include "vm/Unicode.h"
|
||||
|
||||
#include "jsboolinlines.h"
|
||||
|
||||
@@ -1034,6 +1035,7 @@ RegExpPairCountAddress(MacroAssembler& masm, size_t inputOutputDataStartOffset)
|
||||
// otherwise jump to notFound or failure.
|
||||
static bool
|
||||
PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Register input,
|
||||
Register lastIndex, Register sticky,
|
||||
Register temp1, Register temp2, Register temp3,
|
||||
size_t inputOutputDataStartOffset,
|
||||
RegExpShared::CompilationMode mode,
|
||||
@@ -1050,6 +1052,8 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, matches));
|
||||
Address startIndexAddress(masm.getStackPointer(),
|
||||
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, startIndex));
|
||||
Address endIndexAddress(masm.getStackPointer(),
|
||||
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, endIndex));
|
||||
Address matchResultAddress(masm.getStackPointer(),
|
||||
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result));
|
||||
|
||||
@@ -1084,9 +1088,45 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp1);
|
||||
masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
|
||||
|
||||
// Don't handle RegExps which read and write to lastIndex.
|
||||
masm.branchTest32(Assembler::NonZero, Address(temp1, RegExpShared::offsetOfFlags()),
|
||||
Imm32(StickyFlag | GlobalFlag), failure);
|
||||
// ES6 21.2.2.2 step 2.
|
||||
// See RegExp.cpp ExecuteRegExp for more detail.
|
||||
{
|
||||
Label done;
|
||||
|
||||
masm.branchTest32(Assembler::Zero, Address(temp1, RegExpShared::offsetOfFlags()),
|
||||
Imm32(UnicodeFlag), &done);
|
||||
|
||||
// If input is latin1, there should not be surrogate pair.
|
||||
masm.branchLatin1String(input, &done);
|
||||
|
||||
// Check if |lastIndex > 0 && lastIndex < input->length()|.
|
||||
// lastIndex should already have no sign here.
|
||||
masm.branchTest32(Assembler::Zero, lastIndex, lastIndex, &done);
|
||||
masm.loadStringLength(input, temp2);
|
||||
masm.branch32(Assembler::AboveOrEqual, lastIndex, temp2, &done);
|
||||
|
||||
// Check if input[lastIndex] is trail surrogate.
|
||||
masm.loadStringChars(input, temp2);
|
||||
masm.computeEffectiveAddress(BaseIndex(temp2, lastIndex, TimesTwo), temp3);
|
||||
masm.load16ZeroExtend(Address(temp3, 0), temp3);
|
||||
|
||||
masm.branch32(Assembler::Below, temp3, Imm32(unicode::TrailSurrogateMin), &done);
|
||||
masm.branch32(Assembler::Above, temp3, Imm32(unicode::TrailSurrogateMax), &done);
|
||||
|
||||
// Check if input[lastIndex-1] is lead surrogate.
|
||||
masm.move32(lastIndex, temp3);
|
||||
masm.sub32(Imm32(1), temp3);
|
||||
masm.computeEffectiveAddress(BaseIndex(temp2, temp3, TimesTwo), temp3);
|
||||
masm.load16ZeroExtend(Address(temp3, 0), temp3);
|
||||
|
||||
masm.branch32(Assembler::Below, temp3, Imm32(unicode::LeadSurrogateMin), &done);
|
||||
masm.branch32(Assembler::Above, temp3, Imm32(unicode::LeadSurrogateMax), &done);
|
||||
|
||||
// Move lastIndex to lead surrogate.
|
||||
masm.subPtr(Imm32(2), lastIndex);
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
if (mode == RegExpShared::Normal) {
|
||||
// Don't handle RegExps with excessive parens.
|
||||
@@ -1105,19 +1145,43 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
masm.loadStringChars(input, temp2);
|
||||
masm.storePtr(temp2, inputStartAddress);
|
||||
masm.loadStringLength(input, temp3);
|
||||
Label isLatin1, done;
|
||||
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
|
||||
Label stickyCode, done;
|
||||
masm.branchTest32(Assembler::NonZero, sticky, sticky, &stickyCode);
|
||||
{
|
||||
masm.lshiftPtr(Imm32(1), temp3);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, false)), codePointer);
|
||||
Label isLatin1;
|
||||
masm.branchLatin1String(input, &isLatin1);
|
||||
{
|
||||
masm.lshiftPtr(Imm32(1), temp3);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyTwoByteJitCode(mode)),
|
||||
codePointer);
|
||||
}
|
||||
masm.jump(&done);
|
||||
{
|
||||
masm.bind(&isLatin1);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyLatin1JitCode(mode)),
|
||||
codePointer);
|
||||
}
|
||||
}
|
||||
masm.jump(&done);
|
||||
{
|
||||
masm.bind(&isLatin1);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, true)), codePointer);
|
||||
masm.bind(&stickyCode);
|
||||
Label isLatin1;
|
||||
masm.branchLatin1String(input, &isLatin1);
|
||||
{
|
||||
masm.lshiftPtr(Imm32(1), temp3);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyTwoByteJitCode(mode)),
|
||||
codePointer);
|
||||
}
|
||||
masm.jump(&done);
|
||||
{
|
||||
masm.bind(&isLatin1);
|
||||
masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyLatin1JitCode(mode)),
|
||||
codePointer);
|
||||
}
|
||||
}
|
||||
masm.bind(&done);
|
||||
|
||||
masm.addPtr(temp3, temp2);
|
||||
masm.storePtr(temp2, inputEndAddress);
|
||||
}
|
||||
@@ -1130,12 +1194,20 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
if (mode == RegExpShared::Normal) {
|
||||
masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
|
||||
masm.storePtr(temp2, matchesPointerAddress);
|
||||
} else {
|
||||
// Use InputOutputData.endIndex itself for output.
|
||||
masm.computeEffectiveAddress(endIndexAddress, temp2);
|
||||
masm.storePtr(temp2, endIndexAddress);
|
||||
}
|
||||
masm.storePtr(ImmWord(0), startIndexAddress);
|
||||
masm.storePtr(lastIndex, startIndexAddress);
|
||||
masm.store32(Imm32(0), matchResultAddress);
|
||||
|
||||
// Save any volatile inputs.
|
||||
LiveGeneralRegisterSet volatileRegs;
|
||||
if (sticky.volatile_())
|
||||
volatileRegs.add(sticky);
|
||||
if (lastIndex.volatile_())
|
||||
volatileRegs.add(lastIndex);
|
||||
if (input.volatile_())
|
||||
volatileRegs.add(input);
|
||||
if (regexp.volatile_())
|
||||
@@ -1161,6 +1233,8 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
Address pendingInputAddress(temp1, RegExpStatics::offsetOfPendingInput());
|
||||
Address matchesInputAddress(temp1, RegExpStatics::offsetOfMatchesInput());
|
||||
Address lazySourceAddress(temp1, RegExpStatics::offsetOfLazySource());
|
||||
Address lazyIndexAddress(temp1, RegExpStatics::offsetOfLazyIndex());
|
||||
Address lazyStickyAddress(temp1, RegExpStatics::offsetOfLazySticky());
|
||||
|
||||
masm.patchableCallPreBarrier(pendingInputAddress, MIRType_String);
|
||||
masm.patchableCallPreBarrier(matchesInputAddress, MIRType_String);
|
||||
@@ -1168,7 +1242,8 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
|
||||
masm.storePtr(input, pendingInputAddress);
|
||||
masm.storePtr(input, matchesInputAddress);
|
||||
masm.storePtr(ImmWord(0), Address(temp1, RegExpStatics::offsetOfLazyIndex()));
|
||||
masm.storePtr(sticky, lazyStickyAddress);
|
||||
masm.storePtr(lastIndex, Address(temp1, RegExpStatics::offsetOfLazyIndex()));
|
||||
masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
|
||||
|
||||
masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp2);
|
||||
@@ -1177,6 +1252,11 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
|
||||
masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
|
||||
masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
|
||||
|
||||
if (mode == RegExpShared::MatchOnly) {
|
||||
// endIndex is passed via temp3.
|
||||
masm.load32(endIndexAddress, temp3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1303,16 +1383,20 @@ CreateDependentString(MacroAssembler& masm, const JSAtomState& names,
|
||||
}
|
||||
|
||||
JitCode*
|
||||
JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
JitCompartment::generateRegExpMatcherStub(JSContext* cx)
|
||||
{
|
||||
Register regexp = CallTempReg0;
|
||||
Register input = CallTempReg1;
|
||||
Register regexp = RegExpMatcherRegExpReg;
|
||||
Register input = RegExpMatcherStringReg;
|
||||
Register lastIndex = RegExpMatcherLastIndexReg;
|
||||
Register sticky = RegExpMatcherStickyReg;
|
||||
ValueOperand result = JSReturnOperand;
|
||||
|
||||
// We are free to clobber all registers, as LRegExpExec is a call instruction.
|
||||
// We are free to clobber all registers, as LRegExpMatcher is a call instruction.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(input);
|
||||
regs.take(regexp);
|
||||
regs.take(lastIndex);
|
||||
regs.take(sticky);
|
||||
|
||||
// temp5 is used in single byte instructions when creating dependent
|
||||
// strings, and has restrictions on which register it can be on some
|
||||
@@ -1328,8 +1412,14 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
|
||||
Register temp1 = regs.takeAny();
|
||||
Register temp2 = regs.takeAny();
|
||||
Register temp3 = regs.takeAny();
|
||||
Register temp4 = regs.takeAny();
|
||||
|
||||
Register maybeTemp3 = InvalidReg;
|
||||
Register maybeTemp4 = InvalidReg;
|
||||
if (!regs.empty()) {
|
||||
// There are not enough registers on x86.
|
||||
maybeTemp3 = regs.takeAny();
|
||||
maybeTemp4 = regs.takeAny();
|
||||
}
|
||||
|
||||
ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
|
||||
if (!templateObject)
|
||||
@@ -1346,9 +1436,9 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
size_t inputOutputDataStartOffset = sizeof(void*);
|
||||
|
||||
Label notFound, oolEntry;
|
||||
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3,
|
||||
inputOutputDataStartOffset, RegExpShared::Normal,
|
||||
¬Found, &oolEntry))
|
||||
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
|
||||
temp1, temp2, temp5, inputOutputDataStartOffset,
|
||||
RegExpShared::Normal, ¬Found, &oolEntry))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1357,6 +1447,29 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
Register object = temp1;
|
||||
masm.createGCObject(object, temp2, templateObject, gc::DefaultHeap, &oolEntry);
|
||||
|
||||
size_t elementsOffset = NativeObject::offsetOfFixedElements();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert the initial value of initializedLength and length to make sure
|
||||
// restoration on failure case works.
|
||||
{
|
||||
Label initLengthOK, lengthOK;
|
||||
masm.branch32(Assembler::Equal,
|
||||
Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()),
|
||||
Imm32(templateObject->getDenseInitializedLength()),
|
||||
&initLengthOK);
|
||||
masm.assumeUnreachable("Initial value of the match object's initializedLength does not match to restoration.");
|
||||
masm.bind(&initLengthOK);
|
||||
|
||||
masm.branch32(Assembler::Equal,
|
||||
Address(object, elementsOffset + ObjectElements::offsetOfLength()),
|
||||
Imm32(templateObject->length()),
|
||||
&lengthOK);
|
||||
masm.assumeUnreachable("Initial value of The match object's length does not match to restoration.");
|
||||
masm.bind(&lengthOK);
|
||||
}
|
||||
#endif
|
||||
|
||||
Register matchIndex = temp2;
|
||||
masm.move32(Imm32(0), matchIndex);
|
||||
|
||||
@@ -1364,7 +1477,6 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
|
||||
Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
|
||||
|
||||
size_t elementsOffset = NativeObject::offsetOfFixedElements();
|
||||
BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset);
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(MatchPair) == 8);
|
||||
@@ -1377,8 +1489,20 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
// depending on whether the input is latin1.
|
||||
{
|
||||
Label isLatin1, done;
|
||||
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
masm.branchLatin1String(input, &isLatin1);
|
||||
|
||||
Label* failure = &oolEntry;
|
||||
Register temp3 = (maybeTemp3 == InvalidReg) ? sticky : maybeTemp3;
|
||||
Register temp4 = (maybeTemp3 == InvalidReg) ? lastIndex : maybeTemp4;
|
||||
|
||||
Label failureRestore;
|
||||
if (maybeTemp3 == InvalidReg) {
|
||||
failure = &failureRestore;
|
||||
|
||||
// Save sticky and lastIndex values to temporary space.
|
||||
masm.store32(sticky, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
|
||||
masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
|
||||
}
|
||||
|
||||
for (int isLatin = 0; isLatin <= 1; isLatin++) {
|
||||
if (isLatin)
|
||||
@@ -1391,7 +1515,7 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
|
||||
|
||||
CreateDependentString(masm, cx->names(), isLatin, temp3, input, temp4, temp5,
|
||||
stringIndexAddress, stringLimitAddress, &oolEntry);
|
||||
stringIndexAddress, stringLimitAddress, failure);
|
||||
masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
|
||||
|
||||
masm.jump(&storeDone);
|
||||
@@ -1405,6 +1529,26 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
masm.jump(&matchLoop);
|
||||
}
|
||||
|
||||
if (maybeTemp3 == InvalidReg) {
|
||||
// Restore sticky and lastIndex values from temporary space, both
|
||||
// for success and failure cases.
|
||||
|
||||
masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
|
||||
masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&failureRestore);
|
||||
masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
|
||||
masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
|
||||
|
||||
// Restore the match object for failure case.
|
||||
masm.store32(Imm32(templateObject->getDenseInitializedLength()),
|
||||
Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
|
||||
masm.store32(Imm32(templateObject->length()),
|
||||
Address(object, elementsOffset + ObjectElements::offsetOfLength()));
|
||||
masm.jump(&oolEntry);
|
||||
}
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
@@ -1418,8 +1562,9 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
|
||||
MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
|
||||
|
||||
masm.load32(pairsVectorAddress, temp3);
|
||||
masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
|
||||
// sticky is now free, because no more ool entry happens.
|
||||
masm.load32(pairsVectorAddress, sticky);
|
||||
masm.storeValue(JSVAL_TYPE_INT32, sticky, Address(temp2, 0));
|
||||
masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
|
||||
|
||||
// All done!
|
||||
@@ -1436,13 +1581,13 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("RegExpExecStub");
|
||||
AutoFlushICache afc("RegExpMatcherStub");
|
||||
JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
|
||||
if (!code)
|
||||
return nullptr;
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "RegExpExecStub");
|
||||
writePerfSpewerJitCodeProfile(code, "RegExpMatcherStub");
|
||||
#endif
|
||||
|
||||
if (cx->zone()->needsIncrementalBarrier())
|
||||
@@ -1451,36 +1596,41 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
|
||||
return code;
|
||||
}
|
||||
|
||||
class OutOfLineRegExpExec : public OutOfLineCodeBase<CodeGenerator>
|
||||
class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LRegExpExec* lir_;
|
||||
LRegExpMatcher* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRegExpExec(LRegExpExec* lir)
|
||||
explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineRegExpExec(this);
|
||||
codegen->visitOutOfLineRegExpMatcher(this);
|
||||
}
|
||||
|
||||
LRegExpExec* lir() const {
|
||||
LRegExpMatcher* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
typedef bool (*RegExpExecRawFn)(JSContext* cx, HandleObject regexp,
|
||||
HandleString input, MatchPairs* pairs, MutableHandleValue output);
|
||||
static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
|
||||
typedef bool (*RegExpMatcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky,
|
||||
MatchPairs* pairs, MutableHandleValue output);
|
||||
static const VMFunction RegExpMatcherRawInfo = FunctionInfo<RegExpMatcherRawFn>(RegExpMatcherRaw);
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool)
|
||||
CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
|
||||
{
|
||||
LRegExpExec* lir = ool->lir();
|
||||
LRegExpMatcher* lir = ool->lir();
|
||||
Register sticky = ToRegister(lir->sticky());
|
||||
Register lastIndex = ToRegister(lir->lastIndex());
|
||||
Register input = ToRegister(lir->string());
|
||||
Register regexp = ToRegister(lir->regexp());
|
||||
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(sticky);
|
||||
regs.take(lastIndex);
|
||||
regs.take(input);
|
||||
regs.take(regexp);
|
||||
Register temp = regs.takeAny();
|
||||
@@ -1489,64 +1639,88 @@ CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool)
|
||||
sizeof(irregexp::InputOutputData)), temp);
|
||||
|
||||
pushArg(temp);
|
||||
pushArg(sticky);
|
||||
pushArg(lastIndex);
|
||||
pushArg(input);
|
||||
pushArg(regexp);
|
||||
|
||||
callVM(RegExpExecRawInfo, lir);
|
||||
callVM(RegExpMatcherRawInfo, lir);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitRegExpExec(LRegExpExec* lir)
|
||||
CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir)
|
||||
{
|
||||
MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg0);
|
||||
MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg1);
|
||||
MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg);
|
||||
MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg);
|
||||
MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg);
|
||||
MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpMatcherStickyReg);
|
||||
MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
|
||||
|
||||
#if defined(JS_NUNBOX32)
|
||||
MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Type);
|
||||
MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Data);
|
||||
MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Type);
|
||||
MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Data);
|
||||
MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Type);
|
||||
MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Data);
|
||||
MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Type);
|
||||
MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Data);
|
||||
#elif defined(JS_PUNBOX64)
|
||||
MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg);
|
||||
MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg);
|
||||
MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg);
|
||||
MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg);
|
||||
#endif
|
||||
|
||||
masm.reserveStack(RegExpReservedStack);
|
||||
|
||||
OutOfLineRegExpExec* ool = new(alloc()) OutOfLineRegExpExec(lir);
|
||||
OutOfLineRegExpMatcher* ool = new(alloc()) OutOfLineRegExpMatcher(lir);
|
||||
addOutOfLineCode(ool, lir->mir());
|
||||
|
||||
JitCode* regExpExecStub = gen->compartment->jitCompartment()->regExpExecStubNoBarrier();
|
||||
masm.call(regExpExecStub);
|
||||
JitCode* regExpMatcherStub = gen->compartment->jitCompartment()->regExpMatcherStubNoBarrier();
|
||||
masm.call(regExpMatcherStub);
|
||||
masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
|
||||
masm.freeStack(RegExpReservedStack);
|
||||
}
|
||||
|
||||
// The value returned by the RegExp test stub if inline execution failed.
|
||||
static const int32_t RegExpTestFailedValue = 2;
|
||||
static const int32_t RegExpTesterResultNotFound = -1;
|
||||
static const int32_t RegExpTesterResultFailed = -2;
|
||||
|
||||
JitCode*
|
||||
JitCompartment::generateRegExpTestStub(JSContext* cx)
|
||||
JitCompartment::generateRegExpTesterStub(JSContext* cx)
|
||||
{
|
||||
Register regexp = CallTempReg2;
|
||||
Register input = CallTempReg3;
|
||||
Register regexp = RegExpTesterRegExpReg;
|
||||
Register input = RegExpTesterStringReg;
|
||||
Register lastIndex = RegExpTesterLastIndexReg;
|
||||
Register sticky = RegExpTesterStickyReg;
|
||||
Register result = ReturnReg;
|
||||
|
||||
MOZ_ASSERT(regexp != result && input != result);
|
||||
|
||||
// We are free to clobber all registers, as LRegExpTest is a call instruction.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(input);
|
||||
regs.take(regexp);
|
||||
Register temp1 = regs.takeAny();
|
||||
Register temp2 = regs.takeAny();
|
||||
Register temp3 = regs.takeAny();
|
||||
|
||||
MacroAssembler masm(cx);
|
||||
|
||||
#ifdef JS_USE_LINK_REGISTER
|
||||
masm.pushReturnAddress();
|
||||
#endif
|
||||
|
||||
// We are free to clobber all registers, as LRegExpTester is a call instruction.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(input);
|
||||
regs.take(regexp);
|
||||
regs.take(lastIndex);
|
||||
regs.take(sticky);
|
||||
|
||||
Register temp1 = regs.takeAny();
|
||||
Register temp2 = regs.takeAny();
|
||||
Register temp3 = regs.takeAny();
|
||||
|
||||
masm.reserveStack(sizeof(irregexp::InputOutputData));
|
||||
|
||||
Label notFound, oolEntry;
|
||||
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3, 0,
|
||||
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
|
||||
temp1, temp2, temp3, 0,
|
||||
RegExpShared::MatchOnly, ¬Found, &oolEntry))
|
||||
{
|
||||
return nullptr;
|
||||
@@ -1554,28 +1728,29 @@ JitCompartment::generateRegExpTestStub(JSContext* cx)
|
||||
|
||||
Label done;
|
||||
|
||||
masm.move32(Imm32(1), result);
|
||||
// temp3 contains endIndex.
|
||||
masm.move32(temp3, result);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(¬Found);
|
||||
masm.move32(Imm32(0), result);
|
||||
masm.move32(Imm32(RegExpTesterResultNotFound), result);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&oolEntry);
|
||||
masm.move32(Imm32(RegExpTestFailedValue), result);
|
||||
masm.move32(Imm32(RegExpTesterResultFailed), result);
|
||||
|
||||
masm.bind(&done);
|
||||
masm.freeStack(sizeof(irregexp::InputOutputData));
|
||||
masm.ret();
|
||||
|
||||
Linker linker(masm);
|
||||
AutoFlushICache afc("RegExpTestStub");
|
||||
AutoFlushICache afc("RegExpTesterStub");
|
||||
JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
|
||||
if (!code)
|
||||
return nullptr;
|
||||
|
||||
#ifdef JS_ION_PERF
|
||||
writePerfSpewerJitCodeProfile(code, "RegExpTestStub");
|
||||
writePerfSpewerJitCodeProfile(code, "RegExpTesterStub");
|
||||
#endif
|
||||
|
||||
if (cx->zone()->needsIncrementalBarrier())
|
||||
@@ -1584,57 +1759,68 @@ JitCompartment::generateRegExpTestStub(JSContext* cx)
|
||||
return code;
|
||||
}
|
||||
|
||||
class OutOfLineRegExpTest : public OutOfLineCodeBase<CodeGenerator>
|
||||
class OutOfLineRegExpTester : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LRegExpTest* lir_;
|
||||
LRegExpTester* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRegExpTest(LRegExpTest* lir)
|
||||
explicit OutOfLineRegExpTester(LRegExpTester* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineRegExpTest(this);
|
||||
codegen->visitOutOfLineRegExpTester(this);
|
||||
}
|
||||
|
||||
LRegExpTest* lir() const {
|
||||
LRegExpTester* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
typedef bool (*RegExpTestRawFn)(JSContext* cx, HandleObject regexp,
|
||||
HandleString input, bool* result);
|
||||
static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
|
||||
typedef bool (*RegExpTesterRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
|
||||
int32_t lastIndex, bool sticky, int32_t* result);
|
||||
static const VMFunction RegExpTesterRawInfo = FunctionInfo<RegExpTesterRawFn>(RegExpTesterRaw);
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool)
|
||||
CodeGenerator::visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool)
|
||||
{
|
||||
LRegExpTest* lir = ool->lir();
|
||||
LRegExpTester* lir = ool->lir();
|
||||
Register sticky = ToRegister(lir->sticky());
|
||||
Register lastIndex = ToRegister(lir->lastIndex());
|
||||
Register input = ToRegister(lir->string());
|
||||
Register regexp = ToRegister(lir->regexp());
|
||||
|
||||
pushArg(sticky);
|
||||
pushArg(lastIndex);
|
||||
pushArg(input);
|
||||
pushArg(regexp);
|
||||
|
||||
callVM(RegExpTestRawInfo, lir);
|
||||
callVM(RegExpTesterRawInfo, lir);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitRegExpTest(LRegExpTest* lir)
|
||||
CodeGenerator::visitRegExpTester(LRegExpTester* lir)
|
||||
{
|
||||
MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg2);
|
||||
MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg3);
|
||||
MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpTesterRegExpReg);
|
||||
MOZ_ASSERT(ToRegister(lir->string()) == RegExpTesterStringReg);
|
||||
MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpTesterLastIndexReg);
|
||||
MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpTesterStickyReg);
|
||||
MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
|
||||
|
||||
OutOfLineRegExpTest* ool = new(alloc()) OutOfLineRegExpTest(lir);
|
||||
MOZ_ASSERT(RegExpTesterRegExpReg != ReturnReg);
|
||||
MOZ_ASSERT(RegExpTesterStringReg != ReturnReg);
|
||||
MOZ_ASSERT(RegExpTesterLastIndexReg != ReturnReg);
|
||||
MOZ_ASSERT(RegExpTesterStickyReg != ReturnReg);
|
||||
|
||||
OutOfLineRegExpTester* ool = new(alloc()) OutOfLineRegExpTester(lir);
|
||||
addOutOfLineCode(ool, lir->mir());
|
||||
|
||||
JitCode* regExpTestStub = gen->compartment->jitCompartment()->regExpTestStubNoBarrier();
|
||||
masm.call(regExpTestStub);
|
||||
JitCode* regExpTesterStub = gen->compartment->jitCompartment()->regExpTesterStubNoBarrier();
|
||||
masm.call(regExpTesterStub);
|
||||
|
||||
masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTestFailedValue), ool->entry());
|
||||
masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTesterResultFailed), ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
@@ -6040,8 +6226,7 @@ CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destC
|
||||
|
||||
Label isLatin1, done;
|
||||
masm.loadStringLength(input, temp1);
|
||||
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
masm.branchLatin1String(input, &isLatin1);
|
||||
{
|
||||
masm.loadStringChars(input, input);
|
||||
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char16_t), sizeof(char16_t));
|
||||
@@ -6182,8 +6367,7 @@ CodeGenerator::visitSubstr(LSubstr* lir)
|
||||
Address stringStorage(string, JSInlineString::offsetOfInlineStorage());
|
||||
Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
|
||||
|
||||
masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
|
||||
&isInlinedLatin1);
|
||||
masm.branchLatin1String(string, &isInlinedLatin1);
|
||||
{
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
|
||||
Address(output, JSString::offsetOfFlags()));
|
||||
@@ -6224,7 +6408,7 @@ CodeGenerator::visitSubstr(LSubstr* lir)
|
||||
masm.store32(length, Address(output, JSString::offsetOfLength()));
|
||||
masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
|
||||
|
||||
masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
masm.branchLatin1String(string, &isLatin1);
|
||||
{
|
||||
masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
|
||||
masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
|
||||
|
||||
@@ -44,8 +44,8 @@ class OutOfLineTypeOfV;
|
||||
class OutOfLineUpdateCache;
|
||||
class OutOfLineCallPostWriteBarrier;
|
||||
class OutOfLineIsCallable;
|
||||
class OutOfLineRegExpExec;
|
||||
class OutOfLineRegExpTest;
|
||||
class OutOfLineRegExpMatcher;
|
||||
class OutOfLineRegExpTester;
|
||||
class OutOfLineLambdaArrow;
|
||||
|
||||
class CodeGenerator : public CodeGeneratorSpecific
|
||||
@@ -106,10 +106,10 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
|
||||
void visitInteger(LInteger* lir);
|
||||
void visitRegExp(LRegExp* lir);
|
||||
void visitRegExpExec(LRegExpExec* lir);
|
||||
void visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool);
|
||||
void visitRegExpTest(LRegExpTest* lir);
|
||||
void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool);
|
||||
void visitRegExpMatcher(LRegExpMatcher* lir);
|
||||
void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
|
||||
void visitRegExpTester(LRegExpTester* lir);
|
||||
void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
|
||||
void visitRegExpReplace(LRegExpReplace* lir);
|
||||
void visitStringReplace(LStringReplace* lir);
|
||||
void emitSharedStub(ICStub::Kind kind, LInstruction* lir);
|
||||
|
||||
@@ -284,7 +284,7 @@ CompileCompartment::hasObjectMetadataCallback()
|
||||
void
|
||||
CompileCompartment::setSingletonsAsValues()
|
||||
{
|
||||
return JS::CompartmentOptionsRef(compartment()).setSingletonsAsValues();
|
||||
compartment()->behaviors().setSingletonsAsValues();
|
||||
}
|
||||
|
||||
JitCompileOptions::JitCompileOptions()
|
||||
@@ -296,8 +296,7 @@ JitCompileOptions::JitCompileOptions()
|
||||
|
||||
JitCompileOptions::JitCompileOptions(JSContext* cx)
|
||||
{
|
||||
JS::CompartmentOptions& options = cx->compartment()->options();
|
||||
cloneSingletons_ = options.cloneSingletons();
|
||||
cloneSingletons_ = cx->compartment()->creationOptions().cloneSingletons();
|
||||
spsSlowAssertionsEnabled_ = cx->runtime()->spsProfiler.enabled() &&
|
||||
cx->runtime()->spsProfiler.slowAssertionsEnabled();
|
||||
offThreadCompilationAvailable_ = OffThreadCompilationAvailable(cx);
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
_(MathTrunc) \
|
||||
_(MathCbrt) \
|
||||
\
|
||||
_(RegExpExec) \
|
||||
_(RegExpTest) \
|
||||
_(RegExpMatcher) \
|
||||
_(RegExpTester) \
|
||||
_(IsRegExpObject) \
|
||||
\
|
||||
_(String) \
|
||||
_(StringSplit) \
|
||||
|
||||
+10
-10
@@ -401,8 +401,8 @@ JitCompartment::JitCompartment()
|
||||
baselineGetPropReturnAddr_(nullptr),
|
||||
baselineSetPropReturnAddr_(nullptr),
|
||||
stringConcatStub_(nullptr),
|
||||
regExpExecStub_(nullptr),
|
||||
regExpTestStub_(nullptr)
|
||||
regExpMatcherStub_(nullptr),
|
||||
regExpTesterStub_(nullptr)
|
||||
{
|
||||
baselineCallReturnAddrs_[0] = baselineCallReturnAddrs_[1] = nullptr;
|
||||
}
|
||||
@@ -747,11 +747,11 @@ JitCompartment::sweep(FreeOp* fop, JSCompartment* compartment)
|
||||
if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
|
||||
stringConcatStub_ = nullptr;
|
||||
|
||||
if (regExpExecStub_ && !IsMarkedUnbarriered(®ExpExecStub_))
|
||||
regExpExecStub_ = nullptr;
|
||||
if (regExpMatcherStub_ && !IsMarkedUnbarriered(®ExpMatcherStub_))
|
||||
regExpMatcherStub_ = nullptr;
|
||||
|
||||
if (regExpTestStub_ && !IsMarkedUnbarriered(®ExpTestStub_))
|
||||
regExpTestStub_ = nullptr;
|
||||
if (regExpTesterStub_ && !IsMarkedUnbarriered(®ExpTesterStub_))
|
||||
regExpTesterStub_ = nullptr;
|
||||
|
||||
for (size_t i = 0; i <= SimdTypeDescr::LAST_TYPE; i++) {
|
||||
ReadBarrieredObject& obj = simdTemplateObjects_[i];
|
||||
@@ -764,10 +764,10 @@ void
|
||||
JitCompartment::toggleBarriers(bool enabled)
|
||||
{
|
||||
// Toggle barriers in compartment wide stubs that have patchable pre barriers.
|
||||
if (regExpExecStub_)
|
||||
regExpExecStub_->togglePreBarriers(enabled, Reprotect);
|
||||
if (regExpTestStub_)
|
||||
regExpTestStub_->togglePreBarriers(enabled, Reprotect);
|
||||
if (regExpMatcherStub_)
|
||||
regExpMatcherStub_->togglePreBarriers(enabled, Reprotect);
|
||||
if (regExpTesterStub_)
|
||||
regExpTesterStub_->togglePreBarriers(enabled, Reprotect);
|
||||
|
||||
// Toggle barriers in baseline IC stubs.
|
||||
for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
|
||||
|
||||
@@ -1851,10 +1851,6 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
|
||||
MDefinition* use = i->consumer()->toDefinition();
|
||||
if (use->isRegExpReplace())
|
||||
continue;
|
||||
if (use->isRegExpExec())
|
||||
continue;
|
||||
if (use->isRegExpTest())
|
||||
continue;
|
||||
|
||||
hoistable = false;
|
||||
break;
|
||||
|
||||
@@ -5088,6 +5088,8 @@ IonBuilder::inlineScriptedCall(CallInfo& callInfo, JSFunction* target)
|
||||
abortReason_ = AbortReason_Inlining;
|
||||
} else if (inlineBuilder.abortReason_ == AbortReason_Inlining) {
|
||||
abortReason_ = AbortReason_Inlining;
|
||||
} else if (inlineBuilder.abortReason_ == AbortReason_Alloc) {
|
||||
abortReason_ = AbortReason_Alloc;
|
||||
} else if (inlineBuilder.abortReason_ == AbortReason_PreliminaryObjects) {
|
||||
const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
|
||||
MOZ_ASSERT(!groups.empty());
|
||||
|
||||
@@ -814,9 +814,10 @@ class IonBuilder
|
||||
InliningStatus inlineStrCharAt(CallInfo& callInfo);
|
||||
InliningStatus inlineStrReplace(CallInfo& callInfo);
|
||||
|
||||
// RegExp natives.
|
||||
InliningStatus inlineRegExpExec(CallInfo& callInfo);
|
||||
InliningStatus inlineRegExpTest(CallInfo& callInfo);
|
||||
// RegExp intrinsics.
|
||||
InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
|
||||
InliningStatus inlineRegExpTester(CallInfo& callInfo);
|
||||
InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
|
||||
|
||||
// Object natives and intrinsics.
|
||||
InliningStatus inlineObjectCreate(CallInfo& callInfo);
|
||||
|
||||
+16
-16
@@ -402,14 +402,14 @@ class JitCompartment
|
||||
// which may occur off thread and whose barriers are captured during
|
||||
// CodeGenerator::link.
|
||||
JitCode* stringConcatStub_;
|
||||
JitCode* regExpExecStub_;
|
||||
JitCode* regExpTestStub_;
|
||||
JitCode* regExpMatcherStub_;
|
||||
JitCode* regExpTesterStub_;
|
||||
|
||||
mozilla::Array<ReadBarrieredObject, SimdTypeDescr::LAST_TYPE + 1> simdTemplateObjects_;
|
||||
|
||||
JitCode* generateStringConcatStub(JSContext* cx);
|
||||
JitCode* generateRegExpExecStub(JSContext* cx);
|
||||
JitCode* generateRegExpTestStub(JSContext* cx);
|
||||
JitCode* generateRegExpMatcherStub(JSContext* cx);
|
||||
JitCode* generateRegExpTesterStub(JSContext* cx);
|
||||
|
||||
public:
|
||||
JSObject* getSimdTemplateObjectFor(JSContext* cx, Handle<SimdTypeDescr*> descr) {
|
||||
@@ -493,26 +493,26 @@ class JitCompartment
|
||||
return stringConcatStub_;
|
||||
}
|
||||
|
||||
JitCode* regExpExecStubNoBarrier() const {
|
||||
return regExpExecStub_;
|
||||
JitCode* regExpMatcherStubNoBarrier() const {
|
||||
return regExpMatcherStub_;
|
||||
}
|
||||
|
||||
bool ensureRegExpExecStubExists(JSContext* cx) {
|
||||
if (regExpExecStub_)
|
||||
bool ensureRegExpMatcherStubExists(JSContext* cx) {
|
||||
if (regExpMatcherStub_)
|
||||
return true;
|
||||
regExpExecStub_ = generateRegExpExecStub(cx);
|
||||
return regExpExecStub_ != nullptr;
|
||||
regExpMatcherStub_ = generateRegExpMatcherStub(cx);
|
||||
return regExpMatcherStub_ != nullptr;
|
||||
}
|
||||
|
||||
JitCode* regExpTestStubNoBarrier() const {
|
||||
return regExpTestStub_;
|
||||
JitCode* regExpTesterStubNoBarrier() const {
|
||||
return regExpTesterStub_;
|
||||
}
|
||||
|
||||
bool ensureRegExpTestStubExists(JSContext* cx) {
|
||||
if (regExpTestStub_)
|
||||
bool ensureRegExpTesterStubExists(JSContext* cx) {
|
||||
if (regExpTesterStub_)
|
||||
return true;
|
||||
regExpTestStub_ = generateRegExpTestStub(cx);
|
||||
return regExpTestStub_ != nullptr;
|
||||
regExpTesterStub_ = generateRegExpTesterStub(cx);
|
||||
return regExpTesterStub_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+16
-14
@@ -2083,12 +2083,6 @@ MustCloneRegExpForCall(MCall* call, uint32_t useIndex)
|
||||
if (!target || !target->isNative())
|
||||
return true;
|
||||
|
||||
if (useIndex == MCall::IndexOfThis() &&
|
||||
(target->native() == regexp_exec || target->native() == regexp_test))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (useIndex == MCall::IndexOfArgument(0) &&
|
||||
(target->native() == str_split ||
|
||||
target->native() == str_replace ||
|
||||
@@ -2117,8 +2111,8 @@ MustCloneRegExp(MRegExp* regexp)
|
||||
return true;
|
||||
|
||||
MDefinition* def = node->toDefinition();
|
||||
if (def->isRegExpTest()) {
|
||||
MRegExpTest* test = def->toRegExpTest();
|
||||
if (def->isRegExpTester()) {
|
||||
MRegExpTester* test = def->toRegExpTester();
|
||||
if (test->indexOf(*iter) == 1) {
|
||||
// Optimized RegExp.prototype.test.
|
||||
MOZ_ASSERT(test->regexp() == regexp);
|
||||
@@ -2149,25 +2143,33 @@ LIRGenerator::visitRegExp(MRegExp* ins)
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitRegExpExec(MRegExpExec* ins)
|
||||
LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(ins->string()->type() == MIRType_String);
|
||||
MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
|
||||
MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
|
||||
|
||||
LRegExpExec* lir = new(alloc()) LRegExpExec(useFixedAtStart(ins->regexp(), CallTempReg0),
|
||||
useFixedAtStart(ins->string(), CallTempReg1));
|
||||
LRegExpMatcher* lir = new(alloc()) LRegExpMatcher(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
|
||||
useFixedAtStart(ins->string(), RegExpMatcherStringReg),
|
||||
useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg),
|
||||
useFixedAtStart(ins->sticky(), RegExpMatcherStickyReg));
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitRegExpTest(MRegExpTest* ins)
|
||||
LIRGenerator::visitRegExpTester(MRegExpTester* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(ins->string()->type() == MIRType_String);
|
||||
MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
|
||||
MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
|
||||
|
||||
LRegExpTest* lir = new(alloc()) LRegExpTest(useFixedAtStart(ins->regexp(), CallTempReg2),
|
||||
useFixedAtStart(ins->string(), CallTempReg3));
|
||||
LRegExpTester* lir = new(alloc()) LRegExpTester(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg),
|
||||
useFixedAtStart(ins->string(), RegExpTesterStringReg),
|
||||
useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg),
|
||||
useFixedAtStart(ins->sticky(), RegExpTesterStickyReg));
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
@@ -161,8 +161,8 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitToString(MToString* convert);
|
||||
void visitToObjectOrNull(MToObjectOrNull* convert);
|
||||
void visitRegExp(MRegExp* ins);
|
||||
void visitRegExpExec(MRegExpExec* ins);
|
||||
void visitRegExpTest(MRegExpTest* ins);
|
||||
void visitRegExpMatcher(MRegExpMatcher* ins);
|
||||
void visitRegExpTester(MRegExpTester* ins);
|
||||
void visitRegExpReplace(MRegExpReplace* ins);
|
||||
void visitStringReplace(MStringReplace* ins);
|
||||
void visitBinarySharedStub(MBinarySharedStub* ins);
|
||||
|
||||
@@ -174,10 +174,12 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
return inlineMathFunction(callInfo, MMathFunction::Cbrt);
|
||||
|
||||
// RegExp natives.
|
||||
case InlinableNative::RegExpExec:
|
||||
return CallResultEscapes(pc) ? inlineRegExpExec(callInfo) : inlineRegExpTest(callInfo);
|
||||
case InlinableNative::RegExpTest:
|
||||
return inlineRegExpTest(callInfo);
|
||||
case InlinableNative::RegExpMatcher:
|
||||
return inlineRegExpMatcher(callInfo);
|
||||
case InlinableNative::RegExpTester:
|
||||
return inlineRegExpTester(callInfo);
|
||||
case InlinableNative::IsRegExpObject:
|
||||
return inlineIsRegExpObject(callInfo);
|
||||
|
||||
// String natives.
|
||||
case InlinableNative::String:
|
||||
@@ -1816,79 +1818,144 @@ IonBuilder::inlineStrCharAt(CallInfo& callInfo)
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineRegExpExec(CallInfo& callInfo)
|
||||
IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
// This is called from Self-hosted JS, after testing each argument,
|
||||
// most of following tests should be passed.
|
||||
|
||||
if (callInfo.argc() != 4 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (callInfo.thisArg()->type() != MIRType_Object)
|
||||
// regexp
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
|
||||
const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
|
||||
TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
|
||||
const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
|
||||
if (clasp != &RegExpObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
|
||||
// string
|
||||
if (callInfo.getArg(1)->mightBeType(MIRType_Object))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// lastIndex: Only inline if it's Int32.
|
||||
if (callInfo.getArg(2)->type() != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// sticky
|
||||
if (callInfo.getArg(3)->type() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
JSContext* cx = GetJitContext()->cx;
|
||||
if (!cx->compartment()->jitCompartment()->ensureRegExpExecStubExists(cx))
|
||||
if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
|
||||
oom();
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction* exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
|
||||
current->add(exec);
|
||||
current->push(exec);
|
||||
MInstruction* matcher = MRegExpMatcher::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
|
||||
callInfo.getArg(2), callInfo.getArg(3));
|
||||
current->add(matcher);
|
||||
current->push(matcher);
|
||||
|
||||
if (!resumeAfter(exec))
|
||||
if (!resumeAfter(matcher))
|
||||
return InliningStatus_Error;
|
||||
|
||||
if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
|
||||
if (!pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet))
|
||||
return InliningStatus_Error;
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineRegExpTest(CallInfo& callInfo)
|
||||
IonBuilder::inlineRegExpTester(CallInfo& callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||
// This is called from Self-hosted JS, after testing each argument,
|
||||
// most of following tests should be passed.
|
||||
|
||||
if (callInfo.argc() != 4 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// TI can infer a nullptr return type of regexp_test with eager compilation.
|
||||
if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
|
||||
// regexp
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (callInfo.thisArg()->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
|
||||
const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
|
||||
TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
|
||||
const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
|
||||
if (clasp != &RegExpObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
|
||||
|
||||
// string
|
||||
if (callInfo.getArg(1)->mightBeType(MIRType_Object))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// lastIndex: Only inline if it's Int32.
|
||||
if (callInfo.getArg(2)->type() != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// sticky
|
||||
if (callInfo.getArg(3)->type() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
JSContext* cx = GetJitContext()->cx;
|
||||
if (!cx->compartment()->jitCompartment()->ensureRegExpTestStubExists(cx))
|
||||
if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
|
||||
oom();
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction* match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
|
||||
current->add(match);
|
||||
current->push(match);
|
||||
if (!resumeAfter(match))
|
||||
MInstruction* tester = MRegExpTester::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
|
||||
callInfo.getArg(2), callInfo.getArg(3));
|
||||
current->add(tester);
|
||||
current->push(tester);
|
||||
|
||||
if (!resumeAfter(tester))
|
||||
return InliningStatus_Error;
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
|
||||
{
|
||||
if (callInfo.constructing() || callInfo.argc() != 1) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (getInlineReturnType() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition* arg = callInfo.getArg(0);
|
||||
|
||||
bool isRegExpObject;
|
||||
if (!arg->mightBeType(MIRType_Object)) {
|
||||
isRegExpObject = false;
|
||||
} else {
|
||||
if (arg->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
TemporaryTypeSet* types = arg->resultTypeSet();
|
||||
const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
|
||||
if (!clasp || clasp->isProxy())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
isRegExpObject = (clasp == &RegExpObject::class_);
|
||||
}
|
||||
|
||||
pushConstant(BooleanValue(isRegExpObject));
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineStrReplace(CallInfo& callInfo)
|
||||
{
|
||||
|
||||
+51
-24
@@ -7496,33 +7496,46 @@ class MRegExp : public MNullaryInstruction
|
||||
}
|
||||
};
|
||||
|
||||
class MRegExpExec
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >::Data
|
||||
class MRegExpMatcher
|
||||
: public MAryInstruction<4>,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
private:
|
||||
|
||||
MRegExpExec(MDefinition* regexp, MDefinition* string)
|
||||
: MBinaryInstruction(string, regexp)
|
||||
MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
|
||||
MDefinition* sticky)
|
||||
: MAryInstruction<4>()
|
||||
{
|
||||
initOperand(0, regexp);
|
||||
initOperand(1, string);
|
||||
initOperand(2, lastIndex);
|
||||
initOperand(3, sticky);
|
||||
|
||||
// May be object or null.
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(RegExpExec)
|
||||
INSTRUCTION_HEADER(RegExpMatcher)
|
||||
|
||||
static MRegExpExec* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
|
||||
return new(alloc) MRegExpExec(regexp, string);
|
||||
}
|
||||
|
||||
MDefinition* string() const {
|
||||
return getOperand(0);
|
||||
static MRegExpMatcher* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
|
||||
MDefinition* lastIndex, MDefinition* sticky)
|
||||
{
|
||||
return new(alloc) MRegExpMatcher(regexp, string, lastIndex, sticky);
|
||||
}
|
||||
|
||||
MDefinition* regexp() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition* string() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* lastIndex() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
MDefinition* sticky() const {
|
||||
return getOperand(3);
|
||||
}
|
||||
|
||||
bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
|
||||
@@ -7538,31 +7551,45 @@ class MRegExpExec
|
||||
}
|
||||
};
|
||||
|
||||
class MRegExpTest
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >::Data
|
||||
class MRegExpTester
|
||||
: public MAryInstruction<4>,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
private:
|
||||
|
||||
MRegExpTest(MDefinition* regexp, MDefinition* string)
|
||||
: MBinaryInstruction(string, regexp)
|
||||
MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
|
||||
MDefinition* sticky)
|
||||
: MAryInstruction<4>()
|
||||
{
|
||||
setResultType(MIRType_Boolean);
|
||||
initOperand(0, regexp);
|
||||
initOperand(1, string);
|
||||
initOperand(2, lastIndex);
|
||||
initOperand(3, sticky);
|
||||
|
||||
setResultType(MIRType_Int32);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(RegExpTest)
|
||||
INSTRUCTION_HEADER(RegExpTester)
|
||||
|
||||
static MRegExpTest* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
|
||||
return new(alloc) MRegExpTest(regexp, string);
|
||||
static MRegExpTester* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
|
||||
MDefinition* lastIndex, MDefinition* sticky)
|
||||
{
|
||||
return new(alloc) MRegExpTester(regexp, string, lastIndex, sticky);
|
||||
}
|
||||
|
||||
MDefinition* string() const {
|
||||
MDefinition* regexp() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition* regexp() const {
|
||||
MDefinition* string() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* lastIndex() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
MDefinition* sticky() const {
|
||||
return getOperand(3);
|
||||
}
|
||||
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
@@ -7570,7 +7597,7 @@ class MRegExpTest
|
||||
|
||||
bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
bool canRecoverOnBailout() const override {
|
||||
// RegExpTest has a side-effect on the regexp object's lastIndex
|
||||
// RegExpTester has a side-effect on the regexp object's lastIndex
|
||||
// when sticky or global flags are set.
|
||||
// Return false unless we are sure it's not the case.
|
||||
// XXX: always return false for now, to work around bug 1132128.
|
||||
|
||||
@@ -139,8 +139,8 @@ namespace jit {
|
||||
_(Nop) \
|
||||
_(LimitedTruncate) \
|
||||
_(RegExp) \
|
||||
_(RegExpExec) \
|
||||
_(RegExpTest) \
|
||||
_(RegExpMatcher) \
|
||||
_(RegExpTester) \
|
||||
_(RegExpReplace) \
|
||||
_(StringReplace) \
|
||||
_(Lambda) \
|
||||
|
||||
@@ -1242,8 +1242,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output)
|
||||
loadStringChars(str, output);
|
||||
|
||||
Label isLatin1, done;
|
||||
branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
|
||||
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
|
||||
branchLatin1String(str, &isLatin1);
|
||||
load16ZeroExtend(BaseIndex(output, index, TimesTwo), output);
|
||||
jump(&done);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user