Merge inbound to mozilla-central r=merge a=merge

This commit is contained in:
btara 2017-11-14 22:37:02 +02:00
Родитель 9607440bf8 0814b42b38
Коммит d95e1d710b
37 изменённых файлов: 579 добавлений и 406 удалений

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

@ -1876,6 +1876,19 @@ nsAccessibilityService::UnsetConsumers(uint32_t aConsumers) {
NotifyOfConsumersChange();
}
void
nsAccessibilityService::GetConsumers(nsAString& aString)
{
const char16_t* kJSONFmt =
u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
nsString json;
nsTextFormatter::ssprintf(json, kJSONFmt,
gConsumers & eXPCOM ? "true" : "false",
gConsumers & eMainProcess ? "true" : "false",
gConsumers & ePlatformAPI ? "true" : "false");
aString.Assign(json);
}
void
nsAccessibilityService::NotifyOfConsumersChange()
{
@ -1886,15 +1899,10 @@ nsAccessibilityService::NotifyOfConsumersChange()
return;
}
const char16_t* kJSONFmt =
u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
nsString json;
nsTextFormatter::ssprintf(json, kJSONFmt,
gConsumers & eXPCOM ? "true" : "false",
gConsumers & eMainProcess ? "true" : "false",
gConsumers & ePlatformAPI ? "true" : "false");
nsAutoString consumers;
GetConsumers(consumers);
observerService->NotifyObservers(
nullptr, "a11y-consumers-changed", json.get());
nullptr, "a11y-consumers-changed", consumers.get());
}
nsAccessibilityService*

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

@ -309,6 +309,11 @@ private:
*/
void NotifyOfConsumersChange();
/**
* Get a JSON string representing the accessibility service consumers.
*/
void GetConsumers(nsAString& aString);
/**
* Set accessibility service consumers.
*/

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

@ -16,7 +16,7 @@ interface nsIAccessiblePivot;
* nsIAccessible for a given DOM node. More documentation at:
* http://www.mozilla.org/projects/ui/accessibility
*/
[scriptable, builtinclass, uuid(9a6f80fe-25cc-405c-9f8f-25869bc9f94e)]
[scriptable, builtinclass, uuid(2188e3a0-c88e-11e7-8f1a-0800200c9a66)]
interface nsIAccessibilityService : nsISupports
{
/**
@ -97,4 +97,10 @@ interface nsIAccessibilityService : nsISupports
* Return true if the given module is logged.
*/
boolean isLogged(in AString aModule);
/**
* Get the current accessibility service consumers.
* @returns a JSON string representing the accessibility service consumers.
*/
AString getConsumers();
};

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

@ -39,6 +39,10 @@ add_task(async function() {
XPCOM: false, MainProcess: true, PlatformAPI: false
}, "Accessibility service consumers in content are correct."));
Assert.deepEqual(JSON.parse(accService.getConsumers()), {
XPCOM: true, MainProcess: false, PlatformAPI: false
}, "Accessibility service consumers in parent are correct.");
info("Removing a service in parent and waiting for service to be shut " +
"down in content");
// Remove a11y service reference in the main process.

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

@ -47,6 +47,12 @@ add_task(async function() {
XPCOM: true, MainProcess: true, PlatformAPI: false
}, "Accessibility service consumers in content are correct."));
const contentConsumers = await ContentTask.spawn(browser, {}, () =>
accService.getConsumers());
Assert.deepEqual(JSON.parse(contentConsumers), {
XPCOM: true, MainProcess: true, PlatformAPI: false
}, "Accessibility service consumers in parent are correct.");
info("Shutting down a service in parent and making sure the one in " +
"content stays alive");
let contentCanShutdown = false;

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

@ -258,6 +258,18 @@ xpcAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged)
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibilityService::GetConsumers(nsAString& aString)
{
nsAccessibilityService* accService = GetAccService();
if (!accService) {
return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
accService->GetConsumers(aString);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// NS_GetAccessibilityService
////////////////////////////////////////////////////////////////////////////////

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

@ -1012,10 +1012,6 @@
.getService(Ci.nsISerializationHelper);
</field>
<field name="mIconLoadingPrincipal">
null
</field>
<method name="storeIcon">
<parameter name="aBrowser"/>
<parameter name="aURI"/>

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

@ -4,6 +4,8 @@
:root {
/* Photon color variables used on the aboutdevtools page */
--blue-50: #0a84ff;
--blue-50-alpha20: rgba(10, 132, 255, 0.2);
--blue-60: #0060df;
--blue-70: #003eaa;
--blue-80: #002275;
@ -14,7 +16,6 @@
--grey-90-alpha-30: rgba(12, 12, 13, 0.3);
--grey-90-alpha-40: rgba(12, 12, 13, 0.4);
--grey-90-alpha-50: rgba(12, 12, 13, 0.5);
--teal-60: #00c8d7;
--red-50: #ff0039;
--white: #ffffff;
@ -36,7 +37,14 @@ p {
max-width: 850px;
display: flex;
flex-shrink: 0;
padding: 34px 0 50px 0;
}
#install-page .box {
padding: 17% 0 50px 0;
}
#welcome-page .box {
padding: 50px 0;
}
.wrapper {
@ -93,6 +101,7 @@ p {
.feature-link {
display: block;
margin-top: 10px;
}
.external,
@ -129,6 +138,7 @@ p {
.buttons-container {
display: flex;
margin-top: 5px;
}
.buttons-container button:not(:last-child) {
@ -195,6 +205,7 @@ footer {
width: 100%;
min-height: 300px;
flex-grow: 1;
padding-bottom: 15px;
color: var(--white);
background: linear-gradient(0, var(--blue-60), var(--blue-80));
}

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

@ -75,7 +75,7 @@
<button type="submit" id="newsletter-submit" class="primary-button">&aboutDevtools.newsletter.subscribeButton;</button>
</form>
<div id="newsletter-thanks">
<h2>&aboutDevtools.newsletter.thanks.title;</h2>
<h2 class="newsletter-title">&aboutDevtools.newsletter.thanks.title;</h2>
<p>&aboutDevtools.newsletter.thanks.message;</p>
</div>
</div>

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

@ -7,6 +7,13 @@
* It is largely inspired from https://mozilla.github.io/basket-example/
*/
.newsletter-title {
font-size: 17px;
font-weight: 500;
margin-top: 26px;
margin-bottom: -4px;
}
#newsletter-errors {
/* Hidden by default */
display: none;
@ -52,8 +59,10 @@
/* The privacy section is hidden by default and only displayed on focus */
height: 0;
margin-bottom: -20px;
overflow: hidden;
padding: 3px 0 0 3px;
margin: -3px 0 -20px -3px;
}
#newsletter-privacy.animate {
@ -67,7 +76,7 @@
#privacy {
width: 20px;
height: 20px;
margin: 2px;
margin-top: 2px;
margin-inline-end: 10px;
flex-shrink: 0;
}
@ -75,7 +84,7 @@
#email {
width: 100%;
box-sizing: border-box;
padding: 12px 15px;
padding: 8px;
}
#newsletter-form input {
@ -87,8 +96,8 @@
}
#newsletter-form input:focus {
border-color: var(--teal-60);
box-shadow: 0 0 2px 0 var(--teal-60);
border-color: var(--blue-50);
box-shadow: 0 0 0px 3px var(--blue-50-alpha20);
}
#newsletter-form::placeholder {
@ -97,4 +106,5 @@
#newsletter-submit {
display: block;
padding: 8px 20px;
}

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

@ -476,7 +476,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
}
void
mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC)
mozilla::dom::TraceBlackJS(JSTracer* aTrc, bool aIsShutdownGC)
{
#ifdef MOZ_XUL
// Mark the scripts held in the XULPrototypeCache. This is required to keep
@ -544,7 +544,7 @@ mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownG
nsIDocument* doc = window->GetExtantDoc();
if (doc && doc->IsXULDocument()) {
XULDocument* xulDoc = static_cast<XULDocument*>(doc);
xulDoc->TraceProtos(aTrc, aGCNumber);
xulDoc->TraceProtos(aTrc);
}
#endif
}

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

@ -44,7 +44,7 @@ private:
namespace mozilla {
namespace dom {
void TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC);
void TraceBlackJS(JSTracer* aTrc, bool aIsShutdownGC);
} // namespace dom
} // namespace mozilla

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

@ -84,7 +84,7 @@ PublicKeyCredential::SetResponse(RefPtr<AuthenticatorResponse> aResponse)
}
/* static */ already_AddRefed<Promise>
PublicKeyCredential::IsPlatformAuthenticatorAvailable(GlobalObject& aGlobal)
PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(GlobalObject& aGlobal)
{
nsIGlobalObject* globalObject =
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aGlobal.Context()));
@ -94,15 +94,24 @@ PublicKeyCredential::IsPlatformAuthenticatorAvailable(GlobalObject& aGlobal)
ErrorResult rv;
RefPtr<Promise> promise = Promise::Create(globalObject, rv);
if(rv.Failed()) {
if (rv.Failed()) {
return nullptr;
}
// Complete in Bug 1406468. This shouldn't just always return true, it should
// follow the guidelines in
// https://w3c.github.io/webauthn/#isPlatformAuthenticatorAvailable
// such as ensuring that U2FTokenManager isn't in some way disabled.
promise->MaybeResolve(true);
// https://w3c.github.io/webauthn/#isUserVerifyingPlatformAuthenticatorAvailable
//
// We currently implement no platform authenticators, so this would always
// resolve to false. For those cases, the spec recommends a resolve timeout
// on the order of 10 minutes to avoid fingerprinting.
//
// A simple solution is thus to never resolve the promise, otherwise we'd
// have to track every single call to this method along with a promise
// and timer to resolve it after exactly X minutes.
//
// A Relying Party has to deal with a non-response in a timely fashion, so
// we can keep this as-is (and not resolve) even when we support platform
// authenticators but they're not available, or a user rejects a website's
// request to use them.
return promise.forget();
}

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

@ -47,7 +47,7 @@ public:
SetResponse(RefPtr<AuthenticatorResponse>);
static already_AddRefed<Promise>
IsPlatformAuthenticatorAvailable(GlobalObject& aGlobal);
IsUserVerifyingPlatformAuthenticatorAvailable(GlobalObject& aGlobal);
private:
CryptoBuffer mRawId;

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

@ -1,7 +1,7 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for W3C Web Authentication isPlatformAuthenticatorAvailable</title>
<title>Test for W3C Web Authentication isUserVerifyingPlatformAuthenticatorAvailable</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
<script type="text/javascript" src="pkijs/common.js"></script>
@ -12,7 +12,7 @@
</head>
<body>
<h1>Test for W3C Web Authentication isPlatformAuthenticatorAvailable</h1>
<h1>Test for W3C Web Authentication isUserVerifyingPlatformAuthenticatorAvailable</h1>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
<script class="testbody" type="text/javascript">
@ -21,24 +21,29 @@
// Execute the full-scope test
SimpleTest.waitForExplicitFinish();
// Turn off all tokens. This should result in "not allowed" failures
SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
["security.webauth.webauthn_enable_softtoken", true],
["security.webauth.webauthn_enable_usbtoken", false]]},
function() {
PublicKeyCredential.isPlatformAuthenticatorAvailable()
// This test ensures that isUserVerifyingPlatformAuthenticatorAvailable()
// is a callable method, but we currently can't test that it works in an
// automated way. If it resolves to false, per spec, we SHOULD wait
// ~10 minutes before resolving.
let p1 = PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(function(aResult) {
// The specification requires this method, if will return false, to wait 10
// minutes for anti-fingerprinting reasons. So we really can't test that
// in an automated way.
ok(aResult, "Should be available!");
ok(false, "We shouldn't get here.");
})
.catch(function(aProblem) {
is(false, "Problem encountered: " + aProblem);
})
.then(function() {
ok(false, "Problem encountered: " + aProblem);
});
// Finish on the next tick.
let p2 = Promise.resolve();
Promise.race([p1, p2]).then(function() {
ok(true, "isUserVerifyingPlatformAuthenticatorAvailable() is callable");
SimpleTest.finish();
})
});
});
</script>

1
dom/webauthn/u2f-hid-rs/.gitignore поставляемый
Просмотреть файл

@ -1,6 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
**/*.rs.bk
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock

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

@ -27,6 +27,9 @@ pub mod platform;
#[path = "stub/mod.rs"]
pub mod platform;
#[cfg(not(any(target_os = "macos")))]
mod khmatcher;
#[macro_use]
extern crate log;
extern crate rand;
@ -35,7 +38,6 @@ extern crate boxfnonce;
extern crate runloop;
mod consts;
mod khmatcher;
mod u2ftypes;
mod u2fprotocol;

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

@ -2,70 +2,31 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate libc;
extern crate log;
use std::fmt;
use std::io;
use std::io::{Read, Write};
use std::slice;
use std::sync::mpsc::{channel, Sender, Receiver, RecvTimeoutError};
use std::time::Duration;
use core_foundation_sys::base::*;
use libc::c_void;
use consts::{CID_BROADCAST, HID_RPT_SIZE};
use core_foundation_sys::base::*;
use platform::iokit::*;
use std::{fmt, io};
use std::io::{Read, Write};
use std::sync::mpsc::{Receiver, RecvTimeoutError};
use std::time::Duration;
use u2ftypes::U2FDevice;
use super::iokit::*;
const READ_TIMEOUT: u64 = 15;
pub struct Device {
device_ref: IOHIDDeviceRef,
cid: [u8; 4],
report_rx: Receiver<Vec<u8>>,
report_send_void: *mut c_void,
scratch_buf_ptr: *mut u8,
}
impl Device {
pub fn new(device_ref: IOHIDDeviceRef) -> Self {
let (report_tx, report_rx) = channel();
let report_send_void = Box::into_raw(Box::new(report_tx)) as *mut c_void;
let scratch_buf = [0; HID_RPT_SIZE];
let scratch_buf_ptr = Box::into_raw(Box::new(scratch_buf)) as *mut u8;
unsafe {
IOHIDDeviceRegisterInputReportCallback(
device_ref,
scratch_buf_ptr,
HID_RPT_SIZE as CFIndex,
read_new_data_cb,
report_send_void,
);
}
pub fn new(device_ref: IOHIDDeviceRef, report_rx: Receiver<Vec<u8>>) -> Self {
Self {
device_ref,
cid: CID_BROADCAST,
report_rx,
report_send_void,
scratch_buf_ptr,
}
}
}
impl Drop for Device {
fn drop(&mut self) {
debug!("Dropping U2F device {}", self);
unsafe {
// Re-allocate raw pointers for destruction.
let _ = Box::from_raw(self.report_send_void as *mut Sender<Vec<u8>>);
let _ = Box::from_raw(self.scratch_buf_ptr as *mut [u8; HID_RPT_SIZE]);
}
}
}
@ -148,45 +109,3 @@ impl U2FDevice for Device {
self.cid = cid;
}
}
// This is called from the RunLoop thread
extern "C" fn read_new_data_cb(
context: *mut c_void,
_: IOReturn,
_: *mut c_void,
report_type: IOHIDReportType,
report_id: u32,
report: *mut u8,
report_len: CFIndex,
) {
unsafe {
let tx = &mut *(context as *mut Sender<Vec<u8>>);
trace!(
"read_new_data_cb type={} id={} report={:?} len={}",
report_type,
report_id,
report,
report_len
);
let report_len = report_len as usize;
if report_len > HID_RPT_SIZE {
warn!(
"read_new_data_cb got too much data! {} > {}",
report_len,
HID_RPT_SIZE
);
return;
}
let data = slice::from_raw_parts(report, report_len).to_vec();
if let Err(e) = tx.send(data) {
// TOOD: This happens when the channel closes before this thread
// does. This is pretty common, but let's deal with stopping
// properly later.
warn!("Problem returning read_new_data_cb data for thread: {}", e);
};
}
}

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

@ -5,17 +5,14 @@
extern crate log;
extern crate libc;
use std::io;
use super::iokit::*;
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use core_foundation_sys::base::*;
use core_foundation_sys::dictionary::*;
use core_foundation_sys::number::*;
use core_foundation_sys::runloop::*;
use core_foundation_sys::string::*;
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use util::io_err;
use libc::c_void;
use platform::iokit::{CFRunLoopObserverContext, CFRunLoopObserverCreate};
pub struct IOHIDDeviceMatcher {
dict: CFDictionaryRef,
@ -92,36 +89,48 @@ impl Drop for IOHIDDeviceMatcher {
}
}
pub struct IOHIDManager {
manager: IOHIDManagerRef,
pub struct CFRunLoopEntryObserver {
observer: CFRunLoopObserverRef,
// Keep alive until the observer goes away.
context_ptr: *mut CFRunLoopObserverContext,
}
impl IOHIDManager {
pub fn new() -> io::Result<Self> {
let manager = unsafe { IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone) };
impl CFRunLoopEntryObserver {
pub fn new(callback: CFRunLoopObserverCallBack, context: *mut c_void) -> Self {
let context = CFRunLoopObserverContext::new(context);
let context_ptr = Box::into_raw(Box::new(context));
let rv = unsafe { IOHIDManagerOpen(manager, kIOHIDManagerOptionNone) };
if rv != 0 {
return Err(io_err("Couldn't open HID Manager"));
}
unsafe {
IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)
let observer = unsafe {
CFRunLoopObserverCreate(
kCFAllocatorDefault,
kCFRunLoopEntry,
false as Boolean,
0,
callback,
context_ptr,
)
};
Ok(Self { manager })
}
pub fn get(&self) -> IOHIDManagerRef {
self.manager
}
}
impl Drop for IOHIDManager {
fn drop(&mut self) {
let rv = unsafe { IOHIDManagerClose(self.manager, kIOHIDManagerOptionNone) };
if rv != 0 {
warn!("Couldn't close the HID Manager");
Self {
observer,
context_ptr,
}
}
pub fn add_to_current_runloop(&self) {
unsafe {
CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.observer, kCFRunLoopDefaultMode)
};
}
}
impl Drop for CFRunLoopEntryObserver {
fn drop(&mut self) {
unsafe {
CFRelease(self.observer as *mut c_void);
// Drop the CFRunLoopObserverContext.
let _ = Box::from_raw(self.context_ptr);
};
}
}

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

@ -7,11 +7,12 @@
extern crate core_foundation_sys;
extern crate libc;
use libc::c_void;
use core_foundation_sys::base::{CFIndex, CFAllocatorRef};
use core_foundation_sys::base::{Boolean, CFIndex, CFAllocatorRef, CFOptionFlags};
use core_foundation_sys::string::CFStringRef;
use core_foundation_sys::runloop::CFRunLoopRef;
use core_foundation_sys::runloop::{CFRunLoopRef, CFRunLoopObserverRef, CFRunLoopObserverCallBack};
use core_foundation_sys::dictionary::CFDictionaryRef;
use libc::c_void;
use std::ops::Deref;
type IOOptionBits = u32;
@ -28,7 +29,7 @@ pub type IOHIDDeviceCallback = extern "C" fn(context: *mut c_void,
pub type IOHIDReportType = IOOptionBits;
pub type IOHIDReportCallback = extern "C" fn(context: *mut c_void,
result: IOReturn,
sender: *mut c_void,
sender: IOHIDDeviceRef,
report_type: IOHIDReportType,
report_id: u32,
report: *mut u8,
@ -50,8 +51,51 @@ pub struct IOHIDDeviceRef(*const c_void);
unsafe impl Send for IOHIDDeviceRef {}
unsafe impl Sync for IOHIDDeviceRef {}
pub struct SendableRunLoop(pub CFRunLoopRef);
unsafe impl Send for SendableRunLoop {}
impl Deref for SendableRunLoop {
type Target = CFRunLoopRef;
fn deref(&self) -> &CFRunLoopRef {
&self.0
}
}
#[repr(C)]
pub struct CFRunLoopObserverContext {
pub version: CFIndex,
pub info: *mut c_void,
pub retain: Option<extern "C" fn(info: *const c_void) -> *const c_void>,
pub release: Option<extern "C" fn(info: *const c_void)>,
pub copyDescription: Option<extern "C" fn(info: *const c_void) -> CFStringRef>,
}
impl CFRunLoopObserverContext {
pub fn new(context: *mut c_void) -> Self {
Self {
version: 0 as CFIndex,
info: context,
retain: None,
release: None,
copyDescription: None,
}
}
}
#[link(name = "IOKit", kind = "framework")]
extern "C" {
// CFRunLoop
pub fn CFRunLoopObserverCreate(
allocator: CFAllocatorRef,
activities: CFOptionFlags,
repeats: Boolean,
order: CFIndex,
callout: CFRunLoopObserverCallBack,
context: *mut CFRunLoopObserverContext,
) -> CFRunLoopObserverRef;
// IOHIDManager
pub fn IOHIDManagerCreate(
allocator: CFAllocatorRef,
@ -68,6 +112,11 @@ extern "C" {
callback: IOHIDDeviceCallback,
context: *mut c_void,
);
pub fn IOHIDManagerRegisterInputReportCallback(
manager: IOHIDManagerRef,
callback: IOHIDReportCallback,
context: *mut c_void,
);
pub fn IOHIDManagerOpen(manager: IOHIDManagerRef, options: IOHIDManagerOptions) -> IOReturn;
pub fn IOHIDManagerClose(manager: IOHIDManagerRef, options: IOHIDManagerOptions) -> IOReturn;
pub fn IOHIDManagerScheduleWithRunLoop(
@ -84,11 +133,4 @@ extern "C" {
report: *const u8,
reportLength: CFIndex,
) -> IOReturn;
pub fn IOHIDDeviceRegisterInputReportCallback(
device: IOHIDDeviceRef,
report: *const u8,
reportLength: CFIndex,
callback: IOHIDReportCallback,
context: *mut c_void,
);
}

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

@ -3,30 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate log;
extern crate libc;
use std::thread;
use std::time::Duration;
mod device;
mod devicemap;
mod iokit;
mod iohid;
mod monitor;
use self::devicemap::DeviceMap;
use self::monitor::Monitor;
mod transaction;
use consts::PARAMETER_SIZE;
use khmatcher::KeyHandleMatcher;
use runloop::RunLoop;
use platform::device::Device;
use platform::transaction::Transaction;
use std::thread;
use std::time::Duration;
use u2fprotocol::{u2f_init_device, u2f_register, u2f_sign, u2f_is_keyhandle_valid};
use util::{io_err, OnceCallback};
use u2fprotocol::{u2f_register, u2f_sign, u2f_is_keyhandle_valid};
#[derive(Default)]
pub struct PlatformManager {
// Handle to the thread loop.
thread: Option<RunLoop>,
transaction: Option<Transaction>,
}
impl PlatformManager {
@ -47,56 +41,43 @@ impl PlatformManager {
let cbc = callback.clone();
let thread = RunLoop::new_with_timeout(
move |alive| {
let mut devices = DeviceMap::new();
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
let mut matches = KeyHandleMatcher::new(&key_handles);
// Start a new "sign" transaction.
let transaction = Transaction::new(timeout, cbc.clone(), move |device_ref, rx, alive| {
// Create a new device.
let dev = &mut Device::new(device_ref, rx);
'top: while alive() && monitor.alive() {
for event in monitor.events() {
devices.process_event(event);
}
// Try initializing it.
if !u2f_init_device(dev) {
return;
}
// Query newly added devices.
matches.update(devices.iter_mut(), |device, key_handle| {
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
.unwrap_or(false /* no match on failure */)
});
// Iterate the exlude list and see if there are any matches.
// Abort the state machine if we found a valid key handle.
if key_handles.iter().any(|key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
.unwrap_or(false) /* no match on failure */
})
{
return;
}
// Iterate all devices that don't match any of the handles
// in the exclusion list and try to register.
for (path, device) in devices.iter_mut() {
if matches.get(path).is_empty() {
if let Ok(bytes) = u2f_register(device, &challenge, &application) {
callback.call(Ok(bytes));
return;
}
}
// Check to see if monitor.events has any hotplug events that we'll need
// to handle
if monitor.events().size_hint().0 > 0 {
debug!("Hotplug event; restarting loop");
continue 'top;
}
}
thread::sleep(Duration::from_millis(100));
while alive() {
if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
callback.call(Ok(bytes));
break;
}
callback.call(Err(io_err("aborted or timed out")));
},
timeout,
);
// Sleep a bit before trying again.
thread::sleep(Duration::from_millis(100));
}
});
self.thread = Some(try_or!(
thread,
|_| cbc.call(Err(io_err("couldn't create runloop")))
));
// Store the transaction so we can cancel it, if needed.
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
}
pub fn sign(
&mut self,
timeout: u64,
@ -110,85 +91,58 @@ impl PlatformManager {
let cbc = callback.clone();
let thread = RunLoop::new_with_timeout(
move |alive| {
let mut devices = DeviceMap::new();
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
let mut matches = KeyHandleMatcher::new(&key_handles);
// Start a new "register" transaction.
let transaction = Transaction::new(timeout, cbc.clone(), move |device_ref, rx, alive| {
// Create a new device.
let dev = &mut Device::new(device_ref, rx);
'top: while alive() && monitor.alive() {
for event in monitor.events() {
devices.process_event(event);
// Try initializing it.
if !u2f_init_device(dev) {
return;
}
// Find all matching key handles.
let key_handles = key_handles
.iter()
.filter(|key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
.unwrap_or(false) /* no match on failure */
})
.collect::<Vec<&Vec<u8>>>();
while alive() {
// If the device matches none of the given key handles
// then just make it blink with bogus data.
if key_handles.is_empty() {
let blank = vec![0u8; PARAMETER_SIZE];
if let Ok(_) = u2f_register(dev, &blank, &blank) {
callback.call(Err(io_err("invalid key")));
break;
}
// Query newly added devices.
matches.update(devices.iter_mut(), |device, key_handle| {
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
.unwrap_or(false /* no match on failure */)
});
// Iterate all devices.
for (path, device) in devices.iter_mut() {
let key_handles = matches.get(path);
// If the device matches none of the given key handles
// then just make it blink with bogus data.
if key_handles.is_empty() {
let blank = vec![0u8; PARAMETER_SIZE];
if let Ok(_) = u2f_register(device, &blank, &blank) {
callback.call(Err(io_err("invalid key")));
return;
}
continue;
}
// Otherwise, try to sign.
for key_handle in key_handles {
if let Ok(bytes) = u2f_sign(
device,
&challenge,
&application,
key_handle,
)
{
callback.call(Ok((key_handle.to_vec(), bytes)));
return;
}
}
// Check to see if monitor.events has any hotplug events that we'll
// need to handle
if monitor.events().size_hint().0 > 0 {
debug!("Hotplug event; restarting loop");
continue 'top;
} else {
// Otherwise, try to sign.
for key_handle in &key_handles {
if let Ok(bytes) = u2f_sign(dev, &challenge, &application, key_handle) {
callback.call(Ok((key_handle.to_vec(), bytes)));
break;
}
}
thread::sleep(Duration::from_millis(100));
}
callback.call(Err(io_err("aborted or timed out")));
},
timeout,
);
// Sleep a bit before trying again.
thread::sleep(Duration::from_millis(100));
}
});
self.thread = Some(try_or!(
thread,
|_| cbc.call(Err(io_err("couldn't create runloop")))
));
// Store the transaction so we can cancel it, if needed.
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
}
pub fn cancel(&mut self) {
if let Some(thread) = self.thread.take() {
thread.cancel();
if let Some(mut transaction) = self.transaction.take() {
transaction.cancel();
}
}
}
impl Drop for PlatformManager {
fn drop(&mut self) {
debug!("OSX PlatformManager dropped");
self.cancel();
}
}

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

@ -2,115 +2,175 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io;
use std::sync::mpsc::{channel, Sender, Receiver, TryIter};
use std::thread;
use super::iohid::*;
use super::iokit::*;
use core_foundation_sys::runloop::*;
use runloop::RunLoop;
extern crate log;
extern crate libc;
use core_foundation_sys::base::*;
use core_foundation_sys::runloop::*;
use libc::c_void;
use platform::iohid::*;
use platform::iokit::*;
use runloop::RunLoop;
use std::{io, slice};
use std::collections::HashMap;
use std::sync::mpsc::{channel, Receiver, Sender};
use util::io_err;
pub enum Event {
Add(IOHIDDeviceRef),
Remove(IOHIDDeviceRef),
struct DeviceData {
tx: Sender<Vec<u8>>,
runloop: RunLoop,
}
pub struct Monitor {
// Receive events from the thread.
rx: Receiver<Event>,
// Handle to the thread loop.
thread: RunLoop,
pub struct Monitor<F>
where
F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync,
{
manager: IOHIDManagerRef,
// Keep alive until the monitor goes away.
_matcher: IOHIDDeviceMatcher,
map: HashMap<IOHIDDeviceRef, DeviceData>,
new_device_cb: F,
}
impl Monitor {
pub fn new() -> io::Result<Self> {
impl<F> Monitor<F>
where
F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync + 'static,
{
pub fn new(new_device_cb: F) -> Self {
let manager = unsafe { IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone) };
// Match FIDO devices only.
let _matcher = IOHIDDeviceMatcher::new();
unsafe { IOHIDManagerSetDeviceMatching(manager, _matcher.get()) };
Self {
manager,
_matcher,
new_device_cb,
map: HashMap::new(),
}
}
pub fn start(&mut self) -> io::Result<()> {
let context = self as *mut Self as *mut c_void;
unsafe {
IOHIDManagerRegisterDeviceMatchingCallback(
self.manager,
Monitor::<F>::on_device_matching,
context,
);
IOHIDManagerRegisterDeviceRemovalCallback(
self.manager,
Monitor::<F>::on_device_removal,
context,
);
IOHIDManagerRegisterInputReportCallback(
self.manager,
Monitor::<F>::on_input_report,
context,
);
IOHIDManagerScheduleWithRunLoop(
self.manager,
CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode,
);
let rv = IOHIDManagerOpen(self.manager, kIOHIDManagerOptionNone);
if rv == 0 {
Ok(())
} else {
Err(io_err(&format!("Couldn't open HID Manager, rv={}", rv)))
}
}
}
pub fn stop(&mut self) {
// Remove all devices.
while !self.map.is_empty() {
let device_ref = *self.map.keys().next().unwrap();
self.remove_device(&device_ref);
}
// Close the manager and its devices.
unsafe { IOHIDManagerClose(self.manager, kIOHIDManagerOptionNone) };
}
fn remove_device(&mut self, device_ref: &IOHIDDeviceRef) {
if let Some(DeviceData { tx, runloop }) = self.map.remove(device_ref) {
// Dropping `tx` will make Device::read() fail eventually.
drop(tx);
// Wait until the runloop stopped.
runloop.cancel();
}
}
extern "C" fn on_input_report(
context: *mut c_void,
_: IOReturn,
device_ref: IOHIDDeviceRef,
_: IOHIDReportType,
_: u32,
report: *mut u8,
report_len: CFIndex,
) {
let this = unsafe { &mut *(context as *mut Self) };
let mut send_failed = false;
// Ignore the report if we can't find a device for it.
if let Some(&DeviceData { ref tx, .. }) = this.map.get(&device_ref) {
let data = unsafe { slice::from_raw_parts(report, report_len as usize).to_vec() };
send_failed = tx.send(data).is_err();
}
// Remove the device if sending fails.
if send_failed {
this.remove_device(&device_ref);
}
}
extern "C" fn on_device_matching(
context: *mut c_void,
_: IOReturn,
_: *mut c_void,
device_ref: IOHIDDeviceRef,
) {
let this = unsafe { &mut *(context as *mut Self) };
let (tx, rx) = channel();
let f = &this.new_device_cb;
let thread = RunLoop::new(move |alive| -> io::Result<()> {
let tx_box = Box::new(tx);
let tx_ptr = Box::into_raw(tx_box) as *mut libc::c_void;
// This will keep `tx` alive only for the scope.
let _tx = unsafe { Box::from_raw(tx_ptr as *mut Sender<Event>) };
// Create and initialize a scoped HID manager.
let manager = IOHIDManager::new()?;
// Match only U2F devices.
let dict = IOHIDDeviceMatcher::new();
unsafe { IOHIDManagerSetDeviceMatching(manager.get(), dict.get()) };
// Register callbacks.
unsafe {
IOHIDManagerRegisterDeviceMatchingCallback(
manager.get(),
Monitor::device_add_cb,
tx_ptr,
);
IOHIDManagerRegisterDeviceRemovalCallback(
manager.get(),
Monitor::device_remove_cb,
tx_ptr,
);
// Create a new per-device runloop.
let runloop = RunLoop::new(move |alive| {
// Ensure that the runloop is still alive.
if alive() {
f(device_ref, rx, alive);
}
});
// Run the Event Loop. CFRunLoopRunInMode() will dispatch HID
// input reports into the various callbacks
while alive() {
trace!("OSX Runloop running, handle={:?}", thread::current());
if unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, 0) } ==
kCFRunLoopRunStopped
{
debug!("OSX Runloop device stopped.");
break;
}
}
debug!("OSX Runloop completed, handle={:?}", thread::current());
Ok(())
})?;
Ok(Self { rx, thread })
if let Ok(runloop) = runloop {
this.map.insert(device_ref, DeviceData { tx, runloop });
}
}
pub fn events(&self) -> TryIter<Event> {
self.rx.try_iter()
}
pub fn alive(&self) -> bool {
self.thread.alive()
}
extern "C" fn device_add_cb(
extern "C" fn on_device_removal(
context: *mut c_void,
_: IOReturn,
_: *mut c_void,
device: IOHIDDeviceRef,
device_ref: IOHIDDeviceRef,
) {
let tx = unsafe { &*(context as *mut Sender<Event>) };
let _ = tx.send(Event::Add(device));
}
extern "C" fn device_remove_cb(
context: *mut c_void,
_: IOReturn,
_: *mut c_void,
device: IOHIDDeviceRef,
) {
let tx = unsafe { &*(context as *mut Sender<Event>) };
let _ = tx.send(Event::Remove(device));
let this = unsafe { &mut *(context as *mut Self) };
this.remove_device(&device_ref);
}
}
impl Drop for Monitor {
impl<F> Drop for Monitor<F>
where
F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync,
{
fn drop(&mut self) {
debug!("OSX Runloop dropped");
self.thread.cancel();
unsafe { CFRelease(self.manager as *mut libc::c_void) };
}
}

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

@ -0,0 +1,83 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate libc;
use core_foundation_sys::runloop::*;
use libc::c_void;
use platform::iohid::CFRunLoopEntryObserver;
use platform::iokit::{IOHIDDeviceRef, SendableRunLoop};
use platform::monitor::Monitor;
use std::io;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use util::{io_err, to_io_err, OnceCallback};
// A transaction will run the given closure in a new thread, thereby using a
// separate per-thread state machine for each HID. It will either complete or
// fail through user action, timeout, or be cancelled when overridden by a new
// transaction.
pub struct Transaction {
runloop: SendableRunLoop,
thread: Option<thread::JoinHandle<()>>,
}
impl Transaction {
pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
where
F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync + Send + 'static,
T: 'static,
{
let (tx, rx) = channel();
let cbc = callback.clone();
let timeout = (timeout as f64) / 1000.0;
let builder = thread::Builder::new();
let thread = builder.spawn(move || {
// Add a runloop observer that will be notified when we enter the
// runloop and tx.send() the current runloop to the owning thread.
// We need to ensure the runloop was entered before unblocking
// Transaction::new(), so we can always properly cancel.
let context = &tx as *const _ as *mut c_void;
let obs = CFRunLoopEntryObserver::new(Transaction::observe, context);
obs.add_to_current_runloop();
// Create a new HID device monitor and start polling.
let mut monitor = Monitor::new(new_device_cb);
try_or!(monitor.start(), |e| cbc.call(Err(e)));
// This will block until completion, abortion, or timeout.
unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) };
// Close the monitor and its devices.
monitor.stop();
// Send an error, if the callback wasn't called already.
cbc.call(Err(io_err("aborted or timed out")));
})?;
// Block until we enter the CFRunLoop.
let runloop = rx.recv().map_err(to_io_err)?;
Ok(Self {
runloop,
thread: Some(thread),
})
}
extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };
// Send the current runloop to the receiver to unblock it.
let _ = tx.send(SendableRunLoop(unsafe { CFRunLoopGetCurrent() }));
}
pub fn cancel(&mut self) {
// (This call doesn't block.)
unsafe { CFRunLoopStop(*self.runloop) };
// This must never be None. Ignore return value.
let _ = self.thread.take().unwrap().join();
}
}

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

@ -19,7 +19,7 @@ interface PublicKeyCredential : Credential {
[SecureContext]
partial interface PublicKeyCredential {
static Promise<boolean> isPlatformAuthenticatorAvailable();
static Promise<boolean> isUserVerifyingPlatformAuthenticatorAvailable();
};
[SecureContext, Pref="security.webauth.webauthn"]
@ -124,4 +124,5 @@ typedef long COSEAlgorithmIdentifier;
typedef sequence<AAGUID> AuthenticatorSelectionList;
typedef BufferSource AAGUID;
typedef BufferSource AAGUID;

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

@ -2160,15 +2160,15 @@ XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
}
void
XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
XULDocument::TraceProtos(JSTracer* aTrc)
{
uint32_t i, count = mPrototypes.Length();
for (i = 0; i < count; ++i) {
mPrototypes[i]->TraceProtos(aTrc, aGCNumber);
mPrototypes[i]->TraceProtos(aTrc);
}
if (mCurrentPrototype) {
mCurrentPrototype->TraceProtos(aTrc, aGCNumber);
mCurrentPrototype->TraceProtos(aTrc);
}
}

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

@ -188,7 +188,7 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument)
void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber);
void TraceProtos(JSTracer* aTrc);
// WebIDL API
already_AddRefed<nsINode> GetPopupNode();

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

@ -530,14 +530,17 @@ nsXULPrototypeDocument::NotifyLoadDone()
}
void
nsXULPrototypeDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
nsXULPrototypeDocument::TraceProtos(JSTracer* aTrc)
{
// Only trace the protos once per GC.
if (mGCNumber == aGCNumber) {
return;
// Only trace the protos once per GC if we are marking.
if (aTrc->isMarkingTracer()) {
uint32_t currentGCNumber = aTrc->gcNumberForMarking();
if (mGCNumber == currentGCNumber) {
return;
}
mGCNumber = currentGCNumber;
}
mGCNumber = aGCNumber;
if (mRoot) {
mRoot->TraceAllScripts(aTrc);
}

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

@ -114,7 +114,7 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber);
void TraceProtos(JSTracer* aTrc);
protected:
nsCOMPtr<nsIURI> mURI;

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

@ -257,7 +257,9 @@ PaintThread::AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
gfxCriticalNote << "Failed to prepare buffers on the paint thread.";
}
aBridge->NotifyFinishedAsyncPrepareBuffer(aState);
if (aBridge) {
aBridge->NotifyFinishedAsyncPrepareBuffer(aState);
}
}
void

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

@ -616,6 +616,10 @@ ContentClientBasic::CreateBuffer(gfxContentType aType,
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
}
if (!drawTarget) {
return nullptr;
}
return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect, IntPoint(0,0));
}

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

@ -2908,6 +2908,12 @@ imgCacheValidator::AddProxy(imgRequestProxy* aProxy)
mProxies.AppendObject(aProxy);
}
void
imgCacheValidator::RemoveProxy(imgRequestProxy* aProxy)
{
mProxies.RemoveObject(aProxy);
}
/** nsIRequestObserver methods **/
NS_IMETHODIMP

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

@ -555,6 +555,7 @@ public:
bool forcePrincipalCheckForCacheEntry);
void AddProxy(imgRequestProxy* aProxy);
void RemoveProxy(imgRequestProxy* aProxy);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER

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

@ -517,8 +517,14 @@ imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
mCanceled = true;
mForceDispatchLoadGroup = true;
if (GetOwner()) {
GetOwner()->RemoveProxy(this, aStatus);
imgRequest* owner = GetOwner();
if (owner) {
imgCacheValidator* validator = owner->GetValidator();
if (validator) {
validator->RemoveProxy(this);
}
owner->RemoveProxy(this, aStatus);
}
RemoveFromLoadGroup();

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

@ -91,6 +91,10 @@ class JS_PUBLIC_API(JSTracer)
bool checkEdges() { return checkEdges_; }
#endif
// Get the current GC number. Only call this method if |isMarkingTracer()|
// is true.
uint32_t gcNumberForMarking() const;
protected:
JSTracer(JSRuntime* rt, TracerKindTag tag,
WeakMapTraceKind weakTraceKind = TraceWeakMapValues)

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

@ -484,3 +484,10 @@ JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc, void* thing,
JS::CallbackTracer::CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind)
: CallbackTracer(cx->runtime(), weakTraceKind)
{}
uint32_t
JSTracer::gcNumberForMarking() const
{
MOZ_ASSERT(isMarkingTracer());
return runtime()->gc.gcNumber();
}

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

@ -655,9 +655,7 @@ void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
roots->TraceJSAll(trc);
}
JSContext* cx = XPCJSContext::Get()->Context();
dom::TraceBlackJS(trc, JS_GetGCParameter(cx, JSGC_NUMBER),
nsXPConnect::XPConnect()->IsShuttingDown());
dom::TraceBlackJS(trc, nsXPConnect::XPConnect()->IsShuttingDown());
}
void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc)

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

@ -429,7 +429,6 @@ GMPDownloader.prototype = {
log.info("gmpAddon is not valid, will not continue");
return Promise.reject({
target: this,
status,
type: "downloaderr"
});
}