merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-09-26 13:20:34 +02:00
commit 5c58ebda5c
106 changed files with 1773 additions and 956 deletions

View File

@ -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;

View File

@ -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 &&

View File

@ -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>

View File

@ -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();
}

View File

@ -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");

View File

@ -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.

View File

@ -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):

View File

@ -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

View File

@ -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,

View File

@ -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/

View File

@ -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

View File

@ -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 ========================================================

View File

@ -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">

View File

@ -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) {

View File

@ -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;

View File

@ -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)) {

View File

@ -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);

View File

@ -26,7 +26,7 @@ UNIFIED_SOURCES += [
]
if CONFIG['MOZ_B2G_CAMERA']:
SOURCES += [
UNIFIED_SOURCES += [
'GonkCameraControl.cpp',
'GonkCameraHwMgr.cpp',
'GonkCameraManager.cpp',

View File

@ -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();
}

View File

@ -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.

View File

@ -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:

View File

@ -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);
};
/**

View 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>

View File

@ -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

View File

@ -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.

View File

@ -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',

View File

@ -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']

View File

@ -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',

View File

@ -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

View File

@ -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);

View File

@ -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']

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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(); }

View File

@ -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();
}

View File

@ -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 } },

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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']

View File

@ -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']

View File

@ -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']

View File

@ -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']

View File

@ -4,7 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['tests']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']

View File

@ -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']

View File

@ -4,7 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['tests']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
XPIDL_SOURCES += [
'nsIStringBundle.idl',

View File

@ -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']

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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))

View File

@ -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,

View File

@ -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();
}

View File

@ -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()));

View 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);

View File

@ -1,4 +1,4 @@
// |jit-test| ion-eager; debug;
// |jit-test| ion-eager;
var x
(function() {
x

View 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) {}
}
}

View File

@ -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_), &notOwned);
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(&notOwned);
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_), &notOwned);
masm.loadPtr(Address(obj, OwnedTypedObject::offsetOfDataSlot()), out);
masm.jump(&done);
masm.bind(&notOwned);
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;
}

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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 *

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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 *

View File

@ -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);
};

View File

@ -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

View File

@ -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; }

View File

@ -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),

View File

@ -323,9 +323,7 @@ public:
}
bool IsBuildingLayerEventRegions()
{
// Disable for now.
return false;
// return mMode == PAINTING;
return (gfxPrefs::LayoutEventRegionsEnabled() && mMode == PAINTING);
}
bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }

View File

@ -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);

View File

@ -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);

View File

@ -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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -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

View File

@ -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;

View File

@ -92,3 +92,6 @@ if test "$NIGHTLY_BUILD"; then
else
MOZ_ANDROID_MLS_STUMBLER=
fi
# Enable generational GC on mobile.
JSGC_GENERATIONAL=1

View File

@ -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);

View File

@ -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)

View File

@ -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,
},

View File

@ -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

View File

@ -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);
};

View File

@ -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");

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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