зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. CLOSED TREE
This commit is contained in:
Коммит
ea7eda334f
|
@ -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) */",
|
||||
|
|
Загрузка…
Ссылка в новой задаче