mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge inbound to central, a=merge
MozReview-Commit-ID: 7EzhCCVVn7n
This commit is contained in:
commit
7bf1b49628
@ -32,9 +32,7 @@
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
#include "mozilla/dom/Location.h"
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
#include "unicode/uloc.h"
|
||||
#endif
|
||||
|
||||
nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
|
||||
|
||||
@ -638,7 +636,6 @@ nsChromeRegistry::MustLoadURLRemotely(nsIURI *aURI, bool *aResult)
|
||||
bool
|
||||
nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale)
|
||||
{
|
||||
#ifdef ENABLE_INTL_API
|
||||
int pref = mozilla::Preferences::GetInt("intl.uidirection", -1);
|
||||
if (pref >= 0) {
|
||||
return (pref > 0);
|
||||
@ -646,28 +643,6 @@ nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale)
|
||||
nsAutoCString locale(aLocale);
|
||||
SanitizeForBCP47(locale);
|
||||
return uloc_isRightToLeft(locale.get());
|
||||
#else
|
||||
// first check the intl.uidirection.<locale> preference, and if that is not
|
||||
// set, check the same preference but with just the first two characters of
|
||||
// the locale. If that isn't set, default to left-to-right.
|
||||
nsAutoCString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + aLocale;
|
||||
nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
|
||||
if (!prefBranch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCString dir;
|
||||
prefBranch->GetCharPref(prefString.get(), getter_Copies(dir));
|
||||
if (dir.IsEmpty()) {
|
||||
int32_t hyphen = prefString.FindChar('-');
|
||||
if (hyphen >= 1) {
|
||||
nsAutoCString shortPref(Substring(prefString, 0, hyphen));
|
||||
prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir));
|
||||
}
|
||||
}
|
||||
|
||||
return dir.EqualsLiteral("rtl");
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
@ -715,7 +690,6 @@ nsChromeRegistry::GetSingleton()
|
||||
void
|
||||
nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale)
|
||||
{
|
||||
#ifdef ENABLE_INTL_API
|
||||
// Currently, the only locale code we use that's not BCP47-conformant is
|
||||
// "ja-JP-mac" on OS X, but let's try to be more general than just
|
||||
// hard-coding that here.
|
||||
@ -730,13 +704,4 @@ nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale)
|
||||
if (U_SUCCESS(err) && len > 0) {
|
||||
aLocale.Assign(langTag, len);
|
||||
}
|
||||
#else
|
||||
// This is only really needed for Intl API purposes, AFAIK,
|
||||
// so probably won't be used in a non-ENABLE_INTL_API build.
|
||||
// But let's fix up the single anomalous code we actually ship,
|
||||
// just in case:
|
||||
if (aLocale.EqualsLiteral("ja-JP-mac")) {
|
||||
aLocale.AssignLiteral("ja-JP");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -34,9 +34,6 @@ function performTest() {
|
||||
// Make sure that the search box becomes focused when pressing ctrl+f - Bug 1211038
|
||||
gEditor.focus();
|
||||
synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
|
||||
let focusedEl = Services.focus.focusedElement;
|
||||
focusedEl = focusedEl.ownerDocument.getBindingParent(focusedEl) || focusedEl;
|
||||
is(focusedEl, gDebugger.document.getElementById("searchbox"), "Searchbox is focused");
|
||||
|
||||
setText(gSearchBox, "#html");
|
||||
|
||||
|
@ -5,7 +5,7 @@ code, and optionally help with indentation.
|
||||
|
||||
# Upgrade
|
||||
|
||||
Currently used version is 5.29.0. To upgrade: download a new version of
|
||||
Currently used version is 5.30.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].
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
cm.state.closeBrackets = null;
|
||||
}
|
||||
if (val) {
|
||||
ensureBound(getOption(val, "pairs"))
|
||||
cm.state.closeBrackets = val;
|
||||
cm.addKeyMap(keyMap);
|
||||
}
|
||||
@ -34,10 +35,14 @@
|
||||
return defaults[name];
|
||||
}
|
||||
|
||||
var bind = defaults.pairs + "`";
|
||||
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||
for (var i = 0; i < bind.length; i++)
|
||||
keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
|
||||
function ensureBound(chars) {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||
}
|
||||
}
|
||||
ensureBound(defaults.pairs + "`")
|
||||
|
||||
function handler(ch) {
|
||||
return function(cm) { return handleChar(cm, ch); };
|
||||
|
@ -302,7 +302,7 @@
|
||||
setTimeout(function(){cm.focus();}, 20);
|
||||
});
|
||||
|
||||
CodeMirror.signal(data, "select", completions[0], hints.firstChild);
|
||||
CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,7 @@
|
||||
var state = getSearchState(cm);
|
||||
if (state.query) return findNext(cm, rev);
|
||||
var q = cm.getSelection() || state.lastQuery;
|
||||
if (q instanceof RegExp && q.source == "x^") q = null
|
||||
if (persistent && cm.openDialog) {
|
||||
var hiding = null
|
||||
var searchNext = function(query, event) {
|
||||
|
@ -86,7 +86,7 @@
|
||||
if (!array.length) return coverRange(cm, from, to);
|
||||
|
||||
var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
|
||||
if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
|
||||
if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
|
||||
cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
|
||||
return reset(cm);
|
||||
|
||||
|
@ -571,7 +571,7 @@
|
||||
return {type: "part",
|
||||
name: data.name,
|
||||
offsetLines: from.line,
|
||||
text: doc.getRange(from, Pos(endLine, 0))};
|
||||
text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
|
||||
}
|
||||
|
||||
// Generic utilities
|
||||
|
@ -520,13 +520,18 @@ var CodeMirror =
|
||||
}
|
||||
|
||||
// Returns the value from the range [`from`; `to`] that satisfies
|
||||
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
|
||||
// `pred` and is closest to `from`. Assumes that at least `to`
|
||||
// satisfies `pred`. Supports `from` being greater than `to`.
|
||||
function findFirst(pred, from, to) {
|
||||
// At any point we are certain `to` satisfies `pred`, don't know
|
||||
// whether `from` does.
|
||||
var dir = from > to ? -1 : 1
|
||||
for (;;) {
|
||||
if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
|
||||
var mid = Math.floor((from + to) / 2)
|
||||
if (from == to) { return from }
|
||||
var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
|
||||
if (mid == from) { return pred(mid) ? from : to }
|
||||
if (pred(mid)) { to = mid }
|
||||
else { from = mid }
|
||||
else { from = mid + dir }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1139,12 +1144,12 @@ var CodeMirror =
|
||||
// BIDI HELPERS
|
||||
|
||||
function iterateBidiSections(order, from, to, f) {
|
||||
if (!order) { return f(from, to, "ltr") }
|
||||
if (!order) { return f(from, to, "ltr", 0) }
|
||||
var found = false
|
||||
for (var i = 0; i < order.length; ++i) {
|
||||
var part = order[i]
|
||||
if (part.from < to && part.to > from || from == to && part.to == from) {
|
||||
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
|
||||
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@ -1347,112 +1352,6 @@ var CodeMirror =
|
||||
return order
|
||||
}
|
||||
|
||||
function moveCharLogically(line, ch, dir) {
|
||||
var target = skipExtendingChars(line.text, ch + dir, dir)
|
||||
return target < 0 || target > line.text.length ? null : target
|
||||
}
|
||||
|
||||
function moveLogically(line, start, dir) {
|
||||
var ch = moveCharLogically(line, start.ch, dir)
|
||||
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
|
||||
}
|
||||
|
||||
function endOfLine(visually, cm, lineObj, lineNo, dir) {
|
||||
if (visually) {
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
if (order) {
|
||||
var part = dir < 0 ? lst(order) : order[0]
|
||||
var moveInStorageOrder = (dir < 0) == (part.level == 1)
|
||||
var sticky = moveInStorageOrder ? "after" : "before"
|
||||
var ch
|
||||
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
|
||||
// it could be that the last bidi part is not on the last visual line,
|
||||
// since visual lines contain content order-consecutive chunks.
|
||||
// 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) {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
|
||||
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
|
||||
} else { ch = dir < 0 ? part.to : part.from }
|
||||
return new Pos(lineNo, ch, sticky)
|
||||
}
|
||||
}
|
||||
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
|
||||
}
|
||||
|
||||
function moveVisually(cm, line, start, dir) {
|
||||
var bidi = getOrder(line, cm.doc.direction)
|
||||
if (!bidi) { return moveLogically(line, start, dir) }
|
||||
if (start.ch >= line.text.length) {
|
||||
start.ch = line.text.length
|
||||
start.sticky = "before"
|
||||
} else if (start.ch <= 0) {
|
||||
start.ch = 0
|
||||
start.sticky = "after"
|
||||
}
|
||||
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
|
||||
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
|
||||
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
|
||||
// nothing interesting happens.
|
||||
return moveLogically(line, start, dir)
|
||||
}
|
||||
|
||||
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
|
||||
var prep
|
||||
var getWrappedLineExtent = function (ch) {
|
||||
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
|
||||
prep = prep || prepareMeasureForLine(cm, line)
|
||||
return wrappedLineExtentChar(cm, line, prep, ch)
|
||||
}
|
||||
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
|
||||
|
||||
if (cm.doc.direction == "rtl" || part.level == 1) {
|
||||
var moveInStorageOrder = (part.level == 1) == (dir < 0)
|
||||
var ch = mv(start, moveInStorageOrder ? 1 : -1)
|
||||
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
|
||||
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
|
||||
var sticky = moveInStorageOrder ? "before" : "after"
|
||||
return new Pos(start.line, ch, sticky)
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Could not move within this bidi part in this visual line, so leave
|
||||
// the current bidi part
|
||||
|
||||
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
|
||||
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
|
||||
? new Pos(start.line, mv(ch, 1), "before")
|
||||
: new Pos(start.line, ch, "after"); }
|
||||
|
||||
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
|
||||
var part = bidi[partPos]
|
||||
var moveInStorageOrder = (dir > 0) == (part.level != 1)
|
||||
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
|
||||
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
|
||||
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
|
||||
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3a: Look for other bidi parts on the same visual line
|
||||
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
|
||||
if (res) { return res }
|
||||
|
||||
// Case 3b: Look for other bidi parts on the next visual line
|
||||
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
|
||||
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
|
||||
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
|
||||
if (res) { return res }
|
||||
}
|
||||
|
||||
// Case 4: Nowhere to move
|
||||
return null
|
||||
}
|
||||
|
||||
// EVENT HANDLING
|
||||
|
||||
// Lightweight event framework. on/off also work on DOM nodes,
|
||||
@ -2961,15 +2860,22 @@ var CodeMirror =
|
||||
return window.pageYOffset || (document.documentElement || document.body).scrollTop
|
||||
}
|
||||
|
||||
function widgetTopHeight(lineObj) {
|
||||
var height = 0
|
||||
if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
|
||||
{ height += widgetHeight(lineObj.widgets[i]) } } }
|
||||
return height
|
||||
}
|
||||
|
||||
// Converts a {top, bottom, left, right} box from line-local
|
||||
// coordinates into another coordinate system. Context may be one of
|
||||
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
|
||||
// or "page".
|
||||
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
|
||||
if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
|
||||
var size = widgetHeight(lineObj.widgets[i])
|
||||
rect.top += size; rect.bottom += size
|
||||
} } }
|
||||
if (!includeWidgets) {
|
||||
var height = widgetTopHeight(lineObj)
|
||||
rect.top += height; rect.bottom += height
|
||||
}
|
||||
if (context == "line") { return rect }
|
||||
if (!context) { context = "local" }
|
||||
var yOff = heightAtLine(lineObj)
|
||||
@ -3044,7 +2950,7 @@ var CodeMirror =
|
||||
if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
|
||||
|
||||
function getBidi(ch, partPos, invert) {
|
||||
var part = order[partPos], right = (part.level % 2) != 0
|
||||
var part = order[partPos], right = part.level == 1
|
||||
return get(invert ? ch - 1 : ch, right != invert)
|
||||
}
|
||||
var partPos = getBidiPartAt(order, ch, sticky)
|
||||
@ -3102,77 +3008,146 @@ var CodeMirror =
|
||||
}
|
||||
|
||||
function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
|
||||
var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }
|
||||
y -= widgetTopHeight(lineObj)
|
||||
var end = lineObj.text.length
|
||||
var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0)
|
||||
end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
|
||||
var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
|
||||
end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
|
||||
return {begin: begin, end: end}
|
||||
}
|
||||
|
||||
function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
|
||||
if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
|
||||
var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
|
||||
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
|
||||
}
|
||||
|
||||
// Returns true if the given side of a box is after the given
|
||||
// coordinates, in top-to-bottom, left-to-right order.
|
||||
function boxIsAfter(box, x, y, left) {
|
||||
return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
|
||||
}
|
||||
|
||||
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
||||
// Move y into line-local coordinate space
|
||||
y -= heightAtLine(lineObj)
|
||||
var begin = 0, end = lineObj.text.length
|
||||
var preparedMeasure = prepareMeasureForLine(cm, lineObj)
|
||||
var pos
|
||||
// When directly calling `measureCharPrepared`, we have to adjust
|
||||
// for the widgets at this line.
|
||||
var widgetHeight = widgetTopHeight(lineObj)
|
||||
var begin = 0, end = lineObj.text.length, ltr = true
|
||||
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
// If the line isn't plain left-to-right text, first figure out
|
||||
// which bidi section the coordinates fall into.
|
||||
if (order) {
|
||||
if (cm.options.lineWrapping) {
|
||||
;var assign;
|
||||
((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign))
|
||||
}
|
||||
pos = new Pos(lineNo, Math.floor(begin + (end - begin) / 2))
|
||||
var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
|
||||
var dir = beginLeft < x ? 1 : -1
|
||||
var prevDiff, diff = beginLeft - x, prevPos
|
||||
var steps = Math.ceil((end - begin) / 4)
|
||||
outer: do {
|
||||
prevDiff = diff
|
||||
prevPos = pos
|
||||
var i = 0
|
||||
for (; i < steps; ++i) {
|
||||
var prevPos$1 = pos
|
||||
pos = moveVisually(cm, lineObj, pos, dir)
|
||||
if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
|
||||
pos = prevPos$1
|
||||
break outer
|
||||
}
|
||||
}
|
||||
diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
|
||||
if (steps > 1) {
|
||||
var diff_change_per_step = Math.abs(diff - prevDiff) / steps
|
||||
steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step))
|
||||
dir = diff < 0 ? 1 : -1
|
||||
}
|
||||
} while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))))
|
||||
if (Math.abs(diff) > Math.abs(prevDiff)) {
|
||||
if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
|
||||
pos = prevPos
|
||||
}
|
||||
} else {
|
||||
var ch = findFirst(function (ch) {
|
||||
var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
|
||||
if (box.top > y) {
|
||||
// For the cursor stickiness
|
||||
end = Math.min(ch, end)
|
||||
return true
|
||||
}
|
||||
else if (box.bottom <= y) { return false }
|
||||
else if (box.left > x) { return true }
|
||||
else if (box.right < x) { return false }
|
||||
else { return (x - box.left < box.right - x) }
|
||||
}, begin, end)
|
||||
ch = skipExtendingChars(lineObj.text, ch, 1)
|
||||
pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
|
||||
var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
|
||||
(cm, lineObj, lineNo, preparedMeasure, order, x, y)
|
||||
ltr = part.level != 1
|
||||
// The awkward -1 offsets are needed because findFirst (called
|
||||
// on these below) will treat its first bound as inclusive,
|
||||
// second as exclusive, but we want to actually address the
|
||||
// characters in the part's range
|
||||
begin = ltr ? part.from : part.to - 1
|
||||
end = ltr ? part.to : part.from - 1
|
||||
}
|
||||
var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
|
||||
if (y < coords.top || coords.bottom < y) { pos.outside = true }
|
||||
pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
|
||||
return pos
|
||||
|
||||
// A binary search to find the first character whose bounding box
|
||||
// starts after the coordinates. If we run across any whose box wrap
|
||||
// the coordinates, store that.
|
||||
var chAround = null, boxAround = null
|
||||
var ch = findFirst(function (ch) {
|
||||
var box = measureCharPrepared(cm, preparedMeasure, ch)
|
||||
box.top += widgetHeight; box.bottom += widgetHeight
|
||||
if (!boxIsAfter(box, x, y, false)) { return false }
|
||||
if (box.top <= y && box.left <= x) {
|
||||
chAround = ch
|
||||
boxAround = box
|
||||
}
|
||||
return true
|
||||
}, begin, end)
|
||||
|
||||
var baseX, sticky, outside = false
|
||||
// If a box around the coordinates was found, use that
|
||||
if (boxAround) {
|
||||
// Distinguish coordinates nearer to the left or right side of the box
|
||||
var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
|
||||
ch = chAround + (atStart ? 0 : 1)
|
||||
sticky = atStart ? "after" : "before"
|
||||
baseX = atLeft ? boxAround.left : boxAround.right
|
||||
} else {
|
||||
// (Adjust for extended bound, if necessary.)
|
||||
if (!ltr && (ch == end || ch == begin)) { ch++ }
|
||||
// To determine which side to associate with, get the box to the
|
||||
// left of the character and compare it's vertical position to the
|
||||
// coordinates
|
||||
sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
|
||||
(measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
|
||||
"after" : "before"
|
||||
// Now get accurate coordinates for this place, in order to get a
|
||||
// base X position
|
||||
var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
|
||||
baseX = coords.left
|
||||
outside = y < coords.top || y >= coords.bottom
|
||||
}
|
||||
|
||||
ch = skipExtendingChars(lineObj.text, ch, 1)
|
||||
return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
|
||||
}
|
||||
|
||||
function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
|
||||
// Bidi parts are sorted left-to-right, and in a non-line-wrapping
|
||||
// situation, we can take this ordering to correspond to the visual
|
||||
// ordering. This finds the first part whose end is after the given
|
||||
// coordinates.
|
||||
var index = findFirst(function (i) {
|
||||
var part = order[i], ltr = part.level != 1
|
||||
return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
|
||||
"line", lineObj, preparedMeasure), x, y, true)
|
||||
}, 0, order.length - 1)
|
||||
var part = order[index]
|
||||
// If this isn't the first part, the part's start is also after
|
||||
// the coordinates, and the coordinates aren't on the same line as
|
||||
// that start, move one part back.
|
||||
if (index > 0) {
|
||||
var ltr = part.level != 1
|
||||
var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
|
||||
"line", lineObj, preparedMeasure)
|
||||
if (boxIsAfter(start, x, y, true) && start.top > y)
|
||||
{ part = order[index - 1] }
|
||||
}
|
||||
return part
|
||||
}
|
||||
|
||||
function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
|
||||
// In a wrapped line, rtl text on wrapping boundaries can do things
|
||||
// that don't correspond to the ordering in our `order` array at
|
||||
// all, so a binary search doesn't work, and we want to return a
|
||||
// part that only spans one line so that the binary search in
|
||||
// coordsCharInner is safe. As such, we first find the extent of the
|
||||
// wrapped line, and then do a flat search in which we discard any
|
||||
// spans that aren't on the line.
|
||||
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
|
||||
var begin = ref.begin;
|
||||
var end = ref.end;
|
||||
var part = null, closestDist = null
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var p = order[i]
|
||||
if (p.from >= end || p.to <= begin) { continue }
|
||||
var ltr = p.level != 1
|
||||
var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
|
||||
// Weigh against spans ending before this, so that they are only
|
||||
// picked if nothing ends after
|
||||
var dist = endX < x ? x - endX + 1e9 : endX - x
|
||||
if (!part || closestDist > dist) {
|
||||
part = p
|
||||
closestDist = dist
|
||||
}
|
||||
}
|
||||
if (!part) { part = order[order.length - 1] }
|
||||
// Clip the part to the wrapped line.
|
||||
if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
|
||||
if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
|
||||
return part
|
||||
}
|
||||
|
||||
var measureText
|
||||
@ -3298,12 +3273,14 @@ var CodeMirror =
|
||||
}
|
||||
|
||||
function prepareSelection(cm, primary) {
|
||||
if ( primary === void 0 ) primary = true;
|
||||
|
||||
var doc = cm.doc, result = {}
|
||||
var curFragment = result.cursors = document.createDocumentFragment()
|
||||
var selFragment = result.selection = document.createDocumentFragment()
|
||||
|
||||
for (var i = 0; i < doc.sel.ranges.length; i++) {
|
||||
if (primary === false && i == doc.sel.primIndex) { continue }
|
||||
if (!primary && i == doc.sel.primIndex) { continue }
|
||||
var range = doc.sel.ranges[i]
|
||||
if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
|
||||
var collapsed = range.empty()
|
||||
@ -3334,6 +3311,8 @@ var CodeMirror =
|
||||
}
|
||||
}
|
||||
|
||||
function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
|
||||
|
||||
// Draws the given range as a highlighted selection
|
||||
function drawSelectionRange(cm, range, output) {
|
||||
var display = cm.display, doc = cm.doc
|
||||
@ -3356,30 +3335,48 @@ var CodeMirror =
|
||||
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
|
||||
}
|
||||
|
||||
iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
|
||||
var leftPos = coords(from, "left"), rightPos, left, right
|
||||
if (from == to) {
|
||||
rightPos = leftPos
|
||||
left = right = leftPos.left
|
||||
} else {
|
||||
rightPos = coords(to - 1, "right")
|
||||
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
|
||||
left = leftPos.left
|
||||
right = rightPos.right
|
||||
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)
|
||||
}
|
||||
}
|
||||
if (fromArg == null && from == 0) { left = leftSide }
|
||||
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
||||
add(left, leftPos.top, null, leftPos.bottom)
|
||||
left = leftSide
|
||||
if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
|
||||
}
|
||||
if (toArg == null && to == lineLen) { right = rightSide }
|
||||
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
|
||||
{ start = leftPos }
|
||||
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
|
||||
{ end = rightPos }
|
||||
if (left < leftSide + 1) { left = leftSide }
|
||||
add(left, rightPos.top, right - left, rightPos.bottom)
|
||||
|
||||
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
|
||||
if (cmpCoords(toPos, start) < 0) { start = toPos }
|
||||
if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
|
||||
if (cmpCoords(toPos, end) < 0) { end = toPos }
|
||||
})
|
||||
return {start: start, end: end}
|
||||
}
|
||||
@ -4009,7 +4006,7 @@ var CodeMirror =
|
||||
}
|
||||
|
||||
if (op.updatedDisplay || op.selectionChanged)
|
||||
{ op.preparedSelection = display.input.prepareSelection(op.focus) }
|
||||
{ op.preparedSelection = display.input.prepareSelection() }
|
||||
}
|
||||
|
||||
function endOperation_W2(op) {
|
||||
@ -4022,7 +4019,7 @@ var CodeMirror =
|
||||
cm.display.maxLineChanged = false
|
||||
}
|
||||
|
||||
var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
|
||||
var takeFocus = op.focus && op.focus == activeElt()
|
||||
if (op.preparedSelection)
|
||||
{ cm.display.input.showSelection(op.preparedSelection, takeFocus) }
|
||||
if (op.updatedDisplay || op.startHeight != cm.doc.height)
|
||||
@ -5611,7 +5608,8 @@ var CodeMirror =
|
||||
|
||||
function replaceRange(doc, code, from, to, origin) {
|
||||
if (!to) { to = from }
|
||||
if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
|
||||
if (cmp(to, from) < 0) { var assign;
|
||||
(assign = [to, from], from = assign[0], to = assign[1], assign) }
|
||||
if (typeof code == "string") { code = doc.splitLines(code) }
|
||||
makeChange(doc, {from: from, to: to, text: code, origin: origin})
|
||||
}
|
||||
@ -6973,6 +6971,112 @@ var CodeMirror =
|
||||
})
|
||||
}
|
||||
|
||||
function moveCharLogically(line, ch, dir) {
|
||||
var target = skipExtendingChars(line.text, ch + dir, dir)
|
||||
return target < 0 || target > line.text.length ? null : target
|
||||
}
|
||||
|
||||
function moveLogically(line, start, dir) {
|
||||
var ch = moveCharLogically(line, start.ch, dir)
|
||||
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
|
||||
}
|
||||
|
||||
function endOfLine(visually, cm, lineObj, lineNo, dir) {
|
||||
if (visually) {
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
if (order) {
|
||||
var part = dir < 0 ? lst(order) : order[0]
|
||||
var moveInStorageOrder = (dir < 0) == (part.level == 1)
|
||||
var sticky = moveInStorageOrder ? "after" : "before"
|
||||
var ch
|
||||
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
|
||||
// it could be that the last bidi part is not on the last visual line,
|
||||
// since visual lines contain content order-consecutive chunks.
|
||||
// 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) {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
|
||||
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
|
||||
} else { ch = dir < 0 ? part.to : part.from }
|
||||
return new Pos(lineNo, ch, sticky)
|
||||
}
|
||||
}
|
||||
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
|
||||
}
|
||||
|
||||
function moveVisually(cm, line, start, dir) {
|
||||
var bidi = getOrder(line, cm.doc.direction)
|
||||
if (!bidi) { return moveLogically(line, start, dir) }
|
||||
if (start.ch >= line.text.length) {
|
||||
start.ch = line.text.length
|
||||
start.sticky = "before"
|
||||
} else if (start.ch <= 0) {
|
||||
start.ch = 0
|
||||
start.sticky = "after"
|
||||
}
|
||||
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
|
||||
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
|
||||
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
|
||||
// nothing interesting happens.
|
||||
return moveLogically(line, start, dir)
|
||||
}
|
||||
|
||||
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
|
||||
var prep
|
||||
var getWrappedLineExtent = function (ch) {
|
||||
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
|
||||
prep = prep || prepareMeasureForLine(cm, line)
|
||||
return wrappedLineExtentChar(cm, line, prep, ch)
|
||||
}
|
||||
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
|
||||
|
||||
if (cm.doc.direction == "rtl" || part.level == 1) {
|
||||
var moveInStorageOrder = (part.level == 1) == (dir < 0)
|
||||
var ch = mv(start, moveInStorageOrder ? 1 : -1)
|
||||
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
|
||||
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
|
||||
var sticky = moveInStorageOrder ? "before" : "after"
|
||||
return new Pos(start.line, ch, sticky)
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Could not move within this bidi part in this visual line, so leave
|
||||
// the current bidi part
|
||||
|
||||
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
|
||||
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
|
||||
? new Pos(start.line, mv(ch, 1), "before")
|
||||
: new Pos(start.line, ch, "after"); }
|
||||
|
||||
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
|
||||
var part = bidi[partPos]
|
||||
var moveInStorageOrder = (dir > 0) == (part.level != 1)
|
||||
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
|
||||
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
|
||||
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
|
||||
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3a: Look for other bidi parts on the same visual line
|
||||
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
|
||||
if (res) { return res }
|
||||
|
||||
// Case 3b: Look for other bidi parts on the next visual line
|
||||
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
|
||||
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
|
||||
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
|
||||
if (res) { return res }
|
||||
}
|
||||
|
||||
// Case 4: Nowhere to move
|
||||
return null
|
||||
}
|
||||
|
||||
// Commands are parameter-less actions that can be performed on an
|
||||
// editor, mostly used for keybindings.
|
||||
var commands = {
|
||||
@ -7534,7 +7638,7 @@ var CodeMirror =
|
||||
anchor = maxPos(oldRange.to(), range.head)
|
||||
}
|
||||
var ranges$1 = startSel.ranges.slice(0)
|
||||
ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
|
||||
ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
|
||||
setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
|
||||
}
|
||||
}
|
||||
@ -7586,13 +7690,52 @@ var CodeMirror =
|
||||
on(document, "mouseup", up)
|
||||
}
|
||||
|
||||
// Used when mouse-selecting to adjust the anchor to the proper side
|
||||
// of a bidi jump depending on the visual position of the head.
|
||||
function bidiSimplify(cm, range) {
|
||||
var anchor = range.anchor;
|
||||
var head = range.head;
|
||||
var anchorLine = getLine(cm.doc, anchor.line)
|
||||
if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
|
||||
var order = getOrder(anchorLine)
|
||||
if (!order) { return range }
|
||||
var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
|
||||
if (part.from != anchor.ch && part.to != anchor.ch) { return range }
|
||||
var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
|
||||
if (boundary == 0 || boundary == order.length) { return range }
|
||||
|
||||
// Compute the relative visual position of the head compared to the
|
||||
// anchor (<0 is to the left, >0 to the right)
|
||||
var leftSide
|
||||
if (head.line != anchor.line) {
|
||||
leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
|
||||
} else {
|
||||
var headIndex = getBidiPartAt(order, head.ch, head.sticky)
|
||||
var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
|
||||
if (headIndex == boundary - 1 || headIndex == boundary)
|
||||
{ leftSide = dir < 0 }
|
||||
else
|
||||
{ leftSide = dir > 0 }
|
||||
}
|
||||
|
||||
var usePart = order[boundary + (leftSide ? -1 : 0)]
|
||||
var from = leftSide == (usePart.level == 1)
|
||||
var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
|
||||
return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
|
||||
}
|
||||
|
||||
|
||||
// Determines whether an event happened in the gutter, and fires the
|
||||
// handlers for the corresponding event.
|
||||
function gutterEvent(cm, e, type, prevent) {
|
||||
var mX, mY
|
||||
try { mX = e.clientX; mY = e.clientY }
|
||||
catch(e) { return false }
|
||||
if (e.touches) {
|
||||
mX = e.touches[0].clientX
|
||||
mY = e.touches[0].clientY
|
||||
} else {
|
||||
try { mX = e.clientX; mY = e.clientY }
|
||||
catch(e) { return false }
|
||||
}
|
||||
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
|
||||
if (prevent) { e_preventDefault(e) }
|
||||
|
||||
@ -7930,7 +8073,7 @@ var CodeMirror =
|
||||
return dx * dx + dy * dy > 20 * 20
|
||||
}
|
||||
on(d.scroller, "touchstart", function (e) {
|
||||
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
|
||||
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
|
||||
d.input.ensurePolled()
|
||||
clearTimeout(touchFinished)
|
||||
var now = +new Date
|
||||
@ -9714,7 +9857,7 @@ var CodeMirror =
|
||||
|
||||
addLegacyProps(CodeMirror)
|
||||
|
||||
CodeMirror.version = "5.29.0"
|
||||
CodeMirror.version = "5.30.0"
|
||||
|
||||
return CodeMirror;
|
||||
|
||||
@ -10153,6 +10296,7 @@ var CodeMirror =
|
||||
var state = getSearchState(cm);
|
||||
if (state.query) return findNext(cm, rev);
|
||||
var q = cm.getSelection() || state.lastQuery;
|
||||
if (q instanceof RegExp && q.source == "x^") q = null
|
||||
if (persistent && cm.openDialog) {
|
||||
var hiding = null
|
||||
var searchNext = function(query, event) {
|
||||
@ -10462,6 +10606,7 @@ var CodeMirror =
|
||||
cm.state.closeBrackets = null;
|
||||
}
|
||||
if (val) {
|
||||
ensureBound(getOption(val, "pairs"))
|
||||
cm.state.closeBrackets = val;
|
||||
cm.addKeyMap(keyMap);
|
||||
}
|
||||
@ -10473,10 +10618,14 @@ var CodeMirror =
|
||||
return defaults[name];
|
||||
}
|
||||
|
||||
var bind = defaults.pairs + "`";
|
||||
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||
for (var i = 0; i < bind.length; i++)
|
||||
keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
|
||||
function ensureBound(chars) {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||
}
|
||||
}
|
||||
ensureBound(defaults.pairs + "`")
|
||||
|
||||
function handler(ch) {
|
||||
return function(cm) { return handleChar(cm, ch); };
|
||||
@ -10894,7 +11043,7 @@ var CodeMirror =
|
||||
|
||||
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, "throw": C, "debugger": C,
|
||||
"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"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
@ -11264,7 +11413,7 @@ var CodeMirror =
|
||||
function expressionInner(type, noComma) {
|
||||
if (cx.state.fatArrowAt == cx.stream.start) {
|
||||
var body = noComma ? arrowBodyNoComma : arrowBody;
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
|
||||
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
|
||||
}
|
||||
|
||||
@ -11309,6 +11458,11 @@ var CodeMirror =
|
||||
if (type == ".") return cont(property, me);
|
||||
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
|
||||
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
|
||||
if (type == "regexp") {
|
||||
cx.state.lastType = cx.marked = "operator"
|
||||
cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
|
||||
return cont(expr)
|
||||
}
|
||||
}
|
||||
function quasi(type, value) {
|
||||
if (type != "quasi") return pass();
|
||||
@ -11357,6 +11511,9 @@ var CodeMirror =
|
||||
} else if (type == "variable" || cx.style == "keyword") {
|
||||
cx.marked = "property";
|
||||
if (value == "get" || value == "set") return cont(getterSetter);
|
||||
var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
|
||||
if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
|
||||
cx.state.fatArrowAt = cx.stream.pos + m[0].length
|
||||
return cont(afterprop);
|
||||
} else if (type == "number" || type == "string") {
|
||||
cx.marked = jsonldMode ? "property" : (cx.style + " property");
|
||||
@ -11514,7 +11671,8 @@ var CodeMirror =
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
|
||||
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
|
||||
}
|
||||
function funarg(type) {
|
||||
function funarg(type, value) {
|
||||
if (value == "@") cont(expression, funarg)
|
||||
if (type == "spread" || type == "modifier") return cont(funarg);
|
||||
return pass(pattern, maybetype, maybeAssign);
|
||||
}
|
||||
@ -11540,7 +11698,7 @@ var CodeMirror =
|
||||
cx.marked = "keyword";
|
||||
return cont(classBody);
|
||||
}
|
||||
if (type == "variable") {
|
||||
if (type == "variable" || cx.style == "keyword") {
|
||||
cx.marked = "property";
|
||||
return cont(isTS ? classfield : functiondef, classBody);
|
||||
}
|
||||
@ -11602,7 +11760,7 @@ var CodeMirror =
|
||||
|
||||
function expressionAllowed(stream, state, backUp) {
|
||||
return state.tokenize == tokenBase &&
|
||||
/^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
|
||||
}
|
||||
|
||||
|
@ -278,13 +278,18 @@ function skipExtendingChars(str, pos, dir) {
|
||||
}
|
||||
|
||||
// Returns the value from the range [`from`; `to`] that satisfies
|
||||
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
|
||||
// `pred` and is closest to `from`. Assumes that at least `to`
|
||||
// satisfies `pred`. Supports `from` being greater than `to`.
|
||||
function findFirst(pred, from, to) {
|
||||
// At any point we are certain `to` satisfies `pred`, don't know
|
||||
// whether `from` does.
|
||||
var dir = from > to ? -1 : 1
|
||||
for (;;) {
|
||||
if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
|
||||
var mid = Math.floor((from + to) / 2)
|
||||
if (from == to) { return from }
|
||||
var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
|
||||
if (mid == from) { return pred(mid) ? from : to }
|
||||
if (pred(mid)) { to = mid }
|
||||
else { from = mid }
|
||||
else { from = mid + dir }
|
||||
}
|
||||
}
|
||||
|
||||
@ -897,12 +902,12 @@ function findMaxLine(cm) {
|
||||
// BIDI HELPERS
|
||||
|
||||
function iterateBidiSections(order, from, to, f) {
|
||||
if (!order) { return f(from, to, "ltr") }
|
||||
if (!order) { return f(from, to, "ltr", 0) }
|
||||
var found = false
|
||||
for (var i = 0; i < order.length; ++i) {
|
||||
var part = order[i]
|
||||
if (part.from < to && part.to > from || from == to && part.to == from) {
|
||||
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
|
||||
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@ -1105,112 +1110,6 @@ function getOrder(line, direction) {
|
||||
return order
|
||||
}
|
||||
|
||||
function moveCharLogically(line, ch, dir) {
|
||||
var target = skipExtendingChars(line.text, ch + dir, dir)
|
||||
return target < 0 || target > line.text.length ? null : target
|
||||
}
|
||||
|
||||
function moveLogically(line, start, dir) {
|
||||
var ch = moveCharLogically(line, start.ch, dir)
|
||||
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
|
||||
}
|
||||
|
||||
function endOfLine(visually, cm, lineObj, lineNo, dir) {
|
||||
if (visually) {
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
if (order) {
|
||||
var part = dir < 0 ? lst(order) : order[0]
|
||||
var moveInStorageOrder = (dir < 0) == (part.level == 1)
|
||||
var sticky = moveInStorageOrder ? "after" : "before"
|
||||
var ch
|
||||
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
|
||||
// it could be that the last bidi part is not on the last visual line,
|
||||
// since visual lines contain content order-consecutive chunks.
|
||||
// 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) {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
|
||||
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
|
||||
} else { ch = dir < 0 ? part.to : part.from }
|
||||
return new Pos(lineNo, ch, sticky)
|
||||
}
|
||||
}
|
||||
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
|
||||
}
|
||||
|
||||
function moveVisually(cm, line, start, dir) {
|
||||
var bidi = getOrder(line, cm.doc.direction)
|
||||
if (!bidi) { return moveLogically(line, start, dir) }
|
||||
if (start.ch >= line.text.length) {
|
||||
start.ch = line.text.length
|
||||
start.sticky = "before"
|
||||
} else if (start.ch <= 0) {
|
||||
start.ch = 0
|
||||
start.sticky = "after"
|
||||
}
|
||||
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
|
||||
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
|
||||
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
|
||||
// nothing interesting happens.
|
||||
return moveLogically(line, start, dir)
|
||||
}
|
||||
|
||||
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
|
||||
var prep
|
||||
var getWrappedLineExtent = function (ch) {
|
||||
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
|
||||
prep = prep || prepareMeasureForLine(cm, line)
|
||||
return wrappedLineExtentChar(cm, line, prep, ch)
|
||||
}
|
||||
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
|
||||
|
||||
if (cm.doc.direction == "rtl" || part.level == 1) {
|
||||
var moveInStorageOrder = (part.level == 1) == (dir < 0)
|
||||
var ch = mv(start, moveInStorageOrder ? 1 : -1)
|
||||
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
|
||||
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
|
||||
var sticky = moveInStorageOrder ? "before" : "after"
|
||||
return new Pos(start.line, ch, sticky)
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Could not move within this bidi part in this visual line, so leave
|
||||
// the current bidi part
|
||||
|
||||
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
|
||||
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
|
||||
? new Pos(start.line, mv(ch, 1), "before")
|
||||
: new Pos(start.line, ch, "after"); }
|
||||
|
||||
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
|
||||
var part = bidi[partPos]
|
||||
var moveInStorageOrder = (dir > 0) == (part.level != 1)
|
||||
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
|
||||
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
|
||||
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
|
||||
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3a: Look for other bidi parts on the same visual line
|
||||
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
|
||||
if (res) { return res }
|
||||
|
||||
// Case 3b: Look for other bidi parts on the next visual line
|
||||
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
|
||||
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
|
||||
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
|
||||
if (res) { return res }
|
||||
}
|
||||
|
||||
// Case 4: Nowhere to move
|
||||
return null
|
||||
}
|
||||
|
||||
// EVENT HANDLING
|
||||
|
||||
// Lightweight event framework. on/off also work on DOM nodes,
|
||||
@ -2719,15 +2618,22 @@ function pageScrollY() {
|
||||
return window.pageYOffset || (document.documentElement || document.body).scrollTop
|
||||
}
|
||||
|
||||
function widgetTopHeight(lineObj) {
|
||||
var height = 0
|
||||
if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
|
||||
{ height += widgetHeight(lineObj.widgets[i]) } } }
|
||||
return height
|
||||
}
|
||||
|
||||
// Converts a {top, bottom, left, right} box from line-local
|
||||
// coordinates into another coordinate system. Context may be one of
|
||||
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
|
||||
// or "page".
|
||||
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
|
||||
if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
|
||||
var size = widgetHeight(lineObj.widgets[i])
|
||||
rect.top += size; rect.bottom += size
|
||||
} } }
|
||||
if (!includeWidgets) {
|
||||
var height = widgetTopHeight(lineObj)
|
||||
rect.top += height; rect.bottom += height
|
||||
}
|
||||
if (context == "line") { return rect }
|
||||
if (!context) { context = "local" }
|
||||
var yOff = heightAtLine(lineObj)
|
||||
@ -2802,7 +2708,7 @@ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
|
||||
if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
|
||||
|
||||
function getBidi(ch, partPos, invert) {
|
||||
var part = order[partPos], right = (part.level % 2) != 0
|
||||
var part = order[partPos], right = part.level == 1
|
||||
return get(invert ? ch - 1 : ch, right != invert)
|
||||
}
|
||||
var partPos = getBidiPartAt(order, ch, sticky)
|
||||
@ -2860,77 +2766,146 @@ function coordsChar(cm, x, y) {
|
||||
}
|
||||
|
||||
function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
|
||||
var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }
|
||||
y -= widgetTopHeight(lineObj)
|
||||
var end = lineObj.text.length
|
||||
var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0)
|
||||
end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
|
||||
var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
|
||||
end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
|
||||
return {begin: begin, end: end}
|
||||
}
|
||||
|
||||
function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
|
||||
if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
|
||||
var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
|
||||
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
|
||||
}
|
||||
|
||||
// Returns true if the given side of a box is after the given
|
||||
// coordinates, in top-to-bottom, left-to-right order.
|
||||
function boxIsAfter(box, x, y, left) {
|
||||
return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
|
||||
}
|
||||
|
||||
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
||||
// Move y into line-local coordinate space
|
||||
y -= heightAtLine(lineObj)
|
||||
var begin = 0, end = lineObj.text.length
|
||||
var preparedMeasure = prepareMeasureForLine(cm, lineObj)
|
||||
var pos
|
||||
// When directly calling `measureCharPrepared`, we have to adjust
|
||||
// for the widgets at this line.
|
||||
var widgetHeight = widgetTopHeight(lineObj)
|
||||
var begin = 0, end = lineObj.text.length, ltr = true
|
||||
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
// If the line isn't plain left-to-right text, first figure out
|
||||
// which bidi section the coordinates fall into.
|
||||
if (order) {
|
||||
if (cm.options.lineWrapping) {
|
||||
;var assign;
|
||||
((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign))
|
||||
}
|
||||
pos = new Pos(lineNo, Math.floor(begin + (end - begin) / 2))
|
||||
var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
|
||||
var dir = beginLeft < x ? 1 : -1
|
||||
var prevDiff, diff = beginLeft - x, prevPos
|
||||
var steps = Math.ceil((end - begin) / 4)
|
||||
outer: do {
|
||||
prevDiff = diff
|
||||
prevPos = pos
|
||||
var i = 0
|
||||
for (; i < steps; ++i) {
|
||||
var prevPos$1 = pos
|
||||
pos = moveVisually(cm, lineObj, pos, dir)
|
||||
if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
|
||||
pos = prevPos$1
|
||||
break outer
|
||||
}
|
||||
}
|
||||
diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
|
||||
if (steps > 1) {
|
||||
var diff_change_per_step = Math.abs(diff - prevDiff) / steps
|
||||
steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step))
|
||||
dir = diff < 0 ? 1 : -1
|
||||
}
|
||||
} while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))))
|
||||
if (Math.abs(diff) > Math.abs(prevDiff)) {
|
||||
if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
|
||||
pos = prevPos
|
||||
}
|
||||
} else {
|
||||
var ch = findFirst(function (ch) {
|
||||
var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
|
||||
if (box.top > y) {
|
||||
// For the cursor stickiness
|
||||
end = Math.min(ch, end)
|
||||
return true
|
||||
}
|
||||
else if (box.bottom <= y) { return false }
|
||||
else if (box.left > x) { return true }
|
||||
else if (box.right < x) { return false }
|
||||
else { return (x - box.left < box.right - x) }
|
||||
}, begin, end)
|
||||
ch = skipExtendingChars(lineObj.text, ch, 1)
|
||||
pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
|
||||
var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
|
||||
(cm, lineObj, lineNo, preparedMeasure, order, x, y)
|
||||
ltr = part.level != 1
|
||||
// The awkward -1 offsets are needed because findFirst (called
|
||||
// on these below) will treat its first bound as inclusive,
|
||||
// second as exclusive, but we want to actually address the
|
||||
// characters in the part's range
|
||||
begin = ltr ? part.from : part.to - 1
|
||||
end = ltr ? part.to : part.from - 1
|
||||
}
|
||||
var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
|
||||
if (y < coords.top || coords.bottom < y) { pos.outside = true }
|
||||
pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
|
||||
return pos
|
||||
|
||||
// A binary search to find the first character whose bounding box
|
||||
// starts after the coordinates. If we run across any whose box wrap
|
||||
// the coordinates, store that.
|
||||
var chAround = null, boxAround = null
|
||||
var ch = findFirst(function (ch) {
|
||||
var box = measureCharPrepared(cm, preparedMeasure, ch)
|
||||
box.top += widgetHeight; box.bottom += widgetHeight
|
||||
if (!boxIsAfter(box, x, y, false)) { return false }
|
||||
if (box.top <= y && box.left <= x) {
|
||||
chAround = ch
|
||||
boxAround = box
|
||||
}
|
||||
return true
|
||||
}, begin, end)
|
||||
|
||||
var baseX, sticky, outside = false
|
||||
// If a box around the coordinates was found, use that
|
||||
if (boxAround) {
|
||||
// Distinguish coordinates nearer to the left or right side of the box
|
||||
var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
|
||||
ch = chAround + (atStart ? 0 : 1)
|
||||
sticky = atStart ? "after" : "before"
|
||||
baseX = atLeft ? boxAround.left : boxAround.right
|
||||
} else {
|
||||
// (Adjust for extended bound, if necessary.)
|
||||
if (!ltr && (ch == end || ch == begin)) { ch++ }
|
||||
// To determine which side to associate with, get the box to the
|
||||
// left of the character and compare it's vertical position to the
|
||||
// coordinates
|
||||
sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
|
||||
(measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
|
||||
"after" : "before"
|
||||
// Now get accurate coordinates for this place, in order to get a
|
||||
// base X position
|
||||
var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
|
||||
baseX = coords.left
|
||||
outside = y < coords.top || y >= coords.bottom
|
||||
}
|
||||
|
||||
ch = skipExtendingChars(lineObj.text, ch, 1)
|
||||
return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
|
||||
}
|
||||
|
||||
function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
|
||||
// Bidi parts are sorted left-to-right, and in a non-line-wrapping
|
||||
// situation, we can take this ordering to correspond to the visual
|
||||
// ordering. This finds the first part whose end is after the given
|
||||
// coordinates.
|
||||
var index = findFirst(function (i) {
|
||||
var part = order[i], ltr = part.level != 1
|
||||
return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
|
||||
"line", lineObj, preparedMeasure), x, y, true)
|
||||
}, 0, order.length - 1)
|
||||
var part = order[index]
|
||||
// If this isn't the first part, the part's start is also after
|
||||
// the coordinates, and the coordinates aren't on the same line as
|
||||
// that start, move one part back.
|
||||
if (index > 0) {
|
||||
var ltr = part.level != 1
|
||||
var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
|
||||
"line", lineObj, preparedMeasure)
|
||||
if (boxIsAfter(start, x, y, true) && start.top > y)
|
||||
{ part = order[index - 1] }
|
||||
}
|
||||
return part
|
||||
}
|
||||
|
||||
function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
|
||||
// In a wrapped line, rtl text on wrapping boundaries can do things
|
||||
// that don't correspond to the ordering in our `order` array at
|
||||
// all, so a binary search doesn't work, and we want to return a
|
||||
// part that only spans one line so that the binary search in
|
||||
// coordsCharInner is safe. As such, we first find the extent of the
|
||||
// wrapped line, and then do a flat search in which we discard any
|
||||
// spans that aren't on the line.
|
||||
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
|
||||
var begin = ref.begin;
|
||||
var end = ref.end;
|
||||
var part = null, closestDist = null
|
||||
for (var i = 0; i < order.length; i++) {
|
||||
var p = order[i]
|
||||
if (p.from >= end || p.to <= begin) { continue }
|
||||
var ltr = p.level != 1
|
||||
var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
|
||||
// Weigh against spans ending before this, so that they are only
|
||||
// picked if nothing ends after
|
||||
var dist = endX < x ? x - endX + 1e9 : endX - x
|
||||
if (!part || closestDist > dist) {
|
||||
part = p
|
||||
closestDist = dist
|
||||
}
|
||||
}
|
||||
if (!part) { part = order[order.length - 1] }
|
||||
// Clip the part to the wrapped line.
|
||||
if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
|
||||
if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
|
||||
return part
|
||||
}
|
||||
|
||||
var measureText
|
||||
@ -3056,12 +3031,14 @@ function updateSelection(cm) {
|
||||
}
|
||||
|
||||
function prepareSelection(cm, primary) {
|
||||
if ( primary === void 0 ) primary = true;
|
||||
|
||||
var doc = cm.doc, result = {}
|
||||
var curFragment = result.cursors = document.createDocumentFragment()
|
||||
var selFragment = result.selection = document.createDocumentFragment()
|
||||
|
||||
for (var i = 0; i < doc.sel.ranges.length; i++) {
|
||||
if (primary === false && i == doc.sel.primIndex) { continue }
|
||||
if (!primary && i == doc.sel.primIndex) { continue }
|
||||
var range = doc.sel.ranges[i]
|
||||
if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
|
||||
var collapsed = range.empty()
|
||||
@ -3092,6 +3069,8 @@ function drawSelectionCursor(cm, head, output) {
|
||||
}
|
||||
}
|
||||
|
||||
function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
|
||||
|
||||
// Draws the given range as a highlighted selection
|
||||
function drawSelectionRange(cm, range, output) {
|
||||
var display = cm.display, doc = cm.doc
|
||||
@ -3114,30 +3093,48 @@ function drawSelectionRange(cm, range, output) {
|
||||
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
|
||||
}
|
||||
|
||||
iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
|
||||
var leftPos = coords(from, "left"), rightPos, left, right
|
||||
if (from == to) {
|
||||
rightPos = leftPos
|
||||
left = right = leftPos.left
|
||||
} else {
|
||||
rightPos = coords(to - 1, "right")
|
||||
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
|
||||
left = leftPos.left
|
||||
right = rightPos.right
|
||||
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)
|
||||
}
|
||||
}
|
||||
if (fromArg == null && from == 0) { left = leftSide }
|
||||
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
|
||||
add(left, leftPos.top, null, leftPos.bottom)
|
||||
left = leftSide
|
||||
if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
|
||||
}
|
||||
if (toArg == null && to == lineLen) { right = rightSide }
|
||||
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
|
||||
{ start = leftPos }
|
||||
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
|
||||
{ end = rightPos }
|
||||
if (left < leftSide + 1) { left = leftSide }
|
||||
add(left, rightPos.top, right - left, rightPos.bottom)
|
||||
|
||||
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
|
||||
if (cmpCoords(toPos, start) < 0) { start = toPos }
|
||||
if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
|
||||
if (cmpCoords(toPos, end) < 0) { end = toPos }
|
||||
})
|
||||
return {start: start, end: end}
|
||||
}
|
||||
@ -3767,7 +3764,7 @@ function endOperation_R2(op) {
|
||||
}
|
||||
|
||||
if (op.updatedDisplay || op.selectionChanged)
|
||||
{ op.preparedSelection = display.input.prepareSelection(op.focus) }
|
||||
{ op.preparedSelection = display.input.prepareSelection() }
|
||||
}
|
||||
|
||||
function endOperation_W2(op) {
|
||||
@ -3780,7 +3777,7 @@ function endOperation_W2(op) {
|
||||
cm.display.maxLineChanged = false
|
||||
}
|
||||
|
||||
var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
|
||||
var takeFocus = op.focus && op.focus == activeElt()
|
||||
if (op.preparedSelection)
|
||||
{ cm.display.input.showSelection(op.preparedSelection, takeFocus) }
|
||||
if (op.updatedDisplay || op.startHeight != cm.doc.height)
|
||||
@ -5369,7 +5366,8 @@ function makeChangeSingleDocInEditor(cm, change, spans) {
|
||||
|
||||
function replaceRange(doc, code, from, to, origin) {
|
||||
if (!to) { to = from }
|
||||
if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
|
||||
if (cmp(to, from) < 0) { var assign;
|
||||
(assign = [to, from], from = assign[0], to = assign[1], assign) }
|
||||
if (typeof code == "string") { code = doc.splitLines(code) }
|
||||
makeChange(doc, {from: from, to: to, text: code, origin: origin})
|
||||
}
|
||||
@ -6731,6 +6729,112 @@ function deleteNearSelection(cm, compute) {
|
||||
})
|
||||
}
|
||||
|
||||
function moveCharLogically(line, ch, dir) {
|
||||
var target = skipExtendingChars(line.text, ch + dir, dir)
|
||||
return target < 0 || target > line.text.length ? null : target
|
||||
}
|
||||
|
||||
function moveLogically(line, start, dir) {
|
||||
var ch = moveCharLogically(line, start.ch, dir)
|
||||
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
|
||||
}
|
||||
|
||||
function endOfLine(visually, cm, lineObj, lineNo, dir) {
|
||||
if (visually) {
|
||||
var order = getOrder(lineObj, cm.doc.direction)
|
||||
if (order) {
|
||||
var part = dir < 0 ? lst(order) : order[0]
|
||||
var moveInStorageOrder = (dir < 0) == (part.level == 1)
|
||||
var sticky = moveInStorageOrder ? "after" : "before"
|
||||
var ch
|
||||
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
|
||||
// it could be that the last bidi part is not on the last visual line,
|
||||
// since visual lines contain content order-consecutive chunks.
|
||||
// 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) {
|
||||
var prep = prepareMeasureForLine(cm, lineObj)
|
||||
ch = dir < 0 ? lineObj.text.length - 1 : 0
|
||||
var targetTop = measureCharPrepared(cm, prep, ch).top
|
||||
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
|
||||
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
|
||||
} else { ch = dir < 0 ? part.to : part.from }
|
||||
return new Pos(lineNo, ch, sticky)
|
||||
}
|
||||
}
|
||||
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
|
||||
}
|
||||
|
||||
function moveVisually(cm, line, start, dir) {
|
||||
var bidi = getOrder(line, cm.doc.direction)
|
||||
if (!bidi) { return moveLogically(line, start, dir) }
|
||||
if (start.ch >= line.text.length) {
|
||||
start.ch = line.text.length
|
||||
start.sticky = "before"
|
||||
} else if (start.ch <= 0) {
|
||||
start.ch = 0
|
||||
start.sticky = "after"
|
||||
}
|
||||
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
|
||||
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
|
||||
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
|
||||
// nothing interesting happens.
|
||||
return moveLogically(line, start, dir)
|
||||
}
|
||||
|
||||
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
|
||||
var prep
|
||||
var getWrappedLineExtent = function (ch) {
|
||||
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
|
||||
prep = prep || prepareMeasureForLine(cm, line)
|
||||
return wrappedLineExtentChar(cm, line, prep, ch)
|
||||
}
|
||||
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
|
||||
|
||||
if (cm.doc.direction == "rtl" || part.level == 1) {
|
||||
var moveInStorageOrder = (part.level == 1) == (dir < 0)
|
||||
var ch = mv(start, moveInStorageOrder ? 1 : -1)
|
||||
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
|
||||
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
|
||||
var sticky = moveInStorageOrder ? "before" : "after"
|
||||
return new Pos(start.line, ch, sticky)
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Could not move within this bidi part in this visual line, so leave
|
||||
// the current bidi part
|
||||
|
||||
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
|
||||
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
|
||||
? new Pos(start.line, mv(ch, 1), "before")
|
||||
: new Pos(start.line, ch, "after"); }
|
||||
|
||||
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
|
||||
var part = bidi[partPos]
|
||||
var moveInStorageOrder = (dir > 0) == (part.level != 1)
|
||||
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
|
||||
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
|
||||
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
|
||||
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3a: Look for other bidi parts on the same visual line
|
||||
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
|
||||
if (res) { return res }
|
||||
|
||||
// Case 3b: Look for other bidi parts on the next visual line
|
||||
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
|
||||
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
|
||||
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
|
||||
if (res) { return res }
|
||||
}
|
||||
|
||||
// Case 4: Nowhere to move
|
||||
return null
|
||||
}
|
||||
|
||||
// Commands are parameter-less actions that can be performed on an
|
||||
// editor, mostly used for keybindings.
|
||||
var commands = {
|
||||
@ -7292,7 +7396,7 @@ function leftButtonSelect(cm, event, start, behavior) {
|
||||
anchor = maxPos(oldRange.to(), range.head)
|
||||
}
|
||||
var ranges$1 = startSel.ranges.slice(0)
|
||||
ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
|
||||
ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
|
||||
setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
|
||||
}
|
||||
}
|
||||
@ -7344,13 +7448,52 @@ function leftButtonSelect(cm, event, start, behavior) {
|
||||
on(document, "mouseup", up)
|
||||
}
|
||||
|
||||
// Used when mouse-selecting to adjust the anchor to the proper side
|
||||
// of a bidi jump depending on the visual position of the head.
|
||||
function bidiSimplify(cm, range) {
|
||||
var anchor = range.anchor;
|
||||
var head = range.head;
|
||||
var anchorLine = getLine(cm.doc, anchor.line)
|
||||
if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
|
||||
var order = getOrder(anchorLine)
|
||||
if (!order) { return range }
|
||||
var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
|
||||
if (part.from != anchor.ch && part.to != anchor.ch) { return range }
|
||||
var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
|
||||
if (boundary == 0 || boundary == order.length) { return range }
|
||||
|
||||
// Compute the relative visual position of the head compared to the
|
||||
// anchor (<0 is to the left, >0 to the right)
|
||||
var leftSide
|
||||
if (head.line != anchor.line) {
|
||||
leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
|
||||
} else {
|
||||
var headIndex = getBidiPartAt(order, head.ch, head.sticky)
|
||||
var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
|
||||
if (headIndex == boundary - 1 || headIndex == boundary)
|
||||
{ leftSide = dir < 0 }
|
||||
else
|
||||
{ leftSide = dir > 0 }
|
||||
}
|
||||
|
||||
var usePart = order[boundary + (leftSide ? -1 : 0)]
|
||||
var from = leftSide == (usePart.level == 1)
|
||||
var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
|
||||
return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
|
||||
}
|
||||
|
||||
|
||||
// Determines whether an event happened in the gutter, and fires the
|
||||
// handlers for the corresponding event.
|
||||
function gutterEvent(cm, e, type, prevent) {
|
||||
var mX, mY
|
||||
try { mX = e.clientX; mY = e.clientY }
|
||||
catch(e) { return false }
|
||||
if (e.touches) {
|
||||
mX = e.touches[0].clientX
|
||||
mY = e.touches[0].clientY
|
||||
} else {
|
||||
try { mX = e.clientX; mY = e.clientY }
|
||||
catch(e) { return false }
|
||||
}
|
||||
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
|
||||
if (prevent) { e_preventDefault(e) }
|
||||
|
||||
@ -7688,7 +7831,7 @@ function registerEventHandlers(cm) {
|
||||
return dx * dx + dy * dy > 20 * 20
|
||||
}
|
||||
on(d.scroller, "touchstart", function (e) {
|
||||
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
|
||||
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
|
||||
d.input.ensurePolled()
|
||||
clearTimeout(touchFinished)
|
||||
var now = +new Date
|
||||
@ -9472,7 +9615,7 @@ CodeMirror.fromTextArea = fromTextArea
|
||||
|
||||
addLegacyProps(CodeMirror)
|
||||
|
||||
CodeMirror.version = "5.29.0"
|
||||
CodeMirror.version = "5.30.0"
|
||||
|
||||
return CodeMirror;
|
||||
|
||||
|
@ -28,7 +28,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
|
||||
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, "throw": C, "debugger": C,
|
||||
"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"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
@ -398,7 +398,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
function expressionInner(type, noComma) {
|
||||
if (cx.state.fatArrowAt == cx.stream.start) {
|
||||
var body = noComma ? arrowBodyNoComma : arrowBody;
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
|
||||
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
|
||||
}
|
||||
|
||||
@ -443,6 +443,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == ".") return cont(property, me);
|
||||
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
|
||||
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
|
||||
if (type == "regexp") {
|
||||
cx.state.lastType = cx.marked = "operator"
|
||||
cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
|
||||
return cont(expr)
|
||||
}
|
||||
}
|
||||
function quasi(type, value) {
|
||||
if (type != "quasi") return pass();
|
||||
@ -491,6 +496,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
} else if (type == "variable" || cx.style == "keyword") {
|
||||
cx.marked = "property";
|
||||
if (value == "get" || value == "set") return cont(getterSetter);
|
||||
var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
|
||||
if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
|
||||
cx.state.fatArrowAt = cx.stream.pos + m[0].length
|
||||
return cont(afterprop);
|
||||
} else if (type == "number" || type == "string") {
|
||||
cx.marked = jsonldMode ? "property" : (cx.style + " property");
|
||||
@ -648,7 +656,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
|
||||
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
|
||||
}
|
||||
function funarg(type) {
|
||||
function funarg(type, value) {
|
||||
if (value == "@") cont(expression, funarg)
|
||||
if (type == "spread" || type == "modifier") return cont(funarg);
|
||||
return pass(pattern, maybetype, maybeAssign);
|
||||
}
|
||||
@ -674,7 +683,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
cx.marked = "keyword";
|
||||
return cont(classBody);
|
||||
}
|
||||
if (type == "variable") {
|
||||
if (type == "variable" || cx.style == "keyword") {
|
||||
cx.marked = "property";
|
||||
return cont(isTS ? classfield : functiondef, classBody);
|
||||
}
|
||||
@ -736,7 +745,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
|
||||
function expressionAllowed(stream, state, backUp) {
|
||||
return state.tokenize == tokenBase &&
|
||||
/^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
|
||||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
|
||||
}
|
||||
|
||||
|
@ -254,6 +254,11 @@ testCM("coordsCharBidi", function(cm) {
|
||||
}
|
||||
}, {lineNumbers: true});
|
||||
|
||||
testCM("badBidiOptimization", function(cm) {
|
||||
var coords = cm.charCoords(Pos(0, 34))
|
||||
eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34))
|
||||
}, {value: "----------<p class=\"title\">هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟</p>"})
|
||||
|
||||
testCM("posFromIndex", function(cm) {
|
||||
cm.setValue(
|
||||
"This function should\n" +
|
||||
@ -1156,6 +1161,16 @@ testCM("measureWrappedEndOfLine", function(cm) {
|
||||
}
|
||||
}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10);
|
||||
|
||||
testCM("measureEndOfLineBidi", function(cm) {
|
||||
eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after"))
|
||||
}, {value: "إإإإuuuuإإإإ"})
|
||||
|
||||
testCM("measureWrappedBidiLevel2", function(cm) {
|
||||
cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60)
|
||||
var c9 = cm.charCoords(Pos(0, 9))
|
||||
eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9))
|
||||
}, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true})
|
||||
|
||||
testCM("measureWrappedBeginOfLine", function(cm) {
|
||||
if (phantom) return;
|
||||
cm.setSize(null, "auto");
|
||||
@ -2468,6 +2483,23 @@ for (var i = 0; i < 5; ++i) {
|
||||
}
|
||||
*/
|
||||
|
||||
testCM("rtl_wrapped_selection", function(cm) {
|
||||
cm.setSelection(Pos(0, 10), Pos(0, 190))
|
||||
is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3)
|
||||
}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true})
|
||||
|
||||
testCM("bidi_wrapped_selection", function(cm) {
|
||||
if (phantom) return
|
||||
cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
|
||||
cm.setSelection(Pos(0, 37), Pos(0, 80))
|
||||
var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
|
||||
is(blocks.length >= 2)
|
||||
is(blocks.length <= 3)
|
||||
var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect()
|
||||
is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
|
||||
is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
|
||||
}, {value: "<p>مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت</p>", lineWrapping: true})
|
||||
|
||||
testCM("delete_wrapped", function(cm) {
|
||||
makeItWrapAfter(cm, Pos(0, 2));
|
||||
cm.doc.setCursor(Pos(0, 3, "after"));
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/XBLChildrenElement.h"
|
||||
#include "mozilla/dom/HTMLContentElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsIFrame.h"
|
||||
@ -78,17 +77,6 @@ ExplicitChildIterator::GetNextChild()
|
||||
}
|
||||
mIndexInInserted = 0;
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (mShadowIterator) {
|
||||
// If we're inside of a <shadow> element, look through the
|
||||
// explicit children of the projected ShadowRoot via
|
||||
// the mShadowIterator.
|
||||
nsIContent* nextChild = mShadowIterator->GetNextChild();
|
||||
if (nextChild) {
|
||||
return nextChild;
|
||||
}
|
||||
|
||||
mShadowIterator = nullptr;
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (mDefaultChild) {
|
||||
// If we're already in default content, check if there are more nodes there
|
||||
MOZ_ASSERT(mChild);
|
||||
@ -110,23 +98,7 @@ ExplicitChildIterator::GetNextChild()
|
||||
// Iterate until we find a non-insertion point, or an insertion point with
|
||||
// content.
|
||||
while (mChild) {
|
||||
// If the current child being iterated is a shadow insertion point then
|
||||
// the iterator needs to go into the projected ShadowRoot.
|
||||
if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
|
||||
// Look for the next child in the projected ShadowRoot for the <shadow>
|
||||
// element.
|
||||
HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
|
||||
ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
mShadowIterator = new ExplicitChildIterator(projectedShadow);
|
||||
nsIContent* nextChild = mShadowIterator->GetNextChild();
|
||||
if (nextChild) {
|
||||
return nextChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
}
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a content insertion point
|
||||
// then the iterator needs to return the nodes distributed into
|
||||
// the content insertion point.
|
||||
@ -196,11 +168,9 @@ ExplicitChildIterator::Seek(nsIContent* aChildToFind)
|
||||
!aChildToFind->IsRootOfAnonymousSubtree()) {
|
||||
// Fast path: just point ourselves to aChildToFind, which is a
|
||||
// normal DOM child of ours.
|
||||
MOZ_ASSERT(!ShadowRoot::IsShadowInsertionPoint(aChildToFind));
|
||||
MOZ_ASSERT(!nsContentUtils::IsContentInsertionPoint(aChildToFind));
|
||||
mChild = aChildToFind;
|
||||
mIndexInInserted = 0;
|
||||
mShadowIterator = nullptr;
|
||||
mDefaultChild = nullptr;
|
||||
mIsFirst = false;
|
||||
return true;
|
||||
@ -221,9 +191,8 @@ ExplicitChildIterator::Get() const
|
||||
if (mIndexInInserted) {
|
||||
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
|
||||
return assignedChildren[mIndexInInserted - 1];
|
||||
} else if (mShadowIterator) {
|
||||
return mShadowIterator->Get();
|
||||
}
|
||||
|
||||
return mDefaultChild ? mDefaultChild : mChild;
|
||||
}
|
||||
|
||||
@ -239,13 +208,6 @@ ExplicitChildIterator::GetPreviousChild()
|
||||
return assignedChildren[mIndexInInserted - 1];
|
||||
}
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (mShadowIterator) {
|
||||
nsIContent* previousChild = mShadowIterator->GetPreviousChild();
|
||||
if (previousChild) {
|
||||
return previousChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (mDefaultChild) {
|
||||
// If we're already in default content, check if there are more nodes there
|
||||
mDefaultChild = mDefaultChild->GetPreviousSibling();
|
||||
@ -265,22 +227,7 @@ ExplicitChildIterator::GetPreviousChild()
|
||||
// Iterate until we find a non-insertion point, or an insertion point with
|
||||
// content.
|
||||
while (mChild) {
|
||||
if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a shadow insertion point then
|
||||
// the iterator needs to go into the projected ShadowRoot.
|
||||
HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
|
||||
ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
// Create a ExplicitChildIterator that begins iterating from the end.
|
||||
mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
|
||||
nsIContent* previousChild = mShadowIterator->GetPreviousChild();
|
||||
if (previousChild) {
|
||||
return previousChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
}
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a content insertion point
|
||||
// then the iterator needs to return the nodes distributed into
|
||||
// the content insertion point.
|
||||
|
@ -48,16 +48,12 @@ public:
|
||||
ExplicitChildIterator(const ExplicitChildIterator& aOther)
|
||||
: mParent(aOther.mParent), mChild(aOther.mChild),
|
||||
mDefaultChild(aOther.mDefaultChild),
|
||||
mShadowIterator(aOther.mShadowIterator ?
|
||||
new ExplicitChildIterator(*aOther.mShadowIterator) :
|
||||
nullptr),
|
||||
mIsFirst(aOther.mIsFirst),
|
||||
mIndexInInserted(aOther.mIndexInInserted) {}
|
||||
|
||||
ExplicitChildIterator(ExplicitChildIterator&& aOther)
|
||||
: mParent(aOther.mParent), mChild(aOther.mChild),
|
||||
mDefaultChild(aOther.mDefaultChild),
|
||||
mShadowIterator(Move(aOther.mShadowIterator)),
|
||||
mIsFirst(aOther.mIsFirst),
|
||||
mIndexInInserted(aOther.mIndexInInserted) {}
|
||||
|
||||
@ -116,11 +112,6 @@ protected:
|
||||
// to null, we continue iterating at mChild's next sibling.
|
||||
nsIContent* mDefaultChild;
|
||||
|
||||
// If non-null, this points to an iterator of the explicit children of
|
||||
// the ShadowRoot projected by the current shadow element that we're
|
||||
// iterating.
|
||||
nsAutoPtr<ExplicitChildIterator> mShadowIterator;
|
||||
|
||||
// A flag to let us know that we haven't started iterating yet.
|
||||
bool mIsFirst;
|
||||
|
||||
|
@ -939,7 +939,7 @@ DoUpgrade(Element* aElement,
|
||||
} // anonymous namespace
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#upgrades
|
||||
void
|
||||
/* static */ void
|
||||
CustomElementRegistry::Upgrade(Element* aElement,
|
||||
CustomElementDefinition* aDefinition,
|
||||
ErrorResult& aRv)
|
||||
@ -977,8 +977,10 @@ CustomElementRegistry::Upgrade(Element* aElement,
|
||||
(attrValue.IsEmpty() ? VoidString() : attrValue),
|
||||
(namespaceURI.IsEmpty() ? VoidString() : namespaceURI)
|
||||
};
|
||||
EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, aElement,
|
||||
&args, aDefinition);
|
||||
nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
|
||||
nsIDocument::eAttributeChanged,
|
||||
aElement,
|
||||
&args, aDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1003,7 +1005,9 @@ CustomElementRegistry::Upgrade(Element* aElement,
|
||||
data->mState = CustomElementData::State::eCustom;
|
||||
|
||||
// This is for old spec.
|
||||
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, aDefinition);
|
||||
nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
|
||||
nsIDocument::eCreated,
|
||||
aElement, nullptr, aDefinition);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
|
@ -362,11 +362,15 @@ public:
|
||||
void GetCustomPrototype(nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> aPrototype);
|
||||
|
||||
void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
CustomElementDefinition* aDefinition);
|
||||
|
||||
/**
|
||||
* Upgrade an element.
|
||||
* https://html.spec.whatwg.org/multipage/scripting.html#upgrades
|
||||
*/
|
||||
void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
|
||||
static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
~CustomElementRegistry();
|
||||
@ -375,9 +379,6 @@ private:
|
||||
nsIDocument::ElementCallbackType aType, Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition);
|
||||
|
||||
void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
CustomElementDefinition* aDefinition);
|
||||
/**
|
||||
* Registers an unresolved custom element that is a candidate for
|
||||
* upgrade when the definition is registered via registerElement.
|
||||
|
@ -111,9 +111,7 @@ DocumentFragment::Constructor(const GlobalObject& aGlobal,
|
||||
return window->GetDoc()->CreateDocumentFragment();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DocumentFragment,
|
||||
FragmentOrElement,
|
||||
mHost)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DocumentFragment, FragmentOrElement, mHost)
|
||||
|
||||
// QueryInterface implementation for DocumentFragment
|
||||
NS_INTERFACE_MAP_BEGIN(DocumentFragment)
|
||||
|
@ -43,8 +43,7 @@ public:
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentFragment,
|
||||
FragmentOrElement)
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentFragment, FragmentOrElement)
|
||||
|
||||
// interface nsIDOMNode
|
||||
NS_FORWARD_NSIDOMNODE_TO_NSINODE
|
||||
@ -122,15 +121,9 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* GetHost() const
|
||||
{
|
||||
return mHost;
|
||||
}
|
||||
Element* GetHost() const { return mHost; }
|
||||
|
||||
void SetHost(Element* aHost)
|
||||
{
|
||||
mHost = aHost;
|
||||
}
|
||||
void SetHost(Element* aHost) { mHost = aHost; }
|
||||
|
||||
static already_AddRefed<DocumentFragment>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
@ -1106,6 +1106,11 @@ Element::RemoveFromIdTable()
|
||||
already_AddRefed<ShadowRoot>
|
||||
Element::CreateShadowRoot(ErrorResult& aError)
|
||||
{
|
||||
if (GetShadowRoot()) {
|
||||
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
|
||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo;
|
||||
@ -1142,24 +1147,7 @@ Element::CreateShadowRoot(ErrorResult& aError)
|
||||
|
||||
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
|
||||
|
||||
// Replace the old ShadowRoot with the new one and let the old
|
||||
// ShadowRoot know about the younger ShadowRoot because the old
|
||||
// ShadowRoot is projected into the younger ShadowRoot's shadow
|
||||
// insertion point (if it exists).
|
||||
ShadowRoot* olderShadow = GetShadowRoot();
|
||||
SetShadowRoot(shadowRoot);
|
||||
if (olderShadow) {
|
||||
olderShadow->SetYoungerShadow(shadowRoot);
|
||||
|
||||
// Unbind children of older shadow root because they
|
||||
// are no longer in the composed tree.
|
||||
for (nsIContent* child = olderShadow->GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
child->UnbindFromTree(true, false);
|
||||
}
|
||||
|
||||
olderShadow->SetIsComposedDocParticipant(false);
|
||||
}
|
||||
|
||||
// xblBinding takes ownership of docInfo.
|
||||
RefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
|
||||
|
@ -1047,23 +1047,10 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
// for destination insertion points where nodes have been distributed.
|
||||
nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints();
|
||||
if (destPoints && !destPoints->IsEmpty()) {
|
||||
// Push destination insertion points to aVisitor.mDestInsertionPoints
|
||||
// excluding shadow insertion points.
|
||||
bool didPushNonShadowInsertionPoint = false;
|
||||
// Push destination insertion points to aVisitor.mDestInsertionPoints.
|
||||
for (uint32_t i = 0; i < destPoints->Length(); i++) {
|
||||
nsIContent* point = destPoints->ElementAt(i);
|
||||
if (!ShadowRoot::IsShadowInsertionPoint(point)) {
|
||||
aVisitor.mDestInsertionPoints.AppendElement(point);
|
||||
didPushNonShadowInsertionPoint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Next node in the event path is the final destination
|
||||
// (non-shadow) insertion point that was pushed.
|
||||
if (didPushNonShadowInsertionPoint) {
|
||||
parent = aVisitor.mDestInsertionPoints.LastElement();
|
||||
aVisitor.mDestInsertionPoints.SetLength(
|
||||
aVisitor.mDestInsertionPoints.Length() - 1);
|
||||
aVisitor.mDestInsertionPoints.AppendElement(point);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1087,10 +1074,7 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
aVisitor.mDestInsertionPoints.SetLength(
|
||||
aVisitor.mDestInsertionPoints.Length() - 1);
|
||||
} else {
|
||||
// The pool host for the youngest shadow root is shadow DOM host,
|
||||
// for older shadow roots, it is the shadow insertion point
|
||||
// where the shadow root is projected, nullptr if none exists.
|
||||
parent = thisShadowRoot->GetPoolHost();
|
||||
parent = thisShadowRoot->GetHost();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2619,8 +2603,7 @@ FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)
|
||||
NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
|
||||
"on a non-Element is useless");
|
||||
ShadowRoot* shadowRoot = GetShadowRoot();
|
||||
while (shadowRoot) {
|
||||
if (shadowRoot) {
|
||||
shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope);
|
||||
shadowRoot = shadowRoot->GetOlderShadowRoot();
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLContentElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "mozilla/StyleSheetInlines.h"
|
||||
@ -27,10 +26,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
|
||||
DocumentFragment)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPoolHost)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOlderShadow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mYoungerShadow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding)
|
||||
for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
|
||||
iter.Next()) {
|
||||
@ -38,18 +34,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
|
||||
DocumentFragment)
|
||||
if (tmp->mPoolHost) {
|
||||
tmp->mPoolHost->RemoveMutationObserver(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
|
||||
if (tmp->GetHost()) {
|
||||
tmp->GetHost()->RemoveMutationObserver(tmp);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPoolHost)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOlderShadow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mYoungerShadow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding)
|
||||
tmp->mIdentifierMap.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||
@ -62,9 +54,10 @@ NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
|
||||
ShadowRoot::ShadowRoot(Element* aElement,
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
nsXBLPrototypeBinding* aProtoBinding)
|
||||
: DocumentFragment(aNodeInfo), mPoolHost(aElement),
|
||||
mProtoBinding(aProtoBinding), mShadowElement(nullptr),
|
||||
mInsertionPointChanged(false), mIsComposedDocParticipant(false)
|
||||
: DocumentFragment(aNodeInfo)
|
||||
, mProtoBinding(aProtoBinding)
|
||||
, mInsertionPointChanged(false)
|
||||
, mIsComposedDocParticipant(false)
|
||||
{
|
||||
SetHost(aElement);
|
||||
|
||||
@ -81,23 +74,21 @@ ShadowRoot::ShadowRoot(Element* aElement,
|
||||
// Add the ShadowRoot as a mutation observer on the host to watch
|
||||
// for mutations because the insertion points in this ShadowRoot
|
||||
// may need to be updated when the host children are modified.
|
||||
mPoolHost->AddMutationObserver(this);
|
||||
GetHost()->AddMutationObserver(this);
|
||||
}
|
||||
|
||||
ShadowRoot::~ShadowRoot()
|
||||
{
|
||||
if (mPoolHost) {
|
||||
if (GetHost()) {
|
||||
// mPoolHost may have been unlinked or a new ShadowRoot may have been
|
||||
// creating, making this one obsolete.
|
||||
mPoolHost->RemoveMutationObserver(this);
|
||||
GetHost()->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
|
||||
// nsINode destructor expects mSubtreeRoot == this.
|
||||
SetSubtreeRootPointer(this);
|
||||
|
||||
SetHost(nullptr);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -243,15 +234,6 @@ ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
|
||||
mInsertionPoints.RemoveElement(aInsertionPoint);
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow)
|
||||
{
|
||||
mYoungerShadow = aYoungerShadow;
|
||||
mYoungerShadow->mOlderShadow = this;
|
||||
|
||||
ChangePoolHost(mYoungerShadow->GetShadowElement());
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
|
||||
nsTArray<nsIContent*>& aDestInsertionPoints)
|
||||
@ -300,8 +282,7 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||
// Find the appropriate position in the matched node list for the
|
||||
// newly distributed content.
|
||||
bool isIndexFound = false;
|
||||
MOZ_ASSERT(mPoolHost, "Where did the content come from if there is no pool host?");
|
||||
ExplicitChildIterator childIterator(mPoolHost);
|
||||
ExplicitChildIterator childIterator(GetHost());
|
||||
for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
|
||||
// Seek through the host's explicit children until the inserted content
|
||||
// is found or when the current matched node is reached.
|
||||
@ -321,15 +302,6 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||
insertionPoint->AppendMatchedNode(aContent);
|
||||
}
|
||||
|
||||
// Handle the case where the parent of the insertion point is a ShadowRoot
|
||||
// that is projected into the younger ShadowRoot's shadow insertion point.
|
||||
// The node distributed into the insertion point must be reprojected
|
||||
// to the shadow insertion point.
|
||||
if (insertionPoint->GetParent() == this &&
|
||||
mYoungerShadow && mYoungerShadow->GetShadowElement()) {
|
||||
mYoungerShadow->GetShadowElement()->DistributeSingleNode(aContent);
|
||||
}
|
||||
|
||||
// Handle the case where the parent of the insertion point has a ShadowRoot.
|
||||
// The node distributed into the insertion point must be reprojected to the
|
||||
// insertion points of the parent's ShadowRoot.
|
||||
@ -337,16 +309,6 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||
if (parentShadow) {
|
||||
parentShadow->DistributeSingleNode(aContent);
|
||||
}
|
||||
|
||||
// Handle the case where the parent of the insertion point is the <shadow>
|
||||
// element. The node distributed into the insertion point must be reprojected
|
||||
// into the older ShadowRoot's insertion points.
|
||||
if (mShadowElement && mShadowElement == insertionPoint->GetParent()) {
|
||||
ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot();
|
||||
if (olderShadow) {
|
||||
olderShadow->DistributeSingleNode(aContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,15 +330,6 @@ ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
|
||||
|
||||
mInsertionPoints[i]->RemoveMatchedNode(aContent);
|
||||
|
||||
// Handle the case where the parent of the insertion point is a ShadowRoot
|
||||
// that is projected into the younger ShadowRoot's shadow insertion point.
|
||||
// The removed node needs to be removed from the shadow insertion point.
|
||||
if (mInsertionPoints[i]->GetParent() == this) {
|
||||
if (mYoungerShadow && mYoungerShadow->GetShadowElement()) {
|
||||
mYoungerShadow->GetShadowElement()->RemoveDistributedNode(aContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case where the parent of the insertion point has a ShadowRoot.
|
||||
// The removed node needs to be removed from the insertion points of the
|
||||
// parent's ShadowRoot.
|
||||
@ -385,16 +338,6 @@ ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
|
||||
parentShadow->RemoveDistributedNode(aContent);
|
||||
}
|
||||
|
||||
// Handle the case where the parent of the insertion point is the <shadow>
|
||||
// element. The removed node must be removed from the older ShadowRoot's
|
||||
// insertion points.
|
||||
if (mShadowElement && mShadowElement == mInsertionPoints[i]->GetParent()) {
|
||||
ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot();
|
||||
if (olderShadow) {
|
||||
olderShadow->RemoveDistributedNode(aContent);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -405,16 +348,10 @@ ShadowRoot::DistributeAllNodes()
|
||||
{
|
||||
// Create node pool.
|
||||
nsTArray<nsIContent*> nodePool;
|
||||
|
||||
// Make sure there is a pool host, an older shadow may not have
|
||||
// one if the younger shadow does not have a <shadow> element.
|
||||
if (mPoolHost) {
|
||||
ExplicitChildIterator childIterator(mPoolHost);
|
||||
for (nsIContent* content = childIterator.GetNextChild();
|
||||
content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
nodePool.AppendElement(content);
|
||||
}
|
||||
ExplicitChildIterator childIterator(GetHost());
|
||||
for (nsIContent* content = childIterator.GetNextChild(); content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
nodePool.AppendElement(content);
|
||||
}
|
||||
|
||||
nsTArray<ShadowRoot*> shadowsToUpdate;
|
||||
@ -445,20 +382,6 @@ ShadowRoot::DistributeAllNodes()
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a shadow insertion point in this ShadowRoot, the children
|
||||
// of the shadow insertion point needs to be distributed into the insertion
|
||||
// points of the older ShadowRoot.
|
||||
if (mShadowElement && mOlderShadow) {
|
||||
mOlderShadow->DistributeAllNodes();
|
||||
}
|
||||
|
||||
// If there is a younger ShadowRoot with a shadow insertion point,
|
||||
// then the children of this ShadowRoot needs to be distributed to
|
||||
// the younger ShadowRoot's shadow insertion point.
|
||||
if (mYoungerShadow && mYoungerShadow->GetShadowElement()) {
|
||||
mYoungerShadow->GetShadowElement()->DistributeAllNodes();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) {
|
||||
shadowsToUpdate[i]->DistributeAllNodes();
|
||||
}
|
||||
@ -515,59 +438,6 @@ ShadowRoot::StyleSheets()
|
||||
return mStyleSheetList;
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::SetShadowElement(HTMLShadowElement* aShadowElement)
|
||||
{
|
||||
// If there is already a shadow element point, remove
|
||||
// the projected shadow because it is no longer an insertion
|
||||
// point.
|
||||
if (mShadowElement) {
|
||||
mShadowElement->SetProjectedShadow(nullptr);
|
||||
}
|
||||
|
||||
if (mOlderShadow) {
|
||||
// Nodes for distribution will come from the new shadow element.
|
||||
mOlderShadow->ChangePoolHost(aShadowElement);
|
||||
}
|
||||
|
||||
// Set the new shadow element to project the older ShadowRoot because
|
||||
// it is the current shadow insertion point.
|
||||
mShadowElement = aShadowElement;
|
||||
if (mShadowElement) {
|
||||
mShadowElement->SetProjectedShadow(mOlderShadow);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::ChangePoolHost(nsIContent* aNewHost)
|
||||
{
|
||||
if (mPoolHost) {
|
||||
mPoolHost->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
// Clear the nodes matched to content insertion points
|
||||
// because it is no longer relevant.
|
||||
for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
|
||||
mInsertionPoints[i]->ClearMatchedNodes();
|
||||
}
|
||||
|
||||
mPoolHost = aNewHost;
|
||||
if (mPoolHost) {
|
||||
mPoolHost->AddMutationObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ShadowRoot::IsShadowInsertionPoint(nsIContent* aContent)
|
||||
{
|
||||
if (!aContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(aContent);
|
||||
return shadowElem && shadowElem->IsInsertionPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the web components pool population algorithm
|
||||
* on the host would contain |aContent|. This function ignores
|
||||
@ -578,8 +448,7 @@ bool
|
||||
ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
|
||||
nsIContent* aHost)
|
||||
{
|
||||
if (nsContentUtils::IsContentInsertionPoint(aContent) ||
|
||||
IsShadowInsertionPoint(aContent)) {
|
||||
if (nsContentUtils::IsContentInsertionPoint(aContent)) {
|
||||
// Insertion points never end up in the pool.
|
||||
return false;
|
||||
}
|
||||
@ -612,7 +481,7 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
|
||||
int32_t aModType,
|
||||
const nsAttrValue* aOldValue)
|
||||
{
|
||||
if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) {
|
||||
if (!IsPooledNode(aElement, aElement->GetParent(), GetHost())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -645,7 +514,7 @@ ShadowRoot::ContentAppended(nsIDocument* aDocument,
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
|
||||
if (IsPooledNode(currentChild, aContainer, GetHost())) {
|
||||
DistributeSingleNode(currentChild);
|
||||
}
|
||||
|
||||
@ -667,7 +536,7 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
|
||||
|
||||
// Watch for new nodes added to the pool because the node
|
||||
// may need to be added to an insertion point.
|
||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||
if (IsPooledNode(aChild, aContainer, GetHost())) {
|
||||
// Add insertion point to destination insertion points of fallback content.
|
||||
if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
|
||||
HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
|
||||
@ -704,7 +573,7 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
|
||||
|
||||
// Watch for node that is removed from the pool because
|
||||
// it may need to be removed from an insertion point.
|
||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||
if (IsPooledNode(aChild, aContainer, GetHost())) {
|
||||
RemoveDistributedNode(aChild);
|
||||
}
|
||||
}
|
||||
@ -717,15 +586,6 @@ ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::DestroyContent()
|
||||
{
|
||||
if (mOlderShadow) {
|
||||
mOlderShadow->DestroyContent();
|
||||
}
|
||||
DocumentFragment::DestroyContent();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList, StyleSheetList,
|
||||
mShadowRoot)
|
||||
|
||||
|
@ -25,7 +25,6 @@ namespace dom {
|
||||
|
||||
class Element;
|
||||
class HTMLContentElement;
|
||||
class HTMLShadowElement;
|
||||
class ShadowRootStyleSheetList;
|
||||
|
||||
class ShadowRoot final : public DocumentFragment,
|
||||
@ -42,7 +41,8 @@ public:
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
|
||||
ShadowRoot(Element* aElement, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
ShadowRoot(Element* aElement,
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
nsXBLPrototypeBinding* aProtoBinding);
|
||||
|
||||
void AddToIdTable(Element* aElement, nsIAtom* aId);
|
||||
@ -52,24 +52,6 @@ public:
|
||||
bool ApplyAuthorStyles();
|
||||
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
|
||||
StyleSheetList* StyleSheets();
|
||||
HTMLShadowElement* GetShadowElement() { return mShadowElement; }
|
||||
|
||||
/**
|
||||
* Sets the current shadow insertion point where the older
|
||||
* ShadowRoot will be projected.
|
||||
*/
|
||||
void SetShadowElement(HTMLShadowElement* aShadowElement);
|
||||
|
||||
/**
|
||||
* Change the node that populates the distribution pool with
|
||||
* its children. This is distinct from the ShadowRoot host described
|
||||
* in the specifications. The ShadowRoot host is the element
|
||||
* which created this ShadowRoot and does not change. The pool host
|
||||
* is the same as the ShadowRoot host if this is the youngest
|
||||
* ShadowRoot. If this is an older ShadowRoot, the pool host is
|
||||
* the <shadow> element in the younger ShadowRoot (if it exists).
|
||||
*/
|
||||
void ChangePoolHost(nsIContent* aNewHost);
|
||||
|
||||
/**
|
||||
* Distributes a single explicit child of the pool host to the content
|
||||
@ -92,23 +74,15 @@ public:
|
||||
void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
|
||||
void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
|
||||
|
||||
void SetYoungerShadow(ShadowRoot* aYoungerShadow);
|
||||
ShadowRoot* GetYoungerShadowRoot() { return mYoungerShadow; }
|
||||
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
|
||||
|
||||
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
|
||||
|
||||
nsISupports* GetParentObject() const { return mPoolHost; }
|
||||
|
||||
nsIContent* GetPoolHost() { return mPoolHost; }
|
||||
nsTArray<HTMLShadowElement*>& ShadowDescendants() { return mShadowDescendants; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static bool IsPooledNode(nsIContent* aChild, nsIContent* aContainer,
|
||||
nsIContent* aHost);
|
||||
static ShadowRoot* FromNode(nsINode* aNode);
|
||||
static bool IsShadowInsertionPoint(nsIContent* aContent);
|
||||
|
||||
static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
|
||||
nsTArray<nsIContent*>& aDestInsertionPoints);
|
||||
@ -125,7 +99,6 @@ public:
|
||||
void GetInnerHTML(nsAString& aInnerHTML);
|
||||
void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
|
||||
Element* Host();
|
||||
ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; }
|
||||
void StyleSheetChanged();
|
||||
|
||||
bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
|
||||
@ -134,14 +107,9 @@ public:
|
||||
mIsComposedDocParticipant = aIsComposedDocParticipant;
|
||||
}
|
||||
|
||||
virtual void DestroyContent() override;
|
||||
protected:
|
||||
virtual ~ShadowRoot();
|
||||
|
||||
// The pool host is the parent of the nodes that will be distributed
|
||||
// into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
|
||||
nsCOMPtr<nsIContent> mPoolHost;
|
||||
|
||||
// An array of content insertion points that are a descendant of the ShadowRoot
|
||||
// sorted in tree order. Insertion points are responsible for notifying
|
||||
// the ShadowRoot when they are removed or added as a descendant. The insertion
|
||||
@ -149,10 +117,6 @@ protected:
|
||||
// by the array.
|
||||
nsTArray<HTMLContentElement*> mInsertionPoints;
|
||||
|
||||
// An array of the <shadow> elements that are descendant of the ShadowRoot
|
||||
// sorted in tree order. Only the first may be a shadow insertion point.
|
||||
nsTArray<HTMLShadowElement*> mShadowDescendants;
|
||||
|
||||
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
|
||||
nsXBLPrototypeBinding* mProtoBinding;
|
||||
|
||||
@ -163,17 +127,6 @@ protected:
|
||||
|
||||
RefPtr<ShadowRootStyleSheetList> mStyleSheetList;
|
||||
|
||||
// The current shadow insertion point of this ShadowRoot.
|
||||
HTMLShadowElement* mShadowElement;
|
||||
|
||||
// The ShadowRoot that was created by the host element before
|
||||
// this ShadowRoot was created.
|
||||
RefPtr<ShadowRoot> mOlderShadow;
|
||||
|
||||
// The ShadowRoot that was created by the host element after
|
||||
// this ShadowRoot was created.
|
||||
RefPtr<ShadowRoot> mYoungerShadow;
|
||||
|
||||
// A boolean that indicates that an insertion point was added or removed
|
||||
// from this ShadowRoot and that the nodes need to be redistributed into
|
||||
// the insertion points. After this flag is set, nodes will be distributed
|
||||
|
@ -185,6 +185,7 @@ EXPORTS.mozilla.dom += [
|
||||
'IdleRequest.h',
|
||||
'ImageEncoder.h',
|
||||
'ImageTracker.h',
|
||||
'IntlUtils.h',
|
||||
'Link.h',
|
||||
'Location.h',
|
||||
'NameSpaceConstants.h',
|
||||
@ -258,6 +259,7 @@ UNIFIED_SOURCES += [
|
||||
'IdleRequest.cpp',
|
||||
'ImageEncoder.cpp',
|
||||
'ImageTracker.cpp',
|
||||
'IntlUtils.cpp',
|
||||
'Link.cpp',
|
||||
'Location.cpp',
|
||||
'Navigator.cpp',
|
||||
@ -375,14 +377,6 @@ if CONFIG['FUZZING']:
|
||||
'FuzzingFunctions.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
UNIFIED_SOURCES += [
|
||||
'IntlUtils.cpp',
|
||||
]
|
||||
EXPORTS.mozilla.dom += [
|
||||
'IntlUtils.h',
|
||||
]
|
||||
|
||||
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
|
||||
SOURCES += [
|
||||
# Several conflicts with other bindings.
|
||||
|
@ -50,7 +50,6 @@
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/HTMLTemplateElement.h"
|
||||
#include "mozilla/dom/HTMLContentElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/IPCBlobUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
@ -7567,20 +7566,6 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent)
|
||||
return true;
|
||||
}
|
||||
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(aContent);
|
||||
if (shadow) {
|
||||
// Children of a shadow root are distributed to
|
||||
// the shadow insertion point of the younger shadow root.
|
||||
return shadow->GetYoungerShadowRoot();
|
||||
}
|
||||
|
||||
HTMLShadowElement* shadowEl = HTMLShadowElement::FromContent(aContent);
|
||||
if (shadowEl && shadowEl->IsInsertionPoint()) {
|
||||
// Children of a shadow insertion points are distributed
|
||||
// to the insertion points in the older shadow root.
|
||||
return shadowEl->GetOlderShadowRoot();
|
||||
}
|
||||
|
||||
HTMLContentElement* contentEl = HTMLContentElement::FromContent(aContent);
|
||||
if (contentEl && contentEl->IsInsertionPoint()) {
|
||||
// Children of a content insertion point are distributed to the
|
||||
@ -10154,6 +10139,49 @@ nsContentUtils::GetElementDefinitionIfObservingAttr(Element* aCustomElement,
|
||||
return definition;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
|
||||
Element* aElement,
|
||||
CustomElementDefinition* aDefinition)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
|
||||
nsIDocument* doc = aElement->OwnerDoc();
|
||||
nsPIDOMWindowInner* window(doc->GetInnerWindow());
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
|
||||
registry->SyncInvokeReactions(aType, aElement, aDefinition);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::EnqueueUpgradeReaction(Element* aElement,
|
||||
CustomElementDefinition* aDefinition)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
|
||||
nsIDocument* doc = aElement->OwnerDoc();
|
||||
nsPIDOMWindowInner* window(doc->GetInnerWindow());
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
|
||||
CustomElementReactionsStack* stack =
|
||||
doc->GetDocGroup()->CustomElementReactionsStack();
|
||||
stack->EnqueueUpgradeReaction(registry, aElement, aDefinition);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc,
|
||||
nsIDocument::ElementCallbackType aType,
|
||||
|
@ -2998,6 +2998,13 @@ public:
|
||||
nsIAtom* aExtensionType,
|
||||
nsIAtom* aAttrName);
|
||||
|
||||
static void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition);
|
||||
|
||||
static void EnqueueUpgradeReaction(Element* aElement,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition);
|
||||
|
||||
static void EnqueueLifecycleCallback(nsIDocument* aDoc,
|
||||
nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
|
@ -279,6 +279,7 @@
|
||||
#include "mozilla/DocumentStyleRootIterator.h"
|
||||
#include "mozilla/ServoRestyleManager.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsHTMLTags.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -6024,6 +6025,10 @@ nsDocument::CreateElement(const nsAString& aTagName,
|
||||
elem->SetPseudoElementType(pseudoType);
|
||||
}
|
||||
|
||||
if (is) {
|
||||
elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
|
||||
}
|
||||
|
||||
return elem.forget();
|
||||
}
|
||||
|
||||
@ -6075,6 +6080,10 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (is) {
|
||||
element->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
|
||||
}
|
||||
|
||||
return element.forget();
|
||||
}
|
||||
|
||||
@ -6357,11 +6366,26 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
nsDependentAtomString localName(definition->mLocalName);
|
||||
element =
|
||||
document->CreateElem(localName, nullptr, kNameSpaceID_XHTML,
|
||||
(definition->mLocalName != typeAtom) ? &elemName
|
||||
: nullptr);
|
||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo =
|
||||
document->NodeInfoManager()->GetNodeInfo(definition->mLocalName, nullptr,
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
|
||||
int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
|
||||
if (tag == eHTMLTag_userdefined &&
|
||||
nsContentUtils::IsCustomElementName(definition->mType)) {
|
||||
element = NS_NewHTMLElement(nodeInfo.forget(), NOT_FROM_PARSER);
|
||||
} else {
|
||||
element = ::CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
|
||||
}
|
||||
|
||||
element->SetCustomElementData(
|
||||
new CustomElementData(definition->mType,
|
||||
CustomElementData::State::eCustom));
|
||||
|
||||
// It'll be removed when we deprecate custom elements v0.
|
||||
nsContentUtils::SyncInvokeReactions(nsIDocument::eCreated, element,
|
||||
definition);
|
||||
NS_ENSURE_TRUE(element, false);
|
||||
}
|
||||
|
||||
|
@ -1660,7 +1660,6 @@ GK_ATOM(saturate, "saturate")
|
||||
GK_ATOM(saturation, "saturation")
|
||||
GK_ATOM(set, "set")
|
||||
GK_ATOM(seed, "seed")
|
||||
GK_ATOM(shadow, "shadow")
|
||||
GK_ATOM(shape_rendering, "shape-rendering")
|
||||
GK_ATOM(skewX, "skewX")
|
||||
GK_ATOM(skewY, "skewY")
|
||||
|
@ -233,9 +233,7 @@
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "mozilla/dom/HashChangeEvent.h"
|
||||
#ifdef ENABLE_INTL_API
|
||||
#include "mozilla/dom/IntlUtils.h"
|
||||
#endif
|
||||
#include "mozilla/dom/MozSelfSupportBinding.h"
|
||||
#include "mozilla/dom/PopStateEvent.h"
|
||||
#include "mozilla/dom/PopupBlockedEvent.h"
|
||||
@ -2065,9 +2063,7 @@ nsGlobalWindow::CleanUp()
|
||||
|
||||
mServiceWorkerRegistrationTable.Clear();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
mIntlUtils = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -2356,9 +2352,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
|
||||
#ifdef ENABLE_INTL_API
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
|
||||
#endif
|
||||
|
||||
tmp->TraverseHostObjectURIs(cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
@ -2436,9 +2430,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
|
||||
#ifdef ENABLE_INTL_API
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
|
||||
#endif
|
||||
|
||||
tmp->UnlinkHostObjectURIs();
|
||||
|
||||
@ -15091,7 +15083,6 @@ nsGlobalWindow::GetRegionalPrefsLocales(nsTArray<nsString>& aLocales)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
IntlUtils*
|
||||
nsGlobalWindow::GetIntlUtils(ErrorResult& aError)
|
||||
{
|
||||
@ -15103,7 +15094,6 @@ nsGlobalWindow::GetIntlUtils(ErrorResult& aError)
|
||||
|
||||
return mIntlUtils;
|
||||
}
|
||||
#endif
|
||||
|
||||
template class nsPIDOMWindow<mozIDOMWindowProxy>;
|
||||
template class nsPIDOMWindow<mozIDOMWindow>;
|
||||
|
@ -123,9 +123,7 @@ enum class ImageBitmapFormat : uint8_t;
|
||||
class IdleRequest;
|
||||
class IdleRequestCallback;
|
||||
class IncrementalRunnable;
|
||||
#ifdef ENABLE_INTL_API
|
||||
class IntlUtils;
|
||||
#endif
|
||||
class Location;
|
||||
class MediaQueryList;
|
||||
class MozSelfSupport;
|
||||
@ -959,10 +957,8 @@ public:
|
||||
void
|
||||
GetRegionalPrefsLocales(nsTArray<nsString>& aLocales);
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
mozilla::dom::IntlUtils*
|
||||
GetIntlUtils(mozilla::ErrorResult& aRv);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
|
||||
@ -2027,9 +2023,7 @@ protected:
|
||||
uint32_t mAutoActivateVRDisplayID; // Outer windows only
|
||||
int64_t mBeforeUnloadListenerCount; // Inner windows only
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
RefPtr<mozilla::dom::IntlUtils> mIntlUtils;
|
||||
#endif
|
||||
|
||||
friend class nsDOMScriptableHelper;
|
||||
friend class nsDOMWindowUtils;
|
||||
|
@ -66,7 +66,7 @@ using mozilla::AutoJSContext;
|
||||
} \
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(node); \
|
||||
if (shadow) { \
|
||||
node = shadow->GetPoolHost(); \
|
||||
node = shadow->GetHost(); \
|
||||
} else { \
|
||||
node = node->GetParentNode(); \
|
||||
} \
|
||||
@ -94,7 +94,7 @@ using mozilla::AutoJSContext;
|
||||
} \
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(node); \
|
||||
if (shadow) { \
|
||||
node = shadow->GetPoolHost(); \
|
||||
node = shadow->GetHost(); \
|
||||
} else { \
|
||||
node = node->GetParentNode(); \
|
||||
} \
|
||||
|
@ -587,7 +587,7 @@ function testOutsideShadowDOM() {
|
||||
is(records.length, 1);
|
||||
is(records[0].type, "attributes", "Should have got attributes");
|
||||
observer.disconnect();
|
||||
then(testInsideShadowDOM);
|
||||
then(testMarquee);
|
||||
});
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
@ -603,32 +603,6 @@ function testOutsideShadowDOM() {
|
||||
div.setAttribute("foo", "bar");
|
||||
}
|
||||
|
||||
function testInsideShadowDOM() {
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 4);
|
||||
is(records[0].type, "childList");
|
||||
is(records[1].type, "attributes");
|
||||
is(records[2].type, "characterData");
|
||||
is(records[3].type, "childList");
|
||||
observer.disconnect();
|
||||
then(testMarquee);
|
||||
});
|
||||
var sr = div.createShadowRoot();
|
||||
m.observe(sr, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
sr.innerHTML = "<div" + ">text</" + "div>";
|
||||
sr.firstChild.setAttribute("foo", "bar");
|
||||
sr.firstChild.firstChild.data = "text2";
|
||||
sr.firstChild.appendChild(document.createElement("div"));
|
||||
div.setAttribute("foo", "bar2");
|
||||
|
||||
}
|
||||
|
||||
function testMarquee() {
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 1);
|
||||
|
11
dom/cache/CacheStreamControlChild.cpp
vendored
11
dom/cache/CacheStreamControlChild.cpp
vendored
@ -115,11 +115,18 @@ CacheStreamControlChild::OpenStream(const nsID& aId, InputStreamResolver&& aReso
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are on a worker, then we need to hold it alive until the async
|
||||
// IPC operation below completes. While the IPC layer will trigger a
|
||||
// rejection here in many cases, we must handle the case where the
|
||||
// MozPromise resolve runnable is already in the event queue when the
|
||||
// worker wants to shut down.
|
||||
RefPtr<CacheWorkerHolder> holder = GetWorkerHolder();
|
||||
|
||||
SendOpenStream(aId)->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[aResolver](const OptionalIPCStream& aOptionalStream) {
|
||||
[aResolver, holder](const OptionalIPCStream& aOptionalStream) {
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream);
|
||||
aResolver(Move(stream));
|
||||
}, [aResolver](PromiseRejectReason aReason) {
|
||||
}, [aResolver, holder](PromiseRejectReason aReason) {
|
||||
aResolver(nullptr);
|
||||
});
|
||||
}
|
||||
|
9
dom/canvas/crashtests/1296410-1.html
Normal file
9
dom/canvas/crashtests/1296410-1.html
Normal file
@ -0,0 +1,9 @@
|
||||
<canvas id='canv'></canvas>
|
||||
<script>
|
||||
var ctx=document.getElementById('canv').getContext('2d');
|
||||
ctx.globalAlpha=0.81;
|
||||
ctx.scale(22,3406781);
|
||||
ctx.filter='sepia(80%)';
|
||||
ctx.font='52px serif';
|
||||
ctx.measureText('A');
|
||||
</script>
|
@ -37,6 +37,7 @@ load 1288872-1.html
|
||||
load 1290628-1.html
|
||||
load 1283113-1.html
|
||||
load 1286458-1.html
|
||||
load 1296410-1.html
|
||||
load 1299062-1.html
|
||||
load 1305085-1.html
|
||||
load 1305312-1.html
|
||||
|
@ -79,7 +79,7 @@ skip-if = toolkit == 'android'
|
||||
[test_bug605242.html]
|
||||
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_bug607464.html]
|
||||
skip-if = toolkit == 'android' || (e10s && os == 'win') || (e10s && os == "mac") #CRASH_DUMP, RANDOM, bug 1252273
|
||||
skip-if = toolkit == 'android' || e10s #CRASH_DUMP, RANDOM, bug 1252273, bug 1400586
|
||||
[test_bug613634.html]
|
||||
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_bug615597.html]
|
||||
|
@ -1,373 +0,0 @@
|
||||
/* -*- 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 "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
#include "ChildIterator.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDocument.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/HTMLUnknownElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElementBinding.h"
|
||||
|
||||
// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow) to add check for web components
|
||||
// being enabled.
|
||||
nsGenericHTMLElement*
|
||||
NS_NewHTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser)
|
||||
{
|
||||
// When this check is removed, remove the nsDocument.h and
|
||||
// HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLShadowElement.
|
||||
//
|
||||
// We have to jump through some hoops to be able to produce both NodeInfo* and
|
||||
// already_AddRefed<NodeInfo>& for our callees.
|
||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
|
||||
if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
|
||||
already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
|
||||
return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
|
||||
return new mozilla::dom::HTMLShadowElement(nodeInfoArg);
|
||||
}
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
|
||||
{
|
||||
}
|
||||
|
||||
HTMLShadowElement::~HTMLShadowElement()
|
||||
{
|
||||
if (mProjectedShadow) {
|
||||
mProjectedShadow->RemoveMutationObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLShadowElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLShadowElement,
|
||||
nsGenericHTMLElement)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement,
|
||||
nsGenericHTMLElement)
|
||||
if (tmp->mProjectedShadow) {
|
||||
tmp->mProjectedShadow->RemoveMutationObserver(tmp);
|
||||
tmp->mProjectedShadow = nullptr;
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(HTMLShadowElement, Element)
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLShadowElement, Element)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLShadowElement)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE(HTMLShadowElement)
|
||||
|
||||
JSObject*
|
||||
HTMLShadowElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return HTMLShadowElementBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
|
||||
{
|
||||
if (mProjectedShadow) {
|
||||
mProjectedShadow->RemoveMutationObserver(this);
|
||||
|
||||
// The currently projected ShadowRoot is going away,
|
||||
// thus the destination insertion points need to be updated.
|
||||
ExplicitChildIterator childIterator(mProjectedShadow);
|
||||
for (nsIContent* content = childIterator.GetNextChild();
|
||||
content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
|
||||
}
|
||||
}
|
||||
|
||||
mProjectedShadow = aProjectedShadow;
|
||||
if (mProjectedShadow) {
|
||||
// A new ShadowRoot is being projected, thus its explcit
|
||||
// children will be distributed to this shadow insertion point.
|
||||
ExplicitChildIterator childIterator(mProjectedShadow);
|
||||
for (nsIContent* content = childIterator.GetNextChild();
|
||||
content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
content->DestInsertionPoints().AppendElement(this);
|
||||
}
|
||||
|
||||
// Watch for mutations on the projected shadow because
|
||||
// it affects the nodes that are distributed to this shadow
|
||||
// insertion point.
|
||||
mProjectedShadow->AddMutationObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
IsInFallbackContent(nsIContent* aContent)
|
||||
{
|
||||
nsINode* parentNode = aContent->GetParentNode();
|
||||
while (parentNode) {
|
||||
if (parentNode->IsHTMLElement(nsGkAtoms::content)) {
|
||||
return true;
|
||||
}
|
||||
parentNode = parentNode->GetParentNode();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLShadowElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
|
||||
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
if (containingShadow && !oldContainingShadow) {
|
||||
// Keep track of all descendant <shadow> elements in tree order so
|
||||
// that when the current shadow insertion point is removed, the next
|
||||
// one can be found quickly.
|
||||
TreeOrderComparator comparator;
|
||||
containingShadow->ShadowDescendants().InsertElementSorted(this, comparator);
|
||||
|
||||
if (containingShadow->ShadowDescendants()[0] != this) {
|
||||
// Only the first <shadow> (in tree order) of a ShadowRoot can be an insertion point.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (IsInFallbackContent(this)) {
|
||||
// If the first shadow element in tree order is invalid (in fallback content),
|
||||
// the containing ShadowRoot will not have a shadow insertion point.
|
||||
containingShadow->SetShadowElement(nullptr);
|
||||
} else {
|
||||
mIsInsertionPoint = true;
|
||||
containingShadow->SetShadowElement(this);
|
||||
}
|
||||
|
||||
containingShadow->SetInsertionPointChanged();
|
||||
}
|
||||
|
||||
if (mIsInsertionPoint && containingShadow) {
|
||||
// Propagate BindToTree calls to projected shadow root children.
|
||||
ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
|
||||
|
||||
for (nsIContent* child = projectedShadow->GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
rv = child->BindToTree(nullptr, projectedShadow,
|
||||
projectedShadow->GetBindingParent(),
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
|
||||
|
||||
if (mIsInsertionPoint && oldContainingShadow) {
|
||||
// Propagate UnbindFromTree call to previous projected shadow
|
||||
// root children.
|
||||
ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
for (nsIContent* child = projectedShadow->GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
child->UnbindFromTree(true, false);
|
||||
}
|
||||
|
||||
projectedShadow->SetIsComposedDocParticipant(false);
|
||||
}
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
|
||||
if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
|
||||
nsTArray<HTMLShadowElement*>& shadowDescendants =
|
||||
oldContainingShadow->ShadowDescendants();
|
||||
shadowDescendants.RemoveElement(this);
|
||||
oldContainingShadow->SetShadowElement(nullptr);
|
||||
|
||||
// Find the next shadow insertion point.
|
||||
if (shadowDescendants.Length() > 0 &&
|
||||
!IsInFallbackContent(shadowDescendants[0])) {
|
||||
oldContainingShadow->SetShadowElement(shadowDescendants[0]);
|
||||
}
|
||||
|
||||
oldContainingShadow->SetInsertionPointChanged();
|
||||
|
||||
mIsInsertionPoint = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
|
||||
{
|
||||
if (aContent->DestInsertionPoints().Contains(this)) {
|
||||
// Node has already been distrbuted this this node,
|
||||
// we are done.
|
||||
return;
|
||||
}
|
||||
|
||||
aContent->DestInsertionPoints().AppendElement(this);
|
||||
|
||||
// Handle the case where the shadow element is a child of
|
||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||
// this shadow insertion point will need to be reprojected into the
|
||||
// insertion points of the parent's ShadowRoot.
|
||||
ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
|
||||
if (parentShadowRoot) {
|
||||
parentShadowRoot->DistributeSingleNode(aContent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case where the parent of this shadow element is a ShadowRoot
|
||||
// that is projected into a shadow insertion point in the younger ShadowRoot.
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
|
||||
if (youngerShadow && GetParent() == containingShadow) {
|
||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||
if (youngerShadowElement) {
|
||||
youngerShadowElement->DistributeSingleNode(aContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
|
||||
{
|
||||
ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
|
||||
|
||||
// Handle the case where the shadow element is a child of
|
||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||
// this shadow insertion point will need to be removed from the
|
||||
// insertion points of the parent's ShadowRoot.
|
||||
ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
|
||||
if (parentShadowRoot) {
|
||||
parentShadowRoot->RemoveDistributedNode(aContent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case where the parent of this shadow element is a ShadowRoot
|
||||
// that is projected into a shadow insertion point in the younger ShadowRoot.
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
|
||||
if (youngerShadow && GetParent() == containingShadow) {
|
||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||
if (youngerShadowElement) {
|
||||
youngerShadowElement->RemoveDistributedNode(aContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::DistributeAllNodes()
|
||||
{
|
||||
// All the explicit children of the projected ShadowRoot are distributed
|
||||
// into this shadow insertion point so update the destination insertion
|
||||
// points.
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
ShadowRoot* olderShadow = containingShadow->GetOlderShadowRoot();
|
||||
if (olderShadow) {
|
||||
ExplicitChildIterator childIterator(olderShadow);
|
||||
for (nsIContent* content = childIterator.GetNextChild();
|
||||
content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
|
||||
content->DestInsertionPoints().AppendElement(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case where the shadow element is a child of
|
||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||
// this shadow insertion point will need to be reprojected into the
|
||||
// insertion points of the parent's ShadowRoot.
|
||||
ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
|
||||
if (parentShadowRoot) {
|
||||
parentShadowRoot->DistributeAllNodes();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case where the parent of this shadow element is a ShadowRoot
|
||||
// that is projected into a shadow insertion point in the younger ShadowRoot.
|
||||
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
|
||||
if (youngerShadow && GetParent() == containingShadow) {
|
||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||
if (youngerShadowElement) {
|
||||
youngerShadowElement->DistributeAllNodes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::ContentAppended(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
int32_t aNewIndexInContainer)
|
||||
{
|
||||
// Watch for content appended to the projected shadow (the ShadowRoot that
|
||||
// will be rendered in place of this shadow insertion point) because the
|
||||
// nodes may need to be distributed into other insertion points.
|
||||
nsIContent* currentChild = aFirstNewContent;
|
||||
while (currentChild) {
|
||||
if (ShadowRoot::IsPooledNode(currentChild, aContainer, mProjectedShadow)) {
|
||||
DistributeSingleNode(currentChild);
|
||||
}
|
||||
currentChild = currentChild->GetNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::ContentInserted(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
int32_t aIndexInContainer)
|
||||
{
|
||||
// Watch for content appended to the projected shadow (the ShadowRoot that
|
||||
// will be rendered in place of this shadow insertion point) because the
|
||||
// nodes may need to be distributed into other insertion points.
|
||||
if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DistributeSingleNode(aChild);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::ContentRemoved(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
int32_t aIndexInContainer,
|
||||
nsIContent* aPreviousSibling)
|
||||
{
|
||||
// Watch for content removed from the projected shadow (the ShadowRoot that
|
||||
// will be rendered in place of this shadow insertion point) because the
|
||||
// nodes may need to be removed from other insertion points.
|
||||
if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveDistributedNode(aChild);
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
/* -*- 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_HTMLShadowElement_h__
|
||||
#define mozilla_dom_HTMLShadowElement_h__
|
||||
|
||||
#include "nsGenericHTMLElement.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class HTMLShadowElement final : public nsGenericHTMLElement,
|
||||
public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
explicit HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLShadowElement,
|
||||
nsGenericHTMLElement)
|
||||
|
||||
static HTMLShadowElement* FromContent(nsIContent* aContent)
|
||||
{
|
||||
if (aContent->IsHTMLShadowElement()) {
|
||||
return static_cast<HTMLShadowElement*>(aContent);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool IsHTMLShadowElement() const override { return true; }
|
||||
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
|
||||
bool aPreallocateChildren) const override;
|
||||
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers) override;
|
||||
|
||||
virtual void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true) override;
|
||||
|
||||
bool IsInsertionPoint() { return mIsInsertionPoint; }
|
||||
|
||||
/**
|
||||
* Sets the ShadowRoot that will be rendered in place of
|
||||
* this shadow insertion point.
|
||||
*/
|
||||
void SetProjectedShadow(ShadowRoot* aProjectedShadow);
|
||||
|
||||
/**
|
||||
* Distributes a single explicit child of the projected ShadowRoot
|
||||
* to relevant insertion points.
|
||||
*/
|
||||
void DistributeSingleNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Removes a single explicit child of the projected ShadowRoot
|
||||
* from relevant insertion points.
|
||||
*/
|
||||
void RemoveDistributedNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Distributes all the explicit children of the projected ShadowRoot
|
||||
* to the shadow insertion point in the younger ShadowRoot and
|
||||
* the content insertion point of the parent node's ShadowRoot.
|
||||
*/
|
||||
void DistributeAllNodes();
|
||||
|
||||
// WebIDL methods.
|
||||
ShadowRoot* GetOlderShadowRoot() { return mProjectedShadow; }
|
||||
|
||||
protected:
|
||||
virtual ~HTMLShadowElement();
|
||||
|
||||
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// The ShadowRoot that will be rendered in place of this shadow insertion point.
|
||||
RefPtr<ShadowRoot> mProjectedShadow;
|
||||
|
||||
bool mIsInsertionPoint;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_HTMLShadowElement_h__
|
||||
|
@ -95,7 +95,6 @@ EXPORTS.mozilla.dom += [
|
||||
'HTMLProgressElement.h',
|
||||
'HTMLScriptElement.h',
|
||||
'HTMLSelectElement.h',
|
||||
'HTMLShadowElement.h',
|
||||
'HTMLSharedElement.h',
|
||||
'HTMLSharedListElement.h',
|
||||
'HTMLSourceElement.h',
|
||||
@ -175,7 +174,6 @@ UNIFIED_SOURCES += [
|
||||
'HTMLProgressElement.cpp',
|
||||
'HTMLScriptElement.cpp',
|
||||
'HTMLSelectElement.cpp',
|
||||
'HTMLShadowElement.cpp',
|
||||
'HTMLSharedElement.cpp',
|
||||
'HTMLSharedListElement.cpp',
|
||||
'HTMLSourceElement.cpp',
|
||||
|
@ -1581,7 +1581,6 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Pre)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Progress)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Script)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Select)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Shadow)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Source)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Span)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Style)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSubDocumentFrame.h"
|
||||
#include "nsXULElement.h"
|
||||
#include "nsAttrValueOrString.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsNodeUtils.h"
|
||||
#include "nsIContent.h"
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
@ -223,6 +224,26 @@ public:
|
||||
int32_t mStackPos;
|
||||
};
|
||||
|
||||
static void
|
||||
DoCustomElementCreate(Element** aElement, nsIDocument* aDoc,
|
||||
CustomElementConstructor* aConstructor, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<Element> element =
|
||||
aConstructor->Construct("Custom Element Create", aRv);
|
||||
if (aRv.Failed() || !element->IsHTMLElement()) {
|
||||
aRv.ThrowTypeError<MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE>(NS_LITERAL_STRING("HTMLElement"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
|
||||
element->HasChildren() || element->GetAttrCount()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
element.forget(aElement);
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
FromParser aFromParser, const nsAString* aIs)
|
||||
@ -238,6 +259,81 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
||||
|
||||
int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-create-element
|
||||
// We only handle the "synchronous custom elements flag is set" now.
|
||||
// For the unset case (e.g. cloning a node), see bug 1319342 for that.
|
||||
// Step 4.
|
||||
CustomElementDefinition* definition = nullptr;
|
||||
if (CustomElementRegistry::IsCustomElementEnabled()) {
|
||||
definition =
|
||||
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
|
||||
nodeInfo->LocalName(),
|
||||
nodeInfo->NamespaceID(),
|
||||
aIs);
|
||||
}
|
||||
|
||||
// It might be a problem that parser synchronously calls constructor, so filed
|
||||
// bug 1378079 to figure out what we should do for parser case.
|
||||
if (definition) {
|
||||
/*
|
||||
* Synchronous custom elements flag is determined by 3 places in spec,
|
||||
* 1) create an element for a token, the flag is determined by
|
||||
* "will execute script" which is not originally created
|
||||
* for the HTML fragment parsing algorithm.
|
||||
* 2) createElement and createElementNS, the flag is the same as
|
||||
* NOT_FROM_PARSER.
|
||||
* 3) clone a node, our implementation will not go into this function.
|
||||
* For the unset case which is non-synchronous only applied for
|
||||
* inner/outerHTML.
|
||||
*/
|
||||
bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
|
||||
aFromParser == dom::NOT_FROM_PARSER;
|
||||
// Per discussion in https://github.com/w3c/webcomponents/issues/635,
|
||||
// use entry global in those places that are called from JS APIs.
|
||||
nsIGlobalObject* global = GetEntryGlobal();
|
||||
MOZ_ASSERT(global);
|
||||
AutoEntryScript aes(global, "create custom elements");
|
||||
JSContext* cx = aes.cx();
|
||||
ErrorResult rv;
|
||||
|
||||
// Step 5.
|
||||
if (definition->IsCustomBuiltIn()) {
|
||||
// SetupCustomElement() should be called with an element that don't have
|
||||
// CustomElementData setup, if not we will hit the assertion in
|
||||
// SetCustomElementData().
|
||||
nsCOMPtr<nsIAtom> tagAtom = nodeInfo->NameAtom();
|
||||
nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom;
|
||||
// Built-in element
|
||||
*aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
|
||||
(*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
|
||||
if (synchronousCustomElements) {
|
||||
CustomElementRegistry::Upgrade(*aResult, definition, rv);
|
||||
} else {
|
||||
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
|
||||
}
|
||||
|
||||
if (rv.MaybeSetPendingException(cx)) {
|
||||
aes.ReportException();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Step 6.1.
|
||||
if (synchronousCustomElements) {
|
||||
DoCustomElementCreate(aResult, nodeInfo->GetDocument(),
|
||||
definition->mConstructor, rv);
|
||||
if (rv.MaybeSetPendingException(cx)) {
|
||||
NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Step 6.2.
|
||||
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
|
||||
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Per the Custom Element specification, unknown tags that are valid custom
|
||||
// element names should be HTMLElement instead of HTMLUnknownElement.
|
||||
bool isCustomElementName = (tag == eHTMLTag_userdefined &&
|
||||
|
@ -7715,12 +7715,10 @@ private:
|
||||
void
|
||||
SendResults() override;
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
static nsresult
|
||||
UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
|
||||
const IndexMetadata& aIndexMetadata,
|
||||
const nsCString& aLocale);
|
||||
#endif
|
||||
};
|
||||
|
||||
class OpenDatabaseOp::VersionChangeOp final
|
||||
@ -19914,9 +19912,6 @@ DatabaseOperationBase::BindKeyRangeToStatement(
|
||||
mozIStorageStatement* aStatement,
|
||||
const nsCString& aLocale)
|
||||
{
|
||||
#ifndef ENABLE_INTL_API
|
||||
return BindKeyRangeToStatement(aKeyRange, aStatement);
|
||||
#else
|
||||
MOZ_ASSERT(!IsOnBackgroundThread());
|
||||
MOZ_ASSERT(aStatement);
|
||||
MOZ_ASSERT(!aLocale.IsEmpty());
|
||||
@ -19954,7 +19949,6 @@ DatabaseOperationBase::BindKeyRangeToStatement(
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
@ -22017,7 +22011,6 @@ OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection)
|
||||
|
||||
indexMetadata->mCommonMetadata.multiEntry() = !!scratch;
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
const bool localeAware = !stmt->IsNull(6);
|
||||
if (localeAware) {
|
||||
rv = stmt->GetUTF8String(6, indexMetadata->mCommonMetadata.locale());
|
||||
@ -22047,7 +22040,6 @@ OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (NS_WARN_IF(!objectStoreMetadata->mIndexes.Put(indexId, indexMetadata,
|
||||
fallible))) {
|
||||
@ -22073,7 +22065,6 @@ OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
/* static */
|
||||
nsresult
|
||||
OpenDatabaseOp::UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
|
||||
@ -22191,7 +22182,6 @@ OpenDatabaseOp::UpdateLocaleAwareIndex(mozIStorageConnection* aConnection,
|
||||
rv = metaStmt->Execute();
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
OpenDatabaseOp::BeginVersionChange()
|
||||
@ -27966,15 +27956,12 @@ OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen)
|
||||
if (range.isOnly()) {
|
||||
*aKey = range.lower();
|
||||
*aOpen = false;
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (mCursor->IsLocaleAware()) {
|
||||
range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
*aKey = aLowerBound ? range.lower() : range.upper();
|
||||
*aOpen = aLowerBound ? range.lowerOpen() : range.upperOpen();
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (mCursor->IsLocaleAware()) {
|
||||
if (aLowerBound) {
|
||||
range.lower().ToLocaleBasedKey(*aKey, mCursor->mLocale);
|
||||
@ -27982,7 +27969,6 @@ OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen)
|
||||
range.upper().ToLocaleBasedKey(*aKey, mCursor->mLocale);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
*aOpen = false;
|
||||
|
@ -65,13 +65,11 @@ IDBCursor::IDBCursor(Type aType,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
bool
|
||||
IDBCursor::IsLocaleAware() const
|
||||
{
|
||||
return mSourceIndex && !mSourceIndex->Locale().IsEmpty();
|
||||
}
|
||||
#endif
|
||||
|
||||
IDBCursor::~IDBCursor()
|
||||
{
|
||||
@ -437,7 +435,6 @@ IDBCursor::Continue(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (IsLocaleAware() && !key.IsUnset()) {
|
||||
Key tmp;
|
||||
aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
|
||||
@ -448,9 +445,6 @@ IDBCursor::Continue(JSContext* aCx,
|
||||
}
|
||||
|
||||
const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
|
||||
#else
|
||||
const Key& sortKey = mKey;
|
||||
#endif
|
||||
|
||||
if (!key.IsUnset()) {
|
||||
switch (mDirection) {
|
||||
@ -547,7 +541,6 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (IsLocaleAware() && !key.IsUnset()) {
|
||||
Key tmp;
|
||||
aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
|
||||
@ -558,9 +551,6 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx,
|
||||
}
|
||||
|
||||
const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
|
||||
#else
|
||||
const Key& sortKey = mKey;
|
||||
#endif
|
||||
|
||||
if (key.IsUnset()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
||||
|
@ -205,11 +205,9 @@ private:
|
||||
|
||||
~IDBCursor();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
// Checks if this is a locale aware cursor (ie. the index's sortKey is unset)
|
||||
bool
|
||||
IsLocaleAware() const;
|
||||
#endif
|
||||
|
||||
void
|
||||
DropJSObjects();
|
||||
|
@ -1316,9 +1316,7 @@ IDBObjectStore::AppendIndexUpdateInfo(
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
const bool localeAware = !aLocale.IsEmpty();
|
||||
#endif
|
||||
|
||||
if (!aMultiEntry) {
|
||||
Key key;
|
||||
@ -1336,14 +1334,12 @@ IDBObjectStore::AppendIndexUpdateInfo(
|
||||
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
||||
updateInfo->indexId() = aIndexID;
|
||||
updateInfo->value() = key;
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (localeAware) {
|
||||
rv = key.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1383,14 +1379,12 @@ IDBObjectStore::AppendIndexUpdateInfo(
|
||||
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
||||
updateInfo->indexId() = aIndexID;
|
||||
updateInfo->value() = value;
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (localeAware) {
|
||||
rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1404,14 +1398,12 @@ IDBObjectStore::AppendIndexUpdateInfo(
|
||||
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
||||
updateInfo->indexId() = aIndexID;
|
||||
updateInfo->value() = value;
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (localeAware) {
|
||||
rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -2277,11 +2269,9 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
||||
// Valid locale names are always ASCII as per BCP-47.
|
||||
nsCString locale = NS_LossyConvertUTF16toASCII(aOptionalParameters.mLocale);
|
||||
bool autoLocale = locale.EqualsASCII("auto");
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (autoLocale) {
|
||||
locale = IndexedDatabaseManager::GetLocale();
|
||||
}
|
||||
#endif
|
||||
|
||||
IndexMetadata* metadata = indexes.AppendElement(
|
||||
IndexMetadata(transaction->NextIndexId(), nsString(aName), keyPath,
|
||||
|
@ -45,6 +45,8 @@
|
||||
#include "ScriptErrorHelper.h"
|
||||
#include "WorkerScope.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "unicode/locid.h"
|
||||
|
||||
// Bindings for ResolveConstructors
|
||||
#include "mozilla/dom/IDBCursorBinding.h"
|
||||
@ -59,11 +61,6 @@
|
||||
#include "mozilla/dom/IDBTransactionBinding.h"
|
||||
#include "mozilla/dom/IDBVersionChangeEventBinding.h"
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "unicode/locid.h"
|
||||
#endif
|
||||
|
||||
#define IDB_STR "indexedDB"
|
||||
|
||||
// The two possible values for the data argument when receiving the disk space
|
||||
@ -430,7 +427,6 @@ IndexedDatabaseManager::Init()
|
||||
Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback,
|
||||
kPrefMaxSerilizedMsgSize);
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsAutoCString acceptLang;
|
||||
Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
|
||||
|
||||
@ -449,7 +445,6 @@ IndexedDatabaseManager::Init()
|
||||
if (mLocale.IsEmpty()) {
|
||||
mLocale.AssignLiteral("en_US");
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1093,7 +1088,6 @@ IndexedDatabaseManager::LoggingModePrefChangedCallback(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
// static
|
||||
const nsCString&
|
||||
IndexedDatabaseManager::GetLocale()
|
||||
@ -1103,7 +1097,6 @@ IndexedDatabaseManager::GetLocale()
|
||||
|
||||
return idbManager->mLocale;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_IMPL_ADDREF(IndexedDatabaseManager)
|
||||
NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
|
||||
|
@ -189,10 +189,8 @@ public:
|
||||
nsresult
|
||||
FlushPendingFileDeletions();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
static const nsCString&
|
||||
GetLocale();
|
||||
#endif
|
||||
|
||||
static mozilla::Mutex&
|
||||
FileMutex()
|
||||
@ -241,9 +239,7 @@ private:
|
||||
// and FileInfo.mSliceRefCnt
|
||||
mozilla::Mutex mFileMutex;
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsCString mLocale;
|
||||
#endif
|
||||
|
||||
indexedDB::BackgroundUtilsChild* mBackgroundActor;
|
||||
|
||||
|
@ -22,11 +22,8 @@
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "ReportInternalError.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
#include "unicode/ucol.h"
|
||||
#endif
|
||||
#include "xpcpublic.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -110,7 +107,7 @@ namespace indexedDB {
|
||||
[1, 2] // 0x60 bf f0 0 0 0 0 0 0 0x10 c0
|
||||
[[]] // 0x80
|
||||
*/
|
||||
#ifdef ENABLE_INTL_API
|
||||
|
||||
nsresult
|
||||
Key::ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const
|
||||
{
|
||||
@ -207,7 +204,6 @@ Key::ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const
|
||||
aTarget.TrimBuffer();
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
|
||||
@ -507,7 +503,6 @@ Key::EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsresult
|
||||
Key::EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
|
||||
const nsCString& aLocale)
|
||||
@ -545,7 +540,6 @@ Key::EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
|
||||
keyBuffer.Elements()+sortKeyLength,
|
||||
aTypeOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
// static
|
||||
nsresult
|
||||
|
@ -214,10 +214,8 @@ public:
|
||||
nsresult
|
||||
AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal);
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsresult
|
||||
ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const;
|
||||
#endif
|
||||
|
||||
void
|
||||
FinishArray()
|
||||
@ -298,11 +296,9 @@ private:
|
||||
nsresult
|
||||
EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType);
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsresult
|
||||
EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
|
||||
const nsCString& aLocale);
|
||||
#endif
|
||||
|
||||
void
|
||||
EncodeNumber(double aFloat, uint8_t aType);
|
||||
|
@ -7,7 +7,10 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: IndexedDB")
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest-intl-api.ini',
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
@ -18,9 +21,6 @@ XPCSHELL_TESTS_MANIFESTS += [
|
||||
'test/unit/xpcshell-parent-process.ini'
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest-intl-api.ini']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'IDBCursor.h',
|
||||
'IDBDatabase.h',
|
||||
|
@ -106,9 +106,9 @@ enum class CubebState {
|
||||
Shutdown
|
||||
} sCubebState = CubebState::Uninitialized;
|
||||
cubeb* sCubebContext;
|
||||
double sVolumeScale;
|
||||
uint32_t sCubebPlaybackLatencyInMilliseconds;
|
||||
uint32_t sCubebMSGLatencyInFrames;
|
||||
double sVolumeScale = 1.0;
|
||||
uint32_t sCubebPlaybackLatencyInMilliseconds = 100;
|
||||
uint32_t sCubebMSGLatencyInFrames = 512;
|
||||
bool sCubebPlaybackLatencyPrefSet;
|
||||
bool sCubebMSGLatencyPrefSet;
|
||||
bool sAudioStreamInitEverSucceeded = false;
|
||||
|
@ -3,6 +3,7 @@
|
||||
# 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('/media/webrtc/webrtc.mozbuild')
|
||||
|
||||
FILES_PER_UNIFIED_FILE = 6
|
||||
|
||||
@ -316,11 +317,6 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
|
||||
DEFINES['MOZILLA_INTERNAL_API'] = True
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
DEFINES['WEBRTC_WIN'] = True
|
||||
else:
|
||||
DEFINES['WEBRTC_POSIX'] = True
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] > '15':
|
||||
DEFINES['MOZ_OMX_WEBM_DECODER'] = True
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
# 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('/media/webrtc/webrtc.mozbuild')
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
EXPORTS += [
|
||||
@ -22,12 +23,6 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
'/media/webrtc/signaling',
|
||||
'/media/webrtc/trunk',
|
||||
]
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
DEFINES['WEBRTC_WIN'] = True
|
||||
else:
|
||||
DEFINES['WEBRTC_POSIX'] = True
|
||||
# Must match build/gyp.mozbuild: enable_libevent
|
||||
DEFINES['WEBRTC_BUILD_LIBEVENT'] = True
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
DEFINES['WEBRTC_ANDROID'] = True
|
||||
|
@ -3,6 +3,7 @@
|
||||
# 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('/media/webrtc/webrtc.mozbuild')
|
||||
|
||||
with Files('*'):
|
||||
BUG_COMPONENT = ('Core', 'WebRTC: Audio/Video')
|
||||
@ -20,10 +21,6 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
DEFINES['WEBRTC_WIN'] = True
|
||||
else:
|
||||
DEFINES['WEBRTC_POSIX'] = True
|
||||
EXPORTS += ['AudioOutputObserver.h',
|
||||
'MediaEngineRemoteVideoSource.h',
|
||||
'MediaEngineWebRTC.h']
|
||||
|
@ -4,11 +4,13 @@
|
||||
* 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 "BasicCardPayment.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/PaymentRequest.h"
|
||||
#include "mozilla/dom/PaymentResponse.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "BasicCardPayment.h"
|
||||
#include "nsIURLParser.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "PaymentRequestManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -52,6 +54,191 @@ PaymentRequest::PrefEnabled(JSContext* aCx, JSObject* aObj)
|
||||
return Preferences::GetBool("dom.payments.request.enabled");
|
||||
}
|
||||
|
||||
nsresult
|
||||
PaymentRequest::IsValidStandardizedPMI(const nsAString& aIdentifier,
|
||||
nsAString& aErrorMsg)
|
||||
{
|
||||
/*
|
||||
* The syntax of a standardized payment method identifier is given by the
|
||||
* following [ABNF]:
|
||||
*
|
||||
* stdpmi = part *( "-" part )
|
||||
* part = 1loweralpha *( DIGIT / loweralpha )
|
||||
* loweralpha = %x61-7A
|
||||
*/
|
||||
nsString::const_iterator start, end;
|
||||
aIdentifier.BeginReading(start);
|
||||
aIdentifier.EndReading(end);
|
||||
while (start != end) {
|
||||
// the first char must be in the range %x61-7A
|
||||
if ((*start < 'a' || *start > 'z')) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. The character '");
|
||||
aErrorMsg.Append(*start);
|
||||
aErrorMsg.AppendLiteral("' at the beginning or after the '-' must be in the range [a-z].");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
++start;
|
||||
// the rest can be in the range %x61-7A + DIGITs
|
||||
while (start != end && *start != '-' &&
|
||||
((*start >= 'a' && *start <= 'z') || (*start >= '0' && *start <= '9'))) {
|
||||
++start;
|
||||
}
|
||||
// if the char is not in the range %x61-7A + DIGITs, it must be '-'
|
||||
if (start != end && *start != '-') {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. The character '");
|
||||
aErrorMsg.Append(*start);
|
||||
aErrorMsg.AppendLiteral("' must be in the range [a-zA-z0-9-].");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
if (*start == '-') {
|
||||
++start;
|
||||
// the last char can not be '-'
|
||||
if (start == end) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. The last character '");
|
||||
aErrorMsg.Append(*start);
|
||||
aErrorMsg.AppendLiteral("' must be in the range [a-z0-9].");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PaymentRequest::IsValidPaymentMethodIdentifier(const nsAString& aIdentifier,
|
||||
nsAString& aErrorMsg)
|
||||
{
|
||||
if (aIdentifier.IsEmpty()) {
|
||||
aErrorMsg.AssignLiteral("Payment method identifier is required.");
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
/*
|
||||
* URL-based payment method identifier
|
||||
*
|
||||
* 1. If url's scheme is not "https", return false.
|
||||
* 2. If url's username or password is not the empty string, return false.
|
||||
* 3. Otherwise, return true.
|
||||
*/
|
||||
nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
|
||||
MOZ_ASSERT(urlParser);
|
||||
uint32_t schemePos = 0;
|
||||
int32_t schemeLen = 0;
|
||||
uint32_t authorityPos = 0;
|
||||
int32_t authorityLen = 0;
|
||||
NS_ConvertUTF16toUTF8 url(aIdentifier);
|
||||
nsresult rv = urlParser->ParseURL(url.get(),
|
||||
url.Length(),
|
||||
&schemePos, &schemeLen,
|
||||
&authorityPos, &authorityLen,
|
||||
nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_RANGE_ERR);
|
||||
if (schemeLen == -1) {
|
||||
// The PMI is not a URL-based PMI, check if it is a standardized PMI
|
||||
return IsValidStandardizedPMI(aIdentifier, aErrorMsg);
|
||||
}
|
||||
if (!Substring(aIdentifier, schemePos, schemeLen).EqualsASCII("https")) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. The scheme must be 'https'.");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
if (Substring(aIdentifier, authorityPos, authorityLen).IsEmpty()) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
|
||||
uint32_t usernamePos = 0;
|
||||
int32_t usernameLen = 0;
|
||||
uint32_t passwordPos = 0;
|
||||
int32_t passwordLen = 0;
|
||||
uint32_t hostnamePos = 0;
|
||||
int32_t hostnameLen = 0;
|
||||
int32_t port = 0;
|
||||
|
||||
NS_ConvertUTF16toUTF8 authority(Substring(aIdentifier, authorityPos, authorityLen));
|
||||
rv = urlParser->ParseAuthority(authority.get(),
|
||||
authority.Length(),
|
||||
&usernamePos, &usernameLen,
|
||||
&passwordPos, &passwordLen,
|
||||
&hostnamePos, &hostnameLen,
|
||||
&port);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Handle the special cases that URLParser treats it as an invalid URL, but
|
||||
// are used in web-platform-test
|
||||
// For exmaple:
|
||||
// https://:@example.com // should be considered as valid
|
||||
// https://:password@example.com. // should be considered as invalid
|
||||
int32_t atPos = authority.FindChar('@');
|
||||
if (atPos >= 0) {
|
||||
// only accept the case https://:@xxx
|
||||
if (atPos == 1 && authority.CharAt(0) == ':') {
|
||||
usernamePos = 0;
|
||||
usernameLen = 0;
|
||||
passwordPos = 0;
|
||||
passwordLen = 0;
|
||||
} else {
|
||||
// for the fail cases, don't care about what the actual length is.
|
||||
usernamePos = 0;
|
||||
usernameLen = INT32_MAX;
|
||||
passwordPos = 0;
|
||||
passwordLen = INT32_MAX;
|
||||
}
|
||||
} else {
|
||||
usernamePos = 0;
|
||||
usernameLen = -1;
|
||||
passwordPos = 0;
|
||||
passwordLen = -1;
|
||||
}
|
||||
// Parse server information when both username and password are empty or do not
|
||||
// exist.
|
||||
if ((usernameLen <= 0) && (passwordLen <= 0)) {
|
||||
if (authority.Length() - atPos - 1 == 0) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
// Re-using nsIURLParser::ParseServerInfo to extract the hostname and port
|
||||
// information. This can help us to handle complicated IPv6 cases.
|
||||
nsAutoCString serverInfo(Substring(authority,
|
||||
atPos + 1,
|
||||
authority.Length() - atPos - 1));
|
||||
rv = urlParser->ParseServerInfo(serverInfo.get(),
|
||||
serverInfo.Length(),
|
||||
&hostnamePos, &hostnameLen, &port);
|
||||
if (NS_FAILED(rv)) {
|
||||
// ParseServerInfo returns NS_ERROR_MALFORMED_URI in all fail cases, we
|
||||
// probably need a followup bug to figure out the fail reason.
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
// PMI is valid when usernameLen/passwordLen equals to -1 or 0.
|
||||
if (usernameLen > 0 || passwordLen > 0) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AssignLiteral("' is not valid. Username and password must be empty.");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
|
||||
// PMI is valid when hostnameLen is larger than 0
|
||||
if (hostnameLen <= 0) {
|
||||
aErrorMsg.AssignLiteral("'");
|
||||
aErrorMsg.Append(aIdentifier);
|
||||
aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
|
||||
return NS_ERROR_RANGE_ERR;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PaymentRequest::IsValidMethodData(JSContext* aCx,
|
||||
const Sequence<PaymentMethodData>& aMethodData,
|
||||
@ -63,11 +250,12 @@ PaymentRequest::IsValidMethodData(JSContext* aCx,
|
||||
}
|
||||
|
||||
for (const PaymentMethodData& methodData : aMethodData) {
|
||||
if (methodData.mSupportedMethods.IsEmpty()) {
|
||||
aErrorMsg.AssignLiteral(
|
||||
"Payment method identifier is required.");
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
nsresult rv = IsValidPaymentMethodIdentifier(methodData.mSupportedMethods,
|
||||
aErrorMsg);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<BasicCardService> service = BasicCardService::GetService();
|
||||
MOZ_ASSERT(service);
|
||||
if (service->IsBasicCardPayment(methodData.mSupportedMethods)) {
|
||||
@ -299,6 +487,10 @@ PaymentRequest::IsValidDetailsBase(const PaymentDetailsBase& aDetails, nsAString
|
||||
if (aDetails.mModifiers.WasPassed()) {
|
||||
const Sequence<PaymentDetailsModifier>& modifiers = aDetails.mModifiers.Value();
|
||||
for (const PaymentDetailsModifier& modifier : modifiers) {
|
||||
rv = IsValidPaymentMethodIdentifier(modifier.mSupportedMethods, aErrorMsg);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = IsValidCurrencyAmount(NS_LITERAL_STRING("details.modifiers.total"),
|
||||
modifier.mTotal.mAmount,
|
||||
true, // isTotalItem
|
||||
@ -377,7 +569,11 @@ PaymentRequest::Constructor(const GlobalObject& aGlobal,
|
||||
aMethodData,
|
||||
message);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.ThrowTypeError<MSG_ILLEGAL_TYPE_PR_CONSTRUCTOR>(message);
|
||||
if (rv == NS_ERROR_TYPE_ERR) {
|
||||
aRv.ThrowTypeError<MSG_ILLEGAL_TYPE_PR_CONSTRUCTOR>(message);
|
||||
} else if (rv == NS_ERROR_RANGE_ERR) {
|
||||
aRv.ThrowRangeError<MSG_ILLEGAL_RANGE_PR_CONSTRUCTOR>(message);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
rv = IsValidDetailsInit(aDetails, message);
|
||||
|
@ -36,6 +36,12 @@ public:
|
||||
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static nsresult IsValidStandardizedPMI(const nsAString& aIdentifier,
|
||||
nsAString& aErrorMsg);
|
||||
|
||||
static nsresult IsValidPaymentMethodIdentifier(const nsAString& aIdentifier,
|
||||
nsAString& aErrorMsg);
|
||||
|
||||
static nsresult IsValidMethodData(JSContext* aCx,
|
||||
const Sequence<PaymentMethodData>& aMethodData,
|
||||
nsAString& aErrorMsg);
|
||||
|
58
dom/payments/test/PMIValidationChromeScript.js
Normal file
58
dom/payments/test/PMIValidationChromeScript.js
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
|
||||
|
||||
const UIService = {
|
||||
showPayment: function(requestId) {
|
||||
paymentSrv.changeShippingOption(requestId, "");
|
||||
},
|
||||
abortPayment: function(requestId) {
|
||||
let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
|
||||
createInstance(Ci.nsIPaymentAbortActionResponse);
|
||||
abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
|
||||
paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
|
||||
},
|
||||
completePayment: function(requestId) {
|
||||
const completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
|
||||
createInstance(Ci.nsIPaymentCompleteActionResponse);
|
||||
completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
|
||||
paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
|
||||
},
|
||||
updatePayment: function(requestId) {
|
||||
const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
|
||||
createInstance(Ci.nsIGeneralResponseData);
|
||||
showResponseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
|
||||
|
||||
const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
|
||||
createInstance(Ci.nsIPaymentShowActionResponse);
|
||||
showResponse.init(requestId,
|
||||
Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
|
||||
"https://example.com", // payment method
|
||||
showResponseData, // payment method data
|
||||
"Bill A. Pacheco", // payer name
|
||||
"", // payer email
|
||||
""); // payer phone
|
||||
paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
|
||||
};
|
||||
|
||||
function emitTestFail(message) {
|
||||
sendAsyncMessage("test-fail", message);
|
||||
}
|
||||
|
||||
addMessageListener("set-ui-service", function() {
|
||||
paymentSrv.setTestingUIService(UIService.QueryInterface(Ci.nsIPaymentUIService));
|
||||
});
|
||||
|
||||
addMessageListener("teardown", function() {
|
||||
paymentSrv.cleanup();
|
||||
sendAsyncMessage("teardown-complete");
|
||||
});
|
@ -7,7 +7,7 @@ function checkSimplePayment(aSimplePayment) {
|
||||
|
||||
const methodData = aSimplePayment.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
|
||||
ok(methodData, "Fail to get payment methodData.");
|
||||
is(methodData.supportedMethods, "MyPay", "supported method should be 'MyPay'.");
|
||||
is(methodData.supportedMethods, "basic-card", "supported method should be 'basic-card'.");
|
||||
ok(!methodData.data, "methodData.data should not exist.");
|
||||
|
||||
// checking the passed PaymentDetails parameter
|
||||
@ -36,7 +36,7 @@ function checkDupShippingOptionsPayment(aPayment) {
|
||||
|
||||
const methodData = aPayment.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
|
||||
ok(methodData, "Fail to get payment methodData.");
|
||||
is(methodData.supportedMethods, "MyPay", "methodData.supportedMethod name should be 'MyPay'.");
|
||||
is(methodData.supportedMethods, "basic-card", "methodData.supportedMethod name should be 'basic-card'.");
|
||||
ok(!methodData.data, "methodData.data should not exist.");
|
||||
|
||||
// checking the passed PaymentDetails parameter
|
||||
|
@ -8,6 +8,7 @@ support-files =
|
||||
ConstructorChromeScript.js
|
||||
CurrencyAmountValidationChromeScript.js
|
||||
GeneralChromeScript.js
|
||||
PMIValidationChromeScript.js
|
||||
ShowPaymentChromeScript.js
|
||||
|
||||
[test_abortPayment.html]
|
||||
@ -18,4 +19,5 @@ run-if = nightly_build # Bug 1390737: Depends on the Nightly-only UI service
|
||||
[test_constructor.html]
|
||||
[test_currency_amount_validation.html]
|
||||
[test_payment-request-in-iframe.html]
|
||||
[test_pmi_validation.html]
|
||||
[test_showPayment.html]
|
||||
|
@ -9,7 +9,7 @@
|
||||
<script type="text/javascript">
|
||||
|
||||
const supportedInstruments = [{
|
||||
supportedMethods: "MyPay",
|
||||
supportedMethods: "basic-card",
|
||||
}];
|
||||
const details = {
|
||||
id: "simple details",
|
||||
|
@ -88,11 +88,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1345367
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345367">Mozilla Bug 1345367</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -288,11 +288,5 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1375345
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375345">Mozilla Bug 1375345</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -159,11 +159,5 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1345365
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345365">Mozilla Bug 1345365</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -376,11 +376,5 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1388661
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1367669">Mozilla Bug 1367669</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1388661">Mozilla Bug 1388661</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -93,10 +93,5 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1318988
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1318988">Mozilla Bug 1318988</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
241
dom/payments/test/test_pmi_validation.html
Normal file
241
dom/payments/test/test_pmi_validation.html
Normal file
@ -0,0 +1,241 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1389418
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for PaymentRequest API payment method identifier validation</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gUrl = SimpleTest.getTestFileURL('PMIValidationChromeScript.js');
|
||||
var gScript = SpecialPowers.loadChromeScript(gUrl);
|
||||
|
||||
function testFailHandler(message) {
|
||||
ok(false, message);
|
||||
}
|
||||
gScript.addMessageListener("test-fail", testFailHandler);
|
||||
|
||||
const defaultMethods = [{
|
||||
supportedMethods: "basic-card",
|
||||
}];
|
||||
|
||||
const defaultDetails = {
|
||||
total: {
|
||||
label: "total",
|
||||
amount: {
|
||||
currency: "usd",
|
||||
value: "1.00",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const validPMIs = [
|
||||
"https://wpt",
|
||||
"https://wpt.fyi/",
|
||||
"https://wpt.fyi/payment",
|
||||
"https://wpt.fyi/payment-request",
|
||||
"https://wpt.fyi/payment-request?",
|
||||
"https://wpt.fyi/payment-request?this=is",
|
||||
"https://wpt.fyi/payment-request?this=is&totally",
|
||||
"https://wpt.fyi:443/payment-request?this=is&totally",
|
||||
"https://wpt.fyi:443/payment-request?this=is&totally#fine",
|
||||
"https://:@wpt.fyi:443/payment-request?this=is&totally#👍",
|
||||
" \thttps://wpt\n ",
|
||||
"https://xn--c1yn36f",
|
||||
"https://點看",
|
||||
"e",
|
||||
"n6jzof05mk2g4lhxr-u-q-w1-c-i-pa-ty-bdvs9-ho-ae7-p-md8-s-wq3-h-qd-e-q-sa",
|
||||
"a-b-q-n-s-pw0",
|
||||
"m-u",
|
||||
"s-l5",
|
||||
"k9-f",
|
||||
"m-l",
|
||||
"u4-n-t",
|
||||
"i488jh6-g18-fck-yb-v7-i",
|
||||
"x-x-t-t-c34-o",
|
||||
"basic-card",
|
||||
];
|
||||
|
||||
const invalidPMIs = [
|
||||
"https://:password@example.com",
|
||||
"https://username@example.com",
|
||||
"https://username:password@example.com/pay",
|
||||
"http://username:password@example.com/pay",
|
||||
"https://:@example.com:100000000/pay",
|
||||
"https://foo.com:100000000/pay",
|
||||
"basic-💳",
|
||||
"not-https://wpt.fyi/payment-request",
|
||||
"../realitive/url",
|
||||
"/absolute/../path?",
|
||||
"https://",
|
||||
"¡basic-*-card!",
|
||||
"Basic-Card",
|
||||
"0",
|
||||
"-",
|
||||
"--",
|
||||
"a--b",
|
||||
"-a--b",
|
||||
"a-b-",
|
||||
"0-",
|
||||
"0-a",
|
||||
"a0--",
|
||||
"A-",
|
||||
"A-B",
|
||||
"A-b",
|
||||
"a-0",
|
||||
"a-0b",
|
||||
" a-b",
|
||||
"\t\na-b",
|
||||
"a-b ",
|
||||
"a-b\n\t",
|
||||
];
|
||||
|
||||
function testWithValidPMIs() {
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const validPMI of validPMIs) {
|
||||
try {
|
||||
const validMethods = [{supportedMethods: validPMI},];
|
||||
const payRequest = new PaymentRequest(validMethods, defaultDetails);
|
||||
resolve();
|
||||
} catch (e) {
|
||||
ok(false, "Unexpected error '" + e.name + "'.");
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testWithInvalidPMIs() {
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const invalidPMI of invalidPMIs) {
|
||||
try {
|
||||
const invalidMethods = [{supportedMethods: invalidPMI},];
|
||||
const payRequest = new PaymentRequest(invalidMethods, defaultDetails);
|
||||
ok(false, "Expected throw 'RangeError', but got resolved");
|
||||
resolve();
|
||||
} catch (e) {
|
||||
is(e.name, "RangeError", "Expected 'RangeError'.");
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testUpdateWithValidPMI() {
|
||||
gScript.sendAsyncMessage("set-ui-service");
|
||||
return new Promise((resolve, reject) => {
|
||||
const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
|
||||
payRequest.addEventListener("shippingoptionchange", event => {
|
||||
const validDetails = {
|
||||
total: {
|
||||
label: "total",
|
||||
amount: {
|
||||
currency: "USD",
|
||||
value: "1.00",
|
||||
},
|
||||
},
|
||||
modifiers: [{
|
||||
supportedMethods: "https://example.com",
|
||||
total: {
|
||||
label: "total",
|
||||
amount: {
|
||||
currency: "USD",
|
||||
value: "1.00",
|
||||
},
|
||||
}
|
||||
},],
|
||||
}
|
||||
event.updateWith(validDetails);
|
||||
});
|
||||
payRequest.show().then((response) => {
|
||||
response.complete("success").then(() => {
|
||||
resolve();
|
||||
}).catch((e) => {
|
||||
ok(false, "Unexpected error '" + e.name + "'.");
|
||||
resolve();
|
||||
});
|
||||
}).catch((e) => {
|
||||
ok(false, "Unexpected error '" + e.name + "'.");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testUpdateWithInvalidPMI() {
|
||||
gScript.sendAsyncMessage("set-ui-service");
|
||||
return new Promise((resolve, reject) => {
|
||||
const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
|
||||
payRequest.addEventListener("shippingoptionchange", event => {
|
||||
const invalidDetails = {
|
||||
total: {
|
||||
label: "total",
|
||||
amount: {
|
||||
currency: "USD",
|
||||
value: "1.00",
|
||||
},
|
||||
},
|
||||
modifiers: [{
|
||||
supportedMethods: "https://username:password@example.com",
|
||||
total: {
|
||||
label: "total",
|
||||
amount: {
|
||||
currency: "USD",
|
||||
value: "1.00",
|
||||
},
|
||||
},
|
||||
},],
|
||||
}
|
||||
event.updateWith(invalidDetails);
|
||||
});
|
||||
payRequest.show().then((result) => {
|
||||
ok(false, "Expected throw 'RangeError', but got resolved.");
|
||||
resolve();
|
||||
}).catch((e) => {
|
||||
is(e.name, "RangeError", "Expected 'RangeError'.");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
|
||||
gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
|
||||
gScript.removeMessageListener("test-fail", testFailHandler)
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
gScript.sendAsyncMessage("teardown");
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testWithValidPMIs()
|
||||
.then(testWithInvalidPMIs)
|
||||
.then(testUpdateWithValidPMI)
|
||||
.then(testUpdateWithInvalidPMI)
|
||||
.then(teardown)
|
||||
.catch( e => {
|
||||
ok(false, "Unexpected error: " + e.name);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
'set': [
|
||||
['dom.payments.request.enabled', true],
|
||||
]
|
||||
}, runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1389418">Mozilla Bug 1389418</a>
|
||||
</body>
|
||||
</html>
|
@ -300,11 +300,5 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1345366
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345366">Mozilla Bug 1345366</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -513,8 +513,6 @@ var interfaceNamesInGlobalScope =
|
||||
"HTMLScriptElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HTMLSelectElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "HTMLShadowElement", stylo: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HTMLSourceElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -33,8 +33,6 @@ support-files =
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_dest_insertion_points.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_dest_insertion_points_shadow.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_fallback_dest_insertion_points.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_detached_style.html]
|
||||
@ -53,8 +51,6 @@ skip-if = true # disabled - See bug 1390396
|
||||
[test_document_register_stack.html]
|
||||
skip-if = true # disabled - See bug 1390396
|
||||
[test_document_shared_registry.html]
|
||||
[test_event_dispatch.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_event_retarget.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_event_stopping.html]
|
||||
@ -66,16 +62,10 @@ skip-if = stylo # bug 1293844
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_inert_element.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_host.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_style.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_style_multiple_shadow.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_style_order.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_shadowroot_youngershadowroot.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_style_fallback_content.html]
|
||||
skip-if = stylo # bug 1293844
|
||||
[test_unresolved_pseudo_class.html]
|
||||
|
@ -1,68 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=999999
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 999999</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="shadowhost"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 999999 **/
|
||||
var host = document.getElementById("shadowhost");
|
||||
|
||||
// Test destination insertion points of node distributed to shadow element.
|
||||
var olderShadowRoot = host.createShadowRoot();
|
||||
var youngerShadowRoot = host.createShadowRoot();
|
||||
|
||||
var shadowElem = document.createElement("shadow");
|
||||
youngerShadowRoot.appendChild(shadowElem);
|
||||
|
||||
var span = document.createElement("span");
|
||||
olderShadowRoot.appendChild(span);
|
||||
|
||||
is(span.getDestinationInsertionPoints().length, 1, "Child of ShadowRoot should be distributed to shadow insertion point.");
|
||||
is(span.getDestinationInsertionPoints()[0], shadowElem, "Shadow element should be in destination insertion point list.");
|
||||
|
||||
// Test destination insertion points of node removed from tree.
|
||||
olderShadowRoot.removeChild(span);
|
||||
is(span.getDestinationInsertionPoints().length, 0, "Node removed from tree should no longer be distributed.");
|
||||
|
||||
// Test destination insertion points of fallback content being reprojected into a shadow element.
|
||||
var content = document.createElement("content");
|
||||
var fallback = document.createElement("span");
|
||||
|
||||
content.appendChild(fallback);
|
||||
olderShadowRoot.appendChild(content);
|
||||
|
||||
is(fallback.getDestinationInsertionPoints().length, 2, "The fallback content should have 2 destination insertion points, the parent content and the shadow element to which it is reprojected.");
|
||||
is(fallback.getDestinationInsertionPoints()[0], content, "First destination of the fallback content should be the parent content element.");
|
||||
is(fallback.getDestinationInsertionPoints()[1], shadowElem, "Second destination of the fallback content should be the shadow element to which the element is reprojected.");
|
||||
|
||||
// Test destination insertion points of fallback content being removed from tree.
|
||||
content.removeChild(fallback);
|
||||
is(fallback.getDestinationInsertionPoints().length, 0, "The content should no longer be distributed to any nodes because it is no longer fallback content.");
|
||||
|
||||
// Test destination insertion points of distributed content after removing shadow insertion point.
|
||||
var div = document.createElement("div");
|
||||
olderShadowRoot.appendChild(div);
|
||||
is(div.getDestinationInsertionPoints().length, 1, "Children in older shadow root should be distributed to shadow insertion point.");
|
||||
is(div.getDestinationInsertionPoints()[0], shadowElem, "Destination insertion point should include shadow element.");
|
||||
|
||||
youngerShadowRoot.removeChild(shadowElem);
|
||||
is(div.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty after removing shadow element.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -107,7 +107,6 @@ function startTest() {
|
||||
var constructedButton = new buttonConstructor();
|
||||
is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
|
||||
is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
|
||||
is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
|
||||
|
||||
// Try creating an element with a custom element name, but not in the html namespace.
|
||||
var htmlNamespaceProto = Object.create(HTMLElement.prototype);
|
||||
|
@ -291,11 +291,14 @@ function testAttributeChanged() {
|
||||
|
||||
function testAttributeChangedExtended() {
|
||||
var p = Object.create(HTMLButtonElement.prototype);
|
||||
var callbackCalled = false;
|
||||
var callbackCalled = 0;
|
||||
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
||||
is(callbackCalled, false, "Callback should only be called once in this test.");
|
||||
callbackCalled = true;
|
||||
runNextTest();
|
||||
callbackCalled++;
|
||||
if (callbackCalled > 2) {
|
||||
is(false, "Got unexpected attribute changed callback.");
|
||||
} else if (callbackCalled === 2) {
|
||||
runNextTest();
|
||||
}
|
||||
};
|
||||
|
||||
document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
|
||||
|
@ -1,458 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=887541
|
||||
-->
|
||||
<head>
|
||||
<title>Test for event model in web components</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
|
||||
<script>
|
||||
|
||||
var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(SpecialPowers.Ci.nsIEventListenerService);
|
||||
|
||||
function eventListener(e) {
|
||||
eventChain.push(this);
|
||||
}
|
||||
|
||||
function isEventChain(actual, expected, msg) {
|
||||
is(actual.length, expected.length, msg);
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
is(actual[i], expected[i], msg + " at " + i);
|
||||
}
|
||||
|
||||
// Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor
|
||||
if (0 < actual.length) {
|
||||
var chain = els.getEventTargetChainFor(actual[0], true); // Events should be dispatched on actual[0].
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point.
|
||||
*
|
||||
* <div elemOne> ------ <shadow-root shadowOne>
|
||||
* | |
|
||||
* <div elemTwo> <span elemThree>
|
||||
* |
|
||||
* <content elemFour>
|
||||
*/
|
||||
|
||||
var elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
var elemTwo = document.createElement("div");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
var elemThree = document.createElement("span");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
var elemFour = document.createElement("content");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
var shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree.appendChild(elemFour);
|
||||
shadowOne.appendChild(elemThree);
|
||||
elemOne.appendChild(elemTwo);
|
||||
|
||||
var eventChain = [];
|
||||
var customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemTwo.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo.");
|
||||
|
||||
/*
|
||||
* Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points.
|
||||
*
|
||||
* <div elemFive> --- <shadow-root shadowTwo>
|
||||
* | |
|
||||
* <div elemOne> <div elemFour> ----- <shadow-root shadowOne>
|
||||
* | |
|
||||
* <content elemTwo> <p elemSix>
|
||||
* |
|
||||
* <content elemThree>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("content");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("content");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
var elemFour = document.createElement("div");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
var elemFive = document.createElement("div");
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
var elemSix = document.createElement("p");
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
var shadowOne = elemFour.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
var shadowTwo = elemFive.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive.appendChild(elemOne);
|
||||
shadowTwo.appendChild(elemFour);
|
||||
elemFour.appendChild(elemTwo);
|
||||
shadowOne.appendChild(elemSix);
|
||||
elemSix.appendChild(elemThree);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemOne.dispatchEvent(customEvent);
|
||||
is(elemOne.getDestinationInsertionPoints().length, 2, "yes");
|
||||
isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne.");
|
||||
|
||||
/*
|
||||
* Test 3: Test of event dispatch through nested ShadowRoot with content insertion points.
|
||||
*
|
||||
* <div elemOne> ------- <shadow-root shadowOne>
|
||||
* | |
|
||||
* <span elemTwo> <span elemThree> ------------ <shadow-root shadowTwo>
|
||||
* | |
|
||||
* <span elemFour> <content elemSix>
|
||||
* |
|
||||
* <content elemFive>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("span");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("span");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("span");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive = document.createElement("content");
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
elemSix = document.createElement("content");
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemThree.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemOne.appendChild(elemTwo);
|
||||
shadowOne.appendChild(elemThree);
|
||||
elemThree.appendChild(elemFour);
|
||||
elemFour.appendChild(elemFive);
|
||||
shadowTwo.appendChild(elemSix);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemTwo.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo.");
|
||||
|
||||
/*
|
||||
* Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
|
||||
*
|
||||
* <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot)
|
||||
* | | |
|
||||
* <div elemOne> | <div elemSix> -------- <shadow-root shadowOne>
|
||||
* | | |
|
||||
* | <shadow elemFour> <content elemFive>
|
||||
* | |
|
||||
* | <content elemTwo>
|
||||
* |
|
||||
* --- <shadow-root shadowThree> (older ShadowRoot)
|
||||
* | |
|
||||
* | <content elemThree>
|
||||
* |
|
||||
* <div elemEight>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("content");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("content");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("shadow");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive = document.createElement("content");
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
elemSix = document.createElement("div");
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
var elemSeven = document.createElement("div");
|
||||
elemSeven.addEventListener("custom", eventListener);
|
||||
|
||||
var elemEight = document.createElement("div");
|
||||
elemEight.addEventListener("custom", eventListener);
|
||||
|
||||
var shadowThree = elemSeven.createShadowRoot();
|
||||
shadowThree.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemSeven.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemSix.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemSeven.appendChild(elemOne);
|
||||
shadowTwo.appendChild(elemSix);
|
||||
elemSix.appendChild(elemFour);
|
||||
elemFour.appendChild(elemTwo);
|
||||
shadowThree.appendChild(elemEight);
|
||||
shadowThree.appendChild(elemThree);
|
||||
shadowOne.appendChild(elemFive);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemOne.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne.");
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemEight.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight.");
|
||||
|
||||
/*
|
||||
* Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
|
||||
*
|
||||
* <div elemOne> --------- <shadow-root shadowOne>
|
||||
* | | |
|
||||
* | <p elemThree> <span elemFour> ------------------------ <shadow-root shadowTwo>
|
||||
* | | | |
|
||||
* <span elemTwo> | <content select="p" elemFive> <content elemSeven>
|
||||
* |
|
||||
* <content select="span" elemSix>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("span");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("p");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("span");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive = document.createElement("content");
|
||||
elemFive.select = "p";
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
elemSix = document.createElement("content");
|
||||
elemSix.select = "span";
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
elemSeven = document.createElement("content");
|
||||
elemSeven.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemFour.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemOne.appendChild(elemTwo);
|
||||
elemOne.appendChild(elemThree);
|
||||
shadowOne.appendChild(elemFour);
|
||||
elemFour.appendChild(elemSix);
|
||||
elemFour.appendChild(elemFive);
|
||||
shadowTwo.appendChild(elemSeven);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemTwo.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo.");
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemThree.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree.");
|
||||
|
||||
/*
|
||||
* Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
|
||||
*
|
||||
* <div elemOne> --------- <shadow-root shadowOne>;
|
||||
* | | |
|
||||
* | <p elemThree> <span elemFour> ------ <shadow-root shadowTwo>
|
||||
* | | | |
|
||||
* <span elemTwo> <content elemFive> | <content select="p" elemSeven>
|
||||
* |
|
||||
* <content select="span" elemSix>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("span");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("p");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("span");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive = document.createElement("content");
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
elemSix = document.createElement("content");
|
||||
elemSix.select = "span";
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
elemSeven = document.createElement("content");
|
||||
elemSeven.select = "p";
|
||||
elemSeven.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemFour.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemOne.appendChild(elemTwo);
|
||||
elemOne.appendChild(elemThree);
|
||||
shadowOne.appendChild(elemFour);
|
||||
elemFour.appendChild(elemFive);
|
||||
shadowTwo.appendChild(elemSix);
|
||||
shadowTwo.appendChild(elemSeven);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemTwo.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo.");
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemThree.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree.");
|
||||
|
||||
/*
|
||||
* Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
|
||||
*
|
||||
* <div elemOne> --------- <shadow-root shadowOne>
|
||||
* | | |
|
||||
* | <p elemThree> <span elemFour> ------ <shadow-root shadowTwo>
|
||||
* | | |
|
||||
* <span elemTwo> <content elemFive> <span elemEight>
|
||||
* | |
|
||||
* | <content select="p" elemSeven>
|
||||
* |
|
||||
* <content select="span" elemSix>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("span");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("p");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("span");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
elemFive = document.createElement("content");
|
||||
elemFive.addEventListener("custom", eventListener);
|
||||
|
||||
elemSix = document.createElement("content");
|
||||
elemSix.select = "span";
|
||||
elemSix.addEventListener("custom", eventListener);
|
||||
|
||||
elemSeven = document.createElement("content");
|
||||
elemSeven.select = "p";
|
||||
elemSeven.addEventListener("custom", eventListener);
|
||||
|
||||
elemEight = document.createElement("span");
|
||||
elemEight.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemFour.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemOne.appendChild(elemTwo);
|
||||
elemOne.appendChild(elemThree);
|
||||
shadowOne.appendChild(elemFour);
|
||||
elemFour.appendChild(elemFive);
|
||||
shadowTwo.appendChild(elemEight);
|
||||
elemEight.appendChild(elemSix);
|
||||
elemEight.appendChild(elemSeven);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemTwo.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo.");
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemThree.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree.");
|
||||
|
||||
/*
|
||||
* Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
|
||||
*
|
||||
* <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot)
|
||||
* | |
|
||||
* | <div elemFour>
|
||||
* | |
|
||||
* | <shadow elemTwo>
|
||||
* |
|
||||
* --- <shadow-root shadowTwo> (older ShadowRoot)
|
||||
* |
|
||||
* <div elemThree>
|
||||
*/
|
||||
|
||||
elemOne = document.createElement("div");
|
||||
elemOne.addEventListener("custom", eventListener);
|
||||
|
||||
elemTwo = document.createElement("shadow");
|
||||
elemTwo.addEventListener("custom", eventListener);
|
||||
|
||||
elemThree = document.createElement("div");
|
||||
elemThree.addEventListener("custom", eventListener);
|
||||
|
||||
elemFour = document.createElement("div");
|
||||
elemFour.addEventListener("custom", eventListener);
|
||||
|
||||
shadowTwo = elemOne.createShadowRoot();
|
||||
shadowTwo.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne = elemOne.createShadowRoot();
|
||||
shadowOne.addEventListener("custom", eventListener);
|
||||
|
||||
shadowOne.appendChild(elemFour);
|
||||
elemFour.appendChild(elemTwo);
|
||||
shadowTwo.appendChild(elemThree);
|
||||
|
||||
eventChain = [];
|
||||
customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
|
||||
elemThree.dispatchEvent(customEvent);
|
||||
isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,41 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1083587
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1083587</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1083587">Mozilla Bug 1083587</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<div id="host"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1083587 **/
|
||||
var hostInDoc = document.getElementById("host");
|
||||
var shadowOne = hostInDoc.createShadowRoot();
|
||||
is(shadowOne.host, hostInDoc);
|
||||
|
||||
var shadowTwo = hostInDoc.createShadowRoot();
|
||||
is(shadowOne.host, hostInDoc);
|
||||
is(shadowTwo.host, hostInDoc);
|
||||
|
||||
var hostNotInDoc = document.createElement("div");
|
||||
var shadowThree = hostNotInDoc.createShadowRoot();
|
||||
is(shadowThree.host, hostNotInDoc);
|
||||
|
||||
var shadowFour = hostNotInDoc.createShadowRoot();
|
||||
is(shadowThree.host, hostNotInDoc);
|
||||
is(shadowFour.host, hostNotInDoc);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,57 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=806506
|
||||
-->
|
||||
<head>
|
||||
<title>Test for ShadowRoot styles with multiple ShadowRoot on host.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="tall" id="bodydiv"></div>
|
||||
<div id="container"></div>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
|
||||
<script>
|
||||
// Create ShadowRoot.
|
||||
var container = document.getElementById("container");
|
||||
var elem = document.createElement("div");
|
||||
container.appendChild(elem); // Put ShadowRoot host in document.
|
||||
var firstRoot = elem.createShadowRoot();
|
||||
var secondRoot = elem.createShadowRoot();
|
||||
var thirdRoot = elem.createShadowRoot();
|
||||
|
||||
// A style element that will be appended into the ShadowRoot.
|
||||
var firstStyle = document.createElement("style");
|
||||
firstRoot.appendChild(firstStyle);
|
||||
is(firstRoot.styleSheets.length, 1, "firstStyle should be the only style in firstRoot.");
|
||||
is(firstRoot.styleSheets[0].ownerNode, firstStyle, "firstStyle should in the ShadowRoot styleSheets.");
|
||||
|
||||
var secondStyle = document.createElement("style");
|
||||
secondRoot.appendChild(secondStyle);
|
||||
is(secondRoot.styleSheets.length, 1, "secondStyle should be the only style in secondRoot.");
|
||||
is(secondRoot.styleSheets[0].ownerNode, secondStyle, "secondStyle should in the ShadowRoot styleSheets.");
|
||||
|
||||
var thirdStyle = document.createElement("style");
|
||||
thirdRoot.appendChild(thirdStyle);
|
||||
is(thirdRoot.styleSheets.length, 1, "thirdStyle should be the only style in thirdRoot.");
|
||||
is(thirdRoot.styleSheets[0].ownerNode, thirdStyle, "thirdStyle should in the ShadowRoot styleSheets.");
|
||||
|
||||
// Check the stylesheet counts again to make sure that none of the style sheets leaked into the older ShadowRoots.
|
||||
is(firstRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot.");
|
||||
is(secondRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot.");
|
||||
|
||||
// Remove styles and make sure they are removed from the correct ShadowRoot.
|
||||
firstRoot.removeChild(firstStyle);
|
||||
is(firstRoot.styleSheets.length, 0, "firstRoot should no longer have any styles.");
|
||||
|
||||
thirdRoot.removeChild(thirdStyle);
|
||||
is(thirdRoot.styleSheets.length, 0, "thirdRoot should no longer have any styles.");
|
||||
|
||||
secondRoot.removeChild(secondStyle);
|
||||
is(secondRoot.styleSheets.length, 0, "secondRoot should no longer have any styles.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,41 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1083587
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1083587</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1083587">Mozilla Bug 1083587</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<div id="host"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1083587 **/
|
||||
var hostInDoc = document.getElementById("host");
|
||||
var shadowOne = hostInDoc.createShadowRoot();
|
||||
is(shadowOne.olderShadowRoot, null);
|
||||
|
||||
var shadowTwo = hostInDoc.createShadowRoot();
|
||||
is(shadowOne.olderShadowRoot, null);
|
||||
is(shadowTwo.olderShadowRoot, shadowOne);
|
||||
|
||||
var hostNotInDoc = document.createElement("div");
|
||||
var shadowThree = hostNotInDoc.createShadowRoot();
|
||||
is(shadowThree.olderShadowRoot, null);
|
||||
|
||||
var shadowFour = hostNotInDoc.createShadowRoot();
|
||||
is(shadowThree.olderShadowRoot, null);
|
||||
is(shadowFour.olderShadowRoot, shadowThree);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,19 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
|
||||
*
|
||||
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
|
||||
* Opera Software ASA. You are granted a license to use, reproduce
|
||||
* and create derivative works of this document.
|
||||
*/
|
||||
|
||||
[Func="nsDocument::IsWebComponentsEnabled"]
|
||||
interface HTMLShadowElement : HTMLElement
|
||||
{
|
||||
readonly attribute ShadowRoot? olderShadowRoot;
|
||||
};
|
||||
|
@ -20,7 +20,6 @@ interface ShadowRoot : DocumentFragment
|
||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString]
|
||||
attribute DOMString innerHTML;
|
||||
readonly attribute Element host;
|
||||
readonly attribute ShadowRoot? olderShadowRoot;
|
||||
attribute boolean applyAuthorStyles;
|
||||
readonly attribute StyleSheetList styleSheets;
|
||||
};
|
||||
|
@ -520,12 +520,10 @@ partial interface Window {
|
||||
[Func="IsChromeOrXBL"]
|
||||
sequence<DOMString> getRegionalPrefsLocales();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
/**
|
||||
* Getter funcion for IntlUtils, which provides helper functions for
|
||||
* localization.
|
||||
*/
|
||||
[Throws, Func="IsChromeOrXBL"]
|
||||
readonly attribute IntlUtils intlUtils;
|
||||
#endif
|
||||
};
|
||||
|
@ -616,7 +616,6 @@ WEBIDL_FILES = [
|
||||
'HTMLQuoteElement.webidl',
|
||||
'HTMLScriptElement.webidl',
|
||||
'HTMLSelectElement.webidl',
|
||||
'HTMLShadowElement.webidl',
|
||||
'HTMLSourceElement.webidl',
|
||||
'HTMLSpanElement.webidl',
|
||||
'HTMLStyleElement.webidl',
|
||||
@ -656,6 +655,7 @@ WEBIDL_FILES = [
|
||||
'InputEvent.webidl',
|
||||
'InspectorUtils.webidl',
|
||||
'IntersectionObserver.webidl',
|
||||
'IntlUtils.webidl',
|
||||
'IterableIterator.webidl',
|
||||
'KeyAlgorithm.webidl',
|
||||
'KeyboardEvent.webidl',
|
||||
@ -1144,8 +1144,3 @@ if CONFIG['ACCESSIBILITY']:
|
||||
WEBIDL_FILES += [
|
||||
'AccessibleNode.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
WEBIDL_FILES += [
|
||||
'IntlUtils.webidl',
|
||||
]
|
||||
|
@ -720,7 +720,6 @@ static const ElementInfo kElements[eHTMLTag_userdefined] = {
|
||||
GROUP_LEAF),
|
||||
ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
|
||||
ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT),
|
||||
ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
|
||||
ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
|
||||
ELEM(source, false, false, GROUP_PICTURE_CONTENT, GROUP_NONE),
|
||||
ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
|
||||
|
@ -31,7 +31,7 @@ DIRS += [
|
||||
if CONFIG['MOZ_ENABLE_SKIA']:
|
||||
DIRS += ['skia']
|
||||
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY'] and CONFIG['ENABLE_INTL_API']:
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY']:
|
||||
DIRS += ['sfntly/cpp/src']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
|
@ -101,7 +101,7 @@ elif CONFIG['CPU_ARCH'] == 'aarch64' and CONFIG['GNU_CC']:
|
||||
|
||||
DEFINES['SKIA_IMPLEMENTATION'] = 1
|
||||
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY'] and CONFIG['ENABLE_INTL_API']:
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY']:
|
||||
DEFINES['SK_PDF_USE_SFNTLY'] = 1
|
||||
|
||||
if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
|
||||
|
@ -743,7 +743,7 @@ elif CONFIG['CPU_ARCH'] == 'aarch64' and CONFIG['GNU_CC']:
|
||||
|
||||
DEFINES['SKIA_IMPLEMENTATION'] = 1
|
||||
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY'] and CONFIG['ENABLE_INTL_API']:
|
||||
if CONFIG['MOZ_ENABLE_SKIA_PDF_SFNTLY']:
|
||||
DEFINES['SK_PDF_USE_SFNTLY'] = 1
|
||||
|
||||
if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
|
||||
|
@ -86,6 +86,30 @@ ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
|
||||
return imageFlags;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
NotifyImageLoading(ImageURL* aURI)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
RefPtr<ImageURL> uri(aURI);
|
||||
nsCOMPtr<nsIRunnable> ev =
|
||||
NS_NewRunnableFunction("NotifyImageLoading", [uri] () -> void {
|
||||
NotifyImageLoading(uri);
|
||||
});
|
||||
SystemGroup::Dispatch(TaskCategory::Other, ev.forget());
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
|
||||
if (obs) {
|
||||
nsAutoCString spec;
|
||||
aURI->GetSpec(spec);
|
||||
obs->NotifyObservers(nullptr, "image-loading", NS_ConvertUTF8toUTF16(spec).get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */ already_AddRefed<Image>
|
||||
ImageFactory::CreateImage(nsIRequest* aRequest,
|
||||
ProgressTracker* aProgressTracker,
|
||||
@ -102,14 +126,10 @@ ImageFactory::CreateImage(nsIRequest* aRequest,
|
||||
|
||||
#ifdef DEBUG
|
||||
// Record the image load for startup performance testing.
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
|
||||
if (obs) {
|
||||
nsAutoCString spec;
|
||||
aURI->GetSpec(spec);
|
||||
obs->NotifyObservers(nullptr, "image-loading", NS_ConvertUTF8toUTF16(spec).get());
|
||||
}
|
||||
bool match = false;
|
||||
if ((NS_SUCCEEDED(aURI->SchemeIs("resource", &match)) && match) ||
|
||||
(NS_SUCCEEDED(aURI->SchemeIs("chrome", &match)) && match)) {
|
||||
NotifyImageLoading(aURI);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1700,6 +1700,12 @@ RasterImage::NotifyDrawingObservers()
|
||||
return;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
if ((NS_FAILED(mURI->SchemeIs("resource", &match)) || !match) &&
|
||||
(NS_FAILED(mURI->SchemeIs("chrome", &match)) || !match)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the image drawing for startup performance testing.
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
|
||||
|
@ -459,13 +459,26 @@ imgRequest::GetCurrentURI(nsIURI** aURI)
|
||||
}
|
||||
|
||||
bool
|
||||
imgRequest::IsChrome() const
|
||||
imgRequest::IsScheme(const char* aScheme) const
|
||||
{
|
||||
bool isChrome = false;
|
||||
if (NS_WARN_IF(NS_FAILED(mURI->SchemeIs("chrome", &isChrome)))) {
|
||||
MOZ_ASSERT(aScheme);
|
||||
bool isScheme = false;
|
||||
if (NS_WARN_IF(NS_FAILED(mURI->SchemeIs(aScheme, &isScheme)))) {
|
||||
return false;
|
||||
}
|
||||
return isChrome;
|
||||
return isScheme;
|
||||
}
|
||||
|
||||
bool
|
||||
imgRequest::IsChrome() const
|
||||
{
|
||||
return IsScheme("chrome");
|
||||
}
|
||||
|
||||
bool
|
||||
imgRequest::IsData() const
|
||||
{
|
||||
return IsScheme("data");
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -819,13 +832,17 @@ imgRequest::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
|
||||
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// Try to retarget OnDataAvailable to a decode thread.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
||||
// Try to retarget OnDataAvailable to a decode thread. We must process data
|
||||
// URIs synchronously as per the spec however.
|
||||
if (!channel || IsData()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThreadRetargetableRequest> retargetable =
|
||||
do_QueryInterface(aRequest);
|
||||
if (httpChannel && retargetable) {
|
||||
if (retargetable) {
|
||||
nsAutoCString mimeType;
|
||||
nsresult rv = httpChannel->GetContentType(mimeType);
|
||||
nsresult rv = channel->GetContentType(mimeType);
|
||||
if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) {
|
||||
// Retarget OnDataAvailable to the DecodePool's IO thread.
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
|
@ -153,7 +153,9 @@ public:
|
||||
// OK to use on any thread.
|
||||
nsresult GetURI(ImageURL** aURI);
|
||||
nsresult GetCurrentURI(nsIURI** aURI);
|
||||
bool IsScheme(const char* aScheme) const;
|
||||
bool IsChrome() const;
|
||||
bool IsData() const;
|
||||
|
||||
nsresult GetImageErrorCode(void);
|
||||
|
||||
|
@ -556,10 +556,8 @@ GetClass(uint32_t u)
|
||||
/* ZWJ = 42, [ZWJ]*/ CLASS_CHARACTER
|
||||
};
|
||||
|
||||
#if ENABLE_INTL_API
|
||||
static_assert(U_LB_COUNT == mozilla::ArrayLength(sUnicodeLineBreakToClass),
|
||||
"Gecko vs ICU LineBreak class mismatch");
|
||||
#endif
|
||||
|
||||
auto cls = mozilla::unicode::GetLineBreakClass(u);
|
||||
MOZ_ASSERT(cls < mozilla::ArrayLength(sUnicodeLineBreakToClass));
|
||||
|
@ -9,6 +9,10 @@
|
||||
# read from the Unicode Character Database and compiled into multi-level arrays
|
||||
# for efficient lookup.
|
||||
#
|
||||
# Note that for most properties, we now rely on ICU; this tool and the tables
|
||||
# it generates are used only for a couple of properties not readily exposed
|
||||
# via ICU APIs.
|
||||
#
|
||||
# To regenerate the tables in nsUnicodePropertyData.cpp:
|
||||
#
|
||||
# (1) Download the current Unicode data files from
|
||||
@ -17,13 +21,6 @@
|
||||
#
|
||||
# NB: not all the files are actually needed; currently, we require
|
||||
# - UnicodeData.txt
|
||||
# - Scripts.txt
|
||||
# - BidiMirroring.txt
|
||||
# - BidiBrackets.txt
|
||||
# - HangulSyllableType.txt
|
||||
# - LineBreak.txt
|
||||
# - EastAsianWidth.txt
|
||||
# - DerivedCoreProperties.txt
|
||||
# - ReadMe.txt (to record version/date of the UCD)
|
||||
# - Unihan_Variants.txt (from Unihan.zip)
|
||||
# though this may change if we find a need for additional properties.
|
||||
@ -44,7 +41,6 @@
|
||||
# (2) Run this tool using a command line of the form
|
||||
#
|
||||
# perl genUnicodePropertyData.pl \
|
||||
# /path/to/harfbuzz/src \
|
||||
# /path/to/icu/common/unicode \
|
||||
# /path/to/UCD-directory
|
||||
#
|
||||
@ -58,17 +54,15 @@
|
||||
use strict;
|
||||
use List::Util qw(first);
|
||||
|
||||
if ($#ARGV != 2) {
|
||||
if ($#ARGV != 1) {
|
||||
print <<__EOT;
|
||||
# Run this tool using a command line of the form
|
||||
#
|
||||
# perl genUnicodePropertyData.pl \\
|
||||
# /path/to/harfbuzz/src \\
|
||||
# /path/to/icu/common/unicode \\
|
||||
# /path/to/UCD-directory
|
||||
#
|
||||
# where harfbuzz/src is the directory containing harfbuzz .cc and .hh files,
|
||||
# icu/common/unicode is the directory containing ICU 'common' public headers,
|
||||
# where icu/common/unicode is the directory containing ICU 'common' headers,
|
||||
# and UCD-directory is a directory containing the current Unicode Character
|
||||
# Database files (UnicodeData.txt, etc), available from
|
||||
# http://www.unicode.org/Public/UNIDATA/, with additional resources as
|
||||
@ -84,35 +78,11 @@ __EOT
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $HARFBUZZ = $ARGV[0];
|
||||
my $ICU = $ARGV[1];
|
||||
my $UNICODE = $ARGV[2];
|
||||
my $ICU = $ARGV[0];
|
||||
my $UNICODE = $ARGV[1];
|
||||
|
||||
# load HB_Category constants
|
||||
|
||||
my $cc = -1;
|
||||
my %catCode;
|
||||
|
||||
sub readHarfBuzzHeader
|
||||
{
|
||||
my $file = shift;
|
||||
open FH, "< $HARFBUZZ/$file" or die "can't open harfbuzz header $HARFBUZZ/$file\n";
|
||||
while (<FH>) {
|
||||
if (m/HB_UNICODE_GENERAL_CATEGORY_([A-Z_]+)/) {
|
||||
$cc++;
|
||||
$catCode{$1} = $cc;
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
}
|
||||
|
||||
&readHarfBuzzHeader("hb-unicode.h");
|
||||
|
||||
die "didn't find HarfBuzz category codes\n" if $cc == -1;
|
||||
|
||||
my %scriptCode;
|
||||
my @scriptCodeToTag;
|
||||
my @scriptCodeToName;
|
||||
my @idtype;
|
||||
|
||||
my $sc = -1;
|
||||
|
||||
@ -129,8 +99,6 @@ sub readIcuHeader
|
||||
s/SIGN_WRITING/SIGNWRITING/;
|
||||
if (m|USCRIPT_([A-Z_]+)\s*=\s*([0-9]+),\s*/\*\s*([A-Z][a-z]{3})\s*\*/|) {
|
||||
$sc = $2;
|
||||
$scriptCode{$1} = $sc;
|
||||
$scriptCodeToTag[$sc] = $3;
|
||||
$scriptCodeToName[$sc] = $1;
|
||||
}
|
||||
}
|
||||
@ -170,32 +138,6 @@ my %mappedIdType = (
|
||||
"Allowed" => 1
|
||||
);
|
||||
|
||||
my %bidicategoryCode = (
|
||||
"L" => 0, # Left-to-Right
|
||||
"R" => 1, # Right-to-Left
|
||||
"EN" => 2, # European Number
|
||||
"ES" => 3, # European Number Separator
|
||||
"ET" => 4, # European Number Terminator
|
||||
"AN" => 5, # Arabic Number
|
||||
"CS" => 6, # Common Number Separator
|
||||
"B" => 7, # Paragraph Separator
|
||||
"S" => 8, # Segment Separator
|
||||
"WS" => 9, # Whitespace
|
||||
"ON" => 10, # Other Neutrals
|
||||
"LRE" => 11, # Left-to-Right Embedding
|
||||
"LRO" => 12, # Left-to-Right Override
|
||||
"AL" => 13, # Right-to-Left Arabic
|
||||
"RLE" => 14, # Right-to-Left Embedding
|
||||
"RLO" => 15, # Right-to-Left Override
|
||||
"PDF" => 16, # Pop Directional Format
|
||||
"NSM" => 17, # Non-Spacing Mark
|
||||
"BN" => 18, # Boundary Neutral
|
||||
"FSI" => 19, # First Strong Isolate
|
||||
"LRI" => 20, # Left-to-Right Isolate
|
||||
"RLI" => 21, # Right-to-left Isolate
|
||||
"PDI" => 22 # Pop Direcitonal Isolate
|
||||
);
|
||||
|
||||
my %verticalOrientationCode = (
|
||||
'U' => 0, # U - Upright, the same orientation as in the code charts
|
||||
'R' => 1, # R - Rotated 90 degrees clockwise compared to the code charts
|
||||
@ -203,141 +145,18 @@ my %verticalOrientationCode = (
|
||||
'Tr' => 3 # Tr - Transformed typographically, with fallback to Rotated
|
||||
);
|
||||
|
||||
my %lineBreakCode = ( # ordering matches ICU's ULineBreak enum
|
||||
"XX" => 0,
|
||||
"AI" => 1,
|
||||
"AL" => 2,
|
||||
"B2" => 3,
|
||||
"BA" => 4,
|
||||
"BB" => 5,
|
||||
"BK" => 6,
|
||||
"CB" => 7,
|
||||
"CL" => 8,
|
||||
"CM" => 9,
|
||||
"CR" => 10,
|
||||
"EX" => 11,
|
||||
"GL" => 12,
|
||||
"HY" => 13,
|
||||
"ID" => 14,
|
||||
"IN" => 15,
|
||||
"IS" => 16,
|
||||
"LF" => 17,
|
||||
"NS" => 18,
|
||||
"NU" => 19,
|
||||
"OP" => 20,
|
||||
"PO" => 21,
|
||||
"PR" => 22,
|
||||
"QU" => 23,
|
||||
"SA" => 24,
|
||||
"SG" => 25,
|
||||
"SP" => 26,
|
||||
"SY" => 27,
|
||||
"ZW" => 28,
|
||||
"NL" => 29,
|
||||
"WJ" => 30,
|
||||
"H2" => 31,
|
||||
"H3" => 32,
|
||||
"JL" => 33,
|
||||
"JT" => 34,
|
||||
"JV" => 35,
|
||||
"CP" => 36,
|
||||
"CJ" => 37,
|
||||
"HL" => 38,
|
||||
"RI" => 39,
|
||||
"EB" => 40,
|
||||
"EM" => 41,
|
||||
"ZWJ" => 42
|
||||
);
|
||||
|
||||
my %eastAsianWidthCode = (
|
||||
"N" => 0,
|
||||
"A" => 1,
|
||||
"H" => 2,
|
||||
"W" => 3,
|
||||
"F" => 4,
|
||||
"Na" => 5
|
||||
);
|
||||
|
||||
# initialize default properties
|
||||
my @script;
|
||||
my @category;
|
||||
my @combining;
|
||||
my @mirror;
|
||||
my @pairedBracketType;
|
||||
my @hangul;
|
||||
my @casemap;
|
||||
my @idtype;
|
||||
my @numericvalue;
|
||||
my @hanVariant;
|
||||
my @bidicategory;
|
||||
my @fullWidth;
|
||||
my @fullWidthInverse;
|
||||
my @verticalOrientation;
|
||||
my @lineBreak;
|
||||
my @eastAsianWidthFWH;
|
||||
my @defaultIgnorable;
|
||||
for (my $i = 0; $i < 0x110000; ++$i) {
|
||||
$script[$i] = $scriptCode{"UNKNOWN"};
|
||||
$category[$i] = $catCode{"UNASSIGNED"};
|
||||
$combining[$i] = 0;
|
||||
$pairedBracketType[$i] = 0;
|
||||
$casemap[$i] = 0;
|
||||
$idtype[$i] = $mappedIdType{'Restricted'};
|
||||
$numericvalue[$i] = -1;
|
||||
$hanVariant[$i] = 0;
|
||||
$bidicategory[$i] = $bidicategoryCode{"L"};
|
||||
$fullWidth[$i] = 0;
|
||||
$fullWidthInverse[$i] = 0;
|
||||
$verticalOrientation[$i] = 1; # default for unlisted codepoints is 'R'
|
||||
$lineBreak[$i] = $lineBreakCode{"XX"};
|
||||
$eastAsianWidthFWH[$i] = 0;
|
||||
$defaultIgnorable[$i] = 0;
|
||||
}
|
||||
|
||||
# blocks where the default for bidi category is not L
|
||||
for my $i (0x0600..0x07BF, 0x08A0..0x08FF, 0xFB50..0xFDCF, 0xFDF0..0xFDFF, 0xFE70..0xFEFF, 0x1EE00..0x0001EEFF) {
|
||||
$bidicategory[$i] = $bidicategoryCode{"AL"};
|
||||
}
|
||||
for my $i (0x0590..0x05FF, 0x07C0..0x089F, 0xFB1D..0xFB4F, 0x00010800..0x00010FFF, 0x0001E800..0x0001EDFF, 0x0001EF00..0x0001EFFF) {
|
||||
$bidicategory[$i] = $bidicategoryCode{"R"};
|
||||
}
|
||||
for my $i (0x20A0..0x20CF) {
|
||||
$bidicategory[$i] = $bidicategoryCode{"ET"};
|
||||
}
|
||||
|
||||
my %ucd2hb = (
|
||||
'Cc' => 'CONTROL',
|
||||
'Cf' => 'FORMAT',
|
||||
'Cn' => 'UNASSIGNED',
|
||||
'Co' => 'PRIVATE_USE',
|
||||
'Cs' => 'SURROGATE',
|
||||
'Ll' => 'LOWERCASE_LETTER',
|
||||
'Lm' => 'MODIFIER_LETTER',
|
||||
'Lo' => 'OTHER_LETTER',
|
||||
'Lt' => 'TITLECASE_LETTER',
|
||||
'Lu' => 'UPPERCASE_LETTER',
|
||||
'Mc' => 'SPACING_MARK',
|
||||
'Me' => 'ENCLOSING_MARK',
|
||||
'Mn' => 'NON_SPACING_MARK',
|
||||
'Nd' => 'DECIMAL_NUMBER',
|
||||
'Nl' => 'LETTER_NUMBER',
|
||||
'No' => 'OTHER_NUMBER',
|
||||
'Pc' => 'CONNECT_PUNCTUATION',
|
||||
'Pd' => 'DASH_PUNCTUATION',
|
||||
'Pe' => 'CLOSE_PUNCTUATION',
|
||||
'Pf' => 'FINAL_PUNCTUATION',
|
||||
'Pi' => 'INITIAL_PUNCTUATION',
|
||||
'Po' => 'OTHER_PUNCTUATION',
|
||||
'Ps' => 'OPEN_PUNCTUATION',
|
||||
'Sc' => 'CURRENCY_SYMBOL',
|
||||
'Sk' => 'MODIFIER_SYMBOL',
|
||||
'Sm' => 'MATH_SYMBOL',
|
||||
'So' => 'OTHER_SYMBOL',
|
||||
'Zl' => 'LINE_SEPARATOR',
|
||||
'Zp' => 'PARAGRAPH_SEPARATOR',
|
||||
'Zs' => 'SPACE_SEPARATOR'
|
||||
);
|
||||
|
||||
# read ReadMe.txt
|
||||
my @versionInfo;
|
||||
open FH, "< $UNICODE/ReadMe.txt" or die "can't open Unicode ReadMe.txt file\n";
|
||||
@ -347,12 +166,6 @@ while (<FH>) {
|
||||
}
|
||||
close FH;
|
||||
|
||||
my $kTitleToUpper = 0x80000000;
|
||||
my $kUpperToLower = 0x40000000;
|
||||
my $kLowerToTitle = 0x20000000;
|
||||
my $kLowerToUpper = 0x10000000;
|
||||
my $kCaseMapCharMask = 0x001fffff;
|
||||
|
||||
# read UnicodeData.txt
|
||||
open FH, "< $UNICODE/UnicodeData.txt" or die "can't open UCD file UnicodeData.txt\n";
|
||||
while (<FH>) {
|
||||
@ -365,12 +178,6 @@ while (<FH>) {
|
||||
if ($fields[1] =~ /Last/) {
|
||||
my $last = hex "0x$fields[0]";
|
||||
do {
|
||||
$category[$first] = $catCode{$ucd2hb{$fields[2]}};
|
||||
$combining[$first] = $fields[3];
|
||||
$bidicategory[$first] = $bidicategoryCode{$fields[4]};
|
||||
unless (length($fields[7]) == 0) {
|
||||
$numericvalue[$first] = $fields[7];
|
||||
}
|
||||
if ($fields[1] =~ /CJK/) {
|
||||
@hanVariant[$first] = 3;
|
||||
}
|
||||
@ -381,33 +188,6 @@ while (<FH>) {
|
||||
}
|
||||
} else {
|
||||
my $usv = hex "0x$fields[0]";
|
||||
$category[$usv] = $catCode{$ucd2hb{$fields[2]}};
|
||||
$combining[$usv] = $fields[3];
|
||||
my $upper = hex $fields[12];
|
||||
my $lower = hex $fields[13];
|
||||
my $title = hex $fields[14];
|
||||
# we only store one mapping for each character,
|
||||
# but also record what kind of mapping it is
|
||||
if ($upper && $lower) {
|
||||
$casemap[$usv] |= $kTitleToUpper;
|
||||
$casemap[$usv] |= ($usv ^ $upper);
|
||||
}
|
||||
elsif ($lower) {
|
||||
$casemap[$usv] |= $kUpperToLower;
|
||||
$casemap[$usv] |= ($usv ^ $lower);
|
||||
}
|
||||
elsif ($title && ($title != $upper)) {
|
||||
$casemap[$usv] |= $kLowerToTitle;
|
||||
$casemap[$usv] |= ($usv ^ $title);
|
||||
}
|
||||
elsif ($upper) {
|
||||
$casemap[$usv] |= $kLowerToUpper;
|
||||
$casemap[$usv] |= ($usv ^ $upper);
|
||||
}
|
||||
$bidicategory[$usv] = $bidicategoryCode{$fields[4]};
|
||||
unless (length($fields[7]) == 0) {
|
||||
$numericvalue[$usv] = $fields[7];
|
||||
}
|
||||
if ($fields[1] =~ /CJK/) {
|
||||
@hanVariant[$usv] = 3;
|
||||
}
|
||||
@ -427,178 +207,6 @@ while (<FH>) {
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read Scripts.txt
|
||||
open FH, "< $UNICODE/Scripts.txt" or die "can't open UCD file Scripts.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
if (m/([0-9A-F]{4,6})(?:\.\.([0-9A-F]{4,6}))*\s+;\s+([^ ]+)/) {
|
||||
my $script = uc($3);
|
||||
unless (exists $scriptCode{$script}) {
|
||||
warn "unknown ICU script $script";
|
||||
$scriptCode{$script} = $scriptCode{"UNKNOWN"};
|
||||
}
|
||||
$script = $scriptCode{$script};
|
||||
my $start = hex "0x$1";
|
||||
my $end = (defined $2) ? hex "0x$2" : $start;
|
||||
for (my $i = $start; $i <= $end; ++$i) {
|
||||
$script[$i] = $script;
|
||||
}
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read BidiMirroring.txt
|
||||
my @offsets = ();
|
||||
push @offsets, 0;
|
||||
|
||||
open FH, "< $UNICODE/BidiMirroring.txt" or die "can't open UCD file BidiMirroring.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6})/) {
|
||||
my $mirrorOffset = hex("0x$2") - hex("0x$1");
|
||||
my $offsetIndex = first { $offsets[$_] eq $mirrorOffset } 0..$#offsets;
|
||||
if ($offsetIndex == undef) {
|
||||
die "too many offset codes\n" if scalar @offsets == 31;
|
||||
push @offsets, $mirrorOffset;
|
||||
$offsetIndex = $#offsets;
|
||||
}
|
||||
$mirror[hex "0x$1"] = $offsetIndex;
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read BidiBrackets.txt
|
||||
my %pairedBracketTypeCode = (
|
||||
'N' => 0,
|
||||
'O' => 1,
|
||||
'C' => 2
|
||||
);
|
||||
open FH, "< $UNICODE/BidiBrackets.txt" or die "can't open UCD file BidiBrackets.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6});\s*(.)/) {
|
||||
my $mirroredChar = $offsets[$mirror[hex "0x$1"]] + hex "0x$1";
|
||||
die "bidi bracket does not match mirrored char\n" unless $mirroredChar == hex "0x$2";
|
||||
my $pbt = uc($3);
|
||||
warn "unknown Bidi Bracket type" unless exists $pairedBracketTypeCode{$pbt};
|
||||
$pairedBracketType[hex "0x$1"] = $pairedBracketTypeCode{$pbt};
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read HangulSyllableType.txt
|
||||
my %hangulType = (
|
||||
'L' => 0x01,
|
||||
'V' => 0x02,
|
||||
'T' => 0x04,
|
||||
'LV' => 0x03,
|
||||
'LVT' => 0x07
|
||||
);
|
||||
open FH, "< $UNICODE/HangulSyllableType.txt" or die "can't open UCD file HangulSyllableType.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6})(?:\.\.([0-9A-F]{4,6}))*\s*;\s*([^ ]+)/) {
|
||||
my $hangul = uc($3);
|
||||
warn "unknown Hangul syllable type" unless exists $hangulType{$hangul};
|
||||
$hangul = $hangulType{$hangul};
|
||||
my $start = hex "0x$1";
|
||||
my $end = (defined $2) ? hex "0x$2" : $start;
|
||||
for (my $i = $start; $i <= $end; ++$i) {
|
||||
$hangul[$i] = $hangul;
|
||||
}
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read LineBreak.txt
|
||||
open FH, "< $UNICODE/LineBreak.txt" or die "can't open UCD file LineBreak.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6})(?:\.\.([0-9A-F]{4,6}))*\s*;\s*([^ ]+)/) {
|
||||
my $lb = uc($3);
|
||||
warn "unknown LineBreak class" unless exists $lineBreakCode{$lb};
|
||||
$lb = $lineBreakCode{$lb};
|
||||
my $start = hex "0x$1";
|
||||
my $end = (defined $2) ? hex "0x$2" : $start;
|
||||
for (my $i = $start; $i <= $end; ++$i) {
|
||||
$lineBreak[$i] = $lb;
|
||||
}
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read EastAsianWidth.txt
|
||||
open FH, "< $UNICODE/EastAsianWidth.txt" or die "can't open UCD file EastAsianWidth.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6})(?:\.\.([0-9A-F]{4,6}))*\s*;\s*([^ ]+)/) {
|
||||
my $start = hex "0x$1";
|
||||
my $end = (defined $2) ? hex "0x$2" : $start;
|
||||
my $eaw = $3;
|
||||
warn "unknown EastAsianWidth class" unless exists $eastAsianWidthCode{$eaw};
|
||||
my $isFWH = ($eaw =~ m/^[FWH]$/) ? 1 : 0;
|
||||
for (my $i = $start; $i <= $end; ++$i) {
|
||||
$eastAsianWidthFWH[$i] = $isFWH;
|
||||
}
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read DerivedCoreProperties.txt (for Default-Ignorables)
|
||||
open FH, "< $UNICODE/DerivedCoreProperties.txt" or die "can't open UCD file DerivedCoreProperties.txt\n";
|
||||
push @versionInfo, "";
|
||||
while (<FH>) {
|
||||
chomp;
|
||||
push @versionInfo, $_;
|
||||
last if /Date:/;
|
||||
}
|
||||
while (<FH>) {
|
||||
s/#.*//;
|
||||
if (m/([0-9A-F]{4,6})(?:\.\.([0-9A-F]{4,6}))*\s*;\s*Default_Ignorable_Code_Point/) {
|
||||
my $start = hex "0x$1";
|
||||
my $end = (defined $2) ? hex "0x$2" : $start;
|
||||
for (my $i = $start; $i <= $end; ++$i) {
|
||||
$defaultIgnorable[$i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
close FH;
|
||||
|
||||
# read IdentifierStatus.txt
|
||||
open FH, "< $UNICODE/security/IdentifierStatus.txt" or die "can't open UCD file IdentifierStatus.txt\n";
|
||||
push @versionInfo, "";
|
||||
@ -735,50 +343,17 @@ $versionInfo
|
||||
|
||||
__END
|
||||
|
||||
print DATA_TABLES "#if !ENABLE_INTL_API\n";
|
||||
print DATA_TABLES "static const uint32_t sScriptCodeToTag[] = {\n";
|
||||
for (my $i = 0; $i < scalar @scriptCodeToTag; ++$i) {
|
||||
printf DATA_TABLES " HB_TAG('%c','%c','%c','%c')", unpack('cccc', $scriptCodeToTag[$i]);
|
||||
print DATA_TABLES $i < $#scriptCodeToTag ? ",\n" : "\n";
|
||||
}
|
||||
print DATA_TABLES "};\n";
|
||||
print DATA_TABLES "#endif\n\n";
|
||||
|
||||
our $totalData = 0;
|
||||
|
||||
print DATA_TABLES "#if !ENABLE_INTL_API\n";
|
||||
print DATA_TABLES "static const int16_t sMirrorOffsets[] = {\n";
|
||||
for (my $i = 0; $i < scalar @offsets; ++$i) {
|
||||
printf DATA_TABLES " $offsets[$i]";
|
||||
print DATA_TABLES $i < $#offsets ? ",\n" : "\n";
|
||||
}
|
||||
print DATA_TABLES "};\n";
|
||||
print DATA_TABLES "#endif\n\n";
|
||||
|
||||
print HEADER "#pragma pack(1)\n\n";
|
||||
|
||||
sub sprintCharProps1
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("{%d,%d,%d}, ", $mirror[$usv], $hangul[$usv], $combining[$usv]);
|
||||
}
|
||||
my $type = q/
|
||||
struct nsCharProps1 {
|
||||
unsigned char mMirrorOffsetIndex:5;
|
||||
unsigned char mHangulType:3;
|
||||
unsigned char mCombiningClass:8;
|
||||
};
|
||||
/;
|
||||
&genTables("#if !ENABLE_INTL_API", "#endif",
|
||||
"CharProp1", $type, "nsCharProps1", 11, 5, \&sprintCharProps1, 1, 2, 1);
|
||||
|
||||
sub sprintCharProps2_short
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("{%d,%d},",
|
||||
$verticalOrientation[$usv], $idtype[$usv]);
|
||||
}
|
||||
$type = q|
|
||||
my $type = q|
|
||||
struct nsCharProps2 {
|
||||
// Currently only 4 bits are defined here, so 4 more could be added without
|
||||
// affecting the storage requirements for this struct. Or we could pack two
|
||||
@ -787,41 +362,7 @@ struct nsCharProps2 {
|
||||
unsigned char mIdType:2;
|
||||
};
|
||||
|;
|
||||
&genTables("#if ENABLE_INTL_API", "#endif",
|
||||
"CharProp2", $type, "nsCharProps2", 9, 7, \&sprintCharProps2_short, 16, 1, 1);
|
||||
|
||||
sub sprintCharProps2_full
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("{%d,%d,%d,%d,%d,%d,%d,%d,%d,%d},",
|
||||
$script[$usv], $pairedBracketType[$usv],
|
||||
$eastAsianWidthFWH[$usv], $category[$usv],
|
||||
$idtype[$usv], $defaultIgnorable[$usv], $bidicategory[$usv],
|
||||
$verticalOrientation[$usv], $lineBreak[$usv],
|
||||
$numericvalue[$usv]);
|
||||
}
|
||||
$type = q|
|
||||
// This struct currently requires 5 bytes. We try to ensure that whole-byte
|
||||
// fields will not straddle byte boundaries, to optimize access to them.
|
||||
struct nsCharProps2 {
|
||||
unsigned char mScriptCode:8;
|
||||
// -- byte boundary --
|
||||
unsigned char mPairedBracketType:2;
|
||||
unsigned char mEastAsianWidthFWH:1;
|
||||
unsigned char mCategory:5;
|
||||
// -- byte boundary --
|
||||
unsigned char mIdType:2;
|
||||
unsigned char mDefaultIgnorable:1;
|
||||
unsigned char mBidiCategory:5;
|
||||
// -- byte boundary --
|
||||
unsigned char mVertOrient:2;
|
||||
unsigned char mLineBreak:6;
|
||||
// -- byte boundary --
|
||||
signed char mNumericValue; // only 5 bits are actually needed here
|
||||
};
|
||||
|;
|
||||
&genTables("#if !ENABLE_INTL_API", "#endif",
|
||||
"CharProp2", $type, "nsCharProps2", 12, 4, \&sprintCharProps2_full, 16, 5, 1);
|
||||
&genTables("CharProp2", $type, "nsCharProps2", 9, 7, \&sprintCharProps2_short, 16, 1, 1);
|
||||
|
||||
print HEADER "#pragma pack()\n\n";
|
||||
|
||||
@ -837,50 +378,32 @@ sub sprintHanVariants
|
||||
return sprintf("0x%02x,", $val);
|
||||
}
|
||||
## Han Variant data currently unused but may be needed in future, see bug 857481
|
||||
## &genTables("", "", "HanVariant", "", "uint8_t", 9, 7, \&sprintHanVariants, 2, 1, 4);
|
||||
## &genTables("HanVariant", "", "uint8_t", 9, 7, \&sprintHanVariants, 2, 1, 4);
|
||||
|
||||
sub sprintFullWidth
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("0x%04x,", $fullWidth[$usv]);
|
||||
}
|
||||
&genTables("", "", "FullWidth", "", "uint16_t", 10, 6, \&sprintFullWidth, 0, 2, 1);
|
||||
&genTables("FullWidth", "", "uint16_t", 10, 6, \&sprintFullWidth, 0, 2, 1);
|
||||
|
||||
sub sprintFullWidthInverse
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("0x%04x,", $fullWidthInverse[$usv]);
|
||||
}
|
||||
&genTables("", "", "FullWidthInverse", "", "uint16_t", 10, 6, \&sprintFullWidthInverse, 0, 2, 1);
|
||||
|
||||
sub sprintCasemap
|
||||
{
|
||||
my $usv = shift;
|
||||
return sprintf("0x%08x,", $casemap[$usv]);
|
||||
}
|
||||
&genTables("#if !ENABLE_INTL_API", "#endif",
|
||||
"CaseMap", "", "uint32_t", 11, 5, \&sprintCasemap, 1, 4, 1);
|
||||
&genTables("FullWidthInverse", "", "uint16_t", 10, 6, \&sprintFullWidthInverse, 0, 2, 1);
|
||||
|
||||
print STDERR "Total data = $totalData\n";
|
||||
|
||||
printf DATA_TABLES "const uint32_t kTitleToUpper = 0x%08x;\n", $kTitleToUpper;
|
||||
printf DATA_TABLES "const uint32_t kUpperToLower = 0x%08x;\n", $kUpperToLower;
|
||||
printf DATA_TABLES "const uint32_t kLowerToTitle = 0x%08x;\n", $kLowerToTitle;
|
||||
printf DATA_TABLES "const uint32_t kLowerToUpper = 0x%08x;\n", $kLowerToUpper;
|
||||
printf DATA_TABLES "const uint32_t kCaseMapCharMask = 0x%08x;\n\n", $kCaseMapCharMask;
|
||||
|
||||
sub genTables
|
||||
{
|
||||
my ($guardBegin, $guardEnd,
|
||||
$prefix, $typedef, $type, $indexBits, $charBits, $func, $maxPlane, $bytesPerEntry, $charsPerEntry) = @_;
|
||||
my ($prefix, $typedef, $type, $indexBits, $charBits, $func, $maxPlane, $bytesPerEntry, $charsPerEntry) = @_;
|
||||
|
||||
if ($typedef ne '') {
|
||||
print HEADER "$guardBegin\n";
|
||||
print HEADER "$typedef\n";
|
||||
print HEADER "$guardEnd\n\n";
|
||||
}
|
||||
|
||||
print DATA_TABLES "\n$guardBegin\n";
|
||||
print DATA_TABLES "#define k${prefix}MaxPlane $maxPlane\n";
|
||||
print DATA_TABLES "#define k${prefix}IndexBits $indexBits\n";
|
||||
print DATA_TABLES "#define k${prefix}CharBits $charBits\n";
|
||||
@ -949,7 +472,6 @@ sub genTables
|
||||
print DATA_TABLES $i < $#char ? "},\n" : "}\n";
|
||||
}
|
||||
print DATA_TABLES "};\n";
|
||||
print DATA_TABLES "$guardEnd\n";
|
||||
|
||||
my $dataSize = $pmCount * $indexLen * $pmBits/8 +
|
||||
$chCount * $pageLen * $bytesPerEntry +
|
||||
|
@ -13,30 +13,6 @@
|
||||
#define UNICODE_BMP_LIMIT 0x10000
|
||||
#define UNICODE_LIMIT 0x110000
|
||||
|
||||
#ifndef ENABLE_INTL_API
|
||||
static const nsCharProps1&
|
||||
GetCharProps1(uint32_t aCh)
|
||||
{
|
||||
if (aCh < UNICODE_BMP_LIMIT) {
|
||||
return sCharProp1Values[sCharProp1Pages[0][aCh >> kCharProp1CharBits]]
|
||||
[aCh & ((1 << kCharProp1CharBits) - 1)];
|
||||
}
|
||||
if (aCh < (kCharProp1MaxPlane + 1) * 0x10000) {
|
||||
return sCharProp1Values[sCharProp1Pages[sCharProp1Planes[(aCh >> 16) - 1]]
|
||||
[(aCh & 0xffff) >> kCharProp1CharBits]]
|
||||
[aCh & ((1 << kCharProp1CharBits) - 1)];
|
||||
}
|
||||
|
||||
// Default values for unassigned
|
||||
static const nsCharProps1 undefined = {
|
||||
0, // Index to mirrored char offsets
|
||||
0, // Hangul Syllable type
|
||||
0 // Combining class
|
||||
};
|
||||
return undefined;
|
||||
}
|
||||
#endif
|
||||
|
||||
const nsCharProps2&
|
||||
GetCharProps2(uint32_t aCh)
|
||||
{
|
||||
@ -54,21 +30,8 @@ GetCharProps2(uint32_t aCh)
|
||||
// Default values for unassigned
|
||||
using namespace mozilla::unicode;
|
||||
static const nsCharProps2 undefined = {
|
||||
#if ENABLE_INTL_API
|
||||
VERTICAL_ORIENTATION_R,
|
||||
0 // IdentifierType
|
||||
#else
|
||||
uint8_t(Script::UNKNOWN),
|
||||
PAIRED_BRACKET_TYPE_NONE,
|
||||
0, // EastAsianWidthFWH
|
||||
HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED,
|
||||
0, // IdentifierType
|
||||
0, // DefaultIgnorable
|
||||
eCharType_LeftToRight,
|
||||
VERTICAL_ORIENTATION_R,
|
||||
0, // LineBreak
|
||||
-1 // Numeric Value
|
||||
#endif
|
||||
};
|
||||
return undefined;
|
||||
}
|
||||
@ -135,7 +98,6 @@ const nsUGenCategory sDetailedToGeneralCategory[] = {
|
||||
/* SPACE_SEPARATOR */ nsUGenCategory::kSeparator
|
||||
};
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
const hb_unicode_general_category_t sICUtoHBcategory[U_CHAR_CATEGORY_COUNT] = {
|
||||
HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, // U_GENERAL_OTHER_TYPES = 0,
|
||||
HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, // U_UPPERCASE_LETTER = 1,
|
||||
@ -168,142 +130,6 @@ const hb_unicode_general_category_t sICUtoHBcategory[U_CHAR_CATEGORY_COUNT] = {
|
||||
HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, // U_INITIAL_PUNCTUATION = 28,
|
||||
HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, // U_FINAL_PUNCTUATION = 29,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
uint8_t GetGeneralCategory(uint32_t aCh) {
|
||||
return GetCharProps2(aCh).mCategory;
|
||||
}
|
||||
|
||||
nsCharType GetBidiCat(uint32_t aCh) {
|
||||
return nsCharType(GetCharProps2(aCh).mBidiCategory);
|
||||
}
|
||||
|
||||
int8_t GetNumericValue(uint32_t aCh) {
|
||||
return GetCharProps2(aCh).mNumericValue;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetMirroredChar(uint32_t aCh)
|
||||
{
|
||||
return aCh + sMirrorOffsets[GetCharProps1(aCh).mMirrorOffsetIndex];
|
||||
}
|
||||
|
||||
bool
|
||||
HasMirroredChar(uint32_t aCh)
|
||||
{
|
||||
return GetCharProps1(aCh).mMirrorOffsetIndex != 0;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
GetCombiningClass(uint32_t aCh)
|
||||
{
|
||||
return GetCharProps1(aCh).mCombiningClass;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
GetLineBreakClass(uint32_t aCh)
|
||||
{
|
||||
return GetCharProps2(aCh).mLineBreak;
|
||||
}
|
||||
|
||||
Script
|
||||
GetScriptCode(uint32_t aCh)
|
||||
{
|
||||
return Script(GetCharProps2(aCh).mScriptCode);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetScriptTagForCode(Script aScriptCode)
|
||||
{
|
||||
// this will safely return 0 for negative script codes, too :)
|
||||
if (static_cast<uint32_t>(aScriptCode) > ArrayLength(sScriptCodeToTag)) {
|
||||
return 0;
|
||||
}
|
||||
return sScriptCodeToTag[static_cast<uint32_t>(aScriptCode)];
|
||||
}
|
||||
|
||||
PairedBracketType GetPairedBracketType(uint32_t aCh)
|
||||
{
|
||||
return PairedBracketType(GetCharProps2(aCh).mPairedBracketType);
|
||||
}
|
||||
|
||||
uint32_t GetPairedBracket(uint32_t aCh)
|
||||
{
|
||||
return GetPairedBracketType(aCh) != PAIRED_BRACKET_TYPE_NONE
|
||||
? GetMirroredChar(aCh) : aCh;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
GetCaseMapValue(uint32_t aCh)
|
||||
{
|
||||
if (aCh < UNICODE_BMP_LIMIT) {
|
||||
return sCaseMapValues[sCaseMapPages[0][aCh >> kCaseMapCharBits]]
|
||||
[aCh & ((1 << kCaseMapCharBits) - 1)];
|
||||
}
|
||||
if (aCh < (kCaseMapMaxPlane + 1) * 0x10000) {
|
||||
return sCaseMapValues[sCaseMapPages[sCaseMapPlanes[(aCh >> 16) - 1]]
|
||||
[(aCh & 0xffff) >> kCaseMapCharBits]]
|
||||
[aCh & ((1 << kCaseMapCharBits) - 1)];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetUppercase(uint32_t aCh)
|
||||
{
|
||||
uint32_t mapValue = GetCaseMapValue(aCh);
|
||||
if (mapValue & (kLowerToUpper | kTitleToUpper)) {
|
||||
return aCh ^ (mapValue & kCaseMapCharMask);
|
||||
}
|
||||
if (mapValue & kLowerToTitle) {
|
||||
return GetUppercase(aCh ^ (mapValue & kCaseMapCharMask));
|
||||
}
|
||||
return aCh;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetLowercase(uint32_t aCh)
|
||||
{
|
||||
uint32_t mapValue = GetCaseMapValue(aCh);
|
||||
if (mapValue & kUpperToLower) {
|
||||
return aCh ^ (mapValue & kCaseMapCharMask);
|
||||
}
|
||||
if (mapValue & kTitleToUpper) {
|
||||
return GetLowercase(aCh ^ (mapValue & kCaseMapCharMask));
|
||||
}
|
||||
return aCh;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetTitlecaseForLower(uint32_t aCh)
|
||||
{
|
||||
uint32_t mapValue = GetCaseMapValue(aCh);
|
||||
if (mapValue & (kLowerToTitle | kLowerToUpper)) {
|
||||
return aCh ^ (mapValue & kCaseMapCharMask);
|
||||
}
|
||||
return aCh;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetTitlecaseForAll(uint32_t aCh)
|
||||
{
|
||||
uint32_t mapValue = GetCaseMapValue(aCh);
|
||||
if (mapValue & (kLowerToTitle | kLowerToUpper)) {
|
||||
return aCh ^ (mapValue & kCaseMapCharMask);
|
||||
}
|
||||
if (mapValue & kUpperToLower) {
|
||||
return GetTitlecaseForLower(aCh ^ (mapValue & kCaseMapCharMask));
|
||||
}
|
||||
return aCh;
|
||||
}
|
||||
|
||||
bool IsEastAsianWidthFWH(uint32_t aCh)
|
||||
{
|
||||
return GetCharProps2(aCh).mEastAsianWidthFWH;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define DEFINE_BMP_1PLANE_MAPPING_GET_FUNC(prefix_) \
|
||||
uint32_t Get##prefix_(uint32_t aCh) \
|
||||
@ -332,31 +158,18 @@ IsClusterExtender(uint32_t aCh, uint8_t aCategory)
|
||||
}
|
||||
|
||||
enum HSType {
|
||||
#if ENABLE_INTL_API
|
||||
HST_NONE = U_HST_NOT_APPLICABLE,
|
||||
HST_L = U_HST_LEADING_JAMO,
|
||||
HST_V = U_HST_VOWEL_JAMO,
|
||||
HST_T = U_HST_TRAILING_JAMO,
|
||||
HST_LV = U_HST_LV_SYLLABLE,
|
||||
HST_LVT = U_HST_LVT_SYLLABLE
|
||||
#else
|
||||
HST_NONE = 0x00,
|
||||
HST_L = 0x01,
|
||||
HST_V = 0x02,
|
||||
HST_T = 0x04,
|
||||
HST_LV = 0x03,
|
||||
HST_LVT = 0x07
|
||||
#endif
|
||||
};
|
||||
|
||||
static HSType
|
||||
GetHangulSyllableType(uint32_t aCh)
|
||||
{
|
||||
#if ENABLE_INTL_API
|
||||
return HSType(u_getIntPropertyValue(aCh, UCHAR_HANGUL_SYLLABLE_TYPE));
|
||||
#else
|
||||
return HSType(GetCharProps1(aCh).mHangulType);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -12,10 +12,8 @@
|
||||
#include "nsUnicodeScriptCodes.h"
|
||||
#include "harfbuzz/hb.h"
|
||||
|
||||
#if ENABLE_INTL_API
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/uscript.h"
|
||||
#endif
|
||||
|
||||
const nsCharProps2& GetCharProps2(uint32_t aCh);
|
||||
|
||||
@ -48,8 +46,6 @@ enum IdentifierType {
|
||||
IDTYPE_ALLOWED = 1,
|
||||
};
|
||||
|
||||
#if ENABLE_INTL_API // ICU is available, so simply forward to its API
|
||||
|
||||
extern const hb_unicode_general_category_t sICUtoHBcategory[];
|
||||
|
||||
inline uint32_t
|
||||
@ -176,63 +172,6 @@ IsDefaultIgnorable(uint32_t aCh)
|
||||
return u_hasBinaryProperty(aCh, UCHAR_DEFAULT_IGNORABLE_CODE_POINT);
|
||||
}
|
||||
|
||||
#else // not ENABLE_INTL_API
|
||||
|
||||
// Return whether the char has a mirrored-pair counterpart.
|
||||
uint32_t GetMirroredChar(uint32_t aCh);
|
||||
|
||||
bool HasMirroredChar(uint32_t aChr);
|
||||
|
||||
uint8_t GetCombiningClass(uint32_t aCh);
|
||||
|
||||
// returns the detailed General Category in terms of HB_UNICODE_* values
|
||||
uint8_t GetGeneralCategory(uint32_t aCh);
|
||||
|
||||
nsCharType GetBidiCat(uint32_t aCh);
|
||||
|
||||
uint8_t GetLineBreakClass(uint32_t aCh);
|
||||
|
||||
Script GetScriptCode(uint32_t aCh);
|
||||
|
||||
// We don't support ScriptExtensions.txt data when building without ICU.
|
||||
// The most important cases will still be handled in gfxScriptItemizer
|
||||
// by checking IsClusterExtender to avoid breaking script runs within
|
||||
// a cluster.
|
||||
inline bool
|
||||
HasScript(uint32_t aCh, Script aScript)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t GetScriptTagForCode(Script aScriptCode);
|
||||
|
||||
PairedBracketType GetPairedBracketType(uint32_t aCh);
|
||||
uint32_t GetPairedBracket(uint32_t aCh);
|
||||
|
||||
/**
|
||||
* Return the numeric value of the character. The value returned is the value
|
||||
* of the Numeric_Value in field 7 of the UCD, or -1 if field 7 is empty.
|
||||
* To restrict to decimal digits, the caller should also check whether
|
||||
* GetGeneralCategory returns HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER
|
||||
*/
|
||||
int8_t GetNumericValue(uint32_t aCh);
|
||||
|
||||
uint32_t GetUppercase(uint32_t aCh);
|
||||
uint32_t GetLowercase(uint32_t aCh);
|
||||
uint32_t GetTitlecaseForLower(uint32_t aCh); // maps LC to titlecase, UC unchanged
|
||||
uint32_t GetTitlecaseForAll(uint32_t aCh); // maps both UC and LC to titlecase
|
||||
|
||||
// Return whether the char has EastAsianWidth class F or W or H.
|
||||
bool IsEastAsianWidthFWH(uint32_t aCh);
|
||||
|
||||
// Return whether the char is default-ignorable.
|
||||
inline bool IsDefaultIgnorable(uint32_t aCh)
|
||||
{
|
||||
return GetCharProps2(aCh).mDefaultIgnorable;
|
||||
}
|
||||
|
||||
#endif // !ENABLE_INTL_API
|
||||
|
||||
// returns the simplified Gen Category as defined in nsUGenCategory
|
||||
inline nsUGenCategory GetGenCategory(uint32_t aCh) {
|
||||
return sDetailedToGeneralCategory[GetGeneralCategory(aCh)];
|
||||
|
File diff suppressed because one or more lines are too long
@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Created on Wed Jun 28 17:08:23 2017 from UCD data files with version info:
|
||||
* Created on Thu Sep 21 20:35:51 2017 from UCD data files with version info:
|
||||
*
|
||||
|
||||
# Unicode Character Database
|
||||
@ -29,27 +29,6 @@
|
||||
This directory contains the final data files
|
||||
for the Unicode Character Database, for Version 10.0.0 of the Unicode Standard.
|
||||
|
||||
# Scripts-10.0.0.txt
|
||||
# Date: 2017-03-11, 06:40:37 GMT
|
||||
|
||||
# BidiMirroring-10.0.0.txt
|
||||
# Date: 2017-04-12, 17:30:00 GMT [KW, LI]
|
||||
|
||||
# BidiBrackets-10.0.0.txt
|
||||
# Date: 2017-04-12, 17:30:00 GMT [AG, LI, KW]
|
||||
|
||||
# HangulSyllableType-10.0.0.txt
|
||||
# Date: 2017-02-14, 04:26:11 GMT
|
||||
|
||||
# LineBreak-10.0.0.txt
|
||||
# Date: 2017-03-08, 02:00:00 GMT [KW, LI]
|
||||
|
||||
# EastAsianWidth-10.0.0.txt
|
||||
# Date: 2017-03-08, 02:00:00 GMT [KW, LI]
|
||||
|
||||
# DerivedCoreProperties-10.0.0.txt
|
||||
# Date: 2017-03-19, 00:05:15 GMT
|
||||
|
||||
# IdentifierStatus.txt
|
||||
# Date: 2017-04-08, 16:13:41 GMT
|
||||
|
||||
@ -69,17 +48,6 @@ for the Unicode Character Database, for Version 10.0.0 of the Unicode Standard.
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
|
||||
struct nsCharProps1 {
|
||||
unsigned char mMirrorOffsetIndex:5;
|
||||
unsigned char mHangulType:3;
|
||||
unsigned char mCombiningClass:8;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLE_INTL_API
|
||||
|
||||
struct nsCharProps2 {
|
||||
// Currently only 4 bits are defined here, so 4 more could be added without
|
||||
@ -89,31 +57,6 @@ struct nsCharProps2 {
|
||||
unsigned char mIdType:2;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !ENABLE_INTL_API
|
||||
|
||||
// This struct currently requires 5 bytes. We try to ensure that whole-byte
|
||||
// fields will not straddle byte boundaries, to optimize access to them.
|
||||
struct nsCharProps2 {
|
||||
unsigned char mScriptCode:8;
|
||||
// -- byte boundary --
|
||||
unsigned char mPairedBracketType:2;
|
||||
unsigned char mEastAsianWidthFWH:1;
|
||||
unsigned char mCategory:5;
|
||||
// -- byte boundary --
|
||||
unsigned char mIdType:2;
|
||||
unsigned char mDefaultIgnorable:1;
|
||||
unsigned char mBidiCategory:5;
|
||||
// -- byte boundary --
|
||||
unsigned char mVertOrient:2;
|
||||
unsigned char mLineBreak:6;
|
||||
// -- byte boundary --
|
||||
signed char mNumericValue; // only 5 bits are actually needed here
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#pragma pack()
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -3,6 +3,7 @@
|
||||
# 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('/media/webrtc/webrtc.mozbuild')
|
||||
|
||||
EXPORTS += [
|
||||
'nsIIPCBackgroundChildCreateCallback.h',
|
||||
@ -51,7 +52,6 @@ if CONFIG['FUZZING'] == '1':
|
||||
SOURCES += ['Faulty.cpp']
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['WEBRTC_WIN'] = True
|
||||
EXPORTS.mozilla.ipc += [
|
||||
'Transport_win.h',
|
||||
]
|
||||
@ -61,7 +61,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
'WindowsMessageLoop.cpp',
|
||||
]
|
||||
else:
|
||||
DEFINES['WEBRTC_POSIX'] = True
|
||||
EXPORTS.mozilla.ipc += [
|
||||
'Transport_posix.h',
|
||||
]
|
||||
|
@ -150,6 +150,8 @@ class Assembler : public AssemblerShared
|
||||
static void ToggleToCmp(CodeLocationLabel) { MOZ_CRASH(); }
|
||||
static void ToggleCall(CodeLocationLabel, bool) { MOZ_CRASH(); }
|
||||
|
||||
static void Bind(uint8_t*, CodeOffset, CodeOffset) { MOZ_CRASH(); }
|
||||
|
||||
static uintptr_t GetPointer(uint8_t*) { MOZ_CRASH(); }
|
||||
|
||||
static bool HasRoundInstruction(RoundingMode) { return false; }
|
||||
@ -187,7 +189,9 @@ class MacroAssemblerNone : public Assembler
|
||||
size_t numCodeLabels() const { MOZ_CRASH(); }
|
||||
CodeLabel codeLabel(size_t) { MOZ_CRASH(); }
|
||||
|
||||
bool reserve(size_t size) { MOZ_CRASH(); }
|
||||
bool appendRawCode(const uint8_t* code, size_t numBytes) { MOZ_CRASH(); }
|
||||
bool swapBuffer(wasm::Bytes& bytes) { MOZ_CRASH(); }
|
||||
|
||||
void trace(JSTracer*) { MOZ_CRASH(); }
|
||||
static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
|
||||
|
@ -208,15 +208,15 @@ ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
|
||||
(parent->StyleContext()->IsInheritingAnonBox() &&
|
||||
parent->GetContent() == aParent->GetContent()));
|
||||
|
||||
// Now "this" is a ServoRestyleState for aParent, so if parent is not a prev
|
||||
// Now "this" is a ServoRestyleState for aParent, so if parent is not a next
|
||||
// continuation (possibly across ib splits) of aParent we need a new
|
||||
// ServoRestyleState for the kid.
|
||||
Maybe<ServoRestyleState> parentRestyleState;
|
||||
nsIFrame* parentForRestyle = aParent;
|
||||
if (nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) != aParent) {
|
||||
parentRestyleState.emplace(*parent, *this, nsChangeHint_Empty,
|
||||
nsIFrame* parentForRestyle =
|
||||
nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
|
||||
if (parentForRestyle != aParent) {
|
||||
parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
|
||||
Type::InFlow);
|
||||
parentForRestyle = parent;
|
||||
}
|
||||
ServoRestyleState& curRestyleState =
|
||||
parentRestyleState ? *parentRestyleState : *this;
|
||||
|
11
layout/base/crashtests/1401739.html
Normal file
11
layout/base/crashtests/1401739.html
Normal file
@ -0,0 +1,11 @@
|
||||
<style>
|
||||
html { -moz-column-width:0 }
|
||||
</style>
|
||||
<script>
|
||||
document.documentElement.appendChild(document.createElement("option"))
|
||||
document.documentElement.appendChild(document.createElement("th"))
|
||||
document.styleSheets[0].insertRule("c{", 0)
|
||||
document.documentElement.getBoundingClientRect()
|
||||
document.styleSheets[0].deleteRule(0)
|
||||
</script>
|
||||
<body></body>
|
@ -503,4 +503,5 @@ load 1397398-3.html
|
||||
load 1398500.html
|
||||
load 1400438-1.html
|
||||
load 1400599-1.html
|
||||
load 1401739.html
|
||||
load 1401840.html
|
||||
|
@ -45,8 +45,6 @@ span { color:blue; }
|
||||
<span style="color:green">R</span>
|
||||
<div></div>
|
||||
<b style="color:green">V</b>
|
||||
<b style="color:green">W</b>
|
||||
<b style="color:green">X</b>
|
||||
<!-- <b style="color:green">Y</b> -->
|
||||
</body>
|
||||
</html>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user