mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound a=merge
This commit is contained in:
commit
5b358466f8
@ -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);
|
||||
|
@ -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 ? {
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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':
|
||||
|
83
b2g/components/DebuggerActors.js
Normal file
83
b2g/components/DebuggerActors.js
Normal 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;
|
@ -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) {
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
70
content/base/test/test_bug1081686.html
Normal file
70
content/base/test/test_bug1081686.html
Normal 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>
|
@ -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*
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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!");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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>
|
||||
|
@ -352,6 +352,10 @@ DOMInterfaces = {
|
||||
'headerFile': 'mozilla/dom/DOMRect.h',
|
||||
},
|
||||
|
||||
'DOMRequest': {
|
||||
'implicitJSContext': [ 'then' ],
|
||||
},
|
||||
|
||||
'DOMSettableTokenList': {
|
||||
'nativeType': 'nsDOMSettableTokenList',
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -113,6 +113,7 @@ public:
|
||||
size_t MemoryUsage() const;
|
||||
|
||||
TexInternalFormat EffectiveInternalFormat() const { return mEffectiveInternalFormat; }
|
||||
GLsizei Depth() const { return mDepth; }
|
||||
|
||||
protected:
|
||||
/*
|
||||
|
@ -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',
|
||||
|
92
dom/events/BeforeAfterKeyboardEvent.cpp
Normal file
92
dom/events/BeforeAfterKeyboardEvent.cpp
Normal 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;
|
||||
}
|
45
dom/events/BeforeAfterKeyboardEvent.h
Normal file
45
dom/events/BeforeAfterKeyboardEvent.h
Normal 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_
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
20
dom/events/test/bug989198_embedded.html
Normal file
20
dom/events/test/bug989198_embedded.html
Normal 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>
|
200
dom/events/test/bug989198_helper.js
Normal file
200
dom/events/test/bug989198_helper.js
Normal 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();
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
117
dom/events/test/test_dom_before_after_keyboard_event.html
Normal file
117
dom/events/test/test_dom_before_after_keyboard_event.html
Normal 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>
|
142
dom/events/test/test_dom_before_after_keyboard_event_remote.html
Normal file
142
dom/events/test/test_dom_before_after_keyboard_event_remote.html
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -398,6 +398,8 @@ parent:
|
||||
|
||||
ReplyKeyEvent(WidgetKeyboardEvent event);
|
||||
|
||||
DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
|
||||
|
||||
sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
|
||||
returns (MaybeNativeKeyBinding bindings);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {};
|
||||
|
24
dom/webidl/BeforeAfterKeyboardEvent.webidl
Normal file
24
dom/webidl/BeforeAfterKeyboardEvent.webidl
Normal 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;
|
||||
};
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -49,6 +49,7 @@ WEBIDL_FILES = [
|
||||
'AutocompleteInfo.webidl',
|
||||
'BarProp.webidl',
|
||||
'BatteryManager.webidl',
|
||||
'BeforeAfterKeyboardEvent.webidl',
|
||||
'BeforeUnloadEvent.webidl',
|
||||
'BiquadFilterNode.webidl',
|
||||
'Blob.webidl',
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -42,6 +42,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
||||
smallFunctionMaxInlineDepth_ = 10;
|
||||
compilerWarmUpThreshold_ = 1000;
|
||||
inliningWarmUpThresholdFactor_ = 0.125;
|
||||
inliningRecompileThresholdFactor_ = 4;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user