Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-10-03 16:00:09 -04:00
commit 3b1ad0f2bc
99 changed files with 55616 additions and 90125 deletions

View File

@ -8,6 +8,8 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/ContentWebRTC.jsm");
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
"resource:///modules/E10SUtils.jsm");
@ -100,7 +102,15 @@ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
}
if (!defaultPrevented) {
sendSyncMessage("contextmenu", {}, { event: event });
let editFlags = SpellCheckHelper.isEditable(event.target, content);
let spellInfo;
if (editFlags &
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
spellInfo =
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
}
sendSyncMessage("contextmenu", { editFlags, spellInfo }, { event });
}
}, false);
} else {

View File

@ -1,8 +1,10 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
var gContextMenuContentData = null;
@ -518,11 +520,13 @@ nsContextMenu.prototype = {
setTarget: function (aNode, aRangeParent, aRangeOffset) {
// If gContextMenuContentData is not null, this event was forwarded from a
// child process, so use that information instead.
let editFlags;
if (gContextMenuContentData) {
this.isRemote = true;
aNode = gContextMenuContentData.event.target;
aRangeParent = gContextMenuContentData.event.rangeParent;
aRangeOffset = gContextMenuContentData.event.rangeOffset;
editFlags = gContextMenuContentData.editFlags;
} else {
this.isRemote = false;
}
@ -578,6 +582,7 @@ nsContextMenu.prototype = {
if (this.isRemote) {
this.browser = gContextMenuContentData.browser;
} else {
editFlags = SpellCheckHelper.isEditable(this.target, window);
this.browser = this.target.ownerDocument.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
@ -628,24 +633,19 @@ nsContextMenu.prototype = {
this.onAudio = true;
this.mediaURL = this.target.currentSrc || this.target.src;
}
else if (this.target instanceof HTMLInputElement ) {
this.onTextInput = this.isTargetATextBox(this.target);
// Allow spellchecking UI on all text and search inputs.
if (this.onTextInput && ! this.target.readOnly &&
(this.target.type == "text" || this.target.type == "search")) {
this.onEditableArea = true;
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
}
this.onKeywordField = this.isTargetAKeywordField(this.target);
}
else if (this.target instanceof HTMLTextAreaElement) {
this.onTextInput = true;
if (!this.target.readOnly) {
this.onEditableArea = true;
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
if (this.onEditableArea) {
if (gContextMenuContentData) {
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
}
else {
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
}
}
this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
}
else if (this.target instanceof HTMLHtmlElement) {
var bodyElt = this.target.ownerDocument.body;
@ -748,41 +748,35 @@ nsContextMenu.prototype = {
// if the document is editable, show context menu like in text inputs
if (!this.onEditableArea) {
win = this.target.ownerDocument.defaultView;
if (win) {
var isEditable = false;
try {
var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
if (editingSession.windowIsEditable(win) &&
this.getComputedStyle(this.target, "-moz-user-modify") == "read-write") {
isEditable = true;
}
if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
// If this.onEditableArea is false but editFlags is CONTENTEDITABLE, then
// the document itself must be editable.
this.onTextInput = true;
this.onKeywordField = false;
this.onImage = false;
this.onLoadedImage = false;
this.onCompletedImage = false;
this.onMathML = false;
this.inFrame = false;
this.inSrcdocFrame = false;
this.hasBGImage = false;
this.isDesignMode = true;
this.onEditableArea = true;
if (gContextMenuContentData) {
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
}
catch(ex) {
// If someone built with composer disabled, we can't get an editing session.
}
if (isEditable) {
this.onTextInput = true;
this.onKeywordField = false;
this.onImage = false;
this.onLoadedImage = false;
this.onCompletedImage = false;
this.onMathML = false;
this.inFrame = false;
this.inSrcdocFrame = false;
this.hasBGImage = false;
this.isDesignMode = true;
this.onEditableArea = true;
InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
else {
var targetWin = this.target.ownerDocument.defaultView;
var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
this.showItem("spell-check-enabled", canSpell);
this.showItem("spell-separator", canSpell);
}
var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
this.showItem("spell-check-enabled", canSpell);
this.showItem("spell-separator", canSpell);
}
}
},
@ -1502,30 +1496,6 @@ nsContextMenu.prototype = {
return (node instanceof HTMLTextAreaElement);
},
isTargetAKeywordField: function(aNode) {
if (!(aNode instanceof HTMLInputElement))
return false;
var form = aNode.form;
if (!form || aNode.type == "password")
return false;
var method = form.method.toUpperCase();
// These are the following types of forms we can create keywords for:
//
// method encoding type can create keyword
// GET * YES
// * YES
// POST YES
// POST application/x-www-form-urlencoded YES
// POST text/plain NO (a little tricky to do)
// POST multipart/form-data NO
// POST everything else YES
return (method == "GET" || method == "") ||
(form.enctype != "text/plain") && (form.enctype != "multipart/form-data");
},
// Determines whether or not the separator with the specified ID should be
// shown or not by determining if there are any non-hidden items between it
// and the previous separator.

View File

@ -3049,8 +3049,13 @@
break;
}
case "contextmenu": {
let spellInfo = aMessage.data.spellInfo;
if (spellInfo)
spellInfo.target = aMessage.target.messageManager;
gContextMenuContentData = { event: aMessage.objects.event,
browser: browser };
browser: browser,
editFlags: aMessage.data.editFlags,
spellInfo: spellInfo };
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);

View File

@ -31,6 +31,7 @@ const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
const MAX_CLIPBOARD_DATA_SIZE = 8000;
const MAX_USER_INPUT_TIMEOUT = 250; // ms
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
@ -256,6 +257,7 @@ function ChromeActions(url, window, document) {
this.document = document;
this.externalComInitialized = false;
this.allowScriptAccess = false;
this.lastUserInput = 0;
this.crossdomainRequestsCache = Object.create(null);
this.telemetry = {
startTime: Date.now(),
@ -416,9 +418,10 @@ ChromeActions.prototype = {
fallbackToNativePlugin(this.window, !automatic, automatic);
},
setClipboard: function (data) {
// We don't trust our Shumway non-privileged code just yet to verify the
// user input -- using monitorUserInput function below to track that.
if (typeof data !== 'string' ||
data.length > MAX_CLIPBOARD_DATA_SIZE ||
!this.document.hasFocus()) {
(Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) {
return;
}
// TODO other security checks?
@ -472,25 +475,33 @@ ChromeActions.prototype = {
}
},
reportIssue: function(exceptions) {
var base = "http://shumway-issue-reporter.paas.allizom.org/input?";
var urlTemplate = "https://bugzilla.mozilla.org/enter_bug.cgi?op_sys=All&priority=--" +
"&rep_platform=All&target_milestone=---&version=Trunk&product=Firefox" +
"&component=Shumway&short_desc=&comment={comment}" +
"&bug_file_loc={url}";
var windowUrl = this.window.parent.wrappedJSObject.location + '';
var params = 'url=' + encodeURIComponent(windowUrl);
params += '&swf=' + encodeURIComponent(this.url);
var url = urlTemplate.split('{url}').join(encodeURIComponent(windowUrl));
var params = {
swf: encodeURIComponent(this.url)
};
getVersionInfo().then(function (versions) {
params += '&ffbuild=' + encodeURIComponent(versions.geckoMstone + ' (' +
versions.geckoBuildID + ')');
params += '&shubuild=' + encodeURIComponent(versions.shumwayVersion);
params.versions = versions;
}).then(function () {
var postDataStream = StringInputStream.
createInstance(Ci.nsIStringInputStream);
postDataStream.data = 'exceptions=' + encodeURIComponent(exceptions);
var postData = MimeInputStream.createInstance(Ci.nsIMIMEInputStream);
postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
postData.addContentLength = true;
postData.setData(postDataStream);
this.window.openDialog('chrome://browser/content', '_blank',
'all,dialog=no', base + params, null, null,
postData);
params.ffbuild = encodeURIComponent(params.versions.geckoMstone +
' (' + params.versions.geckoBuildID + ')');
params.shubuild = encodeURIComponent(params.versions.shumwayVersion);
params.exceptions = encodeURIComponent(exceptions);
var comment = '%2B%2B%2B This bug was initially via the problem reporting functionality in ' +
'Shumway %2B%2B%2B%0A%0A' +
'Please add any further information that you deem helpful here:%0A%0A%0A' +
'----------------------%0A%0A' +
'Technical Information:%0A' +
'Firefox version: ' + params.ffbuild + '%0A' +
'Shumway version: ' + params.shubuild;
url = url.split('{comment}').join(comment);
//this.window.openDialog('chrome://browser/content', '_blank', 'all,dialog=no', url);
dump(111);
this.window.open(url);
}.bind(this));
},
externalCom: function (data) {
@ -526,6 +537,23 @@ ChromeActions.prototype = {
}
};
function monitorUserInput(actions) {
function notifyUserInput() {
var win = actions.window;
var winUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
if (winUtils.isHandlingUserInput) {
actions.lastUserInput = Date.now();
}
}
var document = actions.document;
document.addEventListener('mousedown', notifyUserInput, false);
document.addEventListener('mouseup', notifyUserInput, false);
document.addEventListener('keydown', notifyUserInput, false);
document.addEventListener('keyup', notifyUserInput, false);
}
// Event listener to trigger chrome privedged code.
function RequestListener(actions) {
this.actions = actions;
@ -737,11 +765,12 @@ function initExternalCom(wrappedWindow, wrappedObject, targetDocument) {
return '<undefined/>';
}
};
var sandbox = new Cu.Sandbox(wrappedWindow, {sandboxPrototype: wrappedWindow});
wrappedWindow.__flash__eval = function (evalInSandbox, sandbox, expr) {
wrappedWindow.__flash__eval = function (expr) {
this.console.log('__flash__eval: ' + expr);
return evalInSandbox(expr, sandbox);
}.bind(wrappedWindow, Cu.evalInSandbox, sandbox);
// allowScriptAccess protects page from unwanted swf scripts,
// we can execute script in the page context without restrictions.
return this.eval(expr);
}.bind(wrappedWindow);
wrappedWindow.__flash__call = function (expr) {
this.console.log('__flash__call (ignored): ' + expr);
};
@ -989,6 +1018,7 @@ ShumwayStreamConverterBase.prototype = {
domWindow.addEventListener('shumway.message', function(event) {
requestListener.receive(event);
}, false, true);
monitorUserInput(actions);
listener.onStopRequest(aRequest, context, statusCode);
}

View File

@ -29,7 +29,6 @@ let Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://shumway/ShumwayStreamConverter.jsm');
let Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
@ -111,6 +110,7 @@ let ShumwayUtils = {
return;
// Load the component and register it.
Cu.import('resource://shumway/ShumwayStreamConverter.jsm');
converterFactory.register(ShumwayStreamConverter);
overlayConverterFactory.register(ShumwayStreamOverlayConverter);
@ -130,6 +130,7 @@ let ShumwayUtils = {
// Remove the contract/component.
converterFactory.unregister();
overlayConverterFactory.unregister();
Cu.unload('resource://shumway/ShumwayStreamConverter.jsm');
Svc.pluginHost.unregisterPlayPreviewMimeType(SWF_CONTENT_TYPE);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
0.9.2697
25bfcc9
0.9.2970
22f884f

View File

@ -24,13 +24,17 @@ limitations under the License.
body {
margin: 0;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.78);
background-color: transparent;
}
body.started {
background-color: transparent;
}
body.started iframe {
display: none;
}
#stageContainer {
position:fixed !important;
left:0;top:0;bottom:0;right:0;
@ -106,7 +110,8 @@ limitations under the License.
<menuitem label="Show URL" id="showURLMenu"></menuitem>
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Report Problems" id="reportMenu"></menuitem>
<menuitem label="Fallback to Flash" id="fallbackMenu" hidden></menuitem>
<menuitem label="Reload in Adobe Flash Player" id="fallbackMenu" hidden></menuitem>
<menuitem label="About Shumway" id="aboutMenu"></menuitem>
</menu>
</section>
</body>

View File

@ -144,12 +144,10 @@ function runViewer() {
fallbackMenu.removeAttribute('hidden');
fallbackMenu.addEventListener('click', fallback);
}
var showURLMenu = document.getElementById('showURLMenu');
showURLMenu.addEventListener('click', showURL);
var inspectorMenu = document.getElementById('inspectorMenu');
inspectorMenu.addEventListener('click', showInInspector);
var reportMenu = document.getElementById('reportMenu');
reportMenu.addEventListener('click', reportIssue);
document.getElementById('showURLMenu').addEventListener('click', showURL);
document.getElementById('inspectorMenu').addEventListener('click', showInInspector);
document.getElementById('reportMenu').addEventListener('click', reportIssue);
document.getElementById('aboutMenu').addEventListener('click', showAbout);
}
function showURL() {
@ -166,23 +164,28 @@ function showInInspector() {
}
function reportIssue() {
var duplicatesMap = Object.create(null);
var prunedExceptions = [];
avm2.exceptions.forEach(function(e) {
var ident = e.source + e.message + e.stack;
var entry = duplicatesMap[ident];
if (!entry) {
entry = duplicatesMap[ident] = {
source: e.source,
message: e.message,
stack: e.stack,
count: 0
};
prunedExceptions.push(entry);
}
entry.count++;
});
FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions));
//var duplicatesMap = Object.create(null);
//var prunedExceptions = [];
//avm2.exceptions.forEach(function(e) {
// var ident = e.source + e.message + e.stack;
// var entry = duplicatesMap[ident];
// if (!entry) {
// entry = duplicatesMap[ident] = {
// source: e.source,
// message: e.message,
// stack: e.stack,
// count: 0
// };
// prunedExceptions.push(entry);
// }
// entry.count++;
//});
//FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions));
FirefoxCom.requestSync('reportIssue');
}
function showAbout() {
window.open('http://areweflashyet.com/');
}
var movieUrl, movieParams, objectParams;
@ -202,6 +205,12 @@ window.addEventListener("message", function handlerMessage(e) {
case 'reportTelemetry':
FirefoxCom.request('reportTelemetry', args.data, null);
break;
case 'setClipboard':
FirefoxCom.request('setClipboard', args.data, null);
break;
case 'started':
document.body.classList.add('started');
break;
}
}, true);
@ -240,7 +249,19 @@ function parseSwf(url, movieParams, objectParams) {
FirefoxCom.request('endActivation', null);
}
var easel = createEasel();
var bgcolor;
if (objectParams) {
var m;
if (objectParams.bgcolor && (m = /#([0-9A-F]{6})/i.exec(objectParams.bgcolor))) {
var hexColor = parseInt(m[1], 16);
bgcolor = hexColor << 8 | 0xff;
}
if (objectParams.wmode === 'transparent') {
bgcolor = 0;
}
}
var easel = createEasel(bgcolor);
easelHost = new Shumway.GFX.Window.WindowEaselHost(easel, playerWindow, window);
easelHost.processExternalCommand = processExternalCommand;
@ -252,6 +273,7 @@ function parseSwf(url, movieParams, objectParams) {
movieParams: movieParams,
objectParams: objectParams,
turboMode: turboMode,
bgcolor: bgcolor,
url: url,
baseUrl: url
}
@ -259,12 +281,12 @@ function parseSwf(url, movieParams, objectParams) {
playerWindow.postMessage(data, '*');
}
function createEasel() {
function createEasel(bgcolor) {
var Stage = Shumway.GFX.Stage;
var Easel = Shumway.GFX.Easel;
var Canvas2DStageRenderer = Shumway.GFX.Canvas2DStageRenderer;
Shumway.GFX.WebGL.SHADER_ROOT = SHUMWAY_ROOT + "gfx/gl/shaders/";
var backend = Shumway.GFX.backend.value | 0;
return new Easel(document.getElementById("stageContainer"), backend);
return new Easel(document.getElementById("stageContainer"), backend, false, bgcolor);
}

View File

@ -48,6 +48,7 @@ function runSwfPlayer(flashParams) {
Shumway.createAVM2(builtinPath, viewerPlayerglobalInfo, avm1Path, sysMode, appMode, function (avm2) {
function runSWF(file) {
var player = new Shumway.Player.Window.WindowPlayer(window, window.parent);
player.defaultStageColor = flashParams.bgcolor;
Shumway.ExternalInterfaceService.instance = player.createExternalInterfaceService();
@ -79,10 +80,17 @@ function setupServices() {
}
};
Shumway.ClipboardService.instance = {
setClipboard: function (data) {
window.parent.postMessage({
callback: 'setClipboard',
data: data
}, '*');
}
};
Shumway.FileLoadingService.instance = {
get baseUrl() {
return movieUrl;
},
baseUrl: null,
nextSessionId: 1, // 0 - is reserved
sessions: [],
createSession: function () {
@ -168,7 +176,11 @@ window.addEventListener('message', function onWindowMessage(e) {
}
setupServices();
runSwfPlayer(data.flashParams);
document.body.style.backgroundColor = 'green';
window.parent.postMessage({
callback: 'started'
}, '*');
break;
}
}, true);

View File

@ -326,20 +326,6 @@ 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

@ -637,8 +637,6 @@ endif
endif # NO_PROFILE_GUIDED_OPTIMIZE
MOZ_PROGRAM_LDFLAGS += $(MOZ_GLUE_PROGRAM_LDFLAGS)
##############################################
checkout:
@ -671,7 +669,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_PROGRAM_LDFLAGS) $(PROGOBJS) $(RESFILE) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
$(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)
ifdef MSMANIFEST_TOOL
@if test -f $@.manifest; then \
if test -f '$(srcdir)/$@.manifest'; then \
@ -692,7 +690,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_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_GLUE_PROGRAM_LDFLAGS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS)
$(call CHECK_BINARY,$@)
endif # WINNT && !GNU_CC
@ -740,7 +738,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_PROGRAM_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_LIBS) $(OS_LIBS)
$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_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; \
@ -748,7 +746,7 @@ ifdef MSMANIFEST_TOOL
fi
endif # MSVC with manifest tool
else
$(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)
$(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)
$(call CHECK_BINARY,$@)
endif # WINNT && !GNU_CC

View File

@ -2141,6 +2141,9 @@ ia64*-hpux*)
# and requires workarounds for perfectly valid code. Also, GCC/clang
# don't warn about it by default. So for consistency/sanity, we turn
# it off on MSVC, too.
# MSVC warning C4267 warns for narrowing type conversions from size_t
# to 32-bit integer types on 64-bit platforms. Since this is virtually
# the same thing as C4244, we disable C4267, too.
# MSVC warning C4345 warns of newly conformant behavior as of VS2003.
# MSVC warning C4351 warns of newly conformant behavior as of VS2005.
# MSVC warning C4482 warns when an enum value is refered specifing the
@ -2152,8 +2155,8 @@ ia64*-hpux*)
# that behavior) that it's better to turn it off.
# MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign)
# on non-Western system locales even if it is in a comment.
CFLAGS="$CFLAGS -wd4244 -wd4819"
CXXFLAGS="$CXXFLAGS -wd4251 -wd4244 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
CFLAGS="$CFLAGS -wd4244 -wd4267 -wd4819"
CXXFLAGS="$CXXFLAGS -wd4251 -wd4244 -wd4267 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
# make 'foo == bar;' error out
CFLAGS="$CFLAGS -we4553"
CXXFLAGS="$CXXFLAGS -we4553"

View File

@ -55,10 +55,18 @@ AudioSink::Init()
nullptr,
MEDIA_THREAD_STACK_SIZE);
if (NS_FAILED(rv)) {
mStateMachine->OnAudioSinkError();
return rv;
}
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
return mThread->Dispatch(event, NS_DISPATCH_NORMAL);
rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
mStateMachine->OnAudioSinkError();
return rv;
}
return NS_OK;
}
int64_t
@ -138,6 +146,8 @@ AudioSink::AudioLoop()
if (NS_FAILED(InitializeAudioStream())) {
NS_WARNING("Initializing AudioStream failed.");
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mStateMachine->OnAudioSinkError();
return;
}
@ -197,10 +207,13 @@ AudioSink::InitializeAudioStream()
// circumstances, so we take care to drop the decoder monitor while
// initializing.
RefPtr<AudioStream> audioStream(new AudioStream());
audioStream->Init(mInfo.mChannels, mInfo.mRate,
mChannel, AudioStream::HighLatency);
// TODO: Check Init's return value and bail on error. Unfortunately this
// causes some tests to fail due to playback failing.
nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
mChannel, AudioStream::HighLatency);
if (NS_FAILED(rv)) {
audioStream->Shutdown();
return rv;
}
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mAudioStream = audioStream;
UpdateStreamSettings();

View File

@ -1154,9 +1154,9 @@ void MediaDecoderStateMachine::StartPlayback()
SetPlayStartTime(TimeStamp::Now());
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
if (NS_FAILED(StartAudioThread())) {
DECODER_WARN("Failed to create audio thread");
}
nsresult rv = StartAudioThread();
NS_ENSURE_SUCCESS_VOID(rv);
mDecoder->GetReentrantMonitor().NotifyAll();
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
DispatchDecodeTasksIfNeeded();
@ -1791,15 +1791,12 @@ MediaDecoderStateMachine::StartAudioThread()
mStopAudioThread = false;
if (HasAudio() && !mAudioSink) {
mAudioCompleted = false;
mAudioSink = new AudioSink(this,
mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
mAudioSink = new AudioSink(this, mAudioStartTime,
mInfo.mAudio, mDecoder->GetAudioChannel());
// OnAudioSinkError() will be called before Init() returns if an error
// occurs during initialization.
nsresult rv = mAudioSink->Init();
if (NS_FAILED(rv)) {
DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
SetState(DECODER_STATE_SHUTDOWN);
mScheduler->ScheduleAndShutdown();
return rv;
}
NS_ENSURE_SUCCESS(rv, rv);
mAudioSink->SetVolume(mVolume);
mAudioSink->SetPlaybackRate(mPlaybackRate);
@ -3124,6 +3121,25 @@ void MediaDecoderStateMachine::OnAudioSinkComplete()
mDecoder->GetReentrantMonitor().NotifyAll();
}
void MediaDecoderStateMachine::OnAudioSinkError()
{
AssertCurrentThreadInMonitor();
// AudioSink not used with captured streams, so ignore errors in this case.
if (mAudioCaptured) {
return;
}
mAudioCompleted = true;
// Notify media decoder/element about this error.
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnDecodeError));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
if (NS_FAILED(rv)) {
DECODER_WARN("Failed to dispatch OnDecodeError");
}
}
} // namespace mozilla
// avoid redefined macro in unified build

View File

@ -636,6 +636,9 @@ protected:
// and the sink is shutting down.
void OnAudioSinkComplete();
// Called by the AudioSink to signal errors.
void OnAudioSinkError();
// The decoder object that created this state machine. The state machine
// holds a strong reference to the decoder to ensure that the decoder stays
// alive once media element has started the decoder shutdown process, and has

View File

@ -98,6 +98,9 @@ public:
private:
void NotifyDataArrived()
{
if (mOmxReader->IsShutdown()) {
return;
}
const char* buffer = mBuffer.get();
while (mLength) {
@ -118,15 +121,23 @@ private:
}
}
nsRefPtr<MediaOmxReader> mOmxReader;
nsRefPtr<MediaOmxReader> mOmxReader;
nsAutoArrayPtr<const char> mBuffer;
uint64_t mLength;
int64_t mOffset;
uint64_t mFullLength;
};
void MediaOmxReader::CancelProcessCachedData()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
mIsShutdown = true;
}
MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
: MediaOmxCommonReader(aDecoder)
, mMutex("MediaOmxReader.Data")
, mMP3FrameParser(-1)
, mHasVideo(false)
, mHasAudio(false)
@ -135,6 +146,7 @@ MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
, mSkipCount(0)
, mUseParserDuration(false)
, mLastParserDuration(-1)
, mIsShutdown(false)
{
#ifdef PR_LOGGING
if (!gMediaDecoderLog) {
@ -164,6 +176,10 @@ void MediaOmxReader::ReleaseDecoder()
void MediaOmxReader::Shutdown()
{
nsCOMPtr<nsIRunnable> cancelEvent =
NS_NewRunnableMethod(this, &MediaOmxReader::CancelProcessCachedData);
NS_DispatchToMainThread(cancelEvent);
ReleaseMediaResources();
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaOmxReader::ReleaseDecoder);
@ -449,7 +465,9 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
void MediaOmxReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
MOZ_ASSERT(NS_IsMainThread());
if (IsShutdown()) {
return;
}
if (HasVideo()) {
return;
}
@ -547,6 +565,10 @@ void MediaOmxReader::EnsureActive() {
int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion)
{
// Could run on decoder thread or IO thread.
if (IsShutdown()) {
return -1;
}
// We read data in chunks of 32 KiB. We can reduce this
// value if media, such as sdcards, is too slow.
// Because of SD card's slowness, need to keep sReadSize to small size.

View File

@ -30,6 +30,8 @@ class AbstractMediaDecoder;
class MediaOmxReader : public MediaOmxCommonReader
{
// This flag protect the mIsShutdown variable, that may access by decoder / main / IO thread.
Mutex mMutex;
nsCString mType;
bool mHasVideo;
bool mHasAudio;
@ -40,6 +42,7 @@ class MediaOmxReader : public MediaOmxCommonReader
int64_t mLastParserDuration;
int32_t mSkipCount;
bool mUseParserDuration;
bool mIsShutdown;
protected:
android::sp<android::OmxDecoder> mOmxDecoder;
android::sp<android::MediaExtractor> mExtractor;
@ -91,10 +94,17 @@ public:
virtual void Shutdown() MOZ_OVERRIDE;
bool IsShutdown() {
MutexAutoLock lock(mMutex);
return mIsShutdown;
}
void ReleaseDecoder();
int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
void CancelProcessCachedData();
android::sp<android::MediaSource> GetAudioOffloadTrack();
};

View File

@ -819,11 +819,13 @@ function MediaTestManager() {
if (this.onFinished) {
this.onFinished();
}
mediaTestCleanup();
var end = new Date();
SimpleTest.info("Finished at " + end + " (" + (end.getTime() / 1000) + "s)");
SimpleTest.info("Running time: " + (end.getTime() - this.startTime.getTime())/1000 + "s");
SimpleTest.finish();
var onCleanup = function() {
var end = new Date();
SimpleTest.info("Finished at " + end + " (" + (end.getTime() / 1000) + "s)");
SimpleTest.info("Running time: " + (end.getTime() - this.startTime.getTime())/1000 + "s");
SimpleTest.finish();
}.bind(this);
mediaTestCleanup(onCleanup);
return;
}
}
@ -832,7 +834,7 @@ function MediaTestManager() {
// Ensures we've got no active video or audio elements in the document, and
// forces a GC to release the address space reserved by the decoders' threads'
// stacks.
function mediaTestCleanup() {
function mediaTestCleanup(callback) {
var V = document.getElementsByTagName("video");
for (i=0; i<V.length; i++) {
removeNodeAndSource(V[i]);
@ -843,7 +845,12 @@ function mediaTestCleanup() {
removeNodeAndSource(A[i]);
A[i] = null;
}
SpecialPowers.forceGC();
var cb = function() {
if (callback) {
callback();
}
}
SpecialPowers.exactGC(window, cb);
}
(function() {

View File

@ -22,7 +22,7 @@
# do ok(true, "Type not supported") and stop the test.
[DEFAULT]
skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet' || contentSandbox != 'off' # contentSandbox(Bug 1042735)
support-files =
320x240.ogv
320x240.ogv^headers^

View File

@ -39,6 +39,7 @@ function stopped(event) {
return;
v._finished = true;
ok(v.paused, "Video should be paused after removing from the Document");
removeNodeAndSource(v);
manager.finished(v.token);
}

View File

@ -42,7 +42,7 @@ function seekEnded(e) {
v.currentTime + " for " + v._name);
ok(!v.ended, "Checking ended is false for " + v._name);
v._finished = true;
v.parentNode.removeChild(v);
removeNodeAndSource(v);
manager.finished(v.token);
}

View File

@ -1,5 +1,5 @@
[DEFAULT]
skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && (toolkit != 'gonk' || debug)) #b2g-debug,b2g-desktop(bug 916135)
skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && (toolkit != 'gonk' || debug)) || contentSandbox != 'off' #b2g-debug,b2g-desktop(bug 916135); contentSandbox(Bug 1042735)
support-files =
audio-expected.wav
audio-mono-expected-2.wav

View File

@ -53,6 +53,7 @@
#include "mozilla/unused.h"
#include "mozInlineSpellChecker.h"
#include "nsIConsoleListener.h"
#include "nsICycleCollectorListener.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
@ -77,6 +78,7 @@
#include "nsIJSRuntimeService.h"
#include "nsThreadManager.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsISpellChecker.h"
#include "IHistory.h"
#include "nsNetUtil.h"
@ -693,7 +695,7 @@ ContentChild::InitXPCOM()
NS_WARNING("Couldn't register console listener for child process");
bool isOffline;
SendGetXPCOMProcessAttributes(&isOffline);
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries);
RecvSetOffline(isOffline);
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
@ -1125,6 +1127,12 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
return true;
}
void
ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries)
{
aDictionaries = mAvailableDictionaries;
}
PFileDescriptorSetChild*
ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
{
@ -1153,7 +1161,7 @@ ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
mozilla::PRemoteSpellcheckEngineChild *
ContentChild::AllocPRemoteSpellcheckEngineChild()
{
NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChilf should never be called");
NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChild should never be called");
return nullptr;
}
@ -1742,6 +1750,14 @@ ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
return true;
}
bool
ContentChild::RecvUpdateDictionaryList(const InfallibleTArray<nsString>& aDictionaries)
{
mAvailableDictionaries = aDictionaries;
mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
return true;
}
bool
ContentChild::RecvAddPermission(const IPC::Permission& permission)
{

View File

@ -293,6 +293,8 @@ public:
virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere) MOZ_OVERRIDE;
virtual bool RecvUpdateDictionaryList(const InfallibleTArray<nsString>& aDictionaries) MOZ_OVERRIDE;
virtual bool RecvAddPermission(const IPC::Permission& permission) MOZ_OVERRIDE;
virtual bool RecvScreenSizeChanged(const gfxIntSize &size) MOZ_OVERRIDE;
@ -381,6 +383,8 @@ public:
const bool& aIsForApp,
const bool& aIsForBrowser) MOZ_OVERRIDE;
void GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries);
private:
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
@ -397,6 +401,8 @@ private:
nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
InfallibleTArray<nsString> mAvailableDictionaries;
/**
* An ID unique to the process containing our corresponding
* content parent.

View File

@ -101,6 +101,7 @@
#include "nsIPresShell.h"
#include "nsIScriptError.h"
#include "nsISiteSecurityService.h"
#include "nsISpellChecker.h"
#include "nsIStyleSheet.h"
#include "nsISupportsPrimitives.h"
#include "nsIURIFixup.h"
@ -2496,7 +2497,8 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
// Update offline settings.
bool isOffline;
RecvGetXPCOMProcessAttributes(&isOffline);
InfallibleTArray<nsString> unusedDictionaries;
RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries);
content->SendSetOffline(isOffline);
PreallocatedProcessManager::PublishSpareProcess(content);
@ -2744,12 +2746,18 @@ ContentParent::RecvGetProcessAttributes(uint64_t* aId,
}
bool
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline)
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
InfallibleTArray<nsString>* dictionaries)
{
nsCOMPtr<nsIIOService> io(do_GetIOService());
NS_ASSERTION(io, "No IO service?");
MOZ_ASSERT(io, "No IO service?");
DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed getting offline?");
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
MOZ_ASSERT(spellChecker, "No spell checker?");
spellChecker->GetDictionaryList(dictionaries);
return true;
}
@ -4053,6 +4061,23 @@ ContentParent::IgnoreIPCPrincipal()
return sIgnoreIPCPrincipal;
}
void
ContentParent::NotifyUpdatedDictionaries()
{
nsAutoTArray<ContentParent*, 8> processes;
GetAll(processes);
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
MOZ_ASSERT(spellChecker, "No spell checker?");
InfallibleTArray<nsString> dictionaries;
spellChecker->GetDictionaryList(&dictionaries);
for (size_t i = 0; i < processes.Length(); ++i) {
unused << processes[i]->SendUpdateDictionaryList(dictionaries);
}
}
} // namespace dom
} // namespace mozilla

View File

@ -139,6 +139,8 @@ public:
static bool IgnoreIPCPrincipal();
static void NotifyUpdatedDictionaries();
virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
const hal::ProcessPriority& aPriority,
uint64_t* aId,
@ -410,7 +412,9 @@ private:
virtual bool RecvGetProcessAttributes(uint64_t* aId,
bool* aIsForApp,
bool* aIsForBrowser) MOZ_OVERRIDE;
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
InfallibleTArray<nsString>* dictionaries)
MOZ_OVERRIDE;
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;

View File

@ -433,6 +433,8 @@ child:
GeolocationUpdate(GeoPosition somewhere);
UpdateDictionaryList(nsString[] dictionaries);
// nsIPermissionManager messages
AddPermission(Permission permission);
@ -442,7 +444,7 @@ child:
GarbageCollect();
CycleCollect();
/**
* Start accessibility engine in content process.
*/
@ -504,7 +506,7 @@ parent:
sync GetProcessAttributes()
returns (uint64_t id, bool isForApp, bool isForBrowser);
sync GetXPCOMProcessAttributes()
returns (bool isOffline);
returns (bool isOffline, nsString[] dictionaries);
sync CreateChildProcess(IPCTabContext context,
ProcessPriority priority)

View File

@ -109,7 +109,9 @@ LOCAL_INCLUDES += [
'/dom/geolocation',
'/dom/mobilemessage/ipc',
'/dom/storage',
'/editor/libeditor',
'/extensions/cookie',
'/extensions/spellcheck/src',
'/hal/sandbox',
'/js/ipc',
'/layout/base',

View File

@ -1,4 +1,5 @@
[DEFAULT]
skip-if = contentSandbox != 'off' # contentSandbox(Bug 1042735)
support-files =
head.js
constraints.js

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -46,16 +47,16 @@
using namespace mozilla;
class UpdateDictionnaryHolder {
class UpdateDictionaryHolder {
private:
nsEditorSpellCheck* mSpellCheck;
public:
explicit UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
explicit UpdateDictionaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
if (mSpellCheck) {
mSpellCheck->BeginUpdateDictionary();
}
}
~UpdateDictionnaryHolder() {
~UpdateDictionaryHolder() {
if (mSpellCheck) {
mSpellCheck->EndUpdateDictionary();
}
@ -699,23 +700,15 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
}
NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
DictionaryFetcher* fetcher = new DictionaryFetcher(this, aCallback,
mDictionaryFetcherGroup);
nsRefPtr<DictionaryFetcher> fetcher =
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
rootContent->GetLang(fetcher->mRootContentLang);
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
NS_ENSURE_STATE(doc);
doc->GetContentLanguage(fetcher->mRootDocContentLang);
if (XRE_GetProcessType() == GeckoProcessType_Content) {
// Content prefs don't work in E10S (Bug 1027898) pretend that we
// didn't have any & trigger the asynchrous completion.
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArg<uint16_t>(fetcher, &DictionaryFetcher::HandleCompletion, 0);
NS_DispatchToMainThread(runnable);
} else {
rv = fetcher->Fetch(mEditor);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = fetcher->Fetch(mEditor);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -723,14 +716,13 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
nsresult
nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
{
MOZ_ASSERT(aFetcher);
nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
nsresult rv = NS_OK;
// Important: declare the holder after the callback caller so that the former
// is destructed first so that it's not active when the callback is called.
CallbackCaller callbackCaller(aFetcher->mCallback);
UpdateDictionnaryHolder holder(this);
UpdateDictionaryHolder holder(this);
if (aFetcher->mGroup < mDictionaryFetcherGroup) {
// SetCurrentDictionary was called after the fetch started. Don't overwrite
@ -742,10 +734,9 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
// If we successfully fetched a dictionary from content prefs, do not go
// further. Use this exact dictionary.
nsAutoString dictName;
dictName.Assign(aFetcher->mDictionary);
nsAutoString dictName(aFetcher->mDictionary);
if (!dictName.IsEmpty()) {
if (NS_FAILED(SetCurrentDictionary(dictName))) {
if (NS_FAILED(SetCurrentDictionary(dictName))) {
// may be dictionary was uninstalled ?
ClearCurrentDictionary(mEditor);
}
@ -773,8 +764,8 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
dictName.Assign(preferedDict);
}
if (dictName.IsEmpty())
{
nsresult rv = NS_OK;
if (dictName.IsEmpty()) {
// Prefs didn't give us a dictionary name, so just get the current
// locale and use that as the default dictionary name!

View File

@ -6,17 +6,17 @@ include protocol PContent;
namespace mozilla {
rpc protocol PRemoteSpellcheckEngine {
sync protocol PRemoteSpellcheckEngine {
manager PContent;
parent:
__delete__();
rpc Check(nsString aWord) returns (bool aIsMisspelled);
sync Check(nsString aWord) returns (bool aIsMisspelled);
rpc CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions);
sync CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions);
rpc SetDictionary(nsString aDictionary) returns (bool success);
sync SetDictionary(nsString aDictionary) returns (bool success);
};
} // namespace mozilla

View File

@ -5,8 +5,9 @@
#include "RemoteSpellCheckEngineChild.h"
namespace mozilla {
RemoteSpellcheckEngineChild::RemoteSpellcheckEngineChild(mozSpellChecker *aOwner)
:mOwner(aOwner)
: mOwner(aOwner)
{
}
@ -15,7 +16,6 @@ RemoteSpellcheckEngineChild::~RemoteSpellcheckEngineChild()
// null out the owner's SpellcheckEngineChild to prevent state corruption
// during shutdown
mOwner->DeleteRemoteEngine();
}
} //namespace mozilla

View File

@ -11,11 +11,12 @@
class mozSpellChecker;
namespace mozilla {
class RemoteSpellcheckEngineChild : public mozilla::PRemoteSpellcheckEngineChild
{
public:
explicit RemoteSpellcheckEngineChild(mozSpellChecker *aOwner);
~RemoteSpellcheckEngineChild();
virtual ~RemoteSpellcheckEngineChild();
private:
mozSpellChecker *mOwner;

View File

@ -1,18 +1,17 @@
/* vim: set ts=2 sw=2 sts=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RemoteSpellCheckEngineParent.h"
#include "mozISpellCheckingEngine.h"
#include "nsISpellChecker.h"
#include "nsServiceManagerUtils.h"
#define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
namespace mozilla {
RemoteSpellcheckEngineParent::RemoteSpellcheckEngineParent()
{
mEngine = do_GetService(DEFAULT_SPELL_CHECKER);
mSpellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID);
}
RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
@ -20,43 +19,38 @@ RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
}
bool
RemoteSpellcheckEngineParent::AnswerSetDictionary(
RemoteSpellcheckEngineParent::RecvSetDictionary(
const nsString& aDictionary,
bool* success)
{
nsresult rv = mEngine->SetDictionary(aDictionary.get());
nsresult rv = mSpellChecker->SetCurrentDictionary(aDictionary);
*success = NS_SUCCEEDED(rv);
return true;
}
bool
RemoteSpellcheckEngineParent::AnswerCheck(
RemoteSpellcheckEngineParent::RecvCheck(
const nsString& aWord,
bool* aIsMisspelled)
{
bool isCorrect = true;
mEngine->Check(aWord.get(), &isCorrect);
*aIsMisspelled = !isCorrect;
nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, nullptr);
// If CheckWord failed, we can't tell whether the word is correctly spelled.
if (NS_FAILED(rv))
*aIsMisspelled = false;
return true;
}
bool
RemoteSpellcheckEngineParent::AnswerCheckAndSuggest(
RemoteSpellcheckEngineParent::RecvCheckAndSuggest(
const nsString& aWord,
bool* aIsMisspelled,
InfallibleTArray<nsString>* aSuggestions)
{
bool isCorrect = true;
mEngine->Check(aWord.get(), &isCorrect);
*aIsMisspelled = !isCorrect;
if (!isCorrect) {
char16_t **suggestions;
uint32_t count = 0;
mEngine->Suggest(aWord.get(), &suggestions, &count);
for (uint32_t i=0; i<count; i++) {
aSuggestions->AppendElement(nsDependentString(suggestions[i]));
}
nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, aSuggestions);
if (NS_FAILED(rv)) {
aSuggestions->Clear();
*aIsMisspelled = false;
}
return true;
}
@ -67,4 +61,3 @@ RemoteSpellcheckEngineParent::ActorDestroy(ActorDestroyReason aWhy)
}
} // namespace mozilla

View File

@ -4,34 +4,34 @@
#ifndef RemoteSpellcheckEngineParent_h_
#define RemoteSpellcheckEngineParent_h_
#include "mozISpellCheckingEngine.h"
#include "mozilla/PRemoteSpellcheckEngineParent.h"
#include "nsCOMPtr.h"
class nsISpellChecker;
namespace mozilla {
class RemoteSpellcheckEngineParent : public mozilla::PRemoteSpellcheckEngineParent {
class RemoteSpellcheckEngineParent : public PRemoteSpellcheckEngineParent
{
public:
RemoteSpellcheckEngineParent();
~RemoteSpellcheckEngineParent();
virtual ~RemoteSpellcheckEngineParent();
virtual void ActorDestroy(ActorDestroyReason aWhy);
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
bool AnswerSetDictionary(const nsString& aDictionary, bool* success);
bool AnswerCheck( const nsString& aWord, bool* aIsMisspelled);
bool AnswerCheckAndSuggest(
const nsString& aWord,
bool* aIsMisspelled,
InfallibleTArray<nsString>* aSuggestions);
virtual bool RecvSetDictionary(const nsString& aDictionary,
bool* success) MOZ_OVERRIDE;
virtual bool RecvCheck(const nsString& aWord, bool* aIsMisspelled) MOZ_OVERRIDE;
virtual bool RecvCheckAndSuggest(const nsString& aWord,
bool* aIsMisspelled,
InfallibleTArray<nsString>* aSuggestions)
MOZ_OVERRIDE;
private:
nsCOMPtr<mozISpellCheckingEngine> mEngine;
nsCOMPtr<nsISpellChecker> mSpellChecker;
};
}

View File

@ -76,7 +76,9 @@
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/ContentParent.h"
using mozilla::dom::ContentParent;
using mozilla::dom::EncodingUtils;
NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
@ -113,7 +115,7 @@ mozHunspell::mozHunspell()
nsresult
mozHunspell::Init()
{
LoadDictionaryList();
LoadDictionaryList(false);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
@ -344,7 +346,7 @@ NS_IMETHODIMP mozHunspell::GetDictionaryList(char16_t ***aDictionaries,
}
void
mozHunspell::LoadDictionaryList()
mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses)
{
mDictionaries.Clear();
@ -424,6 +426,10 @@ mozHunspell::LoadDictionaryList()
// dictionary and any editors which may use it.
mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
if (aNotifyChildProcesses) {
ContentParent::NotifyUpdatedDictionaries();
}
// Check if the current dictionary is still available.
// If not, try to replace it with another dictionary of the same language.
if (!mDictionary.IsEmpty()) {
@ -589,7 +595,7 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
|| !strcmp(aTopic, "profile-after-change"),
"Unexpected observer topic");
LoadDictionaryList();
LoadDictionaryList(false);
return NS_OK;
}
@ -598,7 +604,7 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
{
mDynamicDirectories.AppendObject(aDir);
LoadDictionaryList();
LoadDictionaryList(true);
return NS_OK;
}
@ -606,6 +612,6 @@ NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir)
{
mDynamicDirectories.RemoveObject(aDir);
LoadDictionaryList();
LoadDictionaryList(true);
return NS_OK;
}

View File

@ -96,7 +96,7 @@ public:
nsresult Init();
void LoadDictionaryList();
void LoadDictionaryList(bool aNotifyChildProcesses);
// helper method for converting a word to the charset of the dictionary
nsresult ConvertCharset(const char16_t* aStr, char ** aDst);

View File

@ -1,3 +1,4 @@
/* vim: set ts=2 sts=2 sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -34,38 +35,41 @@ NS_IMPL_CYCLE_COLLECTION(mozSpellChecker,
mPersonalDictionary)
mozSpellChecker::mozSpellChecker()
: mEngine(nullptr)
{
}
mozSpellChecker::~mozSpellChecker()
{
if(mPersonalDictionary){
if (mPersonalDictionary) {
// mPersonalDictionary->Save();
mPersonalDictionary->EndSession();
}
mSpellCheckingEngine = nullptr;
mPersonalDictionary = nullptr;
if(XRE_GetProcessType() == GeckoProcessType_Content) {
if (mEngine) {
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
mEngine->Send__delete__(mEngine);
MOZ_ASSERT(!mEngine);
}
}
nsresult
nsresult
mozSpellChecker::Init()
{
mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
mSpellCheckingEngine = nullptr;
if (XRE_GetProcessType() == GeckoProcessType_Content) {
mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
MOZ_ASSERT(contentChild);
mEngine = new RemoteSpellcheckEngineChild(this);
contentChild->SendPRemoteSpellcheckEngineConstructor(mEngine);
} else {
mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
}
return NS_OK;
}
}
NS_IMETHODIMP
mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, bool aFromStartofDoc)
@ -130,9 +134,9 @@ mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray
nsString wordwrapped = nsString(aWord);
bool rv;
if (aSuggestions) {
rv = mEngine->CallCheckAndSuggest(wordwrapped, aIsMisspelled, aSuggestions);
rv = mEngine->SendCheckAndSuggest(wordwrapped, aIsMisspelled, aSuggestions);
} else {
rv = mEngine->CallCheck(wordwrapped, aIsMisspelled);
rv = mEngine->SendCheck(wordwrapped, aIsMisspelled);
}
return rv ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
@ -302,9 +306,15 @@ mozSpellChecker::GetPersonalDictionary(nsTArray<nsString> *aWordList)
return NS_OK;
}
NS_IMETHODIMP
NS_IMETHODIMP
mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
{
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild *child = ContentChild::GetSingleton();
child->GetAvailableDictionaries(*aDictionaryList);
return NS_OK;
}
nsresult rv;
// For catching duplicates
@ -344,9 +354,14 @@ mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
return NS_OK;
}
NS_IMETHODIMP
NS_IMETHODIMP
mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
{
if (XRE_GetProcessType() == GeckoProcessType_Content) {
aDictionary = mCurrentDictionary;
return NS_OK;
}
if (!mSpellCheckingEngine) {
aDictionary.Truncate();
return NS_OK;
@ -358,14 +373,20 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
return NS_OK;
}
NS_IMETHODIMP
NS_IMETHODIMP
mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
{
if (XRE_GetProcessType() == GeckoProcessType_Content) {
nsString wrappedDict = nsString(aDictionary);
bool isSuccess;
mEngine->CallSetDictionary(wrappedDict, &isSuccess);
return isSuccess ? NS_OK : NS_ERROR_NOT_AVAILABLE;
mEngine->SendSetDictionary(wrappedDict, &isSuccess);
if (!isSuccess) {
mCurrentDictionary.Truncate();
return NS_ERROR_NOT_AVAILABLE;
}
mCurrentDictionary = wrappedDict;
return NS_OK;
}
// Calls to mozISpellCheckingEngine::SetDictionary might destroy us
@ -402,7 +423,7 @@ mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
}
mSpellCheckingEngine = nullptr;
// We could not find any engine with the requested dictionary
return NS_ERROR_NOT_AVAILABLE;
}
@ -515,6 +536,8 @@ mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *ou
nsresult
mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellCheckingEngines)
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
nsresult rv;
bool hasMoreEngines;
@ -564,7 +587,3 @@ mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellChecki
return NS_OK;
}
void mozSpellChecker::DeleteRemoteEngine() {
mEngine = nullptr;
}

View File

@ -49,7 +49,10 @@ public:
NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary);
NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary);
NS_IMETHOD CheckCurrentDictionary();
void DeleteRemoteEngine();
void DeleteRemoteEngine() {
mEngine = nullptr;
}
protected:
virtual ~mozSpellChecker();
@ -61,6 +64,8 @@ protected:
nsCOMPtr<mozISpellCheckingEngine> mSpellCheckingEngine;
bool mFromStart;
nsString mCurrentDictionary;
nsresult SetupDoc(int32_t *outBlockOffset);
nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *outBlockIndex);

View File

@ -87,6 +87,7 @@ public:
/**
* Schedules a runnable to run on the controller/UI thread at some time
* in the future.
* This method must always be called on the controller thread.
*/
virtual void PostDelayedTask(Task* aTask, int aDelayMs) = 0;

View File

@ -3138,6 +3138,7 @@ AsyncPanZoomController::GetZoomConstraints() const
void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
AssertOnControllerThread();
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (controller) {
controller->PostDelayedTask(aTask, aDelayMs);

View File

@ -55,6 +55,7 @@ GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoom
mSpanChange(0.0f),
mPreviousSpan(0.0f),
mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
mLastTapInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
mLongTapTimeoutTask(nullptr),
mMaxTapTimeoutTask(nullptr)
{
@ -168,6 +169,7 @@ nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
// Cancel wait for double tap
CancelMaxTapTimeoutTask();
SetState(GESTURE_MULTI_TOUCH_DOWN);
TriggerSingleTapConfirmedEvent();
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
rv = nsEventStatus_eConsumeNoDefault;
break;
@ -175,6 +177,7 @@ nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
// Cancel wait for single tap
CancelMaxTapTimeoutTask();
SetState(GESTURE_MULTI_TOUCH_DOWN);
TriggerSingleTapConfirmedEvent();
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
rv = nsEventStatus_eConsumeNoDefault;
break;
@ -441,10 +444,10 @@ void GestureEventListener::HandleInputTimeoutMaxTap()
void GestureEventListener::TriggerSingleTapConfirmedEvent()
{
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED,
mLastTouchInput.mTime,
mLastTouchInput.mTimeStamp,
mLastTouchInput.mTouches[0].mScreenPoint,
mLastTouchInput.modifiers);
mLastTapInput.mTime,
mLastTapInput.mTimeStamp,
mLastTapInput.mTouches[0].mScreenPoint,
mLastTapInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
}
@ -498,6 +501,8 @@ void GestureEventListener::CancelMaxTapTimeoutTask()
void GestureEventListener::CreateMaxTapTimeoutTask()
{
mLastTapInput = mLastTouchInput;
mMaxTapTimeoutTask =
NewRunnableMethod(this, &GestureEventListener::HandleInputTimeoutMaxTap);

View File

@ -175,6 +175,15 @@ private:
*/
MultiTouchInput mLastTouchInput;
/**
* Cached copy of the last tap gesture input.
* In the situation when we have a tap followed by a pinch we lose info
* about tap since we keep only last input and to dispatch it correctly
* we save last tap copy into this variable.
* For more info see bug 947892.
*/
MultiTouchInput mLastTapInput;
/**
* Position of the last touch starting. This is only valid during an attempt
* to determine if a touch is a tap. If a touch point moves away from

View File

@ -1478,6 +1478,62 @@ TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
apzc->AssertStateIsReset();
}
// Test for bug 947892
// We test whether we dispatch tap event when the tap is followed by pinch.
TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
int time = 0;
ApzcTap(apzc, 10, 10, time, 100);
int inputId = 0;
MultiTouchInput mti;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
while (mcc->RunThroughDelayedTasks());
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
int time = 0;
ApzcTap(apzc, 10, 10, time, 100);
int inputId = 0;
MultiTouchInput mti;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
while (mcc->RunThroughDelayedTasks());
apzc->AssertStateIsReset();
}
class APZCTreeManagerTester : public ::testing::Test {
protected:
virtual void SetUp() {

View File

@ -1051,16 +1051,19 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
return JSObject::preventExtensions(cx, obj);
}
// ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
static bool
obj_freeze(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx);
if (!GetFirstArgumentAsObject(cx, args, "Object.freeze", &obj))
return false;
args.rval().set(args.get(0));
args.rval().setObject(*obj);
// Step 1.
if (!args.get(0).isObject())
return true;
// Steps 2-5.
RootedObject obj(cx, &args.get(0).toObject());
return JSObject::freeze(cx, obj);
}

View File

@ -1701,8 +1701,11 @@ ia64*-hpux*)
# and requires workarounds for perfectly valid code. Also, GCC/clang
# don't warn about it by default. So for consistency/sanity, we turn
# it off on MSVC, too.
CFLAGS="$CFLAGS -wd4244"
CXXFLAGS="$CXXFLAGS -wd4244 -wd4251"
# MSVC warning C4267 warns for narrowing type conversions from size_t
# to 32-bit integer types on 64-bit platforms. Since this is virtually
# the same thing as C4244, we disable C4267, too.
CFLAGS="$CFLAGS -wd4244 -wd4267"
CXXFLAGS="$CXXFLAGS -wd4244 -wd4267 -wd4251"
# make 'foo == bar;' error out
CFLAGS="$CFLAGS -we4553"
CXXFLAGS="$CXXFLAGS -we4553"

View File

@ -1320,7 +1320,9 @@ NativeRegExpMacroAssembler::CheckSpecialCharacterClass(char16_t type, Label* on_
bool
NativeRegExpMacroAssembler::CanReadUnaligned()
{
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
#if defined(JS_CODEGEN_ARM)
return !jit::HasAlignmentFault();
#elif defined(JS_CODEGEN_MIPS)
return false;
#else
return true;

View File

@ -515,9 +515,9 @@ assertAsmTypeFail('glob', USE_ASM + I32 + "var lt=i4.lessThanOrEqual; function f
assertAsmTypeFail('glob', USE_ASM + I32 + "var ge=i4.greaterThanOrEqual; function f() {} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var ne=i4.notEqual; function f() {} return f");
const LTI32 = 'var lt = i4.lessThan';
const GTI32 = 'var gt = i4.greaterThan';
const EQI32 = 'var eq = i4.equal';
const LTI32 = 'var lt = i4.lessThan;';
const GTI32 = 'var gt = i4.greaterThan;';
const EQI32 = 'var eq = i4.equal;';
CheckI4(LTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=lt(x,y)', [F, F, F, F]);
CheckI4(LTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=lt(x,y)', [T, T, T, T]);
@ -531,38 +531,44 @@ CheckI4(GTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=gt(x,y)', [T, T, T, T])
CheckI4(GTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=gt(x,y)', [F, F, F, F]);
CheckI4(GTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=gt(x,y)', [F, F, F, T]);
const LTF32 = 'var lt=f4.lessThan';
const LEF32 = 'var le=f4.lessThanOrEqual';
const GTF32 = 'var gt=f4.greaterThan';
const GEF32 = 'var ge=f4.greaterThanOrEqual';
const EQF32 = 'var eq=f4.equal';
const NEF32 = 'var ne=f4.notEqual';
const LTF32 = 'var lt=f4.lessThan;';
const LEF32 = 'var le=f4.lessThanOrEqual;';
const GTF32 = 'var gt=f4.greaterThan;';
const GEF32 = 'var ge=f4.greaterThanOrEqual;';
const EQF32 = 'var eq=f4.equal;';
const NEF32 = 'var ne=f4.notEqual;';
assertAsmTypeFail('glob', USE_ASM + F32 + "var lt=f4.lessThan; function f() {var x=f4(1,2,3,4); var y=f4(5,6,7,8); x=lt(x,y);} return f");
CheckF4Comp(LTF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=lt(y,z)', [F, F, F, F]);
CheckF4Comp(LTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=lt(y,z)', [T, T, T, T]);
CheckF4Comp(LTF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=lt(y,z)', [F, T, T, F]);
CheckF4Comp(LTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=lt(y,z);', [F, F, F, F]);
CheckF4Comp(LEF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=le(y,z)', [F, F, F, F]);
CheckF4Comp(LEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=le(y,z)', [T, T, T, T]);
CheckF4Comp(LEF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=le(y,z)', [T, T, T, F]);
CheckF4Comp(LEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=le(y,z);', [T, T, F, F]);
CheckF4Comp(EQF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
CheckF4Comp(EQF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
CheckF4Comp(EQF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=eq(y,z)', [T, F, F, F]);
CheckF4Comp(EQF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=eq(y,z);', [T, T, F, F]);
CheckF4Comp(NEF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
CheckF4Comp(NEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
CheckF4Comp(NEF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=ne(y,z)', [F, T, T, T]);
CheckF4Comp(NEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ne(y,z);', [F, F, T, T]);
CheckF4Comp(GTF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=gt(y,z)', [T, T, T, T]);
CheckF4Comp(GTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, F]);
CheckF4Comp(GTF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, T]);
CheckF4Comp(GTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=gt(y,z);', [F, F, F, F]);
CheckF4Comp(GEF32, 'var y=f4(1,2,3,4); var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=ge(y,z)', [T, T, T, T]);
CheckF4Comp(GEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4); var x=i4(0,0,0,0); x=ge(y,z)', [F, F, F, F]);
CheckF4Comp(GEF32, 'var y=f4(1,0,3,4); var z=f4(1,1,7,0); var x=i4(0,0,0,0); x=ge(y,z)', [T, F, F, T]);
CheckF4Comp(GEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ge(y,z);', [T, T, F, F]);
// Conversions operators
const CVTIF = 'var cvt=f4.fromInt32x4;';

View File

@ -0,0 +1,24 @@
setJitCompilerOption("baseline.warmup.trigger", 10);
setJitCompilerOption("ion.warmup.trigger", 30);
var i;
var uceFault = function (i) {
if (i > 98)
uceFault = function (i) { return true; };
return false;
}
var sqrt5 = Math.sqrt(5);
var phi = (1 + sqrt5) / 2;
function range_analysis_truncate(i) {
var fib = (Math.pow(phi, i) - Math.pow(1 - phi, i)) / sqrt5;
var x = (fib >> 8) * (fib >> 6);
if (uceFault(i) || uceFault(i))
assertEq(x, (fib >> 8) * (fib >> 6));
return x | 0;
}
for (i = 0; i < 100; i++) {
range_analysis_truncate(i);
}

View File

@ -0,0 +1,44 @@
var i = 0;
function cond() {
return i++ < 20;
}
function inline() {
({ b: 1 })
}
function f() {
do {
({ b: 1 })
} while (cond())
}
i = 0;
f();
function g() {
do {
if (cond()) { }
({ b: 1 })
} while (cond())
}
i = 0;
g();
function h() {
do {
inline();
} while (cond())
}
i = 0;
h();
i = 0;
for (i = 0; cond(); i++)
({ set: Math.w });

View File

@ -389,7 +389,7 @@ class SnapshotIteratorForBailout : public SnapshotIterator
SnapshotIteratorForBailout(const IonBailoutIterator &iter)
: SnapshotIterator(iter),
results_()
results_(iter.jsFrame())
{
}

View File

@ -1437,17 +1437,19 @@ OsiIndex::returnPointDisplacement() const
return callPointDisplacement_ + Assembler::PatchWrite_NearCallSize();
}
RInstructionResults::RInstructionResults()
RInstructionResults::RInstructionResults(IonJSFrameLayout *fp)
: results_(nullptr),
fp_(nullptr)
fp_(fp),
initialized_(false)
{
}
RInstructionResults::RInstructionResults(RInstructionResults&& src)
: results_(mozilla::Move(src.results_)),
fp_(src.fp_)
fp_(src.fp_),
initialized_(src.initialized_)
{
src.fp_ = nullptr;
src.initialized_ = false;
}
RInstructionResults&
@ -1465,7 +1467,7 @@ RInstructionResults::~RInstructionResults()
}
bool
RInstructionResults::init(JSContext *cx, uint32_t numResults, IonJSFrameLayout *fp)
RInstructionResults::init(JSContext *cx, uint32_t numResults)
{
if (numResults) {
results_ = cx->make_unique<Values>();
@ -1477,21 +1479,20 @@ RInstructionResults::init(JSContext *cx, uint32_t numResults, IonJSFrameLayout *
(*results_)[i].init(guard);
}
fp_ = fp;
initialized_ = true;
return true;
}
bool
RInstructionResults::isInitialized() const
{
MOZ_ASSERT_IF(results_, fp_);
return fp_;
return initialized_;
}
IonJSFrameLayout *
RInstructionResults::frame() const
{
MOZ_ASSERT(isInitialized());
MOZ_ASSERT(fp_);
return fp_;
}
@ -1798,9 +1799,8 @@ SnapshotIterator::initInstructionResults(MaybeReadFallback &fallback)
// before we initialize the list such as if any recover instruction
// cause a GC, we can ensure that the results are properly traced by the
// activation.
RInstructionResults tmp;
if (!fallback.activation->registerIonFrameRecovery(fallback.frame->jsFrame(),
mozilla::Move(tmp)))
RInstructionResults tmp(fallback.frame->jsFrame());
if (!fallback.activation->registerIonFrameRecovery(mozilla::Move(tmp)))
return false;
results = fallback.activation->maybeIonFrameRecovery(fp);
@ -1832,7 +1832,7 @@ SnapshotIterator::computeInstructionResults(JSContext *cx, RInstructionResults *
// The last instruction will always be a resume point.
size_t numResults = recover_.numInstructions() - 1;
if (!results->isInitialized()) {
if (!results->init(cx, numResults, fp_))
if (!results->init(cx, numResults))
return false;
// No need to iterate over the only resume point.

View File

@ -276,15 +276,20 @@ class RInstructionResults
// bailed out.
IonJSFrameLayout *fp_;
// Record if we tried and succeed at allocating and filling the vector of
// recover instruction results, if needed. This flag is needed in order to
// avoid evaluating the recover instruction twice.
bool initialized_;
public:
RInstructionResults();
RInstructionResults(IonJSFrameLayout *fp);
RInstructionResults(RInstructionResults&& src);
RInstructionResults& operator=(RInstructionResults&& rhs);
~RInstructionResults();
bool init(JSContext *cx, uint32_t numResults, IonJSFrameLayout *fp);
bool init(JSContext *cx, uint32_t numResults);
bool isInitialized() const;
IonJSFrameLayout *frame() const;

View File

@ -3808,12 +3808,12 @@ LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp *ins)
if (ins->compareType() == MSimdBinaryComp::CompareInt32x4) {
LSimdBinaryCompIx4 *add = new(alloc()) LSimdBinaryCompIx4();
return lowerForFPU(add, ins, ins->lhs(), ins->rhs());
return lowerForCompIx4(add, ins, ins->lhs(), ins->rhs());
}
if (ins->compareType() == MSimdBinaryComp::CompareFloat32x4) {
LSimdBinaryCompFx4 *add = new(alloc()) LSimdBinaryCompFx4();
return lowerForFPU(add, ins, ins->lhs(), ins->rhs());
return lowerForCompFx4(add, ins, ins->lhs(), ins->rhs());
}
MOZ_CRASH("Unknown compare type when comparing values");

View File

@ -513,8 +513,14 @@ class MDefinition : public MNode
Truncate = 3
};
// Apply the given truncate to this node itself.
virtual bool truncate(TruncateKind kind);
// |needTruncation| records the truncation kind of the results, such that it
// can be used to truncate the operands of this instruction. If
// |needTruncation| function returns true, then the |truncate| function is
// called on the same instruction to mutate the instruction, such as
// updating the return type, the range and the specialization of the
// instruction.
virtual bool needTruncation(TruncateKind kind);
virtual void truncate();
// Determine what kind of truncate this node prefers for the operand at the
// given index.
@ -1195,7 +1201,7 @@ class MLimitedTruncate
}
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
TruncateKind operandTruncateKind(size_t index) const;
TruncateKind truncateKind() const {
return truncate_;
@ -1253,7 +1259,8 @@ class MConstant : public MNullaryInstruction
}
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
bool canProduceFloat32() const;
@ -1604,6 +1611,21 @@ class MSimdBinaryComp : public MBinaryInstruction
Operation operation() const { return operation_; }
CompareType compareType() const { return compareType_; }
// Swap the operands and reverse the comparison predicate.
void reverse() {
switch (operation()) {
case greaterThan: operation_ = lessThan; break;
case greaterThanOrEqual: operation_ = lessThanOrEqual; break;
case lessThan: operation_ = greaterThan; break;
case lessThanOrEqual: operation_ = greaterThanOrEqual; break;
case equal:
case notEqual:
break;
default: MOZ_CRASH("Unexpected compare operation");
}
swapOperands();
}
bool congruentTo(const MDefinition *ins) const {
if (!binaryCongruentTo(ins))
return false;
@ -3437,7 +3459,8 @@ class MCompare
void trySpecializeFloat32(TempAllocator &alloc);
bool isFloat32Commutative() const { return true; }
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
TruncateKind operandTruncateKind(size_t index) const;
# ifdef DEBUG
@ -4026,7 +4049,8 @@ class MToDouble
}
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
TruncateKind operandTruncateKind(size_t index) const;
#ifdef DEBUG
@ -5218,7 +5242,8 @@ class MAdd : public MBinaryArithInstruction
bool fallible() const;
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
TruncateKind operandTruncateKind(size_t index) const;
bool writeRecoverData(CompactBufferWriter &writer) const;
@ -5261,7 +5286,8 @@ class MSub : public MBinaryArithInstruction
bool fallible() const;
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
TruncateKind operandTruncateKind(size_t index) const;
bool writeRecoverData(CompactBufferWriter &writer) const;
@ -5362,7 +5388,8 @@ class MMul : public MBinaryArithInstruction
bool isFloat32Commutative() const { return true; }
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
TruncateKind operandTruncateKind(size_t index) const;
Mode mode() const { return mode_; }
@ -5466,7 +5493,8 @@ class MDiv : public MBinaryArithInstruction
void computeRange(TempAllocator &alloc);
bool fallible() const;
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
void collectRangeInfoPreTrunc();
TruncateKind operandTruncateKind(size_t index) const;
@ -5547,7 +5575,8 @@ class MMod : public MBinaryArithInstruction
bool fallible() const;
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
void collectRangeInfoPreTrunc();
TruncateKind operandTruncateKind(size_t index) const;
@ -5959,7 +5988,8 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineListNode<MPhi>
}
TruncateKind operandTruncateKind(size_t index) const;
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
void truncate();
};
// The goal of a Beta node is to split a def at a conditionally taken
@ -7908,7 +7938,7 @@ class MLoadTypedArrayElementStatic
}
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool needTruncation(TruncateKind kind);
bool canProduceFloat32() const { return viewType() == Scalar::Float32; }
};

View File

@ -2149,17 +2149,28 @@ Range::wrapAroundToBoolean()
}
bool
MDefinition::truncate(TruncateKind kind)
MDefinition::needTruncation(TruncateKind kind)
{
// No procedure defined for truncating this instruction.
return false;
}
bool
MConstant::truncate(TruncateKind kind)
void
MDefinition::truncate()
{
if (!value_.isDouble())
return false;
MOZ_CRASH("No procedure defined for truncating this instruction.");
}
bool
MConstant::needTruncation(TruncateKind kind)
{
return value_.isDouble();
}
void
MConstant::truncate()
{
MOZ_ASSERT(needTruncation(Truncate));
// Truncate the double to int, since all uses truncates it.
int32_t res = ToInt32(value_.toDouble());
@ -2167,147 +2178,164 @@ MConstant::truncate(TruncateKind kind)
setResultType(MIRType_Int32);
if (range())
range()->setInt32(res, res);
return true;
}
bool
MPhi::truncate(TruncateKind kind)
MPhi::needTruncation(TruncateKind kind)
{
if (type() == MIRType_Double || type() == MIRType_Int32) {
truncateKind_ = kind;
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate && range())
range()->wrapAroundToInt32();
return true;
}
return false;
}
void
MPhi::truncate()
{
setResultType(MIRType_Int32);
if (truncateKind_ >= IndirectTruncate && range())
range()->wrapAroundToInt32();
}
bool
MAdd::truncate(TruncateKind kind)
MAdd::needTruncation(TruncateKind kind)
{
// Remember analysis, needed for fallible checks.
setTruncateKind(kind);
if (type() == MIRType_Double || type() == MIRType_Int32) {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate && range())
range()->wrapAroundToInt32();
return true;
}
return type() == MIRType_Double || type() == MIRType_Int32;
}
return false;
void
MAdd::truncate()
{
MOZ_ASSERT(needTruncation(truncateKind()));
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (truncateKind() >= IndirectTruncate && range())
range()->wrapAroundToInt32();
}
bool
MSub::truncate(TruncateKind kind)
MSub::needTruncation(TruncateKind kind)
{
// Remember analysis, needed for fallible checks.
setTruncateKind(kind);
if (type() == MIRType_Double || type() == MIRType_Int32) {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate && range())
return type() == MIRType_Double || type() == MIRType_Int32;
}
void
MSub::truncate()
{
MOZ_ASSERT(needTruncation(truncateKind()));
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (truncateKind() >= IndirectTruncate && range())
range()->wrapAroundToInt32();
}
bool
MMul::needTruncation(TruncateKind kind)
{
// Remember analysis, needed for fallible checks.
setTruncateKind(kind);
return type() == MIRType_Double || type() == MIRType_Int32;
}
void
MMul::truncate()
{
MOZ_ASSERT(needTruncation(truncateKind()));
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (truncateKind() >= IndirectTruncate) {
setCanBeNegativeZero(false);
if (range())
range()->wrapAroundToInt32();
return true;
}
return false;
}
bool
MMul::truncate(TruncateKind kind)
MDiv::needTruncation(TruncateKind kind)
{
// Remember analysis, needed to remove negative zero checks.
// Remember analysis, needed for fallible checks.
setTruncateKind(kind);
if (type() == MIRType_Double || type() == MIRType_Int32) {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate) {
setCanBeNegativeZero(false);
if (range())
range()->wrapAroundToInt32();
}
return true;
}
return type() == MIRType_Double || type() == MIRType_Int32;
}
return false;
void
MDiv::truncate()
{
MOZ_ASSERT(needTruncation(truncateKind()));
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
// Divisions where the lhs and rhs are unsigned and the result is
// truncated can be lowered more efficiently.
if (tryUseUnsignedOperands())
unsigned_ = true;
}
bool
MDiv::truncate(TruncateKind kind)
MMod::needTruncation(TruncateKind kind)
{
// Remember analysis, needed for fallible checks.
setTruncateKind(kind);
if (type() == MIRType_Double || type() == MIRType_Int32) {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
// Divisions where the lhs and rhs are unsigned and the result is
// truncated can be lowered more efficiently.
if (tryUseUnsignedOperands())
unsigned_ = true;
return true;
}
// No modifications.
return false;
return type() == MIRType_Double || type() == MIRType_Int32;
}
bool
MMod::truncate(TruncateKind kind)
void
MMod::truncate()
{
// Remember analysis, needed to remove negative zero checks.
setTruncateKind(kind);
// As for division, handle unsigned modulus with a truncated result.
if (type() == MIRType_Double || type() == MIRType_Int32) {
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
MOZ_ASSERT(needTruncation(truncateKind()));
specialization_ = MIRType_Int32;
setResultType(MIRType_Int32);
if (tryUseUnsignedOperands())
unsigned_ = true;
return true;
}
// No modifications.
return false;
if (tryUseUnsignedOperands())
unsigned_ = true;
}
bool
MToDouble::truncate(TruncateKind kind)
MToDouble::needTruncation(TruncateKind kind)
{
MOZ_ASSERT(type() == MIRType_Double);
setTruncateKind(kind);
return true;
}
void
MToDouble::truncate()
{
MOZ_ASSERT(needTruncation(truncateKind()));
// We use the return type to flag that this MToDouble should be replaced by
// a MTruncateToInt32 when modifying the graph.
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate) {
if (truncateKind() >= IndirectTruncate) {
if (range())
range()->wrapAroundToInt32();
}
return true;
}
bool
MLoadTypedArrayElementStatic::truncate(TruncateKind kind)
MLoadTypedArrayElementStatic::needTruncation(TruncateKind kind)
{
if (kind >= IndirectTruncate)
setInfallible();
return false;
}
bool
MLimitedTruncate::truncate(TruncateKind kind)
MLimitedTruncate::needTruncation(TruncateKind kind)
{
setTruncateKind(kind);
setResultType(MIRType_Int32);
@ -2316,6 +2344,38 @@ MLimitedTruncate::truncate(TruncateKind kind)
return false;
}
bool
MCompare::needTruncation(TruncateKind kind)
{
// If we're compiling AsmJS, don't try to optimize the comparison type, as
// the code presumably is already using the type it wants. Also, AsmJS
// doesn't support bailouts, so we woudn't be able to rely on
// TruncateAfterBailouts to convert our inputs.
if (block()->info().compilingAsmJS())
return false;
if (!isDoubleComparison())
return false;
// If both operands are naturally in the int32 range, we can convert from
// a double comparison to being an int32 comparison.
if (!Range(lhs()).isInt32() || !Range(rhs()).isInt32())
return false;
return true;
}
void
MCompare::truncate()
{
compareType_ = Compare_Int32;
// Truncating the operands won't change their value because we don't force a
// truncation, but it will change their type, which we need because we
// now expect integer inputs.
truncateOperands_ = true;
}
MDefinition::TruncateKind
MDefinition::operandTruncateKind(size_t index) const
{
@ -2413,34 +2473,6 @@ MMod::operandTruncateKind(size_t index) const
return Min(truncateKind(), TruncateAfterBailouts);
}
bool
MCompare::truncate(TruncateKind kind)
{
// If we're compiling AsmJS, don't try to optimize the comparison type, as
// the code presumably is already using the type it wants. Also, AsmJS
// doesn't support bailouts, so we woudn't be able to rely on
// TruncateAfterBailouts to convert our inputs.
if (block()->info().compilingAsmJS())
return false;
if (!isDoubleComparison())
return false;
// If both operands are naturally in the int32 range, we can convert from
// a double comparison to being an int32 comparison.
if (!Range(lhs()).isInt32() || !Range(rhs()).isInt32())
return false;
compareType_ = Compare_Int32;
// Truncating the operands won't change their value because we don't force a
// truncation, but it will change their type, which we need because we
// now expect integer inputs.
truncateOperands_ = true;
return true;
}
MDefinition::TruncateKind
MCompare::operandTruncateKind(size_t index) const
{
@ -2486,40 +2518,133 @@ TruncateTest(TempAllocator &alloc, MTest *test)
phi->setResultType(MIRType_Int32);
}
// Truncating instruction result is an optimization which implies
// knowing all uses of an instruction. This implies that if one of
// the uses got removed, then Range Analysis is not be allowed to do
// any modification which can change the result, especially if the
// result can be observed.
//
// This corner can easily be understood with UCE examples, but it
// might also happen with type inference assumptions. Note: Type
// inference is implicitly branches where other types might be
// flowing into.
static bool
CloneForDeadBranches(TempAllocator &alloc, MInstruction *candidate)
{
// Compare returns a boolean so it doesn't have to be recovered on bailout
// because the output would remain correct.
if (candidate->isCompare())
return true;
MOZ_ASSERT(candidate->canClone());
MDefinitionVector operands(alloc);
size_t end = candidate->numOperands();
for (size_t i = 0; i < end; i++) {
if (!operands.append(candidate->getOperand(i)))
return false;
}
MInstruction *clone = candidate->clone(alloc, operands);
clone->setRange(nullptr);
// Set UseRemoved flag on the cloned instruction in order to chain recover
// instruction for the bailout path.
clone->setUseRemovedUnchecked();
candidate->block()->insertBefore(candidate, clone);
if (!candidate->isConstant()) {
MOZ_ASSERT(clone->canRecoverOnBailout());
clone->setRecoveredOnBailout();
}
// Replace the candidate by its recovered on bailout clone within recovered
// instructions and resume points operands.
for (MUseIterator i(candidate->usesBegin()); i != candidate->usesEnd(); ) {
MUse *use = *i++;
MNode *ins = use->consumer();
if (ins->isDefinition() && !ins->toDefinition()->isRecoveredOnBailout())
continue;
use->replaceProducer(clone);
}
return true;
}
// Examine all the users of |candidate| and determine the most aggressive
// truncate kind that satisfies all of them.
static MDefinition::TruncateKind
ComputeRequestedTruncateKind(MDefinition *candidate)
ComputeRequestedTruncateKind(MDefinition *candidate, bool *shouldClone)
{
// If the value naturally produces an int32 value (before bailout checks)
// that needs no conversion, we don't have to worry about resume points
// seeing truncated values.
bool needsConversion = !candidate->range() || !candidate->range()->isInt32();
bool isCapturedResult = false;
bool isObservableResult = false;
bool hasUseRemoved = candidate->isUseRemoved();
MDefinition::TruncateKind kind = MDefinition::Truncate;
for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) {
if (!use->consumer()->isDefinition()) {
if (use->consumer()->isResumePoint()) {
// Truncation is a destructive optimization, as such, we need to pay
// attention to removed branches and prevent optimization
// destructive optimizations if we have no alternative. (see
// UseRemoved flag)
if (candidate->isUseRemoved() && needsConversion)
kind = Min(kind, MDefinition::TruncateAfterBailouts);
isCapturedResult = true;
isObservableResult = isObservableResult ||
use->consumer()->toResumePoint()->isObservableOperand(*use);
continue;
}
MDefinition *consumer = use->consumer()->toDefinition();
if (consumer->isRecoveredOnBailout()) {
isCapturedResult = true;
hasUseRemoved = hasUseRemoved || consumer->isUseRemoved();
continue;
}
MDefinition::TruncateKind consumerKind = consumer->operandTruncateKind(consumer->indexOf(*use));
kind = Min(kind, consumerKind);
if (kind == MDefinition::NoTruncate)
break;
}
// If the value naturally produces an int32 value (before bailout checks)
// that needs no conversion, we don't have to worry about resume points
// seeing truncated values.
bool needsConversion = !candidate->range() || !candidate->range()->isInt32();
// If the candidate instruction appears as operand of a resume point or a
// recover instruction, and we have to truncate its result, then we might
// have to either recover the result during the bailout, or avoid the
// truncation.
if (isCapturedResult && needsConversion) {
// 1. Recover instructions are useless if there is no removed uses. Not
// having removed uses means that we know everything about where this
// results flows into.
//
// 2. If the result is observable, then we cannot recover it.
//
// 3. The cloned instruction is expected to be used as a recover
// instruction.
if (hasUseRemoved && !isObservableResult && candidate->canRecoverOnBailout())
*shouldClone = true;
// 1. If uses are removed, then we need to keep the expected result for
// dead branches.
//
// 2. If the result is observable, then the result might be read while
// the frame is on the stack.
else if (hasUseRemoved || isObservableResult)
kind = Min(kind, MDefinition::TruncateAfterBailouts);
}
return kind;
}
static MDefinition::TruncateKind
ComputeTruncateKind(MDefinition *candidate)
ComputeTruncateKind(MDefinition *candidate, bool *shouldClone)
{
// Compare operations might coerce its inputs to int32 if the ranges are
// correct. So we do not need to check if all uses are coerced.
@ -2542,7 +2667,7 @@ ComputeTruncateKind(MDefinition *candidate)
return MDefinition::NoTruncate;
// Ensure all observable uses are truncated.
return ComputeRequestedTruncateKind(candidate);
return ComputeRequestedTruncateKind(candidate, shouldClone);
}
static void
@ -2630,6 +2755,9 @@ RangeAnalysis::truncate()
for (PostorderIterator block(graph_.poBegin()); block != graph_.poEnd(); block++) {
for (MInstructionReverseIterator iter(block->rbegin()); iter != block->rend(); iter++) {
if (iter->isRecoveredOnBailout())
continue;
if (iter->type() == MIRType_None) {
if (iter->isTest())
TruncateTest(alloc(), iter->toTest());
@ -2649,14 +2777,23 @@ RangeAnalysis::truncate()
default:;
}
MDefinition::TruncateKind kind = ComputeTruncateKind(*iter);
bool shouldClone = false;
MDefinition::TruncateKind kind = ComputeTruncateKind(*iter, &shouldClone);
if (kind == MDefinition::NoTruncate)
continue;
// Truncate this instruction if possible.
if (!iter->truncate(kind))
if (!iter->needTruncation(kind))
continue;
// If needed, clone the current instruction for keeping it for the
// bailout path. This give us the ability to truncate instructions
// even after the removal of branches.
if (shouldClone && !CloneForDeadBranches(alloc(), *iter))
return false;
iter->truncate();
// Delay updates of inputs/outputs to avoid creating node which
// would be removed by the truncation of the next operations.
iter->setInWorklist();
@ -2664,13 +2801,15 @@ RangeAnalysis::truncate()
return false;
}
for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) {
MDefinition::TruncateKind kind = ComputeTruncateKind(*iter);
bool shouldClone = false;
MDefinition::TruncateKind kind = ComputeTruncateKind(*iter, &shouldClone);
if (kind == MDefinition::NoTruncate)
continue;
// Truncate this phi if possible.
if (!iter->truncate(kind))
if (shouldClone || !iter->needTruncation(kind))
continue;
iter->truncate();
// Delay updates of inputs/outputs to avoid creating node which
// would be removed by the truncation of the next operations.

View File

@ -139,6 +139,12 @@ IsObjectEscaped(MInstruction *ins)
MOZ_ASSERT(ins->type() == MIRType_Object);
MOZ_ASSERT(ins->isNewObject() || ins->isGuardShape() || ins->isCreateThisWithTemplate());
JSObject *obj = nullptr;
if (ins->isNewObject())
obj = ins->toNewObject()->templateObject();
else if (ins->isCreateThisWithTemplate())
obj = ins->toCreateThisWithTemplate()->templateObject();
// Check if the object is escaped. If the object is not the first argument
// of either a known Store / Load, then we consider it as escaped. This is a
// cheap and conservative escape analysis.
@ -184,7 +190,7 @@ IsObjectEscaped(MInstruction *ins)
case MDefinition::Op_GuardShape: {
MGuardShape *guard = def->toGuardShape();
MOZ_ASSERT(!ins->isGuardShape());
if (ins->toNewObject()->templateObject()->lastProperty() != guard->shape()) {
if (obj->lastProperty() != guard->shape()) {
JitSpewDef(JitSpew_Escape, "Object ", ins);
JitSpewDef(JitSpew_Escape, " has a non-matching guard shape\n", guard);
return true;
@ -333,7 +339,8 @@ ObjectMemoryView::mergeIntoSuccessorState(MBasicBlock *curr, MBasicBlock *succ,
*pSuccState = succState;
}
if (succ->numPredecessors() > 1 && succState->numSlots()) {
MOZ_ASSERT_IF(succ == startBlock_, startBlock_->isLoopHeader());
if (succ->numPredecessors() > 1 && succState->numSlots() && succ != startBlock_) {
// We need to re-compute successorWithPhis as the previous EliminatePhis
// phase might have removed all the Phis from the successor block.
size_t currIndex;
@ -787,7 +794,8 @@ ArrayMemoryView::mergeIntoSuccessorState(MBasicBlock *curr, MBasicBlock *succ,
*pSuccState = succState;
}
if (succ->numPredecessors() > 1 && succState->numElements()) {
MOZ_ASSERT_IF(succ == startBlock_, startBlock_->isLoopHeader());
if (succ->numPredecessors() > 1 && succState->numElements() && succ != startBlock_) {
// We need to re-compute successorWithPhis as the previous EliminatePhis
// phase might have removed all the Phis from the successor block.
size_t currIndex;

View File

@ -153,6 +153,7 @@ ReplaceAllUsesWith(MDefinition *from, MDefinition *to)
{
MOZ_ASSERT(from != to, "GVN shouldn't try to replace a value with itself");
MOZ_ASSERT(from->type() == to->type(), "Def replacement has different type");
MOZ_ASSERT(!to->isDiscarded(), "GVN replaces an instruction by a removed instruction");
// We don't need the extra setting of UseRemoved flags that the regular
// replaceAllUsesWith does because we do it ourselves.
@ -639,7 +640,7 @@ ValueNumberer::leader(MDefinition *def)
VisibleValues::AddPtr p = values_.findLeaderForAdd(def);
if (p) {
MDefinition *rep = *p;
if (rep->block()->dominates(def->block())) {
if (!rep->isDiscarded() && rep->block()->dominates(def->block())) {
// We found a dominating congruent value.
return rep;
}
@ -829,6 +830,8 @@ ValueNumberer::visitControlInstruction(MBasicBlock *block, const MBasicBlock *do
return false;
block->discardIgnoreOperands(control);
block->end(newControl);
if (block->entryResumePoint() && newNumSuccs != oldNumSuccs)
block->flagOperandsOfPrunedBranches(newControl);
return processDeadDefs();
}

View File

@ -38,20 +38,6 @@
# endif
#endif
// Not part of the HWCAP flag, but we need to know these and these bits are not used.
// A bit to flag the use of the ARMv7 arch, otherwise ARMv6.
#define HWCAP_ARMv7 (1 << 28)
// A bit to flag the use of the hardfp ABI.
#define HWCAP_USE_HARDFP_ABI (1 << 27)
// A bit to flag when alignment faults are enabled and signal.
#define HWCAP_ALIGNMENT_FAULT (1 << 26)
// A bit to flag when the flags are uninitialized, so they can be atomically set.
#define HWCAP_UNINITIALIZED (1 << 25)
namespace js {
namespace jit {
@ -142,7 +128,7 @@ CanonicalizeARMHwCapFlags(uint32_t flags)
// The override flags parsed from the ARMHWCAP environment variable or from the
// --arm-hwcap js shell argument.
volatile static uint32_t armHwCapFlags = HWCAP_UNINITIALIZED;
volatile uint32_t armHwCapFlags = HWCAP_UNINITIALIZED;
bool
ParseARMHwCapFlags(const char *armHwCap)
@ -317,14 +303,6 @@ bool HasIDIV()
return armHwCapFlags & HWCAP_IDIVA;
}
// Returns true when cpu alignment faults are enabled and signaled, and thus we
// should ensure loads and stores are aligned.
bool HasAlignmentFault()
{
MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
return armHwCapFlags & HWCAP_ALIGNMENT_FAULT;
}
// This is defined in the header and inlined when not using the simulator.
#if defined(JS_ARM_SIMULATOR)
bool UseHardFpABI()

View File

@ -521,7 +521,31 @@ bool HasVFPv3();
bool HasVFP();
bool Has32DP();
bool HasIDIV();
bool HasAlignmentFault();
extern volatile uint32_t armHwCapFlags;
// Not part of the HWCAP flag, but we need to know these and these bits are not
// used. Define these here so that their use can be inlined by the simulator.
// A bit to flag when the flags are uninitialized, so they can be atomically set.
#define HWCAP_UNINITIALIZED (1 << 25)
// A bit to flag when alignment faults are enabled and signal.
#define HWCAP_ALIGNMENT_FAULT (1 << 26)
// A bit to flag the use of the hardfp ABI.
#define HWCAP_USE_HARDFP_ABI (1 << 27)
// A bit to flag the use of the ARMv7 arch, otherwise ARMv6.
#define HWCAP_ARMv7 (1 << 28)
// Returns true when cpu alignment faults are enabled and signaled, and thus we
// should ensure loads and stores are aligned.
inline bool HasAlignmentFault()
{
MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
return armHwCapFlags & HWCAP_ALIGNMENT_FAULT;
}
// Arm/D32 has double registers that can NOT be treated as float32 and this
// requires some dances in lowering.

View File

@ -172,7 +172,7 @@ LIRGeneratorARM::visitReturn(MReturn *ret)
bool
LIRGeneratorARM::lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input)
{
ins->setOperand(0, useRegister(input));
ins->setOperand(0, ins->snapshot() ? useRegister(input) : useRegisterAtStart(input));
return define(ins, mir,
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
@ -181,8 +181,11 @@ LIRGeneratorARM::lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
bool
LIRGeneratorARM::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
// Some operations depend on checking inputs after writing the result, e.g.
// MulI, but only for bail out paths so useAtStart when no bailouts.
ins->setOperand(0, ins->snapshot() ? useRegister(lhs) : useRegisterAtStart(lhs));
ins->setOperand(1, ins->snapshot() ? useRegisterOrConstant(rhs) :
useRegisterOrConstantAtStart(rhs));
return define(ins, mir,
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
@ -190,7 +193,7 @@ LIRGeneratorARM::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
bool
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input)
{
ins->setOperand(0, useRegister(input));
ins->setOperand(0, useRegisterAtStart(input));
return define(ins, mir,
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
@ -199,8 +202,8 @@ LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
bool
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegister(rhs));
ins->setOperand(0, useRegisterAtStart(lhs));
ins->setOperand(1, useRegisterAtStart(rhs));
return define(ins, mir,
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}

View File

@ -58,6 +58,18 @@ class LIRGeneratorARM : public LIRGeneratorShared
MDefinition *src);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
bool lowerForCompFx4(LSimdBinaryCompFx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerConstantDouble(double d, MInstruction *ins);

View File

@ -1493,8 +1493,13 @@ Simulator::readW(int32_t addr, SimInstruction *instr)
{
// The regexp engine emits unaligned loads, so we don't check for them here
// like most of the other methods do.
intptr_t *ptr = reinterpret_cast<intptr_t*>(addr);
return *ptr;
if ((addr & 3) == 0 || !HasAlignmentFault()) {
intptr_t *ptr = reinterpret_cast<intptr_t*>(addr);
return *ptr;
} else {
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
MOZ_CRASH();
}
}
void
@ -1514,8 +1519,13 @@ Simulator::readHU(int32_t addr, SimInstruction *instr)
{
// The regexp engine emits unaligned loads, so we don't check for them here
// like most of the other methods do.
uint16_t *ptr = reinterpret_cast<uint16_t*>(addr);
return *ptr;
if ((addr & 1) == 0 || !HasAlignmentFault()) {
uint16_t *ptr = reinterpret_cast<uint16_t*>(addr);
return *ptr;
}
printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
MOZ_CRASH();
return 0;
}
int16_t

View File

@ -58,6 +58,18 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
MDefinition *src);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
bool lowerForCompFx4(LSimdBinaryCompFx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs)
{
return lowerForFPU(ins, mir, lhs, rhs);
}
bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerConstantDouble(double d, MInstruction *ins);

View File

@ -42,6 +42,14 @@ class LIRGeneratorNone : public LIRGeneratorShared
bool lowerForALU(T, MDefinition *, MDefinition *, MDefinition *v = nullptr) { MOZ_CRASH(); }
template <typename T>
bool lowerForFPU(T, MDefinition *, MDefinition *, MDefinition *v = nullptr) { MOZ_CRASH(); }
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs) {
MOZ_CRASH();
}
bool lowerForCompFx4(LSimdBinaryCompFx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs) {
MOZ_CRASH();
}
bool lowerForBitAndAndBranch(LBitAndAndBranch *, MInstruction *,
MDefinition *, MDefinition *) {
MOZ_CRASH();

View File

@ -2413,7 +2413,7 @@ CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *ins)
// scr := scr > lhs (i.e. lhs < rhs)
// Improve by doing custom lowering (rhs is tied to the output register)
masm.packedGreaterThanInt32x4(ToOperand(ins->lhs()), ScratchSimdReg);
masm.moveAlignedInt32x4(ScratchFloat32Reg, lhs);
masm.moveAlignedInt32x4(ScratchSimdReg, lhs);
return true;
case MSimdBinaryComp::notEqual:
case MSimdBinaryComp::greaterThanOrEqual:
@ -2446,11 +2446,10 @@ CodeGeneratorX86Shared::visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *ins)
masm.cmpps(rhs, lhs, 0x4);
return true;
case MSimdBinaryComp::greaterThanOrEqual:
masm.cmpps(rhs, lhs, 0x5);
return true;
case MSimdBinaryComp::greaterThan:
masm.cmpps(rhs, lhs, 0x6);
return true;
// We reverse these before register allocation so that we don't have to
// copy into and out of temporaries after codegen.
MOZ_CRASH("lowering should have reversed this");
}
MOZ_CRASH("unexpected SIMD op");
}

View File

@ -17,6 +17,7 @@ using namespace js::jit;
using mozilla::Abs;
using mozilla::FloorLog2;
using mozilla::Swap;
LTableSwitch *
LIRGeneratorX86Shared::newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy,
@ -107,6 +108,31 @@ LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition
return defineReuseInput(ins, mir, 0);
}
bool
LIRGeneratorX86Shared::lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir, MDefinition *lhs, MDefinition *rhs)
{
return lowerForALU(ins, mir, lhs, rhs);
}
bool
LIRGeneratorX86Shared::lowerForCompFx4(LSimdBinaryCompFx4 *ins, MSimdBinaryComp *mir, MDefinition *lhs, MDefinition *rhs)
{
// Swap the operands around to fit the instructions that x86 actually has.
// We do this here, before register allocation, so that we don't need
// temporaries and copying afterwards.
switch (mir->operation()) {
case MSimdBinaryComp::greaterThan:
case MSimdBinaryComp::greaterThanOrEqual:
mir->reverse();
Swap(lhs, rhs);
break;
default:
break;
}
return lowerForFPU(ins, mir, lhs, rhs);
}
bool
LIRGeneratorX86Shared::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs)

View File

@ -33,6 +33,10 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
MDefinition *rhs);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForCompFx4(LSimdBinaryCompFx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs);
bool visitConstant(MConstant *ins);

View File

@ -0,0 +1,21 @@
/*
* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/
*/
var BUGNUMBER = 1076588;
var summary = "Object.freeze() should return its argument with no conversion when the argument is a primitive value";
print(BUGNUMBER + ": " + summary);
assertEq(Object.freeze(), undefined);
assertEq(Object.freeze(undefined), undefined);
assertEq(Object.freeze(null), null);
assertEq(Object.freeze(1), 1);
assertEq(Object.freeze("foo"), "foo");
assertEq(Object.freeze(true), true);
if (typeof Symbol === "function") {
assertEq(Object.freeze(Symbol.for("foo")), Symbol.for("foo"));
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -1552,14 +1552,10 @@ jit::JitActivation::markRematerializedFrames(JSTracer *trc)
}
bool
jit::JitActivation::registerIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults&& results)
jit::JitActivation::registerIonFrameRecovery(RInstructionResults&& results)
{
#ifdef DEBUG
// Check that there is no entry in the vector yet.
RInstructionResults *tmp = maybeIonFrameRecovery(fp);
MOZ_ASSERT_IF(tmp, tmp->isInitialized());
#endif
MOZ_ASSERT(!maybeIonFrameRecovery(results.frame()));
if (!ionRecovery_.append(mozilla::Move(results)))
return false;

View File

@ -1405,7 +1405,7 @@ class JitActivation : public Activation
// Register the results of on Ion frame recovery.
bool registerIonFrameRecovery(IonJSFrameLayout *fp, RInstructionResults&& results);
bool registerIonFrameRecovery(RInstructionResults&& results);
// Return the pointer to the Ion frame recovery, if it is already registered.
RInstructionResults *maybeIonFrameRecovery(IonJSFrameLayout *fp);

View File

@ -3788,11 +3788,12 @@ nsFrame::GetCursor(const nsPoint& aPoint,
if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
// If this is editable, I-beam cursor is better for most elements.
aCursor.mCursor =
(mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT :
NS_STYLE_CURSOR_DEFAULT;
(mContent && mContent->IsEditable())
? GetWritingMode().IsVertical()
? NS_STYLE_CURSOR_VERTICAL_TEXT : NS_STYLE_CURSOR_TEXT
: NS_STYLE_CURSOR_DEFAULT;
}
return NS_OK;
}

View File

@ -4217,9 +4217,10 @@ nsresult
nsTextFrame::GetCursor(const nsPoint& aPoint,
nsIFrame::Cursor& aCursor)
{
FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
aCursor.mCursor = NS_STYLE_CURSOR_TEXT;
aCursor.mCursor = GetWritingMode().IsVertical()
? NS_STYLE_CURSOR_VERTICAL_TEXT : NS_STYLE_CURSOR_TEXT;
// If this is editable, we should ignore tabindex value.
if (mContent->IsEditable()) {
return NS_OK;

View File

@ -18,9 +18,13 @@ __BEGIN_DECLS
#endif
#ifdef CPR_USE_OS_STRCASECMP
/* Use standard library types */
/* Use standard library types, but use the OS's name for the functions */
#ifndef cpr_strcasecmp
#define cpr_strcasecmp strcasecmp
#endif
#ifndef cpr_strncasecmp
#define cpr_strncasecmp strncasecmp
#endif
#else
/* Prototypes */
/**

View File

@ -10,4 +10,6 @@
#define cpr_strcasecmp _stricmp
#define cpr_strncasecmp _strnicmp
#define CPR_USE_OS_STRCASECMP
#endif /* CPR_WIN32_STRINGS_H_ */

View File

@ -664,6 +664,16 @@ public class BrowserApp extends GeckoApp
// Set the maximum bits-per-pixel the favicon system cares about.
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
Class<?> mediaManagerClass = getMediaPlayerManager();
if (mediaManagerClass != null) {
try {
Method init = mediaManagerClass.getMethod("init", Context.class);
init.invoke(null, this);
} catch(Exception ex) {
Log.e(LOGTAG, "Error initializing media manager", ex);
}
}
if (getProfile().inGuestMode()) {
GuestSession.showNotification(this);
} else {
@ -1539,19 +1549,6 @@ public class BrowserApp extends GeckoApp
}
});
if (AppConstants.MOZ_MEDIA_PLAYER) {
// If casting is disabled, these classes aren't built. We use reflection to initialize them.
Class<?> mediaManagerClass = getMediaPlayerManager();
if (mediaManagerClass != null) {
try {
Method init = mediaManagerClass.getMethod("init", Context.class);
init.invoke(null, this);
} catch(Exception ex) {
Log.e(LOGTAG, "Error initializing media manager", ex);
}
}
}
if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED) {
// Start (this acts as ping if started already) the stumbler lib; if the stumbler has queued data it will upload it.
// Stumbler operates on its own thread, and startup impact is further minimized by delaying work (such as upload) a few seconds.
@ -1564,7 +1561,6 @@ public class BrowserApp extends GeckoApp
}
}, oneSecondInMillis);
}
super.handleMessage(event, message);
} else if (event.equals("Gecko:Ready")) {
// Handle this message in GeckoApp, but also enable the Settings

View File

@ -191,7 +191,6 @@ public abstract class GeckoApp
private Telemetry.Timer mGeckoReadyStartupTimer;
private String mPrivateBrowsingSession;
private volatile boolean mIsPaused = true;
private volatile HealthRecorder mHealthRecorder;
private volatile Locale mLastLocale;
@ -247,13 +246,7 @@ public abstract class GeckoApp
}
public void addAppStateListener(GeckoAppShell.AppStateListener listener) {
ThreadUtils.assertOnUiThread();
mAppStateListeners.add(listener);
// If we're already resumed, make sure the listener gets a notification.
if (!mIsPaused) {
listener.onResume();
}
}
public void removeAppStateListener(GeckoAppShell.AppStateListener listener) {
@ -1892,8 +1885,6 @@ public abstract class GeckoApp
listener.onResume();
}
}
// Setting this state will force any listeners added after this to have their onResume() method called
mIsPaused = false;
// We use two times: a pseudo-unique wall-clock time to identify the
// current session across power cycles, and the elapsed realtime to
@ -1970,9 +1961,6 @@ public abstract class GeckoApp
}
});
// Setting this state will keep any listeners registered after this from having their onResume
// method called.
mIsPaused = true;
if (mAppStateListeners != null) {
for(GeckoAppShell.AppStateListener listener: mAppStateListeners) {
listener.onPause();

View File

@ -246,9 +246,9 @@ class HTMLReportingTestResultMixin(object):
def gather_debug(self):
debug = {}
try:
# TODO make screenshot consistant size by using full viewport
# Bug 883294 - Add ability to take full viewport screenshots
self.marionette.switch_context(self.marionette.CONTEXT_CHROME)
debug['screenshot'] = self.marionette.screenshot()
self.marionette.switch_context(self.marionette.CONTEXT_CONTENT)
debug['source'] = self.marionette.page_source
self.marionette.switch_to_frame()
debug['settings'] = json.dumps(self.marionette.execute_async_script("""

View File

@ -1,11 +1,25 @@
from marionette_test import MarionetteTestCase
import base64
import imghdr
from marionette_test import MarionetteTestCase
RED_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVUlEQVRoge3PsQ0AIAzAsI78fzBwBhHykD2ePev80LweAAGJB1ILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdQCqQVSC6QWSC2QWiC1QGp9A7ma+7nyXgOpzQAAAABJRU5ErkJggg=='
GREEN_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAV0lEQVRoge3PQRGAQAwAsWINvXgsNnI3+4iAzM7sDWZn9vneoxXRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNHcF7nBD/Ha5Ye4BbsYAAAAAElFTkSuQmCC'
class ScreenshotTests(MarionetteTestCase):
def testWeCanTakeAScreenShotOfEntireViewport(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
content = self.marionette.screenshot()
self.marionette.set_context(self.marionette.CONTEXT_CHROME)
chrome = self.marionette.screenshot()
# Check the base64 decoded string is a PNG file.
image = base64.decodestring(chrome)
self.assertEqual(imghdr.what('', image), 'png')
self.assertNotEqual(content, chrome)
def testWeCanTakeAScreenShotOfAnElement(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
@ -20,11 +34,12 @@ class ScreenshotTests(MarionetteTestCase):
self.assertEqual(GREEN_ELEMENT_BASE64,
self.marionette.screenshot(element=el, highlights=[el]))
def testWeCanTakeAScreenShotEntireCanvas(self):
def testWeCanTakeAScreenShotOfEntireCanvas(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)
self.assertTrue('iVBORw0KGgo' in
self.marionette.screenshot())
# Check the base64 decoded string is a PNG file.
image = base64.decodestring(self.marionette.screenshot())
self.assertEqual(imghdr.what('', image), 'png')
def testWeCanTakeABinaryScreenShotOfAnElement(self):
test_url = self.marionette.absolute_url('html5Page.html')
@ -33,7 +48,7 @@ class ScreenshotTests(MarionetteTestCase):
binary_data = self.marionette.screenshot(element=el, format="binary")
self.assertEqual(RED_ELEMENT_BASE64,
base64.b64encode(binary_data))
def testNotAllowedScreenshotFormatRaiseValueError(self):
test_url = self.marionette.absolute_url('html5Page.html')
self.marionette.navigate(test_url)

View File

@ -373,6 +373,19 @@ MarionetteServerConnection.prototype = {
return Services.wm.getEnumerator(type);
},
/**
*/
addFrameCloseListener: function MDA_addFrameCloseListener(action) {
let curWindow = this.getCurrentWindow();
let self = this;
this.mozBrowserClose = function() {
curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true);
self.switchToGlobalMessageManager();
self.sendError("The frame closed during the " + action + ", recovering to allow further communications", 500, null, self.command_id);
};
curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true);
},
/**
* Create a new BrowserObj for window and add to known browsers
*
@ -1526,6 +1539,7 @@ MarionetteServerConnection.prototype = {
this.sendError("Command 'singleTap' is not available in chrome context", 500, null, this.command_id);
}
else {
this.addFrameCloseListener("tap");
this.sendAsync("singleTap",
{
id: serId,
@ -1548,6 +1562,7 @@ MarionetteServerConnection.prototype = {
this.sendError("Command 'actionChain' is not available in chrome context", 500, null, this.command_id);
}
else {
this.addFrameCloseListener("action chain");
this.sendAsync("actionChain",
{
chain: aRequest.parameters.chain,
@ -1572,6 +1587,7 @@ MarionetteServerConnection.prototype = {
this.sendError("Command 'multiAction' is not available in chrome context", 500, null, this.command_id);
}
else {
this.addFrameCloseListener("multi action chain");
this.sendAsync("multiAction",
{
value: aRequest.parameters.value,
@ -1734,14 +1750,7 @@ MarionetteServerConnection.prototype = {
// This fires the mozbrowserclose event when it closes so we need to
// listen for it and then just send an error back. The person making the
// call should be aware something isnt right and handle accordingly
let curWindow = this.getCurrentWindow();
let self = this;
this.mozBrowserClose = function() {
curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true);
self.switchToGlobalMessageManager();
self.sendError("The frame closed during the click, recovering to allow further communications", 500, null, command_id);
};
curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true);
this.addFrameCloseListener("click");
this.sendAsync("clickElement",
{ id: aRequest.parameters.id },
command_id);
@ -2362,26 +2371,70 @@ MarionetteServerConnection.prototype = {
},
/**
* Takes a screenshot of a web element or the current frame.
* Takes a screenshot of a web element, current frame, or viewport.
*
* The screen capture is returned as a lossless PNG image encoded as
* a base 64 string. If the <code>id</code> argument is not null
* and refers to a present and visible web element's ID, the capture
* area will be limited to the bounding box of that element.
* Otherwise, the capture area will be the bounding box of the
* current frame.
* a base 64 string.
*
* @param id an optional reference to a web element
* @param highlights an optional list of web elements to draw a red
* box around in the returned capture
* @return PNG image encoded as base 64 string
*/
* If called in the content context, the <code>id</code> argument is not null
* and refers to a present and visible web element's ID, the capture area
* will be limited to the bounding box of that element. Otherwise, the
* capture area will be the bounding box of the current frame.
*
* If called in the chrome context, the screenshot will always represent the
* entire viewport.
*
* @param {string} [id] Reference to a web element.
* @param {string} [highlights] List of web elements to highlight.
* @return {string} PNG image encoded as base 64 string.
*/
takeScreenshot: function MDA_takeScreenshot(aRequest) {
this.command_id = this.getCommandId();
this.sendAsync("takeScreenshot",
if (this.context == "chrome") {
var win = this.getCurrentWindow();
var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
var doc;
if (appName == "B2G") {
doc = win.document.body;
} else {
doc = win.document.getElementsByTagName('window')[0];
}
var docRect = doc.getBoundingClientRect();
var width = docRect.width;
var height = docRect.height;
// Convert width and height from CSS pixels (potentially fractional)
// to device pixels (integer).
var scale = win.devicePixelRatio;
canvas.setAttribute("width", Math.round(width * scale));
canvas.setAttribute("height", Math.round(height * scale));
var context = canvas.getContext("2d");
var flags;
if (appName == "B2G") {
flags =
context.DRAWWINDOW_DRAW_CARET |
context.DRAWWINDOW_DRAW_VIEW |
context.DRAWWINDOW_USE_WIDGET_LAYERS;
} else {
// Bug 1075168 - CanvasRenderingContext2D image is distorted
// when using certain flags in chrome context.
flags =
context.DRAWWINDOW_DRAW_VIEW |
context.DRAWWINDOW_USE_WIDGET_LAYERS;
}
context.scale(scale, scale);
context.drawWindow(win, 0, 0, width, height, "rgb(255,255,255)", flags);
var dataUrl = canvas.toDataURL("image/png", "");
var data = dataUrl.substring(dataUrl.indexOf(",") + 1);
this.sendResponse(data, this.command_id);
}
else {
this.sendAsync("takeScreenshot",
{id: aRequest.parameters.id,
highlights: aRequest.parameters.highlights},
this.command_id);
}
},
/**

View File

@ -46,7 +46,7 @@ class BaseRunner(object):
self.process_args = process_args or {}
self.symbols_path = symbols_path
self.crashed = False
self.crashed = 0
def __del__(self):
self.cleanup()
@ -100,6 +100,7 @@ class BaseRunner(object):
self.process_handler = self.process_class(cmd, env=self.env, **self.process_args)
self.process_handler.run(self.timeout, self.output_timeout)
self.crashed = 0
return self.process_handler.pid
def wait(self, timeout=None):
@ -180,24 +181,26 @@ class BaseRunner(object):
if not dump_directory:
dump_directory = os.path.join(self.profile.profile, 'minidumps')
self.crashed = False
try:
logger = get_default_logger()
if logger is not None:
if test_name is None:
test_name = "runner.py"
self.crashed = mozcrash.log_crashes(logger,
dump_directory,
self.symbols_path,
dump_save_path=dump_save_path,
test=test_name)
self.crashed += mozcrash.log_crashes(
logger,
dump_directory,
self.symbols_path,
dump_save_path=dump_save_path,
test=test_name)
else:
self.crashed = mozcrash.check_for_crashes(
crashed = mozcrash.check_for_crashes(
dump_directory,
self.symbols_path,
dump_save_path=dump_save_path,
test_name=test_name,
quiet=quiet)
if crashed:
self.crashed += 1
except:
traceback.print_exc()

View File

@ -118,6 +118,12 @@ let CrashMonitorInternal = {
return null;
}
// If `notifications` isn't an object, then the monitor data isn't valid.
if (Object(notifications) !== notifications) {
Cu.reportError("Error while parsing crash monitor data: invalid monitor data");
return null;
}
return Object.freeze(notifications);
});

View File

@ -12,19 +12,11 @@ add_task(function test_invalid_file() {
yield OS.File.writeAtomic(sessionCheckpointsPath, data,
{tmpPath: sessionCheckpointsPath + ".tmp"});
// An invalid file will cause |init| to throw an exception
try {
let status = yield CrashMonitor.init();
do_check_true(false);
} catch (ex) {
do_check_true(true);
}
// An invalid file will cause |init| to return null
let status = yield CrashMonitor.init();
do_check_true(status === null ? true : false);
// and |previousCheckpoints| will be rejected
try {
let checkpoints = yield CrashMonitor.previousCheckpoints;
do_check_true(false);
} catch (ex) {
do_check_true(true);
}
// and |previousCheckpoints| will be null
let checkpoints = yield CrashMonitor.previousCheckpoints;
do_check_true(checkpoints === null ? true : false);
});

View File

@ -144,7 +144,7 @@ XULStore.prototype = {
readFile: function() {
const MODE_RDONLY = 0x01;
const FILE_PERMS = 0600;
const FILE_PERMS = 0o600;
let stream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);

View File

@ -538,7 +538,7 @@
<xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
<xul:menuseparator anonid="spell-check-separator"/>
<xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled(window);"/>
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
<xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
<xul:menupopup anonid="spell-dictionaries-menu"
onpopupshowing="event.stopPropagation();"

View File

@ -807,9 +807,14 @@ void UIError_impl(const string& message)
bool UIGetIniPath(string& path)
{
path = gArgv[0];
path.append(".ini");
NSString* tmpPath = [NSString stringWithUTF8String:gArgv[0]];
NSString* iniName = [tmpPath lastPathComponent];
iniName = [iniName stringByAppendingPathExtension:@"ini"];
tmpPath = [tmpPath stringByDeletingLastPathComponent];
tmpPath = [tmpPath stringByDeletingLastPathComponent];
tmpPath = [tmpPath stringByAppendingPathComponent:@"Resources"];
tmpPath = [tmpPath stringByAppendingPathComponent:iniName];
path = [tmpPath UTF8String];
return true;
}

View File

@ -2,11 +2,16 @@
* 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/. */
this.EXPORTED_SYMBOLS = [ "InlineSpellChecker" ];
this.EXPORTED_SYMBOLS = [ "InlineSpellChecker",
"SpellCheckHelper" ];
var gLanguageBundle;
var gRegionBundle;
const MAX_UNDO_STACK_DEPTH = 1;
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
this.InlineSpellChecker = function InlineSpellChecker(aEditor) {
this.init(aEditor);
this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls
@ -26,9 +31,27 @@ InlineSpellChecker.prototype = {
}
},
initFromRemote: function(aSpellInfo)
{
if (this.mRemote)
throw new Error("Unexpected state");
this.uninit();
if (!aSpellInfo)
return;
this.mInlineSpellChecker = this.mRemote = new RemoteSpellChecker(aSpellInfo);
this.mOverMisspelling = aSpellInfo.overMisspelling;
this.mMisspelling = aSpellInfo.misspelling;
},
// call this to clear state
uninit: function()
{
if (this.mRemote) {
this.mRemote.uninit();
this.mRemote = null;
}
this.mEditor = null;
this.mInlineSpellChecker = null;
this.mOverMisspelling = false;
@ -73,10 +96,15 @@ InlineSpellChecker.prototype = {
{
// inline spell checker objects will be created only if there are actual
// dictionaries available
return (this.mInlineSpellChecker != null);
if (this.mRemote)
return this.mRemote.canSpellCheck;
return this.mInlineSpellChecker != null;
},
get initialSpellCheckPending() {
if (this.mRemote) {
return this.mRemote.spellCheckPending;
}
return !!(this.mInlineSpellChecker &&
!this.mInlineSpellChecker.spellChecker &&
this.mInlineSpellChecker.spellCheckPending);
@ -85,12 +113,16 @@ InlineSpellChecker.prototype = {
// Whether spellchecking is enabled in the current box
get enabled()
{
if (this.mRemote)
return this.mRemote.enableRealTimeSpell;
return (this.mInlineSpellChecker &&
this.mInlineSpellChecker.enableRealTimeSpell);
},
set enabled(isEnabled)
{
if (this.mInlineSpellChecker)
if (this.mRemote)
this.mRemote.setSpellcheckUserOverride(isEnabled);
else if (this.mInlineSpellChecker)
this.mEditor.setSpellcheckUserOverride(isEnabled);
},
@ -104,12 +136,12 @@ InlineSpellChecker.prototype = {
// for the word under the cursor. Returns the number of suggestions inserted.
addSuggestionsToMenu: function(menu, insertBefore, maxNumber)
{
if (! this.mInlineSpellChecker || ! this.mOverMisspelling)
if (!this.mRemote && (!this.mInlineSpellChecker || !this.mOverMisspelling))
return 0; // nothing to do
var spellchecker = this.mInlineSpellChecker.spellChecker;
var spellchecker = this.mRemote || this.mInlineSpellChecker.spellChecker;
try {
if (! spellchecker.CheckCurrentWord(this.mMisspelling))
if (!this.mRemote && !spellchecker.CheckCurrentWord(this.mMisspelling))
return 0; // word seems not misspelled after all (?)
} catch(e) {
return 0;
@ -148,31 +180,7 @@ InlineSpellChecker.prototype = {
this.mSuggestionItems = [];
},
// returns the number of dictionary languages. If insertBefore is NULL, this
// does an append to the given menu
addDictionaryListToMenu: function(menu, insertBefore)
{
this.mDictionaryMenu = menu;
this.mDictionaryNames = [];
this.mDictionaryItems = [];
if (! this.mInlineSpellChecker || ! this.enabled)
return 0;
var spellchecker = this.mInlineSpellChecker.spellChecker;
// Cannot access the dictionary list from another process so just return 0.
if (Components.utils.isCrossProcessWrapper(spellchecker))
return 0;
var o1 = {}, o2 = {};
spellchecker.GetDictionaryList(o1, o2);
var list = o1.value;
var listcount = o2.value;
var curlang = "";
try {
curlang = spellchecker.GetCurrentDictionary();
} catch(e) {}
sortDictionaryList: function(list) {
var sortedList = [];
for (var i = 0; i < list.length; i ++) {
sortedList.push({"id": list[i],
@ -186,6 +194,39 @@ InlineSpellChecker.prototype = {
return 0;
});
return sortedList;
},
// returns the number of dictionary languages. If insertBefore is NULL, this
// does an append to the given menu
addDictionaryListToMenu: function(menu, insertBefore)
{
this.mDictionaryMenu = menu;
this.mDictionaryNames = [];
this.mDictionaryItems = [];
if (!this.enabled)
return 0;
var list;
var curlang = "";
if (this.mRemote) {
list = this.mRemote.dictionaryList;
curlang = this.mRemote.currentDictionary;
}
else if (this.mInlineSpellChecker) {
var spellchecker = this.mInlineSpellChecker.spellChecker;
var o1 = {}, o2 = {};
spellchecker.GetDictionaryList(o1, o2);
list = o1.value;
var listcount = o2.value;
try {
curlang = spellchecker.GetCurrentDictionary();
} catch(e) {}
}
var sortedList = this.sortDictionaryList(list);
for (var i = 0; i < sortedList.length; i ++) {
this.mDictionaryNames.push(sortedList[i].id);
var item = menu.ownerDocument.createElement("menuitem");
@ -198,7 +239,7 @@ InlineSpellChecker.prototype = {
} else {
var callback = function(me, val) {
return function(evt) {
me.selectDictionary(val, menu.ownerDocument.defaultView);
me.selectDictionary(val);
}
};
item.addEventListener("command", callback(this, i), true);
@ -281,18 +322,10 @@ InlineSpellChecker.prototype = {
},
// callback for selecting a dictionary
selectDictionary: function(index, aWindow)
selectDictionary: function(index)
{
// Avoid a crash in multiprocess until Bug 1030451 lands
// Remove aWindow parameter at that time
const Ci = Components.interfaces;
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIXULWindow).chromeFlags;
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
if (chromeFlags & chromeRemoteWindow) {
if (this.mRemote) {
this.mRemote.selectDictionary(index);
return;
}
if (! this.mInlineSpellChecker || index < 0 || index >= this.mDictionaryNames.length)
@ -305,6 +338,10 @@ InlineSpellChecker.prototype = {
// callback for selecting a suggesteed replacement
replaceMisspelling: function(index)
{
if (this.mRemote) {
this.mRemote.replaceMisspelling(index);
return;
}
if (! this.mInlineSpellChecker || ! this.mOverMisspelling)
return;
if (index < 0 || index >= this.mSpellSuggestions.length)
@ -314,21 +351,12 @@ InlineSpellChecker.prototype = {
},
// callback for enabling or disabling spellchecking
toggleEnabled: function(aWindow)
toggleEnabled: function()
{
// Avoid a crash in multiprocess until Bug 1030451 lands
// Remove aWindow parameter at that time
const Ci = Components.interfaces;
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIXULWindow).chromeFlags;
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
if (chromeFlags & chromeRemoteWindow) {
return;
}
this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
if (this.mRemote)
this.mRemote.toggleEnabled();
else
this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
},
// callback for adding the current misspelling to the user-defined dictionary
@ -339,7 +367,11 @@ InlineSpellChecker.prototype = {
this.mAddedWordStack.shift();
this.mAddedWordStack.push(this.mMisspelling);
this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling);
if (this.mRemote)
this.mRemote.addToDictionary();
else {
this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling);
}
},
// callback for removing the last added word to the dictionary LIFO fashion
undoAddToDictionary: function()
@ -347,7 +379,10 @@ InlineSpellChecker.prototype = {
if (this.mAddedWordStack.length > 0)
{
var word = this.mAddedWordStack.pop();
this.mInlineSpellChecker.removeWordFromDictionary(word);
if (this.mRemote)
this.mRemote.undoAddToDictionary(word);
else
this.mInlineSpellChecker.removeWordFromDictionary(word);
}
},
canUndo : function()
@ -357,6 +392,182 @@ InlineSpellChecker.prototype = {
},
ignoreWord: function()
{
this.mInlineSpellChecker.ignoreWord(this.mMisspelling);
if (this.mRemote)
this.mRemote.ignoreWord();
else
this.mInlineSpellChecker.ignoreWord(this.mMisspelling);
}
};
var SpellCheckHelper = {
// Set when over a non-read-only <textarea> or editable <input>.
EDITABLE: 0x1,
// Set when over an <input> element of any type.
INPUT: 0x2,
// Set when over any <textarea>.
TEXTAREA: 0x4,
// Set when over any text-entry <input>.
TEXTINPUT: 0x8,
// Set when over an <input> that can be used as a keyword field.
KEYWORD: 0x10,
// Set when over an element that otherwise would not be considered
// "editable" but is because content editable is enabled for the document.
CONTENTEDITABLE: 0x20,
isTargetAKeywordField(aNode, window) {
if (!(aNode instanceof window.HTMLInputElement))
return false;
var form = aNode.form;
if (!form || aNode.type == "password")
return false;
var method = form.method.toUpperCase();
// These are the following types of forms we can create keywords for:
//
// method encoding type can create keyword
// GET * YES
// * YES
// POST YES
// POST application/x-www-form-urlencoded YES
// POST text/plain NO (a little tricky to do)
// POST multipart/form-data NO
// POST everything else YES
return (method == "GET" || method == "") ||
(form.enctype != "text/plain") && (form.enctype != "multipart/form-data");
},
// Returns the computed style attribute for the given element.
getComputedStyle(aElem, aProp) {
return aElem.ownerDocument
.defaultView
.getComputedStyle(aElem, "").getPropertyValue(aProp);
},
isEditable(element, window) {
var flags = 0;
if (element instanceof window.HTMLInputElement) {
flags |= this.INPUT;
if (element.mozIsTextField(false)) {
flags |= this.TEXTINPUT;
// Allow spellchecking UI on all text and search inputs.
if (!element.readOnly &&
(element.type == "text" || element.type == "search")) {
flags |= this.EDITABLE;
}
if (this.isTargetAKeywordField(element, window))
flags |= this.KEYWORD;
}
} else if (element instanceof window.HTMLTextAreaElement) {
flags |= this.TEXTINPUT | this.TEXTAREA;
if (!element.readOnly) {
flags |= this.EDITABLE;
}
}
if (!(flags & this.EDITABLE)) {
var win = element.ownerDocument.defaultView;
if (win) {
var isEditable = false;
try {
var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
if (editingSession.windowIsEditable(win) &&
this.getComputedStyle(element, "-moz-user-modify") == "read-write") {
isEditable = true;
}
}
catch(ex) {
// If someone built with composer disabled, we can't get an editing session.
}
if (isEditable)
flags |= this.CONTENTEDITABLE;
}
}
return flags;
},
};
function RemoteSpellChecker(aSpellInfo) {
this._spellInfo = aSpellInfo;
this._suggestionGenerator = null;
}
RemoteSpellChecker.prototype = {
get canSpellCheck() { return this._spellInfo.canSpellCheck; },
get spellCheckPending() { return this._spellInfo.initialSpellCheckPending; },
get overMisspelling() { return this._spellInfo.overMisspelling; },
get enableRealTimeSpell() { return this._spellInfo.enableRealTimeSpell; },
GetSuggestedWord() {
if (!this._suggestionGenerator) {
this._suggestionGenerator = (function*(spellInfo) {
for (let i of spellInfo.spellSuggestions)
yield i;
})(this._spellInfo);
}
let next = this._suggestionGenerator.next();
if (next.done) {
this._suggestionGenerator = null;
return "";
}
return next.value;
},
get currentDictionary() { return this._spellInfo.currentDictionary },
get dictionaryList() { return this._spellInfo.dictionaryList.slice(); },
selectDictionary(index) {
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:selectDictionary",
{ index });
},
replaceMisspelling(index) {
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:replaceMisspelling",
{ index });
},
toggleEnabled() { this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:toggleEnabled", {}); },
addToDictionary() {
// This is really ugly. There is an nsISpellChecker somewhere in the
// parent that corresponds to our current element's spell checker in the
// child, but it's hard to access it. However, we know that
// addToDictionary adds the word to the singleton personal dictionary, so
// we just do that here.
// NB: We also rely on the fact that we only ever pass an empty string in
// as the "lang".
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
.getService(Ci.mozIPersonalDictionary);
dictionary.addWord(this._spellInfo.misspelling, "");
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
},
undoAddToDictionary(word) {
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
.getService(Ci.mozIPersonalDictionary);
dictionary.removeWord(word, "");
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
},
ignoreWord() {
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
.getService(Ci.mozIPersonalDictionary);
dictionary.ignoreWord(this._spellInfo.misspelling);
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
},
uninit() { this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:uninit", {}); }
};

View File

@ -0,0 +1,141 @@
/* vim: set ts=2 sw=2 sts=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
let { SpellCheckHelper } = Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
this.EXPORTED_SYMBOLS = [ "InlineSpellCheckerContent" ]
var InlineSpellCheckerContent = {
_spellChecker: null,
_manager: null,
initContextMenu(event, editFlags, messageManager) {
this._manager = messageManager;
let spellChecker;
if (!(editFlags & (SpellCheckHelper.TEXTAREA | SpellCheckHelper.INPUT))) {
// Get the editor off the window.
let win = event.target.ownerDocument.defaultView;
let editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession);
spellChecker = this._spellChecker =
new InlineSpellChecker(editingSession.getEditorForWindow(win));
} else {
// Use the element's editor.
spellChecker = this._spellChecker =
new InlineSpellChecker(event.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
}
this._spellChecker.initFromEvent(event.rangeParent, event.rangeOffset)
this._addMessageListeners();
if (!spellChecker.canSpellCheck) {
return { canSpellCheck: false,
initialSpellCheckPending: true,
enableRealTimeSpell: false };
}
if (!spellChecker.mInlineSpellChecker.enableRealTimeSpell) {
return { canSpellCheck: true,
initialSpellCheckPending: spellChecker.initialSpellCheckPending,
enableRealTimeSpell: false };
}
let dictionaryList = {};
let realSpellChecker = spellChecker.mInlineSpellChecker.spellChecker;
realSpellChecker.GetDictionaryList(dictionaryList, {});
// The original list we get is in random order. We need our list to be
// sorted by display names.
dictionaryList = spellChecker.sortDictionaryList(dictionaryList.value).map((obj) => {
return obj.id;
});
spellChecker.mDictionaryNames = dictionaryList;
return { canSpellCheck: spellChecker.canSpellCheck,
initialSpellCheckPending: spellChecker.initialSpellCheckPending,
enableRealTimeSpell: spellChecker.enabled,
overMisspelling: spellChecker.overMisspelling,
misspelling: spellChecker.mMisspelling,
spellSuggestions: this._generateSpellSuggestions(),
currentDictionary: spellChecker.mInlineSpellChecker.spellChecker.GetCurrentDictionary(),
dictionaryList: dictionaryList };
},
uninitContextMenu() {
for (let i of this._messages)
this._manager.removeMessageListener(i, this);
this._manager = null;
this._spellChecker = null;
},
_generateSpellSuggestions() {
let spellChecker = this._spellChecker.mInlineSpellChecker.spellChecker;
try {
spellChecker.CheckCurrentWord(this._spellChecker.mMisspelling);
} catch (e) {
return [];
}
let suggestions = new Array(5);
for (let i = 0; i < 5; ++i) {
suggestions[i] = spellChecker.GetSuggestedWord();
if (suggestions[i].length === 0) {
suggestions.length = i;
break;
}
}
this._spellChecker.mSpellSuggestions = suggestions;
return suggestions;
},
_messages: [
"InlineSpellChecker:selectDictionary",
"InlineSpellChecker:replaceMisspelling",
"InlineSpellChecker:toggleEnabled",
"InlineSpellChecker:recheck",
"InlineSpellChecker:uninit"
],
_addMessageListeners() {
for (let i of this._messages)
this._manager.addMessageListener(i, this);
},
receiveMessage(msg) {
switch (msg.name) {
case "InlineSpellChecker:selectDictionary":
this._spellChecker.selectDictionary(msg.data.index);
break;
case "InlineSpellChecker:replaceMisspelling":
this._spellChecker.replaceMisspelling(msg.data.index);
break;
case "InlineSpellChecker:toggleEnabled":
this._spellChecker.toggleEnabled();
break;
case "InlineSpellChecker:recheck":
this._spellChecker.mInlineSpellChecker.enableRealTimeSpell = true;
break;
case "InlineSpellChecker:uninit":
this.uninitContextMenu();
break;
}
}
};

View File

@ -25,6 +25,7 @@ EXTRA_JS_MODULES += [
'Geometry.jsm',
'Http.jsm',
'InlineSpellChecker.jsm',
'InlineSpellCheckerContent.jsm',
'LoadContextInfo.jsm',
'Log.jsm',
'NewTabUtils.jsm',

View File

@ -1,5 +1,5 @@
[DEFAULT]
skip-if = buildapp == 'mulet' || os == "mac" # see bug 993690 and bug 1076941
skip-if = buildapp == 'mulet' || os == "mac" && os_version == "10.6" # see bug 993690
support-files =
head.js
app.sjs

View File

@ -4571,30 +4571,38 @@ LogE10sBlockedReason(const char *reason) {
bool
mozilla::BrowserTabsRemoteAutostart()
{
if (gBrowserTabsRemoteAutostartInitialized) {
return gBrowserTabsRemoteAutostart;
}
gBrowserTabsRemoteAutostartInitialized = true;
bool optInPref = Preferences::GetBool("browser.tabs.remote.autostart", false);
bool trialPref = Preferences::GetBool("browser.tabs.remote.autostart.1", false);
bool testPref = Preferences::GetBool("layers.offmainthreadcomposition.testing.enabled", false);
#if !defined(NIGHTLY_BUILD)
return false;
#endif
if (!gBrowserTabsRemoteAutostartInitialized) {
bool optInPref = Preferences::GetBool("browser.tabs.remote.autostart", false);
bool trialPref = Preferences::GetBool("browser.tabs.remote.autostart.1", false);
bool prefEnabled = optInPref || trialPref;
// When running tests with 'layers.offmainthreadcomposition.testing.enabled' and autostart
// set to true, return enabled. These tests must be allowed to run remotely.
if (testPref && optInPref) {
gBrowserTabsRemoteAutostart = true;
}
#else
bool prefEnabled = optInPref || trialPref;
bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false);
// Only disable for IME for the automatic pref, not the opt-in one.
bool disabledForIME = trialPref && KeyboardMayHaveIME();
bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false);
// Only disable for IME for the automatic pref, not the opt-in one.
bool disabledForIME = trialPref && KeyboardMayHaveIME();
if (prefEnabled) {
if (gSafeMode) {
LogE10sBlockedReason("Firefox is in safe mode.");
} else if (disabledForA11y) {
LogE10sBlockedReason("An accessibility tool is active.");
} else if (disabledForIME) {
LogE10sBlockedReason("The keyboard being used has activated IME.");
} else {
gBrowserTabsRemoteAutostart = true;
}
if (prefEnabled) {
if (gSafeMode) {
LogE10sBlockedReason("Firefox is in safe mode.");
} else if (disabledForA11y) {
LogE10sBlockedReason("An accessibility tool is active.");
} else if (disabledForIME) {
LogE10sBlockedReason("The keyboard being used has activated IME.");
} else {
gBrowserTabsRemoteAutostart = true;
}
}
#endif
#if defined(XP_WIN) || defined(XP_MACOSX)
// If for any reason we suspect acceleration will be disabled, disabled
@ -4651,19 +4659,15 @@ mozilla::BrowserTabsRemoteAutostart()
}
#endif
gBrowserTabsRemoteAutostartInitialized = true;
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_AUTOSTART, gBrowserTabsRemoteAutostart);
if (Preferences::GetBool("browser.enabledE10SFromPrompt", false)) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_STILL_ACCEPTED_FROM_PROMPT,
gBrowserTabsRemoteAutostart);
}
if (prefEnabled) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_BLOCKED_FROM_RUNNING,
!gBrowserTabsRemoteAutostart);
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_AUTOSTART, gBrowserTabsRemoteAutostart);
if (Preferences::GetBool("browser.enabledE10SFromPrompt", false)) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_STILL_ACCEPTED_FROM_PROMPT,
gBrowserTabsRemoteAutostart);
}
if (prefEnabled) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_BLOCKED_FROM_RUNNING,
!gBrowserTabsRemoteAutostart);
}
return gBrowserTabsRemoteAutostart;
}

View File

@ -117,7 +117,13 @@ main(int argc, char **argv)
NSString* myWebRTPath = [myBundle pathForResource:@"webapprt"
ofType:nil];
if (!myWebRTPath) {
@throw MakeException(@"Missing Web Runtime Files", @"Cannot locate binary for this App");
myWebRTPath = [myBundlePath stringByAppendingPathComponent:@"Contents"];
myWebRTPath = [myWebRTPath stringByAppendingPathComponent:@"MacOS"];
myWebRTPath = [myWebRTPath stringByAppendingPathComponent:@"webapprt"];
if ([[NSFileManager defaultManager] fileExistsAtPath:myWebRTPath] == NO) {
@throw MakeException(@"Missing Web Runtime Files",
@"Cannot locate binary for this App");
}
}
//GET FIREFOX BUILD ID