mirror of
https://github.com/servo/servo.git
synced 2025-06-18 13:24:29 +00:00
Auto merge of #8449 - paulrouget:favicon, r=jdm
mozbrowsericonchange event (Browser API) fixes #8347 <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8449) <!-- Reviewable:end -->
This commit is contained in:
commit
7f076c628b
8 changed files with 154 additions and 25 deletions
|
@ -313,7 +313,7 @@ pub enum MozBrowserEvent {
|
||||||
/// Sent when an error occurred while trying to load content within a browser `<iframe>`.
|
/// Sent when an error occurred while trying to load content within a browser `<iframe>`.
|
||||||
Error,
|
Error,
|
||||||
/// Sent when the favicon of a browser `<iframe>` changes.
|
/// Sent when the favicon of a browser `<iframe>` changes.
|
||||||
IconChange,
|
IconChange(String, String, String),
|
||||||
/// Sent when the browser `<iframe>` has finished loading all its assets.
|
/// Sent when the browser `<iframe>` has finished loading all its assets.
|
||||||
LoadEnd,
|
LoadEnd,
|
||||||
/// Sent when the browser `<iframe>` starts to load a new page.
|
/// Sent when the browser `<iframe>` starts to load a new page.
|
||||||
|
@ -341,7 +341,7 @@ impl MozBrowserEvent {
|
||||||
MozBrowserEvent::Close => "mozbrowserclose",
|
MozBrowserEvent::Close => "mozbrowserclose",
|
||||||
MozBrowserEvent::ContextMenu => "mozbrowsercontextmenu",
|
MozBrowserEvent::ContextMenu => "mozbrowsercontextmenu",
|
||||||
MozBrowserEvent::Error => "mozbrowsererror",
|
MozBrowserEvent::Error => "mozbrowsererror",
|
||||||
MozBrowserEvent::IconChange => "mozbrowsericonchange",
|
MozBrowserEvent::IconChange(_, _, _) => "mozbrowsericonchange",
|
||||||
MozBrowserEvent::LoadEnd => "mozbrowserloadend",
|
MozBrowserEvent::LoadEnd => "mozbrowserloadend",
|
||||||
MozBrowserEvent::LoadStart => "mozbrowserloadstart",
|
MozBrowserEvent::LoadStart => "mozbrowserloadstart",
|
||||||
MozBrowserEvent::LocationChange(_) => "mozbrowserlocationchange",
|
MozBrowserEvent::LocationChange(_) => "mozbrowserlocationchange",
|
||||||
|
@ -353,17 +353,6 @@ impl MozBrowserEvent {
|
||||||
MozBrowserEvent::OpenSearch => "mozbrowseropensearch"
|
MozBrowserEvent::OpenSearch => "mozbrowseropensearch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn detail(&self) -> Option<String> {
|
|
||||||
match *self {
|
|
||||||
MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu |
|
|
||||||
MozBrowserEvent::Error | MozBrowserEvent::IconChange | MozBrowserEvent::LoadEnd |
|
|
||||||
MozBrowserEvent::LoadStart | MozBrowserEvent::OpenWindow | MozBrowserEvent::SecurityChange |
|
|
||||||
MozBrowserEvent::ShowModalPrompt | MozBrowserEvent::UsernameAndPasswordRequired |
|
|
||||||
MozBrowserEvent::OpenSearch => None,
|
|
||||||
MozBrowserEvent::LocationChange(ref new_location) => Some(new_location.clone()),
|
|
||||||
MozBrowserEvent::TitleChange(ref new_title) => Some(new_title.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use dom::attr::{Attr, AttrValue};
|
use dom::attr::{Attr, AttrValue};
|
||||||
|
use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementIconChangeEventDetail;
|
||||||
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
|
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
|
@ -21,8 +22,8 @@ use dom::node::{Node, window_from_node};
|
||||||
use dom::urlhelper::UrlHelper;
|
use dom::urlhelper::UrlHelper;
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use js::jsapi::{JSAutoCompartment, JSAutoRequest, RootedValue};
|
use js::jsapi::{JSAutoCompartment, JSAutoRequest, RootedValue, JSContext, MutableHandleValue};
|
||||||
use js::jsval::UndefinedValue;
|
use js::jsval::{UndefinedValue, NullValue};
|
||||||
use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
|
use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
|
||||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||||
use msg::constellation_msg::{ConstellationChan, IframeLoadInfo, MozBrowserEvent};
|
use msg::constellation_msg::{ConstellationChan, IframeLoadInfo, MozBrowserEvent};
|
||||||
|
@ -143,9 +144,10 @@ impl HTMLIFrameElement {
|
||||||
let _ar = JSAutoRequest::new(cx);
|
let _ar = JSAutoRequest::new(cx);
|
||||||
let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get());
|
let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get());
|
||||||
let mut detail = RootedValue::new(cx, UndefinedValue());
|
let mut detail = RootedValue::new(cx, UndefinedValue());
|
||||||
event.detail().to_jsval(cx, detail.handle_mut());
|
let event_name = DOMString::from(event.name().to_owned());
|
||||||
|
self.build_mozbrowser_event_detail(event, cx, detail.handle_mut());
|
||||||
CustomEvent::new(GlobalRef::Window(window.r()),
|
CustomEvent::new(GlobalRef::Window(window.r()),
|
||||||
DOMString::from(event.name()),
|
event_name,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
detail.handle())
|
detail.handle())
|
||||||
|
@ -225,6 +227,41 @@ impl HTMLIFrameElementLayoutMethods for LayoutJS<HTMLIFrameElement> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait MozBrowserEventDetailBuilder {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn build_mozbrowser_event_detail(&self,
|
||||||
|
event: MozBrowserEvent,
|
||||||
|
cx: *mut JSContext,
|
||||||
|
rval: MutableHandleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MozBrowserEventDetailBuilder for HTMLIFrameElement {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn build_mozbrowser_event_detail(&self,
|
||||||
|
event: MozBrowserEvent,
|
||||||
|
cx: *mut JSContext,
|
||||||
|
rval: MutableHandleValue) {
|
||||||
|
match event {
|
||||||
|
MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu |
|
||||||
|
MozBrowserEvent::Error | MozBrowserEvent::LoadEnd | MozBrowserEvent::LoadStart |
|
||||||
|
MozBrowserEvent::OpenWindow | MozBrowserEvent::SecurityChange | MozBrowserEvent::OpenSearch |
|
||||||
|
MozBrowserEvent::ShowModalPrompt | MozBrowserEvent::UsernameAndPasswordRequired => {
|
||||||
|
rval.set(NullValue());
|
||||||
|
}
|
||||||
|
MozBrowserEvent::LocationChange(ref string) | MozBrowserEvent::TitleChange(ref string) => {
|
||||||
|
string.to_jsval(cx, rval);
|
||||||
|
}
|
||||||
|
MozBrowserEvent::IconChange(rel, href, sizes) => {
|
||||||
|
BrowserElementIconChangeEventDetail {
|
||||||
|
rel: Some(DOMString::from(rel)),
|
||||||
|
href: Some(DOMString::from(href)),
|
||||||
|
sizes: Some(DOMString::from(sizes)),
|
||||||
|
}.to_jsval(cx, rval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn Navigate(iframe: &HTMLIFrameElement, direction: NavigationDirection) -> Fallible<()> {
|
pub fn Navigate(iframe: &HTMLIFrameElement, direction: NavigationDirection) -> Fallible<()> {
|
||||||
if iframe.Mozbrowser() {
|
if iframe.Mozbrowser() {
|
||||||
if iframe.upcast::<Node>().is_in_doc() {
|
if iframe.upcast::<Node>().is_in_doc() {
|
||||||
|
|
|
@ -24,8 +24,8 @@ use encoding::all::UTF_8;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use layout_interface::{LayoutChan, Msg};
|
use layout_interface::{LayoutChan, Msg};
|
||||||
use msg::constellation_msg::ConstellationChan;
|
|
||||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||||
|
use msg::constellation_msg::{ConstellationChan, MozBrowserEvent};
|
||||||
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata};
|
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata};
|
||||||
use network_listener::{NetworkListener, PreInvoke};
|
use network_listener::{NetworkListener, PreInvoke};
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
@ -102,7 +102,7 @@ fn is_favicon(value: &Option<String>) -> bool {
|
||||||
match *value {
|
match *value {
|
||||||
Some(ref value) => {
|
Some(ref value) => {
|
||||||
value.split(HTML_SPACE_CHARACTERS)
|
value.split(HTML_SPACE_CHARACTERS)
|
||||||
.any(|s| s.eq_ignore_ascii_case("icon"))
|
.any(|s| s.eq_ignore_ascii_case("icon") || s.eq_ignore_ascii_case("apple-touch-icon"))
|
||||||
},
|
},
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
|
@ -118,13 +118,23 @@ impl VirtualMethods for HTMLLinkElement {
|
||||||
if !self.upcast::<Node>().is_in_doc() || mutation == AttributeMutation::Removed {
|
if !self.upcast::<Node>().is_in_doc() || mutation == AttributeMutation::Removed {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sizes_atom = &Atom::from_slice("sizes");
|
||||||
let rel = get_attr(self.upcast(), &atom!(rel));
|
let rel = get_attr(self.upcast(), &atom!(rel));
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!(href) => {
|
&atom!(href) => {
|
||||||
if string_is_stylesheet(&rel) {
|
if string_is_stylesheet(&rel) {
|
||||||
self.handle_stylesheet_url(&attr.value());
|
self.handle_stylesheet_url(&attr.value());
|
||||||
} else if is_favicon(&rel) {
|
} else if is_favicon(&rel) {
|
||||||
self.handle_favicon_url(&attr.value());
|
let sizes = get_attr(self.upcast(), sizes_atom);
|
||||||
|
self.handle_favicon_url(rel.as_ref().unwrap(), &attr.value(), &sizes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
atom if atom == sizes_atom => {
|
||||||
|
if is_favicon(&rel) {
|
||||||
|
if let Some(ref href) = get_attr(self.upcast(), &atom!("href")) {
|
||||||
|
self.handle_favicon_url(rel.as_ref().unwrap(), href, &Some(attr.value().to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&atom!(media) => {
|
&atom!(media) => {
|
||||||
|
@ -153,13 +163,14 @@ impl VirtualMethods for HTMLLinkElement {
|
||||||
|
|
||||||
let rel = get_attr(element, &atom!("rel"));
|
let rel = get_attr(element, &atom!("rel"));
|
||||||
let href = get_attr(element, &atom!("href"));
|
let href = get_attr(element, &atom!("href"));
|
||||||
|
let sizes = get_attr(self.upcast(), &Atom::from_slice("sizes"));
|
||||||
|
|
||||||
match (rel, href) {
|
match href {
|
||||||
(ref rel, Some(ref href)) if string_is_stylesheet(rel) => {
|
Some(ref href) if string_is_stylesheet(&rel) => {
|
||||||
self.handle_stylesheet_url(href);
|
self.handle_stylesheet_url(href);
|
||||||
}
|
}
|
||||||
(ref rel, Some(ref href)) if is_favicon(rel) => {
|
Some(ref href) if is_favicon(&rel) => {
|
||||||
self.handle_favicon_url(href);
|
self.handle_favicon_url(rel.as_ref().unwrap(), href, &sizes);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +230,7 @@ impl HTMLLinkElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_favicon_url(&self, href: &str) {
|
fn handle_favicon_url(&self, rel: &str, href: &str, sizes: &Option<String>) {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let window = window.r();
|
let window = window.r();
|
||||||
match UrlParser::new().base_url(&window.get_url()).parse(href) {
|
match UrlParser::new().base_url(&window.get_url()).parse(href) {
|
||||||
|
@ -227,6 +238,12 @@ impl HTMLLinkElement {
|
||||||
let ConstellationChan(ref chan) = window.constellation_chan();
|
let ConstellationChan(ref chan) = window.constellation_chan();
|
||||||
let event = ConstellationMsg::NewFavicon(url.clone());
|
let event = ConstellationMsg::NewFavicon(url.clone());
|
||||||
chan.send(event).unwrap();
|
chan.send(event).unwrap();
|
||||||
|
|
||||||
|
let mozbrowser_event = match *sizes {
|
||||||
|
Some(ref sizes) => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), sizes.to_owned()),
|
||||||
|
None => MozBrowserEvent::IconChange(rel.to_owned(), url.to_string(), "".to_owned())
|
||||||
|
};
|
||||||
|
window.Document().trigger_mozbrowser_event(mozbrowser_event);
|
||||||
}
|
}
|
||||||
Err(e) => debug!("Parsing url {} failed: {}", href, e)
|
Err(e) => debug!("Parsing url {} failed: {}", href, e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,12 @@ callback BrowserElementNextPaintEventCallback = void ();
|
||||||
interface BrowserElement {
|
interface BrowserElement {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dictionary BrowserElementIconChangeEventDetail {
|
||||||
|
DOMString rel;
|
||||||
|
DOMString href;
|
||||||
|
DOMString sizes;
|
||||||
|
};
|
||||||
|
|
||||||
BrowserElement implements BrowserElementCommon;
|
BrowserElement implements BrowserElementCommon;
|
||||||
BrowserElement implements BrowserElementPrivileged;
|
BrowserElement implements BrowserElementPrivileged;
|
||||||
|
|
||||||
|
|
|
@ -5009,6 +5009,12 @@
|
||||||
"url": "/_mozilla/mozilla/load_event.html"
|
"url": "/_mozilla/mozilla/load_event.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mozilla/mozbrowser/mozbrowsericonchange_event.html": [
|
||||||
|
{
|
||||||
|
"path": "mozilla/mozbrowser/mozbrowsericonchange_event.html",
|
||||||
|
"url": "/_mozilla/mozilla/mozbrowser/mozbrowsericonchange_event.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"mozilla/navigator.html": [
|
"mozilla/navigator.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/navigator.html",
|
"path": "mozilla/navigator.html",
|
||||||
|
|
1
tests/wpt/mozilla/meta/mozilla/mozbrowser/__dir__.ini
Normal file
1
tests/wpt/mozilla/meta/mozilla/mozbrowser/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
||||||
|
prefs: ["dom.mozbrowser.enabled:true"]
|
|
@ -0,0 +1,51 @@
|
||||||
|
<head>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
|
||||||
|
var expectedEvents = [
|
||||||
|
{rel: "icon", href: "http://web-platform.test:8000/test1.ico", sizes: "any"},
|
||||||
|
{rel: "icon", href: "http://web-platform.test:8000/_mozilla/mozilla/mozbrowser/test2.ico", sizes: "16x16 24x24"},
|
||||||
|
{rel: "shortcut icon", href: "http://example.com/test3.ico", sizes: ""},
|
||||||
|
{rel: "apple-touch-icon", href: "http://web-platform.test:8000/test4.ico", sizes: ""},
|
||||||
|
{rel: "icon", href: "http://web-platform.test:8000/test5.ico", sizes: ""},
|
||||||
|
{rel: "icon", href: "http://web-platform.test:8000/test6.ico", sizes: "any"},
|
||||||
|
{rel: "icon", href: "http://web-platform.test:8000/test6.ico", sizes: "32x32"},
|
||||||
|
];
|
||||||
|
|
||||||
|
var receivedEvents = [];
|
||||||
|
|
||||||
|
var iframe = document.createElement("iframe");
|
||||||
|
iframe.mozbrowser = "true";
|
||||||
|
iframe.src = "mozbrowsericonchange_event_test.html";
|
||||||
|
|
||||||
|
iframe.addEventListener("mozbrowsericonchange", t.step_func(e => {
|
||||||
|
|
||||||
|
receivedEvents.push({
|
||||||
|
rel: e.detail.rel,
|
||||||
|
href: e.detail.href,
|
||||||
|
sizes: e.detail.sizes,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (receivedEvents.length == expectedEvents.length) {
|
||||||
|
for (var i = 0; i < expectedEvents.length; i++) {
|
||||||
|
var e1 = expectedEvents[i];
|
||||||
|
var e2 = receivedEvents[i];
|
||||||
|
assert_equals(e1.rel, e2.rel);
|
||||||
|
assert_equals(e1.href, e2.href);
|
||||||
|
assert_equals(e1.sizes, e2.sizes);
|
||||||
|
}
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="icon" href="/test1.ico" sizes="any">
|
||||||
|
<link rel="icon" href="./test2.ico" sizes="16x16 24x24">
|
||||||
|
<link rel="shortcut icon" href="http://example.com/test3.ico">
|
||||||
|
<link rel="not-icon" href="xxx">
|
||||||
|
<link rel="icon no-href">
|
||||||
|
<link rel="apple-touch-icon" href="/test4.ico">
|
||||||
|
<script>
|
||||||
|
setTimeout(function() {
|
||||||
|
var link = document.createElement("link");
|
||||||
|
link.rel = "icon";
|
||||||
|
link.href = "/test5.ico"
|
||||||
|
document.head.appendChild(link);
|
||||||
|
link = document.querySelector('link[href="/test1.ico"]');
|
||||||
|
link.href = "/test6.ico";
|
||||||
|
setTimeout(() => link.setAttribute("sizes", "32x32"), 0);
|
||||||
|
}, 0);
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>123</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue