merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: 4PW6ESqLL73
This commit is contained in:
Sebastian Hengst 2017-10-30 23:52:23 +01:00
Родитель ea2bfe23e2 39cca322e7
Коммит f07fc93141
208 изменённых файлов: 3914 добавлений и 1010 удалений

Просмотреть файл

@ -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 = Couldnt 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 = Couldnt 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',

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше