mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
3b1ad0f2bc
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,2 @@
|
||||
0.9.2697
|
||||
25bfcc9
|
||||
0.9.2970
|
||||
22f884f
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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^
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -1,4 +1,5 @@
|
||||
[DEFAULT]
|
||||
skip-if = contentSandbox != 'off' # contentSandbox(Bug 1042735)
|
||||
support-files =
|
||||
head.js
|
||||
constraints.js
|
||||
|
@ -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!
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -11,11 +11,12 @@
|
||||
class mozSpellChecker;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RemoteSpellcheckEngineChild : public mozilla::PRemoteSpellcheckEngineChild
|
||||
{
|
||||
public:
|
||||
explicit RemoteSpellcheckEngineChild(mozSpellChecker *aOwner);
|
||||
~RemoteSpellcheckEngineChild();
|
||||
virtual ~RemoteSpellcheckEngineChild();
|
||||
|
||||
private:
|
||||
mozSpellChecker *mOwner;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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;';
|
||||
|
24
js/src/jit-test/tests/ion/bug1072188.js
Normal file
24
js/src/jit-test/tests/ion/bug1072188.js
Normal 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);
|
||||
}
|
44
js/src/jit-test/tests/ion/bug1074833.js
Normal file
44
js/src/jit-test/tests/ion/bug1074833.js
Normal 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 });
|
||||
|
@ -389,7 +389,7 @@ class SnapshotIteratorForBailout : public SnapshotIterator
|
||||
|
||||
SnapshotIteratorForBailout(const IonBailoutIterator &iter)
|
||||
: SnapshotIterator(iter),
|
||||
results_()
|
||||
results_(iter.jsFrame())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
21
js/src/tests/ecma_6/Object/freeze.js
Normal file
21
js/src/tests/ecma_6/Object/freeze.js
Normal 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);
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
/**
|
||||
|
@ -10,4 +10,6 @@
|
||||
#define cpr_strcasecmp _stricmp
|
||||
#define cpr_strncasecmp _strnicmp
|
||||
|
||||
#define CPR_USE_OS_STRCASECMP
|
||||
|
||||
#endif /* CPR_WIN32_STRINGS_H_ */
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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("""
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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", {}); }
|
||||
};
|
||||
|
141
toolkit/modules/InlineSpellCheckerContent.jsm
Normal file
141
toolkit/modules/InlineSpellCheckerContent.jsm
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
@ -25,6 +25,7 @@ EXTRA_JS_MODULES += [
|
||||
'Geometry.jsm',
|
||||
'Http.jsm',
|
||||
'InlineSpellChecker.jsm',
|
||||
'InlineSpellCheckerContent.jsm',
|
||||
'LoadContextInfo.jsm',
|
||||
'Log.jsm',
|
||||
'NewTabUtils.jsm',
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user