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