Merge mozilla-central to mozilla-inbound. CLOSED TREE

This commit is contained in:
Csoregi Natalia 2018-12-11 11:40:09 +02:00
Родитель 063b476108 adcc169dcf
Коммит ea7eda334f
19 изменённых файлов: 395 добавлений и 63 удалений

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

@ -412,7 +412,8 @@ Tools.accessibility = {
label: l10n("accessibility.label"),
panelLabel: l10n("accessibility.panelLabel"),
get tooltip() {
return l10n("accessibility.tooltip2");
return l10n("accessibility.tooltip3",
"Shift+" + functionkey(l10n("accessibility.commandkey")));
},
inMenu: true,

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

@ -262,11 +262,11 @@ accessibility.panelLabel=Accessibility Panel
# Used for the menuitem in the tool menu
accessibility.accesskey=y
# LOCALIZATION NOTE (accessibility.tooltip2):
# LOCALIZATION NOTE (accessibility.tooltip3):
# This string is displayed in the tooltip of the tab when the Accessibility is
# displayed inside the developer tools window.
# Keyboard shortcut for Accessibility panel will be shown inside the brackets.
accessibility.tooltip2=Accessibility
accessibility.tooltip3=Accessibility (%S)
# LOCALIZATION NOTE (application.label):
# This string is displayed in the title of the tab when the Application panel

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

@ -342,7 +342,7 @@ RootActor.prototype = {
let targetActor;
try {
targetActor = await tabList.getTab(options);
targetActor = await tabList.getTab(options, { forceUnzombify: true });
} catch (error) {
if (error.error) {
// Pipe expected errors as-is to the client

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

@ -51,6 +51,9 @@ FrameTargetActorProxy.prototype = {
}
this.exit();
};
await this._unzombifyIfNeeded();
const connect = DebuggerServer.connectToFrame(this._conn, this._browser, onDestroy);
const form = await connect;
@ -101,6 +104,11 @@ FrameTargetActorProxy.prototype = {
return this.connect();
}
// This function may be called if we are inspecting tabs and the actor proxy
// has already been generated. In that case we need to unzombify tabs.
// If we are not inspecting tabs then this will be a no-op.
await this._unzombifyIfNeeded();
const form = await new Promise(resolve => {
const onFormUpdate = msg => {
// There may be more than one FrameTargetActor up and running
@ -124,11 +132,23 @@ FrameTargetActorProxy.prototype = {
return this;
},
_isZombieTab() {
// Check for Firefox on Android.
if (this._browser.hasAttribute("pending")) {
return true;
}
// Check for other.
const tabbrowser = this._tabbrowser;
const tab = tabbrowser ? tabbrowser.getTabForBrowser(this._browser) : null;
return tab && tab.hasAttribute && tab.hasAttribute("pending");
},
/**
* If we don't have a title from the content side because it's a zombie tab, try to find
* it on the chrome side.
*/
get title() {
_getZombieTabTitle() {
// On Fennec, we can check the session store data for zombie tabs
if (this._browser && this._browser.__SS_restore) {
const sessionStore = this._browser.__SS_data;
@ -145,14 +165,15 @@ FrameTargetActorProxy.prototype = {
return tab.label;
}
}
return "";
return null;
},
/**
* If we don't have a url from the content side because it's a zombie tab, try to find
* it on the chrome side.
*/
get url() {
_getZombieTabUrl() {
// On Fennec, we can check the session store data for zombie tabs
if (this._browser && this._browser.__SS_restore) {
const sessionStore = this._browser.__SS_data;
@ -160,19 +181,38 @@ FrameTargetActorProxy.prototype = {
const entry = sessionStore.entries[sessionStore.index - 1];
return entry.url;
}
return null;
},
async _unzombifyIfNeeded() {
if (!this.options.forceUnzombify || !this._isZombieTab()) {
return;
}
// Unzombify if the browser is a zombie tab on Android.
const browserApp = this._browser ? this._browser.ownerGlobal.BrowserApp : null;
if (browserApp) {
// Wait until the content is loaded so as to ensure that the inspector actor refers
// to same document.
const waitForUnzombify = new Promise(resolve => {
this._browser.addEventListener("DOMContentLoaded", resolve,
{ capture: true, once: true });
});
const tab = browserApp.getTabForBrowser(this._browser);
tab.unzombify();
await waitForUnzombify;
}
},
form() {
const form = Object.assign({}, this._form);
// In some cases, the title and url fields might be empty. Zombie tabs (not yet
// restored) are a good example. In such cases, try to look up values for these
// fields using other data in the parent process.
if (!form.title) {
form.title = this.title;
}
if (!form.url) {
form.url = this.url;
// In case of Zombie tabs (not yet restored), look up title and url from other.
if (this._isZombieTab()) {
form.title = this._getZombieTabTitle() || form.title;
form.url = this._getZombieTabUrl() || form.url;
}
return form;

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

@ -20,6 +20,7 @@ loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker/worker-list", true);
loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
/**
* Browser-specific actors.
@ -143,11 +144,11 @@ exports.createRootActor = function createRootActor(connection) {
* - Title changes:
*
* For tabs living in the child process, we listen for DOMTitleChange message
* via the top-level window's message manager. Doing this also allows listening
* for title changes on Fennec.
* via the top-level window's message manager.
* But as these messages aren't sent for tabs loaded in the parent process,
* we also listen for TabAttrModified event, which is fired only on Firefox
* desktop.
* Also, we listen DOMTitleChange event on Android document.
*/
function BrowserTabList(connection) {
this._connection = connection;
@ -191,6 +192,8 @@ function BrowserTabList(connection) {
/* True if we're testing, and should throw if consistency checks fail. */
this._testing = false;
this._onAndroidDocumentEvent = this._onAndroidDocumentEvent.bind(this);
}
BrowserTabList.prototype.constructor = BrowserTabList;
@ -310,7 +313,8 @@ BrowserTabList.prototype._getActorForBrowser = function(browser, browserActorOpt
return actor.connect();
};
BrowserTabList.prototype.getTab = function({ outerWindowID, tabId }) {
BrowserTabList.prototype.getTab = function({ outerWindowID, tabId },
browserActorOptions) {
if (typeof outerWindowID == "number") {
// First look for in-process frames with this ID
const window = Services.wm.getOuterWindowWithId(outerWindowID);
@ -324,14 +328,14 @@ BrowserTabList.prototype.getTab = function({ outerWindowID, tabId }) {
if (window) {
const iframe = window.windowUtils.containerElement;
if (iframe) {
return this._getActorForBrowser(iframe);
return this._getActorForBrowser(iframe, browserActorOptions);
}
}
// Then also look on registered <xul:browsers> when using outerWindowID for
// OOP tabs
for (const browser of this._getBrowsers()) {
if (browser.outerWindowID == outerWindowID) {
return this._getActorForBrowser(browser);
return this._getActorForBrowser(browser, browserActorOptions);
}
}
return Promise.reject({
@ -344,7 +348,7 @@ BrowserTabList.prototype.getTab = function({ outerWindowID, tabId }) {
if (browser.frameLoader &&
browser.frameLoader.tabParent &&
browser.frameLoader.tabParent.tabId === tabId) {
return this._getActorForBrowser(browser);
return this._getActorForBrowser(browser, browserActorOptions);
}
}
return Promise.reject({
@ -357,7 +361,7 @@ BrowserTabList.prototype.getTab = function({ outerWindowID, tabId }) {
DebuggerServer.chromeWindowType);
if (topXULWindow) {
const selectedBrowser = this._getSelectedBrowser(topXULWindow);
return this._getActorForBrowser(selectedBrowser);
return this._getActorForBrowser(selectedBrowser, browserActorOptions);
}
return Promise.reject({
error: "noTab",
@ -453,11 +457,28 @@ BrowserTabList.prototype._checkListening = function() {
/*
* We also listen for title changed from the child process.
* This allows listening for title changes from Fennec and OOP tabs in Fx.
* This allows listening for title changes from OOP tabs.
* OOP tabs are running browser-child.js frame script which sends DOMTitleChanged
* events through the message manager.
*/
this._listenForMessagesIf(this._onListChanged && this._mustNotify,
"_listeningForTitleChange",
["DOMTitleChanged"]);
/*
* We also listen for title changed event on Android document.
* Android document events are used for single process Gecko View and Firefox for
* Android. They do no execute browser-child.js because of single process, instead
* DOMTitleChanged events are emitted on the top level document.
* Also, Multi process Gecko View is not covered by here since that receives title
* updates via DOMTitleChanged messages.
*/
if (AppConstants.platform === "android") {
this._listenForEventsIf(this._onListChanged && this._mustNotify,
"_listeningForAndroidDocument",
["DOMTitleChanged"],
this._onAndroidDocumentEvent);
}
};
/*
@ -472,12 +493,12 @@ BrowserTabList.prototype._checkListening = function() {
* An array of event names.
*/
BrowserTabList.prototype._listenForEventsIf =
function(shouldListen, guard, eventNames) {
function(shouldListen, guard, eventNames, listener = this) {
if (!shouldListen !== !this[guard]) {
const op = shouldListen ? "addEventListener" : "removeEventListener";
for (const win of Services.wm.getEnumerator(DebuggerServer.chromeWindowType)) {
for (const name of eventNames) {
win[op](name, this, false);
win[op](name, listener, false);
}
}
this[guard] = shouldListen;
@ -487,12 +508,12 @@ BrowserTabList.prototype._listenForEventsIf =
/*
* Add or remove message listeners for all XUL windows.
*
* @param aShouldListen boolean
* @param shouldListen boolean
* True if we should add message listeners; false if we should remove them.
* @param aGuard string
* @param guard string
* The name of a guard property of 'this', indicating whether we're
* already listening for those messages.
* @param aMessageNames array of strings
* @param messageNames array of strings
* An array of message names.
*/
BrowserTabList.prototype._listenForMessagesIf =
@ -508,6 +529,20 @@ BrowserTabList.prototype._listenForMessagesIf =
}
};
/*
* This function assumes to be used as a event listener for Android document.
*/
BrowserTabList.prototype._onAndroidDocumentEvent = function(event) {
switch (event.type) {
case "DOMTitleChanged": {
const win = event.currentTarget.ownerGlobal;
const browser = win.BrowserApp.getBrowserForDocument(event.target);
this._onDOMTitleChanged(browser);
break;
}
}
};
/**
* Implement nsIMessageListener.
*/
@ -516,16 +551,24 @@ BrowserTabList.prototype.receiveMessage = DevToolsUtils.makeInfallible(
const browser = message.target;
switch (message.name) {
case "DOMTitleChanged": {
const actor = this._actorByBrowser.get(browser);
if (actor) {
this._notifyListChanged();
this._checkListening();
}
this._onDOMTitleChanged(browser);
break;
}
}
});
/**
* Handle "DOMTitleChanged" event.
*/
BrowserTabList.prototype._onDOMTitleChanged = DevToolsUtils.makeInfallible(
function(browser) {
const actor = this._actorByBrowser.get(browser);
if (actor) {
this._notifyListChanged();
this._checkListening();
}
});
/**
* Implement nsIDOMEventListener.
*/

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

@ -170,6 +170,12 @@ XPCOMUtils.defineLazyGetter(this, "KeyShortcuts", function() {
shortcut: KeyShortcutsBundle.GetStringFromName("dom.commandkey"),
modifiers,
},
// Key for opening the Accessibility Panel
{
toolId: "accessibility",
shortcut: KeyShortcutsBundle.GetStringFromName("accessibility.commandkey"),
modifiers: "shift",
},
];
if (isMac) {

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

@ -61,3 +61,7 @@ storage.commandkey=VK_F9
# LOCALIZATION NOTE (dom.commandkey):
# Key pressed to open a toolbox with the DOM panel selected
dom.commandkey=W
# LOCALIZATION NOTE (accessibility.commandkey):
# Key pressed to open a toolbox with the accessibility panel selected
accessibility.commandkey=VK_F10

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

@ -4509,7 +4509,12 @@ void nsDisplayBackgroundColor::ApplyOpacity(nsDisplayListBuilder* aBuilder,
IntersectClip(aBuilder, aClip, false);
}
bool nsDisplayBackgroundColor::CanApplyOpacity() const { return true; }
bool nsDisplayBackgroundColor::CanApplyOpacity() const {
// Don't apply opacity if the background color is animated since the color is
// going to be changed on the compositor.
return !EffectCompositor::HasAnimationsForCompositor(
mFrame, eCSSProperty_background_color);
}
LayerState nsDisplayBackgroundColor::GetLayerState(
nsDisplayListBuilder* aBuilder, LayerManager* aManager,

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

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<style>
#target {
background-color: green;
width: 100px;
height: 100px;
opacity: 0.5;
}
</style>
<div id="target"></div>
</html>

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

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html class="reftest-wait reftest-no-flush">
<style>
#target {
background-color: green;
width: 100px;
height: 100px;
transition: background-color 100s steps(1, end);
opacity: 0.5;
}
#target.hover {
background-color: white;
}
</style>
<div id="target"></div>
<script>
document.addEventListener('MozReftestInvalidate', () => {
target.className = 'hover';
target.addEventListener('transitionstart', () => {
document.documentElement.classList.remove('reftest-wait');
});
}, false);
</script>
</html>

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

@ -1,3 +1,4 @@
fuzzy-if((OSX&&!webrender)||(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!gpuProcess&&layersGPUAccelerated),1-1,10000-10000) == background-color-with-opacity.html background-color-with-opacity-ref.html
== transitions-inline-already-wrapped-1.html transitions-inline-ref.html
== transitions-inline-already-wrapped-2.html transitions-inline-ref.html
== transitions-inline-rewrap-1.html transitions-inline-ref.html

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

@ -53,6 +53,10 @@ class GeckoViewTab extends GeckoViewModule {
getBrowserForOuterWindowID: function(aID) {
return this.browser;
},
getBrowserForDocument: function(aDocument) {
return this.selectedBrowser;
},
};
}
}

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

@ -134,8 +134,13 @@ pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParsedCaseSensitivity {
CaseSensitive,
// 's' was specified.
ExplicitCaseSensitive,
// 'i' was specified.
AsciiCaseInsensitive,
// No flags were specified and HTML says this is a case-sensitive attribute.
CaseSensitive,
// No flags were specified and HTML says this is a case-insensitive attribute.
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
}
@ -150,7 +155,10 @@ impl ParsedCaseSensitivity {
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::CaseSensitive => CaseSensitivity::CaseSensitive,
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::ExplicitCaseSensitive => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
}
}

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

@ -1223,6 +1223,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
}
dest.write_char(']')
},
@ -1301,6 +1302,7 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" i")?,
}
},
}
@ -1711,24 +1713,15 @@ where
AttrSelectorOperator::Suffix => value.is_empty(),
};
let mut case_sensitivity = parse_attribute_flags(input)?;
let attribute_flags = parse_attribute_flags(input)?;
let value = value.as_ref().into();
let local_name_lower;
let local_name_is_ascii_lowercase;
let case_sensitivity;
{
let local_name_lower_cow = to_ascii_lowercase(&local_name);
if let ParsedCaseSensitivity::CaseSensitive = case_sensitivity {
if namespace.is_none() && include!(concat!(
env!("OUT_DIR"),
"/ascii_case_insensitive_html_attributes.rs"
))
.contains(&*local_name_lower_cow)
{
case_sensitivity =
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
}
}
case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
local_name_lower = local_name_lower_cow.as_ref().into();
local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..));
}
@ -1758,20 +1751,67 @@ where
}
}
/// An attribute selector can have 's' or 'i' as flags, or no flags at all.
enum AttributeFlags {
// Matching should be case-sensitive ('s' flag).
CaseSensitive,
// Matching should be case-insensitive ('i' flag).
AsciiCaseInsensitive,
// No flags. Matching behavior depends on the name of the attribute.
CaseSensitivityDependsOnName
}
impl AttributeFlags {
fn to_case_sensitivity(
self,
local_name: &str,
have_namespace: bool,
) -> ParsedCaseSensitivity {
match self {
AttributeFlags::CaseSensitive =>
ParsedCaseSensitivity::ExplicitCaseSensitive,
AttributeFlags::AsciiCaseInsensitive =>
ParsedCaseSensitivity::AsciiCaseInsensitive,
AttributeFlags::CaseSensitivityDependsOnName => {
if !have_namespace && include!(concat!(
env!("OUT_DIR"),
"/ascii_case_insensitive_html_attributes.rs"
))
.contains(local_name)
{
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
} else {
ParsedCaseSensitivity::CaseSensitive
}
}
}
}
}
fn parse_attribute_flags<'i, 't>(
input: &mut CssParser<'i, 't>,
) -> Result<ParsedCaseSensitivity, BasicParseError<'i>> {
) -> Result<AttributeFlags, BasicParseError<'i>> {
let location = input.current_source_location();
match input.next() {
Err(_) => {
// Selectors spec says language-defined, but HTML says sensitive.
Ok(ParsedCaseSensitivity::CaseSensitive)
},
Ok(&Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
},
Ok(t) => Err(location.new_basic_unexpected_token_error(t.clone())),
}
let token = match input.next() {
Ok(t) => t,
Err(..) => {
// Selectors spec says language-defined; HTML says it depends on the
// exact attribute name.
return Ok(AttributeFlags::CaseSensitivityDependsOnName);
}
};
let ident = match *token {
Token::Ident(ref i) => i,
ref other => return Err(location.new_basic_unexpected_token_error(other.clone())),
};
Ok(match_ignore_ascii_case! {
ident,
"i" => AttributeFlags::AsciiCaseInsensitive,
"s" => AttributeFlags::CaseSensitive,
_ => return Err(location.new_basic_unexpected_token_error(token.clone())),
})
}
/// Level 3: Parse **one** simple_selector. (Though we might insert a second

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

@ -24,6 +24,9 @@ var tests = [
['[foo="bar" i]', '[foo="bar" i]'],
['[foo="bar" /**/ i]', '[foo="bar" i]'],
['[foo="bar"/**/i]', '[foo="bar" i]'],
['[foo="bar" s]', '[foo="bar" s]'],
['[foo="bar" /**/ s]', '[foo="bar" s]'],
['[foo="bar"/**/s]', '[foo="bar" s]'],
]
tests.forEach(function(arr) {

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

@ -1,5 +1,5 @@
<style></style>
<div id=test foo="BAR"></div>
<div id=test foo="BAR" baz="quux"></div>
<script>
var mode = "quirks mode";
</script>

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

@ -3,7 +3,7 @@
<style></style>
</head>
<body>
<div id="test" foo="BAR"/>
<div id="test" foo="BAR" baz="quux"/>
<script>
var mode = "XML";
</script>

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

@ -12,6 +12,17 @@ setup({explicit_done:true});
var match = [
// [selector, attrs...] (each attr is [ns, name, value])
["[foo='BAR'] /* sanity check (match) */", ["", "foo", "BAR"]],
["[foo='bar'] /* sanity check (match) */", ["", "foo", "bar"]],
["[align='left'] /* sanity check (match) */", ["", "align", "left"]],
["[class~='a'] /* sanity check (match) */", ["", "class", "X a b"]],
["[class~='A'] /* sanity check (match) */", ["", "class", "x A B"]],
["[id^='a'] /* sanity check (match) */", ["", "id", "ab"]],
["[id$='A'] /* sanity check (match) */", ["", "id", "XA"]],
["[lang|='a'] /* sanity check (match) */", ["", "lang", "a-b"]],
["[lang*='A'] /* sanity check (match) */", ["", "lang", "XAB"]],
["@namespace x 'http://www.w3.org/XML/1998/namespace'; [x|lang='A'] /* sanity check (match) */",
["http://www.w3.org/XML/1998/namespace", "lang", "A"]],
// Case-insensitive matching.
["[foo='bar' i]", ["", "foo", "BAR"]],
["[foo='' i]", ["", "foo", ""]],
["[foo='a\u0308' i] /* COMBINING in both */", ["", "foo", "A\u0308"]],
@ -32,9 +43,47 @@ var match = [
["[foo='bar' i][foo='bar' i]", ["", "foo", "BAR"]],
["[foo='BAR'][foo='bar' i]", ["", "foo", "BAR"]],
["[foo='bar' i][foo='BAR']", ["", "foo", "BAR"]],
// Case-sensitive matching.
["[foo='bar' s]", ["", "foo", "bar"]],
["[foo='' s]", ["", "foo", ""]],
["[foo='a\u0308' s] /* COMBINING in both */", ["", "foo", "a\u0308"]],
["[*|foo='bar' s]", ["", "foo", "x"], ["a", "foo", "x"], ["b", "foo", "bar"], ["c", "foo", "x"]],
["[*|foo='bar' s]", ["", "foo", "bar"], ["a", "foo", "x"], ["b", "foo", "x"], ["c", "foo", "x"]],
["[align='left' s]", ["", "align", "left"]],
["[align='LEFT' s]", ["", "align", "LEFT"]],
["[class~='a' s]", ["", "class", "x a b"]],
["[class~='A' s]", ["", "class", "X A B"]],
["[id^='a' s]", ["", "id", "ab"]],
["[id$='A' s]", ["", "id", "XA"]],
["[lang|='a' s]", ["", "lang", "a-b"]],
["[lang*='A' s]", ["", "lang", "XAB"]],
["[*|lang='a' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "a"]],
["[*|lang='A' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "A"]],
["@namespace x 'http://www.w3.org/XML/1998/namespace'; [x|lang='A' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "A"]],
["[foo='BAR' s][foo='BAR' s]", ["", "foo", "BAR"]],
];
var matchHTMLOnly = [
["[align='left'] /* sanity check (match HTML) */", ["", "align", "LEFT"]],
["[align='LEFT'] /* sanity check (match HTML) */", ["", "align", "left"]],
["[lang|='a'] /* sanity check (match HTML) */", ["", "lang", "A-B"]],
["[lang*='A'] /* sanity check (match HTML) */", ["", "lang", "xab"]],
];
var nomatch = [
["[missingattr] /* sanity check (no match) */", ["", "foo", "BAR"]],
["[foo='bar'] /* sanity check (no match) */", ["", "foo", "BAR"]],
["[class~='a'] /* sanity check (no match) */", ["", "class", "X A B"]],
["[class~='A'] /* sanity check (no match) */", ["", "class", "x a b"]],
["[id^='a'] /* sanity check (no match) */", ["", "id", "AB"]],
["[id$='A']", ["", "id", "xa"]],
["[*|lang='a'] /* sanity check (no match) */",
["http://www.w3.org/XML/1998/namespace", "lang", "A"]],
["[*|lang='A'] /* sanity check (no match) */",
["http://www.w3.org/XML/1998/namespace", "lang", "a"]],
["@namespace x 'http://www.w3.org/XML/1998/namespace'; [x|lang='A'] /* sanity check (no match) */",
["http://www.w3.org/XML/1998/namespace", "lang", "a"]],
// Case-insensitive matching.
["[foo='' i]", ["", "foo", "BAR"]],
["[foo='\u0000' i] /* \\0 in selector */", ["", "foo", ""]],
["[foo='' i] /* \\0 in attribute */", ["", "foo", "\u0000"]],
@ -73,6 +122,66 @@ var nomatch = [
["@namespace x 'A'; [x|foo='' i]", ["a", "foo", ""]],
["[foo='bar' i][foo='bar']", ["", "foo", "BAR"]],
["[foo='bar' i]", ["", "baz", "BAR"]],
// Case-sensitive matching
["[foo='' s]", ["", "foo", "BAR"]],
["[foo='\u0000' s] /* \\0 in selector */", ["", "foo", ""]],
["[foo='' s] /* \\0 in attribute */", ["", "foo", "\u0000"]],
["[foo='\u00E4' s]", ["", "foo", "\u00C4"]],
["[foo='\u00C4' s]", ["", "foo", "\u00E4"]],
["[foo='a\u0308' s] /* COMBINING in selector */", ["", "foo", "\u00C4"]],
["[foo~='a\u0308' s] /* COMBINING in selector */", ["", "foo", "\u00E4"]],
["[foo^='A\u0308' s] /* COMBINING in selector */", ["", "foo", "\u00C4"]],
["[foo$='A\u0308' s] /* COMBINING in selector */", ["", "foo", "\u00E4"]],
["[foo*='\u00E4' s] /* COMBINING in attribute */", ["", "foo", "a\u0308"]],
["[foo|='\u00E4' s] /* COMBINING in attribute */", ["", "foo", "A\u0308"]],
["[foo='\u00C4' s] /* COMBINING in attribute */", ["", "foo", "a\u0308"]],
["[foo='\u00C4' s] /* COMBINING in attribute */", ["", "foo", "A\u0308"]],
["[foo='a\u0308' s] /* COMBINING in selector */", ["", "foo", "a"]],
["[foo='a\u0308' s] /* COMBINING in selector */", ["", "foo", "A"]],
["[foo='A\u0308' s] /* COMBINING in selector */", ["", "foo", "a"]],
["[foo='A\u0308' s] /* COMBINING in selector */", ["", "foo", "A"]],
["[foo='a' s] /* COMBINING in attribute */", ["", "foo", "a\u0308"]],
["[foo='A' s] /* COMBINING in attribute */", ["", "foo", "a\u0308"]],
["[foo='a' s] /* COMBINING in attribute */", ["", "foo", "A\u0308"]],
["[foo='A' s] /* COMBINING in attribute */", ["", "foo", "A\u0308"]],
["[foo='i' s]", ["", "foo", "\u0130"]],
["[foo='i' s]", ["", "foo", "\u0131"]],
["[foo='I' s]", ["", "foo", "\u0130"]],
["[foo='I' s]", ["", "foo", "\u0131"]],
["[foo='\u0130' s]", ["", "foo", "i"]],
["[foo='\u0131' s]", ["", "foo", "i"]],
["[foo='\u0130' s]", ["", "foo", "I"]],
["[foo='\u0131' s]", ["", "foo", "I"]],
["[foo='bar' s]", ["", "foo", "x"], ["a", "foo", "BAR"]],
["[|foo='bar' s]", ["", "foo", "x"], ["a", "foo", "BAR"]],
["[foo='bar' s]", ["", "FOO", "bar"]],
["[foo='\t' s] /* tab in selector */", ["", "foo", " "]],
["[foo=' ' s] /* tab in attribute */", ["", "foo", "\t"]],
["@namespace x 'a'; [x|foo='' s]", ["A", "foo", ""]],
["@namespace x 'A'; [x|foo='' s]", ["a", "foo", ""]],
["[foo='bar' s][foo='bar']", ["", "foo", "BAR"]],
["[foo='bar' s]", ["", "baz", "BAR"]],
["[foo='bar' s]", ["", "foo", "BAR"]],
["[foo='a\u0308' s] /* COMBINING in both */", ["", "foo", "A\u0308"]],
["[foo='A\u0308' s] /* COMBINING in both */", ["", "foo", "a\u0308"]],
["[*|foo='bar' s]", ["", "foo", "x"], ["a", "foo", "x"], ["b", "foo", "BAR"], ["c", "foo", "x"]],
["[*|foo='bar' s]", ["", "foo", "BAR"], ["a", "foo", "x"], ["b", "foo", "x"], ["c", "foo", "x"]],
["[align='left' s]", ["", "align", "LEFT"]],
["[align='LEFT' s]", ["", "align", "left"]],
["[class~='a' s]", ["", "class", "X A B"]],
["[class~='A' s]", ["", "class", "x a b"]],
["[id^='a' s]", ["", "id", "AB"]],
["[id$='A' s]", ["", "id", "xa"]],
["[lang|='a' s]", ["", "lang", "A-B"]],
["[lang*='A' s]", ["", "lang", "xab"]],
["[*|lang='a' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "A"]],
["[*|lang='A' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "a"]],
["@namespace x 'http://www.w3.org/XML/1998/namespace'; [x|lang='A' s]", ["http://www.w3.org/XML/1998/namespace", "lang", "a"]],
["[foo='bar' s][foo='bar' s]", ["", "foo", "BAR"]],
["[foo='BAR' s][foo='bar']", ["", "foo", "BAR"]],
["[foo='bar'][foo='BAR' s]", ["", "foo", "BAR"]],
["[foo='BAR'][foo='bar' s]", ["", "foo", "BAR"]],
["[foo='bar' s][foo='BAR']", ["", "foo", "bar"]],
];
var mode = "standards mode";
function format_attrs(attrs) {
@ -107,7 +216,11 @@ onload = function() {
elm.setAttributeNS(attr[0], attr[1], attr[2]);
});
}
match.forEach(function(arr) {
var localMatch = match.slice();
if (global != xml) {
localMatch.push(...matchHTMLOnly);
}
localMatch.forEach(function(arr) {
var s = arr[0];
var attrs = arr.slice(1);
var ns_decl = s.substr(0, "@namespace".length) == "@namespace";

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

@ -5,13 +5,15 @@
<script src="/resources/testharnessreport.js"></script>
<style></style>
<div id=log></div>
<div id=test foo="BAR"></div>
<div id=test foo="BAR" baz="quux"></div>
<iframe id="quirks" src="resources/syntax-quirks.html"></iframe>
<iframe id="xml" src="resources/syntax-xml.xhtml"></iframe>
<script>
setup({explicit_done:true});
var valid = [
"[foo='BAR'] /* sanity check (valid) */",
"[baz='quux'] /* sanity check (valid) */",
// Case-insensitive selectors.
"[foo='bar' i]",
"[foo='bar' I]",
"[foo=bar i]",
@ -28,6 +30,7 @@ var valid = [
"[foo='bar'\ri\r] /* \\r */",
"[foo='bar' \\i]",
"[foo='bar' \\69]",
"[foo='bar' \\49]",
"[foo~='bar' i]",
"[foo^='bar' i]",
"[foo$='bar' i]",
@ -35,6 +38,31 @@ var valid = [
"[foo|='bar' i]",
"[|foo='bar' i]",
"[*|foo='bar' i]",
// Case-sensitive selectors.
"[baz='quux' s]",
"[baz='quux' S]",
"[baz=quux s]",
'[baz="quux" s]',
"[baz='quux's]",
"[baz='quux's ]",
"[baz='quux' s ]",
"[baz='quux' /**/ s]",
"[baz='quux' s /**/ ]",
"[baz='quux'/**/s/**/]",
"[baz=quux/**/s]",
"[baz='quux'\ts\t] /* \\t */",
"[baz='quux'\ns\n] /* \\n */",
"[baz='quux'\rs\r] /* \\r */",
"[baz='quux' \\s]",
"[baz='quux' \\73]",
"[baz='quux' \\53]",
"[baz~='quux' s]",
"[baz^='quux' s]",
"[baz$='quux' s]",
"[baz*='quux' s]",
"[baz|='quux' s]",
"[|baz='quux' s]",
"[*|baz='quux' s]",
];
var invalid = [
"[foo[ /* sanity check (invalid) */",