Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-10-14 17:36:36 -07:00
commit 5b358466f8
168 changed files with 3502 additions and 1194 deletions

View File

@ -788,6 +788,9 @@ pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
// Ignore the "dialog=1" feature in window.open.
pref("dom.disable_window_open_dialog_feature", true);
// Enable before keyboard events and after keyboard events.
pref("dom.beforeAfterKeyboardEvent.enabled", true);
// Screen reader support
pref("accessibility.accessfu.activate", 2);
pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
@ -873,6 +876,8 @@ pref("general.useragent.updates.enabled", true);
pref("general.useragent.updates.url", "https://dynamicua.cdn.mozilla.net/0/%APP_ID%");
pref("general.useragent.updates.interval", 604800); // 1 week
pref("general.useragent.updates.retry", 86400); // 1 day
// Device ID can be composed of letter, numbers, hyphen ("-") and dot (".")
pref("general.useragent.device_id", "");
// Make <audio> and <video> talk to the AudioChannelService.
pref("media.useAudioChannelService", true);

View File

@ -21,6 +21,12 @@ XPCOMUtils.defineLazyGetter(this, "discovery", function() {
return devtools.require("devtools/toolkit/discovery/discovery");
});
XPCOMUtils.defineLazyGetter(this, "B2GTabList", function() {
const { B2GTabList } =
devtools.require("resource://gre/modules/DebuggerActors.js");
return B2GTabList;
});
let RemoteDebugger = {
_promptDone: false,
_promptAnswer: false,
@ -91,15 +97,7 @@ let RemoteDebugger = {
{
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
let parameters = {
// We do not expose browser tab actors yet,
// but we still have to define tabList.getList(),
// otherwise, client won't be able to fetch global actors
// from listTabs request!
tabList: {
getList: function() {
return promise.resolve([]);
}
},
tabList: new B2GTabList(connection),
// Use an explicit global actor list to prevent exposing
// unexpected actors
globalActorFactories: restrictPrivileges ? {

View File

@ -29,7 +29,7 @@ XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() {
return devtools.require('devtools/server/actors/memory').MemoryFront;
});
Cu.import('resource://gre/modules/AppFrames.jsm');
Cu.import('resource://gre/modules/Frames.jsm');
/**
* The Developer HUD is an on-device developer tool that displays widgets,
@ -80,9 +80,10 @@ let developerHUD = {
}
}
AppFrames.addObserver(this);
Frames.addObserver(this);
for (let frame of AppFrames.list()) {
let appFrames = Frames.list().filter(frame => frame.getAttribute('mozapp'));
for (let frame of appFrames) {
this.trackFrame(frame);
}
@ -100,7 +101,7 @@ let developerHUD = {
this.untrackFrame(frame);
}
AppFrames.removeObserver(this);
Frames.removeObserver(this);
this._client.close();
delete this._client;
@ -137,11 +138,19 @@ let developerHUD = {
}
},
onAppFrameCreated: function (frame, isFirstAppFrame) {
onFrameCreated: function (frame, isFirstAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
}
this.trackFrame(frame);
},
onAppFrameDestroyed: function (frame, isLastAppFrame) {
onFrameDestroyed: function (frame, isLastAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
}
this.untrackFrame(frame);
},

View File

@ -324,6 +324,8 @@ var shell = {
chromeEventHandler.addEventListener('keydown', this, true);
chromeEventHandler.addEventListener('keypress', this, true);
chromeEventHandler.addEventListener('keyup', this, true);
chromeEventHandler.addEventListener('mozbrowserbeforekeydown', this, true);
chromeEventHandler.addEventListener('mozbrowserbeforekeyup', this, true);
window.addEventListener('MozApplicationManifest', this);
window.addEventListener('mozfullscreenchange', this);
@ -356,6 +358,8 @@ var shell = {
window.removeEventListener('keydown', this, true);
window.removeEventListener('keypress', this, true);
window.removeEventListener('keyup', this, true);
window.removeEventListener('mozbrowserbeforekeydown', this, true);
window.removeEventListener('mozbrowserbeforekeyup', this, true);
window.removeEventListener('MozApplicationManifest', this);
window.removeEventListener('mozfullscreenchange', this);
window.removeEventListener('sizemodechange', this);
@ -420,17 +424,14 @@ var shell = {
return;
}
// If we didn't return, then the key event represents a hardware key
// and we need to prevent it from propagating to Gaia
evt.stopImmediatePropagation();
evt.preventDefault(); // Prevent keypress events (when #501496 is fixed).
// If it is a key down or key up event, we send a chrome event to Gaia.
// If it is a keypress event we just ignore it.
switch (evt.type) {
case 'mozbrowserbeforekeydown':
case 'keydown':
type = type + '-press';
break;
case 'mozbrowserbeforekeyup':
case 'keyup':
type = type + '-release';
break;
@ -473,6 +474,8 @@ var shell = {
case 'keydown':
case 'keyup':
case 'keypress':
case 'mozbrowserbeforekeydown':
case 'mozbrowserbeforekeyup':
this.filterHardwareKeys(evt);
break;
case 'mozfullscreenchange':

View File

@ -0,0 +1,83 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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";
const { Cu } = require("chrome");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils.js");
const promise = require("promise");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const { BrowserTabList } = require("devtools/server/actors/webbrowser");
XPCOMUtils.defineLazyGetter(this, "Frames", function() {
const { Frames } =
Cu.import("resource://gre/modules/Frames.jsm", {});
return Frames;
});
/**
* Unlike the original BrowserTabList which iterates over XUL windows, we
* override many portions to refer to Frames for the info needed here.
*/
function B2GTabList(connection) {
BrowserTabList.call(this, connection);
this._listening = false;
}
B2GTabList.prototype = Object.create(BrowserTabList.prototype);
B2GTabList.prototype._getBrowsers = function() {
return Frames.list().filter(frame => {
// Ignore app frames
return !frame.getAttribute("mozapp");
});
};
B2GTabList.prototype._getSelectedBrowser = function() {
return this._getBrowsers().find(frame => {
// Find the one visible browser (if any)
return !frame.classList.contains("hidden");
});
};
B2GTabList.prototype._checkListening = function() {
// The conditions from BrowserTabList are merged here, since we must listen to
// all events with our observer design.
this._listenForEventsIf(this._onListChanged && this._mustNotify ||
this._actorByBrowser.size > 0);
};
B2GTabList.prototype._listenForEventsIf = function(shouldListen) {
if (this._listening != shouldListen) {
let op = shouldListen ? "addObserver" : "removeObserver";
Frames[op](this);
this._listening = shouldListen;
}
};
B2GTabList.prototype.onFrameCreated = function(frame) {
let mozapp = frame.getAttribute("mozapp");
if (mozapp) {
// Ignore app frames
return;
}
this._notifyListChanged();
this._checkListening();
};
B2GTabList.prototype.onFrameDestroyed = function(frame) {
let mozapp = frame.getAttribute("mozapp");
if (mozapp) {
// Ignore app frames
return;
}
let actor = this._actorByBrowser.get(frame);
if (actor) {
this._handleActorClose(actor, frame);
}
};
exports.B2GTabList = B2GTabList;

View File

@ -4,7 +4,7 @@
'use strict';
this.EXPORTED_SYMBOLS = ['AppFrames'];
this.EXPORTED_SYMBOLS = ['Frames'];
const Cu = Components.utils;
const Ci = Components.interfaces;
@ -27,11 +27,13 @@ const Observer = {
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'message-manager-disconnect', false);
SystemAppProxy.getAppFrames().forEach((frame) => {
SystemAppProxy.getFrames().forEach(frame => {
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
this._frames.set(mm, frame);
let mozapp = frame.getAttribute('mozapp');
this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1);
if (mozapp) {
this._apps.set(mozapp, (this._apps.get(mozapp) || 0) + 1);
}
});
},
@ -68,16 +70,20 @@ const Observer = {
onMessageManagerCreated: function (mm, frame) {
this._frames.set(mm, frame);
let isFirstAppFrame = null;
let mozapp = frame.getAttribute('mozapp');
let count = (this._apps.get(mozapp) || 0) + 1;
this._apps.set(mozapp, count);
if (mozapp) {
let count = (this._apps.get(mozapp) || 0) + 1;
this._apps.set(mozapp, count);
isFirstAppFrame = (count === 1);
}
let isFirstAppFrame = (count === 1);
listeners.forEach(function (listener) {
try {
listener.onAppFrameCreated(frame, isFirstAppFrame);
listener.onFrameCreated(frame, isFirstAppFrame);
} catch(e) {
dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n');
dump('Exception while calling Frames.jsm listener:' + e + '\n' +
e.stack + '\n');
}
});
},
@ -85,31 +91,35 @@ const Observer = {
onMessageManagerDestroyed: function (mm) {
let frame = this._frames.get(mm);
if (!frame) {
// We receive an event for a non mozapp message manager
// We received an event for an unknown message manager
return;
}
this._frames.delete(mm);
let isLastAppFrame = null;
let mozapp = frame.getAttribute('mozapp');
let count = (this._apps.get(mozapp) || 0) - 1;
this._apps.set(mozapp, count);
if (mozapp) {
let count = (this._apps.get(mozapp) || 0) - 1;
this._apps.set(mozapp, count);
isLastAppFrame = (count === 0);
}
let isLastAppFrame = (count === 0);
listeners.forEach(function (listener) {
try {
listener.onAppFrameDestroyed(frame, isLastAppFrame);
listener.onFrameDestroyed(frame, isLastAppFrame);
} catch(e) {
dump('Exception while calling Frames.jsm listener:' + e + '\n' + e.stack + '\n');
dump('Exception while calling Frames.jsm listener:' + e + '\n' +
e.stack + '\n');
}
});
}
};
let AppFrames = this.AppFrames = {
let Frames = this.Frames = {
list: () => SystemAppProxy.getAppFrames(),
list: () => SystemAppProxy.getFrames(),
addObserver: function (listener) {
if (listeners.indexOf(listener) !== -1) {

View File

@ -117,23 +117,16 @@ let SystemAppProxy = {
}
},
getAppFrames: function systemApp_getAppFrames() {
getFrames: function systemApp_getFrames() {
let systemAppFrame = this._frame;
if (!systemAppFrame) {
return [];
}
let list = [systemAppFrame];
// List all app frames hosted in the system app: the homescreen,
// all regular apps, activities, rocket bar, attention screen and the keyboard.
// Bookmark apps and other system app internal frames like captive portal
// are also hosted in system app, but they are not using mozapp attribute.
let frames = systemAppFrame.contentDocument.querySelectorAll("iframe[mozapp]");
let frames = systemAppFrame.contentDocument.querySelectorAll('iframe');
for (let i = 0; i < frames.length; i++) {
list.push(frames[i]);
}
return list;
}
};

View File

@ -48,9 +48,10 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [
'AlertsHelper.jsm',
'AppFrames.jsm',
'ContentRequestHelper.jsm',
'DebuggerActors.js',
'ErrorPage.jsm',
'Frames.jsm',
'FxAccountsMgmtService.jsm',
'LogCapture.jsm',
'LogParser.jsm',

View File

@ -1292,6 +1292,7 @@ pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
// Developer edition preferences
pref("browser.devedition.theme.enabled", false);
pref("browser.devedition.theme.showCustomizeButton", false);
// Disable the error console
pref("devtools.errorconsole.enabled", false);

View File

@ -37,6 +37,7 @@ const kPrefCustomizationState = "browser.uiCustomization.state";
const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
const kPrefDrawInTitlebar = "browser.tabs.drawInTitlebar";
const kPrefDeveditionTheme = "browser.devedition.theme.enabled";
/**
* The keys are the handlers that are fired when the event type (the value)
@ -139,6 +140,7 @@ let gListeners = new Set();
let gUIStateBeforeReset = {
uiCustomizationState: null,
drawInTitlebar: null,
gUIStateBeforeReset: null,
};
let gModuleName = "[CustomizableUI]";
@ -2299,6 +2301,7 @@ let CustomizableUIInternal = {
_resetUIState: function() {
try {
gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
gUIStateBeforeReset.deveditionTheme = Services.prefs.getBoolPref(kPrefDeveditionTheme);
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
} catch(e) { }
@ -2306,6 +2309,7 @@ let CustomizableUIInternal = {
Services.prefs.clearUserPref(kPrefCustomizationState);
Services.prefs.clearUserPref(kPrefDrawInTitlebar);
Services.prefs.clearUserPref(kPrefDeveditionTheme);
LOG("State reset");
// Reset placements to make restoring default placements possible.
@ -2367,13 +2371,15 @@ let CustomizableUIInternal = {
*/
undoReset: function() {
if (gUIStateBeforeReset.uiCustomizationState == null ||
gUIStateBeforeReset.drawInTitlebar == null) {
gUIStateBeforeReset.drawInTitlebar == null ||
gUIStateBeforeReset.deveditionTheme == null) {
return;
}
gUndoResetting = true;
let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
let deveditionTheme = gUIStateBeforeReset.deveditionTheme;
// Need to clear the previous state before setting the prefs
// because pref observers may check if there is a previous UI state.
@ -2381,6 +2387,7 @@ let CustomizableUIInternal = {
Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
Services.prefs.setBoolPref(kPrefDeveditionTheme, deveditionTheme);
this.loadSavedState();
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
// and we don't need to do anything else here:
@ -2558,6 +2565,10 @@ let CustomizableUIInternal = {
LOG(kPrefDrawInTitlebar + " pref is non-default");
return false;
}
if (Services.prefs.prefHasUserValue(kPrefDeveditionTheme)) {
LOG(kPrefDeveditionTheme + " pref is non-default");
return false;
}
return true;
},
@ -3258,7 +3269,8 @@ this.CustomizableUI = {
*/
get canUndoReset() {
return gUIStateBeforeReset.uiCustomizationState != null ||
gUIStateBeforeReset.drawInTitlebar != null;
gUIStateBeforeReset.drawInTitlebar != null ||
gUIStateBeforeReset.deveditionTheme != null;
},
/**

View File

@ -17,6 +17,8 @@ const kPlaceholderClass = "panel-customization-placeholder";
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
const kDeveditionThemePref = "browser.devedition.theme.enabled";
const kDeveditionButtonPref = "browser.devedition.theme.showCustomizeButton";
const kMaxTransitionDurationMs = 2000;
const kPanelItemContextMenu = "customizationPanelItemContextMenu";
@ -69,8 +71,11 @@ function CustomizeMode(aWindow) {
#ifdef CAN_DRAW_IN_TITLEBAR
this._updateTitlebarButton();
Services.prefs.addObserver(kDrawInTitlebarPref, this, false);
this.window.addEventListener("unload", this);
#endif
this._updateDevEditionThemeButton();
Services.prefs.addObserver(kDeveditionButtonPref, this, false);
Services.prefs.addObserver(kDeveditionThemePref, this, false);
this.window.addEventListener("unload", this);
};
CustomizeMode.prototype = {
@ -105,6 +110,8 @@ CustomizeMode.prototype = {
#ifdef CAN_DRAW_IN_TITLEBAR
Services.prefs.removeObserver(kDrawInTitlebarPref, this);
#endif
Services.prefs.removeObserver(kDeveditionButtonPref, this);
Services.prefs.removeObserver(kDeveditionThemePref, this);
},
toggle: function() {
@ -1447,11 +1454,9 @@ CustomizeMode.prototype = {
this.exit();
}
break;
#ifdef CAN_DRAW_IN_TITLEBAR
case "unload":
this.uninit();
break;
#endif
}
},
@ -1463,6 +1468,7 @@ CustomizeMode.prototype = {
#ifdef CAN_DRAW_IN_TITLEBAR
this._updateTitlebarButton();
#endif
this._updateDevEditionThemeButton();
break;
case "lightweight-theme-window-updated":
if (aSubject == this.window) {
@ -1498,6 +1504,29 @@ CustomizeMode.prototype = {
},
#endif
_updateDevEditionThemeButton: function() {
let button = this.document.getElementById("customization-devedition-theme-button");
let themeEnabled = Services.prefs.getBoolPref(kDeveditionThemePref);
if (themeEnabled) {
button.setAttribute("checked", "true");
} else {
button.removeAttribute("checked");
}
let buttonVisible = Services.prefs.getBoolPref(kDeveditionButtonPref);
if (buttonVisible) {
button.removeAttribute("hidden");
} else {
button.setAttribute("hidden", "true");
}
},
toggleDevEditionTheme: function() {
let button = this.document.getElementById("customization-devedition-theme-button");
let preferenceValue = button.hasAttribute("checked");
Services.prefs.setBoolPref(kDeveditionThemePref, preferenceValue);
},
_onDragStart: function(aEvent) {
__dumpDragData(aEvent);
let item = aEvent.target;

View File

@ -52,6 +52,14 @@
</hbox>
</panel>
</button>
<button id="customization-devedition-theme-button"
class="customizationmode-button"
hidden="true"
label="&customizeMode.deveditionTheme.label;"
oncommand="gCustomizeMode.toggleDevEditionTheme()"
type="checkbox" />
<spacer id="customization-footer-spacer"/>
<button id="customization-undo-reset-button"
class="customizationmode-button"

View File

@ -101,6 +101,44 @@ add_task(function() {
is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
});
// Bug 1082108 - Restore Defaults should clear user pref for devedition theme
add_task(function() {
let prefName = "browser.devedition.theme.enabled";
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", true);
let defaultValue = Services.prefs.getBoolPref(prefName);
let restoreDefaultsButton = document.getElementById("customization-reset-button");
let deveditionThemeButton = document.getElementById("customization-devedition-theme-button");
let undoResetButton = document.getElementById("customization-undo-reset-button");
ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "Devedition theme button should reflect pref value");
is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
Services.prefs.setBoolPref(prefName, !defaultValue);
ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "Devedition theme button should reflect changed pref value");
ok(!CustomizableUI.inDefaultState, "With devedition theme flipped, no longer default");
is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
yield gCustomizeMode.reset();
ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
is(deveditionThemeButton.hasAttribute("checked"), defaultValue, "devedition theme button should reflect default value after reset");
is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset devedition.theme.enabled");
ok(CustomizableUI.inDefaultState, "In default state after devedition theme reset");
is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
yield gCustomizeMode.undoReset();
ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
is(deveditionThemeButton.hasAttribute("checked"), !defaultValue, "devedition theme button should reflect undo-reset value");
ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
Services.prefs.clearUserPref(prefName);
ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
});
add_task(function asyncCleanup() {
yield gCustomizeMode.reset();
yield endCustomizing();

View File

@ -40,7 +40,17 @@ this.EXPORTED_SYMBOLS = ["injectLoopAPI"];
const cloneErrorObject = function(error, targetWindow) {
let obj = new targetWindow.Error();
for (let prop of Object.getOwnPropertyNames(error)) {
obj[prop] = String(error[prop]);
let value = error[prop];
if (typeof value != "string" && typeof value != "number") {
value = String(value);
}
Object.defineProperty(Cu.waiveXrays(obj), prop, {
configurable: false,
enumerable: true,
value: value,
writable: false
});
}
return obj;
};

View File

@ -44,6 +44,43 @@ XPCOMUtils.defineLazyModuleGetter(this, "Weave",
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
// This function isn't public both because it's synchronous and because it is
// going to be removed in bug 1072833.
function IsLivemark(aItemId) {
// Since this check may be done on each dragover event, it's worth maintaining
// a cache.
let self = IsLivemark;
if (!("ids" in self)) {
const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI;
let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO);
self.ids = new Set(idsVec);
let obs = Object.freeze({
QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
onItemAnnotationSet(itemId, annoName) {
if (annoName == LIVEMARK_ANNO)
self.ids.add(itemId);
},
onItemAnnotationRemoved(itemId, annoName) {
// If annoName is set to an empty string, the item is gone.
if (annoName == LIVEMARK_ANNO || annoName == "")
self.ids.delete(itemId);
},
onPageAnnotationSet() { },
onPageAnnotationRemoved() { },
});
PlacesUtils.annotations.addObserver(obs);
PlacesUtils.registerShutdownFunction(() => {
PlacesUtils.annotations.removeObserver(obs);
});
}
return self.ids.has(aItemId);
}
this.PlacesUIUtils = {
ORGANIZER_LEFTPANE_VERSION: 7,
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
@ -560,6 +597,89 @@ this.PlacesUIUtils = {
return "";
},
/**
* Check whether or not the given node represents a removable entry (either in
* history or in bookmarks).
*
* @param aNode
* a node, except the root node of a query.
* @return true if the aNode represents a removable entry, false otherwise.
*/
canUserRemove: function (aNode) {
let parentNode = aNode.parent;
if (!parentNode)
throw new Error("canUserRemove doesn't accept root nodes");
// If it's not a bookmark, we can remove it unless it's a child of a
// livemark.
if (aNode.itemId == -1) {
// Rather than executing a db query, checking the existence of the feedURI
// annotation, detect livemark children by the fact that they are the only
// direct non-bookmark children of bookmark folders.
return !PlacesUtils.nodeIsFolder(parentNode);
}
// Otherwise it has to be a child of an editable folder.
return !this.isContentsReadOnly(parentNode);
},
/**
* DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
* TO GUIDS IS COMPLETE (BUG 1071511).
*
* Check whether or not the given node or item-id points to a folder which
* should not be modified by the user (i.e. its children should be unremovable
* and unmovable, new children should be disallowed, etc).
* These semantics are not inherited, meaning that read-only folder may
* contain editable items (for instance, the places root is read-only, but all
* of its direct children aren't).
*
* You should only pass folder item ids or folder nodes for aNodeOrItemId.
* While this is only enforced for the node case (if an item id of a separator
* or a bookmark is passed, false is returned), it's considered the caller's
* job to ensure that it checks a folder.
* Also note that folder-shortcuts should only be passed as result nodes.
* Otherwise they are just treated as bookmarks (i.e. false is returned).
*
* @param aNodeOrItemId
* any item id or result node.
* @throws if aNodeOrItemId is neither an item id nor a folder result node.
* @note livemark "folders" are considered read-only (but see bug 1072833).
* @return true if aItemId points to a read-only folder, false otherwise.
*/
isContentsReadOnly: function (aNodeOrItemId) {
let itemId;
if (typeof(aNodeOrItemId) == "number") {
itemId = aNodeOrItemId;
}
else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) {
itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId);
}
else {
throw new Error("invalid value for aNodeOrItemId");
}
if (itemId == PlacesUtils.placesRootId || IsLivemark(itemId))
return true;
// leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter
// performing at least a synchronous DB query (and on its very first call
// in a fresh profile, it also creates the entire structure).
// Therefore we don't want to this function, which is called very often by
// isCommandEnabled, to ever be the one that invokes it first, especially
// because isCommandEnabled may be called way before the left pane folder is
// even created (for example, if the user only uses the bookmarks menu or
// toolbar for managing bookmarks). To do so, we avoid comparing to those
// special folder if the lazy getter is still in place. This is safe merely
// because the only way to access the left pane contents goes through
// "resolving" the leftPaneFolderId getter.
if ("get" in Object.getOwnPropertyDescriptor(this, "leftPaneFolderId"))
return false;
return itemId == this.leftPaneFolderId ||
itemId == this.allBookmarksFolderId;
},
/**
* Gives the user a chance to cancel loading lots of tabs at once
*/
@ -962,8 +1082,6 @@ this.PlacesUIUtils = {
// We should never backup this, since it changes between profiles.
as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
0, as.EXPIRE_NEVER);
// Disallow manipulating this folder within the organizer UI.
bs.setFolderReadonly(folderId, true);
if (aIsRoot) {
// Mark as special left pane root.

View File

@ -1376,8 +1376,8 @@ PlacesToolbar.prototype = {
elt.localName != "menupopup") {
let eltRect = elt.getBoundingClientRect();
let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if (PlacesUIUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
// This is a folder.
// If we are in the middle of it, drop inside it.
// Otherwise, drop before it, with regards to RTL mode.

View File

@ -200,7 +200,7 @@ PlacesController.prototype = {
var selectedNode = this._view.selectedNode;
return selectedNode &&
PlacesUtils.nodeIsFolder(selectedNode) &&
!PlacesUtils.nodeIsReadOnly(selectedNode) &&
!PlacesUIUtils.isContentsReadOnly(selectedNode) &&
this._view.result.sortingMode ==
Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
case "placesCmd_createBookmark":
@ -330,21 +330,7 @@ PlacesController.prototype = {
if (nodes[i] == root)
return false;
if (PlacesUtils.nodeIsFolder(nodes[i]) &&
!PlacesControllerDragHelper.canMoveNode(nodes[i]))
return false;
// We don't call nodeIsReadOnly here, because nodeIsReadOnly means that
// a node has children that cannot be edited, reordered or removed. Here,
// we don't care if a node's children can't be reordered or edited, just
// that they're removable. All history results have removable children
// (based on the principle that any URL in the history table should be
// removable), but some special bookmark folders may have non-removable
// children, e.g. live bookmark folder children. It doesn't make sense
// to delete a child of a live bookmark folder, since when the folder
// refreshes, the child will return.
var parent = nodes[i].parent || root;
if (PlacesUtils.isReadonlyFolder(parent))
if (!PlacesUIUtils.canUserRemove(nodes[i]))
return false;
}
}
@ -1560,10 +1546,9 @@ let PlacesControllerDragHelper = {
canMoveUnwrappedNode: function (aUnwrappedNode) {
return aUnwrappedNode.id > 0 &&
!PlacesUtils.isRootItem(aUnwrappedNode.id) &&
aUnwrappedNode.parent != PlacesUtils.placesRootId &&
!PlacesUIUtils.isContentsReadOnly(aUnwrappedNode.parent) ||
aUnwrappedNode.parent != PlacesUtils.tagsFolderId &&
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId &&
!aUnwrappedNode.parentReadOnly;
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId;
},
/**
@ -1575,58 +1560,17 @@ let PlacesControllerDragHelper = {
*/
canMoveNode:
function PCDH_canMoveNode(aNode) {
// Can't move query root.
if (!aNode.parent)
// Only bookmark items are movable.
if (aNode.itemId == -1)
return false;
let parentId = PlacesUtils.getConcreteItemId(aNode.parent);
let concreteId = PlacesUtils.getConcreteItemId(aNode);
// Can't move children of tag containers.
if (PlacesUtils.nodeIsTagQuery(aNode.parent))
return false;
// Can't move children of read-only containers.
if (PlacesUtils.nodeIsReadOnly(aNode.parent))
return false;
// Check for special folders, etc.
if (PlacesUtils.nodeIsContainer(aNode) &&
!this.canMoveContainer(aNode.itemId, parentId))
return false;
return true;
},
/**
* Determines if a container node can be moved.
*
* @param aId
* A bookmark folder id.
* @param [optional] aParentId
* The parent id of the folder.
* @return True if the container can be moved to the target.
*/
canMoveContainer:
function PCDH_canMoveContainer(aId, aParentId) {
if (aId == -1)
return false;
// Disallow moving of roots and special folders.
const ROOTS = [PlacesUtils.placesRootId, PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.tagsFolderId, PlacesUtils.unfiledBookmarksFolderId,
PlacesUtils.toolbarFolderId];
if (ROOTS.indexOf(aId) != -1)
return false;
// Get parent id if necessary.
if (aParentId == null || aParentId == -1)
aParentId = PlacesUtils.bookmarks.getFolderIdForItem(aId);
if (PlacesUtils.bookmarks.getFolderReadonly(aParentId))
return false;
return true;
// Once tags and bookmarked are divorced, the tag-query check should be
// removed.
let parentNode = aNode.parent;
return parentNode != null &&
!(PlacesUtils.nodeIsFolder(parentNode) &&
PlacesUIUtils.isContentsReadOnly(parentNode)) &&
!PlacesUtils.nodeIsTagQuery(parentNode);
},
/**
@ -1726,12 +1670,10 @@ let PlacesControllerDragHelper = {
*/
disallowInsertion: function(aContainer) {
NS_ASSERT(aContainer, "empty container");
// Allow dropping into Tag containers.
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders.
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
// Allow dropping into Tag containers and editable folders.
return !PlacesUtils.nodeIsTagQuery(aContainer) &&
(!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUIUtils.isContentsReadOnly(aContainer));
}
};

View File

@ -106,9 +106,9 @@
let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
elt._placesNode.title : null;
if ((PlacesUtils.nodeIsFolder(elt._placesNode) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode)) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
// This is a folder or a tag container.
if (eventY - eltY < eltHeight * 0.20) {
// If mouse is in the top part of the element, drop above folder.

View File

@ -1649,23 +1649,39 @@ PlacesTreeView.prototype = {
if (aColumn.index != 0)
return false;
// Only bookmark-nodes are editable, and those are never built lazily
let node = this._rows[aRow];
if (!node || node.itemId == -1)
if (!node) {
Cu.reportError("isEditable called for an unbuilt row.");
return false;
}
let itemId = node.itemId;
// Only bookmark-nodes are editable. Fortunately, this checks also takes
// care of livemark children.
if (itemId == -1)
return false;
// The following items are never editable:
// * Read-only items.
// The following items are also not editable, even though they are bookmark
// items.
// * places-roots
// * the left pane special folders and queries (those are place: uri
// bookmarks)
// * separators
if (PlacesUtils.nodeIsReadOnly(node) ||
PlacesUtils.nodeIsSeparator(node))
//
// Note that concrete itemIds aren't used intentionally. For example, we
// have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
// except for the one under All Bookmarks.
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemId))
return false;
if (PlacesUtils.nodeIsFolder(node)) {
let itemId = PlacesUtils.getConcreteItemId(node);
if (PlacesUtils.isRootItem(itemId))
return false;
let parentId = PlacesUtils.getConcreteItemId(node.parent);
if (parentId == PlacesUIUtils.leftPaneFolderId ||
parentId == PlacesUIUtils.allBookmarksFolderId) {
// Note that the for the time being this is the check that actually
// blocks renaming places "roots", and not the isRootItem check above.
// That's because places root are only exposed through folder shortcuts
// descendants of the left pane folder.
return false;
}
return true;

View File

@ -27,8 +27,6 @@ function test() {
validate: function() {
is(rootNode.childCount, 1,
"populate added data to the test root");
is(PlacesControllerDragHelper.canMoveContainer(this.id),
true, "can move regular folder id");
is(PlacesControllerDragHelper.canMoveNode(rootNode.getChild(0)),
true, "can move regular folder node");
}
@ -57,9 +55,6 @@ function test() {
var concreteId = PlacesUtils.getConcreteItemId(shortcutNode);
is(concreteId, folderNode.itemId, "shortcut node id and concrete id match");
is(PlacesControllerDragHelper.canMoveContainer(this.shortcutId),
true, "can move folder shortcut id");
is(PlacesControllerDragHelper.canMoveNode(shortcutNode),
true, "can move folder shortcut node");
}
@ -83,9 +78,6 @@ function test() {
var queryNode = rootNode.getChild(1);
is(queryNode.itemId, this.queryId, "query id and query node item id match");
is(PlacesControllerDragHelper.canMoveContainer(this.queryId),
true, "can move query id");
is(PlacesControllerDragHelper.canMoveNode(queryNode),
true, "can move query node");
}
@ -127,9 +119,6 @@ function test() {
for (var i = 0; i < this.folders.length; i++) {
var id = this.folders[i];
is(PlacesControllerDragHelper.canMoveContainer(id),
false, "shouldn't be able to move special folder id");
var node = getRootChildNode(id);
isnot(node, null, "Node found");
is(PlacesControllerDragHelper.canMoveNode(node),
@ -140,10 +129,6 @@ function test() {
is(shortcutNode.itemId, shortcutId, "shortcut id and shortcut node item id match");
dump("can move shortcut id?\n");
is(PlacesControllerDragHelper.canMoveContainer(shortcutId),
true, "should be able to move special folder shortcut id");
dump("can move shortcut node?\n");
is(PlacesControllerDragHelper.canMoveNode(shortcutNode),
true, "should be able to move special folder shortcut node");
@ -169,46 +154,13 @@ function test() {
is(tagsNode.childCount, 1, "has new tag");
var tagNode = tagsNode.getChild(0);
is(PlacesControllerDragHelper.canMoveNode(tagNode),
false, "should not be able to move tag container node");
tagsNode.containerOpen = false;
}
});
// test that any child of a read-only node cannot be moved
tests.push({
populate: function() {
this.id =
PlacesUtils.bookmarks.createFolder(rootId, "foo", IDX);
PlacesUtils.bookmarks.createFolder(this.id, "bar", IDX);
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
},
validate: function() {
is(rootNode.childCount, 1,
"populate added data to the test root");
var readOnlyFolder = rootNode.getChild(0);
// test that we can move the read-only folder
is(PlacesControllerDragHelper.canMoveContainer(this.id),
true, "can move read-only folder id");
is(PlacesControllerDragHelper.canMoveNode(readOnlyFolder),
true, "can move read-only folder node");
// test that we cannot move the child of a read-only folder
readOnlyFolder.QueryInterface(Ci.nsINavHistoryContainerResultNode);
readOnlyFolder.containerOpen = true;
var childFolder = readOnlyFolder.getChild(0);
is(PlacesControllerDragHelper.canMoveContainer(childFolder.itemId),
false, "cannot move a child of a read-only folder");
is(PlacesControllerDragHelper.canMoveNode(childFolder),
false, "cannot move a child node of a read-only folder node");
readOnlyFolder.containerOpen = false;
}
});
tests.forEach(function(aTest) {
PlacesUtils.bookmarks.removeFolderChildren(rootId);
aTest.populate();

View File

@ -130,6 +130,15 @@
-moz-appearance: none;
}
#customization-titlebar-visibility-button[checked],
#customization-devedition-theme-button[checked] {
background-color: rgb(218, 218, 218);
border-color: rgb(168, 168, 168);
text-shadow: 0 1px rgb(236, 236, 236);
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
inset 0 1px rgb(196, 196, 196);
}
.customizationmode-button[disabled="true"] {
opacity: .5;
}
@ -156,11 +165,6 @@
#customization-titlebar-visibility-button[checked] {
-moz-image-region: rect(0, 48px, 24px, 24px);
background-color: rgb(218, 218, 218);
border-color: rgb(168, 168, 168);
text-shadow: 0 1px rgb(236, 236, 236);
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
inset 0 1px rgb(196, 196, 196);
}
#main-window[customize-entered] #customization-panel-container {

View File

@ -2361,8 +2361,8 @@ public:
virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
virtual mozilla::dom::SVGDocument* AsSVGDocument() { return nullptr; }
// Each import tree has exactly one master document which is
// the root of the tree, and owns the browser context.
// The root document of the import tree. If this document is not an import
// this will return the document itself.
virtual nsIDocument* MasterDocument() = 0;
virtual void SetMasterDocument(nsIDocument* master) = 0;
virtual bool IsMasterDocument() = 0;

View File

@ -83,13 +83,9 @@ public:
, mDisconnected(false)
, mCloseEventWasClean(false)
, mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
, mOutgoingBufferedAmount(0)
, mBinaryType(dom::BinaryType::Blob)
, mScriptLine(0)
, mInnerWindowID(0)
, mMutex("WebSocketImpl::mMutex")
, mWorkerPrivate(nullptr)
, mReadyState(WebSocket::CONNECTING)
{
if (!NS_IsMainThread()) {
mWorkerPrivate = GetCurrentThreadWorkerPrivate();
@ -104,51 +100,6 @@ public:
bool IsTargetThread() const;
uint16_t ReadyState()
{
MutexAutoLock lock(mMutex);
return mReadyState;
}
void SetReadyState(uint16_t aReadyState)
{
MutexAutoLock lock(mMutex);
mReadyState = aReadyState;
}
uint32_t BufferedAmount() const
{
AssertIsOnTargetThread();
return mOutgoingBufferedAmount;
}
dom::BinaryType BinaryType() const
{
AssertIsOnTargetThread();
return mBinaryType;
}
void SetBinaryType(dom::BinaryType aData)
{
AssertIsOnTargetThread();
mBinaryType = aData;
}
void GetUrl(nsAString& aURL) const
{
AssertIsOnTargetThread();
if (mEffectiveURL.IsEmpty()) {
aURL = mOriginalURL;
} else {
aURL = mEffectiveURL;
}
}
void Close(const Optional<uint16_t>& aCode,
const Optional<nsAString>& aReason,
ErrorResult& aRv);
void Init(JSContext* aCx,
nsIPrincipal* aPrincipal,
const nsAString& aURL,
@ -160,12 +111,6 @@ public:
void AsyncOpen(ErrorResult& aRv);
void Send(nsIInputStream* aMsgStream,
const nsACString& aMsgString,
uint32_t aMsgLength,
bool aIsBinary,
ErrorResult& aRv);
nsresult ParseURL(const nsAString& aURL);
nsresult InitializeConnection();
@ -212,9 +157,6 @@ public:
nsCOMPtr<nsIWebSocketChannel> mChannel;
// related to the WebSocket constructor steps
nsString mOriginalURL;
nsString mEffectiveURL; // after redirects
bool mSecure; // if true it is using SSL and the wss scheme,
// otherwise it is using the ws scheme with no SSL
@ -234,16 +176,10 @@ public:
nsCString mURI;
nsCString mRequestedProtocolList;
nsCString mEstablishedProtocol;
nsCString mEstablishedExtensions;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsWeakPtr mOriginDocument;
uint32_t mOutgoingBufferedAmount;
dom::BinaryType mBinaryType;
// Web Socket owner information:
// - the script file name, UTF8 encoded.
// - source code line number where the Web Socket object was constructed.
@ -254,10 +190,6 @@ public:
uint32_t mScriptLine;
uint64_t mInnerWindowID;
// This mutex protects mReadyState that is the only variable that is used in
// different threads.
mozilla::Mutex mMutex;
WorkerPrivate* mWorkerPrivate;
nsAutoPtr<WorkerFeature> mWorkerFeature;
@ -269,9 +201,6 @@ private:
Disconnect();
}
}
// This value should not be used directly but use ReadyState() instead.
uint16_t mReadyState;
};
NS_IMPL_ISUPPORTS(WebSocketImpl,
@ -450,7 +379,7 @@ WebSocketImpl::CloseConnection(uint16_t aReasonCode,
{
AssertIsOnTargetThread();
uint16_t readyState = ReadyState();
uint16_t readyState = mWebSocket->ReadyState();
if (readyState == WebSocket::CLOSING ||
readyState == WebSocket::CLOSED) {
return NS_OK;
@ -458,7 +387,7 @@ WebSocketImpl::CloseConnection(uint16_t aReasonCode,
// The common case...
if (mChannel) {
SetReadyState(WebSocket::CLOSING);
mWebSocket->SetReadyState(WebSocket::CLOSING);
// The channel has to be closed on the main-thread.
@ -481,7 +410,7 @@ WebSocketImpl::CloseConnection(uint16_t aReasonCode,
mCloseEventCode = aReasonCode;
CopyUTF8toUTF16(aReasonString, mCloseEventReason);
SetReadyState(WebSocket::CLOSING);
mWebSocket->SetReadyState(WebSocket::CLOSING);
// Can be called from Cancel() or Init() codepaths, so need to dispatch
// onerror/onclose asynchronously
@ -503,7 +432,7 @@ WebSocketImpl::ConsoleError()
NS_ConvertUTF8toUTF16 specUTF16(mURI);
const char16_t* formatStrings[] = { specUTF16.get() };
if (ReadyState() < WebSocket::OPEN) {
if (mWebSocket->ReadyState() < WebSocket::OPEN) {
PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
MOZ_UTF16("connectionFailure"),
formatStrings, ArrayLength(formatStrings));
@ -621,7 +550,7 @@ WebSocketImpl::DoOnMessageAvailable(const nsACString& aMsg, bool isBinary)
{
AssertIsOnTargetThread();
int16_t readyState = ReadyState();
int16_t readyState = mWebSocket->ReadyState();
if (readyState == WebSocket::CLOSED) {
NS_ERROR("Received message after CLOSED");
return NS_ERROR_UNEXPECTED;
@ -664,7 +593,7 @@ WebSocketImpl::OnStart(nsISupports* aContext)
{
AssertIsOnTargetThread();
int16_t readyState = ReadyState();
int16_t readyState = mWebSocket->ReadyState();
// This is the only function that sets OPEN, and should be called only once
MOZ_ASSERT(readyState != WebSocket::OPEN,
@ -683,13 +612,13 @@ WebSocketImpl::OnStart(nsISupports* aContext)
}
if (!mRequestedProtocolList.IsEmpty()) {
mChannel->GetProtocol(mEstablishedProtocol);
mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
}
mChannel->GetExtensions(mEstablishedExtensions);
mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
UpdateURI();
SetReadyState(WebSocket::OPEN);
mWebSocket->SetReadyState(WebSocket::OPEN);
// Call 'onopen'
rv = mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
@ -710,7 +639,7 @@ WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode)
// We can be CONNECTING here if connection failed.
// We can be OPEN if we have encountered a fatal protocol error
// We can be CLOSING if close() was called and/or server initiated close.
MOZ_ASSERT(ReadyState() != WebSocket::CLOSED,
MOZ_ASSERT(mWebSocket->ReadyState() != WebSocket::CLOSED,
"Shouldn't already be CLOSED when OnStop called");
// called by network stack, not JS, so can dispatch JS events synchronously
@ -755,11 +684,11 @@ WebSocketImpl::OnAcknowledge(nsISupports *aContext, uint32_t aSize)
{
AssertIsOnTargetThread();
if (aSize > mOutgoingBufferedAmount) {
if (aSize > mWebSocket->mOutgoingBufferedAmount) {
return NS_ERROR_UNEXPECTED;
}
mOutgoingBufferedAmount -= aSize;
mWebSocket->mOutgoingBufferedAmount -= aSize;
return NS_OK;
}
@ -769,7 +698,7 @@ WebSocketImpl::OnServerClose(nsISupports *aContext, uint16_t aCode,
{
AssertIsOnTargetThread();
int16_t readyState = ReadyState();
int16_t readyState = mWebSocket->ReadyState();
MOZ_ASSERT(readyState != WebSocket::CONNECTING,
"Received server close before connected?");
@ -807,7 +736,7 @@ WebSocketImpl::GetInterface(const nsIID& aIID, void** aResult)
{
AssertIsOnMainThread();
if (ReadyState() == WebSocket::CLOSED) {
if (!mWebSocket || mWebSocket->ReadyState() == WebSocket::CLOSED) {
return NS_ERROR_FAILURE;
}
@ -841,6 +770,10 @@ WebSocket::WebSocket(nsPIDOMWindow* aOwnerWindow)
, mWorkerPrivate(nullptr)
, mKeepingAlive(false)
, mCheckMustKeepAlive(true)
, mOutgoingBufferedAmount(0)
, mBinaryType(dom::BinaryType::Blob)
, mMutex("WebSocketImpl::mMutex")
, mReadyState(CONNECTING)
{
mImpl = new WebSocketImpl(this);
mWorkerPrivate = mImpl->mWorkerPrivate;
@ -1490,7 +1423,7 @@ void
WebSocketImpl::DispatchConnectionCloseEvents()
{
AssertIsOnTargetThread();
SetReadyState(WebSocket::CLOSED);
mWebSocket->SetReadyState(WebSocket::CLOSED);
// Call 'onerror' if needed
if (mFailed) {
@ -1516,7 +1449,7 @@ nsresult
WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv)) {
@ -1541,7 +1474,7 @@ WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
bool aIsBinary)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
AutoJSAPI jsapi;
@ -1565,7 +1498,7 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx,
bool aIsBinary)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv)) {
@ -1575,11 +1508,11 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx,
// Create appropriate JS object for message
JS::Rooted<JS::Value> jsData(aCx);
if (aIsBinary) {
if (mImpl->mBinaryType == dom::BinaryType::Blob) {
if (mBinaryType == dom::BinaryType::Blob) {
nsresult rv = nsContentUtils::CreateBlobBuffer(aCx, GetOwner(), aData,
&jsData);
NS_ENSURE_SUCCESS(rv, rv);
} else if (mImpl->mBinaryType == dom::BinaryType::Arraybuffer) {
} else if (mBinaryType == dom::BinaryType::Arraybuffer) {
JS::Rooted<JSObject*> arrayBuf(aCx);
nsresult rv = nsContentUtils::CreateArrayBuffer(aCx, aData,
arrayBuf.address());
@ -1625,7 +1558,7 @@ WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
const nsAString &aReason)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv)) {
@ -1767,7 +1700,7 @@ WebSocketImpl::ParseURL(const nsAString& aURL)
}
}
mOriginalURL = aURL;
mWebSocket->mOriginalURL = aURL;
rv = parsedURL->GetSpec(mURI);
MOZ_ASSERT(NS_SUCCEEDED(rv));
@ -1795,9 +1728,9 @@ WebSocket::UpdateMustKeepAlive()
bool shouldKeepAlive = false;
if (mListenerManager) {
switch (mImpl->ReadyState())
switch (ReadyState())
{
case WebSocket::CONNECTING:
case CONNECTING:
{
if (mListenerManager->HasListenersFor(nsGkAtoms::onopen) ||
mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
@ -1808,19 +1741,19 @@ WebSocket::UpdateMustKeepAlive()
}
break;
case WebSocket::OPEN:
case WebSocket::CLOSING:
case OPEN:
case CLOSING:
{
if (mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
mListenerManager->HasListenersFor(nsGkAtoms::onclose) ||
mImpl->mOutgoingBufferedAmount != 0) {
mOutgoingBufferedAmount != 0) {
shouldKeepAlive = true;
}
}
break;
case WebSocket::CLOSED:
case CLOSED:
{
shouldKeepAlive = false;
}
@ -1940,7 +1873,7 @@ WebSocketImpl::UpdateURI()
channel = static_cast<BaseWebSocketChannel*>(mChannel.get());
MOZ_ASSERT(channel);
channel->GetEffectiveURL(mEffectiveURL);
channel->GetEffectiveURL(mWebSocket->mEffectiveURL);
mSecure = channel->IsEncrypted();
return NS_OK;
@ -1966,62 +1899,70 @@ WebSocket::EventListenerRemoved(nsIAtom* aType)
// webIDL: readonly attribute unsigned short readyState;
uint16_t
WebSocket::ReadyState() const
WebSocket::ReadyState()
{
MOZ_ASSERT(mImpl);
return mImpl->ReadyState();
MutexAutoLock lock(mMutex);
return mReadyState;
}
void
WebSocket::SetReadyState(uint16_t aReadyState)
{
MutexAutoLock lock(mMutex);
mReadyState = aReadyState;
}
// webIDL: readonly attribute unsigned long bufferedAmount;
uint32_t
WebSocket::BufferedAmount() const
{
MOZ_ASSERT(mImpl);
return mImpl->BufferedAmount();
AssertIsOnTargetThread();
return mOutgoingBufferedAmount;
}
// webIDL: attribute BinaryType binaryType;
dom::BinaryType
WebSocket::BinaryType() const
{
MOZ_ASSERT(mImpl);
return mImpl->BinaryType();
AssertIsOnTargetThread();
return mBinaryType;
}
// webIDL: attribute BinaryType binaryType;
void
WebSocket::SetBinaryType(dom::BinaryType aData)
{
MOZ_ASSERT(mImpl);
mImpl->SetBinaryType(aData);
AssertIsOnTargetThread();
mBinaryType = aData;
}
// webIDL: readonly attribute DOMString url
void
WebSocket::GetUrl(nsAString& aURL)
{
MOZ_ASSERT(mImpl);
mImpl->GetUrl(aURL);
AssertIsOnTargetThread();
if (mEffectiveURL.IsEmpty()) {
aURL = mOriginalURL;
} else {
aURL = mEffectiveURL;
}
}
// webIDL: readonly attribute DOMString extensions;
void
WebSocket::GetExtensions(nsAString& aExtensions)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
CopyUTF8toUTF16(mImpl->mEstablishedExtensions, aExtensions);
AssertIsOnTargetThread();
CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
}
// webIDL: readonly attribute DOMString protocol;
void
WebSocket::GetProtocol(nsAString& aProtocol)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
CopyUTF8toUTF16(mImpl->mEstablishedProtocol, aProtocol);
AssertIsOnTargetThread();
CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
}
// webIDL: void send(DOMString data);
@ -2029,16 +1970,17 @@ void
WebSocket::Send(const nsAString& aData,
ErrorResult& aRv)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
NS_ConvertUTF16toUTF8 msgString(aData);
mImpl->Send(nullptr, msgString, msgString.Length(), false, aRv);
Send(nullptr, msgString, msgString.Length(), false, aRv);
}
void
WebSocket::Send(File& aData, ErrorResult& aRv)
{
AssertIsOnTargetThread();
nsCOMPtr<nsIInputStream> msgStream;
nsresult rv = aData.GetInternalStream(getter_AddRefs(msgStream));
if (NS_FAILED(rv)) {
@ -2058,15 +2000,14 @@ WebSocket::Send(File& aData, ErrorResult& aRv)
return;
}
mImpl->Send(msgStream, EmptyCString(), msgLength, true, aRv);
Send(msgStream, EmptyCString(), msgLength, true, aRv);
}
void
WebSocket::Send(const ArrayBuffer& aData,
ErrorResult& aRv)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
aData.ComputeLengthAndData();
@ -2076,15 +2017,14 @@ WebSocket::Send(const ArrayBuffer& aData,
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, len);
mImpl->Send(nullptr, msgString, len, true, aRv);
Send(nullptr, msgString, len, true, aRv);
}
void
WebSocket::Send(const ArrayBufferView& aData,
ErrorResult& aRv)
{
MOZ_ASSERT(mImpl);
mImpl->AssertIsOnTargetThread();
AssertIsOnTargetThread();
aData.ComputeLengthAndData();
@ -2094,20 +2034,20 @@ WebSocket::Send(const ArrayBufferView& aData,
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, len);
mImpl->Send(nullptr, msgString, len, true, aRv);
Send(nullptr, msgString, len, true, aRv);
}
void
WebSocketImpl::Send(nsIInputStream* aMsgStream,
const nsACString& aMsgString,
uint32_t aMsgLength,
bool aIsBinary,
ErrorResult& aRv)
WebSocket::Send(nsIInputStream* aMsgStream,
const nsACString& aMsgString,
uint32_t aMsgLength,
bool aIsBinary,
ErrorResult& aRv)
{
AssertIsOnTargetThread();
int64_t readyState = ReadyState();
if (readyState == WebSocket::CONNECTING) {
if (readyState == CONNECTING) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -2115,22 +2055,23 @@ WebSocketImpl::Send(nsIInputStream* aMsgStream,
// Always increment outgoing buffer len, even if closed
mOutgoingBufferedAmount += aMsgLength;
if (readyState == WebSocket::CLOSING ||
readyState == WebSocket::CLOSED) {
if (readyState == CLOSING ||
readyState == CLOSED) {
return;
}
MOZ_ASSERT(readyState == WebSocket::OPEN,
"Unknown state in WebSocket::Send");
// We must have mImpl when connected.
MOZ_ASSERT(mImpl);
MOZ_ASSERT(readyState == OPEN, "Unknown state in WebSocket::Send");
nsresult rv;
if (aMsgStream) {
rv = mChannel->SendBinaryStream(aMsgStream, aMsgLength);
rv = mImpl->mChannel->SendBinaryStream(aMsgStream, aMsgLength);
} else {
if (aIsBinary) {
rv = mChannel->SendBinaryMsg(aMsgString);
rv = mImpl->mChannel->SendBinaryMsg(aMsgString);
} else {
rv = mChannel->SendMsg(aMsgString);
rv = mImpl->mChannel->SendMsg(aMsgString);
}
}
@ -2139,7 +2080,7 @@ WebSocketImpl::Send(nsIInputStream* aMsgStream,
return;
}
mWebSocket->UpdateMustKeepAlive();
UpdateMustKeepAlive();
}
// webIDL: void close(optional unsigned short code, optional DOMString reason):
@ -2147,15 +2088,6 @@ void
WebSocket::Close(const Optional<uint16_t>& aCode,
const Optional<nsAString>& aReason,
ErrorResult& aRv)
{
MOZ_ASSERT(mImpl);
mImpl->Close(aCode, aReason, aRv);
}
void
WebSocketImpl::Close(const Optional<uint16_t>& aCode,
const Optional<nsAString>& aReason,
ErrorResult& aRv)
{
AssertIsOnTargetThread();
@ -2181,18 +2113,21 @@ WebSocketImpl::Close(const Optional<uint16_t>& aCode,
}
int64_t readyState = ReadyState();
if (readyState == WebSocket::CLOSING ||
readyState == WebSocket::CLOSED) {
if (readyState == CLOSING ||
readyState == CLOSED) {
return;
}
if (readyState == WebSocket::CONNECTING) {
FailConnection(closeCode, closeReason);
// If the webSocket is not closed we MUST have a mImpl.
MOZ_ASSERT(mImpl);
if (readyState == CONNECTING) {
mImpl->FailConnection(closeCode, closeReason);
return;
}
MOZ_ASSERT(readyState == WebSocket::OPEN);
CloseConnection(closeCode, closeReason);
MOZ_ASSERT(readyState == OPEN);
mImpl->CloseConnection(closeCode, closeReason);
}
//-----------------------------------------------------------------------------
@ -2206,7 +2141,7 @@ WebSocketImpl::Observe(nsISupports* aSubject,
{
AssertIsOnMainThread();
int64_t readyState = ReadyState();
int64_t readyState = mWebSocket->ReadyState();
if ((readyState == WebSocket::CLOSING) ||
(readyState == WebSocket::CLOSED)) {
return NS_OK;
@ -2235,7 +2170,7 @@ WebSocketImpl::GetName(nsACString& aName)
{
AssertIsOnMainThread();
CopyUTF16toUTF8(mOriginalURL, aName);
CopyUTF16toUTF8(mWebSocket->mOriginalURL, aName);
return NS_OK;
}
@ -2244,7 +2179,7 @@ WebSocketImpl::IsPending(bool* aValue)
{
AssertIsOnTargetThread();
int64_t readyState = ReadyState();
int64_t readyState = mWebSocket->ReadyState();
*aValue = (readyState != WebSocket::CLOSED);
return NS_OK;
}
@ -2323,7 +2258,7 @@ WebSocketImpl::CancelInternal()
{
AssertIsOnTargetThread();
int64_t readyState = ReadyState();
int64_t readyState = mWebSocket->ReadyState();
if (readyState == WebSocket::CLOSING || readyState == WebSocket::CLOSED) {
return NS_OK;
}
@ -2484,5 +2419,11 @@ WebSocketImpl::IsTargetThread() const
return NS_IsMainThread() == !mWorkerPrivate;
}
void
WebSocket::AssertIsOnTargetThread() const
{
MOZ_ASSERT(NS_IsMainThread() == !mWorkerPrivate);
}
} // dom namespace
} // mozilla namespace

View File

@ -23,6 +23,8 @@
#define DEFAULT_WS_SCHEME_PORT 80
#define DEFAULT_WSS_SCHEME_PORT 443
class nsIInputStream;
namespace mozilla {
namespace dom {
@ -88,7 +90,7 @@ public: // WebIDL interface:
void GetUrl(nsAString& aResult);
// webIDL: readonly attribute unsigned short readyState;
uint16_t ReadyState() const;
uint16_t ReadyState();
// webIDL: readonly attribute unsigned long bufferedAmount;
uint32_t BufferedAmount() const;
@ -134,6 +136,8 @@ private: // constructor && distructor
explicit WebSocket(nsPIDOMWindow* aOwnerWindow);
virtual ~WebSocket();
void SetReadyState(uint16_t aReadyState);
// These methods actually do the dispatch for various events.
nsresult CreateAndDispatchSimpleEvent(const nsAString& aName);
nsresult CreateAndDispatchMessageEvent(const nsACString& aData,
@ -157,16 +161,39 @@ private:
WebSocket(const WebSocket& x) MOZ_DELETE; // prevent bad usage
WebSocket& operator=(const WebSocket& x) MOZ_DELETE;
void Send(nsIInputStream* aMsgStream,
const nsACString& aMsgString,
uint32_t aMsgLength,
bool aIsBinary,
ErrorResult& aRv);
void AssertIsOnTargetThread() const;
// Raw pointer because this WebSocketImpl is created, managed and destroyed by
// WebSocket.
WebSocketImpl* mImpl;
// This is used just to check in which thread this object is used when mImpl
// is null.
workers::WorkerPrivate* mWorkerPrivate;
bool mKeepingAlive;
bool mCheckMustKeepAlive;
uint32_t mOutgoingBufferedAmount;
// related to the WebSocket constructor steps
nsString mOriginalURL;
nsString mEffectiveURL; // after redirects
nsCString mEstablishedExtensions;
nsCString mEstablishedProtocol;
dom::BinaryType mBinaryType;
// This mutex protects mReadyState that is the only variable that is used in
// different threads.
mozilla::Mutex mMutex;
// This value should not be used directly but use ReadyState() instead.
uint16_t mReadyState;
};
} //namespace dom

View File

@ -4703,10 +4703,6 @@ nsDocument::GetWindowInternal() const
if (win) {
// mScriptGlobalObject is always the inner window, let's get the outer.
win = win->GetOuterWindow();
} else if (mMasterDocument) {
// For script execution in the imported document we need the window of
// the master document.
win = mMasterDocument->GetWindow();
}
}

View File

@ -793,6 +793,10 @@ GK_ATOM(onmouseover, "onmouseover")
GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
GK_ATOM(onmouseup, "onmouseup")
GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
GK_ATOM(onmozbrowserafterkeydown, "onmozbrowserafterkeydown")
GK_ATOM(onmozbrowserafterkeyup, "onmozbrowserafterkeyup")
GK_ATOM(onmozbrowserbeforekeydown, "onmozbrowserbeforekeydown")
GK_ATOM(onmozbrowserbeforekeyup, "onmozbrowserbeforekeyup")
GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")

View File

@ -292,7 +292,8 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->GetWindow()));
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->MasterDocument()->GetWindow()));
if (!window) {
return NS_ERROR_NULL_POINTER;
}

View File

@ -659,3 +659,5 @@ support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
[test_element.matches.html]
[test_user_select.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
[test_bug1081686.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s

View File

@ -0,0 +1,70 @@
<!DOCTYPE HTML>
<html>
<!--
-->
<head>
<title>bug 1081686</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
var ws;
function forcegc()
{
SpecialPowers.forceGC();
SpecialPowers.gc();
setTimeout(function()
{
SpecialPowers.gc();
}, 0);
}
function testWebSocket () {
ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket_hello");
ws.onopen = function(e) {
ws.send("data");
}
ws.onclose = function(e) {
forcegc();
setTimeout(function() {
is(ws.readyState, 3, 'WebSocket is closed');
is(ws.bufferedAmount, 0, 'WebSocket.bufferedAmount should be empty.');
is(ws.binaryType, 'blob', 'WebSocket.binaryType is blob');
ws.binaryType = 'arraybuffer';
is(ws.binaryType, 'arraybuffer', 'WebSocket.binaryType is arraybuffer');
is(ws.url, 'ws://mochi.test:8888/tests/content/base/test/file_websocket_hello', 'WebSocket.url is correct');
ws.close();
ws.send('foobar');
SimpleTest.finish();
}, 1000);
}
ws.onerror = function(e) {
ok(false, "onerror called!");
SimpleTest.finish();
}
ws.onmessage = function(e) {
is(e.data, "Hello world!", "Wrong data");
ws.close();
}
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<div>
</div>
</body>
</html>

View File

@ -2408,10 +2408,18 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
SetValueInternal(aValue, true, true);
}
return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(this),
NS_LITERAL_STRING("input"), true,
true);
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(this),
NS_LITERAL_STRING("input"), true,
true);
// If this element is not currently focused, it won't receive a change event for this
// update through the normal channels. So fire a change event immediately, instead.
if (!ShouldBlur(this)) {
FireChangeEventIfNeeded();
}
return NS_OK;
}
nsIEditor*

View File

@ -42,7 +42,7 @@ function testUserInput() {
"Change event dispatched when setting the value of the input element (2).");
SpecialPowers.wrap(input).setUserInput("foo");
is(inputChange, 1,
is(inputChange, 2,
"Change event dispatched when input element doesn't have focus.");
textarea.focus();

View File

@ -445,16 +445,18 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLeng
uint8_t* aData,
uint32_t aLength,
const nsAString& aMimeType)
: mPhotoDataLength(aLength)
{
mCallbacks.SwapElements(aCallbacks);
mPhoto.AppendElements(aData, aLength);
mPhotoData = (uint8_t*) moz_malloc(aLength);
memcpy(mPhotoData, aData, mPhotoDataLength);
mMimeType = aMimeType;
}
NS_IMETHOD Run()
{
nsRefPtr<dom::File> blob =
dom::File::CreateMemoryFile(nullptr, mPhoto.Elements(), mPhoto.Length(), mMimeType);
dom::File::CreateMemoryFile(nullptr, mPhotoData, mPhotoDataLength, mMimeType);
uint32_t callbackCounts = mCallbacks.Length();
for (uint8_t i = 0; i < callbackCounts; i++) {
nsRefPtr<dom::File> tempBlob = blob;
@ -466,8 +468,9 @@ MediaEngineGonkVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLeng
}
nsTArray<nsRefPtr<PhotoCallback>> mCallbacks;
nsTArray<uint8_t> mPhoto;
uint8_t* mPhotoData;
nsString mMimeType;
uint32_t mPhotoDataLength;
};
// All elements in mPhotoCallbacks will be swapped in GenerateBlobRunnable

View File

@ -2858,6 +2858,11 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
nsTArray<mozilla::dom::ProfileTimelineMarker> profileTimelineMarkers;
// If we see an unpaired START, we keep it around for the next call
// to PopProfileTimelineMarkers. We store the kept START objects in
// this array.
decltype(mProfileTimelineMarkers) keptMarkers;
for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) {
ProfilerMarkerTracing* startPayload = static_cast<ProfilerMarkerTracing*>(
mProfileTimelineMarkers[i]->mPayload);
@ -2866,6 +2871,8 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
bool hasSeenPaintedLayer = false;
if (startPayload->GetMetaData() == TRACING_INTERVAL_START) {
bool hasSeenEnd = false;
// The assumption is that the devtools timeline flushes markers frequently
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
@ -2893,15 +2900,26 @@ nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
profileTimelineMarkers.AppendElement(marker);
}
// We want the start to be dropped either way.
hasSeenEnd = true;
break;
}
}
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptMarkers.AppendElement(mProfileTimelineMarkers[i]);
mProfileTimelineMarkers.RemoveElementAt(i);
--i;
}
}
}
ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers);
ClearProfileTimelineMarkers();
mProfileTimelineMarkers.SwapElements(keptMarkers);
return NS_OK;
#else
@ -2952,8 +2970,7 @@ nsDocShell::ClearProfileTimelineMarkers()
{
#ifdef MOZ_ENABLE_PROFILER_SPS
for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) {
delete mProfileTimelineMarkers[i]->mPayload;
mProfileTimelineMarkers[i]->mPayload = nullptr;
delete mProfileTimelineMarkers[i];
}
mProfileTimelineMarkers.Clear();
#endif

View File

@ -21,6 +21,7 @@
#include "mozilla/WeakPtr.h"
#include "mozilla/TimeStamp.h"
#include "GeckoProfiler.h"
#include "ProfilerMarkers.h"
// Helper Classes
#include "nsCOMPtr.h"
@ -959,11 +960,17 @@ private:
, mPayload(aPayload)
, mTime(aTime)
{}
~InternalProfileTimelineMarker()
{
delete mPayload;
}
const char* mName;
ProfilerMarkerTracing* mPayload;
float mTime;
};
nsTArray<nsAutoPtr<InternalProfileTimelineMarker>> mProfileTimelineMarkers;
nsTArray<InternalProfileTimelineMarker*> mProfileTimelineMarkers;
// Get the elapsed time (in millis) since the profile timeline recording
// started

View File

@ -493,6 +493,12 @@ this.PermissionsTable = { geolocation: {
trusted: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"before-after-keyboard-event": {
app: DENY_ACTION,
trusted: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
}
};

View File

@ -43,6 +43,11 @@ protected:
private:
DOMCursor() MOZ_DELETE;
// Calling Then() on DOMCursor is a mistake, since the DOMCursor object
// should not have a .then() method from JS' point of view.
already_AddRefed<mozilla::dom::Promise>
Then(JSContext* aCx, AnyCallback* aResolveCallback,
AnyCallback* aRejectCallback, ErrorResult& aRv) MOZ_DELETE;
nsCOMPtr<nsICursorContinueCallback> mCallback;
bool mFinished;

View File

@ -10,11 +10,16 @@
#include "nsThreadUtils.h"
#include "DOMCursor.h"
#include "nsIDOMEvent.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
using mozilla::dom::AnyCallback;
using mozilla::dom::DOMError;
using mozilla::dom::DOMRequest;
using mozilla::dom::DOMRequestService;
using mozilla::dom::DOMCursor;
using mozilla::dom::Promise;
using mozilla::AutoSafeJSContext;
DOMRequest::DOMRequest(nsPIDOMWindow* aWindow)
@ -25,16 +30,24 @@ DOMRequest::DOMRequest(nsPIDOMWindow* aWindow)
{
}
DOMRequest::~DOMRequest()
{
mResult.setUndefined();
mozilla::DropJSObjects(this);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
tmp->mResult = JSVAL_VOID;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -107,6 +120,10 @@ DOMRequest::FireSuccess(JS::Handle<JS::Value> aResult)
mResult = aResult;
FireEvent(NS_LITERAL_STRING("success"), false, false);
if (mPromise) {
mPromise->MaybeResolve(mResult);
}
}
void
@ -120,6 +137,10 @@ DOMRequest::FireError(const nsAString& aError)
mError = new DOMError(GetOwner(), aError);
FireEvent(NS_LITERAL_STRING("error"), true, true);
if (mPromise) {
mPromise->MaybeRejectBrokenly(mError);
}
}
void
@ -133,6 +154,10 @@ DOMRequest::FireError(nsresult aError)
mError = new DOMError(GetOwner(), aError);
FireEvent(NS_LITERAL_STRING("error"), true, true);
if (mPromise) {
mPromise->MaybeRejectBrokenly(mError);
}
}
void
@ -147,6 +172,10 @@ DOMRequest::FireDetailedError(DOMError* aError)
mError = aError;
FireEvent(NS_LITERAL_STRING("error"), true, true);
if (mPromise) {
mPromise->MaybeRejectBrokenly(mError);
}
}
void
@ -175,6 +204,31 @@ DOMRequest::RootResultVal()
mozilla::HoldJSObjects(this);
}
already_AddRefed<Promise>
DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback,
AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv)
{
if (!mPromise) {
mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv);
if (aRv.Failed()) {
return nullptr;
}
if (mDone) {
// Since we create mPromise lazily, it's possible that the DOMRequest object
// has already fired its success/error event. In that case we should
// manually resolve/reject mPromise here. mPromise will take care of
// calling the callbacks on |promise| as needed.
if (mError) {
mPromise->MaybeRejectBrokenly(mError);
} else {
mPromise->MaybeResolve(mResult);
}
}
}
return mPromise->Then(aCx, aResolveCallback, aRejectCallback, aRv);
}
NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService)
NS_IMETHODIMP
@ -253,7 +307,7 @@ public:
const JS::Value& aResult)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
AutoSafeJSContext cx;
mozilla::ThreadsafeAutoSafeJSContext cx;
nsRefPtr<FireSuccessAsyncTask> asyncTask = new FireSuccessAsyncTask(cx, aRequest, aResult);
if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) {
NS_WARNING("Failed to dispatch to main thread!");

View File

@ -16,14 +16,21 @@
#include "nsCOMPtr.h"
namespace mozilla {
class ErrorResult;
namespace dom {
class AnyCallback;
class Promise;
class DOMRequest : public DOMEventTargetHelper,
public nsIDOMDOMRequest
{
protected:
JS::Heap<JS::Value> mResult;
nsRefPtr<DOMError> mError;
nsRefPtr<Promise> mPromise;
bool mDone;
public:
@ -67,6 +74,9 @@ public:
IMPL_EVENT_HANDLER(success)
IMPL_EVENT_HANDLER(error)
already_AddRefed<mozilla::dom::Promise>
Then(JSContext* aCx, AnyCallback* aResolveCallback,
AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv);
void FireSuccess(JS::Handle<JS::Value> aResult);
void FireError(const nsAString& aError);
@ -76,11 +86,7 @@ public:
explicit DOMRequest(nsPIDOMWindow* aWindow);
protected:
virtual ~DOMRequest()
{
mResult = JSVAL_VOID;
mozilla::DropJSObjects(this);
}
virtual ~DOMRequest();
void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);

View File

@ -51,6 +51,7 @@ var tests = [
ok("readyState" in req, "cursor has readyState");
ok("done" in req, "cursor has finished");
ok("continue" in req, "cursor has continue");
ok(!("then" in req), "cursor should not have a then method");
is(req.readyState, "pending", "readyState is pending");
is(req.result, undefined, "result is undefined");

View File

@ -17,59 +17,212 @@
var reqserv = SpecialPowers.getDOMRequestService();
ok("createRequest" in reqserv, "appears to be a service");
// create a request
var req = reqserv.createRequest(window);
ok("result" in req, "request has result");
ok("error" in req, "request has error");
ok("onsuccess" in req, "request has onsuccess");
ok("onerror" in req, "request has onerror");
ok("readyState" in req, "request has readyState");
function testBasics() {
// create a request
var req = reqserv.createRequest(window);
ok("result" in req, "request has result");
ok("error" in req, "request has error");
ok("onsuccess" in req, "request has onsuccess");
ok("onerror" in req, "request has onerror");
ok("readyState" in req, "request has readyState");
ok("then" in req, "request has then");
is(req.readyState, "pending", "readyState is pending");
is(req.result, undefined, "result is undefined");
is(req.onsuccess, null, "onsuccess is null");
is(req.onerror, null, "onerror is null");
is(req.readyState, "pending", "readyState is pending");
is(req.result, undefined, "result is undefined");
is(req.onsuccess, null, "onsuccess is null");
is(req.onerror, null, "onerror is null");
// fire success
var ev = null;
req.onsuccess = function(e) {
ev = e;
runTest();
}
reqserv.fireSuccess(req, "my result");
ok(ev, "got success event");
is(ev.type, "success", "correct type during success");
is(ev.target, req, "correct target during success");
is(req.readyState, "done", "correct readyState after success");
is(req.error, null, "correct error after success");
is(req.result, "my result", "correct result after success");
// fire error
req = reqserv.createRequest(window);
ev = null;
req.onerror = function(e) {
ev = e;
function testSuccess() {
// fire success
var req = reqserv.createRequest(window);
var ev = null;
req.onsuccess = function(e) {
ev = e;
}
var result = null;
var promise = req.then(function(r) {
is(r, "my result", "correct result when resolving the promise");
result = r;
runTest();
}, function(e) {
ok(false, "promise should not be rejected");
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
reqserv.fireSuccess(req, "my result");
ok(ev, "got success event");
is(ev.type, "success", "correct type during success");
is(ev.target, req, "correct target during success");
is(req.readyState, "done", "correct readyState after success");
is(req.error, null, "correct error after success");
is(req.result, "my result", "correct result after success");
is(result, null, "Promise should not be resolved synchronously");
}
reqserv.fireError(req, "OhMyError");
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "OhMyError", "correct error after error");
is(req.result, undefined, "correct result after error");
// fire detailed error
req = reqserv.createRequest(window);
ev = null;
req.onerror = function(e) {
ev = e;
};
reqserv.fireDetailedError(req, new DOMError("detailedError"));
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "detailedError", "correct error after error");
is(req.result, undefined, "correct result after error");
function testError() {
// fire error
var req = reqserv.createRequest(window);
var ev = null;
req.onerror = function(e) {
ev = e;
}
var error = null;
var promise = req.then(function(r) {
ok(false, "promise should not be resolved");
runTest();
}, function(e) {
ok(e instanceof DOMError, "got error rejection");
ok(e === req.error, "got correct error when rejecting the promise");
error = e;
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
reqserv.fireError(req, "OhMyError");
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "OhMyError", "correct error after error");
is(req.result, undefined, "correct result after error");
is(error, null, "Promise should not be rejected synchronously");
}
function testDetailedError() {
// fire detailed error
var req = reqserv.createRequest(window);
var ev = null;
req.onerror = function(e) {
ev = e;
};
var error = null;
var promise = req.then(function(r) {
ok(false, "promise should not be resolved");
runTest();
}, function(e) {
ok(e instanceof DOMError, "got error rejection");
ok(e === req.error, "got correct error when rejecting the promise");
error = e;
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
reqserv.fireDetailedError(req, new DOMError("detailedError"));
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "detailedError", "correct error after error");
is(req.result, undefined, "correct result after error");
is(error, null, "Promise should not be rejected synchronously");
}
function testThenAfterSuccess() {
// fire success
var req = reqserv.createRequest(window);
var ev = null;
req.onsuccess = function(e) {
ev = e;
}
reqserv.fireSuccess(req, "my result");
ok(ev, "got success event");
is(ev.type, "success", "correct type during success");
is(ev.target, req, "correct target during success");
is(req.readyState, "done", "correct readyState after success");
is(req.error, null, "correct error after success");
is(req.result, "my result", "correct result after success");
var result = null;
var promise = req.then(function(r) {
is(r, "my result", "correct result when resolving the promise");
result = r;
runTest();
}, function(e) {
ok(false, "promise should not be rejected");
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
is(result, null, "Promise should not be resolved synchronously");
}
function testThenAfterError() {
// fire error
var req = reqserv.createRequest(window);
var ev = null;
req.onerror = function(e) {
ev = e;
}
reqserv.fireError(req, "OhMyError");
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "OhMyError", "correct error after error");
is(req.result, undefined, "correct result after error");
var error = null;
var promise = req.then(function(r) {
ok(false, "promise should not be resolved");
runTest();
}, function(e) {
ok(e instanceof DOMError, "got error rejection");
ok(e === req.error, "got correct error when rejecting the promise");
error = e;
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
is(error, null, "Promise should not be rejected synchronously");
}
function testDetailedError() {
// fire detailed error
var req = reqserv.createRequest(window);
var ev = null;
req.onerror = function(e) {
ev = e;
};
var error = null;
var promise = req.then(function(r) {
ok(false, "promise should not be resolved");
runTest();
}, function(e) {
ok(e instanceof DOMError, "got error rejection");
ok(e === req.error, "got correct error when rejecting the promise");
error = e;
runTest();
});
ok(promise instanceof Promise, "then() should return a Promise");
reqserv.fireDetailedError(req, new DOMError("detailedError"));
ok(ev, "got error event");
is(ev.type, "error", "correct type during error");
is(ev.target, req, "correct target during error");
is(req.readyState, "done", "correct readyState after error");
is(req.error.name, "detailedError", "correct error after error");
is(req.result, undefined, "correct result after error");
is(error, null, "Promise should not be rejected synchronously");
}
var tests = [
testBasics,
testSuccess,
testError,
testDetailedError,
testThenAfterSuccess,
testThenAfterError,
];
function runTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</pre>
</body>

View File

@ -352,6 +352,10 @@ DOMInterfaces = {
'headerFile': 'mozilla/dom/DOMRect.h',
},
'DOMRequest': {
'implicitJSContext': [ 'then' ],
},
'DOMSettableTokenList': {
'nativeType': 'nsDOMSettableTokenList',
},

View File

@ -236,6 +236,24 @@ ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
return MaybeWrapValue(aCx, aValue);
}
// Accept existing JS values on the Heap (which may not be same-compartment with us
inline bool
ToJSValue(JSContext* aCx, const JS::Heap<JS::Value>& aArgument,
JS::MutableHandle<JS::Value> aValue)
{
aValue.set(aArgument);
return MaybeWrapValue(aCx, aValue);
}
// Accept existing rooted JS values (which may not be same-compartment with us
inline bool
ToJSValue(JSContext* aCx, const JS::Rooted<JS::Value>& aArgument,
JS::MutableHandle<JS::Value> aValue)
{
aValue.set(aArgument);
return MaybeWrapValue(aCx, aValue);
}
// Accept nsresult, for use in rejections, and create an XPCOM
// exception object representing that nsresult.
bool

View File

@ -114,8 +114,8 @@
#undef free // apparently defined by some windows header, clashing with a free()
// method in SkTypes.h
#ifdef USE_SKIA
#include "SkiaGLGlue.h"
#ifdef USE_SKIA
#include "SurfaceTypes.h"
#endif

View File

@ -141,6 +141,13 @@ WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat
if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D"))
return;
GetAndFlushUnderlyingGLErrors();
gl->fTexStorage2D(target, levels, internalformat, width, height);
GLenum error = GetAndFlushUnderlyingGLErrors();
if (error) {
return GenerateWarning("texStorage2D generated error %s", ErrorName(error));
}
WebGLTexture* tex = activeBoundTextureForTarget(target);
tex->SetImmutable();
@ -157,8 +164,6 @@ WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat
w = std::max(1, w / 2);
h = std::max(1, h / 2);
}
gl->fTexStorage2D(target, levels, internalformat, width, height);
}
void
@ -175,6 +180,13 @@ WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat
if (!ValidateTexStorage(target, levels, internalformat, width, height, depth, "texStorage3D"))
return;
GetAndFlushUnderlyingGLErrors();
gl->fTexStorage3D(target, levels, internalformat, width, height, depth);
GLenum error = GetAndFlushUnderlyingGLErrors();
if (error) {
return GenerateWarning("texStorage3D generated error %s", ErrorName(error));
}
WebGLTexture* tex = activeBoundTextureForTarget(target);
tex->SetImmutable();
@ -190,8 +202,6 @@ WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat
h = std::max(1, h >> 1);
d = std::max(1, d >> 1);
}
gl->fTexStorage3D(target, levels, internalformat, width, height, depth);
}
void
@ -272,8 +282,19 @@ WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level,
if (dataLength < bytesNeeded)
return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength);
if (imageInfo.HasUninitializedImageData())
tex->DoDeferredImageInitialization(texImageTarget, level);
if (imageInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
zoffset == 0 &&
width == imageInfo.Width() &&
height == imageInfo.Height() &&
depth == imageInfo.Depth();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
tex->DoDeferredImageInitialization(texImageTarget, level);
}
}
GLenum driverType = LOCAL_GL_NONE;
GLenum driverInternalFormat = LOCAL_GL_NONE;

View File

@ -594,7 +594,15 @@ WebGLContext::CopyTexSubImage2D(GLenum rawTexImgTarget,
ClearBackbufferIfNeeded();
if (imageInfo.HasUninitializedImageData()) {
tex->DoDeferredImageInitialization(texImageTarget, level);
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == texWidth &&
height == texHeight;
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
tex->DoDeferredImageInitialization(texImageTarget, level);
}
}
TexInternalFormat internalformat;
@ -3414,8 +3422,17 @@ WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint
return;
}
if (levelInfo.HasUninitializedImageData())
tex->DoDeferredImageInitialization(texImageTarget, level);
if (levelInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == levelInfo.Width() &&
height == levelInfo.Height();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
tex->DoDeferredImageInitialization(texImageTarget, level);
}
}
MakeContextCurrent();
gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, internalformat, byteLength, view.Data());
@ -3941,9 +3958,17 @@ WebGLContext::TexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
if (byteLength < bytesNeeded)
return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
if (imageInfo.HasUninitializedImageData())
tex->DoDeferredImageInitialization(texImageTarget, level);
if (imageInfo.HasUninitializedImageData()) {
bool coversWholeImage = xoffset == 0 &&
yoffset == 0 &&
width == imageInfo.Width() &&
height == imageInfo.Height();
if (coversWholeImage) {
tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
} else {
tex->DoDeferredImageInitialization(texImageTarget, level);
}
}
MakeContextCurrent();
size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();

View File

@ -113,6 +113,7 @@ public:
size_t MemoryUsage() const;
TexInternalFormat EffectiveInternalFormat() const { return mEffectiveInternalFormat; }
GLsizei Depth() const { return mDepth; }
protected:
/*

View File

@ -129,6 +129,7 @@ LOCAL_INCLUDES += [
'/content/svg/content/src',
'/content/xul/content/src',
'/dom/base',
'/gfx/gl',
'/image/src',
'/js/xpconnect/src',
'/layout/generic',

View File

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/dom/BeforeAfterKeyboardEvent.h"
#include "mozilla/TextEvents.h"
#include "prtime.h"
namespace mozilla {
namespace dom {
BeforeAfterKeyboardEvent::BeforeAfterKeyboardEvent(
EventTarget* aOwner,
nsPresContext* aPresContext,
InternalBeforeAfterKeyboardEvent* aEvent)
: KeyboardEvent(aOwner, aPresContext,
aEvent ? aEvent :
new InternalBeforeAfterKeyboardEvent(false, 0,
nullptr))
{
MOZ_ASSERT(mEvent->mClass == eBeforeAfterKeyboardEventClass,
"event type mismatch eBeforeAfterKeyboardEventClass");
if (!aEvent) {
mEventIsInternal = true;
mEvent->time = PR_Now();
}
}
// static
already_AddRefed<BeforeAfterKeyboardEvent>
BeforeAfterKeyboardEvent::Constructor(
EventTarget* aOwner,
const nsAString& aType,
const BeforeAfterKeyboardEventInit& aParam)
{
nsRefPtr<BeforeAfterKeyboardEvent> event =
new BeforeAfterKeyboardEvent(aOwner, nullptr, nullptr);
ErrorResult rv;
event->InitWithKeyboardEventInit(aOwner, aType, aParam, rv);
NS_WARN_IF(rv.Failed());
event->mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled =
aParam.mEmbeddedCancelled;
return event.forget();
}
// static
already_AddRefed<BeforeAfterKeyboardEvent>
BeforeAfterKeyboardEvent::Constructor(
const GlobalObject& aGlobal,
const nsAString& aType,
const BeforeAfterKeyboardEventInit& aParam,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(owner, aType, aParam);
}
Nullable<bool>
BeforeAfterKeyboardEvent::GetEmbeddedCancelled()
{
nsAutoString type;
GetType(type);
if (type.EqualsLiteral("mozbrowserafterkeydown") ||
type.EqualsLiteral("mozbrowserafterkeyup")) {
return mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled;
}
return Nullable<bool>();
}
} // namespace dom
} // namespace mozilla
using namespace mozilla;
using namespace mozilla::dom;
nsresult
NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
EventTarget* aOwner,
nsPresContext* aPresContext,
InternalBeforeAfterKeyboardEvent* aEvent)
{
BeforeAfterKeyboardEvent* it =
new BeforeAfterKeyboardEvent(aOwner, aPresContext, aEvent);
NS_ADDREF(it);
*aInstancePtrResult = static_cast<Event*>(it);
return NS_OK;
}

View File

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_dom_BeforeAfterKeyboardEvent_h_
#define mozilla_dom_BeforeAfterKeyboardEvent_h_
#include "mozilla/dom/KeyboardEvent.h"
#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
namespace mozilla {
namespace dom {
class BeforeAfterKeyboardEvent : public KeyboardEvent
{
public:
BeforeAfterKeyboardEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
InternalBeforeAfterKeyboardEvent* aEvent);
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
{
return BeforeAfterKeyboardEventBinding::Wrap(aCx, this);
}
static already_AddRefed<BeforeAfterKeyboardEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const BeforeAfterKeyboardEventInit& aParam,
ErrorResult& aRv);
static already_AddRefed<BeforeAfterKeyboardEvent>
Constructor(EventTarget* aOwner, const nsAString& aType,
const BeforeAfterKeyboardEventInit& aEventInitDict);
// This function returns a boolean value when event typs is either
// "mozbrowserafterkeydown" or "mozbrowserafterkeyup".
Nullable<bool> GetEmbeddedCancelled();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_BeforeAfterKeyboardEvent_h_

View File

@ -701,6 +701,9 @@ EventDispatcher::CreateEvent(EventTarget* aOwner,
case eKeyboardEventClass:
return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext,
aEvent->AsKeyboardEvent());
case eBeforeAfterKeyboardEventClass:
return NS_NewDOMBeforeAfterKeyboardEvent(aDOMEvent, aOwner, aPresContext,
aEvent->AsBeforeAfterKeyboardEvent());
case eCompositionEventClass:
return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext,
aEvent->AsCompositionEvent());

View File

@ -237,6 +237,22 @@ EVENT(keyup,
NS_KEY_UP,
EventNameType_HTMLXUL,
eKeyboardEventClass)
NON_IDL_EVENT(mozbrowserbeforekeydown,
NS_KEY_BEFORE_DOWN,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserafterkeydown,
NS_KEY_AFTER_DOWN,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserbeforekeyup,
NS_KEY_BEFORE_UP,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
NON_IDL_EVENT(mozbrowserafterkeyup,
NS_KEY_AFTER_UP,
EventNameType_None,
eBeforeAfterKeyboardEventClass)
EVENT(loadeddata,
NS_LOADEDDATA,
EventNameType_HTML,

View File

@ -651,8 +651,12 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
}
}
// then fall through...
case NS_KEY_BEFORE_DOWN:
case NS_KEY_DOWN:
case NS_KEY_AFTER_DOWN:
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
{
nsIContent* content = GetFocusedContent();
if (content)
@ -3159,7 +3163,9 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
break;
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
break;
case NS_KEY_PRESS:

View File

@ -979,6 +979,16 @@ IMEStateManager::OnCompositionEventDiscarded(
nsRefPtr<TextComposition> composition =
sTextCompositions->GetCompositionFor(aCompositionEvent->widget);
if (!composition) {
// If the PresShell has been being destroyed during composition,
// a TextComposition instance for the composition was already removed from
// the array and destroyed in OnDestroyPresContext(). Therefore, we may
// fail to retrieve a TextComposition instance here.
PR_LOG(sISMLog, PR_LOG_ALWAYS,
("ISM: IMEStateManager::OnCompositionEventDiscarded(), "
"TextComposition instance for the widget has already gone"));
return;
}
composition->OnCompositionEventDiscarded(aCompositionEvent);
}

View File

@ -17,10 +17,8 @@ KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
: UIEvent(aOwner, aPresContext,
aEvent ? aEvent : new WidgetKeyboardEvent(false, 0, nullptr))
, mInitializedByCtor(false)
, mInitialzedWhichValue(0)
, mInitializedWhichValue(0)
{
NS_ASSERTION(mEvent->mClass == eKeyboardEventClass, "event type mismatch");
if (aEvent) {
mEventIsInternal = false;
}
@ -257,8 +255,12 @@ KeyboardEvent::CharCode()
}
switch (mEvent->message) {
case NS_KEY_UP:
case NS_KEY_BEFORE_DOWN:
case NS_KEY_DOWN:
case NS_KEY_AFTER_DOWN:
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
return 0;
case NS_KEY_PRESS:
return mEvent->AsKeyboardEvent()->charCode;
@ -282,10 +284,7 @@ KeyboardEvent::KeyCode()
return mEvent->AsKeyboardEvent()->keyCode;
}
switch (mEvent->message) {
case NS_KEY_UP:
case NS_KEY_PRESS:
case NS_KEY_DOWN:
if (mEvent->HasKeyEventMessage()) {
return mEvent->AsKeyboardEvent()->keyCode;
}
return 0;
@ -296,12 +295,16 @@ KeyboardEvent::Which()
{
// If this event is initialized with ctor, which can have independent value.
if (mInitializedByCtor) {
return mInitialzedWhichValue;
return mInitializedWhichValue;
}
switch (mEvent->message) {
case NS_KEY_UP:
case NS_KEY_BEFORE_DOWN:
case NS_KEY_DOWN:
case NS_KEY_AFTER_DOWN:
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
return KeyCode();
case NS_KEY_PRESS:
//Special case for 4xp bug 62878. Try to make value of which
@ -343,26 +346,35 @@ KeyboardEvent::Constructor(const GlobalObject& aGlobal,
nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
nsRefPtr<KeyboardEvent> newEvent =
new KeyboardEvent(target, nullptr, nullptr);
bool trusted = newEvent->Init(target);
aRv = newEvent->InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
aParam.mShiftKey, aParam.mMetaKey,
aParam.mKeyCode, aParam.mCharCode);
newEvent->SetTrusted(trusted);
newEvent->mDetail = aParam.mDetail;
newEvent->mInitializedByCtor = true;
newEvent->mInitialzedWhichValue = aParam.mWhich;
newEvent->InitWithKeyboardEventInit(target, aType, aParam, aRv);
WidgetKeyboardEvent* internalEvent = newEvent->mEvent->AsKeyboardEvent();
return newEvent.forget();
}
void
KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
const nsAString& aType,
const KeyboardEventInit& aParam,
ErrorResult& aRv)
{
bool trusted = Init(aOwner);
aRv = InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
aParam.mShiftKey, aParam.mMetaKey,
aParam.mKeyCode, aParam.mCharCode);
SetTrusted(trusted);
mDetail = aParam.mDetail;
mInitializedByCtor = true;
mInitializedWhichValue = aParam.mWhich;
WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
internalEvent->location = aParam.mLocation;
internalEvent->mIsRepeat = aParam.mRepeat;
internalEvent->mIsComposing = aParam.mIsComposing;
internalEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
internalEvent->mKeyValue = aParam.mKey;
internalEvent->mCodeNameIndex = CODE_NAME_INDEX_USE_STRING;
internalEvent->mKeyValue = aParam.mKey;
internalEvent->mCodeValue = aParam.mCode;
return newEvent.forget();
}
NS_IMETHODIMP

View File

@ -74,13 +74,19 @@ public:
protected:
~KeyboardEvent() {}
void InitWithKeyboardEventInit(EventTarget* aOwner,
const nsAString& aType,
const KeyboardEventInit& aParam,
ErrorResult& aRv);
private:
// True, if the instance is created with Constructor().
bool mInitializedByCtor;
// If the instance is created with Constructor(), which may have independent
// value. mInitializedWhichValue stores it. I.e., this is invalid when
// mInitializedByCtor is false.
uint32_t mInitialzedWhichValue;
uint32_t mInitializedWhichValue;
};
} // namespace dom

View File

@ -32,6 +32,7 @@ EXPORTS.mozilla += [
EXPORTS.mozilla.dom += [
'AnimationEvent.h',
'BeforeAfterKeyboardEvent.h',
'BeforeUnloadEvent.h',
'ClipboardEvent.h',
'CommandEvent.h',
@ -71,6 +72,7 @@ if CONFIG['MOZ_WEBSPEECH']:
UNIFIED_SOURCES += [
'AnimationEvent.cpp',
'AsyncEventDispatcher.cpp',
'BeforeAfterKeyboardEvent.cpp',
'BeforeUnloadEvent.cpp',
'ClipboardEvent.cpp',
'CommandEvent.cpp',

View File

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Embedded iframe</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="getFocus();">
<p id="display"></p>
<div id="content" style="display: none"></div>
<input id="input" style="display: block;">
<pre id="test">
<script type="application/javascript">
function getFocus() {
input = document.getElementById("input");
input.focus();
}
</script>
</body>
</html>

View File

@ -0,0 +1,200 @@
/*
* Constants and helper functions for testing BeforeAfterKeyboardEvent.
*/
const kUnknownEvent = 0x000;
const kKeyDownEvent = 0x001;
const kKeyUpEvent = 0x002;
const kBeforeEvent = 0x010;
const kAfterEvent = 0x020;
const kParent = 0x100;
const kChild = 0x200;
var gCurrentTest;
function frameScript()
{
function handler(e) {
var results = sendSyncMessage("forwardevent", { type: e.type });
if (results[0]) {
e.preventDefault();
}
}
addEventListener('keydown', handler);
addEventListener('keyup', handler);
addEventListener('mozbrowserbeforekeydown', handler);
addEventListener('mozbrowserbeforekeyup', handler);
addEventListener('mozbrowserafterkeydown', handler);
addEventListener('mozbrowserafterkeyup', handler);
}
function prepareTest(useRemote)
{
setupHandlers(window, embedderHandler);
var iframe = document.createElement("iframe");
iframe.id = "embedded";
iframe.src = "bug989198_embedded.html";
iframe.setAttribute("remote", useRemote ? "true" : "false");
SpecialPowers.wrap(iframe).mozbrowser = true;
iframe.addEventListener("mozbrowserloadend", function onloadend() {
iframe.removeEventListener("mozbrowserloadend", onloadend);
iframe.focus();
var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
mm.addMessageListener("forwardevent", function(msg) {
return embeddedHandler(msg.json);
});
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
runTests();
return;
});
document.body.appendChild(iframe);
}
function cleanupTest()
{
teardownHandlers(window, embedderHandler);
runTests();
}
function setupHandlers(element, handler)
{
element.addEventListener('keydown', handler);
element.addEventListener('keyup', handler);
element.addEventListener('mozbrowserbeforekeydown', handler);
element.addEventListener('mozbrowserbeforekeyup', handler);
element.addEventListener('mozbrowserafterkeydown', handler);
element.addEventListener('mozbrowserafterkeyup', handler);
}
function teardownHandlers(element, handler)
{
element.removeEventListener('keydown', handler);
element.removeEventListener('keyup', handler);
element.removeEventListener('mozbrowserbeforekeydown', handler);
element.removeEventListener('mozbrowserbeforekeyup', handler);
element.removeEventListener('mozbrowserafterkeydown', handler);
element.removeEventListener('mozbrowserafterkeyup', handler);
}
function convertNameToCode(name)
{
switch (name) {
case "mozbrowserbeforekeydown":
return kBeforeEvent | kKeyDownEvent;
case "mozbrowserafterkeydown":
return kAfterEvent | kKeyDownEvent;
case "mozbrowserbeforekeyup":
return kBeforeEvent | kKeyUpEvent;
case "mozbrowserafterkeyup":
return kAfterEvent | kKeyUpEvent;
case "keydown":
return kKeyDownEvent;
case "keyup":
return kKeyUpEvent;
default:
return kUnknownEvent;
}
}
function classifyEvents(test)
{
// Categorize resultEvents into KEYDOWN group and KEYUP group.
for (var i = 0; i < gCurrentTest.resultEvents.length ; i++) {
var code = test.resultEvents[i];
if ((code & 0xF) == 0x1) { // KEYDOWN
test.classifiedEvents[0].push(code);
} else if ((code & 0xF) == 0x2) { // KEYUP
test.classifiedEvents[1].push(code);
} else {
ok(false, "Invalid code for events");
}
}
}
function verifyResults(test)
{
for (var i = 0; i < gCurrentTest.expectedEvents.length; i++) {
is(test.classifiedEvents[i].length,
test.expectedEvents[i].length,
test.description + ": Wrong number of events");
for (var j = 0; j < gCurrentTest.classifiedEvents[i].length; j++) {
var item = test.classifiedEvents[i][j];
is(item, test.expectedEvents[i][j],
test.description + ": Wrong order of events");
}
}
}
function embeddedHandler(e)
{
return handler(e, kChild);
}
function embedderHandler(e)
{
// Verify value of attribute embeddedCancelled
handler(e, kParent, function checkEmbeddedCancelled(code){
switch (code) {
case kBeforeEvent | kKeyDownEvent:
case kBeforeEvent | kKeyUpEvent:
is(e.embeddedCancelled, null,
gCurrentTest.description + ': embeddedCancelled should be null');
break;
case kAfterEvent | kKeyDownEvent:
if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyDownEvent) {
is(e.embeddedCancelled, true,
gCurrentTest.description + ': embeddedCancelled should be true');
} else {
is(e.embeddedCancelled, false,
gCurrentTest.description + ': embeddedCancelled should be false');
}
break;
case kAfterEvent | kKeyUpEvent:
if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyUpEvent) {
is(e.embeddedCancelled, true,
gCurrentTest.description + ': embeddedCancelled should be true');
} else {
is(e.embeddedCancelled, false,
gCurrentTest.description + ': embeddedCancelled should be false');
}
break;
default:
break;
}
});
}
function handler(e, highBit, callback)
{
var code = convertNameToCode(e.type);
var newCode = highBit | code;
gCurrentTest.resultEvents.push(newCode);
if (callback) {
callback(code);
}
// Return and let frameScript to handle
if (highBit == kChild) {
return newCode == gCurrentTest.doPreventDefaultAt;
}
if (newCode == gCurrentTest.doPreventDefaultAt) {
e.preventDefault();
}
}
function runTests()
{
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}

View File

@ -166,3 +166,12 @@ skip-if = buildapp == 'mulet'
[test_onerror_handler_args.html]
[test_wheel_default_action.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s
[test_dom_before_after_keyboard_event.html]
support-files =
bug989198_embedded.html
bug989198_helper.js
[test_dom_before_after_keyboard_event_remote.html]
support-files =
bug989198_embedded.html
bug989198_helper.js
skip-if = buildapp == 'b2g' || e10s

View File

@ -34,6 +34,10 @@ const kEventConstructors = {
},
AudioProcessingEvent: { create: null, // Cannot create untrusted event from JS.
},
BeforeAfterKeyboardEvent: { create: function (aName, aProps) {
return new BeforeAfterKeyboardEvent(aName, aProps);
},
},
BeforeUnloadEvent: { create: function (aName, aProps) {
var e = document.createEvent("beforeunloadevent");
e.initEvent(aName, aProps.bubbles, aProps.cancelable);

View File

@ -0,0 +1,117 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 989198</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
<script type="text/javascript" src="bug989198_helper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="runTests();">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
function testEventOrderAndAttr()
{
const mainDesc = 'Testing the order of the events';
const kTests = [
{
description: mainDesc,
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kUnknownEvent
},
{
description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
},
{
description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
},
{
description: mainDesc + ', calling preventDefault() at "keydown" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kChild | kKeyDownEvent
},
{
description: mainDesc + ', calling preventDefault() at "keyup" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kChild | kKeyUpEvent
}
];
for (var k = 0; k < kTests.length; k++ ) {
gCurrentTest = kTests[k];
synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
classifyEvents(kTests[k]);
verifyResults(kTests[k]);
}
runTests();
}
var tests = [
function addPermissions() {
SpecialPowers.pushPermissions(
[{ type: "before-after-keyboard-event", allow: true, context: document },
{ type: "browser", allow: true, context: document }],
runTests);
},
function addPreferences() {
SpecialPowers.pushPrefEnv(
{ "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
["dom.mozBrowserFramesEnabled", true],
["dom.ipc.tabs.disabled", false]] },
runTests);
},
// Tests for in-process iframe, i.e. <iframe mozbrowser>.
()=>prepareTest(false),
testEventOrderAndAttr,
cleanupTest,
];
</script>
</body>
</html>

View File

@ -0,0 +1,142 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 989198</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
<script type="text/javascript" src="bug989198_helper.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="runTests();">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
const mainDesc = 'Testing the order of the events (OOP)';
var testsForEventOrder = [
{
description: mainDesc,
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kUnknownEvent
},
{
description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
},
{
description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
},
{
description: mainDesc + ', calling preventDefault() at "keydown" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kChild | kKeyDownEvent
},
{
description: mainDesc + ', calling preventDefault() at "keyup" event',
expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
kParent | kKeyDownEvent,
kChild | kKeyDownEvent,
kParent | kAfterEvent | kKeyDownEvent ],
[ kParent | kBeforeEvent | kKeyUpEvent,
kParent | kKeyUpEvent,
kChild | kKeyUpEvent,
kParent | kAfterEvent | kKeyUpEvent ] ],
resultEvents: [],
classifiedEvents: [ [], [] ],
doPreventDefaultAt: kChild | kKeyUpEvent
}
];
function testEventOrder()
{
if (!testsForEventOrder.length) {
runTests();
return;
}
gCurrentTest = testsForEventOrder.shift();
synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
// It take some time to propagate events to a remote iframe.
waitAndVerifyResult(0);
}
function waitAndVerifyResult(count) {
expectedEventLength = gCurrentTest.expectedEvents[0].length +
gCurrentTest.expectedEvents[1].length;
if (gCurrentTest.resultEvents.length >= expectedEventLength || count > 10) {
classifyEvents(gCurrentTest);
verifyResults(gCurrentTest);
testEventOrder();
}
else {
setTimeout(()=>waitAndVerifyResult(count + 1), 100);
}
}
var tests = [
function addPermissions() {
SpecialPowers.pushPermissions(
[{ type: "before-after-keyboard-event", allow: true, context: document },
{ type: "browser", allow: true, context: document }],
runTests);
},
function addPreferences() {
SpecialPowers.pushPrefEnv(
{ "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
["dom.mozBrowserFramesEnabled", true],
["dom.ipc.tabs.disabled", false]] },
runTests);
},
// Tests for out-of-process iframe, i.el. <iframe mozbrowser remote>
()=>prepareTest(true),
testEventOrder,
cleanupTest
];
</script>
</body>
</html>

View File

@ -918,7 +918,9 @@ nsGeolocationService::UpdateAccuracy(bool aForceHigh)
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild* cpc = ContentChild::GetSingleton();
cpc->SendSetGeolocationHigherAccuracy(highRequired);
if (cpc->IsAlive()) {
cpc->SendSetGeolocationHigherAccuracy(highRequired);
}
return;
}

View File

@ -272,6 +272,13 @@ NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext,
mozilla::WidgetKeyboardEvent* aEvent);
nsresult
NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext,
mozilla::InternalBeforeAfterKeyboardEvent* aEvent);
nsresult
NS_NewDOMCompositionEvent(nsIDOMEvent** aInstancePtrResult,
mozilla::dom::EventTarget* aOwner,

View File

@ -515,12 +515,22 @@ InitOnContentProcessCreated()
mozilla::dom::time::InitializeDateCacheCleaner();
}
#if defined(MOZ_TASK_TRACER) && defined(MOZ_NUWA_PROCESS)
static void
ReinitTaskTracer(void* /*aUnused*/)
{
mozilla::tasktracer::InitTaskTracer(
mozilla::tasktracer::FORKED_AFTER_NUWA);
}
#endif
ContentChild::ContentChild()
: mID(uint64_t(-1))
#ifdef ANDROID
,mScreenSize(0, 0)
#endif
, mCanOverrideProcessName(true)
, mIsAlive(true)
{
// This process is a content process, so it's clearly running in
// multiprocess mode!
@ -594,6 +604,12 @@ ContentChild::Init(MessageLoop* aIOLoop,
SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
InitProcessAttributes();
#if defined(MOZ_TASK_TRACER) && defined (MOZ_NUWA_PROCESS)
if (IsNuwaProcess()) {
NuwaAddConstructor(ReinitTaskTracer, nullptr);
}
#endif
return true;
}
@ -653,6 +669,12 @@ ContentChild::GetProcessName(nsAString& aName)
aName.Assign(mProcessName);
}
bool
ContentChild::IsAlive()
{
return mIsAlive;
}
void
ContentChild::GetProcessName(nsACString& aName)
{
@ -1625,6 +1647,7 @@ ContentChild::ActorDestroy(ActorDestroyReason why)
svc->UnregisterListener(mConsoleListener);
mConsoleListener->mChild = nullptr;
}
mIsAlive = false;
XRE_ShutdownChildProcess();
}

View File

@ -87,10 +87,10 @@ public:
const AppInfo& GetAppInfo() {
return mAppInfo;
}
void SetProcessName(const nsAString& aName, bool aDontOverride = false);
void GetProcessName(nsAString& aName);
void GetProcessName(nsACString& aName);
bool IsAlive();
static void AppendProcessId(nsACString& aName);
ContentBridgeParent* GetLastBridge() {
@ -421,6 +421,7 @@ private:
bool mIsForApp;
bool mIsForBrowser;
bool mCanOverrideProcessName;
bool mIsAlive;
nsString mProcessName;
static ContentChild* sSingleton;

View File

@ -398,6 +398,8 @@ parent:
ReplyKeyEvent(WidgetKeyboardEvent event);
DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
returns (MaybeNativeKeyBinding bindings);

View File

@ -81,8 +81,8 @@
#include "ClientLayerManager.h"
#include "LayersLogging.h"
#include "nsIOService.h"
#include "nsColorPickerProxy.h"
#include "nsPresShell.h"
#define BROWSER_ELEMENT_CHILD_SCRIPT \
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
@ -2362,6 +2362,10 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event,
SendReplyKeyEvent(localEvent);
}
if (PresShell::BeforeAfterKeyboardEventEnabled()) {
SendDispatchAfterKeyboardEvent(localEvent);
}
return true;
}

View File

@ -52,6 +52,7 @@
#include "nsIWindowWatcher.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowWatcher.h"
#include "nsPresShell.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
@ -1463,6 +1464,29 @@ TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event)
return true;
}
bool
TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
{
NS_ENSURE_TRUE(mFrameElement, true);
WidgetKeyboardEvent localEvent(aEvent);
localEvent.widget = GetWidget();
nsIDocument* doc = mFrameElement->OwnerDoc();
nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
NS_ENSURE_TRUE(presShell, true);
if (mFrameElement &&
PresShell::BeforeAfterKeyboardEventEnabled() &&
localEvent.message != NS_KEY_PRESS) {
nsCOMPtr<nsINode> node(do_QueryInterface(mFrameElement));
presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent,
aEvent.mFlags.mDefaultPrevented);
}
return true;
}
/**
* Try to answer query event using cached text.
*

View File

@ -122,6 +122,7 @@ public:
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& event);
virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& event);
virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* aActor,
ScrollingBehavior* aScrolling,
TextureFactoryIdentifier* aFactoryIdentifier,

View File

@ -1316,17 +1316,8 @@ function sendMMI(aMmi) {
let deferred = Promise.defer();
telephony.dial(aMmi)
.then(request => {
ok(request instanceof DOMRequest,
"request is instanceof " + request.constructor);
request.addEventListener("success", function(event) {
deferred.resolve(request.result);
});
request.addEventListener("error", function(event) {
deferred.reject(request.error);
});
.then(result => {
deferred.resolve(result);
}, cause => {
deferred.reject(cause);
});

View File

@ -1,5 +1,4 @@
[DEFAULT]
skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #Bug 932350 - Frequent timeouts
support-files =
child_ip_address.html
file_crossdomainprops_inner.html
@ -10,6 +9,7 @@ support-files =
[test_crossdomainprops.html]
[test_innerWidthHeight_script.html]
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Bug 1075071 - Permafail on Android/B2G/Mulet
[test_location.html]
[test_location_framed.html]
[test_location_getters.html]

View File

@ -1,5 +1,5 @@
[DEFAULT]
skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #android(bug 910229) #Bug 932350 - Frequent timeouts
skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
support-files =
DOMTestCase.js
activity-home.css

View File

@ -1,5 +1,5 @@
[DEFAULT]
skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') # android(bug 910229) #Bug 932350 - Frequent timeouts
skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
support-files =
DOMTestCase.js
exclusions.js

View File

@ -1,5 +1,5 @@
[DEFAULT]
skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #android(bug 910229) #Bug 932350 - Frequent timeouts
skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
support-files =
DOMTestCase.js
files/anchor.html

View File

@ -171,6 +171,10 @@ var interfaceNamesInGlobalScope =
"BarProp",
// IMPORTANT: Do not change this list without review from a DOM peer!
"BatteryManager",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BeforeAfterKeyboardEvent", b2g: true,
pref: "dom.beforeAfterKeyboardEvent.enabled",
permission: ["embed-apps", "before-after-keyboard-event"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
"BeforeUnloadEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -180,18 +184,18 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"BlobEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothAdapter", b2g: true, permission: "bluetooth"},
{name: "BluetoothAdapter", b2g: true, permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothDevice", b2g: true, permission: "bluetooth"},
{name: "BluetoothDevice", b2g: true, permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothDeviceEvent", b2g: true, permission: "bluetooth"},
{name: "BluetoothDeviceEvent", b2g: true, permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothDiscoveryStateChangedEvent", b2g: true,
permission: "bluetooth"},
permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothManager", b2g: true, permission: "bluetooth"},
{name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BluetoothStatusChangedEvent", b2g: true, permission: "bluetooth"},
{name: "BluetoothStatusChangedEvent", b2g: true, permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BoxObject", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -751,7 +755,7 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozSettingsEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozSettingsTransactionEvent", permission: "settings-api-read"},
{name: "MozSettingsTransactionEvent", permission: ["settings-api-read"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozSmsEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -771,23 +775,23 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWakeLock", b2g: true, pref: "dom.wakelock.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiCapabilities", b2g: true, permission: "wifi-manage"},
{name: "MozWifiCapabilities", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiConnectionInfoEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiStationInfoEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiManager", b2g: true, permission: "wifi-manage"},
{name: "MozWifiManager", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiNetwork", b2g: true, permission: "wifi-manage"},
{name: "MozWifiNetwork", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiStatusChangeEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pGroupOwner", b2g: true, permission: "wifi-manage"},
{name: "MozWifiP2pGroupOwner", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pManager", b2g: true, permission: "wifi-manage"},
{name: "MozWifiP2pManager", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: "wifi-manage"},
{name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: ["wifi-manage"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MutationEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -845,9 +849,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"PeriodicWave",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PermissionSettings", b2g: true, permission: "permissions"},
{name: "PermissionSettings", b2g: true, permission: ["permissions"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PhoneNumberService", permission: "phonenumberservice"},
{name: "PhoneNumberService", permission: ["phonenumberservice"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Plugin",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -1227,9 +1231,11 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"TreeWalker",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "UDPMessageEvent", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
{name: "UDPMessageEvent", pref: "dom.udpsocket.enabled",
permission: ["udp-socket"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "UDPSocket", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
{name: "UDPSocket", pref: "dom.udpsocket.enabled",
permission: ["udp-socket"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
"UIEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -1339,8 +1345,12 @@ function createInterfaceMap(isXBLScope) {
var isRelease = !version.contains("a");
var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
var hasPermission = function (aPermission) {
return SpecialPowers.hasPermission(aPermission, window.document);
var hasPermission = function (aPermissions) {
var result = false;
for (var p of aPermissions) {
result = result || SpecialPowers.hasPermission(p, window.document);
}
return result;
};
var interfaceMap = {};

View File

@ -0,0 +1,24 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[Constructor(DOMString typeArg,
optional BeforeAfterKeyboardEventInit eventInitDict),
CheckPermissions="embed-apps before-after-keyboard-event",
Pref="dom.beforeAfterKeyboardEvent.enabled"]
interface BeforeAfterKeyboardEvent : KeyboardEvent
{
// The valid value of embeddedCancelled is:
// - "mozbrowserbeforekeydown": null
// - "mozbrowserbeforekeyup": null
// - "mozbrowserafterkeydown": true/false
// - "mozbrowserafterkeyup": true/false
readonly attribute boolean? embeddedCancelled;
};
dictionary BeforeAfterKeyboardEventInit : KeyboardEventInit
{
boolean? embeddedCancelled = null;
};

View File

@ -3,8 +3,10 @@
* 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/. */
interface DOMCursor : DOMRequest {
interface DOMCursor : EventTarget {
readonly attribute boolean done;
[Throws]
void continue();
};
DOMCursor implements DOMRequestShared;

View File

@ -5,7 +5,8 @@
enum DOMRequestReadyState { "pending", "done" };
interface DOMRequest : EventTarget {
[NoInterfaceObject]
interface DOMRequestShared {
readonly attribute DOMRequestReadyState readyState;
readonly attribute any result;
@ -14,3 +15,13 @@ interface DOMRequest : EventTarget {
attribute EventHandler onsuccess;
attribute EventHandler onerror;
};
interface DOMRequest : EventTarget {
// The [TreatNonCallableAsNull] annotation is required since then() should do
// nothing instead of throwing errors when non-callable arguments are passed.
[NewObject, Throws]
Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
[TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
};
DOMRequest implements DOMRequestShared;

View File

@ -49,6 +49,7 @@ WEBIDL_FILES = [
'AutocompleteInfo.webidl',
'BarProp.webidl',
'BatteryManager.webidl',
'BeforeAfterKeyboardEvent.webidl',
'BeforeUnloadEvent.webidl',
'BiquadFilterNode.webidl',
'Blob.webidl',

View File

@ -545,9 +545,8 @@ static const FeatureInfo sFeatureInfoArr[] = {
"transform_feedback2",
400, // OpenGL version
300, // OpenGL ES version
GLContext::Extension_None,
GLContext::ARB_transform_feedback2,
{
GLContext::ARB_transform_feedback2,
GLContext::NV_transform_feedback2,
GLContext::Extensions_End
}

View File

@ -652,7 +652,12 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
case AsmJSImm_ModD:
return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
case AsmJSImm_SinD:
#ifdef _WIN64
// Workaround a VS 2013 sin issue, see math_sin_uncached.
return RedirectCall(FuncCast<double (double)>(js::math_sin_uncached), Args_Double_Double);
#else
return RedirectCall(FuncCast<double (double)>(sin), Args_Double_Double);
#endif
case AsmJSImm_CosD:
return RedirectCall(FuncCast<double (double)>(cos), Args_Double_Double);
case AsmJSImm_TanD:

View File

@ -516,7 +516,7 @@ class StructTypeDescr : public ComplexTypeDescr
}
NativeObject &maybeForwardedFieldInfoObject(size_t slot) const {
return *MaybeForwarded(&fieldInfoObject(slot));
return MaybeForwarded(&getReservedSlot(slot).toObject())->as<NativeObject>();
}
};

View File

@ -472,6 +472,7 @@ class GCRuntime
template <AllowGC allowGC>
static void *refillFreeListFromAnyThread(ThreadSafeContext *cx, AllocKind thingKind);
static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
private:
// For ArenaLists::allocateFromArena()
@ -483,7 +484,6 @@ class GCRuntime
static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
/*
* Return the list of chunks that can be released outside the GC lock.

View File

@ -4008,6 +4008,7 @@ CodeGenerator::emitDebugResultChecks(LInstruction *ins)
default:
return true;
}
return true;
}
#endif
@ -7459,10 +7460,18 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
// Check to make sure we didn't have a mid-build invalidation. If so, we
// will trickle to jit::Compile() and return Method_Skipped.
uint32_t warmUpCount = script->getWarmUpCount();
types::RecompileInfo recompileInfo;
if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
return true;
// IonMonkey could have inferred better type information during
// compilation. Since adding the new information to the actual type
// information can reset the usecount, increase it back to what it was
// before.
if (warmUpCount > script->getWarmUpCount())
script->incWarmUpCounter(warmUpCount - script->getWarmUpCount());
uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
? frameDepth_
: FrameSizeClass::FromDepth(frameDepth_).frameSize();
@ -9720,18 +9729,31 @@ CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
typedef bool (*RecompileFn)(JSContext *);
static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
typedef bool (*ForcedRecompileFn)(JSContext *);
static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
bool
CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
{
Label done;
Register tmp = ToRegister(ins->scratch());
OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
OutOfLineCode *ool;
if (ins->mir()->forceRecompilation())
ool = oolCallVM(ForcedRecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
else
ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
// Check if warm-up counter is high enough.
masm.movePtr(ImmPtr(ins->mir()->script()->addressOfWarmUpCounter()), tmp);
Address ptr(tmp, 0);
masm.add32(Imm32(1), tmp);
masm.branch32(Assembler::BelowOrEqual, ptr, Imm32(ins->mir()->recompileThreshold()), &done);
AbsoluteAddress warmUpCount = AbsoluteAddress(ins->mir()->script()->addressOfWarmUpCounter());
if (ins->mir()->increaseWarmUpCounter()) {
masm.load32(warmUpCount, tmp);
masm.add32(Imm32(1), tmp);
masm.store32(tmp, warmUpCount);
masm.branch32(Assembler::BelowOrEqual, tmp, Imm32(ins->mir()->recompileThreshold()), &done);
} else {
masm.branch32(Assembler::BelowOrEqual, warmUpCount, Imm32(ins->mir()->recompileThreshold()),
&done);
}
// Check if not yet recompiling.
CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), tmp);

View File

@ -2221,7 +2221,7 @@ GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executio
static MethodStatus
Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
bool constructing, ExecutionMode executionMode)
bool constructing, ExecutionMode executionMode, bool forceRecompile = false)
{
MOZ_ASSERT(jit::IsIonEnabled(cx));
MOZ_ASSERT(jit::IsBaselineEnabled(cx));
@ -2258,35 +2258,17 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
if (!scriptIon->method())
return Method_CantCompile;
MethodStatus failedState = Method_Compiled;
// If we keep failing to enter the script due to an OSR pc mismatch,
// recompile with the right pc.
if (osrPc && script->ionScript()->osrPc() != osrPc) {
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
return Method_Skipped;
failedState = Method_Skipped;
}
// Don't recompile/overwrite higher optimized code,
// with a lower optimization level.
if (optimizationLevel < scriptIon->optimizationLevel())
return failedState;
if (optimizationLevel == scriptIon->optimizationLevel() &&
(!osrPc || script->ionScript()->osrPc() == osrPc))
{
return failedState;
}
if (optimizationLevel <= scriptIon->optimizationLevel() && !forceRecompile)
return Method_Compiled;
// Don't start compiling if already compiling
if (scriptIon->isRecompiling())
return failedState;
return Method_Compiled;
if (osrPc)
script->ionScript()->resetOsrPcMismatchCounter();
scriptIon->resetOsrPcMismatchCounter();
recompile = true;
}
@ -2305,11 +2287,8 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode
}
// Compilation succeeded or we invalidated right away or an inlining/alloc abort
if (HasIonScript(script, executionMode)) {
if (osrPc && script->ionScript()->osrPc() != osrPc)
return Method_Skipped;
if (HasIonScript(script, executionMode))
return Method_Compiled;
}
return Method_Skipped;
}
@ -2347,6 +2326,16 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
return Method_CantCompile;
}
// By default a recompilation doesn't happen on osr mismatch.
// Decide if we want to force a recompilation if this happens too much.
bool force = false;
if (script->hasIonScript() && pc != script->ionScript()->osrPc()) {
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
return Method_Skipped;
force = true;
}
// Attempt compilation.
// - Returns Method_Compiled if the right ionscript is present
// (Meaning it was present or a sequantial compile finished)
@ -2354,13 +2343,20 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
// (This means a background thread compilation with that pc could have started or not.)
RootedScript rscript(cx, script);
MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
SequentialExecution);
SequentialExecution, force);
if (status != Method_Compiled) {
if (status == Method_CantCompile)
ForbidCompilation(cx, script);
return status;
}
// Return the compilation was skipped when the osr pc wasn't adjusted.
// This can happen when there was still an IonScript available and a
// background compilation started, but hasn't finished yet.
// Or when we didn't force a recompile.
if (pc != script->ionScript()->osrPc())
return Method_Skipped;
return Method_Compiled;
}
@ -2461,14 +2457,14 @@ jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFram
MethodStatus
jit::Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
bool constructing)
bool constructing, bool force)
{
MOZ_ASSERT(script->hasIonScript());
if (script->ionScript()->isRecompiling())
return Method_Compiled;
MethodStatus status =
Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution);
Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution, force);
if (status != Method_Compiled) {
if (status == Method_CantCompile)
ForbidCompilation(cx, script);

View File

@ -93,7 +93,7 @@ MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
MethodStatus
Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
bool constructing);
bool constructing, bool force);
enum IonExecStatus
{

View File

@ -4454,7 +4454,9 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
!targetScript->baselineScript()->ionCompiledOrInlined() &&
info().executionMode() != DefinitePropertiesAnalysis)
{
return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.",
targetScript->filename(), targetScript->lineno());
return InliningDecision_WarmUpCountTooLow;
}
}
@ -4489,6 +4491,7 @@ IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, Boo
case InliningDecision_Error:
return false;
case InliningDecision_DontInline:
case InliningDecision_WarmUpCountTooLow:
inlineable = false;
break;
case InliningDecision_Inline:
@ -4664,6 +4667,8 @@ IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals,
return InliningStatus_Error;
case InliningDecision_DontInline:
return InliningStatus_NotInlined;
case InliningDecision_WarmUpCountTooLow:
return InliningStatus_WarmUpCountTooLow;
case InliningDecision_Inline:
break;
}
@ -5278,6 +5283,7 @@ IonBuilder::jsop_funcall(uint32_t argc)
case InliningDecision_Error:
return false;
case InliningDecision_DontInline:
case InliningDecision_WarmUpCountTooLow:
break;
case InliningDecision_Inline:
if (target->isInterpreted())
@ -5418,6 +5424,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
case InliningDecision_Error:
return false;
case InliningDecision_DontInline:
case InliningDecision_WarmUpCountTooLow:
break;
case InliningDecision_Inline:
if (target->isInterpreted())
@ -5488,6 +5495,14 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
if (targets.length() == 1)
target = &targets[0]->as<JSFunction>();
if (target && status == InliningStatus_WarmUpCountTooLow) {
MRecompileCheck *check =
MRecompileCheck::New(alloc(), target->nonLazyScript(),
optimizationInfo().inliningRecompileThreshold(),
MRecompileCheck::RecompileCheck_Inlining);
current->add(check);
}
return makeCall(target, callInfo, hasClones);
}
@ -6504,7 +6519,9 @@ IonBuilder::insertRecompileCheck()
OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
uint32_t warmUpThreshold = info->compilerWarmUpThreshold(topBuilder->script());
current->add(MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold));
MRecompileCheck *check = MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold,
MRecompileCheck::RecompileCheck_OptimizationLevel);
current->add(check);
}
JSObject *
@ -9425,6 +9442,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
switch (status) {
case InliningStatus_Error:
return false;
case InliningStatus_WarmUpCountTooLow:
case InliningStatus_NotInlined:
break;
case InliningStatus_Inlined:
@ -9441,6 +9459,7 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName
case InliningDecision_Error:
return false;
case InliningDecision_DontInline:
case InliningDecision_WarmUpCountTooLow:
break;
case InliningDecision_Inline:
inlineable = true;
@ -9865,6 +9884,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
case InliningDecision_Error:
return false;
case InliningDecision_DontInline:
case InliningDecision_WarmUpCountTooLow:
break;
case InliningDecision_Inline:
if (!inlineScriptedCall(callInfo, commonSetter))

View File

@ -666,6 +666,7 @@ class IonBuilder
{
InliningStatus_Error,
InliningStatus_NotInlined,
InliningStatus_WarmUpCountTooLow,
InliningStatus_Inlined
};
@ -673,7 +674,8 @@ class IonBuilder
{
InliningDecision_Error,
InliningDecision_Inline,
InliningDecision_DontInline
InliningDecision_DontInline,
InliningDecision_WarmUpCountTooLow
};
static InliningDecision DontInline(JSScript *targetScript, const char *reason);

View File

@ -42,6 +42,7 @@ OptimizationInfo::initNormalOptimizationInfo()
smallFunctionMaxInlineDepth_ = 10;
compilerWarmUpThreshold_ = 1000;
inliningWarmUpThresholdFactor_ = 0.125;
inliningRecompileThresholdFactor_ = 4;
}
void

View File

@ -108,6 +108,11 @@ class OptimizationInfo
// are inlined, as a fraction of compilerWarmUpThreshold.
double inliningWarmUpThresholdFactor_;
// How many invocations or loop iterations are needed before a function
// is hot enough to recompile the outerScript to inline that function,
// as a multiplication of inliningWarmUpThreshold.
uint32_t inliningRecompileThresholdFactor_;
OptimizationInfo()
{ }
@ -194,6 +199,10 @@ class OptimizationInfo
compilerWarmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold;
return compilerWarmUpThreshold * inliningWarmUpThresholdFactor_;
}
uint32_t inliningRecompileThreshold() const {
return inliningWarmUpThreshold() * inliningRecompileThresholdFactor_;
}
};
class OptimizationInfos

View File

@ -11205,21 +11205,45 @@ class MHasClass
// outermost script (i.e. not the inlined script).
class MRecompileCheck : public MNullaryInstruction
{
public:
enum RecompileCheckType {
RecompileCheck_OptimizationLevel,
RecompileCheck_Inlining
};
private:
JSScript *script_;
uint32_t recompileThreshold_;
bool forceRecompilation_;
bool increaseWarmUpCounter_;
MRecompileCheck(JSScript *script, uint32_t recompileThreshold)
MRecompileCheck(JSScript *script, uint32_t recompileThreshold, RecompileCheckType type)
: script_(script),
recompileThreshold_(recompileThreshold)
{
switch (type) {
case RecompileCheck_OptimizationLevel:
forceRecompilation_ = false;
increaseWarmUpCounter_ = true;
break;
case RecompileCheck_Inlining:
forceRecompilation_ = true;
increaseWarmUpCounter_ = false;
break;
default:
MOZ_CRASH("Unexpected recompile check type");
}
setGuard();
}
public:
INSTRUCTION_HEADER(RecompileCheck);
static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t recompileThreshold) {
return new(alloc) MRecompileCheck(script_, recompileThreshold);
static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t recompileThreshold,
RecompileCheckType type)
{
return new(alloc) MRecompileCheck(script_, recompileThreshold, type);
}
JSScript *script() const {
@ -11230,6 +11254,14 @@ class MRecompileCheck : public MNullaryInstruction
return recompileThreshold_;
}
bool forceRecompilation() const {
return forceRecompilation_;
}
bool increaseWarmUpCounter() const {
return increaseWarmUpCounter_;
}
AliasSet getAliasSet() const {
return AliasSet::None();
}

View File

@ -1049,7 +1049,7 @@ StringReplace(JSContext *cx, HandleString string, HandleString pattern, HandleSt
}
bool
Recompile(JSContext *cx)
RecompileImpl(JSContext *cx, bool force)
{
MOZ_ASSERT(cx->currentlyRunningInJit());
JitActivationIterator activations(cx->runtime());
@ -1065,13 +1065,25 @@ Recompile(JSContext *cx)
if (!IsIonEnabled(cx))
return true;
MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing);
MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing, force);
if (status == Method_Error)
return false;
return true;
}
bool
ForcedRecompile(JSContext *cx)
{
return RecompileImpl(cx, /* force = */ true);
}
bool
Recompile(JSContext *cx)
{
return RecompileImpl(cx, /* force = */ false);
}
bool
SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
bool strict)

View File

@ -724,6 +724,7 @@ JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
bool ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
bool Recompile(JSContext *cx);
bool ForcedRecompile(JSContext *cx);
JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
HandleString repl);
JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,

View File

@ -2157,7 +2157,7 @@ RelocateCell(Zone *zone, TenuredCell *src, AllocKind thingKind, size_t thingSize
MOZ_ASSERT(zone == src->zone());
void *dstAlloc = zone->allocator.arenas.allocateFromFreeList(thingKind, thingSize);
if (!dstAlloc)
dstAlloc = js::gc::ArenaLists::refillFreeListInGC(zone, thingKind);
dstAlloc = GCRuntime::refillFreeListInGC(zone, thingKind);
if (!dstAlloc)
return false;
TenuredCell *dst = TenuredCell::fromPointer(dstAlloc);
@ -2572,14 +2572,22 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
// Flatten |finalizedSorted| into a regular ArenaList.
ArenaList finalized = finalizedSorted.toArenaList();
AutoLockGC lock(fop->runtime());
MOZ_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
// We must take the GC lock to be able to safely modify the ArenaList;
// however, this does not by itself make the changes visible to all threads,
// as not all threads take the GC lock to read the ArenaLists.
// That safety is provided by the ReleaseAcquire memory ordering of the
// background finalize state, which we explicitly set as the final step.
{
AutoLockGC lock(fop->runtime());
MOZ_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
// Join |al| and |finalized| into a single list.
*al = finalized.insertListWithCursorAtEnd(*al);
// Join |al| and |finalized| into a single list.
*al = finalized.insertListWithCursorAtEnd(*al);
lists->arenaListsToSweep[thingKind] = nullptr;
}
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
lists->arenaListsToSweep[thingKind] = nullptr;
}
void

View File

@ -78,6 +78,7 @@ class nsDisplayList;
class nsDisplayListBuilder;
class nsPIDOMWindow;
struct nsPoint;
class nsINode;
struct nsIntPoint;
struct nsIntRect;
struct nsRect;
@ -139,8 +140,8 @@ typedef struct CapturingContentInfo {
} CapturingContentInfo;
#define NS_IPRESSHELL_IID \
{ 0x42e9a352, 0x76f3, 0x4ba3, \
{ 0x94, 0x0b, 0x78, 0x9e, 0x58, 0x38, 0x73, 0x4f } }
{ 0xa0a4b515, 0x0b91, 0x4f13, \
{ 0xa0, 0x60, 0x4b, 0xfb, 0x35, 0x00, 0xdc, 0x00 } }
// debug VerifyReflow flags
#define VERIFY_REFLOW_ON 0x01
@ -855,6 +856,13 @@ public:
nsIDOMEvent* aEvent,
nsEventStatus* aStatus) = 0;
/**
* Dispatch AfterKeyboardEvent with specific target.
*/
virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled) = 0;
/**
* Gets the current target event frame from the PresShell
*/

View File

@ -88,6 +88,7 @@
#include "mozilla/Preferences.h"
#include "nsFrameSelection.h"
#include "FrameLayerBuilder.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
@ -98,10 +99,6 @@
#include "nsTransitionManager.h"
#include "RestyleManager.h"
// Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort().
#ifdef MOZ_WIDGET_GONK
#include "mozilla/layers/AsyncPanZoomController.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
@ -2734,9 +2731,6 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
return NS_OK;
}
// This function is only used on B2G, and some compilers complain about
// unused static functions, so we need to #ifdef it.
#ifdef MOZ_WIDGET_GONK
// aScrollFrame and aScrollFrameAsScrollable must be non-nullptr
static FrameMetrics
CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
@ -2784,7 +2778,6 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
return metrics;
}
#endif
bool
nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
@ -2804,8 +2797,7 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
#ifdef MOZ_WIDGET_GONK
// On B2G, we perform an optimization where we ensure that at least one
// We perform an optimization where we ensure that at least one
// async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
// If that's not the case yet, and we are async-scrollable, we will get a
// displayport.
@ -2834,7 +2826,6 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
// Record that the we now have a scrollable display port.
aBuilder.SetHaveScrollableDisplayPort();
}
#endif
return haveDisplayPort;
}

View File

@ -42,6 +42,7 @@
#include "nsPresShell.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "mozilla/dom/BeforeAfterKeyboardEvent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
#include "mozilla/dom/ShadowRoot.h"
@ -72,6 +73,8 @@
#include "nsAutoPtr.h"
#include "nsReadableUtils.h"
#include "nsIPageSequenceFrame.h"
#include "nsIPermissionManager.h"
#include "nsIMozBrowserFrame.h"
#include "nsCaret.h"
#include "TouchCaret.h"
#include "SelectionCarets.h"
@ -81,6 +84,7 @@
#include "nsILayoutHistoryState.h"
#include "nsILineIterator.h" // for ScrollContentIntoView
#include "pldhash.h"
#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/dom/PointerEventBinding.h"
#include "nsIObserverService.h"
@ -705,6 +709,7 @@ static uint32_t sNextPresShellId;
static bool sPointerEventEnabled = true;
static bool sTouchCaretEnabled = false;
static bool sSelectionCaretEnabled = false;
static bool sBeforeAfterKeyboardEventEnabled = false;
/* static */ bool
PresShell::TouchCaretPrefEnabled()
@ -728,6 +733,18 @@ PresShell::SelectionCaretPrefEnabled()
return sSelectionCaretEnabled;
}
/* static */ bool
PresShell::BeforeAfterKeyboardEventEnabled()
{
static bool sInitialized = false;
if (!sInitialized) {
Preferences::AddBoolVarCache(&sBeforeAfterKeyboardEventEnabled,
"dom.beforeAfterKeyboardEvent.enabled");
sInitialized = true;
}
return sBeforeAfterKeyboardEventEnabled;
}
PresShell::PresShell()
: mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
{
@ -6862,6 +6879,236 @@ private:
nsCOMPtr<nsIContent> mContent;
};
static bool
CheckPermissionForBeforeAfterKeyboardEvent(Element* aElement)
{
// An element which is chrome-privileged should be able to handle before
// events and after events.
nsIPrincipal* principal = aElement->NodePrincipal();
if (nsContentUtils::IsSystemPrincipal(principal)) {
return true;
}
// An element which has "before-after-keyboard-event" permission should be
// able to handle before events and after events.
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
uint32_t permission = nsIPermissionManager::DENY_ACTION;
if (permMgr) {
permMgr->TestPermissionFromPrincipal(principal, "before-after-keyboard-event", &permission);
if (permission == nsIPermissionManager::ALLOW_ACTION) {
return true;
}
// Check "embed-apps" permission for later use.
permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromPrincipal(principal, "embed-apps", &permission);
}
// An element can handle before events and after events if the following
// conditions are met:
// 1) <iframe mozbrowser mozapp>
// 2) it has "embed-apps" permission.
nsCOMPtr<nsIMozBrowserFrame> browserFrame(do_QueryInterface(aElement));
if ((permission == nsIPermissionManager::ALLOW_ACTION) &&
browserFrame && browserFrame->GetReallyIsApp()) {
return true;
}
return false;
}
static void
BuildTargetChainForBeforeAfterKeyboardEvent(nsINode* aTarget,
nsTArray<nsCOMPtr<Element> >& aChain,
bool& aTargetIsIframe)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
nsCOMPtr<nsPIDOMWindow> window;
Element* frameElement;
// Initialize frameElement.
if (content && content->IsHTML(nsGkAtoms::iframe)) {
aTargetIsIframe = true;
frameElement = aTarget->AsElement();
} else {
// If event target is not an iframe, dispatch keydown/keyup event to its
// window after dispatching before events to its ancestors.
aTargetIsIframe = false;
// And skip the event target and get its parent frame.
window = aTarget->OwnerDoc()->GetWindow();
if (window) {
frameElement = window->GetFrameElementInternal();
}
}
// Check permission for all ancestors and add them into the target chain.
while (frameElement) {
if (CheckPermissionForBeforeAfterKeyboardEvent(frameElement)) {
aChain.AppendElement(frameElement);
}
window = frameElement->OwnerDoc()->GetWindow();
frameElement = window ? window->GetFrameElementInternal() : nullptr;
}
}
void
PresShell::DispatchBeforeKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
const WidgetKeyboardEvent& aEvent,
size_t& aChainIndex,
bool& aDefaultPrevented)
{
size_t length = aChain.Length();
if (!CanDispatchEvent(&aEvent) || !length) {
return;
}
uint32_t message =
(aEvent.message == NS_KEY_DOWN) ? NS_KEY_BEFORE_DOWN : NS_KEY_BEFORE_UP;
nsCOMPtr<EventTarget> eventTarget;
// Dispatch before events from the outermost element.
for (int32_t i = length - 1; i >= 0; i--) {
eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
if (!eventTarget || !CanDispatchEvent(&aEvent)) {
return;
}
aChainIndex = i;
InternalBeforeAfterKeyboardEvent beforeEvent(aEvent.mFlags.mIsTrusted,
message, aEvent.widget);
beforeEvent.AssignBeforeAfterKeyEventData(aEvent, false);
EventDispatcher::Dispatch(eventTarget, mPresContext, &beforeEvent);
if (beforeEvent.mFlags.mDefaultPrevented) {
aDefaultPrevented = true;
return;
}
}
}
void
PresShell::DispatchAfterKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
const WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
size_t aStartOffset)
{
size_t length = aChain.Length();
if (!CanDispatchEvent(&aEvent) || !length) {
return;
}
uint32_t message =
(aEvent.message == NS_KEY_DOWN) ? NS_KEY_AFTER_DOWN : NS_KEY_AFTER_UP;
bool embeddedCancelled = aEmbeddedCancelled;
nsCOMPtr<EventTarget> eventTarget;
// Dispatch after events from the innermost element.
for (uint32_t i = aStartOffset; i < length; i++) {
eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
if (!eventTarget || !CanDispatchEvent(&aEvent)) {
return;
}
InternalBeforeAfterKeyboardEvent afterEvent(aEvent.mFlags.mIsTrusted,
message, aEvent.widget);
afterEvent.AssignBeforeAfterKeyEventData(aEvent, false);
afterEvent.mEmbeddedCancelled.SetValue(embeddedCancelled);
EventDispatcher::Dispatch(eventTarget, mPresContext, &afterEvent);
embeddedCancelled = afterEvent.mFlags.mDefaultPrevented;
}
}
void
PresShell::DispatchAfterKeyboardEvent(nsINode* aTarget,
const WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled)
{
MOZ_ASSERT(aTarget);
MOZ_ASSERT(BeforeAfterKeyboardEventEnabled());
if (NS_WARN_IF(aEvent.message != NS_KEY_DOWN &&
aEvent.message != NS_KEY_UP)) {
return;
}
// Build up a target chain. Each item in the chain will receive an after event.
nsAutoTArray<nsCOMPtr<Element>, 5> chain;
bool targetIsIframe = false;
BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
DispatchAfterKeyboardEventInternal(chain, aEvent, aEmbeddedCancelled);
}
bool
PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const
{
bool rv =
mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
if (aEvent) {
rv &= (aEvent && aEvent->widget && !aEvent->widget->Destroyed());
}
return rv;
}
void
PresShell::HandleKeyboardEvent(nsINode* aTarget,
WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
nsEventStatus* aStatus,
EventDispatchingCallback* aEventCB)
{
if (aEvent.message == NS_KEY_PRESS ||
!BeforeAfterKeyboardEventEnabled()) {
EventDispatcher::Dispatch(aTarget, mPresContext,
&aEvent, nullptr, aStatus, aEventCB);
return;
}
MOZ_ASSERT(aTarget);
MOZ_ASSERT(aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_UP);
// Build up a target chain. Each item in the chain will receive a before event.
nsAutoTArray<nsCOMPtr<Element>, 5> chain;
bool targetIsIframe = false;
BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
// Dispatch before events. If each item in the chain consumes the before
// event and doesn't prevent the default action, we will go further to
// dispatch the actual key event and after events in the reverse order.
// Otherwise, only items which has handled the before event will receive an
// after event.
size_t chainIndex;
bool defaultPrevented = false;
DispatchBeforeKeyboardEventInternal(chain, aEvent, chainIndex,
defaultPrevented);
// Dispatch after events to partial items.
if (defaultPrevented) {
DispatchAfterKeyboardEventInternal(chain, aEvent,
aEvent.mFlags.mDefaultPrevented, chainIndex);
// No need to forward the event to child process.
aEvent.mFlags.mNoCrossProcessBoundaryForwarding = true;
return;
}
// Event listeners may kill nsPresContext and nsPresShell.
if (!CanDispatchEvent()) {
return;
}
// Dispatch actual key event to event target.
EventDispatcher::Dispatch(aTarget, mPresContext,
&aEvent, nullptr, aStatus, aEventCB);
// Event listeners may kill nsPresContext and nsPresShell.
if (targetIsIframe || !CanDispatchEvent()) {
return;
}
// Dispatch after events to all items in the chain.
DispatchAfterKeyboardEventInternal(chain, aEvent,
aEvent.mFlags.mDefaultPrevented);
}
nsresult
PresShell::HandleEvent(nsIFrame* aFrame,
WidgetGUIEvent* aEvent,
@ -7866,6 +8113,9 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
IMEStateManager::DispatchCompositionEvent(eventTarget,
mPresContext, aEvent->AsCompositionEvent(), aStatus,
eventCBPtr);
} else if (aEvent->mClass == eKeyboardEventClass) {
HandleKeyboardEvent(eventTarget, *(aEvent->AsKeyboardEvent()),
false, aStatus, eventCBPtr);
} else {
EventDispatcher::Dispatch(eventTarget, mPresContext,
aEvent, nullptr, aStatus, eventCBPtr);

View File

@ -50,6 +50,7 @@ class nsAutoCauseReflowNotifier;
namespace mozilla {
class CSSStyleSheet;
class EventDispatchingCallback;
} // namespace mozilla
// 250ms. This is actually pref-controlled, but we use this value if we fail
@ -75,6 +76,9 @@ public:
// Selection caret preference
static bool SelectionCaretPrefEnabled();
// BeforeAfterKeyboardEvent preference
static bool BeforeAfterKeyboardEventEnabled();
void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
nsCompatibility aCompatMode);
@ -369,6 +373,10 @@ public:
virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled) MOZ_OVERRIDE;
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
protected:
@ -719,6 +727,24 @@ protected:
void EvictTouches();
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
void HandleKeyboardEvent(nsINode* aTarget,
mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
nsEventStatus* aStatus,
mozilla::EventDispatchingCallback* aEventCB);
void DispatchBeforeKeyboardEventInternal(
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
const mozilla::WidgetKeyboardEvent& aEvent,
size_t& aChainIndex,
bool& aDefaultPrevented);
void DispatchAfterKeyboardEventInternal(
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
const mozilla::WidgetKeyboardEvent& aEvent,
bool aEmbeddedCancelled,
size_t aChainIndex = 0);
bool CanDispatchEvent(const mozilla::WidgetGUIEvent* aEvent = nullptr) const;
// A list of images that are visible or almost visible.
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > mVisibleImages;

View File

@ -5183,8 +5183,6 @@ nsComputedDOMStyle::CreatePrimitiveValueForClipPath(
if (aStyleBasicShape &&
aStyleBasicShape->GetShapeType() == nsStyleBasicShape::Type::ePolygon) {
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
// Shape function name and opening parenthesis.
nsAutoString shapeFunctionString;
AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(eCSSKeyword_polygon),
@ -5209,12 +5207,11 @@ nsComputedDOMStyle::CreatePrimitiveValueForClipPath(
shapeFunctionString.Append(coordString);
}
shapeFunctionString.Append(')');
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
val->SetString(shapeFunctionString);
valueList->AppendCSSValue(val);
}
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
if (aSizingBox == NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) {
return valueList;
}
@ -5224,6 +5221,7 @@ nsComputedDOMStyle::CreatePrimitiveValueForClipPath(
nsCSSProps::ValueToKeyword(aSizingBox,
nsCSSProps::kClipShapeSizingKTable),
boxString);
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
val->SetString(boxString);
valueList->AppendCSSValue(val);

Some files were not shown because too many files have changed in this diff Show More