зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central. r=merge a=merge
MozReview-Commit-ID: 4PW6ESqLL73
This commit is contained in:
Коммит
f07fc93141
|
@ -229,7 +229,8 @@ Compatibility::Init()
|
|||
|
||||
// If we have a consumer who is not NVDA, we enable detection for the
|
||||
// InSendMessageEx compatibility hack. NVDA does not require this.
|
||||
if ((sConsumers & (~NVDA)) &&
|
||||
// We also skip UIA, as we see crashes there.
|
||||
if ((sConsumers & (~(UIAUTOMATION | NVDA))) &&
|
||||
BrowserTabsRemoteAutostart()) {
|
||||
sUser32Interceptor.Init("user32.dll");
|
||||
if (!sInSendMessageExStub) {
|
||||
|
|
|
@ -27,10 +27,6 @@ function assertCount(snapshot, expectedCount) {
|
|||
add_task(async function setup() {
|
||||
// These probes are opt-in, meaning we only capture them if extended
|
||||
// Telemetry recording is enabled.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["toolkit.telemetry.enabled", true]]
|
||||
});
|
||||
|
||||
let oldCanRecord = Services.telemetry.canRecordExtended;
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
registerCleanupFunction(() => {
|
||||
|
|
|
@ -39,6 +39,7 @@ support-files =
|
|||
|
||||
[browser_ext_browserAction_area.js]
|
||||
[browser_ext_browserAction_context.js]
|
||||
skip-if = os == 'win' || os == 'mac' # Bug 1405453
|
||||
[browser_ext_browserAction_contextMenu.js]
|
||||
# bug 1369197
|
||||
skip-if = os == 'linux'
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(async function test() {
|
||||
await SpecialPowers.pushPrefEnv({set: [["toolkit.telemetry.enabled", true]]});
|
||||
let engine = await promiseNewEngine("testEngine.xml");
|
||||
let histogramKey = "other-" + engine.name + ".contextmenu";
|
||||
let numSearchesBefore = 0;
|
||||
|
|
|
@ -76,7 +76,6 @@ function test() {
|
|||
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified");
|
||||
SpecialPowers.pushPrefEnv({set: [
|
||||
["toolkit.telemetry.enabled", true],
|
||||
["browser.search.widget.inNavBar", true],
|
||||
]}).then(function() {
|
||||
Services.search.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml",
|
||||
|
|
|
@ -153,7 +153,6 @@ add_task(async function setup() {
|
|||
};
|
||||
|
||||
const prefs = [
|
||||
"toolkit.telemetry.enabled",
|
||||
"browser.translation.detectLanguage",
|
||||
"browser.translation.ui.show"
|
||||
];
|
||||
|
|
|
@ -26,8 +26,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
|
|||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
|
||||
"resource://gre/modules/TelemetryLog.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
|
||||
"resource://gre/modules/TelemetryUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||
"resource://services-common/utils.js");
|
||||
|
||||
|
@ -52,8 +50,6 @@ const PREF_LOGGING_DUMP = PREF_LOGGING + ".dump"; // experiments.logging
|
|||
const PREF_MANIFEST_URI = "manifest.uri"; // experiments.logging.manifest.uri
|
||||
const PREF_FORCE_SAMPLE = "force-sample-value"; // experiments.force-sample-value
|
||||
|
||||
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
|
||||
const CACHE_WRITE_RETRY_DELAY_SEC = 60 * 3;
|
||||
|
@ -386,8 +382,6 @@ Experiments.Experiments.prototype = {
|
|||
this.updateManifest();
|
||||
} else if (data == PREF_BRANCH + PREF_ENABLED) {
|
||||
this._toggleExperimentsEnabled(gPrefs.getBoolPref(PREF_ENABLED, false));
|
||||
} else if (data == PREF_TELEMETRY_ENABLED) {
|
||||
this._telemetryStatusChanged();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -397,15 +391,13 @@ Experiments.Experiments.prototype = {
|
|||
this._shutdown = false;
|
||||
configureLogging();
|
||||
|
||||
gExperimentsEnabled = gPrefs.getBoolPref(PREF_ENABLED, false) && TelemetryUtils.isTelemetryEnabled;
|
||||
gExperimentsEnabled = gPrefs.getBoolPref(PREF_ENABLED, false) && Services.telemetry.canRecordExtended;
|
||||
this._log.trace("enabled=" + gExperimentsEnabled + ", " + this.enabled);
|
||||
|
||||
Services.prefs.addObserver(PREF_BRANCH + PREF_LOGGING, configureLogging);
|
||||
Services.prefs.addObserver(PREF_BRANCH + PREF_MANIFEST_URI, this, true);
|
||||
Services.prefs.addObserver(PREF_BRANCH + PREF_ENABLED, this, true);
|
||||
|
||||
Services.prefs.addObserver(PREF_TELEMETRY_ENABLED, this, true);
|
||||
|
||||
AddonManager.shutdown.addBlocker("Experiments.jsm shutdown",
|
||||
this.uninit.bind(this),
|
||||
this._getState.bind(this)
|
||||
|
@ -453,8 +445,6 @@ Experiments.Experiments.prototype = {
|
|||
Services.prefs.removeObserver(PREF_BRANCH + PREF_MANIFEST_URI, this);
|
||||
Services.prefs.removeObserver(PREF_BRANCH + PREF_ENABLED, this);
|
||||
|
||||
Services.prefs.removeObserver(PREF_TELEMETRY_ENABLED, this);
|
||||
|
||||
if (this._timer) {
|
||||
this._timer.clear();
|
||||
}
|
||||
|
@ -598,7 +588,7 @@ Experiments.Experiments.prototype = {
|
|||
async _toggleExperimentsEnabled(enabled) {
|
||||
this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
|
||||
let wasEnabled = gExperimentsEnabled;
|
||||
gExperimentsEnabled = enabled && TelemetryUtils.isTelemetryEnabled;
|
||||
gExperimentsEnabled = enabled && Services.telemetry.canRecordExtended;
|
||||
|
||||
if (wasEnabled == gExperimentsEnabled) {
|
||||
return;
|
||||
|
@ -614,10 +604,6 @@ Experiments.Experiments.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_telemetryStatusChanged() {
|
||||
this._toggleExperimentsEnabled(gPrefs.getBoolPref(PREF_ENABLED, false));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise that is resolved with an array of `ExperimentInfo` objects,
|
||||
* which provide info on the currently and recently active experiments.
|
||||
|
|
|
@ -26,7 +26,6 @@ const PREF_LOGGING_LEVEL = "experiments.logging.level";
|
|||
const PREF_LOGGING_DUMP = "experiments.logging.dump";
|
||||
const PREF_MANIFEST_URI = "experiments.manifest.uri";
|
||||
const PREF_FETCHINTERVAL = "experiments.manifest.fetchIntervalSeconds";
|
||||
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
|
||||
|
||||
function getExperimentPath(base) {
|
||||
let p = do_get_cwd();
|
||||
|
@ -193,6 +192,4 @@ function replaceExperiments(experiment, list) {
|
|||
});
|
||||
}
|
||||
|
||||
// Experiments require Telemetry to be enabled, and that's not true for debug
|
||||
// builds. Let's just enable it here instead of going through each test.
|
||||
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
|
|
|
@ -10,19 +10,11 @@ add_test(function test_experiments_activation() {
|
|||
loadAddonManager();
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
|
||||
Services.telemetry.canRecordExtended = false;
|
||||
|
||||
let experiments = Experiments.instance();
|
||||
|
||||
Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
|
||||
|
||||
// Patch updateManifest to not do anything when the pref is switched back to true,
|
||||
// otherwise it attempts to connect to the server.
|
||||
experiments.updateManifest = () => Promise.resolve();
|
||||
|
||||
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
|
||||
|
||||
Assert.ok(experiments.enabled, "Experiments must be re-enabled if Telemetry is re-enabled");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
|
|
@ -25,7 +25,6 @@ add_task(async function setup() {
|
|||
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.select_events.enabled", true], // We want select events to be fired.
|
||||
["toolkit.telemetry.enabled", true] // And Extended Telemetry to be enabled.
|
||||
]});
|
||||
|
||||
// Enable event recording for the events tested here.
|
||||
|
|
|
@ -27,9 +27,6 @@ add_task(async function setup() {
|
|||
let engineOneOff = Services.search.getEngineByName("MozSearch2");
|
||||
Services.search.moveEngine(engineOneOff, 0);
|
||||
|
||||
// Enable Extended Telemetry.
|
||||
await SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]});
|
||||
|
||||
// Enable event recording for the events tested here.
|
||||
Services.telemetry.setEventRecordingEnabled("navigation", true);
|
||||
|
||||
|
|
|
@ -90,9 +90,6 @@ add_task(async function setup() {
|
|||
let oldCanRecord = Services.telemetry.canRecordExtended;
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
|
||||
// Enable Extended Telemetry.
|
||||
await SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]});
|
||||
|
||||
// Enable event recording for the events tested here.
|
||||
Services.telemetry.setEventRecordingEnabled("navigation", true);
|
||||
|
||||
|
|
|
@ -89,9 +89,6 @@ add_task(async function setup() {
|
|||
// Enable the urlbar one-off buttons.
|
||||
Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true);
|
||||
|
||||
// Enable Extended Telemetry.
|
||||
await SpecialPowers.pushPrefEnv({"set": [["toolkit.telemetry.enabled", true]]});
|
||||
|
||||
// Enable local telemetry recording for the duration of the tests.
|
||||
let oldCanRecord = Services.telemetry.canRecordExtended;
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
|
|
|
@ -5,7 +5,7 @@ code, and optionally help with indentation.
|
|||
|
||||
# Upgrade
|
||||
|
||||
Currently used version is 5.30.0. To upgrade: download a new version of
|
||||
Currently used version is 5.31.0. To upgrade: download a new version of
|
||||
CodeMirror from the project's page [1] and replace all JavaScript and
|
||||
CSS files inside the codemirror directory [2].
|
||||
|
||||
|
|
|
@ -9,11 +9,6 @@
|
|||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var modes = ["clike", "css", "javascript"];
|
||||
|
||||
for (var i = 0; i < modes.length; ++i)
|
||||
CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "});
|
||||
|
||||
function continueComment(cm) {
|
||||
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
var ranges = cm.listSelections(), mode, inserts = [];
|
||||
|
@ -27,10 +22,10 @@
|
|||
var insert = null;
|
||||
if (mode.blockCommentStart && mode.blockCommentContinue) {
|
||||
var line = cm.getLine(pos.line).slice(0, pos.ch)
|
||||
var end = line.indexOf(mode.blockCommentEnd), found
|
||||
var end = line.lastIndexOf(mode.blockCommentEnd), found
|
||||
if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) {
|
||||
// Comment ended, don't continue it
|
||||
} else if ((found = line.indexOf(mode.blockCommentStart)) > -1) {
|
||||
} else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) {
|
||||
insert = line.slice(0, found)
|
||||
if (/\S/.test(insert)) {
|
||||
insert = ""
|
||||
|
|
|
@ -84,7 +84,8 @@
|
|||
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
cm.operation(function() {
|
||||
cm.replaceSelection("\n\n", null);
|
||||
var linesep = cm.lineSeparator() || "\n";
|
||||
cm.replaceSelection(linesep + linesep, null);
|
||||
cm.execCommand("goCharLeft");
|
||||
ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
var inQuote = eolState.quote !== 0;
|
||||
|
||||
var line = cm.getLine(pos.line), match = listRE.exec(line);
|
||||
if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
|
||||
var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
|
||||
if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
|
||||
cm.execCommand("newlineAndIndent");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1330,13 +1330,15 @@ var CodeMirror =
|
|||
if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
|
||||
}
|
||||
}
|
||||
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
||||
order[0].from = m[0].length
|
||||
order.unshift(new BidiSpan(0, 0, m[0].length))
|
||||
}
|
||||
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
||||
lst(order).to -= m[0].length
|
||||
order.push(new BidiSpan(0, len - m[0].length, len))
|
||||
if (direction == "ltr") {
|
||||
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
||||
order[0].from = m[0].length
|
||||
order.unshift(new BidiSpan(0, 0, m[0].length))
|
||||
}
|
||||
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
||||
lst(order).to -= m[0].length
|
||||
order.push(new BidiSpan(0, len - m[0].length, len))
|
||||
}
|
||||
}
|
||||
|
||||
return direction == "rtl" ? order.reverse() : order
|
||||
|
@ -1711,6 +1713,10 @@ var CodeMirror =
|
|||
var oracle = this.lineOracle
|
||||
return oracle && oracle.lookAhead(n)
|
||||
};
|
||||
StringStream.prototype.baseToken = function () {
|
||||
var oracle = this.lineOracle
|
||||
return oracle && oracle.baseToken(this.pos)
|
||||
};
|
||||
|
||||
var SavedContext = function(state, lookAhead) {
|
||||
this.state = state
|
||||
|
@ -1722,6 +1728,8 @@ var CodeMirror =
|
|||
this.doc = doc
|
||||
this.line = line
|
||||
this.maxLookAhead = lookAhead || 0
|
||||
this.baseTokens = null
|
||||
this.baseTokenPos = 1
|
||||
};
|
||||
|
||||
Context.prototype.lookAhead = function (n) {
|
||||
|
@ -1730,6 +1738,17 @@ var CodeMirror =
|
|||
return line
|
||||
};
|
||||
|
||||
Context.prototype.baseToken = function (n) {
|
||||
var this$1 = this;
|
||||
|
||||
if (!this.baseTokens) { return null }
|
||||
while (this.baseTokens[this.baseTokenPos] <= n)
|
||||
{ this$1.baseTokenPos += 2 }
|
||||
var type = this.baseTokens[this.baseTokenPos + 1]
|
||||
return {type: type && type.replace(/( |^)overlay .*/, ""),
|
||||
size: this.baseTokens[this.baseTokenPos] - n}
|
||||
};
|
||||
|
||||
Context.prototype.nextLine = function () {
|
||||
this.line++
|
||||
if (this.maxLookAhead > 0) { this.maxLookAhead-- }
|
||||
|
@ -1763,6 +1782,7 @@ var CodeMirror =
|
|||
|
||||
// Run overlays, adjust style array.
|
||||
var loop = function ( o ) {
|
||||
context.baseTokens = st
|
||||
var overlay = cm.state.overlays[o], i = 1, at = 0
|
||||
context.state = true
|
||||
runMode(cm, line.text, overlay.mode, context, function (end, style) {
|
||||
|
@ -1786,10 +1806,12 @@ var CodeMirror =
|
|||
}
|
||||
}
|
||||
}, lineClasses)
|
||||
context.state = state
|
||||
context.baseTokens = null
|
||||
context.baseTokenPos = 1
|
||||
};
|
||||
|
||||
for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
|
||||
context.state = state
|
||||
|
||||
return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
|
||||
}
|
||||
|
@ -3129,6 +3151,7 @@ var CodeMirror =
|
|||
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
|
||||
var begin = ref.begin;
|
||||
var end = ref.end;
|
||||
if (/\s/.test(lineObj.text.charAt(end - 1))) { end-- }
|
||||
var part = null, closestDist = null
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var p = order[i]
|
||||
|
@ -3319,6 +3342,7 @@ var CodeMirror =
|
|||
var fragment = document.createDocumentFragment()
|
||||
var padding = paddingH(cm.display), leftSide = padding.left
|
||||
var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
|
||||
var docLTR = doc.direction == "ltr"
|
||||
|
||||
function add(left, top, width, bottom) {
|
||||
if (top < 0) { top = 0 }
|
||||
|
@ -3335,42 +3359,43 @@ var CodeMirror =
|
|||
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
|
||||
}
|
||||
|
||||
function wrapX(pos, dir, side) {
|
||||
var extent = wrappedLineExtentChar(cm, lineObj, null, pos)
|
||||
var prop = (dir == "ltr") == (side == "after") ? "left" : "right"
|
||||
var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
|
||||
return coords(ch, prop)[prop]
|
||||
}
|
||||
|
||||
var order = getOrder(lineObj, doc.direction)
|
||||
iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
|
||||
var fromPos = coords(from, dir == "ltr" ? "left" : "right")
|
||||
var toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
|
||||
if (dir == "ltr") {
|
||||
var fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
|
||||
var toRight = toArg == null && to == lineLen ? rightSide : toPos.right
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
add(fromLeft, toPos.top, toRight - fromLeft, toPos.bottom)
|
||||
} else { // Multiple lines
|
||||
add(fromLeft, fromPos.top, null, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
add(leftSide, toPos.top, toPos.right, toPos.bottom)
|
||||
}
|
||||
} else if (from < to) { // RTL
|
||||
var fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
|
||||
var toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
|
||||
} else { // Multiple lines
|
||||
var topLeft = leftSide
|
||||
if (i) {
|
||||
var topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
|
||||
// The coordinates returned for an RTL wrapped space tend to
|
||||
// be complete bogus, so try to skip that here.
|
||||
topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
|
||||
}
|
||||
add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
var botWidth = null
|
||||
if (i < order.length - 1 || true) {
|
||||
var botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
|
||||
botWidth = coords(botStart, "right").right - toLeft
|
||||
}
|
||||
add(toLeft, toPos.top, botWidth, toPos.bottom)
|
||||
var ltr = dir == "ltr"
|
||||
var fromPos = coords(from, ltr ? "left" : "right")
|
||||
var toPos = coords(to - 1, ltr ? "right" : "left")
|
||||
|
||||
var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
|
||||
var first = i == 0, last = !order || i == order.length - 1
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
var openLeft = (docLTR ? openStart : openEnd) && first
|
||||
var openRight = (docLTR ? openEnd : openStart) && last
|
||||
var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
|
||||
var right = openRight ? rightSide : (ltr ? toPos : fromPos).right
|
||||
add(left, fromPos.top, right - left, fromPos.bottom)
|
||||
} else { // Multiple lines
|
||||
var topLeft, topRight, botLeft, botRight
|
||||
if (ltr) {
|
||||
topLeft = docLTR && openStart && first ? leftSide : fromPos.left
|
||||
topRight = docLTR ? rightSide : wrapX(from, dir, "before")
|
||||
botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
|
||||
botRight = docLTR && openEnd && last ? rightSide : toPos.right
|
||||
} else {
|
||||
topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
|
||||
topRight = !docLTR && openStart && first ? rightSide : fromPos.right
|
||||
botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
|
||||
botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
|
||||
}
|
||||
add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
|
||||
}
|
||||
|
||||
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
|
||||
|
@ -6995,7 +7020,7 @@ var CodeMirror =
|
|||
// Thus, in rtl, we are looking for the first (content-order) character
|
||||
// in the rtl chunk that is on the last line (that is, the same line
|
||||
// as the last (content-order) character).
|
||||
if (part.level > 0) {
|
||||
if (part.level > 0 || cm.doc.direction == "rtl") {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
|
@ -9857,7 +9882,7 @@ var CodeMirror =
|
|||
|
||||
addLegacyProps(CodeMirror)
|
||||
|
||||
CodeMirror.version = "5.30.0"
|
||||
CodeMirror.version = "5.31.0"
|
||||
|
||||
return CodeMirror;
|
||||
|
||||
|
@ -10667,7 +10692,8 @@ var CodeMirror =
|
|||
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
cm.operation(function() {
|
||||
cm.replaceSelection("\n\n", null);
|
||||
var linesep = cm.lineSeparator() || "\n";
|
||||
cm.replaceSelection(linesep + linesep, null);
|
||||
cm.execCommand("goCharLeft");
|
||||
ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -11038,13 +11064,13 @@ var CodeMirror =
|
|||
|
||||
var keywords = function(){
|
||||
function kw(type) {return {type: type, style: "keyword"};}
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
|
||||
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
||||
|
||||
var jsKeywords = {
|
||||
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": C,
|
||||
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
|
||||
"debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
"in": operator, "typeof": operator, "instanceof": operator,
|
||||
|
@ -11143,7 +11169,7 @@ var CodeMirror =
|
|||
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
|
||||
return ret("regexp", "string-2");
|
||||
} else {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
stream.eat("=");
|
||||
return ret("operator", "operator", stream.current());
|
||||
}
|
||||
} else if (ch == "`") {
|
||||
|
@ -11153,8 +11179,14 @@ var CodeMirror =
|
|||
stream.skipToEnd();
|
||||
return ret("error", "error");
|
||||
} else if (isOperatorChar.test(ch)) {
|
||||
if (ch != ">" || !state.lexical || state.lexical.type != ">")
|
||||
stream.eatWhile(isOperatorChar);
|
||||
if (ch != ">" || !state.lexical || state.lexical.type != ">") {
|
||||
if (stream.eat("=")) {
|
||||
if (ch == "!" || ch == "=") stream.eat("=")
|
||||
} else if (/[<>*+\-]/.test(ch)) {
|
||||
stream.eat(ch)
|
||||
if (ch == ">") stream.eat(ch)
|
||||
}
|
||||
}
|
||||
return ret("operator", "operator", stream.current());
|
||||
} else if (wordRE.test(ch)) {
|
||||
stream.eatWhile(wordRE);
|
||||
|
@ -11366,6 +11398,8 @@ var CodeMirror =
|
|||
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
|
||||
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
|
||||
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
||||
if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
|
||||
if (type == "debugger") return cont(expect(";"));
|
||||
if (type == "{") return cont(pushlex("}"), block, poplex);
|
||||
if (type == ";") return cont();
|
||||
if (type == "if") {
|
||||
|
@ -11421,7 +11455,7 @@ var CodeMirror =
|
|||
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
|
||||
if (type == "function") return cont(functiondef, maybeop);
|
||||
if (type == "class") return cont(pushlex("form"), classExpression, poplex);
|
||||
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
|
||||
if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
|
||||
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
|
||||
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
|
||||
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
|
||||
|
@ -11434,10 +11468,6 @@ var CodeMirror =
|
|||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expression);
|
||||
}
|
||||
function maybeexpressionNoComma(type) {
|
||||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expressionNoComma);
|
||||
}
|
||||
|
||||
function maybeoperatorComma(type, value) {
|
||||
if (type == ",") return cont(expression);
|
||||
|
@ -11525,7 +11555,10 @@ var CodeMirror =
|
|||
} else if (type == "[") {
|
||||
return cont(expression, expect("]"), afterprop);
|
||||
} else if (type == "spread") {
|
||||
return cont(expression, afterprop);
|
||||
return cont(expressionNoComma, afterprop);
|
||||
} else if (value == "*") {
|
||||
cx.marked = "keyword";
|
||||
return cont(objprop);
|
||||
} else if (type == ":") {
|
||||
return pass(afterprop)
|
||||
}
|
||||
|
@ -11573,7 +11606,7 @@ var CodeMirror =
|
|||
}
|
||||
}
|
||||
function typeexpr(type, value) {
|
||||
if (type == "variable") {
|
||||
if (type == "variable" || value == "void") {
|
||||
if (value == "keyof") {
|
||||
cx.marked = "keyword"
|
||||
return cont(typeexpr)
|
||||
|
@ -11760,7 +11793,7 @@ var CodeMirror =
|
|||
|
||||
function expressionAllowed(stream, state, backUp) {
|
||||
return state.tokenize == tokenBase &&
|
||||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
|
||||
}
|
||||
|
||||
|
@ -11829,6 +11862,7 @@ var CodeMirror =
|
|||
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
|
||||
blockCommentStart: jsonMode ? null : "/*",
|
||||
blockCommentEnd: jsonMode ? null : "*/",
|
||||
blockCommentContinue: jsonMode ? null : " * ",
|
||||
lineComment: jsonMode ? null : "//",
|
||||
fold: "brace",
|
||||
closeBrackets: "()[]{}''\"\"``",
|
||||
|
@ -12678,6 +12712,7 @@ var CodeMirror =
|
|||
electricChars: "}",
|
||||
blockCommentStart: "/*",
|
||||
blockCommentEnd: "*/",
|
||||
blockCommentContinue: " * ",
|
||||
lineComment: lineComment,
|
||||
fold: "brace"
|
||||
};
|
||||
|
@ -14237,6 +14272,7 @@ var CodeMirror =
|
|||
electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
|
||||
blockCommentStart: "/*",
|
||||
blockCommentEnd: "*/",
|
||||
blockCommentContinue: " * ",
|
||||
lineComment: "//",
|
||||
fold: "brace"
|
||||
};
|
||||
|
@ -14584,7 +14620,7 @@ var CodeMirror =
|
|||
multiLineStrings: true,
|
||||
number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
|
||||
blockKeywords: words("catch class do else finally for if where try while enum"),
|
||||
defKeywords: words("class val var object package interface fun"),
|
||||
defKeywords: words("class val var object interface fun"),
|
||||
atoms: words("true false null this"),
|
||||
hooks: {
|
||||
'"': function(stream, state) {
|
||||
|
@ -15785,20 +15821,67 @@ var CodeMirror =
|
|||
}
|
||||
|
||||
function detachVimMap(cm, next) {
|
||||
if (this == CodeMirror.keyMap.vim)
|
||||
if (this == CodeMirror.keyMap.vim) {
|
||||
CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
|
||||
if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
|
||||
disableFatCursorMark(cm);
|
||||
cm.getInputField().style.caretColor = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (!next || next.attach != attachVimMap)
|
||||
leaveVimMode(cm);
|
||||
}
|
||||
function attachVimMap(cm, prev) {
|
||||
if (this == CodeMirror.keyMap.vim)
|
||||
if (this == CodeMirror.keyMap.vim) {
|
||||
CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
|
||||
if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
|
||||
enableFatCursorMark(cm);
|
||||
cm.getInputField().style.caretColor = "transparent";
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev || prev.attach != attachVimMap)
|
||||
enterVimMode(cm);
|
||||
}
|
||||
|
||||
function fatCursorMarks(cm) {
|
||||
var ranges = cm.listSelections(), result = []
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i]
|
||||
if (range.empty()) {
|
||||
if (range.anchor.ch < cm.getLine(range.anchor.line).length) {
|
||||
result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1),
|
||||
{className: "cm-fat-cursor-mark"}))
|
||||
} else {
|
||||
var widget = document.createElement("span")
|
||||
widget.textContent = "\u00a0"
|
||||
widget.className = "cm-fat-cursor-mark"
|
||||
result.push(cm.setBookmark(range.anchor, {widget: widget}))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function updateFatCursorMark(cm) {
|
||||
var marks = cm.state.fatCursorMarks
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
|
||||
cm.state.fatCursorMarks = fatCursorMarks(cm)
|
||||
}
|
||||
|
||||
function enableFatCursorMark(cm) {
|
||||
cm.state.fatCursorMarks = fatCursorMarks(cm)
|
||||
cm.on("cursorActivity", updateFatCursorMark)
|
||||
}
|
||||
|
||||
function disableFatCursorMark(cm) {
|
||||
var marks = cm.state.fatCursorMarks
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
|
||||
cm.state.fatCursorMarks = null
|
||||
cm.off("cursorActivity", updateFatCursorMark)
|
||||
}
|
||||
|
||||
// Deprecated, simply setting the keymap works again.
|
||||
CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
|
||||
if (val && cm.getOption("keyMap") != "vim")
|
||||
|
@ -17564,7 +17647,8 @@ var CodeMirror =
|
|||
vimGlobalState.registerController.pushText(
|
||||
args.registerName, 'delete', text,
|
||||
args.linewise, vim.visualBlock);
|
||||
return clipCursorToContent(cm, finalHead);
|
||||
var includeLineBreak = vim.insertMode
|
||||
return clipCursorToContent(cm, finalHead, includeLineBreak);
|
||||
},
|
||||
indent: function(cm, args, ranges) {
|
||||
var vim = cm.state.vim;
|
||||
|
@ -18774,7 +18858,7 @@ var CodeMirror =
|
|||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns the boundaries of the next word. If the cursor in the middle of
|
||||
* the word, then returns the boundaries of the current word, starting at
|
||||
* the cursor. If the cursor is at the start/end of a word, and we are going
|
||||
|
@ -19508,6 +19592,15 @@ var CodeMirror =
|
|||
var history = cm.doc.history.done;
|
||||
var event = history[history.length - 2];
|
||||
return event && event.ranges && event.ranges[0].head;
|
||||
} else if (markName == '.') {
|
||||
if (cm.doc.history.lastModTime == 0) {
|
||||
return // If no changes, bail out; don't bother to copy or reverse history array.
|
||||
} else {
|
||||
var changeHistory = cm.doc.history.done.filter(function(el){ if (el.changes !== undefined) { return el } });
|
||||
changeHistory.reverse();
|
||||
var lastEditPos = changeHistory[0].changes[0].to;
|
||||
}
|
||||
return lastEditPos;
|
||||
}
|
||||
|
||||
var mark = vim.marks[markName];
|
||||
|
@ -20318,7 +20411,8 @@ var CodeMirror =
|
|||
// so as to update the ". register as expected in real vim.
|
||||
var text = [];
|
||||
if (!isPlaying) {
|
||||
var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
|
||||
var selLength = lastChange.inVisualBlock && vim.lastSelection ?
|
||||
vim.lastSelection.visualBlock.height : 1;
|
||||
var changes = lastChange.changes;
|
||||
var text = [];
|
||||
var i = 0;
|
||||
|
@ -20704,11 +20798,8 @@ var CodeMirror =
|
|||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var map = CodeMirror.keyMap.sublime = {fallthrough: "default"};
|
||||
var cmds = CodeMirror.commands;
|
||||
var Pos = CodeMirror.Pos;
|
||||
var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
|
||||
var ctrl = mac ? "Cmd-" : "Ctrl-";
|
||||
|
||||
// This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
|
||||
function findPosSubword(doc, start, dir) {
|
||||
|
@ -20742,16 +20833,10 @@ var CodeMirror =
|
|||
});
|
||||
}
|
||||
|
||||
var goSubwordCombo = mac ? "Ctrl-" : "Alt-";
|
||||
cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
|
||||
cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
|
||||
|
||||
cmds[map[goSubwordCombo + "Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); };
|
||||
cmds[map[goSubwordCombo + "Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); };
|
||||
|
||||
if (mac) map["Cmd-Left"] = "goLineStartSmart";
|
||||
|
||||
var scrollLineCombo = mac ? "Ctrl-Alt-" : "Ctrl-";
|
||||
|
||||
cmds[map[scrollLineCombo + "Up"] = "scrollLineUp"] = function(cm) {
|
||||
cmds.scrollLineUp = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
|
||||
|
@ -20760,7 +20845,7 @@ var CodeMirror =
|
|||
}
|
||||
cm.scrollTo(null, info.top - cm.defaultTextHeight());
|
||||
};
|
||||
cmds[map[scrollLineCombo + "Down"] = "scrollLineDown"] = function(cm) {
|
||||
cmds.scrollLineDown = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
|
||||
|
@ -20770,7 +20855,7 @@ var CodeMirror =
|
|||
cm.scrollTo(null, info.top + cm.defaultTextHeight());
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "L"] = "splitSelectionByLine"] = function(cm) {
|
||||
cmds.splitSelectionByLine = function(cm) {
|
||||
var ranges = cm.listSelections(), lineRanges = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
|
@ -20782,14 +20867,12 @@ var CodeMirror =
|
|||
cm.setSelections(lineRanges, 0);
|
||||
};
|
||||
|
||||
map["Shift-Tab"] = "indentLess";
|
||||
|
||||
cmds[map["Esc"] = "singleSelectionTop"] = function(cm) {
|
||||
cmds.singleSelectionTop = function(cm) {
|
||||
var range = cm.listSelections()[0];
|
||||
cm.setSelection(range.anchor, range.head, {scroll: false});
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "L"] = "selectLine"] = function(cm) {
|
||||
cmds.selectLine = function(cm) {
|
||||
var ranges = cm.listSelections(), extended = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
|
@ -20799,8 +20882,6 @@ var CodeMirror =
|
|||
cm.setSelections(extended);
|
||||
};
|
||||
|
||||
map["Shift-Ctrl-K"] = "deleteLine";
|
||||
|
||||
function insertLine(cm, above) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
cm.operation(function() {
|
||||
|
@ -20819,9 +20900,9 @@ var CodeMirror =
|
|||
cm.execCommand("indentAuto");
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { return insertLine(cm, false); };
|
||||
cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
|
||||
|
||||
cmds[map["Shift-" + ctrl + "Enter"] = "insertLineBefore"] = function(cm) { return insertLine(cm, true); };
|
||||
cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
|
||||
|
||||
function wordAt(cm, pos) {
|
||||
var start = pos.ch, end = start, line = cm.getLine(pos.line);
|
||||
|
@ -20830,7 +20911,7 @@ var CodeMirror =
|
|||
return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "D"] = "selectNextOccurrence"] = function(cm) {
|
||||
cmds.selectNextOccurrence = function(cm) {
|
||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||
var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
|
||||
if (CodeMirror.cmpPos(from, to) == 0) {
|
||||
|
@ -20867,10 +20948,8 @@ var CodeMirror =
|
|||
}
|
||||
cm.setSelections(newRanges);
|
||||
}
|
||||
|
||||
var addCursorToLineCombo = mac ? "Shift-Cmd" : 'Alt-Ctrl';
|
||||
cmds[map[addCursorToLineCombo + "Up"] = "addCursorToPrevLine"] = function(cm) { addCursorToSelection(cm, -1); };
|
||||
cmds[map[addCursorToLineCombo + "Down"] = "addCursorToNextLine"] = function(cm) { addCursorToSelection(cm, 1); };
|
||||
cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
|
||||
cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
|
||||
|
||||
function isSelectedRange(ranges, from, to) {
|
||||
for (var i = 0; i < ranges.length; i++)
|
||||
|
@ -20899,14 +20978,14 @@ var CodeMirror =
|
|||
return true;
|
||||
}
|
||||
|
||||
cmds[map["Shift-" + ctrl + "Space"] = "selectScope"] = function(cm) {
|
||||
cmds.selectScope = function(cm) {
|
||||
selectBetweenBrackets(cm) || cm.execCommand("selectAll");
|
||||
};
|
||||
cmds[map["Shift-" + ctrl + "M"] = "selectBetweenBrackets"] = function(cm) {
|
||||
cmds.selectBetweenBrackets = function(cm) {
|
||||
if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "M"] = "goToBracket"] = function(cm) {
|
||||
cmds.goToBracket = function(cm) {
|
||||
cm.extendSelectionsBy(function(range) {
|
||||
var next = cm.scanForBracket(range.head, 1);
|
||||
if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
|
||||
|
@ -20915,9 +20994,7 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
var swapLineCombo = mac ? "Cmd-Ctrl-" : "Shift-Ctrl-";
|
||||
|
||||
cmds[map[swapLineCombo + "Up"] = "swapLineUp"] = function(cm) {
|
||||
cmds.swapLineUp = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -20944,7 +21021,7 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[swapLineCombo + "Down"] = "swapLineDown"] = function(cm) {
|
||||
cmds.swapLineDown = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
|
@ -20968,11 +21045,11 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "/"] = "toggleCommentIndented"] = function(cm) {
|
||||
cmds.toggleCommentIndented = function(cm) {
|
||||
cm.toggleComment({ indent: true });
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "J"] = "joinLines"] = function(cm) {
|
||||
cmds.joinLines = function(cm) {
|
||||
var ranges = cm.listSelections(), joined = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], from = range.from();
|
||||
|
@ -21000,7 +21077,7 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "D"] = "duplicateLine"] = function(cm) {
|
||||
cmds.duplicateLine = function(cm) {
|
||||
cm.operation(function() {
|
||||
var rangeCount = cm.listSelections().length;
|
||||
for (var i = 0; i < rangeCount; i++) {
|
||||
|
@ -21014,7 +21091,6 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
if (!mac) map[ctrl + "T"] = "transposeChars";
|
||||
|
||||
function sortLines(cm, caseSensitive) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
|
@ -21052,10 +21128,10 @@ var CodeMirror =
|
|||
});
|
||||
}
|
||||
|
||||
cmds[map["F9"] = "sortLines"] = function(cm) { sortLines(cm, true); };
|
||||
cmds[map[ctrl + "F9"] = "sortLinesInsensitive"] = function(cm) { sortLines(cm, false); };
|
||||
cmds.sortLines = function(cm) { sortLines(cm, true); };
|
||||
cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
|
||||
|
||||
cmds[map["F2"] = "nextBookmark"] = function(cm) {
|
||||
cmds.nextBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
var current = marks.shift();
|
||||
|
@ -21067,7 +21143,7 @@ var CodeMirror =
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map["Shift-F2"] = "prevBookmark"] = function(cm) {
|
||||
cmds.prevBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
marks.unshift(marks.pop());
|
||||
|
@ -21079,7 +21155,7 @@ var CodeMirror =
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "F2"] = "toggleBookmark"] = function(cm) {
|
||||
cmds.toggleBookmark = function(cm) {
|
||||
var ranges = cm.listSelections();
|
||||
var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -21099,13 +21175,13 @@ var CodeMirror =
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "F2"] = "clearBookmarks"] = function(cm) {
|
||||
cmds.clearBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||
marks.length = 0;
|
||||
};
|
||||
|
||||
cmds[map["Alt-F2"] = "selectBookmarks"] = function(cm) {
|
||||
cmds.selectBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks, ranges = [];
|
||||
if (marks) for (var i = 0; i < marks.length; i++) {
|
||||
var found = marks[i].find();
|
||||
|
@ -21118,10 +21194,6 @@ var CodeMirror =
|
|||
cm.setSelections(ranges, 0);
|
||||
};
|
||||
|
||||
map["Alt-Q"] = "wrapLines";
|
||||
|
||||
var cK = ctrl + "K ";
|
||||
|
||||
function modifyWordOrSelection(cm, mod) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections(), indices = [], replacements = [];
|
||||
|
@ -21141,9 +21213,7 @@ var CodeMirror =
|
|||
});
|
||||
}
|
||||
|
||||
map[cK + ctrl + "Backspace"] = "delLineLeft";
|
||||
|
||||
cmds[map["Backspace"] = "smartBackspace"] = function(cm) {
|
||||
cmds.smartBackspace = function(cm) {
|
||||
if (cm.somethingSelected()) return CodeMirror.Pass;
|
||||
|
||||
cm.operation(function() {
|
||||
|
@ -21171,7 +21241,7 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
|
||||
cmds.delLineRight = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = ranges.length - 1; i >= 0; i--)
|
||||
|
@ -21180,22 +21250,22 @@ var CodeMirror =
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) {
|
||||
cmds.upcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
|
||||
};
|
||||
cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) {
|
||||
cmds.downcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) {
|
||||
cmds.setSublimeMark = function(cm) {
|
||||
if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
|
||||
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
||||
};
|
||||
cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) {
|
||||
cmds.selectToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) cm.setSelection(cm.getCursor(), found);
|
||||
};
|
||||
cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) {
|
||||
cmds.deleteToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
var from = cm.getCursor(), to = found;
|
||||
|
@ -21204,7 +21274,7 @@ var CodeMirror =
|
|||
cm.replaceRange("", from, to);
|
||||
}
|
||||
};
|
||||
cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) {
|
||||
cmds.swapWithSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
cm.state.sublimeMark.clear();
|
||||
|
@ -21212,19 +21282,17 @@ var CodeMirror =
|
|||
cm.setCursor(found);
|
||||
}
|
||||
};
|
||||
cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) {
|
||||
cmds.sublimeYank = function(cm) {
|
||||
if (cm.state.sublimeKilled != null)
|
||||
cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
|
||||
};
|
||||
|
||||
map[cK + ctrl + "G"] = "clearBookmarks";
|
||||
cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) {
|
||||
cmds.showInCenter = function(cm) {
|
||||
var pos = cm.cursorCoords(null, "local");
|
||||
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
|
||||
};
|
||||
|
||||
var selectLinesCombo = mac ? "Ctrl-Shift-" : "Ctrl-Alt-";
|
||||
cmds[map[selectLinesCombo + "Up"] = "selectLinesUpward"] = function(cm) {
|
||||
cmds.selectLinesUpward = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -21234,7 +21302,7 @@ var CodeMirror =
|
|||
}
|
||||
});
|
||||
};
|
||||
cmds[map[selectLinesCombo + "Down"] = "selectLinesDownward"] = function(cm) {
|
||||
cmds.selectLinesDownward = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -21273,9 +21341,9 @@ var CodeMirror =
|
|||
cm.setSelection(target.from, target.to);
|
||||
}
|
||||
};
|
||||
cmds[map[ctrl + "F3"] = "findUnder"] = function(cm) { findAndGoTo(cm, true); };
|
||||
cmds[map["Shift-" + ctrl + "F3"] = "findUnderPrevious"] = function(cm) { findAndGoTo(cm,false); };
|
||||
cmds[map["Alt-F3"] = "findAllUnder"] = function(cm) {
|
||||
cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
|
||||
cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
|
||||
cmds.findAllUnder = function(cm) {
|
||||
var target = getTarget(cm);
|
||||
if (!target) return;
|
||||
var cur = cm.getSearchCursor(target.query);
|
||||
|
@ -21289,17 +21357,134 @@ var CodeMirror =
|
|||
cm.setSelections(matches, primaryIndex);
|
||||
};
|
||||
|
||||
map["Shift-" + ctrl + "["] = "fold";
|
||||
map["Shift-" + ctrl + "]"] = "unfold";
|
||||
map[cK + ctrl + "0"] = map[cK + ctrl + "J"] = "unfoldAll";
|
||||
|
||||
map[ctrl + "I"] = "findIncremental";
|
||||
map["Shift-" + ctrl + "I"] = "findIncrementalReverse";
|
||||
map[ctrl + "H"] = "replace";
|
||||
map["F3"] = "findNext";
|
||||
map["Shift-F3"] = "findPrev";
|
||||
var keyMap = CodeMirror.keyMap;
|
||||
keyMap.macSublime = {
|
||||
"Cmd-Left": "goLineStartSmart",
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-Left": "goSubwordLeft",
|
||||
"Ctrl-Right": "goSubwordRight",
|
||||
"Ctrl-Alt-Up": "scrollLineUp",
|
||||
"Ctrl-Alt-Down": "scrollLineDown",
|
||||
"Cmd-L": "selectLine",
|
||||
"Shift-Cmd-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Cmd-Enter": "insertLineAfter",
|
||||
"Shift-Cmd-Enter": "insertLineBefore",
|
||||
"Cmd-D": "selectNextOccurrence",
|
||||
"Shift-Cmd-Up": "addCursorToPrevLine",
|
||||
"Shift-Cmd-Down": "addCursorToNextLine",
|
||||
"Shift-Cmd-Space": "selectScope",
|
||||
"Shift-Cmd-M": "selectBetweenBrackets",
|
||||
"Cmd-M": "goToBracket",
|
||||
"Cmd-Ctrl-Up": "swapLineUp",
|
||||
"Cmd-Ctrl-Down": "swapLineDown",
|
||||
"Cmd-/": "toggleCommentIndented",
|
||||
"Cmd-J": "joinLines",
|
||||
"Shift-Cmd-D": "duplicateLine",
|
||||
"F9": "sortLines",
|
||||
"Cmd-F9": "sortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Cmd-F2": "toggleBookmark",
|
||||
"Shift-Cmd-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Cmd-K Cmd-K": "delLineRight",
|
||||
"Cmd-K Cmd-U": "upcaseAtCursor",
|
||||
"Cmd-K Cmd-L": "downcaseAtCursor",
|
||||
"Cmd-K Cmd-Space": "setSublimeMark",
|
||||
"Cmd-K Cmd-A": "selectToSublimeMark",
|
||||
"Cmd-K Cmd-W": "deleteToSublimeMark",
|
||||
"Cmd-K Cmd-X": "swapWithSublimeMark",
|
||||
"Cmd-K Cmd-Y": "sublimeYank",
|
||||
"Cmd-K Cmd-C": "showInCenter",
|
||||
"Cmd-K Cmd-G": "clearBookmarks",
|
||||
"Cmd-K Cmd-Backspace": "delLineLeft",
|
||||
"Cmd-K Cmd-0": "unfoldAll",
|
||||
"Cmd-K Cmd-J": "unfoldAll",
|
||||
"Ctrl-Shift-Up": "selectLinesUpward",
|
||||
"Ctrl-Shift-Down": "selectLinesDownward",
|
||||
"Cmd-F3": "findUnder",
|
||||
"Shift-Cmd-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Cmd-[": "fold",
|
||||
"Shift-Cmd-]": "unfold",
|
||||
"Cmd-I": "findIncremental",
|
||||
"Shift-Cmd-I": "findIncrementalReverse",
|
||||
"Cmd-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "macDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.macSublime);
|
||||
|
||||
CodeMirror.normalizeKeyMap(map);
|
||||
keyMap.pcSublime = {
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-T": "transposeChars",
|
||||
"Alt-Left": "goSubwordLeft",
|
||||
"Alt-Right": "goSubwordRight",
|
||||
"Ctrl-Up": "scrollLineUp",
|
||||
"Ctrl-Down": "scrollLineDown",
|
||||
"Ctrl-L": "selectLine",
|
||||
"Shift-Ctrl-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Ctrl-Enter": "insertLineAfter",
|
||||
"Shift-Ctrl-Enter": "insertLineBefore",
|
||||
"Ctrl-D": "selectNextOccurrence",
|
||||
"Alt-CtrlUp": "addCursorToPrevLine",
|
||||
"Alt-CtrlDown": "addCursorToNextLine",
|
||||
"Shift-Ctrl-Space": "selectScope",
|
||||
"Shift-Ctrl-M": "selectBetweenBrackets",
|
||||
"Ctrl-M": "goToBracket",
|
||||
"Shift-Ctrl-Up": "swapLineUp",
|
||||
"Shift-Ctrl-Down": "swapLineDown",
|
||||
"Ctrl-/": "toggleCommentIndented",
|
||||
"Ctrl-J": "joinLines",
|
||||
"Shift-Ctrl-D": "duplicateLine",
|
||||
"F9": "sortLines",
|
||||
"Ctrl-F9": "sortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Ctrl-F2": "toggleBookmark",
|
||||
"Shift-Ctrl-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Ctrl-K Ctrl-K": "delLineRight",
|
||||
"Ctrl-K Ctrl-U": "upcaseAtCursor",
|
||||
"Ctrl-K Ctrl-L": "downcaseAtCursor",
|
||||
"Ctrl-K Ctrl-Space": "setSublimeMark",
|
||||
"Ctrl-K Ctrl-A": "selectToSublimeMark",
|
||||
"Ctrl-K Ctrl-W": "deleteToSublimeMark",
|
||||
"Ctrl-K Ctrl-X": "swapWithSublimeMark",
|
||||
"Ctrl-K Ctrl-Y": "sublimeYank",
|
||||
"Ctrl-K Ctrl-C": "showInCenter",
|
||||
"Ctrl-K Ctrl-G": "clearBookmarks",
|
||||
"Ctrl-K Ctrl-Backspace": "delLineLeft",
|
||||
"Ctrl-K Ctrl-0": "unfoldAll",
|
||||
"Ctrl-K Ctrl-J": "unfoldAll",
|
||||
"Ctrl-Alt-Up": "selectLinesUpward",
|
||||
"Ctrl-Alt-Down": "selectLinesDownward",
|
||||
"Ctrl-F3": "findUnder",
|
||||
"Shift-Ctrl-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Ctrl-[": "fold",
|
||||
"Shift-Ctrl-]": "unfold",
|
||||
"Ctrl-I": "findIncremental",
|
||||
"Shift-Ctrl-I": "findIncrementalReverse",
|
||||
"Ctrl-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "pcDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.pcSublime);
|
||||
|
||||
var mac = keyMap.default == keyMap.macDefault;
|
||||
keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -14,11 +14,8 @@
|
|||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var map = CodeMirror.keyMap.sublime = {fallthrough: "default"};
|
||||
var cmds = CodeMirror.commands;
|
||||
var Pos = CodeMirror.Pos;
|
||||
var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
|
||||
var ctrl = mac ? "Cmd-" : "Ctrl-";
|
||||
|
||||
// This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
|
||||
function findPosSubword(doc, start, dir) {
|
||||
|
@ -52,16 +49,10 @@
|
|||
});
|
||||
}
|
||||
|
||||
var goSubwordCombo = mac ? "Ctrl-" : "Alt-";
|
||||
cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
|
||||
cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
|
||||
|
||||
cmds[map[goSubwordCombo + "Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); };
|
||||
cmds[map[goSubwordCombo + "Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); };
|
||||
|
||||
if (mac) map["Cmd-Left"] = "goLineStartSmart";
|
||||
|
||||
var scrollLineCombo = mac ? "Ctrl-Alt-" : "Ctrl-";
|
||||
|
||||
cmds[map[scrollLineCombo + "Up"] = "scrollLineUp"] = function(cm) {
|
||||
cmds.scrollLineUp = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
|
||||
|
@ -70,7 +61,7 @@
|
|||
}
|
||||
cm.scrollTo(null, info.top - cm.defaultTextHeight());
|
||||
};
|
||||
cmds[map[scrollLineCombo + "Down"] = "scrollLineDown"] = function(cm) {
|
||||
cmds.scrollLineDown = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
|
||||
|
@ -80,7 +71,7 @@
|
|||
cm.scrollTo(null, info.top + cm.defaultTextHeight());
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "L"] = "splitSelectionByLine"] = function(cm) {
|
||||
cmds.splitSelectionByLine = function(cm) {
|
||||
var ranges = cm.listSelections(), lineRanges = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
|
@ -92,14 +83,12 @@
|
|||
cm.setSelections(lineRanges, 0);
|
||||
};
|
||||
|
||||
map["Shift-Tab"] = "indentLess";
|
||||
|
||||
cmds[map["Esc"] = "singleSelectionTop"] = function(cm) {
|
||||
cmds.singleSelectionTop = function(cm) {
|
||||
var range = cm.listSelections()[0];
|
||||
cm.setSelection(range.anchor, range.head, {scroll: false});
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "L"] = "selectLine"] = function(cm) {
|
||||
cmds.selectLine = function(cm) {
|
||||
var ranges = cm.listSelections(), extended = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
|
@ -109,8 +98,6 @@
|
|||
cm.setSelections(extended);
|
||||
};
|
||||
|
||||
map["Shift-Ctrl-K"] = "deleteLine";
|
||||
|
||||
function insertLine(cm, above) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
cm.operation(function() {
|
||||
|
@ -129,9 +116,9 @@
|
|||
cm.execCommand("indentAuto");
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { return insertLine(cm, false); };
|
||||
cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
|
||||
|
||||
cmds[map["Shift-" + ctrl + "Enter"] = "insertLineBefore"] = function(cm) { return insertLine(cm, true); };
|
||||
cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
|
||||
|
||||
function wordAt(cm, pos) {
|
||||
var start = pos.ch, end = start, line = cm.getLine(pos.line);
|
||||
|
@ -140,7 +127,7 @@
|
|||
return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "D"] = "selectNextOccurrence"] = function(cm) {
|
||||
cmds.selectNextOccurrence = function(cm) {
|
||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||
var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
|
||||
if (CodeMirror.cmpPos(from, to) == 0) {
|
||||
|
@ -177,10 +164,8 @@
|
|||
}
|
||||
cm.setSelections(newRanges);
|
||||
}
|
||||
|
||||
var addCursorToLineCombo = mac ? "Shift-Cmd" : 'Alt-Ctrl';
|
||||
cmds[map[addCursorToLineCombo + "Up"] = "addCursorToPrevLine"] = function(cm) { addCursorToSelection(cm, -1); };
|
||||
cmds[map[addCursorToLineCombo + "Down"] = "addCursorToNextLine"] = function(cm) { addCursorToSelection(cm, 1); };
|
||||
cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
|
||||
cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
|
||||
|
||||
function isSelectedRange(ranges, from, to) {
|
||||
for (var i = 0; i < ranges.length; i++)
|
||||
|
@ -209,14 +194,14 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
cmds[map["Shift-" + ctrl + "Space"] = "selectScope"] = function(cm) {
|
||||
cmds.selectScope = function(cm) {
|
||||
selectBetweenBrackets(cm) || cm.execCommand("selectAll");
|
||||
};
|
||||
cmds[map["Shift-" + ctrl + "M"] = "selectBetweenBrackets"] = function(cm) {
|
||||
cmds.selectBetweenBrackets = function(cm) {
|
||||
if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "M"] = "goToBracket"] = function(cm) {
|
||||
cmds.goToBracket = function(cm) {
|
||||
cm.extendSelectionsBy(function(range) {
|
||||
var next = cm.scanForBracket(range.head, 1);
|
||||
if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
|
||||
|
@ -225,9 +210,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
var swapLineCombo = mac ? "Cmd-Ctrl-" : "Shift-Ctrl-";
|
||||
|
||||
cmds[map[swapLineCombo + "Up"] = "swapLineUp"] = function(cm) {
|
||||
cmds.swapLineUp = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -254,7 +237,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[swapLineCombo + "Down"] = "swapLineDown"] = function(cm) {
|
||||
cmds.swapLineDown = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
|
@ -278,11 +261,11 @@
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "/"] = "toggleCommentIndented"] = function(cm) {
|
||||
cmds.toggleCommentIndented = function(cm) {
|
||||
cm.toggleComment({ indent: true });
|
||||
}
|
||||
|
||||
cmds[map[ctrl + "J"] = "joinLines"] = function(cm) {
|
||||
cmds.joinLines = function(cm) {
|
||||
var ranges = cm.listSelections(), joined = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], from = range.from();
|
||||
|
@ -310,7 +293,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "D"] = "duplicateLine"] = function(cm) {
|
||||
cmds.duplicateLine = function(cm) {
|
||||
cm.operation(function() {
|
||||
var rangeCount = cm.listSelections().length;
|
||||
for (var i = 0; i < rangeCount; i++) {
|
||||
|
@ -324,7 +307,6 @@
|
|||
});
|
||||
};
|
||||
|
||||
if (!mac) map[ctrl + "T"] = "transposeChars";
|
||||
|
||||
function sortLines(cm, caseSensitive) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
|
@ -362,10 +344,10 @@
|
|||
});
|
||||
}
|
||||
|
||||
cmds[map["F9"] = "sortLines"] = function(cm) { sortLines(cm, true); };
|
||||
cmds[map[ctrl + "F9"] = "sortLinesInsensitive"] = function(cm) { sortLines(cm, false); };
|
||||
cmds.sortLines = function(cm) { sortLines(cm, true); };
|
||||
cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
|
||||
|
||||
cmds[map["F2"] = "nextBookmark"] = function(cm) {
|
||||
cmds.nextBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
var current = marks.shift();
|
||||
|
@ -377,7 +359,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map["Shift-F2"] = "prevBookmark"] = function(cm) {
|
||||
cmds.prevBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
marks.unshift(marks.pop());
|
||||
|
@ -389,7 +371,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map[ctrl + "F2"] = "toggleBookmark"] = function(cm) {
|
||||
cmds.toggleBookmark = function(cm) {
|
||||
var ranges = cm.listSelections();
|
||||
var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -409,13 +391,13 @@
|
|||
}
|
||||
};
|
||||
|
||||
cmds[map["Shift-" + ctrl + "F2"] = "clearBookmarks"] = function(cm) {
|
||||
cmds.clearBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||
marks.length = 0;
|
||||
};
|
||||
|
||||
cmds[map["Alt-F2"] = "selectBookmarks"] = function(cm) {
|
||||
cmds.selectBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks, ranges = [];
|
||||
if (marks) for (var i = 0; i < marks.length; i++) {
|
||||
var found = marks[i].find();
|
||||
|
@ -428,10 +410,6 @@
|
|||
cm.setSelections(ranges, 0);
|
||||
};
|
||||
|
||||
map["Alt-Q"] = "wrapLines";
|
||||
|
||||
var cK = ctrl + "K ";
|
||||
|
||||
function modifyWordOrSelection(cm, mod) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections(), indices = [], replacements = [];
|
||||
|
@ -451,9 +429,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
map[cK + ctrl + "Backspace"] = "delLineLeft";
|
||||
|
||||
cmds[map["Backspace"] = "smartBackspace"] = function(cm) {
|
||||
cmds.smartBackspace = function(cm) {
|
||||
if (cm.somethingSelected()) return CodeMirror.Pass;
|
||||
|
||||
cm.operation(function() {
|
||||
|
@ -481,7 +457,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
|
||||
cmds.delLineRight = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = ranges.length - 1; i >= 0; i--)
|
||||
|
@ -490,22 +466,22 @@
|
|||
});
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) {
|
||||
cmds.upcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
|
||||
};
|
||||
cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) {
|
||||
cmds.downcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
|
||||
};
|
||||
|
||||
cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) {
|
||||
cmds.setSublimeMark = function(cm) {
|
||||
if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
|
||||
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
||||
};
|
||||
cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) {
|
||||
cmds.selectToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) cm.setSelection(cm.getCursor(), found);
|
||||
};
|
||||
cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) {
|
||||
cmds.deleteToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
var from = cm.getCursor(), to = found;
|
||||
|
@ -514,7 +490,7 @@
|
|||
cm.replaceRange("", from, to);
|
||||
}
|
||||
};
|
||||
cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) {
|
||||
cmds.swapWithSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
cm.state.sublimeMark.clear();
|
||||
|
@ -522,19 +498,17 @@
|
|||
cm.setCursor(found);
|
||||
}
|
||||
};
|
||||
cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) {
|
||||
cmds.sublimeYank = function(cm) {
|
||||
if (cm.state.sublimeKilled != null)
|
||||
cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
|
||||
};
|
||||
|
||||
map[cK + ctrl + "G"] = "clearBookmarks";
|
||||
cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) {
|
||||
cmds.showInCenter = function(cm) {
|
||||
var pos = cm.cursorCoords(null, "local");
|
||||
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
|
||||
};
|
||||
|
||||
var selectLinesCombo = mac ? "Ctrl-Shift-" : "Ctrl-Alt-";
|
||||
cmds[map[selectLinesCombo + "Up"] = "selectLinesUpward"] = function(cm) {
|
||||
cmds.selectLinesUpward = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -544,7 +518,7 @@
|
|||
}
|
||||
});
|
||||
};
|
||||
cmds[map[selectLinesCombo + "Down"] = "selectLinesDownward"] = function(cm) {
|
||||
cmds.selectLinesDownward = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
|
@ -583,9 +557,9 @@
|
|||
cm.setSelection(target.from, target.to);
|
||||
}
|
||||
};
|
||||
cmds[map[ctrl + "F3"] = "findUnder"] = function(cm) { findAndGoTo(cm, true); };
|
||||
cmds[map["Shift-" + ctrl + "F3"] = "findUnderPrevious"] = function(cm) { findAndGoTo(cm,false); };
|
||||
cmds[map["Alt-F3"] = "findAllUnder"] = function(cm) {
|
||||
cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
|
||||
cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
|
||||
cmds.findAllUnder = function(cm) {
|
||||
var target = getTarget(cm);
|
||||
if (!target) return;
|
||||
var cur = cm.getSearchCursor(target.query);
|
||||
|
@ -599,15 +573,132 @@
|
|||
cm.setSelections(matches, primaryIndex);
|
||||
};
|
||||
|
||||
map["Shift-" + ctrl + "["] = "fold";
|
||||
map["Shift-" + ctrl + "]"] = "unfold";
|
||||
map[cK + ctrl + "0"] = map[cK + ctrl + "J"] = "unfoldAll";
|
||||
|
||||
map[ctrl + "I"] = "findIncremental";
|
||||
map["Shift-" + ctrl + "I"] = "findIncrementalReverse";
|
||||
map[ctrl + "H"] = "replace";
|
||||
map["F3"] = "findNext";
|
||||
map["Shift-F3"] = "findPrev";
|
||||
var keyMap = CodeMirror.keyMap;
|
||||
keyMap.macSublime = {
|
||||
"Cmd-Left": "goLineStartSmart",
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-Left": "goSubwordLeft",
|
||||
"Ctrl-Right": "goSubwordRight",
|
||||
"Ctrl-Alt-Up": "scrollLineUp",
|
||||
"Ctrl-Alt-Down": "scrollLineDown",
|
||||
"Cmd-L": "selectLine",
|
||||
"Shift-Cmd-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Cmd-Enter": "insertLineAfter",
|
||||
"Shift-Cmd-Enter": "insertLineBefore",
|
||||
"Cmd-D": "selectNextOccurrence",
|
||||
"Shift-Cmd-Up": "addCursorToPrevLine",
|
||||
"Shift-Cmd-Down": "addCursorToNextLine",
|
||||
"Shift-Cmd-Space": "selectScope",
|
||||
"Shift-Cmd-M": "selectBetweenBrackets",
|
||||
"Cmd-M": "goToBracket",
|
||||
"Cmd-Ctrl-Up": "swapLineUp",
|
||||
"Cmd-Ctrl-Down": "swapLineDown",
|
||||
"Cmd-/": "toggleCommentIndented",
|
||||
"Cmd-J": "joinLines",
|
||||
"Shift-Cmd-D": "duplicateLine",
|
||||
"F9": "sortLines",
|
||||
"Cmd-F9": "sortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Cmd-F2": "toggleBookmark",
|
||||
"Shift-Cmd-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Cmd-K Cmd-K": "delLineRight",
|
||||
"Cmd-K Cmd-U": "upcaseAtCursor",
|
||||
"Cmd-K Cmd-L": "downcaseAtCursor",
|
||||
"Cmd-K Cmd-Space": "setSublimeMark",
|
||||
"Cmd-K Cmd-A": "selectToSublimeMark",
|
||||
"Cmd-K Cmd-W": "deleteToSublimeMark",
|
||||
"Cmd-K Cmd-X": "swapWithSublimeMark",
|
||||
"Cmd-K Cmd-Y": "sublimeYank",
|
||||
"Cmd-K Cmd-C": "showInCenter",
|
||||
"Cmd-K Cmd-G": "clearBookmarks",
|
||||
"Cmd-K Cmd-Backspace": "delLineLeft",
|
||||
"Cmd-K Cmd-0": "unfoldAll",
|
||||
"Cmd-K Cmd-J": "unfoldAll",
|
||||
"Ctrl-Shift-Up": "selectLinesUpward",
|
||||
"Ctrl-Shift-Down": "selectLinesDownward",
|
||||
"Cmd-F3": "findUnder",
|
||||
"Shift-Cmd-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Cmd-[": "fold",
|
||||
"Shift-Cmd-]": "unfold",
|
||||
"Cmd-I": "findIncremental",
|
||||
"Shift-Cmd-I": "findIncrementalReverse",
|
||||
"Cmd-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "macDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.macSublime);
|
||||
|
||||
CodeMirror.normalizeKeyMap(map);
|
||||
keyMap.pcSublime = {
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-T": "transposeChars",
|
||||
"Alt-Left": "goSubwordLeft",
|
||||
"Alt-Right": "goSubwordRight",
|
||||
"Ctrl-Up": "scrollLineUp",
|
||||
"Ctrl-Down": "scrollLineDown",
|
||||
"Ctrl-L": "selectLine",
|
||||
"Shift-Ctrl-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Ctrl-Enter": "insertLineAfter",
|
||||
"Shift-Ctrl-Enter": "insertLineBefore",
|
||||
"Ctrl-D": "selectNextOccurrence",
|
||||
"Alt-CtrlUp": "addCursorToPrevLine",
|
||||
"Alt-CtrlDown": "addCursorToNextLine",
|
||||
"Shift-Ctrl-Space": "selectScope",
|
||||
"Shift-Ctrl-M": "selectBetweenBrackets",
|
||||
"Ctrl-M": "goToBracket",
|
||||
"Shift-Ctrl-Up": "swapLineUp",
|
||||
"Shift-Ctrl-Down": "swapLineDown",
|
||||
"Ctrl-/": "toggleCommentIndented",
|
||||
"Ctrl-J": "joinLines",
|
||||
"Shift-Ctrl-D": "duplicateLine",
|
||||
"F9": "sortLines",
|
||||
"Ctrl-F9": "sortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Ctrl-F2": "toggleBookmark",
|
||||
"Shift-Ctrl-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Ctrl-K Ctrl-K": "delLineRight",
|
||||
"Ctrl-K Ctrl-U": "upcaseAtCursor",
|
||||
"Ctrl-K Ctrl-L": "downcaseAtCursor",
|
||||
"Ctrl-K Ctrl-Space": "setSublimeMark",
|
||||
"Ctrl-K Ctrl-A": "selectToSublimeMark",
|
||||
"Ctrl-K Ctrl-W": "deleteToSublimeMark",
|
||||
"Ctrl-K Ctrl-X": "swapWithSublimeMark",
|
||||
"Ctrl-K Ctrl-Y": "sublimeYank",
|
||||
"Ctrl-K Ctrl-C": "showInCenter",
|
||||
"Ctrl-K Ctrl-G": "clearBookmarks",
|
||||
"Ctrl-K Ctrl-Backspace": "delLineLeft",
|
||||
"Ctrl-K Ctrl-0": "unfoldAll",
|
||||
"Ctrl-K Ctrl-J": "unfoldAll",
|
||||
"Ctrl-Alt-Up": "selectLinesUpward",
|
||||
"Ctrl-Alt-Down": "selectLinesDownward",
|
||||
"Ctrl-F3": "findUnder",
|
||||
"Shift-Ctrl-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Ctrl-[": "fold",
|
||||
"Shift-Ctrl-]": "unfold",
|
||||
"Ctrl-I": "findIncremental",
|
||||
"Shift-Ctrl-I": "findIncrementalReverse",
|
||||
"Ctrl-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "pcDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.pcSublime);
|
||||
|
||||
var mac = keyMap.default == keyMap.macDefault;
|
||||
keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
|
||||
});
|
||||
|
|
|
@ -255,20 +255,67 @@
|
|||
}
|
||||
|
||||
function detachVimMap(cm, next) {
|
||||
if (this == CodeMirror.keyMap.vim)
|
||||
if (this == CodeMirror.keyMap.vim) {
|
||||
CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
|
||||
if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
|
||||
disableFatCursorMark(cm);
|
||||
cm.getInputField().style.caretColor = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (!next || next.attach != attachVimMap)
|
||||
leaveVimMode(cm);
|
||||
}
|
||||
function attachVimMap(cm, prev) {
|
||||
if (this == CodeMirror.keyMap.vim)
|
||||
if (this == CodeMirror.keyMap.vim) {
|
||||
CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
|
||||
if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
|
||||
enableFatCursorMark(cm);
|
||||
cm.getInputField().style.caretColor = "transparent";
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev || prev.attach != attachVimMap)
|
||||
enterVimMode(cm);
|
||||
}
|
||||
|
||||
function fatCursorMarks(cm) {
|
||||
var ranges = cm.listSelections(), result = []
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i]
|
||||
if (range.empty()) {
|
||||
if (range.anchor.ch < cm.getLine(range.anchor.line).length) {
|
||||
result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1),
|
||||
{className: "cm-fat-cursor-mark"}))
|
||||
} else {
|
||||
var widget = document.createElement("span")
|
||||
widget.textContent = "\u00a0"
|
||||
widget.className = "cm-fat-cursor-mark"
|
||||
result.push(cm.setBookmark(range.anchor, {widget: widget}))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function updateFatCursorMark(cm) {
|
||||
var marks = cm.state.fatCursorMarks
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
|
||||
cm.state.fatCursorMarks = fatCursorMarks(cm)
|
||||
}
|
||||
|
||||
function enableFatCursorMark(cm) {
|
||||
cm.state.fatCursorMarks = fatCursorMarks(cm)
|
||||
cm.on("cursorActivity", updateFatCursorMark)
|
||||
}
|
||||
|
||||
function disableFatCursorMark(cm) {
|
||||
var marks = cm.state.fatCursorMarks
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear()
|
||||
cm.state.fatCursorMarks = null
|
||||
cm.off("cursorActivity", updateFatCursorMark)
|
||||
}
|
||||
|
||||
// Deprecated, simply setting the keymap works again.
|
||||
CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
|
||||
if (val && cm.getOption("keyMap") != "vim")
|
||||
|
@ -2034,7 +2081,8 @@
|
|||
vimGlobalState.registerController.pushText(
|
||||
args.registerName, 'delete', text,
|
||||
args.linewise, vim.visualBlock);
|
||||
return clipCursorToContent(cm, finalHead);
|
||||
var includeLineBreak = vim.insertMode
|
||||
return clipCursorToContent(cm, finalHead, includeLineBreak);
|
||||
},
|
||||
indent: function(cm, args, ranges) {
|
||||
var vim = cm.state.vim;
|
||||
|
@ -3244,7 +3292,7 @@
|
|||
return cur;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns the boundaries of the next word. If the cursor in the middle of
|
||||
* the word, then returns the boundaries of the current word, starting at
|
||||
* the cursor. If the cursor is at the start/end of a word, and we are going
|
||||
|
@ -3978,6 +4026,15 @@
|
|||
var history = cm.doc.history.done;
|
||||
var event = history[history.length - 2];
|
||||
return event && event.ranges && event.ranges[0].head;
|
||||
} else if (markName == '.') {
|
||||
if (cm.doc.history.lastModTime == 0) {
|
||||
return // If no changes, bail out; don't bother to copy or reverse history array.
|
||||
} else {
|
||||
var changeHistory = cm.doc.history.done.filter(function(el){ if (el.changes !== undefined) { return el } });
|
||||
changeHistory.reverse();
|
||||
var lastEditPos = changeHistory[0].changes[0].to;
|
||||
}
|
||||
return lastEditPos;
|
||||
}
|
||||
|
||||
var mark = vim.marks[markName];
|
||||
|
@ -4788,7 +4845,8 @@
|
|||
// so as to update the ". register as expected in real vim.
|
||||
var text = [];
|
||||
if (!isPlaying) {
|
||||
var selLength = lastChange.inVisualBlock ? vim.lastSelection.visualBlock.height : 1;
|
||||
var selLength = lastChange.inVisualBlock && vim.lastSelection ?
|
||||
vim.lastSelection.visualBlock.height : 1;
|
||||
var changes = lastChange.changes;
|
||||
var text = [];
|
||||
var i = 0;
|
||||
|
|
|
@ -59,7 +59,12 @@
|
|||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cm-fat-cursor-mark {
|
||||
background-color: rgba(20, 255, 20, 0.5);
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
.cm-animate-fat-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
|
|
|
@ -1088,13 +1088,15 @@ var bidiOrdering = (function() {
|
|||
if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
|
||||
}
|
||||
}
|
||||
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
||||
order[0].from = m[0].length
|
||||
order.unshift(new BidiSpan(0, 0, m[0].length))
|
||||
}
|
||||
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
||||
lst(order).to -= m[0].length
|
||||
order.push(new BidiSpan(0, len - m[0].length, len))
|
||||
if (direction == "ltr") {
|
||||
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
|
||||
order[0].from = m[0].length
|
||||
order.unshift(new BidiSpan(0, 0, m[0].length))
|
||||
}
|
||||
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
|
||||
lst(order).to -= m[0].length
|
||||
order.push(new BidiSpan(0, len - m[0].length, len))
|
||||
}
|
||||
}
|
||||
|
||||
return direction == "rtl" ? order.reverse() : order
|
||||
|
@ -1469,6 +1471,10 @@ StringStream.prototype.lookAhead = function (n) {
|
|||
var oracle = this.lineOracle
|
||||
return oracle && oracle.lookAhead(n)
|
||||
};
|
||||
StringStream.prototype.baseToken = function () {
|
||||
var oracle = this.lineOracle
|
||||
return oracle && oracle.baseToken(this.pos)
|
||||
};
|
||||
|
||||
var SavedContext = function(state, lookAhead) {
|
||||
this.state = state
|
||||
|
@ -1480,6 +1486,8 @@ var Context = function(doc, state, line, lookAhead) {
|
|||
this.doc = doc
|
||||
this.line = line
|
||||
this.maxLookAhead = lookAhead || 0
|
||||
this.baseTokens = null
|
||||
this.baseTokenPos = 1
|
||||
};
|
||||
|
||||
Context.prototype.lookAhead = function (n) {
|
||||
|
@ -1488,6 +1496,17 @@ Context.prototype.lookAhead = function (n) {
|
|||
return line
|
||||
};
|
||||
|
||||
Context.prototype.baseToken = function (n) {
|
||||
var this$1 = this;
|
||||
|
||||
if (!this.baseTokens) { return null }
|
||||
while (this.baseTokens[this.baseTokenPos] <= n)
|
||||
{ this$1.baseTokenPos += 2 }
|
||||
var type = this.baseTokens[this.baseTokenPos + 1]
|
||||
return {type: type && type.replace(/( |^)overlay .*/, ""),
|
||||
size: this.baseTokens[this.baseTokenPos] - n}
|
||||
};
|
||||
|
||||
Context.prototype.nextLine = function () {
|
||||
this.line++
|
||||
if (this.maxLookAhead > 0) { this.maxLookAhead-- }
|
||||
|
@ -1521,6 +1540,7 @@ function highlightLine(cm, line, context, forceToEnd) {
|
|||
|
||||
// Run overlays, adjust style array.
|
||||
var loop = function ( o ) {
|
||||
context.baseTokens = st
|
||||
var overlay = cm.state.overlays[o], i = 1, at = 0
|
||||
context.state = true
|
||||
runMode(cm, line.text, overlay.mode, context, function (end, style) {
|
||||
|
@ -1544,10 +1564,12 @@ function highlightLine(cm, line, context, forceToEnd) {
|
|||
}
|
||||
}
|
||||
}, lineClasses)
|
||||
context.state = state
|
||||
context.baseTokens = null
|
||||
context.baseTokenPos = 1
|
||||
};
|
||||
|
||||
for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
|
||||
context.state = state
|
||||
|
||||
return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
|
||||
}
|
||||
|
@ -2887,6 +2909,7 @@ function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x,
|
|||
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
|
||||
var begin = ref.begin;
|
||||
var end = ref.end;
|
||||
if (/\s/.test(lineObj.text.charAt(end - 1))) { end-- }
|
||||
var part = null, closestDist = null
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var p = order[i]
|
||||
|
@ -3077,6 +3100,7 @@ function drawSelectionRange(cm, range, output) {
|
|||
var fragment = document.createDocumentFragment()
|
||||
var padding = paddingH(cm.display), leftSide = padding.left
|
||||
var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
|
||||
var docLTR = doc.direction == "ltr"
|
||||
|
||||
function add(left, top, width, bottom) {
|
||||
if (top < 0) { top = 0 }
|
||||
|
@ -3093,42 +3117,43 @@ function drawSelectionRange(cm, range, output) {
|
|||
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
|
||||
}
|
||||
|
||||
function wrapX(pos, dir, side) {
|
||||
var extent = wrappedLineExtentChar(cm, lineObj, null, pos)
|
||||
var prop = (dir == "ltr") == (side == "after") ? "left" : "right"
|
||||
var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
|
||||
return coords(ch, prop)[prop]
|
||||
}
|
||||
|
||||
var order = getOrder(lineObj, doc.direction)
|
||||
iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
|
||||
var fromPos = coords(from, dir == "ltr" ? "left" : "right")
|
||||
var toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
|
||||
if (dir == "ltr") {
|
||||
var fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
|
||||
var toRight = toArg == null && to == lineLen ? rightSide : toPos.right
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
add(fromLeft, toPos.top, toRight - fromLeft, toPos.bottom)
|
||||
} else { // Multiple lines
|
||||
add(fromLeft, fromPos.top, null, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
add(leftSide, toPos.top, toPos.right, toPos.bottom)
|
||||
}
|
||||
} else if (from < to) { // RTL
|
||||
var fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
|
||||
var toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
|
||||
} else { // Multiple lines
|
||||
var topLeft = leftSide
|
||||
if (i) {
|
||||
var topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
|
||||
// The coordinates returned for an RTL wrapped space tend to
|
||||
// be complete bogus, so try to skip that here.
|
||||
topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
|
||||
}
|
||||
add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
var botWidth = null
|
||||
if (i < order.length - 1 || true) {
|
||||
var botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
|
||||
botWidth = coords(botStart, "right").right - toLeft
|
||||
}
|
||||
add(toLeft, toPos.top, botWidth, toPos.bottom)
|
||||
var ltr = dir == "ltr"
|
||||
var fromPos = coords(from, ltr ? "left" : "right")
|
||||
var toPos = coords(to - 1, ltr ? "right" : "left")
|
||||
|
||||
var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
|
||||
var first = i == 0, last = !order || i == order.length - 1
|
||||
if (toPos.top - fromPos.top <= 3) { // Single line
|
||||
var openLeft = (docLTR ? openStart : openEnd) && first
|
||||
var openRight = (docLTR ? openEnd : openStart) && last
|
||||
var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
|
||||
var right = openRight ? rightSide : (ltr ? toPos : fromPos).right
|
||||
add(left, fromPos.top, right - left, fromPos.bottom)
|
||||
} else { // Multiple lines
|
||||
var topLeft, topRight, botLeft, botRight
|
||||
if (ltr) {
|
||||
topLeft = docLTR && openStart && first ? leftSide : fromPos.left
|
||||
topRight = docLTR ? rightSide : wrapX(from, dir, "before")
|
||||
botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
|
||||
botRight = docLTR && openEnd && last ? rightSide : toPos.right
|
||||
} else {
|
||||
topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
|
||||
topRight = !docLTR && openStart && first ? rightSide : fromPos.right
|
||||
botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
|
||||
botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
|
||||
}
|
||||
add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
|
||||
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
|
||||
add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
|
||||
}
|
||||
|
||||
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
|
||||
|
@ -6753,7 +6778,7 @@ function endOfLine(visually, cm, lineObj, lineNo, dir) {
|
|||
// Thus, in rtl, we are looking for the first (content-order) character
|
||||
// in the rtl chunk that is on the last line (that is, the same line
|
||||
// as the last (content-order) character).
|
||||
if (part.level > 0) {
|
||||
if (part.level > 0 || cm.doc.direction == "rtl") {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
|
@ -9615,7 +9640,7 @@ CodeMirror.fromTextArea = fromTextArea
|
|||
|
||||
addLegacyProps(CodeMirror)
|
||||
|
||||
CodeMirror.version = "5.30.0"
|
||||
CodeMirror.version = "5.31.0"
|
||||
|
||||
return CodeMirror;
|
||||
|
||||
|
|
|
@ -244,6 +244,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|||
electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
|
||||
blockCommentStart: "/*",
|
||||
blockCommentEnd: "*/",
|
||||
blockCommentContinue: " * ",
|
||||
lineComment: "//",
|
||||
fold: "brace"
|
||||
};
|
||||
|
@ -591,7 +592,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|||
multiLineStrings: true,
|
||||
number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
|
||||
blockKeywords: words("catch class do else finally for if where try while enum"),
|
||||
defKeywords: words("class val var object package interface fun"),
|
||||
defKeywords: words("class val var object interface fun"),
|
||||
atoms: words("true false null this"),
|
||||
hooks: {
|
||||
'"': function(stream, state) {
|
||||
|
|
|
@ -410,6 +410,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
|
|||
electricChars: "}",
|
||||
blockCommentStart: "/*",
|
||||
blockCommentEnd: "*/",
|
||||
blockCommentContinue: " * ",
|
||||
lineComment: lineComment,
|
||||
fold: "brace"
|
||||
};
|
||||
|
|
|
@ -23,13 +23,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
|
||||
var keywords = function(){
|
||||
function kw(type) {return {type: type, style: "keyword"};}
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
|
||||
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
||||
|
||||
var jsKeywords = {
|
||||
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": C,
|
||||
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
|
||||
"debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
"in": operator, "typeof": operator, "instanceof": operator,
|
||||
|
@ -128,7 +128,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
|
||||
return ret("regexp", "string-2");
|
||||
} else {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
stream.eat("=");
|
||||
return ret("operator", "operator", stream.current());
|
||||
}
|
||||
} else if (ch == "`") {
|
||||
|
@ -138,8 +138,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
stream.skipToEnd();
|
||||
return ret("error", "error");
|
||||
} else if (isOperatorChar.test(ch)) {
|
||||
if (ch != ">" || !state.lexical || state.lexical.type != ">")
|
||||
stream.eatWhile(isOperatorChar);
|
||||
if (ch != ">" || !state.lexical || state.lexical.type != ">") {
|
||||
if (stream.eat("=")) {
|
||||
if (ch == "!" || ch == "=") stream.eat("=")
|
||||
} else if (/[<>*+\-]/.test(ch)) {
|
||||
stream.eat(ch)
|
||||
if (ch == ">") stream.eat(ch)
|
||||
}
|
||||
}
|
||||
return ret("operator", "operator", stream.current());
|
||||
} else if (wordRE.test(ch)) {
|
||||
stream.eatWhile(wordRE);
|
||||
|
@ -351,6 +357,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
|
||||
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
|
||||
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
||||
if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
|
||||
if (type == "debugger") return cont(expect(";"));
|
||||
if (type == "{") return cont(pushlex("}"), block, poplex);
|
||||
if (type == ";") return cont();
|
||||
if (type == "if") {
|
||||
|
@ -406,7 +414,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
|
||||
if (type == "function") return cont(functiondef, maybeop);
|
||||
if (type == "class") return cont(pushlex("form"), classExpression, poplex);
|
||||
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
|
||||
if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
|
||||
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
|
||||
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
|
||||
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
|
||||
|
@ -419,10 +427,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expression);
|
||||
}
|
||||
function maybeexpressionNoComma(type) {
|
||||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expressionNoComma);
|
||||
}
|
||||
|
||||
function maybeoperatorComma(type, value) {
|
||||
if (type == ",") return cont(expression);
|
||||
|
@ -510,7 +514,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
} else if (type == "[") {
|
||||
return cont(expression, expect("]"), afterprop);
|
||||
} else if (type == "spread") {
|
||||
return cont(expression, afterprop);
|
||||
return cont(expressionNoComma, afterprop);
|
||||
} else if (value == "*") {
|
||||
cx.marked = "keyword";
|
||||
return cont(objprop);
|
||||
} else if (type == ":") {
|
||||
return pass(afterprop)
|
||||
}
|
||||
|
@ -558,7 +565,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
}
|
||||
}
|
||||
function typeexpr(type, value) {
|
||||
if (type == "variable") {
|
||||
if (type == "variable" || value == "void") {
|
||||
if (value == "keyof") {
|
||||
cx.marked = "keyword"
|
||||
return cont(typeexpr)
|
||||
|
@ -745,7 +752,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
|
||||
function expressionAllowed(stream, state, backUp) {
|
||||
return state.tokenize == tokenBase &&
|
||||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
|
||||
}
|
||||
|
||||
|
@ -814,6 +821,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
|
||||
blockCommentStart: jsonMode ? null : "/*",
|
||||
blockCommentEnd: jsonMode ? null : "*/",
|
||||
blockCommentContinue: jsonMode ? null : " * ",
|
||||
lineComment: jsonMode ? null : "//",
|
||||
fold: "brace",
|
||||
closeBrackets: "()[]{}''\"\"``",
|
||||
|
|
|
@ -236,6 +236,19 @@
|
|||
" [keyword return] [number 2]",
|
||||
"}")
|
||||
|
||||
MT("regexp_corner_case",
|
||||
"[operator +]{} [operator /] [atom undefined];",
|
||||
"[[[meta ...][string-2 /\\//] ]];",
|
||||
"[keyword void] [string-2 /\\//];",
|
||||
"[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);",
|
||||
"[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];",
|
||||
"[string-2 `${][variable async][operator ++][string-2 }//`];",
|
||||
"[string-2 `${]{} [operator /] [string-2 /\\//}`];")
|
||||
|
||||
MT("return_eol",
|
||||
"[keyword return]",
|
||||
"{} [string-2 /5/]")
|
||||
|
||||
var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript")
|
||||
function TS(name) {
|
||||
test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))
|
||||
|
@ -352,6 +365,9 @@
|
|||
" [property constructor]([keyword readonly] [keyword private] [def x]) {}",
|
||||
"}")
|
||||
|
||||
TS("arrow prop",
|
||||
"({[property a]: [def p] [operator =>] [variable-2 p]})")
|
||||
|
||||
var jsonld_mode = CodeMirror.getMode(
|
||||
{indentUnit: 2},
|
||||
{name: "javascript", jsonld: true}
|
||||
|
|
|
@ -2218,6 +2218,21 @@ testCM("getTokenTypeAt", function(cm) {
|
|||
eq(cm.getTokenTypeAt(Pos(0, 6)), "string");
|
||||
}, {value: "1 + 'foo'", mode: "javascript"});
|
||||
|
||||
testCM("addOverlay", function(cm) {
|
||||
cm.addOverlay({
|
||||
token: function(stream) {
|
||||
var base = stream.baseToken()
|
||||
if (!/comment/.test(base.type) && stream.match(/\d+/)) return "x"
|
||||
stream.next()
|
||||
}
|
||||
})
|
||||
var x = byClassName(cm.getWrapperElement(), "cm-x")
|
||||
is(x.length, 1)
|
||||
is(x[0].textContent, "233")
|
||||
cm.replaceRange("", Pos(0, 4), Pos(0, 6))
|
||||
is(byClassName(cm.getWrapperElement(), "cm-x").length, 2)
|
||||
}, {value: "foo /* 100 */\nbar + 233;\nbaz", mode: "javascript"})
|
||||
|
||||
testCM("resizeLineWidget", function(cm) {
|
||||
addDoc(cm, 200, 3);
|
||||
var widget = document.createElement("pre");
|
||||
|
@ -2507,6 +2522,14 @@ testCM("delete_wrapped", function(cm) {
|
|||
eq(cm.getLine(0), "1245");
|
||||
}, {value: "12345", lineWrapping: true})
|
||||
|
||||
testCM("issue_4878", function(cm) {
|
||||
if (phantom) return
|
||||
cm.setCursor(Pos(1, 12, "after"));
|
||||
cm.moveH(-1, "char");
|
||||
eqCursorPos(cm.getCursor(), Pos(0, 113, "before"));
|
||||
}, {value: " في تطبيق السمات مرة واحدة https://github.com/codemirror/CodeMirror/issues/4878#issuecomment-330550964على سبيل المثال <code>\"foo bar\"</code>\n" +
|
||||
" سيتم تعيين", direction: "rtl", lineWrapping: true});
|
||||
|
||||
CodeMirror.defineMode("lookahead_mode", function() {
|
||||
// Colors text as atom if the line two lines down has an x in it
|
||||
return {
|
||||
|
|
|
@ -1386,6 +1386,19 @@ testVim('<C-x>/<C-a> search forward', function(cm, vim, helpers) {
|
|||
helpers.assertCursorAt(0, 11);
|
||||
});
|
||||
}, {value: '__jmp1 jmp2 jmp'});
|
||||
testVim('insert_ctrl_w', function(cm, vim, helpers) {
|
||||
var curStart = makeCursor(0, 10);
|
||||
cm.setCursor(curStart);
|
||||
helpers.doKeys('a');
|
||||
helpers.doKeys('<C-w>');
|
||||
eq('word1/', cm.getValue());
|
||||
var register = helpers.getRegisterController().getRegister();
|
||||
eq('word2', register.toString());
|
||||
is(!register.linewise);
|
||||
var curEnd = makeCursor(0, 6);
|
||||
eqCursorPos(curEnd, cm.getCursor());
|
||||
eq('vim-insert', cm.getOption('keyMap'));
|
||||
}, { value: 'word1/word2' });
|
||||
testVim('a', function(cm, vim, helpers) {
|
||||
cm.setCursor(0, 1);
|
||||
helpers.doKeys('a');
|
||||
|
@ -1662,6 +1675,16 @@ testVim('mark\'', function(cm, vim, helpers) {
|
|||
helpers.doKeys('\'', '\'');
|
||||
helpers.assertCursorAt(2, 3);
|
||||
});
|
||||
testVim('mark.', function(cm, vim, helpers) {
|
||||
cm.setCursor(0, 0);
|
||||
helpers.doKeys('O', 'testing', '<Esc>');
|
||||
cm.setCursor(3, 3);
|
||||
helpers.doKeys('\'', '.');
|
||||
helpers.assertCursorAt(0, 0);
|
||||
cm.setCursor(4, 4);
|
||||
helpers.doKeys('`', '.');
|
||||
helpers.assertCursorAt(0, 6);
|
||||
});
|
||||
testVim('jumpToMark_next', function(cm, vim, helpers) {
|
||||
cm.setCursor(2, 2);
|
||||
helpers.doKeys('m', 't');
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* 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 DOMTypes;
|
||||
include PBackgroundSharedTypes;
|
||||
include ProtocolTypes;
|
||||
using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
|
||||
using ClientType from "mozilla/dom/ClientIPCUtils.h";
|
||||
using FrameType from "mozilla/dom/ClientIPCUtils.h";
|
||||
using VisibilityState from "mozilla/dom/ClientIPCUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct IPCClientInfo
|
||||
{
|
||||
nsID id;
|
||||
ClientType type;
|
||||
PrincipalInfo principalInfo;
|
||||
TimeStamp creationTime;
|
||||
nsCString url;
|
||||
FrameType frameType;
|
||||
};
|
||||
|
||||
struct IPCClientWindowState
|
||||
{
|
||||
VisibilityState visibilityState;
|
||||
TimeStamp lastFocusTime;
|
||||
bool focused;
|
||||
};
|
||||
|
||||
struct IPCClientWorkerState
|
||||
{
|
||||
};
|
||||
|
||||
union IPCClientState
|
||||
{
|
||||
IPCClientWindowState;
|
||||
IPCClientWorkerState;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _mozilla_dom_ClientIPCUtils_h
|
||||
#define _mozilla_dom_ClientIPCUtils_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
// Fix X11 header brain damage that conflicts with FrameType::None
|
||||
#undef None
|
||||
|
||||
#include "mozilla/dom/ClientBinding.h"
|
||||
#include "mozilla/dom/ClientsBinding.h"
|
||||
#include "mozilla/dom/DocumentBinding.h"
|
||||
|
||||
namespace IPC {
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::ClientType> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::ClientType,
|
||||
mozilla::dom::ClientType::Window,
|
||||
mozilla::dom::ClientType::EndGuard_>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::FrameType> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::FrameType,
|
||||
mozilla::dom::FrameType::Auxiliary,
|
||||
mozilla::dom::FrameType::EndGuard_>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::VisibilityState> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::VisibilityState,
|
||||
mozilla::dom::VisibilityState::Hidden,
|
||||
mozilla::dom::VisibilityState::EndGuard_>
|
||||
{};
|
||||
} // namespace IPC
|
||||
|
||||
#endif // _mozilla_dom_ClientIPCUtils_h
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ClientInfo.h"
|
||||
|
||||
#include "mozilla/dom/ClientIPCTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ClientInfo::ClientInfo(const nsID& aId,
|
||||
ClientType aType,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const TimeStamp& aCreationTime)
|
||||
: mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
|
||||
EmptyCString(),
|
||||
mozilla::dom::FrameType::None))
|
||||
{
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo(const IPCClientInfo& aData)
|
||||
: mData(MakeUnique<IPCClientInfo>(aData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo(const ClientInfo& aRight)
|
||||
{
|
||||
operator=(aRight);
|
||||
}
|
||||
|
||||
ClientInfo&
|
||||
ClientInfo::operator=(const ClientInfo& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = MakeUnique<IPCClientInfo>(*aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientInfo::ClientInfo(ClientInfo&& aRight)
|
||||
: mData(Move(aRight.mData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientInfo&
|
||||
ClientInfo::operator=(ClientInfo&& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = Move(aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientInfo::~ClientInfo()
|
||||
{
|
||||
}
|
||||
|
||||
const nsID&
|
||||
ClientInfo::Id() const
|
||||
{
|
||||
return mData->id();
|
||||
}
|
||||
|
||||
ClientType
|
||||
ClientInfo::Type() const
|
||||
{
|
||||
return mData->type();
|
||||
}
|
||||
|
||||
const mozilla::ipc::PrincipalInfo&
|
||||
ClientInfo::PrincipalInfo() const
|
||||
{
|
||||
return mData->principalInfo();
|
||||
}
|
||||
|
||||
const TimeStamp&
|
||||
ClientInfo::CreationTime() const
|
||||
{
|
||||
return mData->creationTime();
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ClientInfo::URL() const
|
||||
{
|
||||
return mData->url();
|
||||
}
|
||||
|
||||
void
|
||||
ClientInfo::SetURL(const nsACString& aURL)
|
||||
{
|
||||
mData->url() = aURL;
|
||||
}
|
||||
|
||||
FrameType
|
||||
ClientInfo::FrameType() const
|
||||
{
|
||||
return mData->frameType();
|
||||
}
|
||||
|
||||
void
|
||||
ClientInfo::SetFrameType(mozilla::dom::FrameType aFrameType)
|
||||
{
|
||||
mData->frameType() = aFrameType;
|
||||
}
|
||||
|
||||
const IPCClientInfo&
|
||||
ClientInfo::ToIPC() const
|
||||
{
|
||||
return *mData;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _mozilla_dom_ClientInfo_h
|
||||
#define _mozilla_dom_ClientInfo_h
|
||||
|
||||
#include "mozilla/dom/ClientBinding.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class IPCClientInfo;
|
||||
|
||||
// This class provides a simple structure that represents a global living
|
||||
// in the system. Its thread safe and can be transferred across process
|
||||
// boundaries. A ClientInfo object can represent either a window or a worker.
|
||||
class ClientInfo final
|
||||
{
|
||||
UniquePtr<IPCClientInfo> mData;
|
||||
|
||||
public:
|
||||
ClientInfo(const nsID& aId,
|
||||
ClientType aType,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const TimeStamp& aCreationTime);
|
||||
|
||||
ClientInfo(const ClientInfo& aRight);
|
||||
|
||||
ClientInfo&
|
||||
operator=(const ClientInfo& aRight);
|
||||
|
||||
ClientInfo(ClientInfo&& aRight);
|
||||
|
||||
ClientInfo&
|
||||
operator=(ClientInfo&& aRight);
|
||||
|
||||
explicit ClientInfo(const IPCClientInfo& aData);
|
||||
|
||||
~ClientInfo();
|
||||
|
||||
// Get the unique identifier chosen at the time of the global's creation.
|
||||
const nsID&
|
||||
Id() const;
|
||||
|
||||
// Determine what kind of global this is; e.g. Window, Worker, SharedWorker,
|
||||
// etc.
|
||||
ClientType
|
||||
Type() const;
|
||||
|
||||
// Every global must have a principal that cannot change.
|
||||
const mozilla::ipc::PrincipalInfo&
|
||||
PrincipalInfo() const;
|
||||
|
||||
// The time at which the global was created.
|
||||
const TimeStamp&
|
||||
CreationTime() const;
|
||||
|
||||
// Each global has the concept of a creation URL. For the most part this
|
||||
// does not change. The one exception is for about:blank replacement
|
||||
// iframes. In this case the URL starts as "about:blank", but is later
|
||||
// overriden with the final URL.
|
||||
const nsCString&
|
||||
URL() const;
|
||||
|
||||
// Override the creation URL. This should only be used for about:blank
|
||||
// replacement iframes.
|
||||
void
|
||||
SetURL(const nsACString& aURL);
|
||||
|
||||
// The frame type is largely a window concept, but we track it as part
|
||||
// of the global here because of the way the Clients WebAPI was designed.
|
||||
// This is set at the time the global becomes execution ready. Workers
|
||||
// will always return None.
|
||||
mozilla::dom::FrameType
|
||||
FrameType() const;
|
||||
|
||||
// Set the frame type for the global. This should only happen once the
|
||||
// global has become execution ready.
|
||||
void
|
||||
SetFrameType(mozilla::dom::FrameType aFrameType);
|
||||
|
||||
// Convert to the ipdl generated type.
|
||||
const IPCClientInfo&
|
||||
ToIPC() const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _mozilla_dom_ClientInfo_h
|
|
@ -0,0 +1,214 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ClientState.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ClientWindowState::ClientWindowState(mozilla::dom::VisibilityState aVisibilityState,
|
||||
const TimeStamp& aLastFocusTime,
|
||||
bool aFocused)
|
||||
: mData(MakeUnique<IPCClientWindowState>(aVisibilityState, aLastFocusTime,
|
||||
aFocused))
|
||||
{
|
||||
}
|
||||
|
||||
ClientWindowState::ClientWindowState(const IPCClientWindowState& aData)
|
||||
: mData(MakeUnique<IPCClientWindowState>(aData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientWindowState::ClientWindowState(const ClientWindowState& aRight)
|
||||
{
|
||||
operator=(aRight);
|
||||
}
|
||||
|
||||
ClientWindowState::ClientWindowState(ClientWindowState&& aRight)
|
||||
: mData(Move(aRight.mData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientWindowState&
|
||||
ClientWindowState::operator=(const ClientWindowState& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = MakeUnique<IPCClientWindowState>(*aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientWindowState&
|
||||
ClientWindowState::operator=(ClientWindowState&& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = Move(aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientWindowState::~ClientWindowState()
|
||||
{
|
||||
}
|
||||
|
||||
mozilla::dom::VisibilityState
|
||||
ClientWindowState::VisibilityState() const
|
||||
{
|
||||
return mData->visibilityState();
|
||||
}
|
||||
|
||||
const TimeStamp&
|
||||
ClientWindowState::LastFocusTime() const
|
||||
{
|
||||
return mData->lastFocusTime();
|
||||
}
|
||||
|
||||
bool
|
||||
ClientWindowState::Focused() const
|
||||
{
|
||||
return mData->focused();
|
||||
}
|
||||
|
||||
const IPCClientWindowState&
|
||||
ClientWindowState::ToIPC() const
|
||||
{
|
||||
return *mData;
|
||||
}
|
||||
|
||||
ClientWorkerState::ClientWorkerState()
|
||||
: mData(MakeUnique<IPCClientWorkerState>())
|
||||
{
|
||||
}
|
||||
|
||||
ClientWorkerState::ClientWorkerState(const IPCClientWorkerState& aData)
|
||||
: mData(MakeUnique<IPCClientWorkerState>(aData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientWorkerState::ClientWorkerState(ClientWorkerState&& aRight)
|
||||
: mData(Move(aRight.mData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientWorkerState::ClientWorkerState(const ClientWorkerState& aRight)
|
||||
{
|
||||
operator=(aRight);
|
||||
}
|
||||
|
||||
ClientWorkerState&
|
||||
ClientWorkerState::operator=(const ClientWorkerState& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = MakeUnique<IPCClientWorkerState>(*aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientWorkerState&
|
||||
ClientWorkerState::operator=(ClientWorkerState&& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = Move(aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientWorkerState::~ClientWorkerState()
|
||||
{
|
||||
}
|
||||
|
||||
const IPCClientWorkerState&
|
||||
ClientWorkerState::ToIPC() const
|
||||
{
|
||||
return *mData;
|
||||
}
|
||||
|
||||
ClientState::ClientState()
|
||||
{
|
||||
}
|
||||
|
||||
ClientState::ClientState(const ClientWindowState& aWindowState)
|
||||
{
|
||||
mData.emplace(AsVariant(aWindowState));
|
||||
}
|
||||
|
||||
ClientState::ClientState(const ClientWorkerState& aWorkerState)
|
||||
{
|
||||
mData.emplace(AsVariant(aWorkerState));
|
||||
}
|
||||
|
||||
ClientState::ClientState(const IPCClientWindowState& aData)
|
||||
{
|
||||
mData.emplace(AsVariant(ClientWindowState(aData)));
|
||||
}
|
||||
|
||||
ClientState::ClientState(const IPCClientWorkerState& aData)
|
||||
{
|
||||
mData.emplace(AsVariant(ClientWorkerState(aData)));
|
||||
}
|
||||
|
||||
ClientState::ClientState(ClientState&& aRight)
|
||||
: mData(Move(aRight.mData))
|
||||
{
|
||||
}
|
||||
|
||||
ClientState&
|
||||
ClientState::operator=(ClientState&& aRight)
|
||||
{
|
||||
mData = Move(aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClientState::~ClientState()
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
ClientState
|
||||
ClientState::FromIPC(const IPCClientState& aData)
|
||||
{
|
||||
switch(aData.type()) {
|
||||
case IPCClientState::TIPCClientWindowState:
|
||||
return ClientState(aData.get_IPCClientWindowState());
|
||||
case IPCClientState::TIPCClientWorkerState:
|
||||
return ClientState(aData.get_IPCClientWorkerState());
|
||||
default:
|
||||
MOZ_CRASH("unexpected IPCClientState type");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ClientState::IsWindowState() const
|
||||
{
|
||||
return mData.isSome() && mData.ref().is<ClientWindowState>();
|
||||
}
|
||||
|
||||
const ClientWindowState&
|
||||
ClientState::AsWindowState() const
|
||||
{
|
||||
return mData.ref().as<ClientWindowState>();
|
||||
}
|
||||
|
||||
bool
|
||||
ClientState::IsWorkerState() const
|
||||
{
|
||||
return mData.isSome() && mData.ref().is<ClientWorkerState>();
|
||||
}
|
||||
|
||||
const ClientWorkerState&
|
||||
ClientState::AsWorkerState() const
|
||||
{
|
||||
return mData.ref().as<ClientWorkerState>();
|
||||
}
|
||||
|
||||
const IPCClientState
|
||||
ClientState::ToIPC() const
|
||||
{
|
||||
if (IsWindowState()) {
|
||||
return IPCClientState(AsWindowState().ToIPC());
|
||||
}
|
||||
|
||||
return IPCClientState(AsWorkerState().ToIPC());
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,135 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _mozilla_dom_ClientState_h
|
||||
#define _mozilla_dom_ClientState_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class IPCClientState;
|
||||
class IPCClientWindowState;
|
||||
class IPCClientWorkerState;
|
||||
|
||||
// This class defines the mutable nsGlobalWindow state we support querying
|
||||
// through the ClientManagerService. It is a snapshot of the state and
|
||||
// is not live updated.
|
||||
class ClientWindowState final
|
||||
{
|
||||
UniquePtr<IPCClientWindowState> mData;
|
||||
|
||||
public:
|
||||
ClientWindowState(mozilla::dom::VisibilityState aVisibilityState,
|
||||
const TimeStamp& aLastFocusTime,
|
||||
bool aFocused);
|
||||
|
||||
explicit ClientWindowState(const IPCClientWindowState& aData);
|
||||
|
||||
ClientWindowState(const ClientWindowState& aRight);
|
||||
ClientWindowState(ClientWindowState&& aRight);
|
||||
|
||||
ClientWindowState&
|
||||
operator=(const ClientWindowState& aRight);
|
||||
|
||||
ClientWindowState&
|
||||
operator=(ClientWindowState&& aRight);
|
||||
|
||||
~ClientWindowState();
|
||||
|
||||
mozilla::dom::VisibilityState
|
||||
VisibilityState() const;
|
||||
|
||||
const TimeStamp&
|
||||
LastFocusTime() const;
|
||||
|
||||
bool
|
||||
Focused() const;
|
||||
|
||||
const IPCClientWindowState&
|
||||
ToIPC() const;
|
||||
};
|
||||
|
||||
// This class defines the mutable worker state we support querying
|
||||
// through the ClientManagerService. It is a snapshot of the state and
|
||||
// is not live updated. Right now, we don't actually providate any
|
||||
// worker specific state values, but we may in the future. This
|
||||
// class also services as a placeholder that the state is referring
|
||||
// to a worker in ClientState.
|
||||
class ClientWorkerState final
|
||||
{
|
||||
UniquePtr<IPCClientWorkerState> mData;
|
||||
|
||||
public:
|
||||
ClientWorkerState();
|
||||
|
||||
explicit ClientWorkerState(const IPCClientWorkerState& aData);
|
||||
|
||||
ClientWorkerState(const ClientWorkerState& aRight);
|
||||
ClientWorkerState(ClientWorkerState&& aRight);
|
||||
|
||||
ClientWorkerState&
|
||||
operator=(const ClientWorkerState& aRight);
|
||||
|
||||
ClientWorkerState&
|
||||
operator=(ClientWorkerState&& aRight);
|
||||
|
||||
~ClientWorkerState();
|
||||
|
||||
const IPCClientWorkerState&
|
||||
ToIPC() const;
|
||||
};
|
||||
|
||||
// This is a union of the various types of mutable state we support
|
||||
// querying in ClientManagerService. Right now it can contain either
|
||||
// window or worker states.
|
||||
class ClientState final
|
||||
{
|
||||
Maybe<Variant<ClientWindowState, ClientWorkerState>> mData;
|
||||
|
||||
public:
|
||||
ClientState();
|
||||
|
||||
explicit ClientState(const ClientWindowState& aWindowState);
|
||||
explicit ClientState(const ClientWorkerState& aWorkerState);
|
||||
explicit ClientState(const IPCClientWindowState& aData);
|
||||
explicit ClientState(const IPCClientWorkerState& aData);
|
||||
|
||||
ClientState(const ClientState& aRight) = default;
|
||||
ClientState(ClientState&& aRight);
|
||||
|
||||
ClientState&
|
||||
operator=(const ClientState& aRight) = default;
|
||||
|
||||
ClientState&
|
||||
operator=(ClientState&& aRight);
|
||||
|
||||
~ClientState();
|
||||
|
||||
static ClientState
|
||||
FromIPC(const IPCClientState& aData);
|
||||
|
||||
bool
|
||||
IsWindowState() const;
|
||||
|
||||
const ClientWindowState&
|
||||
AsWindowState() const;
|
||||
|
||||
bool
|
||||
IsWorkerState() const;
|
||||
|
||||
const ClientWorkerState&
|
||||
AsWorkerState() const;
|
||||
|
||||
const IPCClientState
|
||||
ToIPC() const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _mozilla_dom_ClientState_h
|
|
@ -0,0 +1,36 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ClientInfo.h',
|
||||
'ClientIPCUtils.h',
|
||||
'ClientState.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ClientInfo.cpp',
|
||||
'ClientState.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'ClientIPCTypes.ipdlh',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
]
|
|
@ -1,6 +1,9 @@
|
|||
#
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
|
||||
DIRS += [
|
||||
'manager',
|
||||
]
|
|
@ -100,17 +100,16 @@ private:
|
|||
};
|
||||
|
||||
// This runnable moves a buffer to the IO thread and there, it writes it into
|
||||
// the temporary file.
|
||||
// the temporary file, if its File Descriptor has not been already closed.
|
||||
class WriteRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
static WriteRunnable*
|
||||
CopyBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
|
||||
CopyBuffer(MutableBlobStorage* aBlobStorage,
|
||||
const void* aData, uint32_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aBlobStorage);
|
||||
MOZ_ASSERT(aFD);
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
// We have to take a copy of this buffer.
|
||||
|
@ -120,19 +119,17 @@ public:
|
|||
}
|
||||
|
||||
memcpy((char*)data, aData, aLength);
|
||||
return new WriteRunnable(aBlobStorage, aFD, data, aLength);
|
||||
return new WriteRunnable(aBlobStorage, data, aLength);
|
||||
}
|
||||
|
||||
static WriteRunnable*
|
||||
AdoptBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
|
||||
void* aData, uint32_t aLength)
|
||||
AdoptBuffer(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aBlobStorage);
|
||||
MOZ_ASSERT(aFD);
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
return new WriteRunnable(aBlobStorage, aFD, aData, aLength);
|
||||
return new WriteRunnable(aBlobStorage, aData, aLength);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
|
@ -141,8 +138,15 @@ public:
|
|||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
|
||||
int32_t written = PR_Write(mFD, mData, mLength);
|
||||
PRFileDesc* fd = mBlobStorage->GetFD();
|
||||
if (!fd) {
|
||||
// The file descriptor has been closed in the meantime.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t written = PR_Write(fd, mData, mLength);
|
||||
if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
|
||||
mBlobStorage->CloseFD();
|
||||
return mBlobStorage->EventTarget()->Dispatch(
|
||||
new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
@ -152,19 +156,14 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
WriteRunnable(MutableBlobStorage* aBlobStorage,
|
||||
PRFileDesc* aFD,
|
||||
void* aData,
|
||||
uint32_t aLength)
|
||||
WriteRunnable(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
|
||||
: Runnable("dom::WriteRunnable")
|
||||
, mBlobStorage(aBlobStorage)
|
||||
, mFD(aFD)
|
||||
, mData(aData)
|
||||
, mLength(aLength)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
MOZ_ASSERT(aFD);
|
||||
MOZ_ASSERT(aData);
|
||||
}
|
||||
|
||||
|
@ -174,7 +173,6 @@ private:
|
|||
}
|
||||
|
||||
RefPtr<MutableBlobStorage> mBlobStorage;
|
||||
PRFileDesc* mFD;
|
||||
void* mData;
|
||||
uint32_t mLength;
|
||||
};
|
||||
|
@ -285,13 +283,11 @@ class LastRunnable final : public Runnable
|
|||
{
|
||||
public:
|
||||
LastRunnable(MutableBlobStorage* aBlobStorage,
|
||||
PRFileDesc* aFD,
|
||||
nsISupports* aParent,
|
||||
const nsACString& aContentType,
|
||||
MutableBlobStorageCallback* aCallback)
|
||||
: Runnable("dom::LastRunnable")
|
||||
, mBlobStorage(aBlobStorage)
|
||||
, mFD(aFD)
|
||||
, mParent(aParent)
|
||||
, mContentType(aContentType)
|
||||
, mCallback(aCallback)
|
||||
|
@ -299,7 +295,6 @@ public:
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(aFD);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
|
@ -307,8 +302,7 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
mBlobStorage->CloseFD();
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new CreateBlobRunnable(mBlobStorage, mParent.forget(),
|
||||
|
@ -331,7 +325,6 @@ private:
|
|||
}
|
||||
|
||||
RefPtr<MutableBlobStorage> mBlobStorage;
|
||||
PRFileDesc* mFD;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsCString mContentType;
|
||||
RefPtr<MutableBlobStorageCallback> mCallback;
|
||||
|
@ -398,8 +391,6 @@ MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
|
|||
mStorageState = eClosed;
|
||||
|
||||
if (previousState == eInTemporaryFile) {
|
||||
MOZ_ASSERT(mFD);
|
||||
|
||||
if (NS_FAILED(mErrorResult)) {
|
||||
MOZ_ASSERT(!mActor);
|
||||
|
||||
|
@ -416,9 +407,8 @@ MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
|
|||
// executed in order and this LastRunnable will be... the last one.
|
||||
// This Runnable will also close the FD on the I/O thread.
|
||||
RefPtr<Runnable> runnable =
|
||||
new LastRunnable(this, mFD, aParent, aContentType, aCallback);
|
||||
new LastRunnable(this, aParent, aContentType, aCallback);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
mFD = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -474,10 +464,8 @@ MutableBlobStorage::Append(const void* aData, uint32_t aLength)
|
|||
// If we are already in the temporaryFile mode, we have to dispatch a
|
||||
// runnable.
|
||||
if (mStorageState == eInTemporaryFile) {
|
||||
MOZ_ASSERT(mFD);
|
||||
|
||||
RefPtr<WriteRunnable> runnable =
|
||||
WriteRunnable::CopyBuffer(this, mFD, aData, aLength);
|
||||
WriteRunnable::CopyBuffer(this, aData, aLength);
|
||||
if (NS_WARN_IF(!runnable)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -606,7 +594,7 @@ MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
|
|||
// This runnable takes the ownership of mData and it will write this buffer
|
||||
// into the temporary file.
|
||||
RefPtr<WriteRunnable> runnable =
|
||||
WriteRunnable::AdoptBuffer(this, mFD, mData, mDataLen);
|
||||
WriteRunnable::AdoptBuffer(this, mData, mDataLen);
|
||||
MOZ_ASSERT(runnable);
|
||||
|
||||
mData = nullptr;
|
||||
|
@ -622,10 +610,9 @@ MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
|
|||
MOZ_ASSERT(mPendingCallback);
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new LastRunnable(this, mFD, mPendingParent, mPendingContentType,
|
||||
new LastRunnable(this, mPendingParent, mPendingContentType,
|
||||
mPendingCallback);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
mFD = nullptr;
|
||||
|
||||
mPendingParent = nullptr;
|
||||
mPendingCallback = nullptr;
|
||||
|
@ -680,5 +667,22 @@ MutableBlobStorage::SizeOfCurrentMemoryBuffer() const
|
|||
return mStorageState < eInTemporaryFile ? mDataLen : 0;
|
||||
}
|
||||
|
||||
PRFileDesc*
|
||||
MutableBlobStorage::GetFD() const
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
return mFD;
|
||||
}
|
||||
|
||||
void
|
||||
MutableBlobStorage::CloseFD()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mFD);
|
||||
|
||||
PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
|
|
@ -76,6 +76,10 @@ public:
|
|||
// Note that this intentionally ignores the data in the temp file.
|
||||
size_t SizeOfCurrentMemoryBuffer() const;
|
||||
|
||||
PRFileDesc* GetFD() const;
|
||||
|
||||
void CloseFD();
|
||||
|
||||
private:
|
||||
~MutableBlobStorage();
|
||||
|
||||
|
|
|
@ -31,12 +31,13 @@ interface nsIWorkerDebugger;
|
|||
interface nsIServiceWorkerInfo : nsISupports
|
||||
{
|
||||
// State values below should match the ServiceWorkerState enumeration.
|
||||
const unsigned short STATE_INSTALLING = 0;
|
||||
const unsigned short STATE_INSTALLED = 1;
|
||||
const unsigned short STATE_ACTIVATING = 2;
|
||||
const unsigned short STATE_ACTIVATED = 3;
|
||||
const unsigned short STATE_REDUNDANT = 4;
|
||||
const unsigned short STATE_UNKNOWN = 5;
|
||||
const unsigned short STATE_PARSED = 0;
|
||||
const unsigned short STATE_INSTALLING = 1;
|
||||
const unsigned short STATE_INSTALLED = 2;
|
||||
const unsigned short STATE_ACTIVATING = 3;
|
||||
const unsigned short STATE_ACTIVATED = 4;
|
||||
const unsigned short STATE_REDUNDANT = 5;
|
||||
const unsigned short STATE_UNKNOWN = 6;
|
||||
|
||||
readonly attribute DOMString scriptSpec;
|
||||
readonly attribute DOMString cacheName;
|
||||
|
|
|
@ -63,6 +63,7 @@ interface nsIContentSecurityPolicy : nsISerializable
|
|||
const unsigned short BLOCK_ALL_MIXED_CONTENT = 19;
|
||||
const unsigned short REQUIRE_SRI_FOR = 20;
|
||||
const unsigned short SANDBOX_DIRECTIVE = 21;
|
||||
const unsigned short WORKER_SRC_DIRECTIVE = 22;
|
||||
|
||||
/**
|
||||
* Accessor method for a read-only string version of the policy at a given
|
||||
|
|
|
@ -26,7 +26,6 @@ add_task(async function test_memory_distribution() {
|
|||
return;
|
||||
}
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [["toolkit.telemetry.enabled", true]]});
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
|
||||
let histogram = Services.telemetry.getKeyedHistogramById("MEMORY_DISTRIBUTION_AMONG_CONTENT");
|
||||
|
|
|
@ -8,7 +8,6 @@ add_task(async function test_memory_distribution() {
|
|||
return;
|
||||
}
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [["toolkit.telemetry.enabled", true]]});
|
||||
let canRecordExtended = Services.telemetry.canRecordExtended;
|
||||
Services.telemetry.canRecordExtended = true;
|
||||
registerCleanupFunction(() => Services.telemetry.canRecordExtended = canRecordExtended);
|
||||
|
|
|
@ -112,9 +112,10 @@ couldntParsePort = Couldn’t parse port in %1$S
|
|||
# LOCALIZATION NOTE (duplicateDirective):
|
||||
# %1$S is the name of the duplicate directive
|
||||
duplicateDirective = Duplicate %1$S directives detected. All but the first instance will be ignored.
|
||||
# LOCALIZATION NOTE (deprecatedDirective):
|
||||
# %1$S is the name of the deprecated directive, %2$S is the name of the replacement.
|
||||
deprecatedDirective = Directive ‘%1$S’ has been deprecated. Please use directive ‘%2$S’ instead.
|
||||
# LOCALIZATION NOTE (deprecatedChildSrcDirective):
|
||||
# %1$S is the value of the deprecated directive.
|
||||
# Do not localize: worker-src, frame-src
|
||||
deprecatedChildSrcDirective = Directive ‘%1$S’ has been deprecated. Please use directive ‘worker-src’ to control workers, or directive ‘frame-src’ to control frames respectively.
|
||||
# LOCALIZATION NOTE (couldntParseInvalidSandboxFlag):
|
||||
# %1$S is the option that could not be understood
|
||||
couldntParseInvalidSandboxFlag = Couldn’t parse invalid sandbox flag ‘%1$S’
|
||||
|
|
|
@ -50,6 +50,7 @@ DIRS += [
|
|||
'browser-element',
|
||||
'cache',
|
||||
'canvas',
|
||||
'clients',
|
||||
'commandhandler',
|
||||
'credentialmanagement',
|
||||
'crypto',
|
||||
|
|
|
@ -134,6 +134,8 @@ nsCSPParser::nsCSPParser(cspTokens& aTokens,
|
|||
, mUnsafeInlineKeywordSrc(nullptr)
|
||||
, mChildSrc(nullptr)
|
||||
, mFrameSrc(nullptr)
|
||||
, mWorkerSrc(nullptr)
|
||||
, mScriptSrc(nullptr)
|
||||
, mParsingFrameAncestorsDir(false)
|
||||
, mTokens(aTokens)
|
||||
, mSelfURI(aSelfURI)
|
||||
|
@ -1110,21 +1112,37 @@ nsCSPParser::directiveName()
|
|||
return new nsUpgradeInsecureDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
}
|
||||
|
||||
// child-src has it's own class to handle frame-src if necessary
|
||||
// child-src by itself is deprecatd but will be enforced
|
||||
// * for workers (if worker-src is not explicitly specified)
|
||||
// * for frames (if frame-src is not explicitly specified)
|
||||
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE)) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"deprecatedChildSrcDirective",
|
||||
params, ArrayLength(params));
|
||||
mChildSrc = new nsCSPChildSrcDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
return mChildSrc;
|
||||
}
|
||||
|
||||
// if we have a frame-src, cache it so we can decide whether to use child-src
|
||||
// if we have a frame-src, cache it so we can discard child-src for frames
|
||||
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
|
||||
const char16_t* params[] = { mCurToken.get(), u"child-src" };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedDirective",
|
||||
params, ArrayLength(params));
|
||||
mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
return mFrameSrc;
|
||||
}
|
||||
|
||||
// if we have a worker-src, cache it so we can discard child-src for workers
|
||||
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE)) {
|
||||
mWorkerSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
return mWorkerSrc;
|
||||
}
|
||||
|
||||
// if we have a script-src, cache it as a fallback for worker-src
|
||||
// in case child-src is not present
|
||||
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE)) {
|
||||
mScriptSrc = new nsCSPScriptSrcDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
return mScriptSrc;
|
||||
}
|
||||
|
||||
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
|
||||
return new nsRequireSRIForDirective(CSP_StringToCSPDirective(mCurToken));
|
||||
}
|
||||
|
@ -1301,9 +1319,22 @@ nsCSPParser::policy()
|
|||
directive();
|
||||
}
|
||||
|
||||
if (mChildSrc && !mFrameSrc) {
|
||||
// if we have a child-src, it handles frame-src too, unless frame-src is set
|
||||
mChildSrc->setHandleFrameSrc();
|
||||
if (mChildSrc) {
|
||||
if (!mFrameSrc) {
|
||||
// if frame-src is specified explicitly for that policy than child-src should
|
||||
// not restrict frames; if not, than child-src needs to restrict frames.
|
||||
mChildSrc->setRestrictFrames();
|
||||
}
|
||||
if (!mWorkerSrc) {
|
||||
// if worker-src is specified explicitly for that policy than child-src should
|
||||
// not restrict workers; if not, than child-src needs to restrict workers.
|
||||
mChildSrc->setRestrictWorkers();
|
||||
}
|
||||
}
|
||||
// if script-src is specified, but not worker-src and also no child-src, then
|
||||
// script-src has to govern workers.
|
||||
if (mScriptSrc && !mWorkerSrc && !mChildSrc) {
|
||||
mScriptSrc->setRestrictWorkers();
|
||||
}
|
||||
|
||||
return mPolicy;
|
||||
|
|
|
@ -242,14 +242,17 @@ class nsCSPParser {
|
|||
bool mStrictDynamic; // false, if 'strict-dynamic' is not defined
|
||||
nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate()
|
||||
|
||||
// cache variables for child-src and frame-src directive handling.
|
||||
// frame-src is deprecated in favor of child-src, however if we
|
||||
// see a frame-src directive, it takes precedence for frames and iframes.
|
||||
// At the end of parsing, if we have a child-src directive, we need to
|
||||
// decide whether it will handle frames, or if there is a frame-src we
|
||||
// should honor instead.
|
||||
nsCSPChildSrcDirective* mChildSrc;
|
||||
nsCSPDirective* mFrameSrc;
|
||||
// cache variables for child-src, frame-src and worker-src handling;
|
||||
// in CSP 3 child-src is deprecated. For backwards compatibility
|
||||
// child-src needs to restrict:
|
||||
// (*) frames, in case frame-src is not expicitly specified
|
||||
// (*) workers, in case worker-src is not expicitly specified
|
||||
// If neither worker-src, nor child-src is present, then script-src
|
||||
// needs to govern workers.
|
||||
nsCSPChildSrcDirective* mChildSrc;
|
||||
nsCSPDirective* mFrameSrc;
|
||||
nsCSPDirective* mWorkerSrc;
|
||||
nsCSPScriptSrcDirective* mScriptSrc;
|
||||
|
||||
// cache variable to let nsCSPHostSrc know that it's within
|
||||
// the frame-ancestors directive.
|
||||
|
|
|
@ -232,7 +232,7 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
|
|||
case nsIContentPolicy::TYPE_INTERNAL_WORKER:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
|
||||
return nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE;
|
||||
return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE;
|
||||
|
||||
case nsIContentPolicy::TYPE_SUBDOCUMENT:
|
||||
return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
|
||||
|
@ -1190,6 +1190,11 @@ nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
|
|||
outCSP.mSandbox.Value() = mozilla::Move(srcs);
|
||||
return;
|
||||
|
||||
case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE:
|
||||
outCSP.mWorker_src.Construct();
|
||||
outCSP.mWorker_src.Value() = mozilla::Move(srcs);
|
||||
return;
|
||||
|
||||
// REFERRER_DIRECTIVE and REQUIRE_SRI_FOR are handled in nsCSPPolicy::toDomCSPStruct()
|
||||
|
||||
default:
|
||||
|
@ -1242,7 +1247,8 @@ bool nsCSPDirective::equals(CSPDirective aDirective) const
|
|||
|
||||
nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
|
||||
: nsCSPDirective(aDirective)
|
||||
, mHandleFrameSrc(false)
|
||||
, mRestrictFrames(false)
|
||||
, mRestrictWorkers(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1250,30 +1256,58 @@ nsCSPChildSrcDirective::~nsCSPChildSrcDirective()
|
|||
{
|
||||
}
|
||||
|
||||
void nsCSPChildSrcDirective::setHandleFrameSrc()
|
||||
{
|
||||
mHandleFrameSrc = true;
|
||||
}
|
||||
|
||||
bool nsCSPChildSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const
|
||||
{
|
||||
if (aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
return mHandleFrameSrc;
|
||||
return mRestrictFrames;
|
||||
}
|
||||
|
||||
return (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER
|
||||
|| aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER
|
||||
|| aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
|
||||
);
|
||||
if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER ||
|
||||
aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) {
|
||||
return mRestrictWorkers;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const
|
||||
{
|
||||
if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
|
||||
return mHandleFrameSrc;
|
||||
return mRestrictFrames;
|
||||
}
|
||||
if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
|
||||
return mRestrictWorkers;
|
||||
}
|
||||
return (mDirective == aDirective);
|
||||
}
|
||||
|
||||
return (aDirective == nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE);
|
||||
/* =============== nsCSPScriptSrcDirective ============= */
|
||||
|
||||
nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
|
||||
: nsCSPDirective(aDirective)
|
||||
, mRestrictWorkers(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective()
|
||||
{
|
||||
}
|
||||
|
||||
bool nsCSPScriptSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const
|
||||
{
|
||||
if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER ||
|
||||
aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) {
|
||||
return mRestrictWorkers;
|
||||
}
|
||||
return mDirective == CSP_ContentTypeToDirective(aContentType);
|
||||
}
|
||||
|
||||
bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const
|
||||
{
|
||||
if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
|
||||
return mRestrictWorkers;
|
||||
}
|
||||
return (mDirective == aDirective);
|
||||
}
|
||||
|
||||
/* =============== nsBlockAllMixedContentDirective ============= */
|
||||
|
|
|
@ -94,7 +94,8 @@ static const char* CSPStrDirectives[] = {
|
|||
"child-src", // CHILD_SRC_DIRECTIVE
|
||||
"block-all-mixed-content", // BLOCK_ALL_MIXED_CONTENT
|
||||
"require-sri-for", // REQUIRE_SRI_FOR
|
||||
"sandbox" // SANDBOX_DIRECTIVE
|
||||
"sandbox", // SANDBOX_DIRECTIVE
|
||||
"worker-src" // WORKER_SRC_DIRECTIVE
|
||||
};
|
||||
|
||||
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
|
||||
|
@ -470,7 +471,7 @@ class nsCSPDirective {
|
|||
|
||||
bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
CSPDirective mDirective;
|
||||
nsTArray<nsCSPBaseSrc*> mSrcs;
|
||||
};
|
||||
|
@ -478,26 +479,52 @@ class nsCSPDirective {
|
|||
/* =============== nsCSPChildSrcDirective ============= */
|
||||
|
||||
/*
|
||||
* In CSP 2, the child-src directive covers both workers and
|
||||
* subdocuments (i.e., frames and iframes). Workers were removed
|
||||
* from script-src, but frames can be controlled by either child-src
|
||||
* or frame-src directives, so child-src needs to know whether it should
|
||||
* also restrict frames. When both are present the frame-src directive
|
||||
* takes precedent.
|
||||
* In CSP 3 child-src is deprecated. For backwards compatibility
|
||||
* child-src needs to restrict:
|
||||
* (*) frames, in case frame-src is not expicitly specified
|
||||
* (*) workers, in case worker-src is not expicitly specified
|
||||
*/
|
||||
class nsCSPChildSrcDirective : public nsCSPDirective {
|
||||
public:
|
||||
explicit nsCSPChildSrcDirective(CSPDirective aDirective);
|
||||
virtual ~nsCSPChildSrcDirective();
|
||||
|
||||
void setHandleFrameSrc();
|
||||
void setRestrictFrames()
|
||||
{ mRestrictFrames = true; }
|
||||
|
||||
void setRestrictWorkers()
|
||||
{ mRestrictWorkers = true; }
|
||||
|
||||
virtual bool restrictsContentType(nsContentPolicyType aContentType) const;
|
||||
|
||||
virtual bool equals(CSPDirective aDirective) const;
|
||||
|
||||
private:
|
||||
bool mHandleFrameSrc;
|
||||
bool mRestrictFrames;
|
||||
bool mRestrictWorkers;
|
||||
};
|
||||
|
||||
/* =============== nsCSPScriptSrcDirective ============= */
|
||||
|
||||
/*
|
||||
* In CSP 3 worker-src restricts workers, for backwards compatibily
|
||||
* script-src has to restrict workers as the ultimate fallback if
|
||||
* neither worker-src nor child-src is present in a CSP.
|
||||
*/
|
||||
class nsCSPScriptSrcDirective : public nsCSPDirective {
|
||||
public:
|
||||
explicit nsCSPScriptSrcDirective(CSPDirective aDirective);
|
||||
virtual ~nsCSPScriptSrcDirective();
|
||||
|
||||
void setRestrictWorkers()
|
||||
{ mRestrictWorkers = true; }
|
||||
|
||||
virtual bool restrictsContentType(nsContentPolicyType aContentType) const;
|
||||
|
||||
virtual bool equals(CSPDirective aDirective) const;
|
||||
|
||||
private:
|
||||
bool mRestrictWorkers;
|
||||
};
|
||||
|
||||
/* =============== nsBlockAllMixedContentDirective === */
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
let testframe = document.getElementById("testframe");
|
||||
testframe.onload = function() {
|
||||
parent.postMessage({
|
||||
result: "frame-allowed",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
}
|
||||
testframe.onerror = function() {
|
||||
parent.postMessage({
|
||||
result: "frame-blocked",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
}
|
||||
testframe.src = "file_frame_src_inner.html"
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="child-src https://example.com">";
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="testframe"></iframe>
|
||||
<script type="text/javascript" src="file_frame_src.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="frame-src https://example.com; child-src 'none'">";
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="testframe"></iframe>
|
||||
<script type="text/javascript" src="file_frame_src.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
dummy iframe
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
// dummy file
|
|
@ -0,0 +1,7 @@
|
|||
onconnect = function(e) {
|
||||
var port = e.ports[0];
|
||||
port.addEventListener("message", function(e) {
|
||||
port.postMessage("shared worker is executing");
|
||||
});
|
||||
port.start();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
postMessage("worker is executing");
|
|
@ -0,0 +1,53 @@
|
|||
var mySharedWorker = new SharedWorker('file_spawn_shared_worker.js');
|
||||
mySharedWorker.port.onmessage = function(ev) {
|
||||
parent.postMessage({
|
||||
result: "shared-worker-allowed",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
mySharedWorker.port.close();
|
||||
}
|
||||
mySharedWorker.onerror = function(evt) {
|
||||
evt.preventDefault();
|
||||
parent.postMessage({
|
||||
result: "shared-worker-blocked",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
mySharedWorker.port.close();
|
||||
}
|
||||
mySharedWorker.port.start();
|
||||
mySharedWorker.port.postMessage('foo');
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
let myWorker = new Worker("file_spawn_worker.js");
|
||||
myWorker.onmessage = function(event) {
|
||||
parent.postMessage({
|
||||
result: "worker-allowed",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
}
|
||||
myWorker.onerror = function(event) {
|
||||
parent.postMessage({
|
||||
result: "worker-blocked",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
navigator.serviceWorker.register('file_spawn_service_worker.js')
|
||||
.then(function(reg) {
|
||||
// registration worked
|
||||
reg.unregister().then(function() {
|
||||
parent.postMessage({
|
||||
result: "service-worker-allowed",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
});
|
||||
}).catch(function(error) {
|
||||
// registration failed
|
||||
parent.postMessage({
|
||||
result: "service-worker-blocked",
|
||||
href: document.location.href,
|
||||
}, "*");
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="child-src https://example.com; script-src 'nonce-foo'">";
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-foo' https://example.com">";
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="worker-src https://example.com; child-src 'none'; script-src 'nonce-foo'">";
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -331,3 +331,18 @@ support-files =
|
|||
support-files =
|
||||
file_sandbox_allow_scripts.html
|
||||
file_sandbox_allow_scripts.html^headers^
|
||||
[test_worker_src.html]
|
||||
support-files =
|
||||
file_worker_src_worker_governs.html
|
||||
file_worker_src_child_governs.html
|
||||
file_worker_src_script_governs.html
|
||||
file_worker_src.js
|
||||
file_spawn_worker.js
|
||||
file_spawn_shared_worker.js
|
||||
file_spawn_service_worker.js
|
||||
[test_frame_src.html]
|
||||
support-files =
|
||||
file_frame_src_frame_governs.html
|
||||
file_frame_src_child_governs.html
|
||||
file_frame_src.js
|
||||
file_frame_src_inner.html
|
||||
|
|
|
@ -83,19 +83,19 @@
|
|||
id: "script-src-worker",
|
||||
file: WORKER_TEST_FILE,
|
||||
result : "blocked",
|
||||
policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
|
||||
policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
|
||||
},
|
||||
'script-src-service_worker': {
|
||||
id: "script-src-service_worker",
|
||||
file: SERVICE_WORKER_TEST_FILE,
|
||||
result : "blocked",
|
||||
policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
|
||||
policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
|
||||
},
|
||||
'script-src-self-shared_worker': {
|
||||
id: "script-src-self-shared_worker",
|
||||
file: SHARED_WORKER_TEST_FILE,
|
||||
result : "blocked",
|
||||
policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
|
||||
policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1302667 - Test frame-src</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<iframe style="width:100%;" id="testframe"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
/* Description of the test:
|
||||
* We load a page inlcuding a frame a CSP of:
|
||||
* >> frame-src https://example.com; child-src 'none'
|
||||
* and make sure that frame-src governs frames correctly. In addition,
|
||||
* we make sure that child-src is discarded in case frame-src is specified.
|
||||
*/
|
||||
|
||||
const ORIGIN_1 = "https://example.com/tests/dom/security/test/csp/";
|
||||
const ORIGIN_2 = "https://test1.example.com/tests/dom/security/test/csp/";
|
||||
|
||||
let TESTS = [
|
||||
// frame-src tests
|
||||
ORIGIN_1 + "file_frame_src_frame_governs.html",
|
||||
ORIGIN_2 + "file_frame_src_frame_governs.html",
|
||||
// child-src tests
|
||||
ORIGIN_1 + "file_frame_src_child_governs.html",
|
||||
ORIGIN_2 + "file_frame_src_child_governs.html",
|
||||
];
|
||||
|
||||
let testIndex = 0;
|
||||
|
||||
function checkFinish() {
|
||||
if (testIndex >= TESTS.length) {
|
||||
window.removeEventListener("message", receiveMessage);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage);
|
||||
function receiveMessage(event) {
|
||||
let href = event.data.href;
|
||||
let result = event.data.result;
|
||||
|
||||
if (href.startsWith("https://example.com")) {
|
||||
if (result == "frame-allowed") {
|
||||
ok(true, "allowing frame from https://example.com (" + result + ")");
|
||||
}
|
||||
else {
|
||||
ok(false, "blocking frame from https://example.com (" + result + ")");
|
||||
}
|
||||
}
|
||||
else if (href.startsWith("https://test1.example.com")) {
|
||||
if (result == "frame-blocked") {
|
||||
ok(true, "blocking frame from https://test1.example.com (" + result + ")");
|
||||
}
|
||||
else {
|
||||
ok(false, "allowing frame from https://test1.example.com (" + result + ")");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sanity check, we should never enter that branch, bust just in case...
|
||||
ok(false, "unexpected result: " + result);
|
||||
}
|
||||
checkFinish();
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
document.getElementById("testframe").src = TESTS[testIndex];
|
||||
testIndex++;
|
||||
}
|
||||
|
||||
// fire up the tests
|
||||
runNextTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1302667 - Test worker-src</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<iframe style="width:100%;" id="testframe"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestLongerTimeout(3);
|
||||
|
||||
/* Description of the test:
|
||||
* We load a page inlcuding a worker, a shared worker as well as a
|
||||
* service worker with a CSP of:
|
||||
* >> worker-src https://example.com; child-src 'none'; script-src 'nonce-foo'
|
||||
* and make sure that worker-src governs these three kinds of workers correctly.
|
||||
* In addition, we make sure that child-src as well as script-src is discarded
|
||||
* in case worker-src is specified. Ideally we would use "script-src 'none'" but
|
||||
* we have to whitelist the actual script that spawns the workers, hence the nonce.
|
||||
*/
|
||||
|
||||
let ALLOWED_HOST = "https://example.com/tests/dom/security/test/csp/";
|
||||
let BLOCKED_HOST = "https://test1.example.com/tests/dom/security/test/csp/";
|
||||
|
||||
let TESTS = [
|
||||
// allowed
|
||||
ALLOWED_HOST + "file_worker_src_worker_governs.html",
|
||||
ALLOWED_HOST + "file_worker_src_child_governs.html",
|
||||
ALLOWED_HOST + "file_worker_src_script_governs.html",
|
||||
// blocked
|
||||
BLOCKED_HOST + "file_worker_src_worker_governs.html",
|
||||
BLOCKED_HOST + "file_worker_src_child_governs.html",
|
||||
BLOCKED_HOST + "file_worker_src_script_governs.html",
|
||||
];
|
||||
|
||||
let numberSubTests = 3; // 1 web worker, 1 shared worker, 1 service worker
|
||||
let subTestCounter = 0; // keeps track of how many
|
||||
let testIndex = 0;
|
||||
|
||||
function checkFinish() {
|
||||
subTestCounter = 0;
|
||||
testIndex++;
|
||||
if (testIndex < TESTS.length) {
|
||||
runNextTest();
|
||||
return;
|
||||
}
|
||||
window.removeEventListener("message", receiveMessage);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage);
|
||||
function receiveMessage(event) {
|
||||
let href = event.data.href;
|
||||
let result = event.data.result;
|
||||
|
||||
if (href.startsWith("https://example.com")) {
|
||||
if (result == "worker-allowed" ||
|
||||
result == "shared-worker-allowed" ||
|
||||
result == "service-worker-allowed") {
|
||||
ok(true, "allowing worker from https://example.com (" + result + ")");
|
||||
}
|
||||
else {
|
||||
ok(false, "blocking worker from https://example.com (" + result + ")");
|
||||
}
|
||||
}
|
||||
else if (href.startsWith("https://test1.example.com")) {
|
||||
if (result == "worker-blocked" ||
|
||||
result == "shared-worker-blocked" ||
|
||||
result == "service-worker-blocked") {
|
||||
ok(true, "blocking worker from https://test1.example.com (" + result + ")");
|
||||
}
|
||||
else {
|
||||
ok(false, "allowing worker from https://test1.example.com (" + result + ")");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sanity check, we should never enter that branch, bust just in case...
|
||||
ok(false, "unexpected result: " + result);
|
||||
}
|
||||
subTestCounter++;
|
||||
if (subTestCounter < numberSubTests) {
|
||||
return;
|
||||
}
|
||||
checkFinish();
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
document.getElementById("testframe").src = TESTS[testIndex];
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]}, function() {
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -219,6 +219,10 @@ TEST(CSPParser, Directives)
|
|||
"script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https:" },
|
||||
{ "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https: ",
|
||||
"default-src 'sha256-siVR8' 'unsafe-inline' https:" },
|
||||
{ "worker-src https://example.com",
|
||||
"worker-src https://example.com" },
|
||||
{ "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com",
|
||||
"worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com" },
|
||||
};
|
||||
|
||||
uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
|
||||
|
|
|
@ -408,7 +408,6 @@ function SetupPrefTestEnvironment(which, additional_prefs) {
|
|||
settings.use_hsts],
|
||||
["security.mixed_content.send_hsts_priming",
|
||||
settings.send_hsts_priming],
|
||||
["toolkit.telemetry.enabled", true],
|
||||
];
|
||||
|
||||
if (additional_prefs) {
|
||||
|
|
|
@ -4,6 +4,7 @@ skip-if = toolkit == 'android'
|
|||
[test_closewindow-with-pointerlock.html]
|
||||
|
||||
[test_pointerlock-api.html]
|
||||
skip-if = os == "linux" || os == "win" # Bug 1357082
|
||||
tags = fullscreen
|
||||
support-files =
|
||||
pointerlock_utils.js
|
||||
|
|
|
@ -30,6 +30,7 @@ dictionary CSP {
|
|||
sequence<DOMString> block-all-mixed-content;
|
||||
sequence<DOMString> require-sri-for;
|
||||
sequence<DOMString> sandbox;
|
||||
sequence<DOMString> worker-src;
|
||||
};
|
||||
|
||||
dictionary CSPPolicies {
|
||||
|
|
|
@ -26,6 +26,9 @@ interface ServiceWorker : EventTarget {
|
|||
ServiceWorker implements AbstractWorker;
|
||||
|
||||
enum ServiceWorkerState {
|
||||
// https://github.com/w3c/ServiceWorker/issues/1162
|
||||
"parsed",
|
||||
|
||||
"installing",
|
||||
"installed",
|
||||
"activating",
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* 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 PBackgroundSharedTypes;
|
||||
|
||||
using ServiceWorkerState from "mozilla/dom/ServiceWorkerIPCUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// IPC type with enough information to create a ServiceWorker DOM object
|
||||
// in a child process. Note that the state may be slightly out-of-sync
|
||||
// with the parent and should be updated dynamically if necessary.
|
||||
struct IPCServiceWorkerDescriptor
|
||||
{
|
||||
uint64_t id;
|
||||
PrincipalInfo principalInfo;
|
||||
nsCString scope;
|
||||
ServiceWorkerState state;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerDescriptor.h"
|
||||
#include "mozilla/dom/IPCServiceWorkerDescriptor.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor()
|
||||
: mData(MakeUnique<IPCServiceWorkerDescriptor>())
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(uint64_t aId,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerState aState)
|
||||
: mData(MakeUnique<IPCServiceWorkerDescriptor>(aId, aPrincipalInfo,
|
||||
nsCString(aScope), aState))
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(const IPCServiceWorkerDescriptor& aDescriptor)
|
||||
: mData(MakeUnique<IPCServiceWorkerDescriptor>(aDescriptor))
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(const ServiceWorkerDescriptor& aRight)
|
||||
{
|
||||
operator=(aRight);
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor&
|
||||
ServiceWorkerDescriptor::operator=(const ServiceWorkerDescriptor& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = MakeUnique<IPCServiceWorkerDescriptor>(*aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(ServiceWorkerDescriptor&& aRight)
|
||||
: mData(Move(aRight.mData))
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor&
|
||||
ServiceWorkerDescriptor::operator=(ServiceWorkerDescriptor&& aRight)
|
||||
{
|
||||
mData.reset();
|
||||
mData = Move(aRight.mData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::~ServiceWorkerDescriptor()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerDescriptor::operator==(const ServiceWorkerDescriptor& aRight) const
|
||||
{
|
||||
return *mData == *aRight.mData;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ServiceWorkerDescriptor::Id() const
|
||||
{
|
||||
return mData->id();
|
||||
}
|
||||
|
||||
const mozilla::ipc::PrincipalInfo&
|
||||
ServiceWorkerDescriptor::PrincipalInfo() const
|
||||
{
|
||||
return mData->principalInfo();
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ServiceWorkerDescriptor::Scope() const
|
||||
{
|
||||
return mData->scope();
|
||||
}
|
||||
|
||||
ServiceWorkerState
|
||||
ServiceWorkerDescriptor::State() const
|
||||
{
|
||||
return mData->state();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerDescriptor::SetState(ServiceWorkerState aState)
|
||||
{
|
||||
mData->state() = aState;
|
||||
}
|
||||
|
||||
const IPCServiceWorkerDescriptor&
|
||||
ServiceWorkerDescriptor::ToIPC() const
|
||||
{
|
||||
return *mData;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _mozilla_dom_ServiceWorkerDescriptor_h
|
||||
#define _mozilla_dom_ServiceWorkerDescriptor_h
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class IPCServiceWorkerDescriptor;
|
||||
enum class ServiceWorkerState : uint8_t;
|
||||
|
||||
// This class represents a snapshot of a particular ServiceWorkerInfo object.
|
||||
// It is threadsafe and can be transferred across processes. This is useful
|
||||
// because most of its values are immutable and can be relied upon to be
|
||||
// accurate. Currently the only variable field is the ServiceWorkerState.
|
||||
class ServiceWorkerDescriptor final
|
||||
{
|
||||
// This class is largely a wrapper wround an IPDL generated struct. We
|
||||
// need the wrapper class since IPDL generated code includes windows.h
|
||||
// which is in turn incompatible with bindings code.
|
||||
UniquePtr<IPCServiceWorkerDescriptor> mData;
|
||||
|
||||
public:
|
||||
ServiceWorkerDescriptor();
|
||||
|
||||
ServiceWorkerDescriptor(uint64_t aId,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerState aState);
|
||||
|
||||
explicit ServiceWorkerDescriptor(const IPCServiceWorkerDescriptor& aDescriptor);
|
||||
|
||||
ServiceWorkerDescriptor(const ServiceWorkerDescriptor& aRight);
|
||||
|
||||
ServiceWorkerDescriptor&
|
||||
operator=(const ServiceWorkerDescriptor& aRight);
|
||||
|
||||
ServiceWorkerDescriptor(ServiceWorkerDescriptor&& aRight);
|
||||
|
||||
ServiceWorkerDescriptor&
|
||||
operator=(ServiceWorkerDescriptor&& aRight);
|
||||
|
||||
~ServiceWorkerDescriptor();
|
||||
|
||||
bool
|
||||
operator==(const ServiceWorkerDescriptor& aRight) const;
|
||||
|
||||
uint64_t
|
||||
Id() const;
|
||||
|
||||
const mozilla::ipc::PrincipalInfo&
|
||||
PrincipalInfo() const;
|
||||
|
||||
const nsCString&
|
||||
Scope() const;
|
||||
|
||||
ServiceWorkerState
|
||||
State() const;
|
||||
|
||||
void
|
||||
SetState(ServiceWorkerState aState);
|
||||
|
||||
// Expose the underlying IPC type so that it can be passed via IPC.
|
||||
const IPCServiceWorkerDescriptor&
|
||||
ToIPC() const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _mozilla_dom_ServiceWorkerDescriptor_h
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef _mozilla_dom_ServiceWorkerIPCUtils_h
|
||||
#define _mozilla_dom_ServiceWorkerIPCUtils_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::ServiceWorkerState> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::ServiceWorkerState,
|
||||
mozilla::dom::ServiceWorkerState::Parsed,
|
||||
mozilla::dom::ServiceWorkerState::EndGuard_>
|
||||
{};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // _mozilla_dom_ServiceWorkerIPCUtils_h
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
static_assert(nsIServiceWorkerInfo::STATE_PARSED == static_cast<uint16_t>(ServiceWorkerState::Parsed),
|
||||
"ServiceWorkerState enumeration value should match state values from nsIServiceWorkerInfo.");
|
||||
static_assert(nsIServiceWorkerInfo::STATE_INSTALLING == static_cast<uint16_t>(ServiceWorkerState::Installing),
|
||||
"ServiceWorkerState enumeration value should match state values from nsIServiceWorkerInfo.");
|
||||
static_assert(nsIServiceWorkerInfo::STATE_INSTALLED == static_cast<uint16_t>(ServiceWorkerState::Installed),
|
||||
|
@ -46,7 +48,7 @@ ServiceWorkerInfo::GetState(uint16_t* aState)
|
|||
{
|
||||
MOZ_ASSERT(aState);
|
||||
AssertIsOnMainThread();
|
||||
*aState = static_cast<uint16_t>(mState);
|
||||
*aState = static_cast<uint16_t>(State());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -182,25 +184,30 @@ ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
|
|||
// Any state can directly transition to redundant, but everything else is
|
||||
// ordered.
|
||||
if (aState != ServiceWorkerState::Redundant) {
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
|
||||
MOZ_ASSERT_IF(State() == ServiceWorkerState::EndGuard_,
|
||||
aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(State() == ServiceWorkerState::Installing,
|
||||
aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(State() == ServiceWorkerState::Installed,
|
||||
aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(State() == ServiceWorkerState::Activating,
|
||||
aState == ServiceWorkerState::Activated);
|
||||
}
|
||||
// Activated can only go to redundant.
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
|
||||
MOZ_ASSERT_IF(State() == ServiceWorkerState::Activated,
|
||||
aState == ServiceWorkerState::Redundant);
|
||||
#endif
|
||||
// Flush any pending functional events to the worker when it transitions to the
|
||||
// activated state.
|
||||
// TODO: Do we care that these events will race with the propagation of the
|
||||
// state change?
|
||||
if (aState == ServiceWorkerState::Activated && mState != aState) {
|
||||
mServiceWorkerPrivate->Activated();
|
||||
if (State() != aState) {
|
||||
mServiceWorkerPrivate->UpdateState(aState);
|
||||
}
|
||||
mState = aState;
|
||||
nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, mState);
|
||||
mDescriptor.SetState(aState);
|
||||
nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, State());
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget()));
|
||||
if (mState == ServiceWorkerState::Redundant) {
|
||||
if (State() == ServiceWorkerState::Redundant) {
|
||||
serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName);
|
||||
}
|
||||
}
|
||||
|
@ -211,12 +218,9 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
|||
const nsAString& aCacheName,
|
||||
nsLoadFlags aImportsLoadFlags)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCacheName(aCacheName)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
, mImportsLoadFlags(aImportsLoadFlags)
|
||||
, mServiceWorkerID(GetNextID())
|
||||
, mCreationTime(PR_Now())
|
||||
, mCreationTimeStamp(TimeStamp::Now())
|
||||
, mInstalledTime(0)
|
||||
|
@ -229,7 +233,6 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
|||
MOZ_ASSERT(mPrincipal);
|
||||
// cache origin attributes so we can use them off main thread
|
||||
mOriginAttributes = mPrincipal->OriginAttributesRef();
|
||||
MOZ_ASSERT(!mScope.IsEmpty());
|
||||
MOZ_ASSERT(!mScriptSpec.IsEmpty());
|
||||
MOZ_ASSERT(!mCacheName.IsEmpty());
|
||||
|
||||
|
@ -237,6 +240,12 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
|||
// Otherwise, we might not be able to update a service worker correctly, if
|
||||
// there is a service worker generating the script.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mImportsLoadFlags & nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
|
||||
|
||||
PrincipalInfo principalInfo;
|
||||
MOZ_ALWAYS_SUCCEEDS(PrincipalToPrincipalInfo(aPrincipal, &principalInfo));
|
||||
|
||||
mDescriptor = ServiceWorkerDescriptor(GetNextID(), principalInfo, aScope,
|
||||
ServiceWorkerState::Parsed);
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::~ServiceWorkerInfo()
|
||||
|
@ -279,7 +288,7 @@ ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow)
|
|||
void
|
||||
ServiceWorkerInfo::UpdateInstalledTime()
|
||||
{
|
||||
MOZ_ASSERT(mState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT(State() == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT(mInstalledTime == 0);
|
||||
|
||||
mInstalledTime =
|
||||
|
@ -290,7 +299,7 @@ ServiceWorkerInfo::UpdateInstalledTime()
|
|||
void
|
||||
ServiceWorkerInfo::UpdateActivatedTime()
|
||||
{
|
||||
MOZ_ASSERT(mState == ServiceWorkerState::Activated);
|
||||
MOZ_ASSERT(State() == ServiceWorkerState::Activated);
|
||||
MOZ_ASSERT(mActivatedTime == 0);
|
||||
|
||||
mActivatedTime =
|
||||
|
@ -301,7 +310,7 @@ ServiceWorkerInfo::UpdateActivatedTime()
|
|||
void
|
||||
ServiceWorkerInfo::UpdateRedundantTime()
|
||||
{
|
||||
MOZ_ASSERT(mState == ServiceWorkerState::Redundant);
|
||||
MOZ_ASSERT(State() == ServiceWorkerState::Redundant);
|
||||
MOZ_ASSERT(mRedundantTime == 0);
|
||||
|
||||
mRedundantTime =
|
||||
|
|
|
@ -28,10 +28,9 @@ class ServiceWorkerInfo final : public nsIServiceWorkerInfo
|
|||
{
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
ServiceWorkerDescriptor mDescriptor;
|
||||
const nsCString mScriptSpec;
|
||||
const nsString mCacheName;
|
||||
ServiceWorkerState mState;
|
||||
OriginAttributes mOriginAttributes;
|
||||
|
||||
// This LoadFlags is only applied to imported scripts, since the main script
|
||||
|
@ -44,10 +43,6 @@ private:
|
|||
// depends on whether the update timer is expired.
|
||||
const nsLoadFlags mImportsLoadFlags;
|
||||
|
||||
// This id is shared with WorkerPrivate to match requests issued by service
|
||||
// workers to their corresponding serviceWorkerInfo.
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
// Timestamp to track SW's state
|
||||
PRTime mCreationTime;
|
||||
TimeStamp mCreationTimeStamp;
|
||||
|
@ -106,7 +101,7 @@ public:
|
|||
const nsCString&
|
||||
Scope() const
|
||||
{
|
||||
return mScope;
|
||||
return mDescriptor.Scope();
|
||||
}
|
||||
|
||||
bool SkipWaitingFlag() const
|
||||
|
@ -130,7 +125,7 @@ public:
|
|||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
return mDescriptor.State();
|
||||
}
|
||||
|
||||
const OriginAttributes&
|
||||
|
@ -154,7 +149,13 @@ public:
|
|||
uint64_t
|
||||
ID() const
|
||||
{
|
||||
return mServiceWorkerID;
|
||||
return mDescriptor.Id();
|
||||
}
|
||||
|
||||
const ServiceWorkerDescriptor&
|
||||
Descriptor() const
|
||||
{
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -165,7 +166,7 @@ public:
|
|||
SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mState = aState;
|
||||
mDescriptor.SetState(aState);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -2542,9 +2542,11 @@ public:
|
|||
{
|
||||
AssertIsOnMainThread();
|
||||
NS_WARNING("Unexpected error while dispatching fetch event!");
|
||||
DebugOnly<nsresult> rv = mChannel->ResetInterception();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to resume intercepted network request");
|
||||
nsresult rv = mChannel->ResetInterception();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to resume intercepted network request");
|
||||
mChannel->CancelInterception(rv);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
|
|
|
@ -1556,8 +1556,10 @@ private:
|
|||
mChannel->SaveTimeStamps();
|
||||
|
||||
nsresult rv = mChannel->ResetInterception();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to resume intercepted network request");
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to resume intercepted network request");
|
||||
mChannel->CancelInterception(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
@ -1684,7 +1686,11 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
|
|||
// condition we handle the reset here instead of returning an error which
|
||||
// would in turn trigger a console report.
|
||||
if (!registration) {
|
||||
aChannel->ResetInterception();
|
||||
nsresult rv = aChannel->ResetInterception();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to resume intercepted network request");
|
||||
aChannel->CancelInterception(rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1692,7 +1698,11 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
|
|||
// any fetch event handlers, then abort the interception and maybe trigger
|
||||
// the soft update algorithm.
|
||||
if (!mInfo->HandlesFetch()) {
|
||||
aChannel->ResetInterception();
|
||||
nsresult rv = aChannel->ResetInterception();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to resume intercepted network request");
|
||||
aChannel->CancelInterception(rv);
|
||||
}
|
||||
|
||||
// Trigger soft updates if necessary.
|
||||
registration->MaybeScheduleTimeCheckAndUpdate();
|
||||
|
@ -1807,7 +1817,16 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
|||
info.mResolvedScriptURI = info.mBaseURI;
|
||||
MOZ_ASSERT(!mInfo->CacheName().IsEmpty());
|
||||
info.mServiceWorkerCacheName = mInfo->CacheName();
|
||||
info.mServiceWorkerID = mInfo->ID();
|
||||
|
||||
PrincipalInfo principalInfo;
|
||||
rv = PrincipalToPrincipalInfo(mInfo->Principal(), &principalInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
info.mServiceWorkerDescriptor.emplace(ServiceWorkerDescriptor(mInfo->ID(),
|
||||
principalInfo,
|
||||
mInfo->Scope(),
|
||||
mInfo->State()));
|
||||
|
||||
info.mLoadGroup = aLoadGroup;
|
||||
info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
|
||||
|
||||
|
@ -1945,15 +1964,48 @@ ServiceWorkerPrivate::NoteDeadServiceWorkerInfo()
|
|||
TerminateWorker();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class UpdateStateControlRunnable final : public MainThreadWorkerControlRunnable
|
||||
{
|
||||
const ServiceWorkerState mState;
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->UpdateServiceWorkerState(mState);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
UpdateStateControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
ServiceWorkerState aState)
|
||||
: MainThreadWorkerControlRunnable(aWorkerPrivate)
|
||||
, mState(aState)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ServiceWorkerPrivate::Activated()
|
||||
ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// If we had to queue up events due to the worker activating, that means
|
||||
// the worker must be currently running. We should be called synchronously
|
||||
// when the worker becomes activated.
|
||||
MOZ_ASSERT_IF(!mPendingFunctionalEvents.IsEmpty(), mWorkerPrivate);
|
||||
if (!mWorkerPrivate) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPendingFunctionalEvents.IsEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<WorkerRunnable> r =
|
||||
new UpdateStateControlRunnable(mWorkerPrivate, aState);
|
||||
Unused << r->Dispatch();
|
||||
|
||||
if (aState != ServiceWorkerState::Activated) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<WorkerRunnable>> pendingEvents;
|
||||
mPendingFunctionalEvents.SwapElements(pendingEvents);
|
||||
|
|
|
@ -145,7 +145,7 @@ public:
|
|||
NoteStoppedControllingDocuments();
|
||||
|
||||
void
|
||||
Activated();
|
||||
UpdateState(ServiceWorkerState aState);
|
||||
|
||||
nsresult
|
||||
GetDebugger(nsIWorkerDebugger** aResult);
|
||||
|
|
|
@ -1787,7 +1787,6 @@ END_WORKERS_NAMESPACE
|
|||
WorkerLoadInfo::WorkerLoadInfo()
|
||||
: mLoadFlags(nsIRequest::LOAD_NORMAL)
|
||||
, mWindowID(UINT64_MAX)
|
||||
, mServiceWorkerID(0)
|
||||
, mReferrerPolicy(net::RP_Unset)
|
||||
, mFromWindow(false)
|
||||
, mEvalAllowed(false)
|
||||
|
@ -1844,9 +1843,9 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
|
|||
mDomain = aOther.mDomain;
|
||||
mOrigin = aOther.mOrigin;
|
||||
mServiceWorkerCacheName = aOther.mServiceWorkerCacheName;
|
||||
mServiceWorkerDescriptor = aOther.mServiceWorkerDescriptor;
|
||||
mLoadFlags = aOther.mLoadFlags;
|
||||
mWindowID = aOther.mWindowID;
|
||||
mServiceWorkerID = aOther.mServiceWorkerID;
|
||||
mReferrerPolicy = aOther.mReferrerPolicy;
|
||||
mFromWindow = aOther.mFromWindow;
|
||||
mEvalAllowed = aOther.mEvalAllowed;
|
||||
|
@ -2827,7 +2826,7 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
|||
: mMutex("WorkerPrivateParent Mutex"),
|
||||
mCondVar(mMutex, "WorkerPrivateParent CondVar"),
|
||||
mParent(aParent), mScriptURL(aScriptURL),
|
||||
mWorkerName(aWorkerName), mServiceWorkerScope(aServiceWorkerScope),
|
||||
mWorkerName(aWorkerName),
|
||||
mLoadingWorkerScript(false), mBusyCount(0), mParentWindowPausedDepth(0),
|
||||
mParentStatus(Pending), mParentFrozen(false),
|
||||
mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
|
||||
|
|
|
@ -219,8 +219,6 @@ private:
|
|||
nsString mScriptURL;
|
||||
// This is the worker name for shared workers and dedicated workers.
|
||||
nsString mWorkerName;
|
||||
// This is the worker scope for service workers.
|
||||
nsCString mServiceWorkerScope;
|
||||
LocationInfo mLocationInfo;
|
||||
// The lifetime of these objects within LoadInfo is managed explicitly;
|
||||
// they do not need to be cycle collected.
|
||||
|
@ -523,14 +521,13 @@ public:
|
|||
uint64_t
|
||||
ServiceWorkerID() const
|
||||
{
|
||||
return mLoadInfo.mServiceWorkerID;
|
||||
return GetServiceWorkerDescriptor().Id();
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ServiceWorkerScope() const
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
|
||||
return mServiceWorkerScope;
|
||||
return GetServiceWorkerDescriptor().Scope();
|
||||
}
|
||||
|
||||
nsIURI*
|
||||
|
@ -553,11 +550,27 @@ public:
|
|||
const nsString&
|
||||
ServiceWorkerCacheName() const
|
||||
{
|
||||
MOZ_ASSERT(IsServiceWorker());
|
||||
MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
|
||||
AssertIsOnMainThread();
|
||||
return mLoadInfo.mServiceWorkerCacheName;
|
||||
}
|
||||
|
||||
const ServiceWorkerDescriptor&
|
||||
GetServiceWorkerDescriptor() const
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
|
||||
return mLoadInfo.mServiceWorkerDescriptor.ref();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateServiceWorkerState(ServiceWorkerState aState)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
|
||||
return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState);
|
||||
}
|
||||
|
||||
const ChannelInfo&
|
||||
GetChannelInfo() const
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "mozilla/dom/ChannelInfo.h"
|
||||
#include "mozilla/dom/ServiceWorkerDescriptor.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
#define BEGIN_WORKERS_NAMESPACE \
|
||||
|
@ -245,12 +246,12 @@ struct WorkerLoadInfo
|
|||
nsString mOrigin; // Derived from mPrincipal; can be used on worker thread.
|
||||
|
||||
nsString mServiceWorkerCacheName;
|
||||
Maybe<ServiceWorkerDescriptor> mServiceWorkerDescriptor;
|
||||
|
||||
ChannelInfo mChannelInfo;
|
||||
nsLoadFlags mLoadFlags;
|
||||
|
||||
uint64_t mWindowID;
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
net::ReferrerPolicy mReferrerPolicy;
|
||||
bool mFromWindow;
|
||||
|
|
|
@ -12,7 +12,9 @@ EXPORTS.mozilla.dom += [
|
|||
'FileReaderSync.h',
|
||||
'ServiceWorkerCommon.h',
|
||||
'ServiceWorkerContainer.h',
|
||||
'ServiceWorkerDescriptor.h',
|
||||
'ServiceWorkerEvents.h',
|
||||
'ServiceWorkerIPCUtils.h',
|
||||
'ServiceWorkerRegistrar.h',
|
||||
'ServiceWorkerRegistration.h',
|
||||
'WorkerLocation.h',
|
||||
|
@ -60,6 +62,7 @@ UNIFIED_SOURCES += [
|
|||
'ServiceWorkerClient.cpp',
|
||||
'ServiceWorkerClients.cpp',
|
||||
'ServiceWorkerContainer.cpp',
|
||||
'ServiceWorkerDescriptor.cpp',
|
||||
'ServiceWorkerEvents.cpp',
|
||||
'ServiceWorkerInfo.cpp',
|
||||
'ServiceWorkerJob.cpp',
|
||||
|
@ -91,6 +94,7 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'IPCServiceWorkerDescriptor.ipdlh',
|
||||
'PServiceWorkerManager.ipdl',
|
||||
'PServiceWorkerUpdater.ipdl',
|
||||
'ServiceWorkerRegistrarTypes.ipdlh',
|
||||
|
|
|
@ -14,35 +14,17 @@ namespace mozilla {
|
|||
namespace gl {
|
||||
|
||||
void
|
||||
AndroidSurfaceTexture::GetTransformMatrix(java::sdk::SurfaceTexture::LocalRef aSurfaceTexture,
|
||||
gfx::Matrix4x4& aMatrix)
|
||||
AndroidSurfaceTexture::GetTransformMatrix(java::sdk::SurfaceTexture::Param surfaceTexture,
|
||||
gfx::Matrix4x4* outMatrix)
|
||||
{
|
||||
JNIEnv* const env = jni::GetEnvForThread();
|
||||
|
||||
auto jarray = jni::FloatArray::LocalRef::Adopt(env, env->NewFloatArray(16));
|
||||
aSurfaceTexture->GetTransformMatrix(jarray);
|
||||
surfaceTexture->GetTransformMatrix(jarray);
|
||||
|
||||
jfloat* array = env->GetFloatArrayElements(jarray.Get(), nullptr);
|
||||
|
||||
aMatrix._11 = array[0];
|
||||
aMatrix._12 = array[1];
|
||||
aMatrix._13 = array[2];
|
||||
aMatrix._14 = array[3];
|
||||
|
||||
aMatrix._21 = array[4];
|
||||
aMatrix._22 = array[5];
|
||||
aMatrix._23 = array[6];
|
||||
aMatrix._24 = array[7];
|
||||
|
||||
aMatrix._31 = array[8];
|
||||
aMatrix._32 = array[9];
|
||||
aMatrix._33 = array[10];
|
||||
aMatrix._34 = array[11];
|
||||
|
||||
aMatrix._41 = array[12];
|
||||
aMatrix._42 = array[13];
|
||||
aMatrix._43 = array[14];
|
||||
aMatrix._44 = array[15];
|
||||
memcpy(&(outMatrix->_11), array, sizeof(float)*16);
|
||||
|
||||
env->ReleaseFloatArrayElements(jarray.Get(), array, 0);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ namespace gl {
|
|||
|
||||
class AndroidSurfaceTexture {
|
||||
public:
|
||||
static void GetTransformMatrix(java::sdk::SurfaceTexture::LocalRef aSurfaceTexture,
|
||||
mozilla::gfx::Matrix4x4& aMatrix);
|
||||
static void GetTransformMatrix(java::sdk::SurfaceTexture::Param surfaceTexture,
|
||||
mozilla::gfx::Matrix4x4* outMatrix);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "GPUVideoImage.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "GeneratedJNIWrappers.h"
|
||||
#include "AndroidSurfaceTexture.h"
|
||||
#include "GLImages.h"
|
||||
#include "GLLibraryEGL.h"
|
||||
|
@ -54,13 +55,14 @@ const char* const kFragHeader_Tex2DRect = "\
|
|||
#endif \n\
|
||||
";
|
||||
const char* const kFragHeader_TexExt = "\
|
||||
#extension GL_OES_EGL_image_external : require \n\
|
||||
#define SAMPLER samplerExternalOES \n\
|
||||
#if __VERSION__ >= 130 \n\
|
||||
#extension GL_OES_EGL_image_external_essl3 : require \n\
|
||||
#define TEXTURE texture \n\
|
||||
#else \n\
|
||||
#extension GL_OES_EGL_image_external : require \n\
|
||||
#define TEXTURE texture2D \n\
|
||||
#endif \n\
|
||||
#define SAMPLER samplerExternalOES \n\
|
||||
";
|
||||
|
||||
const char* const kFragBody_RGBA = "\
|
||||
|
@ -757,14 +759,71 @@ GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage,
|
|||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
bool
|
||||
GLBlitHelper::BlitImage(layers::SurfaceTextureImage* srcImage, const gfx::IntSize&,
|
||||
const OriginPos) const
|
||||
GLBlitHelper::BlitImage(layers::SurfaceTextureImage* srcImage, const gfx::IntSize& destSize,
|
||||
const OriginPos destOrigin) const
|
||||
{
|
||||
// FIXME
|
||||
AndroidSurfaceTextureHandle handle = srcImage->GetHandle();
|
||||
const auto& surfaceTexture = java::GeckoSurfaceTexture::Lookup(handle);
|
||||
|
||||
if (!surfaceTexture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
|
||||
|
||||
if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) {
|
||||
GLuint tex;
|
||||
mGL->MakeCurrent();
|
||||
mGL->fGenTextures(1, &tex);
|
||||
|
||||
if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) {
|
||||
mGL->fDeleteTextures(1, &tex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(), LOCAL_GL_TEXTURE_EXTERNAL);
|
||||
surfaceTexture->UpdateTexImage();
|
||||
|
||||
gfx::Matrix4x4 transform4;
|
||||
AndroidSurfaceTexture::GetTransformMatrix(java::sdk::SurfaceTexture::Ref::From(surfaceTexture),
|
||||
&transform4);
|
||||
Mat3 transform3;
|
||||
transform3.at(0,0) = transform4._11;
|
||||
transform3.at(0,1) = transform4._12;
|
||||
transform3.at(0,2) = transform4._14;
|
||||
transform3.at(1,0) = transform4._21;
|
||||
transform3.at(1,1) = transform4._22;
|
||||
transform3.at(1,2) = transform4._24;
|
||||
transform3.at(2,0) = transform4._41;
|
||||
transform3.at(2,1) = transform4._42;
|
||||
transform3.at(2,2) = transform4._44;
|
||||
|
||||
// We don't do w-divison, so if these aren't what we expect, we're probably doing
|
||||
// something wrong.
|
||||
MOZ_ASSERT(transform3.at(0,2) == 0);
|
||||
MOZ_ASSERT(transform3.at(1,2) == 0);
|
||||
MOZ_ASSERT(transform3.at(2,2) == 1);
|
||||
|
||||
const auto& srcOrigin = srcImage->GetOriginPos();
|
||||
(void)srcOrigin;
|
||||
gfxCriticalError() << "BlitImage(SurfaceTextureImage) not implemented.";
|
||||
return false;
|
||||
|
||||
// I honestly have no idea why this logic is flipped, but changing the
|
||||
// source origin would mean we'd have to flip it in the compositor
|
||||
// which makes just as little sense as this.
|
||||
const bool yFlip = (srcOrigin == destOrigin);
|
||||
|
||||
const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_RGBA});
|
||||
MOZ_RELEASE_ASSERT(prog);
|
||||
|
||||
// There is no padding on these images, so we can use the GetTransformMatrix directly.
|
||||
const DrawBlitProg::BaseArgs baseArgs = { transform3, yFlip, destSize, Nothing() };
|
||||
prog->Draw(baseArgs, nullptr);
|
||||
|
||||
if (surfaceTexture->IsSingleBuffer()) {
|
||||
surfaceTexture->ReleaseTexImage();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,6 +12,43 @@
|
|||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
bool
|
||||
SourceSurfaceSharedDataWrapper::Init(const IntSize& aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat,
|
||||
const SharedMemoryBasic::Handle& aHandle,
|
||||
base::ProcessId aCreatorPid)
|
||||
{
|
||||
MOZ_ASSERT(!mBuf);
|
||||
mSize = aSize;
|
||||
mStride = aStride;
|
||||
mFormat = aFormat;
|
||||
mCreatorPid = aCreatorPid;
|
||||
|
||||
size_t len = GetAlignedDataLength();
|
||||
mBuf = MakeAndAddRef<SharedMemoryBasic>();
|
||||
if (NS_WARN_IF(!mBuf->SetHandle(aHandle, ipc::SharedMemory::RightsReadOnly)) ||
|
||||
NS_WARN_IF(!mBuf->Map(len))) {
|
||||
mBuf = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mBuf->CloseHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface)
|
||||
{
|
||||
MOZ_ASSERT(!mBuf);
|
||||
MOZ_ASSERT(aSurface);
|
||||
mSize = aSurface->mSize;
|
||||
mStride = aSurface->mStride;
|
||||
mFormat = aSurface->mFormat;
|
||||
mCreatorPid = base::GetCurrentProcId();
|
||||
mBuf = aSurface->mBuf;
|
||||
}
|
||||
|
||||
bool
|
||||
SourceSurfaceSharedData::Init(const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
|
@ -68,6 +105,7 @@ SourceSurfaceSharedData::ShareToProcess(base::ProcessId aPid,
|
|||
SharedMemoryBasic::Handle& aHandle)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mHandleCount > 0);
|
||||
|
||||
if (mClosed) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -87,6 +125,9 @@ SourceSurfaceSharedData::CloseHandleInternal()
|
|||
mMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (mClosed) {
|
||||
MOZ_ASSERT(mHandleCount == 0);
|
||||
MOZ_ASSERT(mFinalized);
|
||||
MOZ_ASSERT(mShared);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -100,6 +141,7 @@ bool
|
|||
SourceSurfaceSharedData::ReallocHandle()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mHandleCount > 0);
|
||||
MOZ_ASSERT(mClosed);
|
||||
MOZ_ASSERT(mFinalized);
|
||||
|
||||
|
|
|
@ -14,6 +14,104 @@
|
|||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class SourceSurfaceSharedData;
|
||||
|
||||
/**
|
||||
* This class is used to wrap shared (as in process) data buffers allocated by
|
||||
* a SourceSurfaceSharedData object. It may live in the same process or a
|
||||
* different process from the actual SourceSurfaceSharedData object.
|
||||
*
|
||||
* If it is in the same process, mBuf is the same object as that in the surface.
|
||||
* It is a useful abstraction over just using the surface directly, because it
|
||||
* can have a different lifetime from the surface; if the surface gets freed,
|
||||
* consumers may continue accessing the data in the buffer. Releasing the
|
||||
* original surface is a signal which feeds into SharedSurfacesParent to decide
|
||||
* to release the SourceSurfaceSharedDataWrapper.
|
||||
*
|
||||
* If it is in a different process, mBuf is a new SharedMemoryBasic object which
|
||||
* mapped in the given shared memory handle as read only memory.
|
||||
*/
|
||||
class SourceSurfaceSharedDataWrapper final : public DataSourceSurface
|
||||
{
|
||||
typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
|
||||
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper, override)
|
||||
|
||||
SourceSurfaceSharedDataWrapper()
|
||||
: mStride(0)
|
||||
, mFormat(SurfaceFormat::UNKNOWN)
|
||||
{ }
|
||||
|
||||
bool Init(const IntSize& aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat,
|
||||
const SharedMemoryBasic::Handle& aHandle,
|
||||
base::ProcessId aCreatorPid);
|
||||
|
||||
void Init(SourceSurfaceSharedData *aSurface);
|
||||
|
||||
base::ProcessId GetCreatorPid() const
|
||||
{
|
||||
return mCreatorPid;
|
||||
}
|
||||
|
||||
int32_t Stride() override { return mStride; }
|
||||
|
||||
SurfaceType GetType() const override { return SurfaceType::DATA; }
|
||||
IntSize GetSize() const override { return mSize; }
|
||||
SurfaceFormat GetFormat() const override { return mFormat; }
|
||||
|
||||
uint8_t* GetData() override
|
||||
{
|
||||
return static_cast<uint8_t*>(mBuf->memory());
|
||||
}
|
||||
|
||||
bool OnHeap() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map(MapType, MappedSurface *aMappedSurface) override
|
||||
{
|
||||
aMappedSurface->mData = GetData();
|
||||
aMappedSurface->mStride = mStride;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unmap() override
|
||||
{ }
|
||||
|
||||
bool AddConsumer()
|
||||
{
|
||||
return ++mConsumers == 1;
|
||||
}
|
||||
|
||||
bool RemoveConsumer()
|
||||
{
|
||||
MOZ_ASSERT(mConsumers > 0);
|
||||
return --mConsumers == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t GetDataLength() const
|
||||
{
|
||||
return static_cast<size_t>(mStride) * mSize.height;
|
||||
}
|
||||
|
||||
size_t GetAlignedDataLength() const
|
||||
{
|
||||
return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
|
||||
}
|
||||
|
||||
int32_t mStride;
|
||||
uint32_t mConsumers;
|
||||
IntSize mSize;
|
||||
RefPtr<SharedMemoryBasic> mBuf;
|
||||
SurfaceFormat mFormat;
|
||||
base::ProcessId mCreatorPid;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is used to wrap shared (as in process) data buffers used by a
|
||||
* source surface.
|
||||
|
@ -29,6 +127,7 @@ public:
|
|||
: mMutex("SourceSurfaceSharedData")
|
||||
, mStride(0)
|
||||
, mMapCount(0)
|
||||
, mHandleCount(0)
|
||||
, mFormat(SurfaceFormat::UNKNOWN)
|
||||
, mClosed(false)
|
||||
, mFinalized(false)
|
||||
|
@ -36,7 +135,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
bool Init(const IntSize &aSize,
|
||||
bool Init(const IntSize& aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat);
|
||||
|
||||
|
@ -134,17 +233,65 @@ public:
|
|||
bool ReallocHandle();
|
||||
|
||||
/**
|
||||
* Indicates we have finished writing to the buffer and it may be marked as
|
||||
* Signals we have finished writing to the buffer and it may be marked as
|
||||
* read only. May release the handle if possible (see CloseHandleInternal).
|
||||
*/
|
||||
void Finalize();
|
||||
|
||||
/**
|
||||
* Indicates whether or not the buffer can change. If this returns true, it is
|
||||
* guaranteed to continue to do so for the remainder of the surface's life.
|
||||
*/
|
||||
bool IsFinalized() const
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mFinalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* While a HandleLock exists for the given surface, the shared memory handle
|
||||
* cannot be released.
|
||||
*/
|
||||
class MOZ_STACK_CLASS HandleLock final {
|
||||
public:
|
||||
explicit HandleLock(SourceSurfaceSharedData* aSurface)
|
||||
: mSurface(aSurface)
|
||||
{
|
||||
mSurface->LockHandle();
|
||||
}
|
||||
|
||||
~HandleLock()
|
||||
{
|
||||
mSurface->UnlockHandle();
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<SourceSurfaceSharedData> mSurface;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class SourceSurfaceSharedDataWrapper;
|
||||
|
||||
~SourceSurfaceSharedData() override
|
||||
{
|
||||
MOZ_ASSERT(mMapCount == 0);
|
||||
}
|
||||
|
||||
void LockHandle()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
++mHandleCount;
|
||||
}
|
||||
|
||||
void UnlockHandle()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mHandleCount > 0);
|
||||
--mHandleCount;
|
||||
mShared = true;
|
||||
CloseHandleInternal();
|
||||
}
|
||||
|
||||
uint8_t* GetDataInternal() const;
|
||||
|
||||
size_t GetDataLength() const
|
||||
|
@ -166,6 +313,7 @@ private:
|
|||
mutable Mutex mMutex;
|
||||
int32_t mStride;
|
||||
int32_t mMapCount;
|
||||
int32_t mHandleCount;
|
||||
IntSize mSize;
|
||||
RefPtr<SharedMemoryBasic> mBuf;
|
||||
RefPtr<SharedMemoryBasic> mOldBuf;
|
||||
|
|
|
@ -435,12 +435,13 @@ ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
|
|||
|
||||
// If we have an existing buffer, but the content type has changed or we
|
||||
// have transitioned into/out of component alpha, then we need to recreate it.
|
||||
if (canReuseBuffer &&
|
||||
if (canKeepBufferContents &&
|
||||
mBuffer &&
|
||||
(contentType != BufferContentType() ||
|
||||
(mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()))
|
||||
{
|
||||
// Restart the decision process; we won't re-enter since we guard on
|
||||
// being able to re-use the buffer.
|
||||
// being able to keep the buffer contents.
|
||||
canReuseBuffer = false;
|
||||
canKeepBufferContents = false;
|
||||
validRegion.SetEmpty();
|
||||
|
|
|
@ -49,8 +49,21 @@ public:
|
|||
CreateSameProcessWidgetCompositorBridge(LayerManager* aLayerManager,
|
||||
uint32_t aNamespace);
|
||||
|
||||
static CompositorManagerChild* GetInstance()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
bool CanSend() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mCanSend;
|
||||
}
|
||||
|
||||
uint32_t GetNextResourceId()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return ++mResourceId;
|
||||
}
|
||||
|
||||
|
@ -59,6 +72,19 @@ public:
|
|||
return mNamespace;
|
||||
}
|
||||
|
||||
bool OwnsExternalImageId(const wr::ExternalImageId& aId) const
|
||||
{
|
||||
return mNamespace == static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
|
||||
}
|
||||
|
||||
wr::ExternalImageId GetNextExternalImageId()
|
||||
{
|
||||
uint64_t id = GetNextResourceId();
|
||||
MOZ_RELEASE_ASSERT(id != 0);
|
||||
id |= (static_cast<uint64_t>(mNamespace) << 32);
|
||||
return wr::ToExternalImageId(id);
|
||||
}
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aReason) override;
|
||||
|
||||
void HandleFatalError(const char* aName, const char* aMsg) const override;
|
||||
|
@ -86,12 +112,6 @@ private:
|
|||
{
|
||||
}
|
||||
|
||||
bool CanSend() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mCanSend;
|
||||
}
|
||||
|
||||
void DeallocPCompositorManagerChild() override;
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "VsyncSource.h"
|
||||
|
||||
|
@ -150,6 +151,8 @@ CompositorManagerParent::BindComplete()
|
|||
void
|
||||
CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason)
|
||||
{
|
||||
SharedSurfacesParent::DestroyProcess(OtherPid());
|
||||
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
if (sInstance == this) {
|
||||
sInstance = nullptr;
|
||||
|
@ -273,5 +276,20 @@ CompositorManagerParent::DeallocPCompositorBridgeParent(PCompositorBridgeParent*
|
|||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
CompositorManagerParent::RecvAddSharedSurface(const wr::ExternalImageId& aId,
|
||||
const SurfaceDescriptorShared& aDesc)
|
||||
{
|
||||
SharedSurfacesParent::Add(aId, aDesc, OtherPid());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
CompositorManagerParent::RecvRemoveSharedSurface(const wr::ExternalImageId& aId)
|
||||
{
|
||||
SharedSurfacesParent::Remove(aId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -40,6 +40,10 @@ public:
|
|||
bool aUseExternalSurfaceSize,
|
||||
const gfx::IntSize& aSurfaceSize);
|
||||
|
||||
mozilla::ipc::IPCResult RecvAddSharedSurface(const wr::ExternalImageId& aId,
|
||||
const SurfaceDescriptorShared& aDesc) override;
|
||||
mozilla::ipc::IPCResult RecvRemoveSharedSurface(const wr::ExternalImageId& aId) override;
|
||||
|
||||
void BindComplete();
|
||||
void ActorDestroy(ActorDestroyReason aReason) override;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||
#include "mozilla/media/MediaSystemResourceService.h"
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsExceptionHandler.h" // for CrashReporter
|
||||
|
@ -98,6 +99,7 @@ CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread)
|
|||
MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
|
||||
|
||||
CompositorBridgeParent::Shutdown();
|
||||
SharedSurfacesParent::Shutdown();
|
||||
delete aCompositorThread;
|
||||
sFinishedCompositorShutDown = true;
|
||||
}
|
||||
|
@ -132,6 +134,7 @@ CompositorThreadHolder::CreateCompositorThread()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
SharedSurfacesParent::Initialize();
|
||||
CompositorBridgeParent::Setup();
|
||||
ImageBridgeParent::Setup();
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ using namespace mozilla::ipc;
|
|||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::media;
|
||||
|
||||
std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeParent::sImageBridges;
|
||||
ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges;
|
||||
|
||||
StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
|
||||
|
||||
|
@ -369,8 +369,9 @@ ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo
|
|||
notifications.AppendElement(aNotifications[end].mNotification);
|
||||
++end;
|
||||
}
|
||||
GetInstance(pid)->SendPendingAsyncMessages();
|
||||
if (!GetInstance(pid)->SendDidComposite(notifications)) {
|
||||
RefPtr<ImageBridgeParent> bridge = GetInstance(pid);
|
||||
bridge->SendPendingAsyncMessages();
|
||||
if (!bridge->SendDidComposite(notifications)) {
|
||||
ok = false;
|
||||
}
|
||||
i = end;
|
||||
|
@ -385,13 +386,18 @@ ImageBridgeParent::DeferredDestroy()
|
|||
mSelfRef = nullptr; // "this" ImageBridge may get deleted here.
|
||||
}
|
||||
|
||||
RefPtr<ImageBridgeParent>
|
||||
already_AddRefed<ImageBridgeParent>
|
||||
ImageBridgeParent::GetInstance(ProcessId aId)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
MonitorAutoLock lock(*sImageBridgesLock);
|
||||
NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process");
|
||||
return sImageBridges[aId];
|
||||
ImageBridgeMap::const_iterator i = sImageBridges.find(aId);
|
||||
if (i == sImageBridges.end()) {
|
||||
NS_ASSERTION(false, "Cannot find image bridge for process!");
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<ImageBridgeParent> bridge = i->second;
|
||||
return bridge.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
|
||||
virtual bool IsSameProcess() const override;
|
||||
|
||||
static RefPtr<ImageBridgeParent> GetInstance(ProcessId aId);
|
||||
static already_AddRefed<ImageBridgeParent> GetInstance(ProcessId aId);
|
||||
|
||||
static bool NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo>& aNotifications);
|
||||
|
||||
|
@ -141,7 +141,8 @@ private:
|
|||
/**
|
||||
* Map of all living ImageBridgeParent instances
|
||||
*/
|
||||
static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
|
||||
typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap;
|
||||
static ImageBridgeMap sImageBridges;
|
||||
|
||||
RefPtr<CompositorThreadHolderDebug> mCompositorThreadHolder;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
|
|||
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
|
||||
using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
|
||||
using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
|
||||
using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
|
||||
using gfxImageFormat from "gfxTypes.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -129,6 +130,14 @@ struct SurfaceDescriptorBuffer {
|
|||
MemoryOrShmem data;
|
||||
};
|
||||
|
||||
struct SurfaceDescriptorShared
|
||||
{
|
||||
IntSize size;
|
||||
int32_t stride;
|
||||
SurfaceFormat format;
|
||||
Handle handle;
|
||||
};
|
||||
|
||||
union SurfaceDescriptor {
|
||||
SurfaceDescriptorBuffer;
|
||||
SurfaceDescriptorDIB;
|
||||
|
@ -141,6 +150,7 @@ union SurfaceDescriptor {
|
|||
SurfaceDescriptorMacIOSurface;
|
||||
SurfaceDescriptorSharedGLTexture;
|
||||
SurfaceDescriptorGPUVideo;
|
||||
SurfaceDescriptorShared;
|
||||
null_t;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,12 +6,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PCompositorBridge;
|
||||
include LayersSurfaces;
|
||||
include "mozilla/GfxMessageUtils.h";
|
||||
include "mozilla/layers/WebRenderMessageUtils.h";
|
||||
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
using mozilla::TimeDuration from "mozilla/TimeStamp.h";
|
||||
using mozilla::CSSToLayoutDeviceScale from "Units.h";
|
||||
using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
|
||||
using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
|
||||
using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
|
||||
using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -67,6 +72,9 @@ parent:
|
|||
* See gfx/layers/ipc/PCompositorBridge.ipdl for more details.
|
||||
*/
|
||||
async PCompositorBridge(CompositorBridgeOptions options);
|
||||
|
||||
async AddSharedSurface(ExternalImageId aId, SurfaceDescriptorShared aDesc);
|
||||
async RemoveSharedSurface(ExternalImageId aId);
|
||||
};
|
||||
|
||||
} // layers
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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 "SharedSurfacesChild.h"
|
||||
#include "SharedSurfacesParent.h"
|
||||
#include "CompositorManagerChild.h"
|
||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
||||
#include "mozilla/SystemGroup.h" // for SystemGroup
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
class SharedSurfacesChild::SharedUserData final
|
||||
{
|
||||
public:
|
||||
explicit SharedUserData(const wr::ExternalImageId& aId)
|
||||
: mId(aId)
|
||||
, mShared(false)
|
||||
{ }
|
||||
|
||||
~SharedUserData()
|
||||
{
|
||||
if (mShared) {
|
||||
mShared = false;
|
||||
if (NS_IsMainThread()) {
|
||||
SharedSurfacesChild::Unshare(mId);
|
||||
} else {
|
||||
wr::ExternalImageId id = mId;
|
||||
SystemGroup::Dispatch(TaskCategory::Other,
|
||||
NS_NewRunnableFunction("DestroySharedUserData",
|
||||
[id]() -> void {
|
||||
SharedSurfacesChild::Unshare(id);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wr::ExternalImageId& Id() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
void SetId(const wr::ExternalImageId& aId)
|
||||
{
|
||||
mId = aId;
|
||||
mShared = false;
|
||||
}
|
||||
|
||||
bool IsShared() const
|
||||
{
|
||||
return mShared;
|
||||
}
|
||||
|
||||
void MarkShared()
|
||||
{
|
||||
MOZ_ASSERT(!mShared);
|
||||
mShared = true;
|
||||
}
|
||||
|
||||
private:
|
||||
wr::ExternalImageId mId;
|
||||
bool mShared : 1;
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesChild::DestroySharedUserData(void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
auto data = static_cast<SharedUserData*>(aClosure);
|
||||
delete data;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
|
||||
wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
|
||||
if (NS_WARN_IF(!manager || !manager->CanSend())) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
static UserDataKey sSharedKey;
|
||||
SharedUserData* data =
|
||||
static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
|
||||
if (!data) {
|
||||
data = new SharedUserData(manager->GetNextExternalImageId());
|
||||
aSurface->AddUserData(&sSharedKey, data, DestroySharedUserData);
|
||||
} else if (!manager->OwnsExternalImageId(data->Id())) {
|
||||
// If the id isn't owned by us, that means the bridge was reinitialized, due
|
||||
// to the GPU process crashing. All previous mappings have been released.
|
||||
MOZ_ASSERT(manager->OtherPid() != base::GetCurrentProcId());
|
||||
data->SetId(manager->GetNextExternalImageId());
|
||||
} else if (data->IsShared()) {
|
||||
// It has already been shared with the GPU process, reuse the id.
|
||||
aId = data->Id();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Ensure that the handle doesn't get released until after we have finished
|
||||
// sending the buffer to the GPU process and/or reallocating it.
|
||||
// FinishedSharing is not a sufficient condition because another thread may
|
||||
// decide we are done while we are in the processing of sharing our newly
|
||||
// reallocated handle. Once it goes out of scope, it may release the handle.
|
||||
SourceSurfaceSharedData::HandleLock lock(aSurface);
|
||||
|
||||
// If we live in the same process, then it is a simple matter of directly
|
||||
// asking the parent instance to store a pointer to the same data, no need
|
||||
// to map the data into our memory space twice.
|
||||
auto pid = manager->OtherPid();
|
||||
if (pid == base::GetCurrentProcId()) {
|
||||
SharedSurfacesParent::AddSameProcess(data->Id(), aSurface);
|
||||
data->MarkShared();
|
||||
aId = data->Id();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Attempt to share a handle with the GPU process. The handle may or may not
|
||||
// be available -- it will only be available if it is either not yet finalized
|
||||
// and/or if it has been finalized but never used for drawing in process.
|
||||
ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle();
|
||||
nsresult rv = aSurface->ShareToProcess(pid, handle);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// It is at least as expensive to copy the image to the GPU process if we
|
||||
// have already closed the handle necessary to share, but if we reallocate
|
||||
// the shared buffer to get a new handle, we can save some memory.
|
||||
if (NS_WARN_IF(!aSurface->ReallocHandle())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Reattempt the sharing of the handle to the GPU process.
|
||||
rv = aSurface->ShareToProcess(pid, handle);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
SurfaceFormat format = aSurface->GetFormat();
|
||||
MOZ_RELEASE_ASSERT(format == SurfaceFormat::B8G8R8X8 ||
|
||||
format == SurfaceFormat::B8G8R8A8, "bad format");
|
||||
|
||||
data->MarkShared();
|
||||
aId = data->Id();
|
||||
manager->SendAddSharedSurface(aId,
|
||||
SurfaceDescriptorShared(aSurface->GetSize(),
|
||||
aSurface->Stride(),
|
||||
format, handle));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
SharedSurfacesChild::Share(ImageContainer* aContainer,
|
||||
wr::ExternalImageId& aId,
|
||||
uint32_t& aGeneration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aContainer);
|
||||
|
||||
if (aContainer->IsAsync()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
AutoTArray<ImageContainer::OwningImage,4> images;
|
||||
aContainer->GetCurrentImages(&images, &aGeneration);
|
||||
if (images.IsEmpty()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
|
||||
if (!surface) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (surface->GetType() != SurfaceType::DATA_SHARED) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
|
||||
return Share(sharedSurface, aId);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
|
||||
if (MOZ_UNLIKELY(!manager || !manager->CanSend())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (manager->OtherPid() == base::GetCurrentProcId()) {
|
||||
// We are in the combined UI/GPU process. Call directly to it to remove its
|
||||
// wrapper surface to free the underlying buffer.
|
||||
MOZ_ASSERT(manager->OwnsExternalImageId(aId));
|
||||
SharedSurfacesParent::RemoveSameProcess(aId);
|
||||
} else if (manager->OwnsExternalImageId(aId)) {
|
||||
// Only attempt to release current mappings in the GPU process. It is
|
||||
// possible we had a surface that was previously shared, the GPU process
|
||||
// crashed / was restarted, and then we freed the surface. In that case
|
||||
// we know the mapping has already been freed.
|
||||
manager->SendRemoveSharedSurface(aId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_SHAREDSURFACESCHILD_H
|
||||
#define MOZILLA_GFX_SHAREDSURFACESCHILD_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint32_t, uint64_t
|
||||
#include "mozilla/Attributes.h" // for override
|
||||
#include "mozilla/RefPtr.h" // for already_AddRefed
|
||||
#include "mozilla/StaticPtr.h" // for StaticRefPtr
|
||||
#include "mozilla/webrender/WebRenderTypes.h" // for wr::ExternalImageId
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class SourceSurfaceSharedData;
|
||||
} // namespace gfx
|
||||
|
||||
namespace layers {
|
||||
|
||||
class CompositorManagerChild;
|
||||
|
||||
class SharedSurfacesChild final
|
||||
{
|
||||
public:
|
||||
static nsresult Share(gfx::SourceSurfaceSharedData* aSurface, wr::ExternalImageId& aId);
|
||||
static nsresult Share(ImageContainer* aContainer, wr::ExternalImageId& aId, uint32_t& aGeneration);
|
||||
|
||||
private:
|
||||
SharedSurfacesChild() = delete;
|
||||
~SharedSurfacesChild() = delete;
|
||||
|
||||
class SharedUserData;
|
||||
|
||||
static void Unshare(const wr::ExternalImageId& aId);
|
||||
static void DestroySharedUserData(void* aClosure);
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,193 @@
|
|||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* -*- Mode: C++; tab-width: 20; 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 "SharedSurfacesParent.h"
|
||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/webrender/RenderSharedSurfaceTextureHost.h"
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance;
|
||||
|
||||
SharedSurfacesParent::SharedSurfacesParent()
|
||||
{
|
||||
}
|
||||
|
||||
SharedSurfacesParent::~SharedSurfacesParent()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::Initialize()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInstance) {
|
||||
sInstance = new SharedSurfacesParent();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<DataSourceSurface>
|
||||
SharedSurfacesParent::Acquire(const wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
if (!sInstance) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurfaceSharedDataWrapper> surface;
|
||||
sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
|
||||
|
||||
if (surface) {
|
||||
DebugOnly<bool> rv = surface->AddConsumer();
|
||||
MOZ_ASSERT(!rv);
|
||||
}
|
||||
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SharedSurfacesParent::Release(const wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
if (!sInstance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t id = wr::AsUint64(aId);
|
||||
RefPtr<SourceSurfaceSharedDataWrapper> surface;
|
||||
sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
|
||||
if (!surface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (surface->RemoveConsumer()) {
|
||||
sInstance->mSurfaces.Remove(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId,
|
||||
SourceSurfaceSharedData* aSurface)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If the child bridge detects it is in the combined UI/GPU process, then it
|
||||
// will insert a wrapper surface holding the shared memory buffer directly.
|
||||
// This is good because we avoid mapping the same shared memory twice, but
|
||||
// still allow the original surface to be freed and remove the wrapper from
|
||||
// the table when it is no longer needed.
|
||||
RefPtr<SourceSurfaceSharedDataWrapper> surface =
|
||||
new SourceSurfaceSharedDataWrapper();
|
||||
surface->Init(aSurface);
|
||||
|
||||
uint64_t id = wr::AsUint64(aId);
|
||||
RefPtr<Runnable> task = NS_NewRunnableFunction(
|
||||
"layers::SharedSurfacesParent::AddSameProcess",
|
||||
[surface, id]() -> void {
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
|
||||
|
||||
RefPtr<wr::RenderSharedSurfaceTextureHost> texture =
|
||||
new wr::RenderSharedSurfaceTextureHost(surface);
|
||||
wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
|
||||
|
||||
sInstance->mSurfaces.Put(id, surface);
|
||||
});
|
||||
|
||||
CompositorThreadHolder::Loop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::RemoveSameProcess(const wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
const wr::ExternalImageId id(aId);
|
||||
RefPtr<Runnable> task = NS_NewRunnableFunction(
|
||||
"layers::SharedSurfacesParent::RemoveSameProcess",
|
||||
[id]() -> void {
|
||||
Remove(id);
|
||||
});
|
||||
|
||||
CompositorThreadHolder::Loop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::DestroyProcess(base::ProcessId aPid)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that the destruction of a parent may not be cheap if it still has a
|
||||
// lot of surfaces still bound that require unmapping.
|
||||
for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) {
|
||||
if (i.Data()->GetCreatorPid() == aPid) {
|
||||
wr::RenderThread::Get()->UnregisterExternalImage(i.Key());
|
||||
i.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::Add(const wr::ExternalImageId& aId,
|
||||
const SurfaceDescriptorShared& aDesc,
|
||||
base::ProcessId aPid)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
MOZ_ASSERT(aPid != base::GetCurrentProcId());
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that the surface wrapper maps in the given handle as read only.
|
||||
RefPtr<SourceSurfaceSharedDataWrapper> surface =
|
||||
new SourceSurfaceSharedDataWrapper();
|
||||
if (NS_WARN_IF(!surface->Init(aDesc.size(), aDesc.stride(),
|
||||
aDesc.format(), aDesc.handle(),
|
||||
aPid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t id = wr::AsUint64(aId);
|
||||
MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
|
||||
|
||||
RefPtr<wr::RenderSharedSurfaceTextureHost> texture =
|
||||
new wr::RenderSharedSurfaceTextureHost(surface);
|
||||
wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
|
||||
|
||||
sInstance->mSurfaces.Put(id, surface.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedSurfacesParent::Remove(const wr::ExternalImageId& aId)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
DebugOnly<bool> rv = Release(aId);
|
||||
MOZ_ASSERT(rv);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_SHAREDSURFACESPARENT_H
|
||||
#define MOZILLA_GFX_SHAREDSURFACESPARENT_H
|
||||
|
||||
#include <stdint.h> // for uint32_t
|
||||
#include "mozilla/Attributes.h" // for override
|
||||
#include "mozilla/StaticPtr.h" // for StaticAutoPtr
|
||||
#include "mozilla/RefPtr.h" // for already_AddRefed
|
||||
#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
|
||||
#include "mozilla/gfx/2D.h" // for SurfaceFormat
|
||||
#include "mozilla/gfx/Point.h" // for IntSize
|
||||
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptorShared
|
||||
#include "mozilla/webrender/WebRenderTypes.h" // for wr::ExternalImageId
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
class SourceSurfaceSharedData;
|
||||
class SourceSurfaceSharedDataWrapper;
|
||||
} // namespace gfx
|
||||
|
||||
namespace layers {
|
||||
|
||||
class SharedSurfacesChild;
|
||||
|
||||
class SharedSurfacesParent final
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Shutdown();
|
||||
|
||||
static already_AddRefed<gfx::DataSourceSurface>
|
||||
Acquire(const wr::ExternalImageId& aId);
|
||||
|
||||
static bool Release(const wr::ExternalImageId& aId);
|
||||
|
||||
static void Add(const wr::ExternalImageId& aId,
|
||||
const SurfaceDescriptorShared& aDesc,
|
||||
base::ProcessId aPid);
|
||||
|
||||
static void Remove(const wr::ExternalImageId& aId);
|
||||
|
||||
static void DestroyProcess(base::ProcessId aPid);
|
||||
|
||||
~SharedSurfacesParent();
|
||||
|
||||
private:
|
||||
friend class SharedSurfacesChild;
|
||||
|
||||
SharedSurfacesParent();
|
||||
|
||||
static void AddSameProcess(const wr::ExternalImageId& aId,
|
||||
gfx::SourceSurfaceSharedData* aSurface);
|
||||
static void RemoveSameProcess(const wr::ExternalImageId& aId);
|
||||
|
||||
static StaticAutoPtr<SharedSurfacesParent> sInstance;
|
||||
|
||||
nsRefPtrHashtable<nsUint64HashKey, gfx::SourceSurfaceSharedDataWrapper> mSurfaces;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -194,6 +194,8 @@ EXPORTS.mozilla.layers += [
|
|||
'ipc/ShadowLayers.h',
|
||||
'ipc/SharedPlanarYCbCrImage.h',
|
||||
'ipc/SharedRGBImage.h',
|
||||
'ipc/SharedSurfacesChild.h',
|
||||
'ipc/SharedSurfacesParent.h',
|
||||
'ipc/SynchronousTask.h',
|
||||
'ipc/TextureForwarder.h',
|
||||
'ipc/UiCompositorControllerChild.h',
|
||||
|
@ -416,6 +418,8 @@ UNIFIED_SOURCES += [
|
|||
'ipc/ShadowLayers.cpp',
|
||||
'ipc/SharedPlanarYCbCrImage.cpp',
|
||||
'ipc/SharedRGBImage.cpp',
|
||||
'ipc/SharedSurfacesChild.cpp',
|
||||
'ipc/SharedSurfacesParent.cpp',
|
||||
'ipc/UiCompositorControllerChild.cpp',
|
||||
'ipc/UiCompositorControllerParent.cpp',
|
||||
'ipc/VideoBridgeChild.cpp',
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче