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)
- Bug 1245801 - 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)
- Bug 1204752 - 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:
2023-07-31 14:29:48 +08:00
parent 9fcdb1e308
commit 01cb9ab79e
234 changed files with 4120 additions and 2175 deletions
+4 -4
View File
@@ -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)
{
}
+3 -5
View File
@@ -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;
+8 -14
View File
@@ -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);
{
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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
View File
@@ -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)
-17
View File
@@ -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.
+1
View File
@@ -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 \
+15
View File
@@ -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
View File
@@ -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)
+62 -4
View File
@@ -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();
}
}
+6
View File
@@ -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)]
+8 -1
View File
@@ -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
{
+16
View File
@@ -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)
{
+1
View File
@@ -65,6 +65,7 @@ private:
bool mURIWasModified;
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
bool mIsSrcdocEntry;
bool mScrollRestorationIsManual;
nsString mSrcdocData;
nsCOMPtr<nsIURI> mBaseURI;
};
+1
View File
@@ -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;">
&nbsp;</div>
<div id="bottom">Hello world</div>
<a href="#hash" name="hash">hash</a>
</body>
</html>
+1
View File
@@ -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.
+51
View File
@@ -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>
+12
View File
@@ -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
+268 -1
View File
@@ -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
+63
View File
@@ -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
+10
View File
@@ -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
+13 -1
View File
@@ -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
+31 -4
View File
@@ -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();
}
}
+6 -5
View File
@@ -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;
+1
View File
@@ -39,6 +39,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'/dom/base',
'/layout/style',
]
FINAL_LIBRARY = 'xul'
+11 -20
View File
@@ -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,
+1 -2
View File
@@ -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,
+2 -2
View File
@@ -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,
+3 -5
View File
@@ -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
View File
@@ -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(&currentScrollRestorationIsManual);
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
+4
View File
@@ -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);
+21 -62
View File
@@ -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) \
{ \
}
+6
View File
@@ -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();
}
+1 -1
View File
@@ -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));
+1 -1
View File
@@ -269,7 +269,7 @@ nsHTMLDocument::CreateShell(nsPresContext* aContext,
nsViewManager* aViewManager,
nsStyleSet* aStyleSet)
{
return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
return doCreateShell(aContext, aViewManager, aStyleSet);
}
void
+4
View File
@@ -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);
+2 -2
View File
@@ -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)
+4 -3
View File
@@ -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,
-1
View File
@@ -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
+2 -4
View File
@@ -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
View File
@@ -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;
}
-23
View File
@@ -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;
};
+2 -3
View File
@@ -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;
}
+1 -1
View File
@@ -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; }
+2 -3
View File
@@ -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;
}
+1 -1
View File
@@ -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; }
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -3
View File
@@ -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,
+3 -1
View File
@@ -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);
+3 -3
View File
@@ -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;
}
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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) {
+3 -4
View File
@@ -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;
+5 -5
View File
@@ -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();
+4 -5
View File
@@ -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;
}
+1 -1
View File
@@ -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,
+5 -6
View File
@@ -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);
}
}
+1 -2
View File
@@ -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
+3 -2
View File
@@ -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),
+6 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+117
View File
@@ -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");
+16 -2
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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();
}
+3 -2
View File
@@ -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);
}
}
+4 -1
View File
@@ -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;
+4 -4
View File
@@ -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
+2 -2
View File
@@ -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) \
+5 -3
View File
@@ -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();
}
+1
View File
@@ -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));
+2
View File
@@ -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);
+1 -1
View File
@@ -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);
+3 -2
View File
@@ -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
View File
@@ -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,
&notFound, &oolEntry))
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
temp1, temp2, temp5, inputOutputDataStartOffset,
RegExpShared::Normal, &notFound, &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, &notFound, &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(&notFound);
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);
+6 -6
View File
@@ -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);
+2 -3
View File
@@ -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);
+3 -2
View File
@@ -65,8 +65,9 @@
_(MathTrunc) \
_(MathCbrt) \
\
_(RegExpExec) \
_(RegExpTest) \
_(RegExpMatcher) \
_(RegExpTester) \
_(IsRegExpObject) \
\
_(String) \
_(StringSplit) \
+10 -10
View File
@@ -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(&regExpExecStub_))
regExpExecStub_ = nullptr;
if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
regExpMatcherStub_ = nullptr;
if (regExpTestStub_ && !IsMarkedUnbarriered(&regExpTestStub_))
regExpTestStub_ = nullptr;
if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
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()) {
-4
View File
@@ -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;
+2
View File
@@ -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());
+4 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+2 -2
View File
@@ -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);
+97 -30
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -139,8 +139,8 @@ namespace jit {
_(Nop) \
_(LimitedTruncate) \
_(RegExp) \
_(RegExpExec) \
_(RegExpTest) \
_(RegExpMatcher) \
_(RegExpTester) \
_(RegExpReplace) \
_(StringReplace) \
_(Lambda) \
+1 -2
View File
@@ -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