mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Embed user agent stylesheets and media control resouces in libservo (#36803)
Embed user agent stylesheets and media control resouces in libservo as decided in https://github.com/servo/servo/pull/36788#issuecomment-2845332210 Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
parent
7e2d2ed0ce
commit
3db0194e5a
11 changed files with 26 additions and 73 deletions
|
@ -16,7 +16,6 @@ use base::Epoch;
|
|||
use base::id::{PipelineId, WebViewId};
|
||||
use compositing_traits::CrossProcessCompositorApi;
|
||||
use constellation_traits::ScrollState;
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
use embedder_traits::{UntrustedNodeAddress, ViewportDetails};
|
||||
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
|
||||
use euclid::{Point2D, Scale, Size2D, Vector2D};
|
||||
|
@ -100,6 +99,18 @@ thread_local!(static SEEN_POINTERS: LazyCell<RefCell<HashSet<*const c_void>>> =
|
|||
LazyCell::new(|| RefCell::new(HashSet::new()))
|
||||
});
|
||||
|
||||
/// A CSS file to style the user agent stylesheet.
|
||||
static USER_AGENT_CSS: &[u8] = include_bytes!("./stylesheets/user-agent.css");
|
||||
|
||||
/// A CSS file to style the Servo browser.
|
||||
static SERVO_CSS: &[u8] = include_bytes!("./stylesheets/servo.css");
|
||||
|
||||
/// A CSS file to style the presentational hints.
|
||||
static PRESENTATIONAL_HINTS_CSS: &[u8] = include_bytes!("./stylesheets/presentational-hints.css");
|
||||
|
||||
/// A CSS file to style the quirks mode.
|
||||
static QUIRKS_MODE_CSS: &[u8] = include_bytes!("./stylesheets/quirks-mode.css");
|
||||
|
||||
/// Information needed by layout.
|
||||
pub struct LayoutThread {
|
||||
/// The ID of the pipeline that we belong to.
|
||||
|
@ -983,20 +994,12 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
|
|||
// FIXME: presentational-hints.css should be at author origin with zero specificity.
|
||||
// (Does it make a difference?)
|
||||
let mut user_or_user_agent_stylesheets = vec![
|
||||
parse_ua_stylesheet(
|
||||
shared_lock,
|
||||
"user-agent.css",
|
||||
&resources::read_bytes(Resource::UserAgentCSS),
|
||||
)?,
|
||||
parse_ua_stylesheet(
|
||||
shared_lock,
|
||||
"servo.css",
|
||||
&resources::read_bytes(Resource::ServoCSS),
|
||||
)?,
|
||||
parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS)?,
|
||||
parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS)?,
|
||||
parse_ua_stylesheet(
|
||||
shared_lock,
|
||||
"presentational-hints.css",
|
||||
&resources::read_bytes(Resource::PresentationalHintsCSS),
|
||||
PRESENTATIONAL_HINTS_CSS,
|
||||
)?,
|
||||
];
|
||||
|
||||
|
@ -1017,11 +1020,8 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
|
|||
)));
|
||||
}
|
||||
|
||||
let quirks_mode_stylesheet = parse_ua_stylesheet(
|
||||
shared_lock,
|
||||
"quirks-mode.css",
|
||||
&resources::read_bytes(Resource::QuirksModeCSS),
|
||||
)?;
|
||||
let quirks_mode_stylesheet =
|
||||
parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS)?;
|
||||
|
||||
Ok(UserAgentStylesheets {
|
||||
shared_lock: shared_lock.clone(),
|
||||
|
|
267
components/layout/stylesheets/presentational-hints.css
Normal file
267
components/layout/stylesheets/presentational-hints.css
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
https://html.spec.whatwg.org/multipage/#presentational-hints
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
pre[wrap] { white-space: pre-wrap; }
|
||||
|
||||
div[align=left i] { text-align: -moz-left; }
|
||||
div[align=right i] { text-align: -moz-right; }
|
||||
div[align=center i], div[align=middle i] { text-align: -moz-center; }
|
||||
div[align=justify i] { text-align: justify; }
|
||||
|
||||
|
||||
br[clear=left i] { clear: left; }
|
||||
br[clear=right i] { clear: right; }
|
||||
br[clear=all i], br[clear=both i] { clear: both; }
|
||||
|
||||
|
||||
ol[type="1"], li[type="1"] { list-style-type: decimal; }
|
||||
ol[type=a s], li[type=a s] { list-style-type: lower-alpha; }
|
||||
ol[type=A s], li[type=A s] { list-style-type: upper-alpha; }
|
||||
ol[type=i s], li[type=i s] { list-style-type: lower-roman; }
|
||||
ol[type=I s], li[type=I s] { list-style-type: upper-roman; }
|
||||
ul[type=none i], li[type=none i] { list-style-type: none; }
|
||||
ul[type=disc i], li[type=disc i] { list-style-type: disc; }
|
||||
ul[type=circle i], li[type=circle i] { list-style-type: circle; }
|
||||
ul[type=square i], li[type=square i] { list-style-type: square; }
|
||||
|
||||
|
||||
table[align=left i] { float: left; }
|
||||
table[align=right i] { float: right; }
|
||||
table[align=center i] { margin-left: auto; margin-right: auto; }
|
||||
:matches(thead, tbody, tfoot, tr, td, th)[align=absmiddle i] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
caption[align=bottom i] { caption-side: bottom; }
|
||||
p[align=left i], h1[align=left i], h2[align=left i], h3[align=left i], h4[align=left i], h5[align=left i], h6[align=left i] { text-align: left; }
|
||||
p[align=right i], h1[align=right i], h2[align=right i], h3[align=right i], h4[align=right i], h5[align=right i], h6[align=right i] { text-align: right; }
|
||||
p[align=center i], h1[align=center i], h2[align=center i], h3[align=center i], h4[align=center i], h5[align=center i], h6[align=center i] { text-align: center; }
|
||||
p[align=justify i], h1[align=justify i], h2[align=justify i], h3[align=justify i], h4[align=justify i], h5[align=justify i], h6[align=justify i] { text-align: justify; }
|
||||
thead[valign=top i], tbody[valign=top i], tfoot[valign=top i], tr[valign=top i], td[valign=top i], th[valign=top i] { vertical-align: top; }
|
||||
thead[valign=middle i], tbody[valign=middle i], tfoot[valign=middle i], tr[valign=middle i], td[valign=middle i], th[valign=middle i] { vertical-align: middle; }
|
||||
thead[valign=bottom i], tbody[valign=bottom i], tfoot[valign=bottom i], tr[valign=bottom i], td[valign=bottom i], th[valign=bottom i] { vertical-align: bottom; }
|
||||
thead[valign=baseline i], tbody[valign=baseline i], tfoot[valign=baseline i], tr[valign=baseline i], td[valign=baseline i], th[valign=baseline i] { vertical-align: baseline; }
|
||||
|
||||
td[nowrap], th[nowrap] { white-space: nowrap; }
|
||||
|
||||
table[rules=none i], table[rules=groups i], table[rules=rows i], table[rules=cols i], table[rules=all i] {
|
||||
border-style: hidden;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table:-servo-nonzero-border {
|
||||
border-style: outset;
|
||||
}
|
||||
table[frame=void i] { border-style: hidden; }
|
||||
table[frame=above i] { border-style: outset hidden hidden hidden; }
|
||||
table[frame=below i] { border-style: hidden hidden outset hidden; }
|
||||
table[frame=hsides i] { border-style: outset hidden outset hidden; }
|
||||
table[frame=lhs i] { border-style: hidden hidden hidden outset; }
|
||||
table[frame=rhs i] { border-style: hidden outset hidden hidden; }
|
||||
table[frame=vsides i] { border-style: hidden outset; }
|
||||
table[frame=box i], table[frame=border i] { border-style: outset; }
|
||||
|
||||
|
||||
table:-servo-nonzero-border > tr > td,
|
||||
table:-servo-nonzero-border > tr > th,
|
||||
table:-servo-nonzero-border > thead > tr > td,
|
||||
table:-servo-nonzero-border > thead > tr > th,
|
||||
table:-servo-nonzero-border > tbody > tr > td,
|
||||
table:-servo-nonzero-border > tbody > tr > th,
|
||||
table:-servo-nonzero-border > tfoot > tr > td,
|
||||
table:-servo-nonzero-border > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
table[rules=none i] > tr > td, table[rules=groups i] > tr > td, table[rules=rows i] > tr > td, table[rules=none i] > tr > th, table[rules=groups i] > tr > th, table[rules=rows i] > tr > th,
|
||||
table[rules=none i] > thead > tr > td, table[rules=groups i] > thead > tr > td, table[rules=rows i] > thead > tr > td, table[rules=none i] > thead > tr > th, table[rules=groups i] > thead > tr > th, table[rules=rows i] > thead > tr > th,
|
||||
table[rules=none i] > tbody > tr > td, table[rules=groups i] > tbody > tr > td, table[rules=rows i] > tbody > tr > td, table[rules=none i] > tbody > tr > th, table[rules=groups i] > tbody > tr > th, table[rules=rows i] > tbody > tr > th,
|
||||
table[rules=none i] > tfoot > tr > td, table[rules=groups i] > tfoot > tr > td, table[rules=rows i] > tfoot > tr > td, table[rules=none i] > tfoot > tr > th, table[rules=groups i] > tfoot > tr > th, table[rules=rows i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: none;
|
||||
}
|
||||
table[rules=cols i] > tr > td, table[rules=cols i] > tr > th,
|
||||
table[rules=cols i] > thead > tr > td, table[rules=cols i] > thead > tr > th,
|
||||
table[rules=cols i] > tbody > tr > td, table[rules=cols i] > tbody > tr > th,
|
||||
table[rules=cols i] > tfoot > tr > td, table[rules=cols i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: none solid;
|
||||
}
|
||||
table[rules=all i] > tr > td, table[rules=all i] > tr > th,
|
||||
table[rules=all i] > thead > tr > td, table[rules=all i] > thead > tr > th,
|
||||
table[rules=all i] > tbody > tr > td, table[rules=all i] > tbody > tr > th,
|
||||
table[rules=all i] > tfoot > tr > td, table[rules=all i] > tfoot > tr > th {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
table[rules=groups i] > colgroup {
|
||||
border-left-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
}
|
||||
table[rules=groups i] > tr,
|
||||
table[rules=groups i] > thead > tr,
|
||||
table[rules=groups i] > tbody > tr,
|
||||
table[rules=groups i] > tfoot > tr {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
table[rules=rows i] > tr,
|
||||
table[rules=rows i] > thead > tr,
|
||||
table[rules=rows i] > tbody > tr,
|
||||
table[rules=rows i] > tfoot > tr {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
|
||||
hr[align=left] { margin-left: 0; margin-right: auto; }
|
||||
hr[align=right] { margin-left: auto; margin-right: 0; }
|
||||
hr[align=center] { margin-left: auto; margin-right: auto; }
|
||||
hr[color], hr[noshade] { border-style: solid; }
|
||||
|
||||
|
||||
|
||||
iframe[frameborder="0"], iframe[frameborder=no i] { border: none; }
|
||||
|
||||
embed[align=left i], iframe[align=left i], img[type=image i][align=left i], object[align=left i] {
|
||||
float: left;
|
||||
}
|
||||
embed[align=right i], iframe[align=right i], img[type=image i][align=right i], object[align=right i] {
|
||||
float: right;
|
||||
}
|
||||
embed[align=top i], iframe[align=top i], img[type=image i][align=top i], object[align=top i] {
|
||||
vertical-align: top;
|
||||
}
|
||||
embed[align=baseline i], iframe[align=baseline i], img[type=image i][align=baseline i], object[align=baseline i] {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
embed[align=texttop i], iframe[align=texttop i], img[type=image i][align=texttop i], object[align=texttop i] {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
embed[align=absmiddle i], iframe[align=absmiddle i], img[type=image i][align=absmiddle i], object[align=absmiddle i],
|
||||
embed[align=abscenter i], iframe[align=abscenter i], img[type=image i][align=abscenter i], object[align=abscenter i] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
embed[align=bottom i], iframe[align=bottom i], img[type=image i][align=bottom i], object[align=bottom i] {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
/*
|
||||
FIXME:
|
||||
:matches(embed, iframe, img, input[type=image i], object):matches([align=center i], [align=middle i]) {
|
||||
vertical-align: "aligns the vertical middle of the element with the parent element's baseline."
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Presentational attributes which can not currently be expressed in CSS.
|
||||
FIXME: Deal with them with attr(foo dimension) and the like?
|
||||
|
||||
body
|
||||
marginheight
|
||||
marginwidth
|
||||
topmargin
|
||||
rightmargin
|
||||
bottommargin
|
||||
leftmargin
|
||||
background
|
||||
bgcolor
|
||||
text
|
||||
link
|
||||
vlink
|
||||
alink
|
||||
|
||||
frame, iframe
|
||||
marginheight
|
||||
marginwidth
|
||||
|
||||
font
|
||||
face
|
||||
color
|
||||
size
|
||||
|
||||
table
|
||||
cellspacing
|
||||
cellpadding
|
||||
hspace
|
||||
vspace
|
||||
height
|
||||
width
|
||||
bordercolor
|
||||
border
|
||||
|
||||
col
|
||||
width
|
||||
|
||||
tr
|
||||
height
|
||||
|
||||
td, th
|
||||
width
|
||||
height
|
||||
|
||||
caption, thead, tbody, tfoot, tr, td, and th
|
||||
align
|
||||
|
||||
table, thead, tbody, tfoot, tr, td, or th
|
||||
background
|
||||
bgcolor
|
||||
|
||||
(quirks mode) th, td
|
||||
nowrap
|
||||
|
||||
hr
|
||||
color
|
||||
noshade
|
||||
size
|
||||
width
|
||||
|
||||
legend
|
||||
align
|
||||
|
||||
embed, iframe, img, input[type=image i], object
|
||||
hspace
|
||||
vspace
|
||||
|
||||
img, input[type=image i], object
|
||||
border
|
||||
|
||||
embed, iframe, img, input[type=image i], object, video
|
||||
width
|
||||
height
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Extra
|
||||
ol > li
|
||||
https://html.spec.whatwg.org/multipage/#ordinal-value
|
||||
col
|
||||
span
|
||||
colgroup (if not col child)
|
||||
span
|
||||
td, th
|
||||
colspan
|
||||
rowspan
|
||||
|
||||
:computed-value(text-align is initial) > th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#rendered-legend
|
||||
|
||||
*/
|
||||
|
44
components/layout/stylesheets/quirks-mode.css
Normal file
44
components/layout/stylesheets/quirks-mode.css
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
|
||||
https://html.spec.whatwg.org/multipage/#flow-content-3
|
||||
|
||||
> In quirks mode, the following rules are also expected to apply:
|
||||
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
|
||||
form { margin-bottom: 1em; }
|
||||
|
||||
|
||||
table {
|
||||
font-weight: initial;
|
||||
font-style: initial;
|
||||
font-variant: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
white-space: initial;
|
||||
/* text-align: initial; -- see FIXME below */
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME(pcwalton): Actually saying `text-align: initial` above breaks `<table>` inside `<center>`
|
||||
* in quirks mode. This is because we (following Gecko, WebKit, and Blink) implement the HTML5
|
||||
* align-descendants rules with a special `text-align: -moz-center`. `text-align: initial`, if
|
||||
* placed on the `<table>` element per the spec, would break this behavior. So we place it on
|
||||
* `<tbody>` instead.
|
||||
*/
|
||||
tbody {
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: https://html.spec.whatwg.org/multipage/#margin-collapsing-quirks */
|
||||
|
||||
|
||||
input:not([type=image]), textarea { box-sizing: border-box; }
|
||||
|
||||
|
||||
img[align=left i] { margin-right: 3px; }
|
||||
img[align=right i] { margin-left: 3px; }
|
259
components/layout/stylesheets/servo.css
Normal file
259
components/layout/stylesheets/servo.css
Normal file
|
@ -0,0 +1,259 @@
|
|||
button {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
background: white;
|
||||
border: solid lightgrey 1px;
|
||||
color: black;
|
||||
font-family: sans-serif;
|
||||
font-size: 0.8333em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: white;
|
||||
border: solid lightgrey 1px;
|
||||
color: black;
|
||||
font-family: sans-serif;
|
||||
font-size: 0.8333em;
|
||||
}
|
||||
|
||||
input::selection,
|
||||
textarea::selection {
|
||||
background: rgba(176, 214, 255, 1.0);
|
||||
color: black;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="submit"],
|
||||
input[type="reset"] {
|
||||
background: lightgrey;
|
||||
border-top: solid 1px #EEEEEE;
|
||||
border-left: solid 1px #CCCCCC;
|
||||
border-right: solid 1px #999999;
|
||||
border-bottom: solid 1px #999999;
|
||||
color: black;
|
||||
}
|
||||
|
||||
input[type="hidden"] { display: none !important }
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
font-family: monospace !important;
|
||||
border: none !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
input[type="checkbox"]::before {
|
||||
display: inline-block;
|
||||
border: solid currentcolor 1px;
|
||||
content: "";
|
||||
padding: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before { content: "✓"; }
|
||||
input[type="checkbox"]:indeterminate::before { content: "-"; }
|
||||
|
||||
input[type="radio"]::before {
|
||||
display: inline-block;
|
||||
border: solid currentcolor 1px;
|
||||
content: "";
|
||||
padding: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked::before { content: "●"; line-height: 1em; }
|
||||
|
||||
input[type="file"]::before {
|
||||
content: "Choose File";
|
||||
background: lightgrey;
|
||||
border-top: solid 1px #EEEEEE;
|
||||
border-left: solid 1px #CCCCCC;
|
||||
border-right: solid 1px #999999;
|
||||
border-bottom: solid 1px #999999;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
text-align: center;
|
||||
color: black;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
td[align="left"] { text-align: left; }
|
||||
td[align="center"] { text-align: center; }
|
||||
td[align="right"] { text-align: right; }
|
||||
|
||||
center { text-align: -moz-center; }
|
||||
|
||||
label { cursor: default; }
|
||||
|
||||
img {
|
||||
overflow: clip !important;
|
||||
overflow-clip-margin: 0 !important;
|
||||
}
|
||||
|
||||
input:not([type=radio i]):not([type=checkbox i]):not([type=reset i]):not([type=button i]):not([type=submit i]) {
|
||||
cursor: text;
|
||||
overflow: hidden !important;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
textarea {
|
||||
cursor: text;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements */
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
details::-servo-details-summary {
|
||||
margin-left: 40px;
|
||||
display: list-item;
|
||||
list-style: disclosure-closed;
|
||||
}
|
||||
|
||||
details[open]::-servo-details-summary {
|
||||
list-style: disclosure-open;
|
||||
}
|
||||
|
||||
*|*::-servo-details-content {
|
||||
margin-left: 40px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Until servo supports svg properly, make sure to at least prevent svg
|
||||
* children from being layed out and rendered like usual html.
|
||||
* https://github.com/servo/servo/issues/10646
|
||||
*/
|
||||
svg > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-box {
|
||||
unicode-bidi: inherit;
|
||||
direction: inherit;
|
||||
writing-mode: inherit;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-table-row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
*|*::-servo-anonymous-table-cell {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
*|*::-servo-table-grid {
|
||||
all: inherit;
|
||||
margin: unset;
|
||||
float: unset;
|
||||
clear: unset;
|
||||
position: unset;
|
||||
z-index: unset;
|
||||
page-break-before: unset;
|
||||
page-break-after: unset;
|
||||
page-break-inside: unset;
|
||||
vertical-align: unset;
|
||||
line-height: unset;
|
||||
transform: unset;
|
||||
transform-origin: unset;
|
||||
backface-visibility: unset;
|
||||
clip: unset;
|
||||
transform-style: unset;
|
||||
rotate: unset;
|
||||
scale: unset;
|
||||
translate: unset;
|
||||
align-self: unset;
|
||||
justify-self: unset;
|
||||
grid-column-start: unset;
|
||||
grid-column-end: unset;
|
||||
grid-row-start: unset;
|
||||
grid-row-end: unset;
|
||||
order: unset;
|
||||
outline: unset;
|
||||
outline-offset: unset;
|
||||
column-span: unset;
|
||||
contain: unset;
|
||||
container: unset;
|
||||
scroll-margin: unset;
|
||||
|
||||
/* The grid needs to be block-level, so avoid inheriting `display: inline-table`. */
|
||||
display: table;
|
||||
}
|
||||
|
||||
meter {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(#e6e6e6, #e6e6e6, #eeeeee 20%, #cccccc 45%, #cccccc 55%);
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
/* FIXME: These should use the ::-moz-meter-bar pseudo element */
|
||||
meter div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
meter:-moz-meter-optimum div {
|
||||
background: linear-gradient(#ad7, #ad7, #cea 20%, #7a3 45%, #7a3 55%);
|
||||
}
|
||||
meter:-moz-meter-sub-optimum div {
|
||||
background: linear-gradient(#fe7, #fe7, #ffc 20%, #db3 45%, #db3 55%);
|
||||
}
|
||||
meter:-moz-meter-sub-sub-optimum div {
|
||||
background: linear-gradient(#f77, #f77, #fcc 20%, #d44 45%, #d44 55%);
|
||||
}
|
||||
|
||||
/* https://html.spec.whatwg.org/#the-details-and-summary-elements */
|
||||
details, summary {
|
||||
display: block;
|
||||
}
|
||||
details > summary:first-of-type {
|
||||
display: list-item;
|
||||
counter-increment: list-item 0;
|
||||
list-style: disclosure-closed inside;
|
||||
}
|
||||
details[open] > summary:first-of-type {
|
||||
list-style-type: disclosure-open;
|
||||
}
|
||||
|
||||
/* Styles for the <progress> element */
|
||||
progress {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
/* FIXME: This should use ::-moz-progress-bar */
|
||||
progress #-servo-progress-bar {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: #7a3;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: lightgrey;
|
||||
border-radius: 5px;
|
||||
border: 1px solid gray;
|
||||
padding: 0 0.25em;
|
||||
/* Don't show a text cursor when hovering selected option */
|
||||
cursor: default;
|
||||
}
|
433
components/layout/stylesheets/user-agent.css
Normal file
433
components/layout/stylesheets/user-agent.css
Normal file
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
https://html.spec.whatwg.org/multipage/#form-controls
|
||||
*/
|
||||
|
||||
@namespace url(http://www.w3.org/1999/xhtml);
|
||||
|
||||
[hidden], area, base, basefont, datalist, head, link, menu[type=popup i], meta,
|
||||
noembed, noframes, param, rp, script, source, style, template, track, title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
embed[hidden] { display: inline; height: 0; width: 0; }
|
||||
|
||||
/* FIXME: only if scripting is enabled */
|
||||
noscript { display: none !important; }
|
||||
|
||||
input[type=hidden i] { display: none !important; }
|
||||
|
||||
|
||||
html, body { display: block; }
|
||||
|
||||
body { margin: 8px; }
|
||||
|
||||
|
||||
address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
|
||||
legend, listing, main, p, plaintext, pre, summary, xmp {
|
||||
display: block;
|
||||
}
|
||||
|
||||
blockquote, figure, listing, p, plaintext, pre, xmp {
|
||||
margin-top: 1em; margin-bottom: 1em;
|
||||
}
|
||||
|
||||
blockquote, figure { margin-left: 40px; margin-right: 40px; }
|
||||
|
||||
address { font-style: italic; }
|
||||
listing, plaintext, pre, xmp {
|
||||
font-family: monospace; white-space: pre;
|
||||
}
|
||||
|
||||
dialog:not([open]) { display: none; }
|
||||
dialog {
|
||||
position: absolute;
|
||||
left: 0; right: 0;
|
||||
/* FIXME: support fit-content */
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
border: solid;
|
||||
padding: 1em;
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
/* FIXME: support ::backdrop */
|
||||
dialog::backdrop {
|
||||
position: fixed;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* for small devices, modal dialogs go full-screen */
|
||||
@media screen and (max-width: 540px) {
|
||||
/* FIXME: support :modal */
|
||||
dialog:modal {
|
||||
top: 0;
|
||||
width: auto;
|
||||
margin: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cite, dfn, em, i, var { font-style: italic; }
|
||||
b, strong { font-weight: bolder; }
|
||||
code, kbd, samp, tt { font-family: monospace; }
|
||||
big { font-size: larger; }
|
||||
small { font-size: smaller; }
|
||||
|
||||
sub { vertical-align: sub; }
|
||||
sup { vertical-align: super; }
|
||||
sub, sup { line-height: normal; font-size: smaller; }
|
||||
|
||||
ruby { display: ruby; }
|
||||
rt { display: ruby-text; }
|
||||
|
||||
/*
|
||||
* All tag names that can be links are listed here, because applying pseudo-class selectors
|
||||
* disables style sharing, so we want to apply pseudo-class selectors to as few elements as
|
||||
* possible.
|
||||
*/
|
||||
a:link, area:link, link:link { color: #0000EE; }
|
||||
a:visited, area:visited, link:visited { color: #551A8B; }
|
||||
a:link, a:visited,
|
||||
area:link, area:visited,
|
||||
link:link, link:visited { text-decoration: underline; cursor: pointer; }
|
||||
a:link[rel~=help], a:visited[rel~=help],
|
||||
area:link[rel~=help], area:visited[rel~=help],
|
||||
link:link[rel~=help], link:visited[rel~=help] { cursor: help; }
|
||||
|
||||
/*
|
||||
* FIXME: use `outline: auto;`
|
||||
*/
|
||||
a:focus, area:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, button:focus {
|
||||
outline: thin solid black;
|
||||
}
|
||||
|
||||
mark { background: yellow; color: black; }
|
||||
|
||||
abbr[title], acronym[title] { text-decoration: dotted underline; }
|
||||
ins, u { text-decoration: underline; }
|
||||
del, s, strike { text-decoration: line-through; }
|
||||
blink { text-decoration: blink; }
|
||||
|
||||
q::before { content: open-quote; }
|
||||
q::after { content: close-quote; }
|
||||
|
||||
/*br { display-outside: newline; } /* this also has bidi implications */
|
||||
br::before { content: "\A"; white-space: pre }
|
||||
|
||||
nobr { white-space: nowrap; }
|
||||
wbr { display-outside: break-opportunity; } /* this also has bidi implications */
|
||||
nobr wbr { white-space: normal; }
|
||||
|
||||
|
||||
/* Eventually we will want the following, but currently Servo does not
|
||||
properly parse the :dir pseudo-selector.
|
||||
[dir=ltr i], bdi:dir(ltr), input[type=tel]:dir(ltr) { direction: ltr; }
|
||||
*/
|
||||
[dir=ltr i] { direction: ltr; }
|
||||
[dir=rtl i] { direction: rtl; }
|
||||
[dir=ltr i], [dir=rtl i], [dir=auto i] { unicode-bidi: isolate; }
|
||||
|
||||
/* To ensure http://www.w3.org/TR/REC-html40/struct/dirlang.html#style-bidi:
|
||||
*
|
||||
* "When a block element that does not have a dir attribute is transformed to
|
||||
* the style of an inline element by a style sheet, the resulting presentation
|
||||
* should be equivalent, in terms of bidirectional formatting, to the
|
||||
* formatting obtained by explicitly adding a dir attribute (assigned the
|
||||
* inherited value) to the transformed element."
|
||||
*
|
||||
* and the rules in http://dev.w3.org/html5/spec/rendering.html#rendering
|
||||
*/
|
||||
address,
|
||||
article,
|
||||
aside,
|
||||
blockquote,
|
||||
body,
|
||||
caption,
|
||||
center,
|
||||
col,
|
||||
colgroup,
|
||||
dd,
|
||||
dir,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
fieldset,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
form,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
header,
|
||||
hgroup,
|
||||
hr,
|
||||
html,
|
||||
legend,
|
||||
li,
|
||||
listing,
|
||||
main,
|
||||
marquee,
|
||||
menu,
|
||||
nav,
|
||||
noframes,
|
||||
ol,
|
||||
p,
|
||||
plaintext,
|
||||
pre,
|
||||
search,
|
||||
section,
|
||||
summary,
|
||||
table,
|
||||
tbody,
|
||||
td,
|
||||
tfoot,
|
||||
th,
|
||||
thead,
|
||||
tr,
|
||||
ul,
|
||||
xmp
|
||||
{
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
bdi, output {
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
bdo, bdo[dir] { unicode-bidi: isolate-override; }
|
||||
|
||||
textarea[dir=auto i], pre[dir=auto i] { unicode-bidi: plaintext; }
|
||||
|
||||
|
||||
article, aside, h1, h2, h3, h4, h5, h6, hgroup, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 { margin-top: 0.67em; margin-bottom: 0.67em; font-size: 2.00em; font-weight: bold; }
|
||||
h2 { margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em; font-weight: bold; }
|
||||
h3 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; font-weight: bold; }
|
||||
h4 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; font-weight: bold; }
|
||||
h5 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; font-weight: bold; }
|
||||
h6 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; font-weight: bold; }
|
||||
|
||||
:matches(article, aside, nav, section) h1 { margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) h1 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h2 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h3 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h4 { margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em; }
|
||||
:matches(article, aside, nav, section) :matches(article, aside, nav, section) hgroup > h1 ~ h4 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
:matches(article, aside, nav, section) hgroup > h1 ~ h5 { margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em; }
|
||||
|
||||
|
||||
dir, dd, dl, dt, menu, ol, ul { display: block; }
|
||||
li { display: list-item; }
|
||||
|
||||
dir, dl, menu, ol, ul { margin-top: 1em; margin-bottom: 1em; }
|
||||
|
||||
:matches(dir, dl, menu, ol, ul) :matches(dir, dl, menu, ol, ul) {
|
||||
margin-top: 0; margin-bottom: 0;
|
||||
}
|
||||
|
||||
dd { margin-left: 40px; } /* FIXME: use margin-inline-start when supported */
|
||||
dir, menu, ol, ul { padding-left: 40px; } /* FIXME: use padding-inline-start when supported */
|
||||
|
||||
ol { list-style-type: decimal; }
|
||||
|
||||
dir, menu, ul { list-style-type: disc; }
|
||||
|
||||
:matches(dir, menu, ol, ul) :matches(dir, menu, ul) {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
:matches(dir, menu, ol, ul) :matches(dir, menu, ol, ul) :matches(dir, menu, ul) {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
|
||||
table { display: table; }
|
||||
caption {
|
||||
display: table-caption;
|
||||
text-align: center;
|
||||
}
|
||||
colgroup, colgroup[hidden] { display: table-column-group; }
|
||||
col, col[hidden] { display: table-column; }
|
||||
thead, thead[hidden] { display: table-header-group; }
|
||||
tbody, tbody[hidden] { display: table-row-group; }
|
||||
tfoot, tfoot[hidden] { display: table-footer-group; }
|
||||
tr, tr[hidden] { display: table-row; }
|
||||
td, th, td[hidden], th[hidden] { display: table-cell; }
|
||||
|
||||
colgroup[hidden], col[hidden], thead[hidden], tbody[hidden],
|
||||
tfoot[hidden], tr[hidden], td[hidden], th[hidden] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
table {
|
||||
box-sizing: border-box;
|
||||
border-spacing: 2px;
|
||||
border-collapse: separate;
|
||||
text-indent: initial;
|
||||
}
|
||||
td, th { padding: 1px; }
|
||||
th {
|
||||
font-weight: bold;
|
||||
text-align: -moz-center-or-inherit;
|
||||
}
|
||||
|
||||
thead, tbody, tfoot, table > tr { vertical-align: middle; }
|
||||
tr, td, th { vertical-align: inherit; }
|
||||
|
||||
|
||||
table, td, th { border-color: gray; }
|
||||
thead, tbody, tfoot, tr { border-color: inherit; }
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i],
|
||||
[frame=void i], [frame=above i], [frame=below i],
|
||||
[frame=hsides i], [frame=lhs i], [frame=rhs i],
|
||||
[frame=vsides i], [frame=box i], [frame=border i]
|
||||
),
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i]
|
||||
) > tr > :matches(td, th),
|
||||
table:matches(
|
||||
[rules=none i], [rules=groups i], [rules=rows i],
|
||||
[rules=cols i], [rules=all i]
|
||||
) > :matches(thead, tbody, tfoot) > tr > :matches(td, th) {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
|
||||
:matches(table, thead, tbody, tfoot, tr) > form {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
input, select, button, textarea {
|
||||
letter-spacing: initial;
|
||||
word-spacing: initial;
|
||||
line-height: initial;
|
||||
text-transform: initial;
|
||||
text-indent: initial;
|
||||
text-shadow: initial;
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
input:not([type=image i], [type=range i], [type=checkbox i], [type=radio i]) {
|
||||
overflow: clip !important;
|
||||
overflow-clip-margin: 0 !important;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
:autofill {
|
||||
field-sizing: fixed !important;
|
||||
}
|
||||
|
||||
input:is([type=reset i], [type=button i], [type=submit i]), button {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input, textarea, select, button { display: inline-block; }
|
||||
|
||||
input[type=hidden i], input[type=file i], input[type=image i] {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
input[type=radio i], input[type=checkbox i], input[type=reset i], input[type=button i], input[type=submit i],
|
||||
input[type=color i], input[type=search i], select, button {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea { white-space: pre-wrap; }
|
||||
|
||||
hr {
|
||||
color: gray;
|
||||
border-style: inset;
|
||||
border-width: 1px;
|
||||
margin-block-start: 0.5em;
|
||||
margin-inline-end: auto;
|
||||
margin-block-end: 0.5em;
|
||||
margin-inline-start: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
fieldset {
|
||||
display: block; /* https://www.w3.org/Bugs/Public/show_bug.cgi?id=27018 */
|
||||
margin-left: 2px; margin-right: 2px;
|
||||
border: groove 2px;
|
||||
border-color: ThreeDFace; /* FIXME: system color */
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding-left: 2px; padding-right: 2px;
|
||||
}
|
||||
|
||||
iframe:not([seamless]) { border: 2px inset; }
|
||||
iframe[seamless] { display: block; }
|
||||
video { object-fit: contain; }
|
||||
|
||||
|
||||
textarea { white-space: pre-wrap; }
|
||||
|
||||
*|*:not(:root):fullscreen {
|
||||
position:fixed !important;
|
||||
top:0 !important; right:0 !important; bottom:0 !important; left:0 !important;
|
||||
margin:0 !important;
|
||||
box-sizing:border-box !important;
|
||||
min-width:0 !important;
|
||||
max-width:none !important;
|
||||
min-height:0 !important;
|
||||
max-height:none !important;
|
||||
width:100% !important;
|
||||
height:100% !important;
|
||||
transform:none !important;
|
||||
|
||||
/* intentionally not !important */
|
||||
object-fit:contain;
|
||||
|
||||
/* The internal-only -servo-top-layer property is used
|
||||
to implement https://fullscreen.spec.whatwg.org/#top-layer */
|
||||
-servo-top-layer: top;
|
||||
}
|
||||
|
||||
iframe:fullscreen {
|
||||
border:none !important;
|
||||
padding:0 !important;
|
||||
}
|
||||
|
||||
/* https://drafts.csswg.org/css-lists-3/#ua-stylesheet */
|
||||
*::marker {
|
||||
text-align: end;
|
||||
text-transform: none;
|
||||
unicode-bidi: isolate;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: pre;
|
||||
}
|
|
@ -225,7 +225,7 @@ fn test_fetch_blob() {
|
|||
|
||||
#[test]
|
||||
fn test_file() {
|
||||
let path = Path::new("../../resources/servo.css")
|
||||
let path = Path::new("../../resources/ahem.css")
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let url = ServoUrl::from_file_path(path.clone()).unwrap();
|
||||
|
|
|
@ -12,7 +12,6 @@ use std::{f64, mem};
|
|||
use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
|
||||
use content_security_policy as csp;
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::resources::{self, Resource as EmbedderResource};
|
||||
use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
|
||||
use euclid::default::Size2D;
|
||||
use headers::{ContentLength, ContentRange, HeaderMapExt};
|
||||
|
@ -110,6 +109,12 @@ use crate::realms::{InRealm, enter_realm};
|
|||
use crate::script_runtime::CanGc;
|
||||
use crate::script_thread::ScriptThread;
|
||||
|
||||
/// A CSS file to style the media controls.
|
||||
static MEDIA_CONTROL_CSS: &str = include_str!("../resources/media-controls.css");
|
||||
|
||||
/// A JS file to control the media controls.
|
||||
static MEDIA_CONTROL_JS: &str = include_str!("../resources/media-controls.js");
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum FrameStatus {
|
||||
Locked,
|
||||
|
@ -1949,14 +1954,13 @@ impl HTMLMediaElement {
|
|||
ElementCreator::ScriptCreated,
|
||||
can_gc,
|
||||
);
|
||||
let mut media_controls_script = resources::read_string(EmbedderResource::MediaControlsJS);
|
||||
// This is our hacky way to temporarily workaround the lack of a privileged
|
||||
// JS context.
|
||||
// The media controls UI accesses the document.servoGetMediaControls(id) API
|
||||
// to get an instance to the media controls ShadowRoot.
|
||||
// `id` needs to match the internally generated UUID assigned to a media element.
|
||||
let id = document.register_media_controls(&shadow_root);
|
||||
let media_controls_script = media_controls_script.as_mut_str().replace("@@@id@@@", &id);
|
||||
let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
|
||||
*self.media_controls_id.borrow_mut() = Some(id);
|
||||
script
|
||||
.upcast::<Node>()
|
||||
|
@ -1969,7 +1973,6 @@ impl HTMLMediaElement {
|
|||
return;
|
||||
}
|
||||
|
||||
let media_controls_style = resources::read_string(EmbedderResource::MediaControlsCSS);
|
||||
let style = HTMLStyleElement::new(
|
||||
local_name!("script"),
|
||||
None,
|
||||
|
@ -1980,7 +1983,7 @@ impl HTMLMediaElement {
|
|||
);
|
||||
style
|
||||
.upcast::<Node>()
|
||||
.SetTextContent(Some(DOMString::from(media_controls_style)), can_gc);
|
||||
.SetTextContent(Some(DOMString::from(MEDIA_CONTROL_CSS)), can_gc);
|
||||
|
||||
if let Err(e) = shadow_root
|
||||
.upcast::<Node>()
|
||||
|
|
61
components/script/resources/media-controls.css
Normal file
61
components/script/resources/media-controls.css
Normal file
|
@ -0,0 +1,61 @@
|
|||
button {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: var(--button-size);
|
||||
min-height: var(--button-size);
|
||||
padding: 6px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.root {
|
||||
display: block;
|
||||
position: relative;
|
||||
min-height: 40px;
|
||||
min-width: 230px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: rgba(26,26,26,.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playing {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAALElEQVR4AWMYQeD///8KQOwAxQq4xQkb1PAfARpwiQ9Kg0YNGjWI8iwycgAA8BVdyMCA4SwAAAAASUVORK5CYII=") no-repeat;
|
||||
}
|
||||
|
||||
.paused {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAATklEQVR4AWMYgeD///8C1DLoARA7UG4QAqynyHX/UcF7II6n3CAE2A/ECpQbhHBdPTUM+gDEDZQadIB8ryFckUBpYG8ARf/AJ0i4K0YBAOu3zFnxHnpDAAAAAElFTkSuQmCC") no-repeat;
|
||||
}
|
||||
|
||||
.ended {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAArElEQVR4AbWRIQ7CQBBFR7QBhcYRFKISVwQcgfsgmiDq8S22BgkGAxwBJBp0BSEkhOaxYs2wW6jhjZn8eeqPfEJbfkHItoECDRRgQq9eSrBQsWLgl1rsAJgyo+TG6IsmBrocudKp1ew25MVcNG5PHDjr05i+oy946qCkcKSCUgdrLgQqCUyy0VIMJE5rsWjIqUgJbfcpFUvfUzLgzsnMA8gIxQcROXszOZH8hTfry4rnukG7bgAAAABJRU5ErkJggg==") no-repeat;
|
||||
}
|
||||
|
||||
.volumeup {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAAi0lEQVR4AWOgNfh/4n/Yfybc0g1gcs////8P/RfCoeT/fyjL9v+H/1f+c2NTAgRwnun/v/+nYCoBAzB7yX9GILni/8//IjBpFAAVyQKSLkA6BJ+i3UBSDEiX4lO0C6qoDJ+iTCDpCqRDCTl8JdDhooSDYDL+wLT7/xEYmDz4omU3ZrRgKj0NjmCqAwANy7W0uf6FTQAAAABJRU5ErkJggg==") no-repeat;
|
||||
}
|
||||
|
||||
.muted {
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAAy0lEQVR4AayRtUICUBRA7a7Jye5RF7u+wM4vsF2s0e7O37BzkZqZ+AF2WGk4XLqZeO/dPq/TUtuoZSki7mIgFloFlv3+icgOoKE8GlsH1sVOgOgy9jGjpzIuRjqv/rgNI3/hQCuD0t+8WK7Eywx6NfSGIAX+5t90CwOF5ODgNj4EO2TzxbDkdXwlgmCdLOolr+UlEbTn37QAK0fRBz8BAImvEYwMnmiJfdRzHxTAYoAQJjqPWWxJMf+3vHEAzCf+8A4avXYR9xSpm5QAaOvUDhZuhCsAAAAASUVORK5CYII=") no-repeat;
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAa1JREFUSA3Vlj1Ow0AQhW1OAFUaKmoqOEAklCIlJQ3nyBFyBahc0IAEEnUUunQWVLiloqEKNzDfmJ2V7Z1JlpKRRrt+82bezq7/yrZtH4qiOMUt2wJelWX5aQXJnYDf4UdWHOyjgFTjlm0Bz53ECAsHF65lzUFkDiffXM5Y+esQTq8CZ0ZEchLzBF5gviVsBwgiz2aYvrwtuiFWmkkjEN7S2h+wpn8Gso/ViLhXBP64+COYnkkUiAdqJLgiHhdcD74TWAnQ79pL/AsniGxki+ReTswQmSuJ2BTvm9klhGPNMUcIur+LMYHYIiiYxcd895oiUy9IbI5n3WlejX+OS/uyDV4bxNzt83IiHorfMortOuRlTLImJCe3EpisXIsz7Syulis53L6ZIhAm8hxs8PigMbeKDwpkcuRpXolAg3evipxE3YVdXGL6qqhVAKwTeZJJzwYr1+I6wrO6rcD1ZTcQ6NXtpjuL7xHRWq5ApQVyRqpJJ+PuRaT2vmiXBOPBZ4icwbmweJ7AIeR1jkjgrOFLTmLSmvy2nCSRX0B+W6755n5ZcXLlGbrHvd+W9x/rQiZUwmH0RgAAAABJRU5ErkJggg==') no-repeat;
|
||||
}
|
||||
|
||||
.fullscreen.fullscreen-active {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAbtJREFUSA3tlj0vBFEUhnd8hFhrE4mPbSREovIfdFQ6hcJ/EJVColP6GRoqhZbEFqKmET2xDSpWYjxncu915u6ZzWS20DjJ6577no/3zL1mqNWwNE1HwKb4g5rrdcjayHo54oxVbG8QAepl0FNphLVBQ6bflZ2ySiLU6+a+3bY/Hq/qA0EEog4m4ieDmwOzwrNazUOPwgQKpfkVWDAEVuHvQAsUDhjqSLKmeIQXKxKQ2Jv8UPY7eejuHJIsEantJ6B6l/glIXsKvOgq/DICT+Rld6IHH9IbEursz8GM5kv68+RdxiKJLibYYr+sOeffJknyqXlyZbhJzTn/g9yuwf9Tf3QC8SUPM8eoMUuXi/vWPJcsuSuac36H3I7BZ98UeQduQGwbcQEJ03ES+3vQ8y7kakkQkQegrYzAKwXyLuQs96K5yDrrYi6r3KZJ2jEicnS2EdwCX8Dbu3P6PcGzT3brCWuvCGTc/BpOPsVyJ0UC2ZkT3wfa8iJErObZZ4BYEyzFzww3DsKF4tsiBNaAPhaZ3PrGxBo9e0PkQP6ajYELIFa5uVejh3+SNn74z0JEjkClyX1zv9Jnxzf/AaCJ561FockLAAAAAElFTkSuQmCC') no-repeat;
|
||||
}
|
416
components/script/resources/media-controls.js
Normal file
416
components/script/resources/media-controls.js
Normal file
|
@ -0,0 +1,416 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
// States.
|
||||
const BUFFERING = "buffering";
|
||||
const ENDED = "ended";
|
||||
const ERRORED = "errored";
|
||||
const PAUSED = "paused";
|
||||
const PLAYING = "playing";
|
||||
|
||||
// State transitions.
|
||||
const TRANSITIONS = {
|
||||
buffer: {
|
||||
paused: BUFFERING
|
||||
},
|
||||
end: {
|
||||
playing: ENDED,
|
||||
paused: ENDED
|
||||
},
|
||||
error: {
|
||||
buffering: ERRORED,
|
||||
playing: ERRORED,
|
||||
paused: ERRORED
|
||||
},
|
||||
pause: {
|
||||
buffering: PAUSED,
|
||||
playing: PAUSED
|
||||
},
|
||||
play: {
|
||||
buffering: PLAYING,
|
||||
ended: PLAYING,
|
||||
paused: PLAYING
|
||||
}
|
||||
};
|
||||
|
||||
function generateMarkup(isAudioOnly) {
|
||||
return `
|
||||
<div class="controls">
|
||||
<button id="play-pause-button"></button>
|
||||
<input id="progress" type="range" value="0" min="0" max="100" step="1"></input>
|
||||
<span id="position-duration-box" class="hidden">
|
||||
<span id="position-text">#1</span>
|
||||
<span id="duration"> / #2</span>
|
||||
</span>
|
||||
<button id="volume-switch"></button>
|
||||
<input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input>
|
||||
${isAudioOnly ? "" : '<button id="fullscreen-switch" class="fullscreen"></button>'}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function camelCase(str) {
|
||||
const rdashes = /-(.)/g;
|
||||
return str.replace(rdashes, (str, p1) => {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(time, showHours = false) {
|
||||
// Format the duration as "h:mm:ss" or "m:ss"
|
||||
time = Math.round(time / 1000);
|
||||
|
||||
const hours = Math.floor(time / 3600);
|
||||
const mins = Math.floor((time % 3600) / 60);
|
||||
const secs = Math.floor(time % 60);
|
||||
|
||||
const formattedHours =
|
||||
hours || showHours ? `${hours.toString().padStart(2, "0")}:` : "";
|
||||
|
||||
return `${formattedHours}${mins
|
||||
.toString()
|
||||
.padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
class MediaControls {
|
||||
constructor() {
|
||||
this.nonce = Date.now();
|
||||
// Get the instance of the shadow root where these controls live.
|
||||
this.controls = document.servoGetMediaControls("@@@id@@@");
|
||||
// Get the instance of the host of these controls.
|
||||
this.media = this.controls.host;
|
||||
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
// We can only get here if the `controls` attribute is removed.
|
||||
this.cleanup();
|
||||
});
|
||||
this.mutationObserver.observe(this.media, {
|
||||
attributeFilter: ["controls"]
|
||||
});
|
||||
|
||||
this.isAudioOnly = this.media.localName == "audio";
|
||||
|
||||
// Create root element and load markup.
|
||||
this.root = document.createElement("div");
|
||||
this.root.classList.add("root");
|
||||
this.root.innerHTML = generateMarkup(this.isAudioOnly);
|
||||
this.controls.appendChild(this.root);
|
||||
|
||||
|
||||
const elementNames = [
|
||||
"duration",
|
||||
"play-pause-button",
|
||||
"position-duration-box",
|
||||
"position-text",
|
||||
"progress",
|
||||
"volume-switch",
|
||||
"volume-level"
|
||||
];
|
||||
|
||||
if (!this.isAudioOnly) {
|
||||
elementNames.push("fullscreen-switch");
|
||||
}
|
||||
|
||||
// Import elements.
|
||||
this.elements = {};
|
||||
elementNames.forEach(id => {
|
||||
this.elements[camelCase(id)] = this.controls.getElementById(id);
|
||||
});
|
||||
|
||||
// Init position duration box.
|
||||
const positionTextNode = this.elements.positionText;
|
||||
const durationSpan = this.elements.duration;
|
||||
const durationFormat = durationSpan.textContent;
|
||||
const positionFormat = positionTextNode.textContent;
|
||||
|
||||
durationSpan.classList.add("duration");
|
||||
durationSpan.setAttribute("role", "none");
|
||||
|
||||
Object.defineProperties(this.elements.positionDurationBox, {
|
||||
durationSpan: {
|
||||
value: durationSpan
|
||||
},
|
||||
position: {
|
||||
get: () => {
|
||||
return positionTextNode.textContent;
|
||||
},
|
||||
set: v => {
|
||||
positionTextNode.textContent = positionFormat.replace("#1", v);
|
||||
}
|
||||
},
|
||||
duration: {
|
||||
get: () => {
|
||||
return durationSpan.textContent;
|
||||
},
|
||||
set: v => {
|
||||
durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
|
||||
}
|
||||
},
|
||||
show: {
|
||||
value: (currentTime, duration) => {
|
||||
const self = this.elements.positionDurationBox;
|
||||
if (self.position != currentTime) {
|
||||
self.position = currentTime;
|
||||
}
|
||||
if (self.duration != duration) {
|
||||
self.duration = duration;
|
||||
}
|
||||
self.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add event listeners.
|
||||
this.mediaEvents = [
|
||||
"play",
|
||||
"pause",
|
||||
"ended",
|
||||
"volumechange",
|
||||
"loadeddata",
|
||||
"loadstart",
|
||||
"timeupdate",
|
||||
"progress",
|
||||
"playing",
|
||||
"waiting",
|
||||
"canplay",
|
||||
"canplaythrough",
|
||||
"seeking",
|
||||
"seeked",
|
||||
"emptied",
|
||||
"loadedmetadata",
|
||||
"error",
|
||||
"suspend"
|
||||
];
|
||||
this.mediaEvents.forEach(event => {
|
||||
this.media.addEventListener(event, this);
|
||||
});
|
||||
|
||||
this.controlEvents = [
|
||||
{ el: this.elements.playPauseButton, type: "click" },
|
||||
{ el: this.elements.volumeSwitch, type: "click" },
|
||||
{ el: this.elements.volumeLevel, type: "input" }
|
||||
];
|
||||
|
||||
if (!this.isAudioOnly) {
|
||||
this.controlEvents.push({ el: this.elements.fullscreenSwitch, type: "click" });
|
||||
}
|
||||
|
||||
this.controlEvents.forEach(({ el, type }) => {
|
||||
el.addEventListener(type, this);
|
||||
});
|
||||
|
||||
// Create state transitions.
|
||||
//
|
||||
// It exposes one method per transition. i.e. this.pause(), this.play(), etc.
|
||||
// For each transition, we check that the transition is possible and call
|
||||
// the `onStateChange` handler.
|
||||
for (let name in TRANSITIONS) {
|
||||
if (!TRANSITIONS.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
this[name] = () => {
|
||||
const from = this.state;
|
||||
|
||||
// Checks if the transition is valid in the current state.
|
||||
if (!TRANSITIONS[name][from]) {
|
||||
const error = `Transition "${name}" invalid for the current state "${from}"`;
|
||||
console.error(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const to = TRANSITIONS[name][from];
|
||||
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Transition to the next state.
|
||||
this.state = to;
|
||||
this.onStateChange(from);
|
||||
};
|
||||
}
|
||||
|
||||
// Set initial state.
|
||||
this.state = this.media.paused ? PAUSED : PLAYING;
|
||||
this.onStateChange(null);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.mutationObserver.disconnect();
|
||||
this.mediaEvents.forEach(event => {
|
||||
this.media.removeEventListener(event, this);
|
||||
});
|
||||
this.controlEvents.forEach(({ el, type }) => {
|
||||
el.removeEventListener(type, this);
|
||||
});
|
||||
}
|
||||
|
||||
// State change handler
|
||||
onStateChange(from) {
|
||||
this.render(from);
|
||||
}
|
||||
|
||||
render(from = this.state) {
|
||||
if (!this.isAudioOnly) {
|
||||
// XXX This should ideally use clientHeight/clientWidth,
|
||||
// but for some reason I couldn't figure out yet,
|
||||
// using it breaks layout.
|
||||
this.root.style.height = this.media.videoHeight;
|
||||
this.root.style.width = this.media.videoWidth;
|
||||
}
|
||||
|
||||
// Error
|
||||
if (this.state == ERRORED) {
|
||||
//XXX render errored state
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state != from) {
|
||||
// Play/Pause button.
|
||||
const playPauseButton = this.elements.playPauseButton;
|
||||
playPauseButton.classList.remove(from);
|
||||
playPauseButton.classList.add(this.state);
|
||||
}
|
||||
|
||||
// Progress.
|
||||
const positionPercent =
|
||||
(this.media.currentTime / this.media.duration) * 100;
|
||||
if (Number.isFinite(positionPercent)) {
|
||||
this.elements.progress.value = positionPercent;
|
||||
} else {
|
||||
this.elements.progress.value = 0;
|
||||
}
|
||||
|
||||
// Current time and duration.
|
||||
let currentTime = formatTime(0);
|
||||
let duration = formatTime(0);
|
||||
if (!isNaN(this.media.currentTime) && !isNaN(this.media.duration)) {
|
||||
currentTime = formatTime(Math.round(this.media.currentTime * 1000));
|
||||
duration = formatTime(Math.round(this.media.duration * 1000));
|
||||
}
|
||||
this.elements.positionDurationBox.show(currentTime, duration);
|
||||
|
||||
// Volume.
|
||||
this.elements.volumeSwitch.className =
|
||||
this.media.muted || !this.media.volume ? "muted" : "volumeup";
|
||||
const volumeLevelValue = this.media.muted
|
||||
? 0
|
||||
: Math.round(this.media.volume * 100);
|
||||
if (this.elements.volumeLevel.value != volumeLevelValue) {
|
||||
this.elements.volumeLevel.value = volumeLevelValue;
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (!event.isTrusted) {
|
||||
console.warn(`Drop untrusted event ${event.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mediaEvents.includes(event.type)) {
|
||||
this.onMediaEvent(event);
|
||||
} else {
|
||||
this.onControlEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
onControlEvent(event) {
|
||||
switch (event.type) {
|
||||
case "click":
|
||||
switch (event.currentTarget) {
|
||||
case this.elements.playPauseButton:
|
||||
this.playOrPause();
|
||||
break;
|
||||
case this.elements.volumeSwitch:
|
||||
this.toggleMuted();
|
||||
break;
|
||||
case this.elements.fullscreenSwitch:
|
||||
this.toggleFullscreen();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "input":
|
||||
switch (event.currentTarget) {
|
||||
case this.elements.volumeLevel:
|
||||
this.changeVolume();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown event ${event.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
// HTMLMediaElement event handler
|
||||
onMediaEvent(event) {
|
||||
switch (event.type) {
|
||||
case "ended":
|
||||
this.end();
|
||||
break;
|
||||
case "play":
|
||||
case "pause":
|
||||
// Transition to PLAYING or PAUSED state.
|
||||
this[event.type]();
|
||||
break;
|
||||
case "volumechange":
|
||||
case "timeupdate":
|
||||
case "resize":
|
||||
this.render();
|
||||
break;
|
||||
case "loadedmetadata":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Media actions */
|
||||
|
||||
playOrPause() {
|
||||
switch (this.state) {
|
||||
case PLAYING:
|
||||
this.media.pause();
|
||||
break;
|
||||
case BUFFERING:
|
||||
case ENDED:
|
||||
case PAUSED:
|
||||
this.media.play();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid state ${this.state}`);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMuted() {
|
||||
this.media.muted = !this.media.muted;
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
const { fullscreenEnabled, fullscreenElement } = document;
|
||||
|
||||
const isElementFullscreen = fullscreenElement && fullscreenElement === this.media;
|
||||
|
||||
if (fullscreenEnabled && isElementFullscreen) {
|
||||
document.exitFullscreen().then(() => {
|
||||
this.elements.fullscreenSwitch.classList.remove("fullscreen-active");
|
||||
});
|
||||
} else {
|
||||
this.media.requestFullscreen().then(() => {
|
||||
this.elements.fullscreenSwitch.classList.add("fullscreen-active");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeVolume() {
|
||||
const volume = parseInt(this.elements.volumeLevel.value);
|
||||
if (!isNaN(volume)) {
|
||||
this.media.volume = volume / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new MediaControls();
|
||||
})();
|
||||
|
|
@ -91,18 +91,6 @@ pub enum Resource {
|
|||
/// The message can contain a placeholder `${reason}` for the error code.
|
||||
/// It can be empty but then nothing will be displayed when an internal error occurs.
|
||||
NetErrorHTML,
|
||||
/// A CSS file to style the user agent stylesheet.
|
||||
/// It can be empty but then there's simply no user agent stylesheet.
|
||||
UserAgentCSS,
|
||||
/// A CSS file to style the Servo browser.
|
||||
/// It can be empty but several features might not work as expected.
|
||||
ServoCSS,
|
||||
/// A CSS file to style the presentational hints.
|
||||
/// It can be empty but then presentational hints will not be styled.
|
||||
PresentationalHintsCSS,
|
||||
/// A CSS file to style the quirks mode.
|
||||
/// It can be empty but then quirks mode will not be styled.
|
||||
QuirksModeCSS,
|
||||
/// A placeholder image to display if we couldn't get the requested image.
|
||||
///
|
||||
/// ## Panic
|
||||
|
@ -110,12 +98,6 @@ pub enum Resource {
|
|||
/// If the resource is not provided, servo will fallback to a baked in default (See resources/rippy.png).
|
||||
/// However, if the image is provided but invalid, Servo will crash.
|
||||
RippyPNG,
|
||||
/// A CSS file to style the media controls.
|
||||
/// It can be empty but then media controls will not be styled.
|
||||
MediaControlsCSS,
|
||||
/// A JS file to control the media controls.
|
||||
/// It can be empty but then media controls will not work.
|
||||
MediaControlsJS,
|
||||
/// A placeholder HTML page to display when the code responsible for rendering a page panics and the original
|
||||
/// page can no longer be displayed.
|
||||
/// The message can contain a placeholder `${details}` for the error details.
|
||||
|
@ -137,13 +119,7 @@ impl Resource {
|
|||
Resource::HstsPreloadList => "hsts_preload.json",
|
||||
Resource::BadCertHTML => "badcert.html",
|
||||
Resource::NetErrorHTML => "neterror.html",
|
||||
Resource::UserAgentCSS => "user-agent.css",
|
||||
Resource::ServoCSS => "servo.css",
|
||||
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
||||
Resource::QuirksModeCSS => "quirks-mode.css",
|
||||
Resource::RippyPNG => "rippy.png",
|
||||
Resource::MediaControlsCSS => "media-controls.css",
|
||||
Resource::MediaControlsJS => "media-controls.js",
|
||||
Resource::CrashHTML => "crash.html",
|
||||
Resource::DirectoryListingHTML => "directory-listing.html",
|
||||
Resource::AboutMemoryHTML => "about-memory.html",
|
||||
|
@ -183,21 +159,7 @@ fn resources_for_tests() -> Box<dyn ResourceReaderMethods + Sync + Send> {
|
|||
},
|
||||
Resource::BadCertHTML => &include_bytes!("../../../resources/badcert.html")[..],
|
||||
Resource::NetErrorHTML => &include_bytes!("../../../resources/neterror.html")[..],
|
||||
Resource::UserAgentCSS => &include_bytes!("../../../resources/user-agent.css")[..],
|
||||
Resource::ServoCSS => &include_bytes!("../../../resources/servo.css")[..],
|
||||
Resource::PresentationalHintsCSS => {
|
||||
&include_bytes!("../../../resources/presentational-hints.css")[..]
|
||||
},
|
||||
Resource::QuirksModeCSS => {
|
||||
&include_bytes!("../../../resources/quirks-mode.css")[..]
|
||||
},
|
||||
Resource::RippyPNG => &include_bytes!("../../../resources/rippy.png")[..],
|
||||
Resource::MediaControlsCSS => {
|
||||
&include_bytes!("../../../resources/media-controls.css")[..]
|
||||
},
|
||||
Resource::MediaControlsJS => {
|
||||
&include_bytes!("../../../resources/media-controls.js")[..]
|
||||
},
|
||||
Resource::CrashHTML => &include_bytes!("../../../resources/crash.html")[..],
|
||||
Resource::DirectoryListingHTML => {
|
||||
&include_bytes!("../../../resources/directory-listing.html")[..]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue