mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
5c58ebda5c
@ -265,6 +265,12 @@ this.EventManager.prototype = {
|
||||
}
|
||||
case Events.DOCUMENT_LOAD_COMPLETE:
|
||||
{
|
||||
let position = this.contentControl.vc.position;
|
||||
if (position && Utils.isInSubtree(position, aEvent.accessible)) {
|
||||
// Do not automove into the document if the virtual cursor is already
|
||||
// positioned inside it.
|
||||
break;
|
||||
}
|
||||
this.contentControl.autoMove(
|
||||
aEvent.accessible, { delay: 500 });
|
||||
break;
|
||||
|
@ -96,6 +96,9 @@ var gSimpleTraversalRoles =
|
||||
Roles.SPINBUTTON,
|
||||
Roles.OPTION,
|
||||
Roles.LISTITEM,
|
||||
Roles.GRID_CELL,
|
||||
Roles.COLUMNHEADER,
|
||||
Roles.ROWHEADER,
|
||||
// Used for traversing in to child OOP frames.
|
||||
Roles.INTERNAL_FRAME];
|
||||
|
||||
@ -144,11 +147,16 @@ var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
|
||||
return TraversalRules._shouldSkipImage(aAccessible);
|
||||
case Roles.HEADER:
|
||||
case Roles.HEADING:
|
||||
case Roles.COLUMNHEADER:
|
||||
case Roles.ROWHEADER:
|
||||
if ((aAccessible.childCount > 0 || aAccessible.name) &&
|
||||
(isSingleLineage(aAccessible) || isFlatSubtree(aAccessible))) {
|
||||
return Filters.MATCH | Filters.IGNORE_SUBTREE;
|
||||
}
|
||||
return Filters.IGNORE;
|
||||
case Roles.GRID_CELL:
|
||||
return isSingleLineage(aAccessible) || isFlatSubtree(aAccessible) ?
|
||||
Filters.MATCH | Filters.IGNORE_SUBTREE : Filters.IGNORE;
|
||||
case Roles.LISTITEM:
|
||||
{
|
||||
let item = aAccessible.childCount === 2 &&
|
||||
|
@ -411,6 +411,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
|
||||
["555-12345", {"string": "listboxoption"}]],
|
||||
expectedBraille: [[{"string": "listboxoptionAbbr"}, "555-12345"],
|
||||
["555-12345", {"string": "listboxoptionAbbr"}]]
|
||||
}, {
|
||||
accOrElmOrID: "columnheader",
|
||||
oldAccOrElmOrID: "grid",
|
||||
expectedUtterance: [[{"string": "columnInfo", "args": [1]},
|
||||
{"string": "rowInfo", "args" :[1]}, "Sunday"], ["Sunday",
|
||||
{"string": "columnInfo", "args": [1]},
|
||||
{"string": "rowInfo", "args" :[1]}]],
|
||||
expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 1]},
|
||||
"Sunday"], ["Sunday", {"string": "cellInfoAbbr", "args": [1, 1]}]]
|
||||
}, {
|
||||
accOrElmOrID: "rowheader",
|
||||
oldAccOrElmOrID: "grid",
|
||||
expectedUtterance: [[{"string": "columnInfo", "args": [1]},
|
||||
{"string": "rowInfo", "args": [2]}, "Sunday", "Week 1"], ["Week 1",
|
||||
{"string": "columnInfo", "args": [1]},
|
||||
{"string": "rowInfo", "args": [2]}, "Sunday"]],
|
||||
expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 2]},
|
||||
"Sunday", "Week 1"], ["Week 1",
|
||||
{"string": "cellInfoAbbr", "args": [1, 2]}, "Sunday"]]
|
||||
}, {
|
||||
accOrElmOrID: "gridcell1",
|
||||
oldAccOrElmOrID: "grid",
|
||||
expectedUtterance: [["3"], ["3"]],
|
||||
expectedBraille: [["3"], ["3"]]
|
||||
}, {
|
||||
accOrElmOrID: "gridcell2",
|
||||
oldAccOrElmOrID: "grid",
|
||||
expectedUtterance: [["4", "7"], ["4", "7"]],
|
||||
expectedBraille: [["4", "7"], ["4", "7"]]
|
||||
}];
|
||||
|
||||
// Test all possible utterance order preference values.
|
||||
@ -539,6 +568,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
|
||||
555-12345
|
||||
</li>
|
||||
</ul>
|
||||
<section id="grid" role="grid">
|
||||
<ol role="row">
|
||||
<li role="presentation"></li>
|
||||
<li id="columnheader" role="columnheader" aria-label="Sunday">S</li>
|
||||
<li role="columnheader">M</li>
|
||||
</ol>
|
||||
<ol role="row">
|
||||
<li id="rowheader" role="rowheader" aria-label="Week 1">1</li>
|
||||
<li id="gridcell1" role="gridcell"><span>3</span><div></div></li>
|
||||
<li id="gridcell2" role="gridcell"><span>4</span><div>7</div></li>
|
||||
</ol>
|
||||
<ol role="row">
|
||||
<li role="rowheader">2</li>
|
||||
<li role="gridcell">5</li>
|
||||
<li role="gridcell">6</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -119,10 +119,10 @@
|
||||
'heading-5', 'image-2', 'image-3',
|
||||
'Not actually an image', 'link-1', 'anchor-1',
|
||||
'link-2', 'anchor-2', 'link-3', '3', '1', '4',
|
||||
'1', 'S', 'M', '1', '3', '4', '7', '2', '5', '8',
|
||||
'6', 'Just an innocuous separator', 'Dirty Words',
|
||||
'Meaning', 'Mud', 'Wet Dirt', 'Dirt',
|
||||
'Messy Stuff']);
|
||||
'1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
|
||||
'5 8', 'gridcell4', 'Just an innocuous separator',
|
||||
'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
|
||||
'Dirt', 'Messy Stuff']);
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
// The following boilerplate makes sure that XPCom calls
|
||||
// The following boilerplate makes sure that XPCOM calls
|
||||
// that use the profile directory work.
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -326,6 +326,20 @@ if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$DEVELOPER_OPTIONS"; then
|
||||
DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
|
||||
fi
|
||||
fi
|
||||
|
||||
# bionic in Android < 4.1 doesn't support PIE
|
||||
if test "$GNU_CC" -a "$OS_TARGET" != Android; then
|
||||
AC_MSG_CHECKING([for PIE support])
|
||||
_SAVE_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS -pie"
|
||||
AC_TRY_LINK(,,AC_MSG_RESULT([yes])
|
||||
[MOZ_PROGRAM_LDFLAGS="$MOZ_PROGRAM_LDFLAGS -pie"],
|
||||
AC_MSG_RESULT([no]))
|
||||
LDFLAGS=$_SAVE_LDFLAGS
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_PROGRAM_LDFLAGS)
|
||||
|
||||
])
|
||||
|
||||
dnl GCC and clang will fail if given an unknown warning option like -Wfoobar.
|
||||
|
@ -207,7 +207,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold):
|
||||
r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+"
|
||||
r"-?\d+\s+(?P<numLeaked>-?\d+)")
|
||||
|
||||
processString = " %s process:" % processType
|
||||
processString = "%s process:" % processType
|
||||
crashedOnPurpose = False
|
||||
totalBytesLeaked = None
|
||||
logAsWarning = False
|
||||
@ -238,7 +238,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold):
|
||||
# log, particularly on B2G. Eventually, these should be split into multiple
|
||||
# logs (bug 1068869), but for now, we report the largest leak.
|
||||
if totalBytesLeaked != None:
|
||||
leakAnalysis.append("WARNING | leakcheck |%s multiple BloatView byte totals found"
|
||||
leakAnalysis.append("WARNING | leakcheck | %s multiple BloatView byte totals found"
|
||||
% processString)
|
||||
else:
|
||||
totalBytesLeaked = 0
|
||||
@ -251,13 +251,13 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold):
|
||||
else:
|
||||
recordLeakedObjects = False
|
||||
if size < 0 or bytesLeaked < 0 or numLeaked < 0:
|
||||
leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck |%s negative leaks caught!"
|
||||
leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s negative leaks caught!"
|
||||
% processString)
|
||||
logAsWarning = True
|
||||
continue
|
||||
if name != "TOTAL" and numLeaked != 0 and recordLeakedObjects:
|
||||
leakedObjectNames.append(name)
|
||||
leakedObjectAnalysis.append("TEST-INFO | leakcheck |%s leaked %d %s (%s bytes)"
|
||||
leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s (%s bytes)"
|
||||
% (processString, numLeaked, name, bytesLeaked))
|
||||
|
||||
leakAnalysis.extend(leakedObjectAnalysis)
|
||||
@ -271,17 +271,17 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold):
|
||||
if totalBytesLeaked is None:
|
||||
# We didn't see a line with name 'TOTAL'
|
||||
if crashedOnPurpose:
|
||||
log.info("TEST-INFO | leakcheck |%s deliberate crash and thus no leak log"
|
||||
log.info("TEST-INFO | leakcheck | %s deliberate crash and thus no leak log"
|
||||
% processString)
|
||||
else:
|
||||
# TODO: This should be a TEST-UNEXPECTED-FAIL, but was changed to a warning
|
||||
# due to too many intermittent failures (see bug 831223).
|
||||
log.info("WARNING | leakcheck |%s missing output line for total leaks!"
|
||||
log.info("WARNING | leakcheck | %s missing output line for total leaks!"
|
||||
% processString)
|
||||
return
|
||||
|
||||
if totalBytesLeaked == 0:
|
||||
log.info("TEST-PASS | leakcheck |%s no leaks detected!" % processString)
|
||||
log.info("TEST-PASS | leakcheck | %s no leaks detected!" % processString)
|
||||
return
|
||||
|
||||
# totalBytesLeaked was seen and is non-zero.
|
||||
@ -305,10 +305,10 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold):
|
||||
leakedObjectSummary += ', ...'
|
||||
|
||||
if logAsWarning:
|
||||
log.warning("%s | leakcheck |%s %d bytes leaked (%s)"
|
||||
log.warning("%s | leakcheck | %s %d bytes leaked (%s)"
|
||||
% (prefix, processString, totalBytesLeaked, leakedObjectSummary))
|
||||
else:
|
||||
log.info("%s | leakcheck |%s %d bytes leaked (%s)"
|
||||
log.info("%s | leakcheck | %s %d bytes leaked (%s)"
|
||||
% (prefix, processString, totalBytesLeaked, leakedObjectSummary))
|
||||
|
||||
def processLeakLog(leakLogFile, leakThreshold = 0):
|
||||
|
@ -100,6 +100,7 @@ AUTOMATION_EXTRA_CMDLINE-upload = 2>&1 | tee $(AUTOMATION_UPLOAD_OUTPUT)
|
||||
|
||||
# Note: We have to force -j1 here, at least until bug 1036563 is fixed.
|
||||
AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1
|
||||
AUTOMATION_EXTRA_CMDLINE-pretty-l10n-check = -j1
|
||||
|
||||
# The commands only run if the corresponding MOZ_AUTOMATION_* variable is
|
||||
# enabled. This means, for example, if we enable MOZ_AUTOMATION_UPLOAD, then
|
||||
|
@ -996,14 +996,6 @@ nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx, int line
|
||||
|
||||
nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
|
||||
|
||||
bool exists = false;
|
||||
rv = rph->HasSubstitution(host, &exists);
|
||||
if (exists) {
|
||||
LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
|
||||
"Duplicate resource declaration for '%s' ignored.", package);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
|
||||
if (!resolved) {
|
||||
LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
|
||||
|
@ -1,9 +1,6 @@
|
||||
# Should work
|
||||
resource test1 test1/
|
||||
|
||||
# Duplicates should be ignored
|
||||
resource test1 foo/
|
||||
|
||||
# Mapping into jar files should work
|
||||
resource test3 jar:test3.jar!/resources/
|
||||
|
||||
|
@ -637,6 +637,8 @@ endif
|
||||
|
||||
endif # NO_PROFILE_GUIDED_OPTIMIZE
|
||||
|
||||
MOZ_PROGRAM_LDFLAGS += $(MOZ_GLUE_PROGRAM_LDFLAGS)
|
||||
|
||||
##############################################
|
||||
|
||||
checkout:
|
||||
@ -676,7 +678,7 @@ $(PROGRAM): $(PROGOBJS) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESF
|
||||
$(REPORT_BUILD)
|
||||
@$(RM) $@.manifest
|
||||
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
|
||||
$(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
|
||||
$(EXPAND_LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
|
||||
ifdef MSMANIFEST_TOOL
|
||||
@if test -f $@.manifest; then \
|
||||
if test -f '$(srcdir)/$@.manifest'; then \
|
||||
@ -697,7 +699,7 @@ ifdef MOZ_PROFILE_GENERATE
|
||||
touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink
|
||||
endif
|
||||
else # !WINNT || GNU_CC
|
||||
$(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS)
|
||||
$(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS)
|
||||
$(call CHECK_BINARY,$@)
|
||||
endif # WINNT && !GNU_CC
|
||||
|
||||
@ -745,7 +747,7 @@ endif
|
||||
$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
|
||||
$(REPORT_BUILD)
|
||||
ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
|
||||
$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
|
||||
$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_PROGRAM_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
|
||||
ifdef MSMANIFEST_TOOL
|
||||
@if test -f $@.manifest; then \
|
||||
mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
|
||||
@ -753,7 +755,7 @@ ifdef MSMANIFEST_TOOL
|
||||
fi
|
||||
endif # MSVC with manifest tool
|
||||
else
|
||||
$(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS)
|
||||
$(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(MOZ_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS)
|
||||
$(call CHECK_BINARY,$@)
|
||||
endif # WINNT && !GNU_CC
|
||||
|
||||
|
13
configure.in
13
configure.in
@ -3519,7 +3519,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
|
||||
_USE_SYSTEM_NSS=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSS"; then
|
||||
AM_PATH_NSS(3.16.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
AM_PATH_NSS(3.17.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSS"; then
|
||||
@ -7271,17 +7271,6 @@ if test -n "$JS_GC_ZEAL" -o -n "$MOZ_DEBUG"; then
|
||||
AC_DEFINE(JS_GC_ZEAL)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Perform moving GC stack rooting analysis
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(root-analysis,
|
||||
[ --enable-root-analysis Enable moving GC stack root analysis],
|
||||
JSGC_ROOT_ANALYSIS=1,
|
||||
JSGC_ROOT_ANALYSIS= )
|
||||
if test -n "$JSGC_ROOT_ANALYSIS"; then
|
||||
AC_DEFINE(JSGC_ROOT_ANALYSIS)
|
||||
fi
|
||||
|
||||
MOZ_CHECK_CCACHE
|
||||
|
||||
dnl ========================================================
|
||||
|
@ -35,18 +35,19 @@ window.onload = function() {
|
||||
var resource = getPlayableVideo(gSeekTests);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.cache_size", 40000]]}, beginTest);
|
||||
function beginTest() {
|
||||
if (resource != null) {
|
||||
for (var i=0; i<20; ++i) {
|
||||
var v = document.createElement("video");
|
||||
v.src = resource.name;
|
||||
v.preload = "metadata";
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
} else {
|
||||
todo(false, "No types supported");
|
||||
}
|
||||
}
|
||||
beginTest();
|
||||
</script>
|
||||
|
||||
<pre id="test">
|
||||
|
@ -44,6 +44,8 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
static const unsigned char SIMPLEBLOCK_ID = 0xa3;
|
||||
static const uint32_t BLOCK_TIMECODE_LENGTH = 2;
|
||||
|
||||
static const unsigned char CLUSTER_SYNC_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
|
||||
|
||||
const unsigned char* p = aBuffer;
|
||||
|
||||
// Parse each byte in aBuffer one-by-one, producing timecodes and updating
|
||||
@ -63,6 +65,18 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
||||
mState = READ_VINT;
|
||||
mNextState = PARSE_ELEMENT;
|
||||
break;
|
||||
case FIND_CLUSTER_SYNC:
|
||||
if (*p++ == CLUSTER_SYNC_ID[mClusterSyncPos]) {
|
||||
mClusterSyncPos += 1;
|
||||
} else {
|
||||
mClusterSyncPos = 0;
|
||||
}
|
||||
if (mClusterSyncPos == sizeof(CLUSTER_SYNC_ID)) {
|
||||
mVInt.mValue = CLUSTER_ID;
|
||||
mVInt.mLength = sizeof(CLUSTER_SYNC_ID);
|
||||
mState = READ_ELEMENT_SIZE;
|
||||
}
|
||||
break;
|
||||
case PARSE_ELEMENT:
|
||||
mElement.mSize = mVInt;
|
||||
switch (mElement.mID.mValue) {
|
||||
|
@ -51,8 +51,13 @@ struct WebMBufferedParser
|
||||
{
|
||||
explicit WebMBufferedParser(int64_t aOffset)
|
||||
: mStartOffset(aOffset), mCurrentOffset(aOffset), mState(READ_ELEMENT_ID),
|
||||
mVIntRaw(false), mTimecodeScale(1000000), mGotTimecodeScale(false)
|
||||
{}
|
||||
mVIntRaw(false), mClusterSyncPos(0), mTimecodeScale(1000000),
|
||||
mGotTimecodeScale(false)
|
||||
{
|
||||
if (mStartOffset != 0) {
|
||||
mState = FIND_CLUSTER_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetTimecodeScale() {
|
||||
MOZ_ASSERT(mGotTimecodeScale);
|
||||
@ -100,6 +105,10 @@ private:
|
||||
// READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
|
||||
READ_ELEMENT_SIZE,
|
||||
|
||||
// Parser start state for parsers started at an arbitrary offset. Scans
|
||||
// forward for the first cluster, then move to READ_ELEMENT_ID.
|
||||
FIND_CLUSTER_SYNC,
|
||||
|
||||
// Simplistic core of the parser. Does not pay attention to nesting of
|
||||
// elements. Checks mElement for an element ID of interest, then moves
|
||||
// to the next state as determined by the element ID.
|
||||
@ -160,6 +169,10 @@ private:
|
||||
|
||||
bool mVIntRaw;
|
||||
|
||||
// Current match position within CLUSTER_SYNC_ID. Used to find sync
|
||||
// within arbitrary data.
|
||||
uint32_t mClusterSyncPos;
|
||||
|
||||
// Number of bytes of mVInt left to read. mVInt is complete once this
|
||||
// reaches 0.
|
||||
uint32_t mVIntLeft;
|
||||
|
@ -585,8 +585,16 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
||||
}
|
||||
|
||||
VorbisPCMValue** pcm = 0;
|
||||
int32_t frames = 0;
|
||||
while ((frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm)) > 0) {
|
||||
int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
||||
// If the first packet of audio in the media produces no data, we
|
||||
// still need to produce an AudioData for it so that the correct media
|
||||
// start time is calculated. Otherwise we'd end up with a media start
|
||||
// time derived from the timecode of the first packet that produced
|
||||
// data.
|
||||
if (frames == 0 && mAudioFrames == 0) {
|
||||
AudioQueue().Push(new AudioData(aOffset, tstamp_usecs, 0, 0, nullptr, mChannels, rate));
|
||||
}
|
||||
while (frames > 0) {
|
||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * mChannels]);
|
||||
for (uint32_t j = 0; j < mChannels; ++j) {
|
||||
VorbisPCMValue* channel = pcm[j];
|
||||
@ -624,6 +632,8 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
||||
if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
|
||||
}
|
||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||
#ifdef MOZ_OPUS
|
||||
@ -1052,8 +1062,14 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
ranges[index].mEnd,
|
||||
&start, &end);
|
||||
if (rv) {
|
||||
double startTime = start / NS_PER_S - aStartTime;
|
||||
double endTime = end / NS_PER_S - aStartTime;
|
||||
int64_t startOffset = aStartTime * NS_PER_USEC;
|
||||
NS_ASSERTION(startOffset >= 0 && uint64_t(startOffset) <= start,
|
||||
"startOffset negative or larger than start time");
|
||||
if (!(startOffset >= 0 && uint64_t(startOffset) <= start)) {
|
||||
startOffset = 0;
|
||||
}
|
||||
double startTime = (start - startOffset) / NS_PER_S;
|
||||
double endTime = (end - startOffset) / NS_PER_S;
|
||||
// If this range extends to the end of the file, the true end time
|
||||
// is the file's duration.
|
||||
if (mContext && resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
||||
|
@ -3583,7 +3583,7 @@ XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
|
||||
JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx));
|
||||
NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(baseGlobal), NS_OK);
|
||||
|
||||
JSAddonId* addonId = MapURIToAddonID(mCurrentPrototype->GetURI());
|
||||
JSAddonId* addonId = mCurrentPrototype ? MapURIToAddonID(mCurrentPrototype->GetURI()) : nullptr;
|
||||
JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId));
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -26,7 +26,7 @@ UNIFIED_SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_CAMERA']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkCameraControl.cpp',
|
||||
'GonkCameraHwMgr.cpp',
|
||||
'GonkCameraManager.cpp',
|
||||
|
@ -164,8 +164,6 @@ static const char*
|
||||
GetNotifyIMEMessageName(IMEMessage aMessage)
|
||||
{
|
||||
switch (aMessage) {
|
||||
case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
|
||||
return "NOTIFY_IME_OF_CURSOR_POS_CHANGED";
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
return "NOTIFY_IME_OF_FOCUS";
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
@ -298,28 +296,13 @@ IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
|
||||
// composition events which are caused by following APIs are ignored due
|
||||
// to unsafe to run script (in PresShell::HandleEvent()).
|
||||
nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
|
||||
if (widget) {
|
||||
nsresult rv =
|
||||
compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
|
||||
if (NS_FAILED(rv)) {
|
||||
compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
|
||||
}
|
||||
// By calling the APIs, the composition may have been finished normally.
|
||||
compositionInContent =
|
||||
sTextCompositions->GetCompositionFor(
|
||||
compositionInContent->GetPresContext(),
|
||||
compositionInContent->GetEventTargetNode());
|
||||
MOZ_ASSERT(widget, "Why is there no widget?");
|
||||
nsresult rv =
|
||||
compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
|
||||
if (NS_FAILED(rv)) {
|
||||
compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
|
||||
}
|
||||
}
|
||||
|
||||
// If the compositionInContent is still available, we should finish the
|
||||
// composition just on the content forcibly.
|
||||
if (compositionInContent) {
|
||||
PR_LOG(sISMLog, PR_LOG_DEBUG,
|
||||
("ISM: IMEStateManager::OnRemoveContent(), "
|
||||
"composition in the content still alive, committing it forcibly..."));
|
||||
compositionInContent->SynthesizeCommit(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sPresContext || !sContent ||
|
||||
@ -904,17 +887,20 @@ IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
nsPresContext* aPresContext,
|
||||
WidgetEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventDispatchingCallback* aCallBack)
|
||||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized)
|
||||
{
|
||||
PR_LOG(sISMLog, PR_LOG_ALWAYS,
|
||||
("ISM: IMEStateManager::DispatchCompositionEvent(aNode=0x%p, "
|
||||
"aPresContext=0x%p, aEvent={ mClass=%s, message=%s, "
|
||||
" mFlags={ mIsTrusted=%s, mPropagationStopped=%s } })",
|
||||
"mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
|
||||
"aIsSynthesized=%s)",
|
||||
aEventTargetNode, aPresContext,
|
||||
GetEventClassIDName(aEvent->mClass),
|
||||
GetEventMessageName(aEvent->message),
|
||||
GetBoolName(aEvent->mFlags.mIsTrusted),
|
||||
GetBoolName(aEvent->mFlags.mPropagationStopped)));
|
||||
GetBoolName(aEvent->mFlags.mPropagationStopped),
|
||||
GetBoolName(aIsSynthesized)));
|
||||
|
||||
MOZ_ASSERT(aEvent->mClass == eCompositionEventClass ||
|
||||
aEvent->mClass == eTextEventClass);
|
||||
@ -929,6 +915,11 @@ IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
nsRefPtr<TextComposition> composition =
|
||||
sTextCompositions->GetCompositionFor(GUIEvent->widget);
|
||||
if (!composition) {
|
||||
// If synthesized event comes after delayed native composition events
|
||||
// for request of commit or cancel, we should ignore it.
|
||||
if (NS_WARN_IF(aIsSynthesized)) {
|
||||
return;
|
||||
}
|
||||
PR_LOG(sISMLog, PR_LOG_DEBUG,
|
||||
("ISM: IMEStateManager::DispatchCompositionEvent(), "
|
||||
"adding new TextComposition to the array"));
|
||||
@ -943,12 +934,23 @@ IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
// Dispatch the event on composing target.
|
||||
composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
|
||||
composition->DispatchEvent(GUIEvent, aStatus, aCallBack, aIsSynthesized);
|
||||
|
||||
// WARNING: the |composition| might have been destroyed already.
|
||||
|
||||
// Remove the ended composition from the array.
|
||||
if (aEvent->message == NS_COMPOSITION_END) {
|
||||
// NOTE: When TextComposition is synthesizing compositionend event for
|
||||
// emulating a commit, the instance shouldn't be removed from the array
|
||||
// because IME may perform it later. Then, we need to ignore the
|
||||
// following commit events in TextComposition::DispatchEvent().
|
||||
// However, if commit or cancel for a request is performed synchronously
|
||||
// during not safe to dispatch events, PresShell must have discarded
|
||||
// compositionend event. Then, the synthesized compositionend event is
|
||||
// the last event for the composition. In this case, we need to
|
||||
// destroy the TextComposition with synthesized compositionend event.
|
||||
if ((!aIsSynthesized ||
|
||||
composition->WasNativeCompositionEndEventDiscarded()) &&
|
||||
aEvent->message == NS_COMPOSITION_END) {
|
||||
TextCompositionArray::index_type i =
|
||||
sTextCompositions->IndexOf(GUIEvent->widget);
|
||||
if (i != TextCompositionArray::NoIndex) {
|
||||
@ -962,6 +964,38 @@ IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEStateManager::OnCompositionEventDiscarded(WidgetEvent* aEvent)
|
||||
{
|
||||
// Note that this method is never called for synthesized events for emulating
|
||||
// commit or cancel composition.
|
||||
|
||||
PR_LOG(sISMLog, PR_LOG_ALWAYS,
|
||||
("ISM: IMEStateManager::OnCompositionEventDiscarded(aEvent={ mClass=%s, "
|
||||
"message=%s, mFlags={ mIsTrusted=%s } })",
|
||||
GetEventClassIDName(aEvent->mClass),
|
||||
GetEventMessageName(aEvent->message),
|
||||
GetBoolName(aEvent->mFlags.mIsTrusted)));
|
||||
|
||||
MOZ_ASSERT(aEvent->mClass == eCompositionEventClass ||
|
||||
aEvent->mClass == eTextEventClass);
|
||||
if (!aEvent->mFlags.mIsTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore compositionstart for now because sTextCompositions may not have
|
||||
// been created yet.
|
||||
if (aEvent->message == NS_COMPOSITION_START) {
|
||||
return;
|
||||
}
|
||||
|
||||
WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
|
||||
nsRefPtr<TextComposition> composition =
|
||||
sTextCompositions->GetCompositionFor(GUIEvent->widget);
|
||||
composition->OnCompositionEventDiscarded(GUIEvent);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IMEStateManager::NotifyIME(IMEMessage aMessage,
|
||||
@ -972,11 +1006,14 @@ IMEStateManager::NotifyIME(IMEMessage aMessage,
|
||||
composition = sTextCompositions->GetCompositionFor(aWidget);
|
||||
}
|
||||
|
||||
bool isSynthesizedForTests =
|
||||
composition && composition->IsSynthesizedForTests();
|
||||
|
||||
PR_LOG(sISMLog, PR_LOG_ALWAYS,
|
||||
("ISM: IMEStateManager::NotifyIME(aMessage=%s, aWidget=0x%p), "
|
||||
"composition=0x%p, composition->IsSynthesizedForTests()=%s",
|
||||
GetNotifyIMEMessageName(aMessage), aWidget, composition.get(),
|
||||
GetBoolName(composition ? composition->IsSynthesizedForTests() : false)));
|
||||
GetBoolName(isSynthesizedForTests)));
|
||||
|
||||
if (NS_WARN_IF(!aWidget)) {
|
||||
PR_LOG(sISMLog, PR_LOG_ERROR,
|
||||
@ -984,80 +1021,22 @@ IMEStateManager::NotifyIME(IMEMessage aMessage,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!composition || !composition->IsSynthesizedForTests()) {
|
||||
switch (aMessage) {
|
||||
case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
|
||||
return aWidget->NotifyIME(IMENotification(aMessage));
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
return composition ?
|
||||
aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
|
||||
default:
|
||||
MOZ_CRASH("Unsupported notification");
|
||||
}
|
||||
MOZ_CRASH(
|
||||
"Failed to handle the notification for non-synthesized composition");
|
||||
}
|
||||
|
||||
// If the composition is synthesized events for automated tests, we should
|
||||
// dispatch composition events for emulating the native composition behavior.
|
||||
// NOTE: The dispatched events are discarded if it's not safe to run script.
|
||||
switch (aMessage) {
|
||||
case REQUEST_TO_COMMIT_COMPOSITION: {
|
||||
nsCOMPtr<nsIWidget> widget(aWidget);
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
if (!composition->LastData().IsEmpty()) {
|
||||
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
textEvent.theText = composition->LastData();
|
||||
textEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&textEvent, status);
|
||||
if (widget->Destroyed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
status = nsEventStatus_eIgnore;
|
||||
WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
|
||||
endEvent.data = composition->LastData();
|
||||
endEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&endEvent, status);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
case REQUEST_TO_CANCEL_COMPOSITION: {
|
||||
nsCOMPtr<nsIWidget> widget(aWidget);
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
if (!composition->LastData().IsEmpty()) {
|
||||
WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
|
||||
updateEvent.data = composition->LastData();
|
||||
updateEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&updateEvent, status);
|
||||
if (widget->Destroyed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
status = nsEventStatus_eIgnore;
|
||||
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
textEvent.theText = composition->LastData();
|
||||
textEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&textEvent, status);
|
||||
if (widget->Destroyed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
status = nsEventStatus_eIgnore;
|
||||
WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
|
||||
endEvent.data = composition->LastData();
|
||||
endEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&endEvent, status);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
return composition ?
|
||||
composition->RequestToCommit(aWidget, false) : NS_OK;
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
return composition ?
|
||||
composition->RequestToCommit(aWidget, true) : NS_OK;
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
return composition && !isSynthesizedForTests ?
|
||||
aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
|
||||
default:
|
||||
return NS_OK;
|
||||
MOZ_CRASH("Unsupported notification");
|
||||
}
|
||||
MOZ_CRASH(
|
||||
"Failed to handle the notification for non-synthesized composition");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -1105,6 +1084,16 @@ IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
|
||||
nsINode* root = nullptr;
|
||||
nsINode* node = aContent;
|
||||
while (node && IsEditable(node)) {
|
||||
// If the node has independent selection like <input type="text"> or
|
||||
// <textarea>, the node should be the root editable node for aContent.
|
||||
// FYI: <select> element also has independent selection but IsEditable()
|
||||
// returns false.
|
||||
// XXX: If somebody adds new editable element which has independent
|
||||
// selection but doesn't own editor, we'll need more checks here.
|
||||
if (node->IsContent() &&
|
||||
node->AsContent()->HasIndependentSelection()) {
|
||||
return node;
|
||||
}
|
||||
root = node;
|
||||
node = node->GetParentNode();
|
||||
}
|
||||
|
@ -102,7 +102,14 @@ public:
|
||||
nsPresContext* aPresContext,
|
||||
WidgetEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventDispatchingCallback* aCallBack);
|
||||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized = false);
|
||||
|
||||
/**
|
||||
* This is called when PresShell ignores composition event or text event due
|
||||
* to not safe to dispatch events.
|
||||
*/
|
||||
static void OnCompositionEventDiscarded(WidgetEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Get TextComposition from widget.
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/MiscEvents.h"
|
||||
@ -20,6 +21,8 @@ using namespace mozilla::widget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING("\x3000"))
|
||||
|
||||
/******************************************************************************
|
||||
* TextComposition
|
||||
******************************************************************************/
|
||||
@ -35,6 +38,10 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
|
||||
, mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
|
||||
, mIsComposing(false)
|
||||
, mIsEditorHandlingEvent(false)
|
||||
, mIsRequestingCommit(false)
|
||||
, mIsRequestingCancel(false)
|
||||
, mRequestedToCommitOrCancel(false)
|
||||
, mWasNativeCompositionEndEventDiscarded(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -53,11 +60,135 @@ TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
|
||||
return mNativeContext == aWidget->GetInputContext().mNativeIMEContext;
|
||||
}
|
||||
|
||||
bool
|
||||
TextComposition::MaybeDispatchCompositionUpdate(const WidgetTextEvent* aEvent)
|
||||
{
|
||||
if (Destroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mLastData == aEvent->theText) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WidgetCompositionEvent compositionUpdate(aEvent->mFlags.mIsTrusted,
|
||||
NS_COMPOSITION_UPDATE,
|
||||
aEvent->widget);
|
||||
compositionUpdate.time = aEvent->time;
|
||||
compositionUpdate.timeStamp = aEvent->timeStamp;
|
||||
compositionUpdate.data = aEvent->theText;
|
||||
compositionUpdate.mFlags.mIsSynthesizedForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
|
||||
nsEventStatus status = nsEventStatus_eConsumeNoDefault;
|
||||
if (aEvent->mFlags.mIsSynthesizedForTests &&
|
||||
(mIsRequestingCommit || mIsRequestingCancel)) {
|
||||
// At emulating commit/cancel request, compositionupdate should be
|
||||
// dispatched via widget since it's more similar path to native event.
|
||||
aEvent->widget->DispatchEvent(&compositionUpdate, status);
|
||||
} else {
|
||||
mLastData = compositionUpdate.data;
|
||||
EventDispatcher::Dispatch(mNode, mPresContext,
|
||||
&compositionUpdate, nullptr, &status, nullptr);
|
||||
}
|
||||
return !Destroyed();
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::OnCompositionEventDiscarded(const WidgetGUIEvent* aEvent)
|
||||
{
|
||||
// Note that this method is never called for synthesized events for emulating
|
||||
// commit or cancel composition.
|
||||
|
||||
MOZ_ASSERT(aEvent->mFlags.mIsTrusted,
|
||||
"Shouldn't be called with untrusted event");
|
||||
MOZ_ASSERT(aEvent->mClass == eCompositionEventClass ||
|
||||
aEvent->mClass == eTextEventClass);
|
||||
|
||||
// XXX If composition events are discarded, should we dispatch them with
|
||||
// runnable event? However, even if we do so, it might make native IME
|
||||
// confused due to async modification. Especially when native IME is
|
||||
// TSF.
|
||||
if (aEvent->message != NS_COMPOSITION_END) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWasNativeCompositionEndEventDiscarded = true;
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventDispatchingCallback* aCallBack)
|
||||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized)
|
||||
{
|
||||
if (Destroyed()) {
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this instance has requested to commit or cancel composition but
|
||||
// is not synthesizing commit event, that means that the IME commits or
|
||||
// cancels the composition asynchronously. Typically, iBus behaves so.
|
||||
// Then, synthesized events which were dispatched immediately after
|
||||
// the request has already committed our editor's composition string and
|
||||
// told it to web apps. Therefore, we should ignore the delayed events.
|
||||
if (mRequestedToCommitOrCancel && !aIsSynthesized) {
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
return;
|
||||
}
|
||||
|
||||
// IME may commit composition with empty string for a commit request or
|
||||
// with non-empty string for a cancel request. We should prevent such
|
||||
// unexpected result. E.g., web apps may be confused if they implement
|
||||
// autocomplete which attempts to commit composition forcibly when the user
|
||||
// selects one of suggestions but composition string is cleared by IME.
|
||||
// Note that most Chinese IMEs don't expose actual composition string to us.
|
||||
// They typically tell us an IDEOGRAPHIC SPACE or empty string as composition
|
||||
// string. Therefore, we should hack it only when:
|
||||
// 1. committing string is empty string at requesting commit but the last
|
||||
// data isn't IDEOGRAPHIC SPACE.
|
||||
// 2. non-empty string is committed at requesting cancel.
|
||||
if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) {
|
||||
nsString* committingData = nullptr;
|
||||
switch (aEvent->message) {
|
||||
case NS_COMPOSITION_UPDATE:
|
||||
case NS_COMPOSITION_END:
|
||||
committingData = &aEvent->AsCompositionEvent()->data;
|
||||
break;
|
||||
case NS_TEXT_TEXT:
|
||||
committingData = &aEvent->AsTextEvent()->theText;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unexpected event comes during committing or "
|
||||
"canceling composition");
|
||||
break;
|
||||
}
|
||||
if (committingData) {
|
||||
if (mIsRequestingCommit && committingData->IsEmpty() &&
|
||||
mLastData != IDEOGRAPHIC_SPACE) {
|
||||
committingData->Assign(mLastData);
|
||||
} else if (mIsRequestingCancel && !committingData->IsEmpty()) {
|
||||
committingData->Truncate();
|
||||
}
|
||||
|
||||
if (aEvent->message == NS_COMPOSITION_UPDATE) {
|
||||
// If committing string is not different from the last data,
|
||||
// we don't need to dispatch this.
|
||||
if (committingData->Equals(mLastData)) {
|
||||
return;
|
||||
}
|
||||
} else if (aEvent->message == NS_TEXT_TEXT) {
|
||||
// If committing string is different from the last data,
|
||||
// we need to dispatch compositionupdate before dispatching text event.
|
||||
if (!MaybeDispatchCompositionUpdate(aEvent->AsTextEvent())) {
|
||||
NS_WARNING("Dispatching compositionupdate caused destroying");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvent->message == NS_COMPOSITION_UPDATE) {
|
||||
mLastData = aEvent->AsCompositionEvent()->data;
|
||||
}
|
||||
@ -65,7 +196,7 @@ TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
|
||||
EventDispatcher::Dispatch(mNode, mPresContext,
|
||||
aEvent, nullptr, aStatus, aCallBack);
|
||||
|
||||
if (!mPresContext) {
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -122,23 +253,108 @@ TextComposition::NotityUpdateComposition(WidgetGUIEvent* aEvent)
|
||||
|
||||
void
|
||||
TextComposition::DispatchCompositionEventRunnable(uint32_t aEventMessage,
|
||||
const nsAString& aData)
|
||||
const nsAString& aData,
|
||||
bool aIsSynthesizingCommit)
|
||||
{
|
||||
nsContentUtils::AddScriptRunner(
|
||||
new CompositionEventDispatcher(mPresContext, mNode,
|
||||
aEventMessage, aData));
|
||||
new CompositionEventDispatcher(this, mNode, aEventMessage, aData,
|
||||
aIsSynthesizingCommit));
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::SynthesizeCommit(bool aDiscard)
|
||||
nsresult
|
||||
TextComposition::RequestToCommit(nsIWidget* aWidget, bool aDiscard)
|
||||
{
|
||||
nsRefPtr<TextComposition> kungFuDeathGrip(this);
|
||||
nsAutoString data(aDiscard ? EmptyString() : mLastData);
|
||||
if (mLastData != data) {
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data);
|
||||
DispatchCompositionEventRunnable(NS_TEXT_TEXT, data);
|
||||
// If this composition is already requested to be committed or canceled,
|
||||
// we don't need to request it again because even if the first request
|
||||
// failed, new request won't success, probably. And we shouldn't synthesize
|
||||
// events for committing or canceling composition twice or more times.
|
||||
if (mRequestedToCommitOrCancel) {
|
||||
return NS_OK;
|
||||
}
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_END, data);
|
||||
|
||||
nsRefPtr<TextComposition> kungFuDeathGrip(this);
|
||||
const nsAutoString lastData(mLastData);
|
||||
|
||||
{
|
||||
AutoRestore<bool> saveRequestingCancel(mIsRequestingCancel);
|
||||
AutoRestore<bool> saveRequestingCommit(mIsRequestingCommit);
|
||||
if (aDiscard) {
|
||||
mIsRequestingCancel = true;
|
||||
mIsRequestingCommit = false;
|
||||
} else {
|
||||
mIsRequestingCancel = false;
|
||||
mIsRequestingCommit = true;
|
||||
}
|
||||
if (!mIsSynthesizedForTests) {
|
||||
// FYI: CompositionEvent and TextEvent caused by a call of NotifyIME()
|
||||
// may be discarded by PresShell if it's not safe to dispatch the
|
||||
// event.
|
||||
nsresult rv =
|
||||
aWidget->NotifyIME(IMENotification(aDiscard ?
|
||||
REQUEST_TO_CANCEL_COMPOSITION :
|
||||
REQUEST_TO_COMMIT_COMPOSITION));
|
||||
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
|
||||
return rv;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// Emulates to commit or cancel the composition
|
||||
// FYI: These events may be discarded by PresShell if it's not safe to
|
||||
// dispatch the event.
|
||||
nsCOMPtr<nsIWidget> widget(aWidget);
|
||||
nsAutoString commitData(aDiscard ? EmptyString() : lastData);
|
||||
bool changingData = lastData != commitData;
|
||||
|
||||
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
textEvent.theText = commitData;
|
||||
textEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
|
||||
MaybeDispatchCompositionUpdate(&textEvent);
|
||||
|
||||
// If changing the data or committing string isn't empty, we need to
|
||||
// dispatch text event for setting the composition string without
|
||||
// IME selection.
|
||||
if (!Destroyed() && !widget->Destroyed() &&
|
||||
(changingData || !commitData.IsEmpty())) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
widget->DispatchEvent(&textEvent, status);
|
||||
}
|
||||
|
||||
if (!Destroyed() && !widget->Destroyed()) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
|
||||
endEvent.data = commitData;
|
||||
endEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
widget->DispatchEvent(&endEvent, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRequestedToCommitOrCancel = true;
|
||||
|
||||
// If the request is performed synchronously, this must be already destroyed.
|
||||
if (Destroyed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Otherwise, synthesize the commit in content.
|
||||
nsAutoString data(aDiscard ? EmptyString() : lastData);
|
||||
bool changingData = lastData != data;
|
||||
if (changingData) {
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data, true);
|
||||
}
|
||||
// If the last composition string and new data are different, we need to
|
||||
// dispatch text event for removing IME selection. However, if the commit
|
||||
// string is empty string and it's not changed from the last data, we don't
|
||||
// need to dispatch text event.
|
||||
if (changingData || !data.IsEmpty()) {
|
||||
DispatchCompositionEventRunnable(NS_TEXT_TEXT, data, true);
|
||||
}
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_END, data, true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -203,51 +419,74 @@ TextComposition::HasEditor() const
|
||||
******************************************************************************/
|
||||
|
||||
TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
|
||||
nsPresContext* aPresContext,
|
||||
TextComposition* aComposition,
|
||||
nsINode* aEventTarget,
|
||||
uint32_t aEventMessage,
|
||||
const nsAString& aData) :
|
||||
mPresContext(aPresContext), mEventTarget(aEventTarget),
|
||||
mEventMessage(aEventMessage), mData(aData)
|
||||
const nsAString& aData,
|
||||
bool aIsSynthesizedEvent)
|
||||
: mTextComposition(aComposition)
|
||||
, mEventTarget(aEventTarget)
|
||||
, mEventMessage(aEventMessage)
|
||||
, mData(aData)
|
||||
, mIsSynthesizedEvent(aIsSynthesizedEvent)
|
||||
{
|
||||
mWidget = mPresContext->GetRootWidget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TextComposition::CompositionEventDispatcher::Run()
|
||||
{
|
||||
if (!mPresContext->GetPresShell() ||
|
||||
mPresContext->GetPresShell()->IsDestroying()) {
|
||||
nsRefPtr<nsPresContext> presContext = mTextComposition->mPresContext;
|
||||
if (!presContext || !presContext->GetPresShell() ||
|
||||
presContext->GetPresShell()->IsDestroying()) {
|
||||
return NS_OK; // cannot dispatch any events anymore
|
||||
}
|
||||
|
||||
// The widget can be different from the widget which has dispatched
|
||||
// composition events because GetWidget() returns a widget which is proper
|
||||
// for calling NotifyIME(). However, this must no be problem since both
|
||||
// widget should share native IME context. Therefore, even if an event
|
||||
// handler uses the widget for requesting IME to commit or cancel, it works.
|
||||
nsCOMPtr<nsIWidget> widget(mTextComposition->GetWidget());
|
||||
if (NS_WARN_IF(!widget)) {
|
||||
return NS_OK; // cannot dispatch any events anymore
|
||||
}
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
switch (mEventMessage) {
|
||||
case NS_COMPOSITION_START: {
|
||||
WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
|
||||
WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, widget);
|
||||
WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT,
|
||||
mWidget);
|
||||
ContentEventHandler handler(mPresContext);
|
||||
widget);
|
||||
ContentEventHandler handler(presContext);
|
||||
handler.OnQuerySelectedText(&selectedText);
|
||||
NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
|
||||
compStart.data = selectedText.mReply.mString;
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
|
||||
&compStart, &status, nullptr);
|
||||
compStart.mFlags.mIsSynthesizedForTests =
|
||||
mTextComposition->IsSynthesizedForTests();
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
|
||||
&compStart, &status, nullptr,
|
||||
mIsSynthesizedEvent);
|
||||
break;
|
||||
}
|
||||
case NS_COMPOSITION_UPDATE:
|
||||
case NS_COMPOSITION_END: {
|
||||
WidgetCompositionEvent compEvent(true, mEventMessage, mWidget);
|
||||
WidgetCompositionEvent compEvent(true, mEventMessage, widget);
|
||||
compEvent.data = mData;
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
|
||||
&compEvent, &status, nullptr);
|
||||
compEvent.mFlags.mIsSynthesizedForTests =
|
||||
mTextComposition->IsSynthesizedForTests();
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
|
||||
&compEvent, &status, nullptr,
|
||||
mIsSynthesizedEvent);
|
||||
break;
|
||||
}
|
||||
case NS_TEXT_TEXT: {
|
||||
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
|
||||
WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
textEvent.theText = mData;
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
|
||||
&textEvent, &status, nullptr);
|
||||
textEvent.mFlags.mIsSynthesizedForTests =
|
||||
mTextComposition->IsSynthesizedForTests();
|
||||
IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
|
||||
&textEvent, &status, nullptr,
|
||||
mIsSynthesizedEvent);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -60,6 +60,11 @@ public:
|
||||
// XXX We should return |const TextRangeArray*| here, but it causes compile
|
||||
// error due to inaccessible Release() method.
|
||||
TextRangeArray* GetRanges() const { return mRanges; }
|
||||
// Returns the widget which is proper to call NotifyIME().
|
||||
nsIWidget* GetWidget() const
|
||||
{
|
||||
return mPresContext ? mPresContext->GetRootWidget() : nullptr;
|
||||
}
|
||||
// Returns true if the composition is started with synthesized event which
|
||||
// came from nsDOMWindowUtils.
|
||||
bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
|
||||
@ -72,12 +77,10 @@ public:
|
||||
void Destroy();
|
||||
|
||||
/**
|
||||
* SynthesizeCommit() dispatches compositionupdate, text and compositionend
|
||||
* events for emulating commit on the content.
|
||||
*
|
||||
* @param aDiscard true when committing with empty string. Otherwise, false.
|
||||
* Request to commit (or cancel) the composition to IME. This method should
|
||||
* be called only by IMEStateManager::NotifyIME().
|
||||
*/
|
||||
void SynthesizeCommit(bool aDiscard);
|
||||
nsresult RequestToCommit(nsIWidget* aWidget, bool aDiscard);
|
||||
|
||||
/**
|
||||
* Send a notification to IME. It depends on the IME or platform spec what
|
||||
@ -195,6 +198,24 @@ private:
|
||||
// string.
|
||||
bool mIsEditorHandlingEvent;
|
||||
|
||||
// mIsRequestingCommit or mIsRequestingCancel is true *only* while we're
|
||||
// requesting commit or canceling the composition. In other words, while
|
||||
// one of these values is true, we're handling the request.
|
||||
bool mIsRequestingCommit;
|
||||
bool mIsRequestingCancel;
|
||||
|
||||
// mRequestedToCommitOrCancel is true *after* we requested IME to commit or
|
||||
// cancel the composition. In other words, we already requested of IME that
|
||||
// it commits or cancels current composition.
|
||||
// NOTE: Before this is set true, both mIsRequestingCommit and
|
||||
// mIsRequestingCancel are set false.
|
||||
bool mRequestedToCommitOrCancel;
|
||||
|
||||
// mWasNativeCompositionEndEventDiscarded is true if this composition was
|
||||
// requested commit or cancel itself but native compositionend event is
|
||||
// discarded by PresShell due to not safe to dispatch events.
|
||||
bool mWasNativeCompositionEndEventDiscarded;
|
||||
|
||||
// Hide the default constructor and copy constructor.
|
||||
TextComposition() {}
|
||||
TextComposition(const TextComposition& aOther);
|
||||
@ -228,7 +249,32 @@ private:
|
||||
*/
|
||||
void DispatchEvent(WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventDispatchingCallback* aCallBack);
|
||||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized);
|
||||
|
||||
/**
|
||||
* MaybeDispatchCompositionUpdate() may dispatch a compositionupdate event
|
||||
* if aEvent changes composition string.
|
||||
* @return Returns false if dispatching the compositionupdate event caused
|
||||
* destroying this composition.
|
||||
*/
|
||||
bool MaybeDispatchCompositionUpdate(const WidgetTextEvent* aEvent);
|
||||
|
||||
/**
|
||||
* If IME has already dispatched compositionend event but it was discarded
|
||||
* by PresShell due to not safe to dispatch, this returns true.
|
||||
*/
|
||||
bool WasNativeCompositionEndEventDiscarded() const
|
||||
{
|
||||
return mWasNativeCompositionEndEventDiscarded;
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCompositionEventDiscarded() is called when PresShell discards
|
||||
* compositionupdate, compositionend or text event due to not safe to
|
||||
* dispatch event.
|
||||
*/
|
||||
void OnCompositionEventDiscarded(const WidgetGUIEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Calculate composition offset then notify composition update to widget
|
||||
@ -242,18 +288,19 @@ private:
|
||||
class CompositionEventDispatcher : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CompositionEventDispatcher(nsPresContext* aPresContext,
|
||||
CompositionEventDispatcher(TextComposition* aTextComposition,
|
||||
nsINode* aEventTarget,
|
||||
uint32_t aEventMessage,
|
||||
const nsAString& aData);
|
||||
const nsAString& aData,
|
||||
bool aIsSynthesizedEvent = false);
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsRefPtr<nsPresContext> mPresContext;
|
||||
nsRefPtr<TextComposition> mTextComposition;
|
||||
nsCOMPtr<nsINode> mEventTarget;
|
||||
nsCOMPtr<nsIWidget> mWidget;
|
||||
uint32_t mEventMessage;
|
||||
nsString mData;
|
||||
bool mIsSynthesizedEvent;
|
||||
|
||||
CompositionEventDispatcher() {};
|
||||
};
|
||||
@ -270,9 +317,13 @@ private:
|
||||
* NS_COMPOSITION_UPDATE or NS_COMPOSITION_END.
|
||||
* Used for theText value if aEventMessage is
|
||||
* NS_TEXT_TEXT.
|
||||
* @param aIsSynthesizingCommit true if this is called for synthesizing
|
||||
* commit or cancel composition. Otherwise,
|
||||
* false.
|
||||
*/
|
||||
void DispatchCompositionEventRunnable(uint32_t aEventMessage,
|
||||
const nsAString& aData);
|
||||
const nsAString& aData,
|
||||
bool aIsSynthesizingCommit = false);
|
||||
};
|
||||
|
||||
/**
|
||||
|
18
dom/events/crashtests/1072137-1.html
Normal file
18
dom/events/crashtests/1072137-1.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var textareaRoot = document.createElement("textarea");
|
||||
document.removeChild(document.documentElement);
|
||||
document.appendChild(textareaRoot);
|
||||
var input = document.createElement("input");
|
||||
textareaRoot.appendChild(input);
|
||||
input.select();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
@ -8,6 +8,7 @@ load 682637-1.html
|
||||
load 1033343.html
|
||||
load 1035654-1.html
|
||||
load 1035654-2.html
|
||||
needs-focus load 1072137-1.html
|
||||
load eventctor-nulldictionary.html
|
||||
load eventctor-nullstorage.html
|
||||
load recursive-onload.html
|
||||
|
@ -1549,6 +1549,8 @@ TabChild::DestroyWindow()
|
||||
void
|
||||
TabChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
DestroyWindow();
|
||||
|
||||
if (mTabChildGlobal) {
|
||||
// The messageManager relays messages via the TabChild which
|
||||
// no longer exists.
|
||||
|
@ -4,7 +4,9 @@
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIEditingSession.idl',
|
||||
|
@ -1,10 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
||||
|
@ -4,7 +4,12 @@
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'tests/browserscope/mochitest.ini',
|
||||
'tests/mochitest.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChangeAttributeTxn.cpp',
|
||||
|
@ -2074,19 +2074,8 @@ nsEditor::ForceCompositionEnd()
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mComposition) {
|
||||
// XXXmnakano see bug 558976, ResetInputState() has two meaning which are
|
||||
// "commit the composition" and "cursor is moved". This method name is
|
||||
// "ForceCompositionEnd", so, ResetInputState() should be used only for the
|
||||
// former here. However, ResetInputState() is also used for the latter here
|
||||
// because even if we don't have composition, we call ResetInputState() on
|
||||
// Linux. Currently, nsGtkIMModule can know the timing of the cursor move,
|
||||
// so, the latter meaning should be gone.
|
||||
// XXX This may commit a composition in another editor.
|
||||
return IMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc);
|
||||
}
|
||||
|
||||
return IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc);
|
||||
return mComposition ?
|
||||
IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc) : NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -859,14 +859,24 @@ nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
|
||||
nsresult rv = GetSelection(getter_AddRefs(selection));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// NOTE: TextComposition should receive selection change notification before
|
||||
// TextEventHandlingMarker notifies TextComposition of the end of
|
||||
// handling TextEvent because TextComposition may need to ignore
|
||||
// selection changes caused by composition. Therefore,
|
||||
// TextEventHandlingMarker must be destroyed after a call of
|
||||
// NotifiyEditorObservers(eNotifyEditorObserversOfEnd) or
|
||||
// NotifiyEditorObservers(eNotifyEditorObserversOfCancel) which notifies
|
||||
// TextComposition of a selection change.
|
||||
MOZ_ASSERT(!mPlaceHolderBatch,
|
||||
"UpdateIMEComposition() must be called without place holder batch");
|
||||
TextComposition::TextEventHandlingMarker
|
||||
textEventHandlingMarker(mComposition, widgetTextEvent);
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
|
||||
|
||||
nsRefPtr<nsCaret> caretP = ps->GetCaret();
|
||||
|
||||
{
|
||||
TextComposition::TextEventHandlingMarker
|
||||
textEventHandlingMarker(mComposition, widgetTextEvent);
|
||||
|
||||
nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
|
||||
|
||||
rv = InsertText(widgetTextEvent->theText);
|
||||
|
@ -1,11 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['browserscope/mochitest.ini',
|
||||
'mochitest.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
||||
|
@ -388,7 +388,10 @@ DrawTargetD2D::DrawFilter(FilterNode *aNode,
|
||||
hr = rt->QueryInterface((ID2D1DeviceContext**)byRef(dc));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
dc->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
||||
FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
|
||||
node->WillDraw(this);
|
||||
|
||||
dc->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
||||
|
||||
Rect destRect = aSourceRect;
|
||||
destRect.MoveBy(aDestPoint);
|
||||
@ -1320,7 +1323,7 @@ DrawTargetD2D::CreateFilter(FilterType aType)
|
||||
HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
return FilterNodeD2D1::Create(this, dc, aType);
|
||||
return FilterNodeD2D1::Create(dc, aType);
|
||||
}
|
||||
#endif
|
||||
return FilterNodeSoftware::Create(aType);
|
||||
|
@ -155,7 +155,10 @@ DrawTargetD2D1::DrawFilter(FilterNode *aNode,
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
mDC->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
||||
FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
|
||||
node->WillDraw(this);
|
||||
|
||||
mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
||||
|
||||
FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
||||
}
|
||||
@ -701,7 +704,7 @@ DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops,
|
||||
TemporaryRef<FilterNode>
|
||||
DrawTargetD2D1::CreateFilter(FilterType aType)
|
||||
{
|
||||
return FilterNodeD2D1::Create(this, mDC, aType);
|
||||
return FilterNodeD2D1::Create(mDC, aType);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -523,10 +523,10 @@ static inline REFCLSID GetCLDIDForFilterType(FilterType aType)
|
||||
|
||||
/* static */
|
||||
TemporaryRef<FilterNode>
|
||||
FilterNodeD2D1::Create(DrawTarget* aDT, ID2D1DeviceContext *aDC, FilterType aType)
|
||||
FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
|
||||
{
|
||||
if (aType == FilterType::CONVOLVE_MATRIX) {
|
||||
return new FilterNodeConvolveD2D1(aDT, aDC);
|
||||
return new FilterNodeConvolveD2D1(aDC);
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Effect> effect;
|
||||
@ -544,9 +544,9 @@ FilterNodeD2D1::Create(DrawTarget* aDT, ID2D1DeviceContext *aDC, FilterType aTyp
|
||||
case FilterType::GAMMA_TRANSFER:
|
||||
case FilterType::TABLE_TRANSFER:
|
||||
case FilterType::DISCRETE_TRANSFER:
|
||||
return new FilterNodeComponentTransferD2D1(aDT, aDC, effect, aType);
|
||||
return new FilterNodeComponentTransferD2D1(aDC, effect, aType);
|
||||
default:
|
||||
return new FilterNodeD2D1(aDT, effect, aType);
|
||||
return new FilterNodeD2D1(effect, aType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,8 +579,23 @@ FilterNodeD2D1::SetInput(uint32_t aIndex, SourceSurface *aSurface)
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Image> image = GetImageForSourceSurface(mDT, aSurface);
|
||||
effect->SetInput(input, image);
|
||||
MOZ_ASSERT(input < effect->GetInputCount());
|
||||
|
||||
mInputSurfaces.resize(effect->GetInputCount());
|
||||
mInputFilters.resize(effect->GetInputCount());
|
||||
|
||||
// In order to convert aSurface into an ID2D1Image, we need to know what
|
||||
// DrawTarget we paint into. However, the same FilterNode object can be
|
||||
// used on different DrawTargets, so we need to hold on to the SourceSurface
|
||||
// objects and delay the conversion until we're actually painted and know
|
||||
// our target DrawTarget.
|
||||
// The conversion happens in WillDraw().
|
||||
|
||||
mInputSurfaces[input] = aSurface;
|
||||
mInputFilters[input] = nullptr;
|
||||
|
||||
// Clear the existing image from the effect.
|
||||
effect->SetInput(input, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
@ -599,15 +614,47 @@ FilterNodeD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(input < effect->GetInputCount());
|
||||
MOZ_ASSERT(input < effect->GetInputCount());
|
||||
|
||||
if (aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
|
||||
gfxWarning() << "Unknown input SourceSurface set on effect.";
|
||||
if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
|
||||
gfxWarning() << "Unknown input FilterNode set on effect.";
|
||||
MOZ_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
effect->SetInputEffect(input, static_cast<FilterNodeD2D1*>(aFilter)->OutputEffect());
|
||||
FilterNodeD2D1* filter = static_cast<FilterNodeD2D1*>(aFilter);
|
||||
|
||||
mInputSurfaces.resize(effect->GetInputCount());
|
||||
mInputFilters.resize(effect->GetInputCount());
|
||||
|
||||
// We hold on to the FilterNode object so that we can call WillDraw() on it.
|
||||
mInputSurfaces[input] = nullptr;
|
||||
mInputFilters[input] = filter;
|
||||
|
||||
if (filter) {
|
||||
effect->SetInputEffect(input, filter->OutputEffect());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterNodeD2D1::WillDraw(DrawTarget *aDT)
|
||||
{
|
||||
// Convert input SourceSurfaces into ID2D1Images and set them on the effect.
|
||||
for (size_t inputIndex = 0; inputIndex < mInputSurfaces.size(); inputIndex++) {
|
||||
if (mInputSurfaces[inputIndex]) {
|
||||
ID2D1Effect* effect = InputEffect();
|
||||
RefPtr<ID2D1Image> image = GetImageForSourceSurface(aDT, mInputSurfaces[inputIndex]);
|
||||
effect->SetInput(inputIndex, image);
|
||||
}
|
||||
}
|
||||
|
||||
// Call WillDraw() on our input filters.
|
||||
for (std::vector<RefPtr<FilterNodeD2D1>>::iterator it = mInputFilters.begin();
|
||||
it != mInputFilters.end(); it++) {
|
||||
if (*it) {
|
||||
(*it)->WillDraw(aDT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -771,8 +818,8 @@ FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
|
||||
mEffect->SetValue(input, D2DMatrix(aMatrix));
|
||||
}
|
||||
|
||||
FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(DrawTarget *aDT, ID2D1DeviceContext *aDC)
|
||||
: FilterNodeD2D1(aDT, nullptr, FilterType::CONVOLVE_MATRIX)
|
||||
FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
|
||||
: FilterNodeD2D1(nullptr, FilterType::CONVOLVE_MATRIX)
|
||||
, mEdgeMode(EDGE_MODE_DUPLICATE)
|
||||
{
|
||||
// Correctly handling the interaction of edge mode and source rect is a bit
|
||||
@ -845,31 +892,10 @@ FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(DrawTarget *aDT, ID2D1DeviceConte
|
||||
UpdateSourceRect();
|
||||
}
|
||||
|
||||
void
|
||||
FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, SourceSurface *aSurface)
|
||||
{
|
||||
MOZ_ASSERT(aIndex == 0);
|
||||
|
||||
mInput = GetImageForSourceSurface(mDT, aSurface);
|
||||
|
||||
mInputEffect = nullptr;
|
||||
|
||||
UpdateChain();
|
||||
}
|
||||
|
||||
void
|
||||
FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, FilterNode *aFilter)
|
||||
{
|
||||
MOZ_ASSERT(aIndex == 0);
|
||||
|
||||
if (aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
|
||||
gfxWarning() << "Unknown input SourceSurface set on effect.";
|
||||
MOZ_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
mInput = nullptr;
|
||||
mInputEffect = static_cast<FilterNodeD2D1*>(aFilter)->mEffect;
|
||||
FilterNodeD2D1::SetInput(aIndex, aFilter);
|
||||
|
||||
UpdateChain();
|
||||
}
|
||||
@ -886,6 +912,12 @@ FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
|
||||
UpdateChain();
|
||||
}
|
||||
|
||||
ID2D1Effect*
|
||||
FilterNodeConvolveD2D1::InputEffect()
|
||||
{
|
||||
return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mCompositeEffect.get();
|
||||
}
|
||||
|
||||
void
|
||||
FilterNodeConvolveD2D1::UpdateChain()
|
||||
{
|
||||
@ -897,19 +929,18 @@ FilterNodeConvolveD2D1::UpdateChain()
|
||||
// EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP:
|
||||
// input -------v
|
||||
// flood --> composite --> crop --> border --> convolvematrix
|
||||
//
|
||||
// mEffect is convolvematrix.
|
||||
|
||||
ID2D1Effect *firstEffect = mCompositeEffect;
|
||||
if (mEdgeMode == EDGE_MODE_NONE) {
|
||||
firstEffect = mEffect;
|
||||
} else {
|
||||
if (mEdgeMode != EDGE_MODE_NONE) {
|
||||
mEffect->SetInputEffect(0, mBorderEffect.get());
|
||||
}
|
||||
|
||||
if (mInputEffect) {
|
||||
firstEffect->SetInputEffect(0, mInputEffect);
|
||||
} else {
|
||||
firstEffect->SetInput(0, mInput);
|
||||
RefPtr<ID2D1Effect> inputEffect;
|
||||
if (mInputFilters.size() > 0 && mInputFilters[0]) {
|
||||
inputEffect = mInputFilters[0]->OutputEffect();
|
||||
}
|
||||
InputEffect()->SetInputEffect(0, inputEffect);
|
||||
|
||||
if (mEdgeMode == EDGE_MODE_DUPLICATE) {
|
||||
mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X, D2D1_BORDER_EDGE_MODE_CLAMP);
|
||||
@ -980,9 +1011,9 @@ FilterNodeConvolveD2D1::UpdateSourceRect()
|
||||
Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
|
||||
}
|
||||
|
||||
FilterNodeComponentTransferD2D1::FilterNodeComponentTransferD2D1(DrawTarget *aDT, ID2D1DeviceContext *aDC,
|
||||
FilterNodeComponentTransferD2D1::FilterNodeComponentTransferD2D1(ID2D1DeviceContext *aDC,
|
||||
ID2D1Effect *aEffect, FilterType aType)
|
||||
: FilterNodeD2D1(aDT, aEffect, aType)
|
||||
: FilterNodeD2D1(aEffect, aType)
|
||||
{
|
||||
// D2D1 component transfer effects do strange things when it comes to
|
||||
// premultiplication.
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
#include <vector>
|
||||
#include <d2d1_1.h>
|
||||
#include <cguid.h>
|
||||
|
||||
@ -18,11 +19,10 @@ class FilterNodeD2D1 : public FilterNode
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeD2D1)
|
||||
static TemporaryRef<FilterNode> Create(DrawTarget* aDT, ID2D1DeviceContext *aDC, FilterType aType);
|
||||
static TemporaryRef<FilterNode> Create(ID2D1DeviceContext *aDC, FilterType aType);
|
||||
|
||||
FilterNodeD2D1(DrawTarget* aDT, ID2D1Effect *aEffect, FilterType aType)
|
||||
: mDT(aDT)
|
||||
, mEffect(aEffect)
|
||||
FilterNodeD2D1(ID2D1Effect *aEffect, FilterType aType)
|
||||
: mEffect(aEffect)
|
||||
, mType(aType)
|
||||
{
|
||||
InitUnmappedProperties();
|
||||
@ -48,6 +48,12 @@ public:
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue);
|
||||
|
||||
// Called by DrawTarget before it draws our OutputEffect, and recursively
|
||||
// by the filter nodes that have this filter as one of their inputs. This
|
||||
// gives us a chance to convert any input surfaces to the target format for
|
||||
// the DrawTarget that we will draw to.
|
||||
virtual void WillDraw(DrawTarget *aDT);
|
||||
|
||||
protected:
|
||||
friend class DrawTargetD2D1;
|
||||
friend class DrawTargetD2D;
|
||||
@ -58,8 +64,9 @@ protected:
|
||||
|
||||
void InitUnmappedProperties();
|
||||
|
||||
RefPtr<DrawTarget> mDT;
|
||||
RefPtr<ID2D1Effect> mEffect;
|
||||
std::vector<RefPtr<FilterNodeD2D1>> mInputFilters;
|
||||
std::vector<RefPtr<SourceSurface>> mInputSurfaces;
|
||||
FilterType mType;
|
||||
};
|
||||
|
||||
@ -67,9 +74,8 @@ class FilterNodeConvolveD2D1 : public FilterNodeD2D1
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveD2D1)
|
||||
FilterNodeConvolveD2D1(DrawTarget *aDT, ID2D1DeviceContext *aDC);
|
||||
FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC);
|
||||
|
||||
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface);
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter);
|
||||
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue);
|
||||
@ -77,13 +83,14 @@ public:
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
|
||||
|
||||
protected:
|
||||
virtual ID2D1Effect* InputEffect();
|
||||
|
||||
private:
|
||||
void UpdateChain();
|
||||
void UpdateOffset();
|
||||
void UpdateSourceRect();
|
||||
|
||||
RefPtr<ID2D1Image> mInput;
|
||||
RefPtr<ID2D1Effect> mInputEffect;
|
||||
RefPtr<ID2D1Effect> mFloodEffect;
|
||||
RefPtr<ID2D1Effect> mCompositeEffect;
|
||||
RefPtr<ID2D1Effect> mCropEffect;
|
||||
@ -98,7 +105,7 @@ class FilterNodeComponentTransferD2D1 : public FilterNodeD2D1
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferD2D1)
|
||||
FilterNodeComponentTransferD2D1(DrawTarget *aDT, ID2D1DeviceContext *aDC, ID2D1Effect *aEffect, FilterType aType);
|
||||
FilterNodeComponentTransferD2D1(ID2D1DeviceContext *aDC, ID2D1Effect *aEffect, FilterType aType);
|
||||
|
||||
protected:
|
||||
virtual ID2D1Effect* InputEffect() MOZ_OVERRIDE { return mPrePremultiplyEffect.get(); }
|
||||
|
@ -932,7 +932,7 @@ FilterNodeSoftware::~FilterNodeSoftware()
|
||||
void
|
||||
FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
|
||||
{
|
||||
if (aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
|
||||
if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
|
||||
MOZ_ASSERT(false, "can only take software filters as inputs");
|
||||
return;
|
||||
}
|
||||
@ -955,10 +955,8 @@ FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
|
||||
MOZ_CRASH();
|
||||
return;
|
||||
}
|
||||
if ((uint32_t)inputIndex >= mInputSurfaces.size()) {
|
||||
if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
|
||||
mInputSurfaces.resize(inputIndex + 1);
|
||||
}
|
||||
if ((uint32_t)inputIndex >= mInputFilters.size()) {
|
||||
mInputFilters.resize(inputIndex + 1);
|
||||
}
|
||||
mInputSurfaces[inputIndex] = aSurface;
|
||||
@ -969,6 +967,10 @@ FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
|
||||
aFilter->AddInvalidationListener(this);
|
||||
}
|
||||
mInputFilters[inputIndex] = aFilter;
|
||||
if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
|
||||
mInputSurfaces.resize(inputIndex);
|
||||
mInputFilters.resize(inputIndex);
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
@ -105,6 +105,7 @@ static const char *sExtensionNames[] = {
|
||||
"GL_EXT_color_buffer_half_float",
|
||||
"GL_EXT_copy_texture",
|
||||
"GL_EXT_draw_buffers",
|
||||
"GL_EXT_draw_buffers2",
|
||||
"GL_EXT_draw_instanced",
|
||||
"GL_EXT_draw_range_elements",
|
||||
"GL_EXT_frag_depth",
|
||||
@ -964,7 +965,6 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
||||
{ (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVarying", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedback", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedback", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegeri_v", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
@ -980,7 +980,6 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
||||
{ (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVaryingEXT", "GetTransformFeedbackVaryingNV", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedbackNV", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedbackNV", nullptr } },
|
||||
{ (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegerIndexedvEXT", "GetIntegerIndexedvNV", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
@ -1149,6 +1148,41 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSupported(GLFeature::get_integer_indexed)) {
|
||||
SymLoadStruct coreSymbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegeri_v", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
SymLoadStruct extSymbols[] ={
|
||||
{ (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegerIndexedvEXT", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::get_integer_indexed);
|
||||
|
||||
if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
|
||||
NS_ERROR("GL supports get_integer_indexed without supplying its functions.");
|
||||
|
||||
MarkUnsupported(GLFeature::get_integer_indexed);
|
||||
ClearSymbols(coreSymbols);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSupported(GLFeature::get_integer64_indexed)) {
|
||||
SymLoadStruct coreSymbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fGetInteger64i_v, { "GetInteger64i_v", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
|
||||
if (!LoadSymbols(coreSymbols, trygl, prefix)) {
|
||||
NS_ERROR("GL supports get_integer64_indexed without supplying its functions.");
|
||||
|
||||
MarkUnsupported(GLFeature::get_integer64_indexed);
|
||||
ClearSymbols(coreSymbols);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSupported(GLFeature::gpu_shader4)) {
|
||||
SymLoadStruct gpuShader4Symbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fVertexAttribI4i, { "VertexAttribI4i", "VertexAttribI4iEXT", nullptr } },
|
||||
|
@ -94,6 +94,8 @@ MOZ_BEGIN_ENUM_CLASS(GLFeature)
|
||||
framebuffer_blit,
|
||||
framebuffer_multisample,
|
||||
framebuffer_object,
|
||||
get_integer_indexed,
|
||||
get_integer64_indexed,
|
||||
get_query_object_iv,
|
||||
gpu_shader4,
|
||||
instanced_arrays,
|
||||
@ -385,6 +387,7 @@ public:
|
||||
EXT_color_buffer_half_float,
|
||||
EXT_copy_texture,
|
||||
EXT_draw_buffers,
|
||||
EXT_draw_buffers2,
|
||||
EXT_draw_instanced,
|
||||
EXT_draw_range_elements,
|
||||
EXT_frag_depth,
|
||||
@ -2788,6 +2791,12 @@ public:
|
||||
AFTER_GL_CALL;
|
||||
}
|
||||
|
||||
void fGetInteger64i_v(GLenum target, GLuint index, GLint64* data) {
|
||||
ASSERT_SYMBOL_PRESENT(fGetInteger64i_v);
|
||||
BEFORE_GL_CALL;
|
||||
mSymbols.fGetInteger64i_v(target, index, data);
|
||||
AFTER_GL_CALL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Package XXX_vertex_array_object
|
||||
|
@ -219,6 +219,25 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"get_integer_indexed",
|
||||
300, // OpenGL version
|
||||
300, // OpenGL ES version
|
||||
GLContext::Extension_None,
|
||||
{
|
||||
GLContext::EXT_draw_buffers2,
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"get_integer64_indexed",
|
||||
320, // OpenGL version
|
||||
300, // OpenGL ES version
|
||||
GLContext::Extension_None,
|
||||
{
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"get_query_object_iv",
|
||||
200, // OpenGL version
|
||||
|
@ -503,6 +503,8 @@ struct GLContextSymbols
|
||||
|
||||
typedef void (GLAPIENTRY * PFNGLGETINTEGERI_V) (GLenum param, GLuint index, GLint* values);
|
||||
PFNGLGETINTEGERI_V fGetIntegeri_v;
|
||||
typedef void (GLAPIENTRY * PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64* data);
|
||||
PFNGLGETINTEGER64I_VPROC fGetInteger64i_v;
|
||||
|
||||
// EXT_transform_feedback only
|
||||
typedef void (GLAPIENTRY * PFNGLBINDBUFFEROFFSET) (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
|
||||
|
@ -296,6 +296,7 @@ private:
|
||||
DECL_GFX_PREF(Once, "layout.css.touch_action.enabled", TouchActionEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1);
|
||||
DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false);
|
||||
DECL_GFX_PREF(Live, "layout.event-regions.enabled", LayoutEventRegionsEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layout.paint_rects_separately", LayoutPaintRectsSeparately, bool, true);
|
||||
|
||||
DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
|
||||
|
@ -6,5 +6,11 @@
|
||||
|
||||
DIRS += ['public', 'src', 'decoders', 'encoders']
|
||||
DIRS += ['build']
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
|
@ -1,8 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
@ -1,10 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
||||
|
@ -1,9 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['mochitest', 'browser']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
@ -4,7 +4,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIStringBundle.idl',
|
||||
|
@ -1,7 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
@ -87,8 +87,7 @@ template<typename Elem>
|
||||
static Elem
|
||||
TypedObjectMemory(HandleValue v)
|
||||
{
|
||||
TypedObject &obj = v.toObject().as<TypedObject>();
|
||||
MOZ_ASSERT(!obj.owner().isNeutered());
|
||||
OwnedTypedObject &obj = v.toObject().as<OwnedTypedObject>();
|
||||
return reinterpret_cast<Elem>(obj.typedMem());
|
||||
}
|
||||
|
||||
@ -139,7 +138,7 @@ static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
TypedObject &typedObj = args.thisv().toObject().as<TypedObject>();
|
||||
OwnedTypedObject &typedObj = args.thisv().toObject().as<OwnedTypedObject>();
|
||||
TypeDescr &descr = typedObj.typeDescr();
|
||||
if (descr.kind() != type::Simd || descr.as<SimdTypeDescr>().type() != SimdType::type) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
@ -148,7 +147,6 @@ static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!typedObj.owner().isNeutered());
|
||||
Elem *data = reinterpret_cast<Elem *>(typedObj.typedMem());
|
||||
int32_t mx = data[0] < 0.0 ? 1 : 0;
|
||||
int32_t my = data[1] < 0.0 ? 1 : 0;
|
||||
@ -324,11 +322,10 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0));
|
||||
Rooted<TypedObject*> result(cx, OwnedTypedObject::createZeroed(cx, descr, 0));
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(!result->owner().isNeutered());
|
||||
switch (descr->type()) {
|
||||
case SimdTypeDescr::TYPE_INT32: {
|
||||
int32_t *mem = reinterpret_cast<int32_t*>(result->typedMem());
|
||||
@ -720,11 +717,10 @@ js::CreateSimd(JSContext *cx, typename V::Elem *data)
|
||||
Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
|
||||
JS_ASSERT(typeDescr);
|
||||
|
||||
Rooted<TypedObject *> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
|
||||
Rooted<TypedObject *> result(cx, OwnedTypedObject::createZeroed(cx, typeDescr, 0));
|
||||
if (!result)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(!result->owner().isNeutered());
|
||||
Elem *resultMem = reinterpret_cast<Elem *>(result->typedMem());
|
||||
memcpy(resultMem, data, sizeof(Elem) * V::lanes);
|
||||
return result;
|
||||
|
@ -519,7 +519,7 @@ const Class UnsizedArrayTypeDescr::class_ = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
TypedObject::constructUnsized,
|
||||
OwnedTypedObject::constructUnsized,
|
||||
nullptr
|
||||
};
|
||||
|
||||
@ -536,7 +536,7 @@ const Class SizedArrayTypeDescr::class_ = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
TypedObject::constructSized,
|
||||
OwnedTypedObject::constructSized,
|
||||
nullptr
|
||||
};
|
||||
|
||||
@ -833,7 +833,7 @@ const Class StructTypeDescr::class_ = {
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
TypedObject::constructSized,
|
||||
OwnedTypedObject::constructSized,
|
||||
nullptr /* trace */
|
||||
};
|
||||
|
||||
@ -1463,44 +1463,124 @@ js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
|
||||
* Typed objects
|
||||
*/
|
||||
|
||||
/*static*/ TypedObject *
|
||||
TypedObject::createUnattached(JSContext *cx,
|
||||
HandleTypeDescr descr,
|
||||
int32_t length)
|
||||
int32_t
|
||||
TypedObject::offset() const
|
||||
{
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return 0;
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET).toInt32();
|
||||
}
|
||||
|
||||
int32_t
|
||||
TypedObject::length() const
|
||||
{
|
||||
JS_ASSERT(typeDescr().kind() == type::SizedArray ||
|
||||
typeDescr().kind() == type::UnsizedArray);
|
||||
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return typeDescr().as<SizedArrayTypeDescr>().length();
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_LENGTH).toInt32();
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
TypedObject::typedMem() const
|
||||
{
|
||||
JS_ASSERT(isAttached());
|
||||
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return as<InlineOpaqueTypedObject>().inlineTypedMem();
|
||||
return as<OwnedTypedObject>().outOfLineTypedMem();
|
||||
}
|
||||
|
||||
bool
|
||||
TypedObject::isAttached() const
|
||||
{
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return true;
|
||||
if (!as<OwnedTypedObject>().outOfLineTypedMem())
|
||||
return false;
|
||||
JSObject &owner = as<OwnedTypedObject>().owner();
|
||||
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isNeutered())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypedObject::maybeForwardedIsAttached() const
|
||||
{
|
||||
if (is<InlineOpaqueTypedObject>())
|
||||
return true;
|
||||
if (!as<OwnedTypedObject>().outOfLineTypedMem())
|
||||
return false;
|
||||
JSObject &owner = *MaybeForwarded(&as<OwnedTypedObject>().owner());
|
||||
if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isNeutered())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TypedObject::GetBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setObject(args[0].toObject().as<TransparentTypedObject>().owner());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TypedObject::GetByteOffset(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setInt32(args[0].toObject().as<TypedObject>().offset());
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Owned typed objects
|
||||
*/
|
||||
|
||||
/*static*/ OwnedTypedObject *
|
||||
OwnedTypedObject::createUnattached(JSContext *cx,
|
||||
HandleTypeDescr descr,
|
||||
int32_t length)
|
||||
{
|
||||
if (descr->opaque())
|
||||
return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
|
||||
return createUnattachedWithClass(cx, &OwnedOpaqueTypedObject::class_, descr, length);
|
||||
else
|
||||
return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ TypedObject *
|
||||
TypedObject::createUnattachedWithClass(JSContext *cx,
|
||||
const Class *clasp,
|
||||
HandleTypeDescr type,
|
||||
int32_t length)
|
||||
static JSObject *
|
||||
PrototypeForTypeDescr(JSContext *cx, HandleTypeDescr descr)
|
||||
{
|
||||
JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
|
||||
clasp == &OpaqueTypedObject::class_);
|
||||
JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS);
|
||||
JS_ASSERT(clasp->hasPrivate());
|
||||
|
||||
RootedObject proto(cx);
|
||||
if (type->is<SimpleTypeDescr>()) {
|
||||
if (descr->is<SimpleTypeDescr>()) {
|
||||
// FIXME Bug 929651 -- What prototype to use?
|
||||
proto = type->global().getOrCreateObjectPrototype(cx);
|
||||
} else {
|
||||
RootedValue protoVal(cx);
|
||||
if (!JSObject::getProperty(cx, type, type,
|
||||
cx->names().prototype, &protoVal))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
proto = &protoVal.toObject();
|
||||
return cx->global()->getOrCreateObjectPrototype(cx);
|
||||
}
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
|
||||
RootedValue protoVal(cx);
|
||||
if (!JSObject::getProperty(cx, descr, descr,
|
||||
cx->names().prototype, &protoVal))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &protoVal.toObject();
|
||||
}
|
||||
|
||||
/*static*/ OwnedTypedObject *
|
||||
OwnedTypedObject::createUnattachedWithClass(JSContext *cx,
|
||||
const Class *clasp,
|
||||
HandleTypeDescr type,
|
||||
int32_t length)
|
||||
{
|
||||
JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
|
||||
clasp == &OwnedOpaqueTypedObject::class_);
|
||||
|
||||
RootedObject proto(cx, PrototypeForTypeDescr(cx, type));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, proto, nullptr));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
@ -1509,11 +1589,11 @@ TypedObject::createUnattachedWithClass(JSContext *cx,
|
||||
obj->initReservedSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(length));
|
||||
obj->initReservedSlot(JS_BUFVIEW_SLOT_OWNER, NullValue());
|
||||
|
||||
return static_cast<TypedObject*>(&*obj);
|
||||
return &obj->as<OwnedTypedObject>();
|
||||
}
|
||||
|
||||
void
|
||||
TypedObject::attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset)
|
||||
OwnedTypedObject::attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset)
|
||||
{
|
||||
JS_ASSERT(offset >= 0);
|
||||
JS_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
|
||||
@ -1527,12 +1607,26 @@ TypedObject::attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset)
|
||||
}
|
||||
|
||||
void
|
||||
TypedObject::attach(JSContext *cx, TypedObject &typedObj, int32_t offset)
|
||||
OwnedTypedObject::attach(JSContext *cx, TypedObject &typedObj, int32_t offset)
|
||||
{
|
||||
JS_ASSERT(!typedObj.owner().isNeutered());
|
||||
JS_ASSERT(typedObj.typedMem() != NULL);
|
||||
JS_ASSERT(typedObj.isAttached());
|
||||
|
||||
attach(cx, typedObj.owner(), typedObj.offset() + offset);
|
||||
JSObject *owner = &typedObj;
|
||||
if (typedObj.is<OwnedTypedObject>()) {
|
||||
owner = &typedObj.as<OwnedTypedObject>().owner();
|
||||
offset += typedObj.offset();
|
||||
}
|
||||
|
||||
if (owner->is<ArrayBufferObject>()) {
|
||||
attach(cx, owner->as<ArrayBufferObject>(), offset);
|
||||
} else {
|
||||
JS_ASSERT(owner->is<InlineOpaqueTypedObject>());
|
||||
initPrivate(owner->as<InlineOpaqueTypedObject>().inlineTypedMem() + offset);
|
||||
PostBarrierTypedArrayObject(this);
|
||||
|
||||
setReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
|
||||
setReservedSlot(JS_BUFVIEW_SLOT_OWNER, ObjectValue(*owner));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
|
||||
@ -1556,19 +1650,19 @@ TypedObjLengthFromType(TypeDescr &descr)
|
||||
MOZ_CRASH("Invalid kind");
|
||||
}
|
||||
|
||||
/*static*/ TypedObject *
|
||||
TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
HandleTypedObject typedObj, int32_t offset)
|
||||
/*static*/ OwnedTypedObject *
|
||||
OwnedTypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
HandleTypedObject typedObj, int32_t offset)
|
||||
{
|
||||
JS_ASSERT(!typedObj->owner().isNeutered());
|
||||
JS_ASSERT(typedObj->typedMem() != NULL);
|
||||
JS_ASSERT(offset <= typedObj->size());
|
||||
JS_ASSERT(offset + type->size() <= typedObj->size());
|
||||
|
||||
int32_t length = TypedObjLengthFromType(*type);
|
||||
|
||||
const js::Class *clasp = typedObj->getClass();
|
||||
Rooted<TypedObject*> obj(cx);
|
||||
const js::Class *clasp = typedObj->is<TransparentTypedObject>()
|
||||
? &TransparentTypedObject::class_
|
||||
: &OwnedOpaqueTypedObject::class_;
|
||||
Rooted<OwnedTypedObject*> obj(cx);
|
||||
obj = createUnattachedWithClass(cx, clasp, type, length);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
@ -1578,12 +1672,20 @@ TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
|
||||
}
|
||||
|
||||
/*static*/ TypedObject *
|
||||
TypedObject::createZeroed(JSContext *cx,
|
||||
HandleTypeDescr descr,
|
||||
int32_t length)
|
||||
TypedObject::createZeroed(JSContext *cx, HandleTypeDescr descr, int32_t length)
|
||||
{
|
||||
// If possible, create an object with inline data.
|
||||
if (descr->opaque() &&
|
||||
descr->is<SizedTypeDescr>() &&
|
||||
(size_t) descr->as<SizedTypeDescr>().size() <= InlineOpaqueTypedObject::MaximumSize)
|
||||
{
|
||||
InlineOpaqueTypedObject *obj = InlineOpaqueTypedObject::create(cx, descr);
|
||||
descr->as<SizedTypeDescr>().initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Create unattached wrapper object.
|
||||
Rooted<TypedObject*> obj(cx, createUnattached(cx, descr, length));
|
||||
Rooted<OwnedTypedObject*> obj(cx, OwnedTypedObject::createUnattached(cx, descr, length));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
@ -1650,13 +1752,10 @@ ReportTypedObjTypeError(JSContext *cx,
|
||||
return false;
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
TypedObject::obj_trace(JSTracer *trace, JSObject *object)
|
||||
/* static */ void
|
||||
OwnedTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
{
|
||||
ArrayBufferViewObject::trace(trace, object);
|
||||
|
||||
JS_ASSERT(object->is<TypedObject>());
|
||||
TypedObject &typedObj = object->as<TypedObject>();
|
||||
OwnedTypedObject &typedObj = object->as<OwnedTypedObject>();
|
||||
|
||||
// When this is called for compacting GC, the related objects we touch here
|
||||
// may not have had their slots updated yet. Note that this does not apply
|
||||
@ -1664,27 +1763,40 @@ TypedObject::obj_trace(JSTracer *trace, JSObject *object)
|
||||
// prototypes) are never allocated in the nursery.
|
||||
TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
|
||||
|
||||
if (descr.opaque()) {
|
||||
uint8_t *mem = typedObj.typedMem();
|
||||
if (!mem)
|
||||
return; // partially constructed
|
||||
if (!descr.opaque() || !typedObj.maybeForwardedIsAttached()) {
|
||||
gc::MarkSlot(trc, &typedObj.getFixedSlotRef(JS_BUFVIEW_SLOT_OWNER), "typed object owner");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typedObj.owner().isNeutered())
|
||||
return;
|
||||
// Mark the owner, watching in case it is moved by the tracer.
|
||||
JSObject *oldOwner = &typedObj.owner();
|
||||
gc::MarkSlot(trc, &typedObj.getFixedSlotRef(JS_BUFVIEW_SLOT_OWNER), "typed object owner");
|
||||
JSObject *owner = &typedObj.owner();
|
||||
|
||||
switch (descr.kind()) {
|
||||
case type::Scalar:
|
||||
case type::Reference:
|
||||
case type::Struct:
|
||||
case type::SizedArray:
|
||||
case type::Simd:
|
||||
descr.as<SizedTypeDescr>().traceInstances(trace, mem, 1);
|
||||
break;
|
||||
uint8_t *mem = typedObj.outOfLineTypedMem();
|
||||
|
||||
case type::UnsizedArray:
|
||||
descr.as<UnsizedArrayTypeDescr>().elementType().traceInstances(trace, mem, typedObj.length());
|
||||
break;
|
||||
}
|
||||
// Update the data pointer if the owner moved and the owner's data is
|
||||
// inline with it.
|
||||
if (owner != oldOwner &&
|
||||
(owner->is<InlineOpaqueTypedObject>() ||
|
||||
owner->as<ArrayBufferObject>().hasInlineData()))
|
||||
{
|
||||
mem += reinterpret_cast<uint8_t *>(owner) - reinterpret_cast<uint8_t *>(oldOwner);
|
||||
typedObj.setPrivate(mem);
|
||||
}
|
||||
|
||||
switch (descr.kind()) {
|
||||
case type::Scalar:
|
||||
case type::Reference:
|
||||
case type::Struct:
|
||||
case type::SizedArray:
|
||||
case type::Simd:
|
||||
descr.as<SizedTypeDescr>().traceInstances(trc, mem, 1);
|
||||
break;
|
||||
|
||||
case type::UnsizedArray:
|
||||
descr.as<UnsizedArrayTypeDescr>().elementType().traceInstances(trc, mem, typedObj.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1832,7 +1944,7 @@ TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiv
|
||||
case type::SizedArray:
|
||||
case type::UnsizedArray:
|
||||
if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||
if (typedObj->owner().isNeutered() || !typedObj->typedMem()) { // unattached
|
||||
if (!typedObj->isAttached()) {
|
||||
JS_ReportErrorNumber(
|
||||
cx, js_GetErrorMessage,
|
||||
nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
|
||||
@ -2211,34 +2323,34 @@ TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
TypedObject::offsetOfOwnerSlot()
|
||||
OwnedTypedObject::offsetOfOwnerSlot()
|
||||
{
|
||||
return JSObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_OWNER);
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
TypedObject::offsetOfDataSlot()
|
||||
OwnedTypedObject::offsetOfDataSlot()
|
||||
{
|
||||
# ifdef DEBUG
|
||||
#ifdef DEBUG
|
||||
// Compute offset of private data based on TransparentTypedObject;
|
||||
// both OpaqueTypedObject and TransparentTypedObject have the same
|
||||
// both OpaqueOwnedTypedObject and TransparentTypedObject have the same
|
||||
// number of slots, so no problem there.
|
||||
gc::AllocKind allocKind = gc::GetGCObjectKind(&TransparentTypedObject::class_);
|
||||
size_t nfixed = gc::GetGCKindSlots(allocKind);
|
||||
JS_ASSERT(JS_TYPEDOBJ_SLOT_DATA == nfixed - 1);
|
||||
# endif
|
||||
JS_ASSERT(DATA_SLOT == nfixed - 1);
|
||||
#endif
|
||||
|
||||
return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA);
|
||||
return JSObject::getPrivateDataOffset(DATA_SLOT);
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
TypedObject::offsetOfByteOffsetSlot()
|
||||
OwnedTypedObject::offsetOfByteOffsetSlot()
|
||||
{
|
||||
return JSObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_BYTEOFFSET);
|
||||
}
|
||||
|
||||
void
|
||||
TypedObject::neuter(void *newData)
|
||||
OwnedTypedObject::neuter(void *newData)
|
||||
{
|
||||
setSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(0));
|
||||
setSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(0));
|
||||
@ -2246,51 +2358,107 @@ TypedObject::neuter(void *newData)
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Typed Objects
|
||||
* Inline typed objects
|
||||
*/
|
||||
|
||||
const Class TransparentTypedObject::class_ = {
|
||||
"TypedObject",
|
||||
Class::NON_NATIVE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
JS_PropertyStub,
|
||||
JS_DeletePropertyStub,
|
||||
JS_PropertyStub,
|
||||
JS_StrictPropertyStub,
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
TypedObject::obj_trace,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
TypedObject::obj_lookupGeneric,
|
||||
TypedObject::obj_lookupProperty,
|
||||
TypedObject::obj_lookupElement,
|
||||
TypedObject::obj_defineGeneric,
|
||||
TypedObject::obj_defineProperty,
|
||||
TypedObject::obj_defineElement,
|
||||
TypedObject::obj_getGeneric,
|
||||
TypedObject::obj_getProperty,
|
||||
TypedObject::obj_getElement,
|
||||
TypedObject::obj_setGeneric,
|
||||
TypedObject::obj_setProperty,
|
||||
TypedObject::obj_setElement,
|
||||
TypedObject::obj_getGenericAttributes,
|
||||
TypedObject::obj_setGenericAttributes,
|
||||
TypedObject::obj_deleteGeneric,
|
||||
nullptr, nullptr, // watch/unwatch
|
||||
nullptr, /* slice */
|
||||
TypedObject::obj_enumerate,
|
||||
nullptr, /* thisObject */
|
||||
/* static */ InlineOpaqueTypedObject *
|
||||
InlineOpaqueTypedObject::create(JSContext *cx, HandleTypeDescr descr)
|
||||
{
|
||||
gc::AllocKind allocKind = allocKindForTypeDescriptor(descr);
|
||||
|
||||
RootedObject proto(cx, PrototypeForTypeDescr(cx, descr));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithClassProto(cx, &class_, proto, nullptr, allocKind));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
return &obj->as<InlineOpaqueTypedObject>();
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
InlineOpaqueTypedObject::inlineTypedMem() const
|
||||
{
|
||||
return fixedData(0);
|
||||
}
|
||||
|
||||
/* static */
|
||||
size_t
|
||||
InlineOpaqueTypedObject::offsetOfDataStart()
|
||||
{
|
||||
return getFixedSlotOffset(0);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
InlineOpaqueTypedObject::obj_trace(JSTracer *trc, JSObject *object)
|
||||
{
|
||||
InlineOpaqueTypedObject &typedObj = object->as<InlineOpaqueTypedObject>();
|
||||
|
||||
// When this is called for compacting GC, the related objects we touch here
|
||||
// may not have had their slots updated yet.
|
||||
TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
|
||||
|
||||
descr.as<SizedTypeDescr>().traceInstances(trc, typedObj.inlineTypedMem(), 1);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Typed object classes
|
||||
*/
|
||||
|
||||
#define DEFINE_TYPEDOBJ_CLASS(Name, Flags, Trace) \
|
||||
const Class Name::class_ = { \
|
||||
# Name, \
|
||||
Class::NON_NATIVE | \
|
||||
JSCLASS_IMPLEMENTS_BARRIERS | \
|
||||
Flags, \
|
||||
JS_PropertyStub, \
|
||||
JS_DeletePropertyStub, \
|
||||
JS_PropertyStub, \
|
||||
JS_StrictPropertyStub, \
|
||||
JS_EnumerateStub, \
|
||||
JS_ResolveStub, \
|
||||
JS_ConvertStub, \
|
||||
nullptr, /* finalize */ \
|
||||
nullptr, /* call */ \
|
||||
nullptr, /* hasInstance */ \
|
||||
nullptr, /* construct */ \
|
||||
Trace, \
|
||||
JS_NULL_CLASS_SPEC, \
|
||||
JS_NULL_CLASS_EXT, \
|
||||
{ \
|
||||
TypedObject::obj_lookupGeneric, \
|
||||
TypedObject::obj_lookupProperty, \
|
||||
TypedObject::obj_lookupElement, \
|
||||
TypedObject::obj_defineGeneric, \
|
||||
TypedObject::obj_defineProperty, \
|
||||
TypedObject::obj_defineElement, \
|
||||
TypedObject::obj_getGeneric, \
|
||||
TypedObject::obj_getProperty, \
|
||||
TypedObject::obj_getElement, \
|
||||
TypedObject::obj_setGeneric, \
|
||||
TypedObject::obj_setProperty, \
|
||||
TypedObject::obj_setElement, \
|
||||
TypedObject::obj_getGenericAttributes, \
|
||||
TypedObject::obj_setGenericAttributes, \
|
||||
TypedObject::obj_deleteGeneric, \
|
||||
nullptr, nullptr, /* watch/unwatch */ \
|
||||
nullptr, /* slice */ \
|
||||
TypedObject::obj_enumerate, \
|
||||
nullptr, /* thisObject */ \
|
||||
} \
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(TransparentTypedObject,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(DATA_SLOT) | JSCLASS_HAS_PRIVATE,
|
||||
OwnedTypedObject::obj_trace);
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(OwnedOpaqueTypedObject,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(DATA_SLOT) | JSCLASS_HAS_PRIVATE,
|
||||
OwnedTypedObject::obj_trace);
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject, 0,
|
||||
InlineOpaqueTypedObject::obj_trace);
|
||||
|
||||
static int32_t
|
||||
LengthForType(TypeDescr &descr)
|
||||
@ -2403,8 +2571,8 @@ TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<TypedObject*> obj(cx);
|
||||
obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee));
|
||||
Rooted<OwnedTypedObject*> obj(cx);
|
||||
obj = OwnedTypedObject::createUnattached(cx, callee, LengthForType(*callee));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
@ -2541,8 +2709,8 @@ TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<TypedObject*> obj(cx);
|
||||
obj = TypedObject::createUnattached(cx, callee, length);
|
||||
Rooted<OwnedTypedObject*> obj(cx);
|
||||
obj = OwnedTypedObject::createUnattached(cx, callee, length);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
@ -2590,61 +2758,6 @@ TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Handles
|
||||
*/
|
||||
|
||||
const Class OpaqueTypedObject::class_ = {
|
||||
"Handle",
|
||||
Class::NON_NATIVE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_IMPLEMENTS_BARRIERS,
|
||||
JS_PropertyStub,
|
||||
JS_DeletePropertyStub,
|
||||
JS_PropertyStub,
|
||||
JS_StrictPropertyStub,
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* construct */
|
||||
nullptr, /* hasInstance */
|
||||
TypedObject::obj_trace,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
TypedObject::obj_lookupGeneric,
|
||||
TypedObject::obj_lookupProperty,
|
||||
TypedObject::obj_lookupElement,
|
||||
TypedObject::obj_defineGeneric,
|
||||
TypedObject::obj_defineProperty,
|
||||
TypedObject::obj_defineElement,
|
||||
TypedObject::obj_getGeneric,
|
||||
TypedObject::obj_getProperty,
|
||||
TypedObject::obj_getElement,
|
||||
TypedObject::obj_setGeneric,
|
||||
TypedObject::obj_setProperty,
|
||||
TypedObject::obj_setElement,
|
||||
TypedObject::obj_getGenericAttributes,
|
||||
TypedObject::obj_setGenericAttributes,
|
||||
TypedObject::obj_deleteGeneric,
|
||||
nullptr, nullptr, // watch/unwatch
|
||||
nullptr, // slice
|
||||
TypedObject::obj_enumerate,
|
||||
nullptr, /* thisObject */
|
||||
}
|
||||
};
|
||||
|
||||
const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
|
||||
{"move", {nullptr, nullptr}, 3, 0, "HandleMove"},
|
||||
{"get", {nullptr, nullptr}, 1, 0, "HandleGet"},
|
||||
{"set", {nullptr, nullptr}, 2, 0, "HandleSet"},
|
||||
{"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"},
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Intrinsics
|
||||
*/
|
||||
@ -2658,8 +2771,8 @@ js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
|
||||
int32_t length = TypedObjLengthFromType(*descr);
|
||||
Rooted<TypedObject*> obj(cx);
|
||||
obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
|
||||
Rooted<OwnedTypedObject*> obj(cx);
|
||||
obj = OwnedTypedObject::createUnattachedWithClass(cx, &OwnedOpaqueTypedObject::class_, descr, length);
|
||||
if (!obj)
|
||||
return false;
|
||||
args.rval().setObject(*obj);
|
||||
@ -2680,7 +2793,7 @@ js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp)
|
||||
int32_t offset = args[2].toInt32();
|
||||
|
||||
Rooted<TypedObject*> obj(cx);
|
||||
obj = TypedObject::createDerived(cx, descr, typedObj, offset);
|
||||
obj = OwnedTypedObject::createDerived(cx, descr, typedObj, offset);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
@ -2693,13 +2806,11 @@ js::AttachTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 3);
|
||||
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[2].isInt32());
|
||||
|
||||
TypedObject &handle = args[0].toObject().as<TypedObject>();
|
||||
OwnedTypedObject &handle = args[0].toObject().as<OwnedTypedObject>();
|
||||
TypedObject &target = args[1].toObject().as<TypedObject>();
|
||||
JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already
|
||||
JS_ASSERT(!handle.isAttached());
|
||||
size_t offset = args[2].toInt32();
|
||||
|
||||
if (cx->isForkJoinContext()) {
|
||||
@ -2723,13 +2834,13 @@ js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[1].isInt32());
|
||||
|
||||
TypedObject &typedObj = args[0].toObject().as<TypedObject>();
|
||||
OwnedTypedObject &typedObj = args[0].toObject().as<OwnedTypedObject>();
|
||||
int32_t offset = args[1].toInt32();
|
||||
|
||||
JS_ASSERT(!typedObj.owner().isNeutered());
|
||||
JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already
|
||||
JS_ASSERT(typedObj.isAttached());
|
||||
int32_t oldOffset = typedObj.offset();
|
||||
|
||||
typedObj.setPrivate(typedObj.owner().dataPointer() + offset);
|
||||
typedObj.setPrivate((typedObj.typedMem() - oldOffset) + offset);
|
||||
typedObj.setReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2779,8 +2890,7 @@ js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 1);
|
||||
JS_ASSERT(args[0].isObject());
|
||||
args.rval().setBoolean(args[0].toObject().is<OpaqueTypedObject>());
|
||||
args.rval().setBoolean(!args[0].toObject().is<TransparentTypedObject>());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2793,7 +2903,6 @@ js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 1);
|
||||
JS_ASSERT(args[0].isObject());
|
||||
args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
|
||||
return true;
|
||||
}
|
||||
@ -2868,9 +2977,8 @@ bool
|
||||
js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
|
||||
TypedObject &typedObj = args[0].toObject().as<TypedObject>();
|
||||
args.rval().setBoolean(!typedObj.owner().isNeutered() && typedObj.typedMem() != nullptr);
|
||||
args.rval().setBoolean(typedObj.isAttached());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -58,12 +58,23 @@
|
||||
*
|
||||
* - Typed objects:
|
||||
*
|
||||
* A typed object is an instance of a *type object* (note the past
|
||||
* participle). There is one class for *transparent* typed objects and
|
||||
* one for *opaque* typed objects. These classes are equivalent in
|
||||
* basically every way, except that requesting the backing buffer of
|
||||
* an opaque typed object yields null. We use distinct js::Classes to
|
||||
* avoid the need for an extra slot in every typed object.
|
||||
* A typed object is an instance of a *type object* (note the past participle).
|
||||
* Typed objects can be either transparent or opaque, depending on whether
|
||||
* their underlying buffer can be accessed. Transparent and opaque typed
|
||||
* objects have different classes, and can have different physical layouts.
|
||||
* The following layouts are currently possible:
|
||||
*
|
||||
* InlineOpaqueTypedObject: Typed objects whose data immediately follows the
|
||||
* object's header are inline typed objects. These do not have an associated
|
||||
* array buffer, so only opaque typed objects can be inline.
|
||||
*
|
||||
* OwnedTypedObject: Transparent or opaque typed objects whose data is owned by
|
||||
* another object, which can be either an array buffer or an inline typed
|
||||
* object (opaque objects only). Owned typed objects may be attached or
|
||||
* unattached. An unattached typed object has no memory associated with it.
|
||||
* When first created, objects are always attached, but they can become
|
||||
* unattached if their buffer is neutered (note that this implies that typed
|
||||
* objects of opaque types can't be unattached).
|
||||
*
|
||||
* Note that whether a typed object is opaque is not directly
|
||||
* connected to its type. That is, opaque types are *always*
|
||||
@ -77,31 +88,6 @@
|
||||
* fully override the property accessors etc. The overridden accessor
|
||||
* methods are the same in each and are defined in methods of
|
||||
* TypedObject.
|
||||
*
|
||||
* Typed objects may be attached or unattached. An unattached typed
|
||||
* object has no memory associated with it; it is basically a null
|
||||
* pointer. When first created, objects are always attached, but they
|
||||
* can become unattached if their buffer is neutered (note that this
|
||||
* implies that typed objects of opaque types can never be unattached).
|
||||
*
|
||||
* When a new typed object instance is created, fresh memory is
|
||||
* allocated and set as that typed object's private field. The object
|
||||
* is then considered the *owner* of that memory: when the object is
|
||||
* collected, its finalizer will free the memory. The fact that an
|
||||
* object `o` owns its memory is indicated by setting its reserved
|
||||
* slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other
|
||||
* words).
|
||||
*
|
||||
* Later, *derived* typed objects can be created, typically via an
|
||||
* access like `o.f` where `f` is some complex (non-scalar) type, but
|
||||
* also explicitly via Handle objects. In those cases, the memory
|
||||
* pointer of the derived object is set to alias the owner's memory
|
||||
* pointer, and the owner slot for the derived object is set to the
|
||||
* owner object, thus ensuring that the owner is not collected while
|
||||
* the derived object is alive. We always maintain the invariant that
|
||||
* JS_TYPEDOBJ_SLOT_OWNER is the true owner of the memory, meaning
|
||||
* that there is a shallow tree. This prevents an access pattern like
|
||||
* `a.b.c.d` from keeping all the intermediate objects alive.
|
||||
*/
|
||||
|
||||
namespace js {
|
||||
@ -537,10 +523,7 @@ class TypedObjectModuleObject : public JSObject {
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Base type for typed objects and handles. Basically any type whose
|
||||
* contents consist of typed memory.
|
||||
*/
|
||||
/* Base type for transparent and opaque typed objects. */
|
||||
class TypedObject : public ArrayBufferViewObject
|
||||
{
|
||||
private:
|
||||
@ -561,8 +544,6 @@ class TypedObject : public ArrayBufferViewObject
|
||||
MutableHandleValue vp);
|
||||
|
||||
protected:
|
||||
static void obj_trace(JSTracer *trace, JSObject *object);
|
||||
|
||||
static bool obj_lookupGeneric(JSContext *cx, HandleObject obj,
|
||||
HandleId id, MutableHandleObject objp,
|
||||
MutableHandleShape propp);
|
||||
@ -616,76 +597,6 @@ class TypedObject : public ArrayBufferViewObject
|
||||
MutableHandleValue statep, MutableHandleId idp);
|
||||
|
||||
public:
|
||||
static size_t offsetOfOwnerSlot();
|
||||
|
||||
// Each typed object contains a void* pointer pointing at the
|
||||
// binary data that it represents. (That data may be owned by this
|
||||
// object or this object may alias data owned by someone else.)
|
||||
// This function returns the offset in bytes within the object
|
||||
// where the `void*` pointer can be found. It is intended for use
|
||||
// by the JIT.
|
||||
static size_t offsetOfDataSlot();
|
||||
|
||||
// Offset of the byte offset slot.
|
||||
static size_t offsetOfByteOffsetSlot();
|
||||
|
||||
// Helper for createUnattached()
|
||||
static TypedObject *createUnattachedWithClass(JSContext *cx,
|
||||
const Class *clasp,
|
||||
HandleTypeDescr type,
|
||||
int32_t length);
|
||||
|
||||
// Creates an unattached typed object or handle (depending on the
|
||||
// type parameter T). Note that it is only legal for unattached
|
||||
// handles to escape to the end user; for non-handles, the caller
|
||||
// should always invoke one of the `attach()` methods below.
|
||||
//
|
||||
// Arguments:
|
||||
// - type: type object for resulting object
|
||||
// - length: 0 unless this is an array, otherwise the length
|
||||
static TypedObject *createUnattached(JSContext *cx, HandleTypeDescr type,
|
||||
int32_t length);
|
||||
|
||||
// Creates a typedObj that aliases the memory pointed at by `owner`
|
||||
// at the given offset. The typedObj will be a handle iff type is a
|
||||
// handle and a typed object otherwise.
|
||||
static TypedObject *createDerived(JSContext *cx,
|
||||
HandleSizedTypeDescr type,
|
||||
Handle<TypedObject*> typedContents,
|
||||
int32_t offset);
|
||||
|
||||
// Creates a new typed object whose memory is freshly allocated
|
||||
// and initialized with zeroes (or, in the case of references, an
|
||||
// appropriate default value).
|
||||
static TypedObject *createZeroed(JSContext *cx,
|
||||
HandleTypeDescr typeObj,
|
||||
int32_t length);
|
||||
|
||||
// User-accessible constructor (`new TypeDescriptor(...)`)
|
||||
// used for sized types. Note that the callee here is the *type descriptor*,
|
||||
// not the typedObj.
|
||||
static bool constructSized(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
// As `constructSized`, but for unsized array types.
|
||||
static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
// Use this method when `buffer` is the owner of the memory.
|
||||
void attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset);
|
||||
|
||||
// Otherwise, use this to attach to memory referenced by another typedObj.
|
||||
void attach(JSContext *cx, TypedObject &typedObj, int32_t offset);
|
||||
|
||||
// Invoked when array buffer is transferred elsewhere
|
||||
void neuter(void *newData);
|
||||
|
||||
int32_t offset() const {
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET).toInt32();
|
||||
}
|
||||
|
||||
ArrayBufferObject &owner() const {
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject().as<ArrayBufferObject>();
|
||||
}
|
||||
|
||||
TypedProto &typedProto() const {
|
||||
return getProto()->as<TypedProto>();
|
||||
}
|
||||
@ -702,13 +613,11 @@ class TypedObject : public ArrayBufferViewObject
|
||||
return maybeForwardedTypedProto().maybeForwardedTypeDescr();
|
||||
}
|
||||
|
||||
uint8_t *typedMem() const {
|
||||
return (uint8_t*) getPrivate();
|
||||
}
|
||||
|
||||
int32_t length() const {
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_LENGTH).toInt32();
|
||||
}
|
||||
int32_t offset() const;
|
||||
int32_t length() const;
|
||||
uint8_t *typedMem() const;
|
||||
bool isAttached() const;
|
||||
bool maybeForwardedIsAttached() const;
|
||||
|
||||
int32_t size() const {
|
||||
switch (typeDescr().kind()) {
|
||||
@ -736,23 +645,128 @@ class TypedObject : public ArrayBufferViewObject
|
||||
JS_ASSERT(offset <= (size_t) size());
|
||||
return typedMem() + offset;
|
||||
}
|
||||
|
||||
// Creates a new typed object whose memory is freshly allocated and
|
||||
// initialized with zeroes (or, in the case of references, an appropriate
|
||||
// default value).
|
||||
static TypedObject *createZeroed(JSContext *cx, HandleTypeDescr typeObj, int32_t length);
|
||||
|
||||
// User-accessible constructor (`new TypeDescriptor(...)`) used for sized
|
||||
// types. Note that the callee here is the type descriptor.
|
||||
static bool constructSized(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
// As `constructSized`, but for unsized array types.
|
||||
static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/* Accessors for self hosted code. */
|
||||
static bool GetBuffer(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool GetByteOffset(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
typedef Handle<TypedObject*> HandleTypedObject;
|
||||
|
||||
class TransparentTypedObject : public TypedObject
|
||||
class OwnedTypedObject : public TypedObject
|
||||
{
|
||||
public:
|
||||
static const size_t DATA_SLOT = 3;
|
||||
|
||||
static size_t offsetOfOwnerSlot();
|
||||
|
||||
// Each typed object contains a void* pointer pointing at the
|
||||
// binary data that it represents. (That data may be owned by this
|
||||
// object or this object may alias data owned by someone else.)
|
||||
// This function returns the offset in bytes within the object
|
||||
// where the `void*` pointer can be found. It is intended for use
|
||||
// by the JIT.
|
||||
static size_t offsetOfDataSlot();
|
||||
|
||||
// Offset of the byte offset slot.
|
||||
static size_t offsetOfByteOffsetSlot();
|
||||
|
||||
JSObject &owner() const {
|
||||
return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject();
|
||||
}
|
||||
|
||||
uint8_t *outOfLineTypedMem() const {
|
||||
return static_cast<uint8_t *>(getPrivate(DATA_SLOT));
|
||||
}
|
||||
|
||||
// Helper for createUnattached()
|
||||
static OwnedTypedObject *createUnattachedWithClass(JSContext *cx,
|
||||
const Class *clasp,
|
||||
HandleTypeDescr type,
|
||||
int32_t length);
|
||||
|
||||
// Creates an unattached typed object or handle (depending on the
|
||||
// type parameter T). Note that it is only legal for unattached
|
||||
// handles to escape to the end user; for non-handles, the caller
|
||||
// should always invoke one of the `attach()` methods below.
|
||||
//
|
||||
// Arguments:
|
||||
// - type: type object for resulting object
|
||||
// - length: 0 unless this is an array, otherwise the length
|
||||
static OwnedTypedObject *createUnattached(JSContext *cx, HandleTypeDescr type,
|
||||
int32_t length);
|
||||
|
||||
// Creates a typedObj that aliases the memory pointed at by `owner`
|
||||
// at the given offset. The typedObj will be a handle iff type is a
|
||||
// handle and a typed object otherwise.
|
||||
static OwnedTypedObject *createDerived(JSContext *cx,
|
||||
HandleSizedTypeDescr type,
|
||||
Handle<TypedObject*> typedContents,
|
||||
int32_t offset);
|
||||
|
||||
// Use this method when `buffer` is the owner of the memory.
|
||||
void attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset);
|
||||
|
||||
// Otherwise, use this to attach to memory referenced by another typedObj.
|
||||
void attach(JSContext *cx, TypedObject &typedObj, int32_t offset);
|
||||
|
||||
// Invoked when array buffer is transferred elsewhere
|
||||
void neuter(void *newData);
|
||||
|
||||
static void obj_trace(JSTracer *trace, JSObject *object);
|
||||
};
|
||||
|
||||
// Class for a transparent typed object, whose owner is an array buffer.
|
||||
class TransparentTypedObject : public OwnedTypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
typedef Handle<TransparentTypedObject*> HandleTransparentTypedObject;
|
||||
|
||||
class OpaqueTypedObject : public TypedObject
|
||||
// Class for an opaque typed object, whose owner may be either an array buffer
|
||||
// or an opaque inlined typed object.
|
||||
class OwnedOpaqueTypedObject : public OwnedTypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
static const JSFunctionSpec handleStaticMethods[];
|
||||
};
|
||||
|
||||
// Class for an opaque typed object whose data is allocated inline.
|
||||
class InlineOpaqueTypedObject : public TypedObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static const size_t MaximumSize = JSObject::MAX_FIXED_SLOTS * sizeof(Value);
|
||||
|
||||
static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
|
||||
size_t nbytes = descr->as<SizedTypeDescr>().size();
|
||||
JS_ASSERT(nbytes <= MaximumSize);
|
||||
|
||||
size_t dataSlots = AlignBytes(nbytes, sizeof(Value) / sizeof(Value));
|
||||
JS_ASSERT(nbytes <= dataSlots * sizeof(Value));
|
||||
return gc::GetGCObjectKind(dataSlots);
|
||||
}
|
||||
|
||||
uint8_t *inlineTypedMem() const;
|
||||
|
||||
static void obj_trace(JSTracer *trace, JSObject *object);
|
||||
|
||||
static size_t offsetOfDataStart();
|
||||
|
||||
static InlineOpaqueTypedObject *create(JSContext *cx, HandleTypeDescr descr);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -968,7 +982,8 @@ inline bool
|
||||
IsTypedObjectClass(const Class *class_)
|
||||
{
|
||||
return class_ == &TransparentTypedObject::class_ ||
|
||||
class_ == &OpaqueTypedObject::class_;
|
||||
class_ == &OwnedOpaqueTypedObject::class_ ||
|
||||
class_ == &InlineOpaqueTypedObject::class_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -1040,18 +1055,12 @@ JSObject::is<js::TypedObject>() const
|
||||
return IsTypedObjectClass(getClass());
|
||||
}
|
||||
|
||||
template<>
|
||||
template <>
|
||||
inline bool
|
||||
JSObject::is<js::SizedArrayTypeDescr>() const
|
||||
JSObject::is<js::OwnedTypedObject>() const
|
||||
{
|
||||
return getClass() == &js::SizedArrayTypeDescr::class_;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
JSObject::is<js::UnsizedArrayTypeDescr>() const
|
||||
{
|
||||
return getClass() == &js::UnsizedArrayTypeDescr::class_;
|
||||
return getClass() == &js::TransparentTypedObject::class_ ||
|
||||
getClass() == &js::OwnedOpaqueTypedObject::class_;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -33,14 +33,7 @@
|
||||
#define TYPROTO_DESCR(obj) \
|
||||
UnsafeGetReservedSlot(obj, JS_TYPROTO_SLOT_DESCR)
|
||||
|
||||
// Typed object slots
|
||||
|
||||
#define TYPEDOBJ_BYTEOFFSET(obj) \
|
||||
TO_INT32(UnsafeGetReservedSlot(obj, JS_BUFVIEW_SLOT_BYTEOFFSET))
|
||||
#define TYPEDOBJ_OWNER(obj) \
|
||||
UnsafeGetReservedSlot(obj, JS_BUFVIEW_SLOT_OWNER)
|
||||
#define TYPEDOBJ_LENGTH(obj) \
|
||||
TO_INT32(UnsafeGetReservedSlot(obj, JS_BUFVIEW_SLOT_LENGTH))
|
||||
// Other
|
||||
|
||||
#define HAS_PROPERTY(obj, prop) \
|
||||
callFunction(std_Object_hasOwnProperty, obj, prop)
|
||||
@ -570,9 +563,9 @@ function StorageOfTypedObject(obj) {
|
||||
else
|
||||
byteLength = DESCR_SIZE(descr);
|
||||
|
||||
return { buffer: TYPEDOBJ_OWNER(obj),
|
||||
return { buffer: TypedObjectBuffer(obj),
|
||||
byteLength: byteLength,
|
||||
byteOffset: TYPEDOBJ_BYTEOFFSET(obj) };
|
||||
byteOffset: TypedObjectByteOffset(obj) };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1185,7 +1178,7 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
|
||||
// Below we will be adjusting offsets within the input to point at
|
||||
// successive entries; we'll need to know the offset of inArray
|
||||
// relative to its owner (which is often but not always 0).
|
||||
const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray);
|
||||
const inBaseOffset = TypedObjectByteOffset(inArray);
|
||||
|
||||
ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode), outArray);
|
||||
return outArray;
|
||||
|
@ -104,10 +104,6 @@
|
||||
#define JS_TYPEDARR_SLOT_DATA 3 // see (**) below
|
||||
#define JS_TYPEDARR_SLOTS 3 // Number of slots for typed arrays
|
||||
|
||||
// Specific to typed objects:
|
||||
#define JS_TYPEDOBJ_SLOT_DATA 3
|
||||
#define JS_TYPEDOBJ_SLOTS 3 // Number of slots for typed objs
|
||||
|
||||
// (*) The interpretation of the JS_BUFVIEW_SLOT_LENGTH slot depends on
|
||||
// the kind of view:
|
||||
// - DataView: stores the length in bytes
|
||||
|
@ -2002,7 +2002,7 @@ Parser<ParseHandler>::templateLiteral()
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
|
||||
Parser<ParseHandler>::functionDef(HandlePropertyName funName,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind)
|
||||
{
|
||||
@ -2042,6 +2042,9 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
Directives directives(pc);
|
||||
Directives newDirectives = directives;
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
|
||||
while (true) {
|
||||
if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives,
|
||||
bodyLevelHoistedUse))
|
||||
@ -2055,8 +2058,6 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
directives = newDirectives;
|
||||
|
||||
tokenStream.seek(start);
|
||||
if (funName && tokenStream.getToken() == TOK_ERROR)
|
||||
return null();
|
||||
|
||||
// functionArgsAndBody may have already set pn->pn_body before failing.
|
||||
handler.setFunctionBody(pn, null());
|
||||
@ -2444,17 +2445,13 @@ Parser<ParseHandler>::functionStmt()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
|
||||
RootedPropertyName name(context);
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken();
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken();
|
||||
generatorKind = StarGenerator;
|
||||
tt = tokenStream.getToken();
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
@ -2463,6 +2460,8 @@ Parser<ParseHandler>::functionStmt()
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_ERROR) {
|
||||
return null();
|
||||
} else {
|
||||
/* Unnamed function expressions are forbidden in statement context. */
|
||||
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
|
||||
@ -2474,7 +2473,7 @@ Parser<ParseHandler>::functionStmt()
|
||||
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
return null();
|
||||
|
||||
return functionDef(name, start, Normal, Statement, generatorKind);
|
||||
return functionDef(name, Normal, Statement, generatorKind);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -2483,16 +2482,12 @@ Parser<ParseHandler>::functionExpr()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken();
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken();
|
||||
generatorKind = StarGenerator;
|
||||
tt = tokenStream.getToken();
|
||||
}
|
||||
|
||||
RootedPropertyName name(context);
|
||||
@ -2502,11 +2497,13 @@ Parser<ParseHandler>::functionExpr()
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_ERROR) {
|
||||
return null();
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
}
|
||||
|
||||
return functionDef(name, start, Normal, Expression, generatorKind);
|
||||
return functionDef(name, Normal, Expression, generatorKind);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5854,7 +5851,7 @@ Parser<ParseHandler>::assignExpr()
|
||||
return null();
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
|
||||
return functionDef(NullPtr(), Normal, Arrow, NotGenerator);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -7587,9 +7584,7 @@ Parser<ParseHandler>::methodDefinition(Node literal, Node propname, FunctionType
|
||||
else
|
||||
funName = nullptr;
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
Node fn = functionDef(funName, start, type, kind, generatorKind);
|
||||
Node fn = functionDef(funName, type, kind, generatorKind);
|
||||
if (!fn)
|
||||
return false;
|
||||
if (!handler.addMethodDefinition(literal, propname, fn, op))
|
||||
|
@ -569,8 +569,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
*/
|
||||
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool *hasRest);
|
||||
|
||||
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
|
||||
Node functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind);
|
||||
bool functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
|
@ -493,10 +493,6 @@ class MOZ_STACK_CLASS TokenStream
|
||||
void seek(const Position &pos);
|
||||
bool seek(const Position &pos, const TokenStream &other);
|
||||
|
||||
size_t positionToOffset(const Position &pos) const {
|
||||
return pos.buf - userbuf.base();
|
||||
}
|
||||
|
||||
const char16_t *rawBase() const {
|
||||
return userbuf.base();
|
||||
}
|
||||
|
@ -389,6 +389,16 @@ GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
|
||||
return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
|
||||
}
|
||||
|
||||
// Inlined opaque typed objects are followed by their data, so make sure we
|
||||
// copy it all over to the new object.
|
||||
if (obj->is<InlineOpaqueTypedObject>()) {
|
||||
// Figure out the size of this object, from the prototype's TypeDescr.
|
||||
// The objects we are traversing here are all tenured, so we don't need
|
||||
// to check forwarding pointers.
|
||||
TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
|
||||
return InlineOpaqueTypedObject::allocKindForTypeDescriptor(descr);
|
||||
}
|
||||
|
||||
AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots());
|
||||
JS_ASSERT(!IsBackgroundFinalized(kind));
|
||||
JS_ASSERT(CanBeFinalizedInBackground(kind, obj->getClass()));
|
||||
|
22
js/src/jit-test/tests/TypedObject/inlineopaque.js
Normal file
22
js/src/jit-test/tests/TypedObject/inlineopaque.js
Normal file
@ -0,0 +1,22 @@
|
||||
var TO = TypedObject;
|
||||
|
||||
var PointType = new TO.StructType({x: TO.float64, y: TO.float64, name:TO.string});
|
||||
var LineType = new TO.StructType({from: PointType, to: PointType});
|
||||
|
||||
function testBasic(gc) {
|
||||
var line = new LineType();
|
||||
var from = line.from;
|
||||
var to = line.to;
|
||||
line.from.x = 12;
|
||||
line.from.name = "three";
|
||||
if (gc)
|
||||
minorgc();
|
||||
assertEq(to.name, "");
|
||||
assertEq(from.name, "three");
|
||||
assertEq(from.x, 12);
|
||||
assertEq(from.y, 0);
|
||||
}
|
||||
for (var i = 0; i < 5; i++)
|
||||
testBasic(false);
|
||||
for (var i = 0; i < 5; i++)
|
||||
testBasic(true);
|
@ -1,4 +1,4 @@
|
||||
// |jit-test| ion-eager; debug;
|
||||
// |jit-test| ion-eager;
|
||||
var x
|
||||
(function() {
|
||||
x
|
||||
|
20
js/src/jit-test/tests/gc/bug-1070638.js
Normal file
20
js/src/jit-test/tests/gc/bug-1070638.js
Normal file
@ -0,0 +1,20 @@
|
||||
function m() {
|
||||
for (var j = 0; j < 99; ++j) {
|
||||
for (var k = 0; k < 99; ++k) {
|
||||
try {
|
||||
undefined()()
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
m()
|
||||
m()
|
||||
m()
|
||||
m()
|
||||
for (var j = 0; j < 99; ++j) {
|
||||
for (var k = 0; k < 99; ++k) {
|
||||
try {
|
||||
gcslice(1)()
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
@ -4376,13 +4376,19 @@ CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
masm.extractObject(Address(obj, TypedObject::offsetOfOwnerSlot()), temp);
|
||||
Label notOwned;
|
||||
masm.loadObjClass(obj, temp);
|
||||
masm.branchPtr(Assembler::Equal, temp, ImmPtr(&InlineOpaqueTypedObject::class_), ¬Owned);
|
||||
|
||||
masm.extractObject(Address(obj, OwnedTypedObject::offsetOfOwnerSlot()), temp);
|
||||
masm.unboxInt32(Address(temp, ArrayBufferObject::flagsOffset()), temp);
|
||||
|
||||
Imm32 flag(ArrayBufferObject::neuteredFlag());
|
||||
if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
masm.bind(¬Owned);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4408,7 +4414,18 @@ CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Register out = ToRegister(lir->output());
|
||||
masm.loadPtr(Address(obj, TypedObject::offsetOfDataSlot()), out);
|
||||
|
||||
Label notOwned, done;
|
||||
masm.loadObjClass(obj, out);
|
||||
masm.branchPtr(Assembler::Equal, out, ImmPtr(&InlineOpaqueTypedObject::class_), ¬Owned);
|
||||
|
||||
masm.loadPtr(Address(obj, OwnedTypedObject::offsetOfDataSlot()), out);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(¬Owned);
|
||||
masm.computeEffectiveAddress(Address(obj, InlineOpaqueTypedObject::offsetOfDataStart()), out);
|
||||
masm.bind(&done);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4446,17 +4463,17 @@ CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir)
|
||||
// }
|
||||
|
||||
// temp0 = typedObj->byteOffset;
|
||||
masm.unboxInt32(Address(object, TypedObject::offsetOfByteOffsetSlot()), temp0);
|
||||
masm.unboxInt32(Address(object, OwnedTypedObject::offsetOfByteOffsetSlot()), temp0);
|
||||
|
||||
// temp0 -= offset;
|
||||
masm.subPtr(offset, temp0);
|
||||
|
||||
// obj->pointer -= temp0;
|
||||
masm.subPtr(temp0, Address(object, TypedObject::offsetOfDataSlot()));
|
||||
masm.subPtr(temp0, Address(object, OwnedTypedObject::offsetOfDataSlot()));
|
||||
|
||||
// obj->byteOffset = offset;
|
||||
masm.storeValue(JSVAL_TYPE_INT32, offset,
|
||||
Address(object, TypedObject::offsetOfByteOffsetSlot()));
|
||||
Address(object, OwnedTypedObject::offsetOfByteOffsetSlot()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -757,10 +757,9 @@ class IonBuilder
|
||||
InliningStatus inlineToInteger(CallInfo &callInfo);
|
||||
InliningStatus inlineToString(CallInfo &callInfo);
|
||||
InliningStatus inlineDump(CallInfo &callInfo);
|
||||
InliningStatus inlineHasClass(CallInfo &callInfo, const Class *clasp) {
|
||||
return inlineHasClasses(callInfo, clasp, nullptr);
|
||||
}
|
||||
InliningStatus inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Class *clasp2);
|
||||
InliningStatus inlineHasClass(CallInfo &callInfo, const Class *clasp,
|
||||
const Class *clasp2 = nullptr,
|
||||
const Class *clasp3 = nullptr);
|
||||
InliningStatus inlineIsConstructing(CallInfo &callInfo);
|
||||
|
||||
// Testing functions.
|
||||
|
@ -2479,7 +2479,7 @@ bool
|
||||
LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins)
|
||||
{
|
||||
JS_ASSERT(ins->type() == MIRType_Elements);
|
||||
return define(new(alloc()) LTypedObjectElements(useRegisterAtStart(ins->object())), ins);
|
||||
return define(new(alloc()) LTypedObjectElements(useRegister(ins->object())), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include "vm/StringObject-inl.h"
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
@ -178,20 +180,24 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
||||
|
||||
// TypedObject intrinsics.
|
||||
if (native == intrinsic_ObjectIsTypedObject)
|
||||
return inlineHasClasses(callInfo,
|
||||
&TransparentTypedObject::class_, &OpaqueTypedObject::class_);
|
||||
return inlineHasClass(callInfo,
|
||||
&TransparentTypedObject::class_,
|
||||
&OwnedOpaqueTypedObject::class_,
|
||||
&InlineOpaqueTypedObject::class_);
|
||||
if (native == intrinsic_ObjectIsTransparentTypedObject)
|
||||
return inlineHasClass(callInfo, &TransparentTypedObject::class_);
|
||||
if (native == intrinsic_ObjectIsOpaqueTypedObject)
|
||||
return inlineHasClass(callInfo, &OpaqueTypedObject::class_);
|
||||
return inlineHasClass(callInfo,
|
||||
&OwnedOpaqueTypedObject::class_,
|
||||
&InlineOpaqueTypedObject::class_);
|
||||
if (native == intrinsic_ObjectIsTypeDescr)
|
||||
return inlineObjectIsTypeDescr(callInfo);
|
||||
if (native == intrinsic_TypeDescrIsSimpleType)
|
||||
return inlineHasClasses(callInfo,
|
||||
&ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
|
||||
return inlineHasClass(callInfo,
|
||||
&ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
|
||||
if (native == intrinsic_TypeDescrIsArrayType)
|
||||
return inlineHasClasses(callInfo,
|
||||
&SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_);
|
||||
return inlineHasClass(callInfo,
|
||||
&SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_);
|
||||
if (native == intrinsic_TypeDescrIsSizedArrayType)
|
||||
return inlineHasClass(callInfo, &SizedArrayTypeDescr::class_);
|
||||
if (native == intrinsic_TypeDescrIsUnsizedArrayType)
|
||||
@ -1759,11 +1765,8 @@ IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo)
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Class *clasp2)
|
||||
IonBuilder::inlineHasClass(CallInfo &callInfo, const Class *clasp1, const Class *clasp2, const Class *clasp3)
|
||||
{
|
||||
// Thus far there has been no reason to complicate this beyond two classes,
|
||||
// though it generalizes pretty well.
|
||||
// clasp2 may be NULL.
|
||||
if (callInfo.constructing() || callInfo.argc() != 1)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
@ -1775,21 +1778,27 @@ IonBuilder::inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Clas
|
||||
types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
|
||||
const Class *knownClass = types ? types->getKnownClass() : nullptr;
|
||||
if (knownClass) {
|
||||
pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2));
|
||||
pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2 || knownClass == clasp3));
|
||||
} else {
|
||||
MHasClass *hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
|
||||
current->add(hasClass1);
|
||||
if (clasp2 == nullptr) {
|
||||
|
||||
if (!clasp2 && !clasp3) {
|
||||
current->push(hasClass1);
|
||||
} else {
|
||||
// The following turns into branch-free, box-free code on x86, and should do so on ARM.
|
||||
MHasClass *hasClass2 = MHasClass::New(alloc(), callInfo.getArg(0), clasp2);
|
||||
current->add(hasClass2);
|
||||
MBitOr *either = MBitOr::New(alloc(), hasClass1, hasClass2);
|
||||
either->infer(inspector, pc);
|
||||
current->add(either);
|
||||
const Class *remaining[] = { clasp2, clasp3 };
|
||||
MDefinition *last = hasClass1;
|
||||
for (size_t i = 0; i < ArrayLength(remaining); i++) {
|
||||
MHasClass *hasClass = MHasClass::New(alloc(), callInfo.getArg(0), remaining[i]);
|
||||
current->add(hasClass);
|
||||
MBitOr *either = MBitOr::New(alloc(), last, hasClass);
|
||||
either->infer(inspector, pc);
|
||||
current->add(either);
|
||||
last = either;
|
||||
}
|
||||
|
||||
// Convert to bool with the '!!' idiom
|
||||
MNot *resultInverted = MNot::New(alloc(), either);
|
||||
MNot *resultInverted = MNot::New(alloc(), last);
|
||||
resultInverted->cacheOperandMightEmulateUndefined();
|
||||
current->add(resultInverted);
|
||||
MNot *result = MNot::New(alloc(), resultInverted);
|
||||
|
@ -101,9 +101,10 @@ jit::ParallelWriteGuard(ForkJoinContext *cx, JSObject *object)
|
||||
if (IsInTargetRegion(cx, &typedObj))
|
||||
return true;
|
||||
|
||||
// Also check whether owner is thread-local.
|
||||
ArrayBufferObject &owner = typedObj.owner();
|
||||
return cx->isThreadLocal(&owner);
|
||||
// Check whether the object which owns the memory is thread-local.
|
||||
if (typedObj.is<OwnedTypedObject>())
|
||||
return cx->isThreadLocal(&typedObj.as<OwnedTypedObject>().owner());
|
||||
return cx->isThreadLocal(&typedObj);
|
||||
}
|
||||
|
||||
// For other kinds of writable objects, must be thread-local.
|
||||
|
@ -1087,7 +1087,7 @@ RNewDerivedTypedObject::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||
// while bailing out, which could try to walk the stack.
|
||||
types::AutoEnterAnalysis enter(cx);
|
||||
|
||||
JSObject *obj = TypedObject::createDerived(cx, descr, owner, offset);
|
||||
JSObject *obj = OwnedTypedObject::createDerived(cx, descr, owner, offset);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -1019,7 +1019,7 @@ CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
|
||||
JS_ASSERT(owner->is<TypedObject>());
|
||||
Rooted<SizedTypeDescr*> descr1(cx, &descr->as<SizedTypeDescr>());
|
||||
Rooted<TypedObject*> owner1(cx, &owner->as<TypedObject>());
|
||||
return TypedObject::createDerived(cx, descr1, owner1, offset);
|
||||
return OwnedTypedObject::createDerived(cx, descr1, owner1, offset);
|
||||
}
|
||||
|
||||
JSString *
|
||||
|
@ -245,9 +245,6 @@ JS_CopyPropertyFrom(JSContext *cx, JS::HandleId id, JS::HandleObject target,
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_WrapPropertyDescriptor(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_WrapAutoIdVector(JSContext *cx, JS::AutoIdVector &props);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_EnumerateState(JSContext *cx, JS::HandleObject obj, JSIterateOp enum_op,
|
||||
JS::MutableHandleValue statep, JS::MutableHandleId idp);
|
||||
|
@ -161,6 +161,11 @@ var obj = {
|
||||
assertEq(obj.meth, 3);
|
||||
assertThrowsInstanceOf(function() {obj.meth();}, TypeError);
|
||||
|
||||
// Strict mode
|
||||
a = {b(c){"use strict";return c;}};
|
||||
assertEq(a.b(1), 1);
|
||||
a = {["b"](c){"use strict";return c;}};
|
||||
assertEq(a.b(1), 1);
|
||||
|
||||
// Tests provided by benvie in the bug to distinguish from ES5 desugar.
|
||||
assertEq(({ method() {} }).method.name, "method");
|
||||
|
@ -66,5 +66,10 @@ assertEq(next.done, true);
|
||||
assertEq(next.value.hello, 2);
|
||||
assertEq(next.value.world, 3);
|
||||
|
||||
// Strict mode
|
||||
a = {*b(c){"use strict";yield c;}};
|
||||
assertEq(a.b(1).next().value, 1);
|
||||
a = {*["b"](c){"use strict";return c;}};
|
||||
assertEq(a.b(1).next().value, 1);
|
||||
|
||||
reportCompare(0, 0, "ok");
|
||||
|
@ -996,7 +996,17 @@ ArrayBufferViewObject::neuter(void *newData)
|
||||
else if (is<TypedArrayObject>())
|
||||
as<TypedArrayObject>().neuter(newData);
|
||||
else
|
||||
as<TypedObject>().neuter(newData);
|
||||
as<OwnedTypedObject>().neuter(newData);
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
ArrayBufferViewObject::dataPointer()
|
||||
{
|
||||
if (is<DataViewObject>())
|
||||
return static_cast<uint8_t *>(as<DataViewObject>().dataPointer());
|
||||
if (is<TypedArrayObject>())
|
||||
return static_cast<uint8_t *>(as<TypedArrayObject>().viewData());
|
||||
return as<TypedObject>().typedMem();
|
||||
}
|
||||
|
||||
/* static */ ArrayBufferObject *
|
||||
|
@ -335,9 +335,8 @@ class ArrayBufferViewObject : public JSObject
|
||||
|
||||
void neuter(void *newData);
|
||||
|
||||
uint8_t *dataPointer() {
|
||||
return static_cast<uint8_t *>(getPrivate());
|
||||
}
|
||||
uint8_t *dataPointer();
|
||||
|
||||
static void trace(JSTracer *trc, JSObject *obj);
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "proxy/Proxy.h"
|
||||
#include "vm/ProxyObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
@ -40,15 +41,17 @@ ClassCanHaveFixedData(const Class *clasp)
|
||||
// arrays we only use enough to cover the class reserved slots, so that
|
||||
// the remaining space in the object's allocation is available for the
|
||||
// buffer's data.
|
||||
return clasp == &ArrayBufferObject::class_ || IsTypedArrayClass(clasp);
|
||||
return clasp == &ArrayBufferObject::class_
|
||||
|| clasp == &InlineOpaqueTypedObject::class_
|
||||
|| IsTypedArrayClass(clasp);
|
||||
}
|
||||
|
||||
inline void *
|
||||
inline uint8_t *
|
||||
ObjectImpl::fixedData(size_t nslots) const
|
||||
{
|
||||
JS_ASSERT(ClassCanHaveFixedData(getClass()));
|
||||
JS_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
|
||||
return &fixedSlots()[nslots];
|
||||
return reinterpret_cast<uint8_t *>(&fixedSlots()[nslots]);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
@ -873,7 +873,7 @@ class ObjectImpl : public gc::Cell
|
||||
* following this object, for use with objects which allocate a larger size
|
||||
* class than they need and store non-elements data inline.
|
||||
*/
|
||||
inline void *fixedData(size_t nslots) const;
|
||||
inline uint8_t *fixedData(size_t nslots) const;
|
||||
|
||||
/* GC support. */
|
||||
static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
|
||||
|
@ -861,6 +861,12 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("NewDerivedTypedObject",
|
||||
js::NewDerivedTypedObject,
|
||||
3, 0),
|
||||
JS_FN("TypedObjectBuffer",
|
||||
TypedObject::GetBuffer,
|
||||
1, 0),
|
||||
JS_FN("TypedObjectByteOffset",
|
||||
TypedObject::GetByteOffset,
|
||||
1, 0),
|
||||
JS_FNINFO("AttachTypedObject",
|
||||
JSNativeThreadSafeWrapper<js::AttachTypedObject>,
|
||||
&js::AttachTypedObjectJitInfo, 3, 0),
|
||||
|
@ -323,9 +323,7 @@ public:
|
||||
}
|
||||
bool IsBuildingLayerEventRegions()
|
||||
{
|
||||
// Disable for now.
|
||||
return false;
|
||||
// return mMode == PAINTING;
|
||||
return (gfxPrefs::LayoutEventRegionsEnabled() && mMode == PAINTING);
|
||||
}
|
||||
|
||||
bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }
|
||||
|
@ -5091,17 +5091,19 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
|
||||
|
||||
gfxSize destScale = didSnap ? gfxSize(currentMatrix._11, currentMatrix._22)
|
||||
: gfxSize(1.0, 1.0);
|
||||
gfxSize snappedDest(NSAppUnitsToIntPixels(dest.width * destScale.width,
|
||||
aAppUnitsPerDevPixel),
|
||||
NSAppUnitsToIntPixels(dest.height * destScale.height,
|
||||
aAppUnitsPerDevPixel));
|
||||
gfxSize appUnitScaledDest(dest.width * destScale.width,
|
||||
dest.height * destScale.height);
|
||||
gfxSize scaledDest = appUnitScaledDest / aAppUnitsPerDevPixel;
|
||||
gfxSize snappedScaledDest =
|
||||
gfxSize(NSAppUnitsToIntPixels(appUnitScaledDest.width, aAppUnitsPerDevPixel),
|
||||
NSAppUnitsToIntPixels(appUnitScaledDest.height, aAppUnitsPerDevPixel));
|
||||
|
||||
if (snappedDest.IsEmpty()) {
|
||||
if (scaledDest.IsEmpty() || snappedScaledDest.IsEmpty()) {
|
||||
return SnappedImageDrawingParameters();
|
||||
}
|
||||
|
||||
nsIntSize intImageSize =
|
||||
aImage->OptimalImageSizeForDest(snappedDest,
|
||||
aImage->OptimalImageSizeForDest(snappedScaledDest,
|
||||
imgIContainer::FRAME_CURRENT,
|
||||
aGraphicsFilter, aImageFlags);
|
||||
gfxSize imageSize(intImageSize.width, intImageSize.height);
|
||||
@ -5149,7 +5151,7 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
|
||||
anchorPoint.Round();
|
||||
}
|
||||
|
||||
gfxRect anchoredDestRect(anchorPoint, snappedDest);
|
||||
gfxRect anchoredDestRect(anchorPoint, scaledDest);
|
||||
gfxRect anchoredImageRect(imageSpaceAnchorPoint, imageSize);
|
||||
transform = TransformBetweenRects(anchoredImageRect, anchoredDestRect);
|
||||
invTransform = TransformBetweenRects(anchoredDestRect, anchoredImageRect);
|
||||
|
@ -6942,6 +6942,10 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
||||
|
||||
if (!nsContentUtils::IsSafeToRunScript() &&
|
||||
aEvent->IsAllowedToDispatchDOMEvent()) {
|
||||
if (aEvent->mClass == eCompositionEventClass ||
|
||||
aEvent->mClass == eTextEventClass) {
|
||||
IMEStateManager::OnCompositionEventDiscarded(aEvent);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (aEvent->IsIMERelatedEvent()) {
|
||||
nsPrintfCString warning("%d event is discarded", aEvent->message);
|
||||
|
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html reftest-zoom="1.3">
|
||||
<head>
|
||||
<title>Pixel rounding testcase</title>
|
||||
<style type="text/css">
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
background-color: lime;
|
||||
border: 1px solid black;
|
||||
margin: 5px;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
div.horizontal {
|
||||
height: 10px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
div.vertical {
|
||||
top: 120px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
div.horizontal-single-color {
|
||||
height: 10px;
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
div.vertical-single-color {
|
||||
top: 160px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="horizontal" style="top: 0px; width: 30px"></div>
|
||||
<div class="horizontal" style="top: 20px; width: 30.1px"></div>
|
||||
<div class="horizontal" style="top: 40px; width: 30.3px"></div>
|
||||
<div class="horizontal" style="top: 60px; width: 30.5px"></div>
|
||||
<div class="horizontal" style="top: 80px; width: 30.7px"></div>
|
||||
<div class="horizontal" style="top: 100px; width: 30.9px"></div>
|
||||
|
||||
<div class="horizontal-single-color" style="top: 0px; width: 30px"></div>
|
||||
<div class="horizontal-single-color" style="top: 20px; width: 30.1px"></div>
|
||||
<div class="horizontal-single-color" style="top: 40px; width: 30.3px"></div>
|
||||
<div class="horizontal-single-color" style="top: 60px; width: 30.5px"></div>
|
||||
<div class="horizontal-single-color" style="top: 80px; width: 30.7px"></div>
|
||||
<div class="horizontal-single-color" style="top: 100px; width: 30.9px"></div>
|
||||
|
||||
<div class="vertical" style="left: 0px; height: 30px"></div>
|
||||
<div class="vertical" style="left: 20px; height: 30.1px"></div>
|
||||
<div class="vertical" style="left: 40px; height: 30.3px"></div>
|
||||
<div class="vertical" style="left: 60px; height: 30.5px"></div>
|
||||
<div class="vertical" style="left: 80px; height: 30.7px"></div>
|
||||
<div class="vertical" style="left: 100px; height: 30.9px"></div>
|
||||
|
||||
<div class="vertical-single-color" style="left: 0px; height: 30px"></div>
|
||||
<div class="vertical-single-color" style="left: 20px; height: 30.1px"></div>
|
||||
<div class="vertical-single-color" style="left: 40px; height: 30.3px"></div>
|
||||
<div class="vertical-single-color" style="left: 60px; height: 30.5px"></div>
|
||||
<div class="vertical-single-color" style="left: 80px; height: 30.7px"></div>
|
||||
<div class="vertical-single-color" style="left: 100px; height: 30.9px"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
82
layout/reftests/backgrounds/background-tiling-zoom-1.html
Normal file
82
layout/reftests/backgrounds/background-tiling-zoom-1.html
Normal file
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html reftest-zoom="1.3">
|
||||
<head>
|
||||
<title>Pixel rounding testcase</title>
|
||||
<style type="text/css">
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
border: 1px solid black;
|
||||
margin: 5px;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
/* For these tests we use images containing two colors, even though only one
|
||||
color will be visible, to avoid special single-color-image code paths. */
|
||||
div.horizontal {
|
||||
background-image: url(lime-and-blue-1x25.png);
|
||||
height: 10px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
div.vertical {
|
||||
background-image: url(lime-and-blue-25x1.png);
|
||||
top: 120px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/* For these tests we use images containing only one color to ensure that we
|
||||
hit the special single-color-image code paths. */
|
||||
div.horizontal-single-color {
|
||||
background-image: url(lime-1x25.png);
|
||||
height: 10px;
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
div.vertical-single-color {
|
||||
background-image: url(lime-25x1.png);
|
||||
top: 160px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="horizontal" style="top: 0px; width: 30px"></div>
|
||||
<div class="horizontal" style="top: 20px; width: 30.1px"></div>
|
||||
<div class="horizontal" style="top: 40px; width: 30.3px"></div>
|
||||
<div class="horizontal" style="top: 60px; width: 30.5px"></div>
|
||||
<div class="horizontal" style="top: 80px; width: 30.7px"></div>
|
||||
<div class="horizontal" style="top: 100px; width: 30.9px"></div>
|
||||
|
||||
<div class="horizontal-single-color" style="top: 0px; width: 30px"></div>
|
||||
<div class="horizontal-single-color" style="top: 20px; width: 30.1px"></div>
|
||||
<div class="horizontal-single-color" style="top: 40px; width: 30.3px"></div>
|
||||
<div class="horizontal-single-color" style="top: 60px; width: 30.5px"></div>
|
||||
<div class="horizontal-single-color" style="top: 80px; width: 30.7px"></div>
|
||||
<div class="horizontal-single-color" style="top: 100px; width: 30.9px"></div>
|
||||
|
||||
<div class="vertical" style="left: 0px; height: 30px"></div>
|
||||
<div class="vertical" style="left: 20px; height: 30.1px"></div>
|
||||
<div class="vertical" style="left: 40px; height: 30.3px"></div>
|
||||
<div class="vertical" style="left: 60px; height: 30.5px"></div>
|
||||
<div class="vertical" style="left: 80px; height: 30.7px"></div>
|
||||
<div class="vertical" style="left: 100px; height: 30.9px"></div>
|
||||
|
||||
<div class="vertical-single-color" style="left: 0px; height: 30px"></div>
|
||||
<div class="vertical-single-color" style="left: 20px; height: 30.1px"></div>
|
||||
<div class="vertical-single-color" style="left: 40px; height: 30.3px"></div>
|
||||
<div class="vertical-single-color" style="left: 60px; height: 30.5px"></div>
|
||||
<div class="vertical-single-color" style="left: 80px; height: 30.7px"></div>
|
||||
<div class="vertical-single-color" style="left: 100px; height: 30.9px"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
layout/reftests/backgrounds/lime-1x25.png
Normal file
BIN
layout/reftests/backgrounds/lime-1x25.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
layout/reftests/backgrounds/lime-25x1.png
Normal file
BIN
layout/reftests/backgrounds/lime-25x1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
layout/reftests/backgrounds/lime-and-blue-1x25.png
Normal file
BIN
layout/reftests/backgrounds/lime-and-blue-1x25.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
layout/reftests/backgrounds/lime-and-blue-25x1.png
Normal file
BIN
layout/reftests/backgrounds/lime-and-blue-25x1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -161,3 +161,5 @@ fails-if(Android&&AndroidVersion==15) fuzzy-if(!Android||(Android&&AndroidVersio
|
||||
|
||||
== background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html
|
||||
== background-repeat-large-area.html background-repeat-large-area-ref.html
|
||||
|
||||
fuzzy(30,474) == background-tiling-zoom-1.html background-tiling-zoom-1-ref.html
|
||||
|
@ -66,6 +66,19 @@ static int GetCPUInfo(CPUFeature feature) {
|
||||
(void)feature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(WEBRTC_GONK) && !defined(ANDROID)
|
||||
#ifdef WEBRTC_ARCH_ARM_V7
|
||||
uint64_t WebRtc_GetCPUFeaturesARM(void) {
|
||||
return kCPUFeatureARMv7
|
||||
#ifdef WEBRTC_ARCH_ARM_NEON
|
||||
| kCPUFeatureNEON
|
||||
#endif
|
||||
| kCPUFeatureVFPv3;
|
||||
}
|
||||
#endif // WEBRTC_ARCH_ARM_V7
|
||||
#endif // !WEBRTC_GONK && !ANDROID
|
||||
|
||||
#endif
|
||||
|
||||
WebRtc_CPUInfo WebRtc_GetCPUInfo = GetCPUInfo;
|
||||
|
@ -92,3 +92,6 @@ if test "$NIGHTLY_BUILD"; then
|
||||
else
|
||||
MOZ_ANDROID_MLS_STUMBLER=
|
||||
fi
|
||||
|
||||
# Enable generational GC on mobile.
|
||||
JSGC_GENERATIONAL=1
|
||||
|
@ -440,6 +440,9 @@ pref("layers.async-pan-zoom.enabled", false);
|
||||
// Whether to enable containerless async scrolling
|
||||
pref("layout.async-containerless-scrolling.enabled", true);
|
||||
|
||||
// Whether to enable event region building during painting
|
||||
pref("layout.event-regions.enabled", false);
|
||||
|
||||
// APZ preferences. For documentation/details on what these prefs do, check
|
||||
// gfx/layers/apz/src/AsyncPanZoomController.cpp.
|
||||
pref("apz.allow_checkerboarding", true);
|
||||
|
@ -421,6 +421,26 @@ class MozbuildObject(ProcessExecutionMixin):
|
||||
if filename:
|
||||
args.extend(['-f', filename])
|
||||
|
||||
if num_jobs == 0 and self.mozconfig['make_flags']:
|
||||
flags = iter(self.mozconfig['make_flags'])
|
||||
for flag in flags:
|
||||
if flag == '-j':
|
||||
try:
|
||||
flag = flags.next()
|
||||
except StopIteration:
|
||||
break
|
||||
try:
|
||||
num_jobs = int(flag)
|
||||
except ValueError:
|
||||
args.append(flag)
|
||||
elif flag.startswith('-j'):
|
||||
try:
|
||||
num_jobs = int(flag[2:])
|
||||
except (ValueError, IndexError):
|
||||
break
|
||||
else:
|
||||
args.append(flag)
|
||||
|
||||
if allow_parallel:
|
||||
if num_jobs > 0:
|
||||
args.append('-j%d' % num_jobs)
|
||||
|
@ -924,11 +924,11 @@ class RunDmd(MachCommandBase):
|
||||
help='Do not pass the -no-remote argument by default.')
|
||||
@CommandArgument('--background', '-b', action='store_true',
|
||||
help='Do not pass the -foreground argument by default on Mac')
|
||||
@CommandArgument('--sample_below', default=None, type=str,
|
||||
@CommandArgument('--sample-below', default=None, type=str,
|
||||
help='The sample size to use, [1..n]. Default is 4093.')
|
||||
@CommandArgument('--max_frames', default=None, type=str,
|
||||
@CommandArgument('--max-frames', default=None, type=str,
|
||||
help='The max number of stack frames to capture in allocation traces, [1..24] Default is 24.')
|
||||
@CommandArgument('--max_records', default=None, type=str,
|
||||
@CommandArgument('--max-records', default=None, type=str,
|
||||
help='Number of stack trace records to print of each kind, [1..1000000]. Default is 1000.')
|
||||
def dmd(self, params, remote, background, sample_below, max_frames, max_records):
|
||||
args = get_run_args(self, params, remote, background)
|
||||
@ -963,6 +963,7 @@ class RunDmd(MachCommandBase):
|
||||
"DMD": dmd_str,
|
||||
},
|
||||
"Linux": {
|
||||
"LD_PRELOAD": dmd_lib,
|
||||
"LD_LIBRARY_PATH": lib_dir,
|
||||
"DMD": dmd_str,
|
||||
},
|
||||
|
@ -26,7 +26,7 @@
|
||||
* arbitrary data, passed as a (char*, size) tuple.
|
||||
*
|
||||
* Clients should use the GetSingleton() static method to access the cache. It
|
||||
* will be available from the end of XPCOM init (NS_InitXPCOM3 in nsXPComInit.cpp),
|
||||
* will be available from the end of XPCOM init (NS_InitXPCOM3 in XPCOMInit.cpp),
|
||||
* until XPCOM shutdown begins. The GetSingleton() method will return null if the cache
|
||||
* is unavailable. The cache is only provided for libxul builds --
|
||||
* it will fail to link in non-libxul builds. The XPCOM interface is provided
|
||||
|
@ -262,7 +262,7 @@ ComponentRegistrarInterposition.methods.registerFactory =
|
||||
|
||||
ComponentRegistrarInterposition.methods.unregisterFactory =
|
||||
function(addon, target, class_, factory) {
|
||||
AboutProtocolParent.tryUnregisterFactory(class_, factory);
|
||||
AboutProtocolParent.unregisterFactory(class_, factory);
|
||||
target.unregisterFactory(class_, factory);
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ Cu.import("resource://testing-common/httpd.js");
|
||||
// XXX until bug 937114 is fixed
|
||||
Cu.importGlobalProperties(["atob"]);
|
||||
|
||||
// The following boilerplate makes sure that XPCom calls
|
||||
// The following boilerplate makes sure that XPCOM calls
|
||||
// that use the profile directory work.
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -2926,7 +2926,7 @@ class XREMain
|
||||
{
|
||||
public:
|
||||
XREMain() :
|
||||
mScopedXPCom(nullptr)
|
||||
mScopedXPCOM(nullptr)
|
||||
, mAppData(nullptr)
|
||||
, mStartOffline(false)
|
||||
, mShuttingDown(false)
|
||||
@ -2942,9 +2942,9 @@ public:
|
||||
if (mAppData) {
|
||||
delete mAppData;
|
||||
}
|
||||
if (mScopedXPCom) {
|
||||
if (mScopedXPCOM) {
|
||||
NS_WARNING("Scoped xpcom should have been deleted!");
|
||||
delete mScopedXPCom;
|
||||
delete mScopedXPCOM;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2962,7 +2962,7 @@ public:
|
||||
nsCOMPtr<nsIRemoteService> mRemoteService;
|
||||
#endif
|
||||
|
||||
ScopedXPCOMStartup* mScopedXPCom;
|
||||
ScopedXPCOMStartup* mScopedXPCOM;
|
||||
ScopedAppData* mAppData;
|
||||
nsXREDirProvider mDirProvider;
|
||||
nsAutoCString mProfileName;
|
||||
@ -3917,7 +3917,7 @@ nsresult
|
||||
XREMain::XRE_mainRun()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
NS_ASSERTION(mScopedXPCom, "Scoped xpcom not initialized.");
|
||||
NS_ASSERTION(mScopedXPCOM, "Scoped xpcom not initialized.");
|
||||
|
||||
#ifdef MOZ_B2G_LOADER
|
||||
mozilla::ipc::ProcLoaderClientGeckoInit();
|
||||
@ -3943,7 +3943,7 @@ XREMain::XRE_mainRun()
|
||||
}
|
||||
#endif
|
||||
|
||||
rv = mScopedXPCom->SetWindowCreator(mNativeApp);
|
||||
rv = mScopedXPCOM->SetWindowCreator(mNativeApp);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
@ -4224,11 +4224,11 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
||||
bool appInitiatedRestart = false;
|
||||
|
||||
// Start the real application
|
||||
mScopedXPCom = new ScopedXPCOMStartup();
|
||||
if (!mScopedXPCom)
|
||||
mScopedXPCOM = new ScopedXPCOMStartup();
|
||||
if (!mScopedXPCOM)
|
||||
return 1;
|
||||
|
||||
rv = mScopedXPCom->Initialize();
|
||||
rv = mScopedXPCOM->Initialize();
|
||||
NS_ENSURE_SUCCESS(rv, 1);
|
||||
|
||||
// run!
|
||||
@ -4257,8 +4257,8 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
||||
#endif /* MOZ_ENABLE_XREMOTE */
|
||||
}
|
||||
|
||||
delete mScopedXPCom;
|
||||
mScopedXPCom = nullptr;
|
||||
delete mScopedXPCOM;
|
||||
mScopedXPCOM = nullptr;
|
||||
|
||||
// unlock the profile after ScopedXPCOMStartup object (xpcom)
|
||||
// has gone out of scope. see bug #386739 for more details
|
||||
@ -4330,11 +4330,11 @@ XRE_metroStartup(bool runXREMain)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Start the real application
|
||||
xreMainPtr->mScopedXPCom = new ScopedXPCOMStartup();
|
||||
if (!xreMainPtr->mScopedXPCom)
|
||||
xreMainPtr->mScopedXPCOM = new ScopedXPCOMStartup();
|
||||
if (!xreMainPtr->mScopedXPCOM)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
rv = xreMainPtr->mScopedXPCom->Initialize();
|
||||
rv = xreMainPtr->mScopedXPCOM->Initialize();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (runXREMain) {
|
||||
@ -4347,8 +4347,8 @@ XRE_metroStartup(bool runXREMain)
|
||||
void
|
||||
XRE_metroShutdown()
|
||||
{
|
||||
delete xreMainPtr->mScopedXPCom;
|
||||
xreMainPtr->mScopedXPCom = nullptr;
|
||||
delete xreMainPtr->mScopedXPCOM;
|
||||
xreMainPtr->mScopedXPCOM = nullptr;
|
||||
|
||||
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
|
||||
mozilla::ShutdownEventTracing();
|
||||
@ -4428,7 +4428,7 @@ XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData)
|
||||
|
||||
// XRE_metroShutdown should have already been called on the worker
|
||||
// thread that called XRE_metroStartup.
|
||||
NS_ASSERTION(!xreMainPtr->mScopedXPCom,
|
||||
NS_ASSERTION(!xreMainPtr->mScopedXPCOM,
|
||||
"XPCOM Shutdown hasn't occured, and we are exiting.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -65,15 +65,17 @@ const static bool kUseSimpleContextDefault = MOZ_WIDGET_GTK == 2;
|
||||
nsGtkIMModule* nsGtkIMModule::sLastFocusedModule = nullptr;
|
||||
bool nsGtkIMModule::sUseSimpleContext;
|
||||
|
||||
nsGtkIMModule::nsGtkIMModule(nsWindow* aOwnerWindow) :
|
||||
mOwnerWindow(aOwnerWindow), mLastFocusedWindow(nullptr),
|
||||
mContext(nullptr),
|
||||
mSimpleContext(nullptr),
|
||||
mDummyContext(nullptr),
|
||||
mCompositionStart(UINT32_MAX), mProcessingKeyEvent(nullptr),
|
||||
mCompositionTargetOffset(UINT32_MAX),
|
||||
mCompositionState(eCompositionState_NotComposing),
|
||||
mIsIMFocused(false), mIgnoreNativeCompositionEvent(false)
|
||||
nsGtkIMModule::nsGtkIMModule(nsWindow* aOwnerWindow)
|
||||
: mOwnerWindow(aOwnerWindow)
|
||||
, mLastFocusedWindow(nullptr)
|
||||
, mContext(nullptr)
|
||||
, mSimpleContext(nullptr)
|
||||
, mDummyContext(nullptr)
|
||||
, mCompositionStart(UINT32_MAX)
|
||||
, mProcessingKeyEvent(nullptr)
|
||||
, mCompositionTargetOffset(UINT32_MAX)
|
||||
, mCompositionState(eCompositionState_NotComposing)
|
||||
, mIsIMFocused(false)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gGtkIMLog) {
|
||||
@ -175,7 +177,7 @@ nsGtkIMModule::OnDestroyWindow(nsWindow* aWindow)
|
||||
NS_PRECONDITION(aWindow, "aWindow must not be null");
|
||||
|
||||
if (mLastFocusedWindow == aWindow) {
|
||||
CancelIMEComposition(aWindow);
|
||||
EndIMEComposition(aWindow);
|
||||
if (mIsIMFocused) {
|
||||
Blur();
|
||||
}
|
||||
@ -385,20 +387,12 @@ nsGtkIMModule::OnFocusChangeInGecko(bool aFocus)
|
||||
{
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
("GtkIMModule(%p): OnFocusChangeInGecko, aFocus=%s, "
|
||||
"mCompositionState=%s, mIsIMFocused=%s, "
|
||||
"mIgnoreNativeCompositionEvent=%s",
|
||||
"mCompositionState=%s, mIsIMFocused=%s",
|
||||
this, aFocus ? "YES" : "NO", GetCompositionStateName(),
|
||||
mIsIMFocused ? "YES" : "NO",
|
||||
mIgnoreNativeCompositionEvent ? "YES" : "NO"));
|
||||
mIsIMFocused ? "YES" : "NO"));
|
||||
|
||||
// We shouldn't carry over the removed string to another editor.
|
||||
mSelectedString.Truncate();
|
||||
|
||||
if (aFocus) {
|
||||
// If we failed to commit forcedely in previous focused editor,
|
||||
// we should reopen the gate for native signals in new focused editor.
|
||||
mIgnoreNativeCompositionEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -415,19 +409,18 @@ nsGtkIMModule::ResetIME()
|
||||
return;
|
||||
}
|
||||
|
||||
mIgnoreNativeCompositionEvent = true;
|
||||
gtk_im_context_reset(im);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGtkIMModule::CommitIMEComposition(nsWindow* aCaller)
|
||||
nsGtkIMModule::EndIMEComposition(nsWindow* aCaller)
|
||||
{
|
||||
if (MOZ_UNLIKELY(IsDestroyed())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
("GtkIMModule(%p): CommitIMEComposition, aCaller=%p, "
|
||||
("GtkIMModule(%p): EndIMEComposition, aCaller=%p, "
|
||||
"mCompositionState=%s",
|
||||
this, aCaller, GetCompositionStateName()));
|
||||
|
||||
@ -442,44 +435,15 @@ nsGtkIMModule::CommitIMEComposition(nsWindow* aCaller)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXX We should commit composition ourselves temporary...
|
||||
// Currently, GTK has API neither to commit nor to cancel composition
|
||||
// forcibly. Therefore, TextComposition will recompute commit string for
|
||||
// the request even if native IME will cause unexpected commit string.
|
||||
// So, we don't need to emulate commit or cancel composition with
|
||||
// proper composition events and a text event.
|
||||
// XXX ResetIME() might not enough for finishing compositoin on some
|
||||
// environments. We should emulate focus change too because some IMEs
|
||||
// may commit or cancel composition at blur.
|
||||
ResetIME();
|
||||
CommitCompositionBy(mDispatchedCompositionString);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGtkIMModule::CancelIMEComposition(nsWindow* aCaller)
|
||||
{
|
||||
if (MOZ_UNLIKELY(IsDestroyed())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
("GtkIMModule(%p): CancelIMEComposition, aCaller=%p",
|
||||
this, aCaller));
|
||||
|
||||
if (aCaller != mLastFocusedWindow) {
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
(" FAILED, the caller isn't focused window, mLastFocusedWindow=%p",
|
||||
mLastFocusedWindow));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!IsComposing()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GtkIMContext *im = GetContext();
|
||||
if (MOZ_UNLIKELY(!im)) {
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
(" FAILED, there are no context"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ResetIME();
|
||||
CommitCompositionBy(EmptyString());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -535,7 +499,7 @@ nsGtkIMModule::SetInputContext(nsWindow* aCaller,
|
||||
|
||||
// Release current IME focus if IME is enabled.
|
||||
if (changingEnabledState && IsEditable()) {
|
||||
CommitIMEComposition(mLastFocusedWindow);
|
||||
EndIMEComposition(mLastFocusedWindow);
|
||||
Blur();
|
||||
}
|
||||
|
||||
@ -696,6 +660,29 @@ nsGtkIMModule::Blur()
|
||||
mIsIMFocused = false;
|
||||
}
|
||||
|
||||
void
|
||||
nsGtkIMModule::OnSelectionChange(nsWindow* aCaller)
|
||||
{
|
||||
if (MOZ_UNLIKELY(IsDestroyed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
("GtkIMModule(%p): OnSelectionChange(aCaller=0x%p), "
|
||||
"mCompositionState=%s",
|
||||
this, aCaller, GetCompositionStateName()));
|
||||
|
||||
if (aCaller != mLastFocusedWindow) {
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
(" WARNING: the caller isn't focused window, "
|
||||
"mLastFocusedWindow=%p",
|
||||
mLastFocusedWindow));
|
||||
return;
|
||||
}
|
||||
|
||||
ResetIME();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsGtkIMModule::OnStartCompositionCallback(GtkIMContext *aContext,
|
||||
@ -748,15 +735,7 @@ nsGtkIMModule::OnEndCompositionNative(GtkIMContext *aContext)
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldIgnoreThisEvent = ShouldIgnoreNativeCompositionEvent();
|
||||
|
||||
// Finish the cancelling mode here rather than DispatchCompositionEnd()
|
||||
// because DispatchCompositionEnd() is called ourselves when we need to
|
||||
// commit the composition string *before* the focus moves completely.
|
||||
// Note that the native commit can be fired *after* ResetIME().
|
||||
mIgnoreNativeCompositionEvent = false;
|
||||
|
||||
if (!IsComposing() || shouldIgnoreThisEvent) {
|
||||
if (!IsComposing()) {
|
||||
// If we already handled the commit event, we should do nothing here.
|
||||
return;
|
||||
}
|
||||
@ -788,10 +767,6 @@ nsGtkIMModule::OnChangeCompositionNative(GtkIMContext *aContext)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldIgnoreNativeCompositionEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString compositionString;
|
||||
GetCompositionString(compositionString);
|
||||
if (!IsComposing() && compositionString.IsEmpty()) {
|
||||
@ -912,10 +887,6 @@ nsGtkIMModule::OnCommitCompositionNative(GtkIMContext *aContext,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldIgnoreNativeCompositionEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If IME doesn't change their keyevent that generated this commit,
|
||||
// don't send it through XIM - just send it as a normal key press
|
||||
// event.
|
||||
@ -1035,12 +1006,6 @@ nsGtkIMModule::DispatchCompositionStart()
|
||||
}
|
||||
}
|
||||
|
||||
if (mIgnoreNativeCompositionEvent) {
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
(" WARNING, mIgnoreNativeCompositionEvent is already TRUE, but we forcedly reset"));
|
||||
mIgnoreNativeCompositionEvent = false;
|
||||
}
|
||||
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
(" mCompositionStart=%u", mCompositionStart));
|
||||
mCompositionState = eCompositionState_CompositionStartDispatched;
|
||||
@ -1635,18 +1600,3 @@ nsGtkIMModule::InitEvent(WidgetGUIEvent& aEvent)
|
||||
{
|
||||
aEvent.time = PR_Now() / 1000;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGtkIMModule::ShouldIgnoreNativeCompositionEvent()
|
||||
{
|
||||
PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
|
||||
("GtkIMModule(%p): ShouldIgnoreNativeCompositionEvent, mLastFocusedWindow=%p, mIgnoreNativeCompositionEvent=%s",
|
||||
this, mLastFocusedWindow,
|
||||
mIgnoreNativeCompositionEvent ? "YES" : "NO"));
|
||||
|
||||
if (!mLastFocusedWindow) {
|
||||
return true; // cannot continue
|
||||
}
|
||||
|
||||
return mIgnoreNativeCompositionEvent;
|
||||
}
|
||||
|
@ -69,6 +69,9 @@ public:
|
||||
void OnDestroyWindow(nsWindow* aWindow);
|
||||
// OnFocusChangeInGecko is a notification that an editor gets focus.
|
||||
void OnFocusChangeInGecko(bool aFocus);
|
||||
// OnSelectionChange is a notification that selection (caret) is changed
|
||||
// in the focused editor.
|
||||
void OnSelectionChange(nsWindow* aCaller);
|
||||
|
||||
// OnKeyEvent is called when aWindow gets a native key press event or a
|
||||
// native key release event. If this returns TRUE, the key event was
|
||||
@ -79,12 +82,11 @@ public:
|
||||
bool aKeyDownEventWasSent = false);
|
||||
|
||||
// IME related nsIWidget methods.
|
||||
nsresult CommitIMEComposition(nsWindow* aCaller);
|
||||
nsresult EndIMEComposition(nsWindow* aCaller);
|
||||
void SetInputContext(nsWindow* aCaller,
|
||||
const InputContext* aContext,
|
||||
const InputContextAction* aAction);
|
||||
InputContext GetInputContext();
|
||||
nsresult CancelIMEComposition(nsWindow* aCaller);
|
||||
void OnUpdateComposition();
|
||||
|
||||
// If a software keyboard has been opened, this returns TRUE.
|
||||
@ -187,11 +189,6 @@ protected:
|
||||
// be processed as simple key event, this is set to TRUE by the commit
|
||||
// handler.
|
||||
bool mFilterKeyEvent;
|
||||
// When mIgnoreNativeCompositionEvent is TRUE, all native composition
|
||||
// should be ignored except that the compositon should be restarted in
|
||||
// another content (nsIContent). Don't refer this value directly, use
|
||||
// ShouldIgnoreNativeCompositionEvent().
|
||||
bool mIgnoreNativeCompositionEvent;
|
||||
// mKeyDownEventWasSent is used by OnKeyEvent() and
|
||||
// DispatchCompositionStart(). DispatchCompositionStart() dispatches
|
||||
// a keydown event if the composition start is caused by a native
|
||||
@ -289,8 +286,6 @@ protected:
|
||||
// Called before destroying the context to work around some platform bugs.
|
||||
void PrepareToDestroyContext(GtkIMContext *aContext);
|
||||
|
||||
bool ShouldIgnoreNativeCompositionEvent();
|
||||
|
||||
/**
|
||||
* WARNING:
|
||||
* Following methods dispatch gecko events. Then, the focused widget
|
||||
|
@ -5939,26 +5939,12 @@ NS_IMETHODIMP
|
||||
nsWindow::NotifyIME(const IMENotification& aIMENotification)
|
||||
{
|
||||
if (MOZ_UNLIKELY(!mIMModule)) {
|
||||
switch (aIMENotification.mMessage) {
|
||||
case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
switch (aIMENotification.mMessage) {
|
||||
// TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
|
||||
// NOTIFY_IME_OF_SELECTION_CHANGE. The required behavior is
|
||||
// really different from committing composition.
|
||||
case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
return mIMModule->CommitIMEComposition(this);
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
return mIMModule->CancelIMEComposition(this);
|
||||
return mIMModule->EndIMEComposition(this);
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
mIMModule->OnFocusChangeInGecko(true);
|
||||
return NS_OK;
|
||||
@ -5968,6 +5954,9 @@ nsWindow::NotifyIME(const IMENotification& aIMENotification)
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
mIMModule->OnUpdateComposition();
|
||||
return NS_OK;
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
mIMModule->OnSelectionChange(this);
|
||||
return NS_OK;
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@ -6001,6 +5990,18 @@ nsWindow::GetInputContext()
|
||||
return context;
|
||||
}
|
||||
|
||||
nsIMEUpdatePreference
|
||||
nsWindow::GetIMEUpdatePreference()
|
||||
{
|
||||
nsIMEUpdatePreference updatePreference(
|
||||
nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE);
|
||||
// We shouldn't notify IME of selection change caused by changes of
|
||||
// composition string. Therefore, we don't need to be notified selection
|
||||
// changes which are caused by text events handled.
|
||||
updatePreference.DontNotifyChangesCausedByComposition();
|
||||
return updatePreference;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
|
||||
const WidgetKeyboardEvent& aEvent,
|
||||
|
@ -264,6 +264,7 @@ public:
|
||||
NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
|
||||
const InputContextAction& aAction);
|
||||
NS_IMETHOD_(InputContext) GetInputContext();
|
||||
virtual nsIMEUpdatePreference GetIMEUpdatePreference();
|
||||
NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
|
||||
NativeKeyBindingsType aType,
|
||||
const mozilla::WidgetKeyboardEvent& aEvent,
|
||||
|
@ -502,11 +502,8 @@ struct SizeConstraints {
|
||||
typedef int8_t IMEMessageType;
|
||||
enum IMEMessage MOZ_ENUM_TYPE(IMEMessageType)
|
||||
{
|
||||
// XXX We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
|
||||
// NOTIFY_IME_OF_SELECTION_CHANGE later.
|
||||
NOTIFY_IME_OF_CURSOR_POS_CHANGED,
|
||||
// An editable content is getting focus
|
||||
NOTIFY_IME_OF_FOCUS,
|
||||
NOTIFY_IME_OF_FOCUS = 1,
|
||||
// An editable content is losing focus
|
||||
NOTIFY_IME_OF_BLUR,
|
||||
// Selection in the focused editable content is changed
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user