Merge inbound to mozilla-central. a=merge

This commit is contained in:
Ciure Andrei 2018-02-22 23:55:25 +02:00
Родитель 68ba2c72a2 1e98fe5231
Коммит 852a0c8890
133 изменённых файлов: 4698 добавлений и 920 удалений

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

@ -121,6 +121,7 @@ skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
[browser_net_filter-flags.js]
[browser_net_footer-summary.js]
[browser_net_headers-alignment.js]
[browser_net_headers_filter.js]
[browser_net_headers_sorted.js]
[browser_net_image-tooltip.js]
[browser_net_json-b64.js]

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

@ -0,0 +1,72 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if Request-Headers and Response-Headers are correctly filtered in Headers tab.
*/
add_task(async function () {
let { tab, monitor } = await initNetMonitor(SIMPLE_SJS);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, 1);
tab.linkedBrowser.reload();
await wait;
wait = waitUntil(() => document.querySelector(".headers-overview"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
await wait;
await waitUntil(() => {
let request = getSortedRequests(store.getState()).get(0);
return request.requestHeaders && request.responseHeaders;
});
document.querySelectorAll(".devtools-filterinput")[1].focus();
EventUtils.synthesizeKey("con", {});
await waitUntil(() => document.querySelector(".treeRow.hidden"));
info("Check if Headers are filtered correctly");
let totalResponseHeaders = ["cache-control", "connection", "content-length",
"content-type", "date", "expires", "foo-bar",
"foo-bar", "foo-bar", "pragma", "server", "set-cookie",
"set-cookie"];
let expectedResponseHeaders = ["cache-control", "connection", "content-length",
"content-type"];
let expectedRequestHeaders = ["Cache-Control", "Connection"];
let labelCells = document.querySelectorAll(".treeLabelCell");
let filteredResponseHeaders = [];
let filteredRequestHeaders = [];
let responseHeadersLength = totalResponseHeaders.length;
for (let i = 1; i < responseHeadersLength + 1; i++) {
if (labelCells[i].offsetHeight > 0) {
filteredResponseHeaders.push(labelCells[i].innerText);
}
}
for (let i = responseHeadersLength + 2; i < labelCells.length; i++) {
if (labelCells[i].offsetHeight > 0) {
filteredRequestHeaders.push(labelCells[i].innerText);
}
}
is(filteredResponseHeaders.toString(), expectedResponseHeaders.toString(),
"Response Headers are filtered");
is(filteredRequestHeaders.toString(), expectedRequestHeaders.toString(),
"Request Headers are filtered");
await teardown(monitor);
});

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

@ -82,7 +82,7 @@
/* Filtering */
.treeTable .treeRow.hidden {
display: none;
display: none !important;
}
.treeTable .treeValueCellDivider {

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

@ -143,5 +143,23 @@ CredentialsContainer::Store(const Credential& aCredential, ErrorResult& aRv)
return mManager->Store(aCredential);
}
already_AddRefed<Promise>
CredentialsContainer::PreventSilentAccess(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
promise->MaybeResolveWithUndefined();
return promise.forget();
}
} // namespace dom
} // namespace mozilla

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

@ -41,6 +41,9 @@ public:
already_AddRefed<Promise>
Store(const Credential& aCredential, ErrorResult& aRv);
already_AddRefed<Promise>
PreventSilentAccess(ErrorResult& aRv);
private:
~CredentialsContainer();

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

@ -42,6 +42,15 @@ function testSameOrigin() {
.catch(function sameOriginCatch(aResult) {
local_ok(false, "Should not have failed " + aResult);
})
.then(function sameOriginPreventSilentAccess() {
return navigator.credentials.preventSilentAccess();
})
.then(function sameOriginPreventSilentAccessThen(aResult) {
local_ok(aResult == undefined, "PreventSilentAccess worked " + aResult);
})
.catch(function sameOriginPreventSilentAccessCatch(aResult) {
local_ok(false, "Should not have failed " + aResult);
})
.then(function() {
local_finished();
});
@ -58,6 +67,15 @@ function testCrossOrigin() {
local_ok(aResult.toString().startsWith("NotAllowedError"),
"Expecting a NotAllowedError, received " + aResult);
})
.then(function crossOriginPreventSilentAccess() {
return navigator.credentials.preventSilentAccess();
})
.then(function crossOriginPreventSilentAccessThen(aResult) {
local_ok(aResult == undefined, "PreventSilentAccess worked " + aResult);
})
.catch(function crossOriginPreventSilentAccessCatch(aResult) {
local_ok(false, "Should not have failed " + aResult);
})
.then(function() {
local_finished();
});

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

@ -9,6 +9,7 @@
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/dom/WebAuthnUtil.h"
#include "nsContentUtils.h"
#include "nsICryptoHash.h"
#include "nsIEffectiveTLDService.h"
@ -128,110 +129,6 @@ RegisteredKeysToScopedCredentialList(const nsAString& aAppId,
}
}
enum class U2FOperation
{
Register,
Sign
};
static ErrorCode
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
{
// Facet is the specification's way of referring to the web origin.
nsAutoCString facetString = NS_ConvertUTF16toUTF8(aOrigin);
nsCOMPtr<nsIURI> facetUri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(facetUri), facetString))) {
return ErrorCode::BAD_REQUEST;
}
// If the facetId (origin) is not HTTPS, reject
bool facetIsHttps = false;
if (NS_FAILED(facetUri->SchemeIs("https", &facetIsHttps)) || !facetIsHttps) {
return ErrorCode::BAD_REQUEST;
}
// If the appId is empty or null, overwrite it with the facetId and accept
if (aAppId.IsEmpty() || aAppId.EqualsLiteral("null")) {
aAppId.Assign(aOrigin);
return ErrorCode::OK;
}
// AppID is user-supplied. It's quite possible for this parse to fail.
nsAutoCString appIdString = NS_ConvertUTF16toUTF8(aAppId);
nsCOMPtr<nsIURI> appIdUri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(appIdUri), appIdString))) {
return ErrorCode::BAD_REQUEST;
}
// if the appId URL is not HTTPS, reject.
bool appIdIsHttps = false;
if (NS_FAILED(appIdUri->SchemeIs("https", &appIdIsHttps)) || !appIdIsHttps) {
return ErrorCode::BAD_REQUEST;
}
nsAutoCString appIdHost;
if (NS_FAILED(appIdUri->GetAsciiHost(appIdHost))) {
return ErrorCode::BAD_REQUEST;
}
// Allow localhost.
if (appIdHost.EqualsLiteral("localhost")) {
nsAutoCString facetHost;
if (NS_FAILED(facetUri->GetAsciiHost(facetHost))) {
return ErrorCode::BAD_REQUEST;
}
if (facetHost.EqualsLiteral("localhost")) {
return ErrorCode::OK;
}
}
// Run the HTML5 algorithm to relax the same-origin policy, copied from W3C
// Web Authentication. See Bug 1244959 comment #8 for context on why we are
// doing this instead of implementing the external-fetch FacetID logic.
nsCOMPtr<nsIDocument> document = aParent->GetDoc();
if (!document || !document->IsHTMLDocument()) {
return ErrorCode::BAD_REQUEST;
}
nsHTMLDocument* html = document->AsHTMLDocument();
if (NS_WARN_IF(!html)) {
return ErrorCode::BAD_REQUEST;
}
// Use the base domain as the facet for evaluation. This lets this algorithm
// relax the whole eTLD+1.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
return ErrorCode::BAD_REQUEST;
}
nsAutoCString lowestFacetHost;
if (NS_FAILED(tldService->GetBaseDomain(facetUri, 0, lowestFacetHost))) {
return ErrorCode::BAD_REQUEST;
}
MOZ_LOG(gU2FLog, LogLevel::Debug,
("AppId %s Facet %s", appIdHost.get(), lowestFacetHost.get()));
if (html->IsRegistrableDomainSuffixOfOrEqualTo(NS_ConvertUTF8toUTF16(lowestFacetHost),
appIdHost)) {
return ErrorCode::OK;
}
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
if (aOp == U2FOperation::Sign && lowestFacetHost.EqualsLiteral("google.com") &&
(aAppId.Equals(kGoogleAccountsAppId1) ||
aAppId.Equals(kGoogleAccountsAppId2))) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("U2F permitted for Google Accounts via Bug #1436085"));
return ErrorCode::OK;
}
return ErrorCode::BAD_REQUEST;
}
static nsresult
BuildTransactionHashes(const nsCString& aRpId,
const nsCString& aClientDataJSON,
@ -375,13 +272,10 @@ U2F::Register(const nsAString& aAppId,
}
// Evaluate the AppID
nsString adjustedAppId;
adjustedAppId.Assign(aAppId);
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, U2FOperation::Register,
adjustedAppId);
if (appIdResult != ErrorCode::OK) {
nsString adjustedAppId(aAppId);
if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Register, adjustedAppId)) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(appIdResult));
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
ExecuteCallback(response, callback);
return;
}
@ -538,13 +432,10 @@ U2F::Sign(const nsAString& aAppId,
}
// Evaluate the AppID
nsString adjustedAppId;
adjustedAppId.Assign(aAppId);
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, U2FOperation::Sign,
adjustedAppId);
if (appIdResult != ErrorCode::OK) {
nsString adjustedAppId(aAppId);
if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Sign, adjustedAppId)) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(appIdResult));
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
ExecuteCallback(response, callback);
return;
}

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

@ -30,8 +30,20 @@ struct WebAuthnScopedCredential {
uint8_t transports;
};
struct WebAuthnExtension {
/* TODO Fill in with predefined extensions */
struct WebAuthnExtensionAppId {
uint8_t[] AppId;
};
union WebAuthnExtension {
WebAuthnExtensionAppId;
};
struct WebAuthnExtensionResultAppId {
bool AppId;
};
union WebAuthnExtensionResult {
WebAuthnExtensionResultAppId;
};
struct WebAuthnMakeCredentialInfo {
@ -57,8 +69,10 @@ struct WebAuthnGetAssertionInfo {
};
struct WebAuthnGetAssertionResult {
uint8_t[] RpIdHash;
uint8_t[] CredentialID;
uint8_t[] SigBuffer;
WebAuthnExtensionResult[] Extensions;
};
async protocol PWebAuthnTransaction {

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

@ -115,6 +115,19 @@ PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(GlobalObject&
return promise.forget();
}
void
PublicKeyCredential::GetClientExtensionResults(AuthenticationExtensionsClientOutputs& aResult)
{
aResult = mClientExtensionOutputs;
}
void
PublicKeyCredential::SetClientExtensionResultAppId(bool aResult)
{
mClientExtensionOutputs.mAppid.Construct();
mClientExtensionOutputs.mAppid.Value() = aResult;
}
} // namespace dom
} // namespace mozilla

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

@ -49,12 +49,17 @@ public:
static already_AddRefed<Promise>
IsUserVerifyingPlatformAuthenticatorAvailable(GlobalObject& aGlobal);
void
GetClientExtensionResults(AuthenticationExtensionsClientOutputs& aResult);
void
SetClientExtensionResultAppId(bool aResult);
private:
CryptoBuffer mRawId;
JS::Heap<JSObject*> mRawIdCachedObj;
RefPtr<AuthenticatorResponse> mResponse;
// Extensions are not supported yet.
// <some type> mClientExtensionResults;
AuthenticationExtensionsClientOutputs mClientExtensionOutputs;
};
} // namespace dom

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

@ -127,6 +127,7 @@ U2FHIDTokenManager::Register(const nsTArray<WebAuthnScopedCredential>& aCredenti
}
ClearPromises();
mCurrentAppId = aApplication;
mTransactionId = rust_u2f_mgr_register(mU2FManager,
registerFlags,
(uint64_t)aTimeoutMS,
@ -164,6 +165,7 @@ RefPtr<U2FSignPromise>
U2FHIDTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
const nsTArray<WebAuthnExtension>& aExtensions,
bool aRequireUserVerification,
uint32_t aTimeoutMS)
{
@ -176,15 +178,25 @@ U2FHIDTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
signFlags |= U2F_FLAG_REQUIRE_USER_VERIFICATION;
}
nsTArray<nsTArray<uint8_t>> appIds;
appIds.AppendElement(aApplication);
// Process extensions.
for (const WebAuthnExtension& ext: aExtensions) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
}
}
ClearPromises();
mCurrentAppId = aApplication;
mTransactionId = rust_u2f_mgr_sign(mU2FManager,
signFlags,
(uint64_t)aTimeoutMS,
u2f_sign_callback,
aChallenge.Elements(),
aChallenge.Length(),
aApplication.Elements(),
aApplication.Length(),
U2FAppIds(appIds).Get(),
U2FKeyHandles(aCredentials).Get());
if (mTransactionId == 0) {
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
@ -234,6 +246,12 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
MOZ_ASSERT(!mSignPromise.IsEmpty());
nsTArray<uint8_t> appId;
if (!aResult->CopyAppId(appId)) {
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
return;
}
nsTArray<uint8_t> keyHandle;
if (!aResult->CopyKeyHandle(keyHandle)) {
mSignPromise.Reject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
@ -246,7 +264,14 @@ U2FHIDTokenManager::HandleSignResult(UniquePtr<U2FResult>&& aResult)
return;
}
WebAuthnGetAssertionResult result(keyHandle, signature);
nsTArray<WebAuthnExtensionResult> extensions;
if (appId != mCurrentAppId) {
// Indicate to the RP that we used the FIDO appId.
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
}
WebAuthnGetAssertionResult result(appId, keyHandle, signature, extensions);
mSignPromise.Resolve(Move(result), __func__);
}

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

@ -18,13 +18,32 @@
namespace mozilla {
namespace dom {
class U2FAppIds {
public:
explicit U2FAppIds(const nsTArray<nsTArray<uint8_t>>& aApplications)
{
mAppIds = rust_u2f_app_ids_new();
for (auto& app_id: aApplications) {
rust_u2f_app_ids_add(mAppIds, app_id.Elements(), app_id.Length());
}
}
rust_u2f_app_ids* Get() { return mAppIds; }
~U2FAppIds() { rust_u2f_app_ids_free(mAppIds); }
private:
rust_u2f_app_ids* mAppIds;
};
class U2FKeyHandles {
public:
explicit U2FKeyHandles(const nsTArray<WebAuthnScopedCredential>& aCredentials)
{
mKeyHandles = rust_u2f_khs_new();
for (auto cred: aCredentials) {
for (auto& cred: aCredentials) {
rust_u2f_khs_add(mKeyHandles,
cred.id().Elements(),
cred.id().Length(),
@ -66,6 +85,11 @@ public:
return CopyBuffer(U2F_RESBUF_ID_SIGNATURE, aBuffer);
}
bool CopyAppId(nsTArray<uint8_t>& aBuffer)
{
return CopyBuffer(U2F_RESBUF_ID_APPID, aBuffer);
}
private:
bool CopyBuffer(uint8_t aResBufID, nsTArray<uint8_t>& aBuffer) {
if (!mResult) {
@ -104,6 +128,7 @@ public:
Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
const nsTArray<WebAuthnExtension>& aExtensions,
bool aRequireUserVerification,
uint32_t aTimeoutMS) override;
@ -123,6 +148,7 @@ private:
rust_u2f_manager* mU2FManager;
uint64_t mTransactionId;
nsTArray<uint8_t> mCurrentAppId;
MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
MozPromiseHolder<U2FSignPromise> mSignPromise;
};

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

@ -693,6 +693,27 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredential>& aCredent
return U2FRegisterPromise::CreateAndResolve(Move(result), __func__);
}
bool
U2FSoftTokenManager::FindRegisteredKeyHandle(const nsTArray<nsTArray<uint8_t>>& aAppIds,
const nsTArray<WebAuthnScopedCredential>& aCredentials,
/*out*/ nsTArray<uint8_t>& aKeyHandle,
/*out*/ nsTArray<uint8_t>& aAppId)
{
for (const nsTArray<uint8_t>& app_id: aAppIds) {
for (const WebAuthnScopedCredential& cred: aCredentials) {
bool isRegistered = false;
nsresult rv = IsRegistered(cred.id(), app_id, isRegistered);
if (NS_SUCCEEDED(rv) && isRegistered) {
aKeyHandle.Assign(cred.id());
aAppId.Assign(app_id);
return true;
}
}
}
return false;
}
// A U2F Sign operation creates a signature over the "param" arguments (plus
// some other stuff) using the private key indicated in the key handle argument.
//
@ -713,6 +734,7 @@ RefPtr<U2FSignPromise>
U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
const nsTArray<WebAuthnExtension>& aExtensions,
bool aRequireUserVerification,
uint32_t aTimeoutMS)
{
@ -728,18 +750,21 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
nsTArray<uint8_t> keyHandle;
for (auto cred: aCredentials) {
bool isRegistered = false;
nsresult rv = IsRegistered(cred.id(), aApplication, isRegistered);
if (NS_SUCCEEDED(rv) && isRegistered) {
keyHandle.Assign(cred.id());
break;
nsTArray<nsTArray<uint8_t>> appIds;
appIds.AppendElement(aApplication);
// Process extensions.
for (const WebAuthnExtension& ext: aExtensions) {
if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
}
}
// Fail if we didn't recognize a key id.
if (keyHandle.IsEmpty()) {
nsTArray<uint8_t> chosenAppId(aApplication);
nsTArray<uint8_t> keyHandle;
// Fail if we can't find a valid key handle.
if (!FindRegisteredKeyHandle(appIds, aCredentials, keyHandle, chosenAppId)) {
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
@ -748,10 +773,10 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
if (NS_WARN_IF((aChallenge.Length() != kParamLen) || (aApplication.Length() != kParamLen))) {
if (NS_WARN_IF((aChallenge.Length() != kParamLen) || (chosenAppId.Length() != kParamLen))) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
(uint32_t)aChallenge.Length(), (uint32_t)aApplication.Length(), kParamLen));
(uint32_t)aChallenge.Length(), (uint32_t)chosenAppId.Length(), kParamLen));
return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
}
@ -760,8 +785,8 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials
UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
const_cast<uint8_t*>(keyHandle.Elements()),
keyHandle.Length(),
const_cast<uint8_t*>(aApplication.Elements()),
aApplication.Length());
const_cast<uint8_t*>(chosenAppId.Elements()),
chosenAppId.Length());
if (NS_WARN_IF(!privKey.get())) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!"));
return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
@ -790,7 +815,7 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials
// It's OK to ignore the return values here because we're writing into
// pre-allocated space
signedDataBuf.AppendElements(aApplication.Elements(), aApplication.Length(),
signedDataBuf.AppendElements(chosenAppId.Elements(), chosenAppId.Length(),
mozilla::fallible);
signedDataBuf.AppendElement(0x01, mozilla::fallible);
signedDataBuf.AppendSECItem(counterItem);
@ -832,7 +857,15 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials
signatureBuf.AppendSECItem(counterItem);
signatureBuf.AppendSECItem(signatureItem);
WebAuthnGetAssertionResult result(keyHandle, nsTArray<uint8_t>(signatureBuf));
nsTArray<uint8_t> signature(signatureBuf);
nsTArray<WebAuthnExtensionResult> extensions;
if (chosenAppId != aApplication) {
// Indicate to the RP that we used the FIDO appId.
extensions.AppendElement(WebAuthnExtensionResultAppId(true));
}
WebAuthnGetAssertionResult result(chosenAppId, keyHandle, signature, extensions);
return U2FSignPromise::CreateAndResolve(Move(result), __func__);
}

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

@ -34,6 +34,7 @@ public:
Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
const nsTArray<WebAuthnExtension>& aExtensions,
bool aRequireUserVerification,
uint32_t aTimeoutMS) override;
@ -47,6 +48,12 @@ private:
const nsTArray<uint8_t>& aAppParam,
bool& aResult);
bool
FindRegisteredKeyHandle(const nsTArray<nsTArray<uint8_t>>& aAppIds,
const nsTArray<WebAuthnScopedCredential>& aCredentials,
/*out*/ nsTArray<uint8_t>& aKeyHandle,
/*out*/ nsTArray<uint8_t>& aAppId);
bool mInitialized;
mozilla::UniquePK11SymKey mWrappingKey;

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

@ -322,6 +322,7 @@ U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
mTokenManagerImpl->Sign(aTransactionInfo.AllowList(),
aTransactionInfo.RpIdHash(),
aTransactionInfo.ClientDataHash(),
aTransactionInfo.Extensions(),
aTransactionInfo.RequireUserVerification(),
aTransactionInfo.TimeoutMS())
->Then(GetCurrentThreadSerialEventTarget(), __func__,

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

@ -38,6 +38,7 @@ public:
Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
const nsTArray<WebAuthnExtension>& aExtensions,
bool aRequireUserVerification,
uint32_t aTimeoutMS) = 0;

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

@ -49,8 +49,11 @@ NS_IMPL_ISUPPORTS(WebAuthnManager, nsIDOMEventListener);
**********************************************************************/
static nsresult
AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
const nsAString& aType, /* out */ nsACString& aJsonOut)
AssembleClientData(const nsAString& aOrigin,
const CryptoBuffer& aChallenge,
const nsAString& aType,
const AuthenticationExtensionsClientInputs& aExtensions,
/* out */ nsACString& aJsonOut)
{
MOZ_ASSERT(NS_IsMainThread());
@ -65,6 +68,7 @@ AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
clientDataObject.mChallenge.Assign(challengeBase64);
clientDataObject.mOrigin.Assign(aOrigin);
clientDataObject.mHashAlgorithm.AssignLiteral(u"SHA-256");
clientDataObject.mClientExtensions = aExtensions;
nsAutoString temp;
if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
@ -264,6 +268,12 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
}
}
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
if (aOptions.mExtensions.mAppid.WasPassed()) {
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return promise.forget();
}
CryptoBuffer rpIdHash;
if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
@ -345,7 +355,8 @@ WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptio
nsAutoCString clientDataJSON;
srv = AssembleClientData(origin, challenge,
NS_LITERAL_STRING("webauthn.create"), clientDataJSON);
NS_LITERAL_STRING("webauthn.create"),
aOptions.mExtensions, clientDataJSON);
if (NS_WARN_IF(NS_FAILED(srv))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
@ -537,7 +548,7 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
nsAutoCString clientDataJSON;
srv = AssembleClientData(origin, challenge, NS_LITERAL_STRING("webauthn.get"),
clientDataJSON);
aOptions.mExtensions, clientDataJSON);
if (NS_WARN_IF(NS_FAILED(srv))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
@ -593,14 +604,40 @@ WebAuthnManager::GetAssertion(const PublicKeyCredentialRequestOptions& aOptions,
bool requireUserVerification =
aOptions.mUserVerification == UserVerificationRequirement::Required;
// TODO: Add extension list building
// If extensions was specified, process any extensions supported by this
// If extensions were specified, process any extensions supported by this
// client platform, to produce the extension data that needs to be sent to the
// authenticator. If an error is encountered while processing an extension,
// skip that extension and do not produce any extension data for it. Call the
// result of this processing clientExtensions.
nsTArray<WebAuthnExtension> extensions;
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
if (aOptions.mExtensions.mAppid.WasPassed()) {
nsString appId(aOptions.mExtensions.mAppid.Value());
// Check that the appId value is allowed.
if (!EvaluateAppID(mParent, origin, U2FOperation::Sign, appId)) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
CryptoBuffer appIdHash;
if (!appIdHash.SetLength(SHA256_LENGTH, fallible)) {
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return promise.forget();
}
// We need the SHA-256 hash of the appId.
nsresult srv = HashCString(hashService, NS_ConvertUTF16toUTF8(appId), appIdHash);
if (NS_WARN_IF(NS_FAILED(srv))) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
// Append the hash and send it to the backend.
extensions.AppendElement(WebAuthnExtensionAppId(appIdHash));
}
WebAuthnGetAssertionInfo info(rpIdHash,
clientDataHash,
adjustedTimeout,
@ -807,7 +844,7 @@ WebAuthnManager::FinishGetAssertion(const uint64_t& aTransactionId,
}
CryptoBuffer rpIdHashBuf;
if (!rpIdHashBuf.Assign(mTransaction.ref().mRpIdHash)) {
if (!rpIdHashBuf.Assign(aResult.RpIdHash())) {
RejectTransaction(NS_ERROR_OUT_OF_MEMORY);
return;
}
@ -862,6 +899,14 @@ WebAuthnManager::FinishGetAssertion(const uint64_t& aTransactionId,
credential->SetRawId(credentialBuf);
credential->SetResponse(assertion);
// Forward client extension results.
for (auto& ext: aResult.Extensions()) {
if (ext.type() == WebAuthnExtensionResult::TWebAuthnExtensionResultAppId) {
bool appid = ext.get_WebAuthnExtensionResultAppId().AppId();
credential->SetClientExtensionResultAppId(appid);
}
}
mTransaction.ref().mPromise->MaybeResolve(credential);
ClearTransaction();
}

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

@ -5,11 +5,113 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/WebAuthnUtil.h"
#include "nsIEffectiveTLDService.h"
#include "nsNetUtil.h"
#include "pkixutil.h"
namespace mozilla {
namespace dom {
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
"https://www.gstatic.com/securitykey/origins.json");
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
"https://www.gstatic.com/securitykey/a/google.com/origins.json");
bool
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
{
// Facet is the specification's way of referring to the web origin.
nsAutoCString facetString = NS_ConvertUTF16toUTF8(aOrigin);
nsCOMPtr<nsIURI> facetUri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(facetUri), facetString))) {
return false;
}
// If the facetId (origin) is not HTTPS, reject
bool facetIsHttps = false;
if (NS_FAILED(facetUri->SchemeIs("https", &facetIsHttps)) || !facetIsHttps) {
return false;
}
// If the appId is empty or null, overwrite it with the facetId and accept
if (aAppId.IsEmpty() || aAppId.EqualsLiteral("null")) {
aAppId.Assign(aOrigin);
return true;
}
// AppID is user-supplied. It's quite possible for this parse to fail.
nsAutoCString appIdString = NS_ConvertUTF16toUTF8(aAppId);
nsCOMPtr<nsIURI> appIdUri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(appIdUri), appIdString))) {
return false;
}
// if the appId URL is not HTTPS, reject.
bool appIdIsHttps = false;
if (NS_FAILED(appIdUri->SchemeIs("https", &appIdIsHttps)) || !appIdIsHttps) {
return false;
}
nsAutoCString appIdHost;
if (NS_FAILED(appIdUri->GetAsciiHost(appIdHost))) {
return false;
}
// Allow localhost.
if (appIdHost.EqualsLiteral("localhost")) {
nsAutoCString facetHost;
if (NS_FAILED(facetUri->GetAsciiHost(facetHost))) {
return false;
}
if (facetHost.EqualsLiteral("localhost")) {
return true;
}
}
// Run the HTML5 algorithm to relax the same-origin policy, copied from W3C
// Web Authentication. See Bug 1244959 comment #8 for context on why we are
// doing this instead of implementing the external-fetch FacetID logic.
nsCOMPtr<nsIDocument> document = aParent->GetDoc();
if (!document || !document->IsHTMLDocument()) {
return false;
}
nsHTMLDocument* html = document->AsHTMLDocument();
if (NS_WARN_IF(!html)) {
return false;
}
// Use the base domain as the facet for evaluation. This lets this algorithm
// relax the whole eTLD+1.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
return false;
}
nsAutoCString lowestFacetHost;
if (NS_FAILED(tldService->GetBaseDomain(facetUri, 0, lowestFacetHost))) {
return false;
}
if (html->IsRegistrableDomainSuffixOfOrEqualTo(NS_ConvertUTF8toUTF16(lowestFacetHost),
appIdHost)) {
return true;
}
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
if (aOp == U2FOperation::Sign && lowestFacetHost.EqualsLiteral("google.com") &&
(aAppId.Equals(kGoogleAccountsAppId1) ||
aAppId.Equals(kGoogleAccountsAppId2))) {
return true;
}
return false;
}
nsresult
ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
uint32_t aLen)

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

@ -16,6 +16,17 @@
namespace mozilla {
namespace dom {
enum class U2FOperation
{
Register,
Sign
};
bool
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
const U2FOperation& aOp, /* in/out */ nsString& aAppId);
nsresult
AssembleAuthenticatorData(const CryptoBuffer& rpIdHashBuf,
const uint8_t flags,

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

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
head.js
tab_webauthn_result.html
tab_webauthn_success.html
../cbor/*
@ -8,4 +9,5 @@ support-files =
skip-if = !e10s
[browser_abort_visibility.js]
[browser_fido_appid_extension.js]
[browser_webauthn_telemetry.js]

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

@ -0,0 +1,153 @@
/* 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/. */
"use strict";
const TEST_URL = "https://example.com/";
function arrivingHereIsBad(aResult) {
ok(false, "Bad result! Received a: " + aResult);
}
function expectError(aType) {
let expected = `${aType}Error`;
return function (aResult) {
is(aResult.slice(0, expected.length), expected, `Expecting a ${aType}Error`);
};
}
let expectNotSupportedError = expectError("NotSupported");
let expectNotAllowedError = expectError("NotAllowed");
let expectSecurityError = expectError("Security");
function promiseU2FRegister(tab, app_id) {
let challenge = crypto.getRandomValues(new Uint8Array(16));
challenge = bytesToBase64UrlSafe(challenge);
return ContentTask.spawn(tab.linkedBrowser, [app_id, challenge], function ([app_id, challenge]) {
return new Promise(resolve => {
content.u2f.register(app_id, [{version: "U2F_V2", challenge}], [], resolve);
});
}).then(res => {
is(res.errorCode, 0, "u2f.register() succeeded");
let data = base64ToBytesUrlSafe(res.registrationData);
is(data[0], 0x05, "Reserved byte is correct");
return data.slice(67, 67 + data[66]);
});
}
function promiseWebAuthnRegister(tab, appid) {
return ContentTask.spawn(tab.linkedBrowser, [appid], ([appid]) => {
const cose_alg_ECDSA_w_SHA256 = -7;
let challenge = content.crypto.getRandomValues(new Uint8Array(16));
let pubKeyCredParams = [{
type: "public-key",
alg: cose_alg_ECDSA_w_SHA256
}];
let publicKey = {
rp: {id: content.document.domain, name: "none", icon: "none"},
user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
pubKeyCredParams,
extensions: {appid},
challenge
};
return content.navigator.credentials.create({publicKey})
.then(res => res.rawId);
});
}
function promiseWebAuthnSign(tab, key_handle, extensions = {}) {
return ContentTask.spawn(tab.linkedBrowser, [key_handle, extensions],
([key_handle, extensions]) => {
let challenge = content.crypto.getRandomValues(new Uint8Array(16));
let credential = {
id: key_handle,
type: "public-key",
transports: ["usb"]
};
let publicKey = {
challenge,
extensions,
rpId: content.document.domain,
allowCredentials: [credential],
};
return content.navigator.credentials.get({publicKey})
.then(credential => {
return {
authenticatorData: credential.response.authenticatorData,
clientDataJSON: credential.response.clientDataJSON,
extensions: credential.getClientExtensionResults()
};
})
});
}
add_task(function test_setup() {
Services.prefs.setBoolPref("security.webauth.u2f", true);
Services.prefs.setBoolPref("security.webauth.webauthn", true);
Services.prefs.setBoolPref("security.webauth.webauthn_enable_softtoken", true);
Services.prefs.setBoolPref("security.webauth.webauthn_enable_usbtoken", false);
});
add_task(async function test_appid() {
// Open a new tab.
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
// Get a keyHandle for a FIDO AppId.
let appid = "https://example.com/appId";
let keyHandle = await promiseU2FRegister(tab, appid);
// The FIDO AppId extension can't be used for MakeCredential.
await promiseWebAuthnRegister(tab, appid)
.then(arrivingHereIsBad)
.catch(expectNotSupportedError);
// Using the keyHandle shouldn't work without the FIDO AppId extension.
await promiseWebAuthnSign(tab, keyHandle)
.then(arrivingHereIsBad)
.catch(expectNotAllowedError);
// Invalid app IDs (for the current origin) must be rejected.
await promiseWebAuthnSign(tab, keyHandle, {appid: "https://bogus.com/appId"})
.then(arrivingHereIsBad)
.catch(expectSecurityError);
// Non-matching app IDs must be rejected.
await promiseWebAuthnSign(tab, keyHandle, {appid: appid + "2"})
.then(arrivingHereIsBad)
.catch(expectNotAllowedError);
let rpId = new TextEncoder("utf-8").encode(appid);
let rpIdHash = await crypto.subtle.digest("SHA-256", rpId);
// Succeed with the right fallback rpId.
await promiseWebAuthnSign(tab, keyHandle, {appid})
.then(({authenticatorData, clientDataJSON, extensions}) => {
is(extensions.appid, true, "appid extension was acted upon");
// Check that the correct rpIdHash is returned.
let rpIdHashSign = authenticatorData.slice(0, 32);
ok(memcmp(rpIdHash, rpIdHashSign), "rpIdHash is correct");
let clientData = JSON.parse(buffer2string(clientDataJSON));
is(clientData.clientExtensions.appid, appid, "appid extension sent");
});
// Close tab.
await BrowserTestUtils.removeTab(tab);
});
add_task(function test_cleanup() {
Services.prefs.clearUserPref("security.webauth.u2f");
Services.prefs.clearUserPref("security.webauth.webauthn");
Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
});

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

@ -0,0 +1,65 @@
/* 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/. */
"use strict";
function bytesToBase64(u8a){
let CHUNK_SZ = 0x8000;
let c = [];
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return window.btoa(c.join(""));
}
function bytesToBase64UrlSafe(buf) {
return bytesToBase64(buf)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
function base64ToBytes(b64encoded) {
return new Uint8Array(window.atob(b64encoded).split("").map(function(c) {
return c.charCodeAt(0);
}));
}
function base64ToBytesUrlSafe(str) {
if (!str || str.length % 4 == 1) {
throw "Improper b64 string";
}
var b64 = str.replace(/\-/g, "+").replace(/\_/g, "/");
while (b64.length % 4 != 0) {
b64 += "=";
}
return base64ToBytes(b64);
}
function buffer2string(buf) {
let str = "";
if (!(buf.constructor === Uint8Array)) {
buf = new Uint8Array(buf);
}
buf.map(function(x){ return str += String.fromCharCode(x) });
return str;
}
function memcmp(x, y) {
let xb = new Uint8Array(x);
let yb = new Uint8Array(y);
if (x.byteLength != y.byteLength) {
return false;
}
for (let i = 0; i < xb.byteLength; ++i) {
if (xb[i] != yb[i]) {
return false;
}
}
return true;
}

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

@ -68,6 +68,10 @@ function() {
is(clientData.hashAlgorithm, "SHA-256", "Hash algorithm is correct");
is(clientData.type, "webauthn.create", "Type is correct");
let extensions = aCredInfo.getClientExtensionResults();
is(extensions.appid, undefined, "appid extension wasn't used");
is(clientData.clientExtensions.appid, undefined, "appid extension wasn't sent");
return webAuthnDecodeCBORAttestation(aCredInfo.response.attestationObject)
.then(function(aAttestationObj) {
// Make sure the RP ID hash matches what we calculate.

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

@ -82,7 +82,7 @@ fn main() {
flags,
15_000,
chall_bytes,
app_bytes,
vec![app_bytes],
vec![key_handle],
move |rv| { tx.send(rv.unwrap()).unwrap(); },
)

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

@ -9,6 +9,7 @@ use std::{ptr, slice};
use U2FManager;
type U2FAppIds = Vec<::AppId>;
type U2FKeyHandles = Vec<::KeyHandle>;
type U2FResult = HashMap<u8, Vec<u8>>;
type U2FCallback = extern "C" fn(u64, *mut U2FResult);
@ -16,6 +17,7 @@ type U2FCallback = extern "C" fn(u64, *mut U2FResult);
const RESBUF_ID_REGISTRATION: u8 = 0;
const RESBUF_ID_KEYHANDLE: u8 = 1;
const RESBUF_ID_SIGNATURE: u8 = 2;
const RESBUF_ID_APPID: u8 = 3;
// Generates a new 64-bit transaction id with collision probability 2^-32.
fn new_tid() -> u64 {
@ -42,6 +44,27 @@ pub unsafe extern "C" fn rust_u2f_mgr_free(mgr: *mut U2FManager) {
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_new() -> *mut U2FAppIds {
Box::into_raw(Box::new(vec![]))
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_add(
ids: *mut U2FAppIds,
id_ptr: *const u8,
id_len: usize
) {
(*ids).push(from_raw(id_ptr, id_len));
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_app_ids_free(ids: *mut U2FAppIds) {
if !ids.is_null() {
Box::from_raw(ids);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_u2f_khs_new() -> *mut U2FKeyHandles {
Box::into_raw(Box::new(vec![]))
@ -165,8 +188,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
callback: U2FCallback,
challenge_ptr: *const u8,
challenge_len: usize,
application_ptr: *const u8,
application_len: usize,
app_ids: *const U2FAppIds,
khs: *const U2FKeyHandles,
) -> u64 {
if mgr.is_null() || khs.is_null() {
@ -174,13 +196,18 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
}
// Check buffers.
if challenge_ptr.is_null() || application_ptr.is_null() {
if challenge_ptr.is_null() {
return 0;
}
// Need at least one app_id.
if (*app_ids).len() < 1 {
return 0;
}
let flags = ::SignFlags::from_bits_truncate(flags);
let challenge = from_raw(challenge_ptr, challenge_len);
let application = from_raw(application_ptr, application_len);
let app_ids = (*app_ids).clone();
let key_handles = (*khs).clone();
let tid = new_tid();
@ -188,13 +215,14 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
flags,
timeout,
challenge,
application,
app_ids,
key_handles,
move |rv| {
if let Ok((key_handle, signature)) = rv {
if let Ok((app_id, key_handle, signature)) = rv {
let mut result = U2FResult::new();
result.insert(RESBUF_ID_KEYHANDLE, key_handle);
result.insert(RESBUF_ID_SIGNATURE, signature);
result.insert(RESBUF_ID_APPID, app_id);
callback(tid, Box::into_raw(Box::new(result)));
} else {
callback(tid, ptr::null_mut());

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

@ -75,6 +75,10 @@ pub struct KeyHandle {
pub transports: AuthenticatorTransports,
}
pub type AppId = Vec<u8>;
pub type RegisterResult = Vec<u8>;
pub type SignResult = (AppId, Vec<u8>, Vec<u8>);
#[cfg(fuzzing)]
pub use u2fprotocol::*;
#[cfg(fuzzing)]

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

@ -16,17 +16,17 @@ enum QueueAction {
flags: ::RegisterFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
application: ::AppId,
key_handles: Vec<::KeyHandle>,
callback: OnceCallback<Vec<u8>>,
callback: OnceCallback<::RegisterResult>,
},
Sign {
flags: ::SignFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
app_ids: Vec<::AppId>,
key_handles: Vec<::KeyHandle>,
callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
callback: OnceCallback<::SignResult>,
},
Cancel,
}
@ -68,7 +68,7 @@ impl U2FManager {
flags,
timeout,
challenge,
application,
app_ids,
key_handles,
callback,
}) => {
@ -77,7 +77,7 @@ impl U2FManager {
flags,
timeout,
challenge,
application,
app_ids,
key_handles,
callback,
);
@ -109,12 +109,12 @@ impl U2FManager {
flags: ::RegisterFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
application: ::AppId,
key_handles: Vec<::KeyHandle>,
callback: F,
) -> io::Result<()>
where
F: FnOnce(io::Result<Vec<u8>>),
F: FnOnce(io::Result<::RegisterResult>),
F: Send + 'static,
{
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
@ -150,21 +150,37 @@ impl U2FManager {
flags: ::SignFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
app_ids: Vec<::AppId>,
key_handles: Vec<::KeyHandle>,
callback: F,
) -> io::Result<()>
where
F: FnOnce(io::Result<(Vec<u8>, Vec<u8>)>),
F: FnOnce(io::Result<::SignResult>),
F: Send + 'static,
{
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
if challenge.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid parameter sizes",
));
}
if app_ids.len() < 1 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"No app IDs given",
));
}
for app_id in &app_ids {
if app_id.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid app_id size",
));
}
}
for key_handle in &key_handles {
if key_handle.credential.len() > 256 {
return Err(io::Error::new(
@ -179,7 +195,7 @@ impl U2FManager {
flags,
timeout,
challenge,
application,
app_ids,
key_handles,
callback,
};

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

@ -14,6 +14,31 @@ fn is_valid_transport(transports: ::AuthenticatorTransports) -> bool {
transports.is_empty() || transports.contains(::AuthenticatorTransports::USB)
}
fn find_valid_key_handles<'a, F>(
app_ids: &'a Vec<::AppId>,
key_handles: &'a Vec<::KeyHandle>,
mut is_valid: F,
) -> (&'a ::AppId, Vec<&'a ::KeyHandle>)
where
F: FnMut(&Vec<u8>, &::KeyHandle) -> bool,
{
// Try all given app_ids in order.
for app_id in app_ids {
// Find all valid key handles for the current app_id.
let valid_handles = key_handles
.iter()
.filter(|key_handle| is_valid(app_id, key_handle))
.collect::<Vec<_>>();
// If there's at least one, stop.
if valid_handles.len() > 0 {
return (app_id, valid_handles);
}
}
return (&app_ids[0], vec![]);
}
#[derive(Default)]
pub struct StateMachine {
transaction: Option<Transaction>,
@ -29,9 +54,9 @@ impl StateMachine {
flags: ::RegisterFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
application: ::AppId,
key_handles: Vec<::KeyHandle>,
callback: OnceCallback<Vec<u8>>,
callback: OnceCallback<::RegisterResult>,
) {
// Abort any prior register/sign calls.
self.cancel();
@ -93,9 +118,9 @@ impl StateMachine {
flags: ::SignFlags,
timeout: u64,
challenge: Vec<u8>,
application: Vec<u8>,
app_ids: Vec<::AppId>,
key_handles: Vec<::KeyHandle>,
callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
callback: OnceCallback<::SignResult>,
) {
// Abort any prior register/sign calls.
self.cancel();
@ -125,14 +150,15 @@ impl StateMachine {
return;
}
// Find all matching key handles.
let key_handles = key_handles
.iter()
.filter(|key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
.unwrap_or(false) /* no match on failure */
})
.collect::<Vec<_>>();
// For each appId, try all key handles. If there's at least one
// valid key handle for an appId, we'll use that appId below.
let (app_id, valid_handles) =
find_valid_key_handles(&app_ids, &key_handles,
|app_id, key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, app_id,
&key_handle.credential)
.unwrap_or(false) /* no match on failure */
});
// Aggregate distinct transports from all given credentials.
let transports = key_handles.iter().fold(
@ -149,7 +175,7 @@ impl StateMachine {
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() {
if valid_handles.is_empty() {
let blank = vec![0u8; PARAMETER_SIZE];
if let Ok(_) = u2f_register(dev, &blank, &blank) {
callback.call(Err(io_err("invalid key")));
@ -157,15 +183,17 @@ impl StateMachine {
}
} else {
// Otherwise, try to sign.
for key_handle in &key_handles {
for key_handle in &valid_handles {
if let Ok(bytes) = u2f_sign(
dev,
&challenge,
&application,
app_id,
&key_handle.credential,
)
{
callback.call(Ok((key_handle.credential.clone(), bytes)));
callback.call(Ok((app_id.clone(),
key_handle.credential.clone(),
bytes)));
break;
}
}

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

@ -14,6 +14,7 @@ extern "C" {
const uint8_t U2F_RESBUF_ID_REGISTRATION = 0;
const uint8_t U2F_RESBUF_ID_KEYHANDLE = 1;
const uint8_t U2F_RESBUF_ID_SIGNATURE = 2;
const uint8_t U2F_RESBUF_ID_APPID = 3;
const uint64_t U2F_FLAG_REQUIRE_RESIDENT_KEY = 1;
const uint64_t U2F_FLAG_REQUIRE_USER_VERIFICATION = 2;
@ -34,6 +35,9 @@ const uint8_t U2F_AUTHENTICATOR_TRANSPORT_BLE = 4;
// The `rust_u2f_mgr` opaque type is equivalent to the rust type `U2FManager`
struct rust_u2f_manager;
// The `rust_u2f_app_ids` opaque type is equivalent to the rust type `U2FAppIds`
struct rust_u2f_app_ids;
// The `rust_u2f_key_handles` opaque type is equivalent to the rust type `U2FKeyHandles`
struct rust_u2f_key_handles;
@ -65,13 +69,21 @@ uint64_t rust_u2f_mgr_sign(rust_u2f_manager* mgr,
rust_u2f_callback,
const uint8_t* challenge_ptr,
size_t challenge_len,
const uint8_t* application_ptr,
size_t application_len,
const rust_u2f_app_ids* app_ids,
const rust_u2f_key_handles* khs);
uint64_t rust_u2f_mgr_cancel(rust_u2f_manager* mgr);
/// U2FAppIds functions.
rust_u2f_app_ids* rust_u2f_app_ids_new();
void rust_u2f_app_ids_add(rust_u2f_app_ids* ids,
const uint8_t* id,
size_t id_len);
/* unsafe */ void rust_u2f_app_ids_free(rust_u2f_app_ids* ids);
/// U2FKeyHandles functions.
rust_u2f_key_handles* rust_u2f_khs_new();

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

@ -21,6 +21,8 @@ interface CredentialsContainer {
Promise<Credential?> create(optional CredentialCreationOptions options);
[Throws]
Promise<Credential> store(Credential credential);
[Throws]
Promise<void> preventSilentAccess();
};
dictionary CredentialRequestOptions {

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

@ -13,8 +13,7 @@
interface PublicKeyCredential : Credential {
[SameObject] readonly attribute ArrayBuffer rawId;
[SameObject] readonly attribute AuthenticatorResponse response;
// Extensions are not supported yet.
// [SameObject] readonly attribute AuthenticationExtensions clientExtensionResults; // Add in Bug 1406458
AuthenticationExtensionsClientOutputs getClientExtensionResults();
};
[SecureContext]
@ -104,10 +103,18 @@ dictionary PublicKeyCredentialRequestOptions {
AuthenticationExtensionsClientInputs extensions;
};
// TODO - Use partial dictionaries when bug 1436329 is fixed.
dictionary AuthenticationExtensionsClientInputs {
// FIDO AppID Extension (appid)
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
USVString appid;
};
// TODO - Use partial dictionaries when bug 1436329 is fixed.
dictionary AuthenticationExtensionsClientOutputs {
// FIDO AppID Extension (appid)
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
boolean appid;
};
typedef record<DOMString, DOMString> AuthenticationExtensionsAuthenticatorInputs;
@ -144,3 +151,16 @@ typedef sequence<AAGUID> AuthenticatorSelectionList;
typedef BufferSource AAGUID;
/*
// FIDO AppID Extension (appid)
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
partial dictionary AuthenticationExtensionsClientInputs {
USVString appid;
};
// FIDO AppID Extension (appid)
// <https://w3c.github.io/webauthn/#sctn-appid-extension>
partial dictionary AuthenticationExtensionsClientOutputs {
boolean appid;
};
*/

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

@ -235,7 +235,10 @@ XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
Abort();
}
mParseEndListener = nullptr;
if (mParseEndListener) {
mParseEndListener->SetIsStale();
mParseEndListener = nullptr;
}
MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
mFlagSyncLooping = false;

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

@ -875,8 +875,13 @@ public:
mXHR = nullptr;
return NS_OK;
}
explicit nsXHRParseEndListener(XMLHttpRequestMainThread* aXHR)
: mXHR(aXHR) {}
void SetIsStale() {
mXHR = nullptr;
}
private:
virtual ~nsXHRParseEndListener() {}

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

@ -0,0 +1,24 @@
var ab = new ArrayBuffer(64 * 1024);
var arr = new Uint8Array(ab);
(function(glob, imp, b) {
"use asm";
var arr = new glob.Uint8Array(b);
return {}
})(this, null, ab);
function testSimdX4() {
for (var i = 10; i --> 0;) {
var caught;
try {
v = SIMD.Int32x4.load(arr, 65534);
} catch (e) {
caught = e;
}
assertEq(caught instanceof RangeError, true);
}
}
setJitCompilerOption('ion.warmup.trigger', 0);
testSimdX4();

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

@ -4676,7 +4676,7 @@ BaselineCompiler::emit_JSOP_RESUME()
GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
frame.syncStack(0);
masm.checkStackAlignment();
masm.assertStackAlignment(sizeof(Value), 0);
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(BaselineFrameReg);
@ -4790,7 +4790,7 @@ BaselineCompiler::emit_JSOP_RESUME()
masm.push(BaselineFrameReg);
masm.moveStackPtrTo(BaselineFrameReg);
masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
masm.checkStackAlignment();
masm.assertStackAlignment(sizeof(Value), 0);
// Store flags and env chain.
masm.store32(Imm32(BaselineFrame::HAS_INITIAL_ENV), frame.addressOfFlags());

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

@ -3449,7 +3449,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
regs.take(scratch);
masm.checkStackAlignment();
masm.assertStackAlignment(sizeof(Value), 0);
// Native functions have the signature:
//

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

@ -3833,10 +3833,12 @@ void
CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
{
ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
Register scratch = ToTempRegisterOrInvalid(lir->temp());
Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
Label miss;
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(), scratch, &miss);
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(),
unboxScratch, objScratch, &miss);
bailoutFrom(&miss, lir->snapshot());
}
@ -3869,10 +3871,12 @@ void
CodeGenerator::visitMonitorTypes(LMonitorTypes* lir)
{
ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
Register scratch = ToTempUnboxRegister(lir->temp());
Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
Label matched, miss;
masm.guardTypeSet(operand, lir->mir()->typeSet(), lir->mir()->barrierKind(), scratch, &miss);
masm.guardTypeSet(operand, lir->mir()->typeSet(), lir->mir()->barrierKind(), unboxScratch,
objScratch, &miss);
bailoutFrom(&miss, lir->snapshot());
}
@ -5086,7 +5090,9 @@ CodeGenerator::generateArgumentsChecks(bool assert)
MResumePoint* rp = mir.entryResumePoint();
// No registers are allocated yet, so it's safe to grab anything.
Register temp = AllocatableGeneralRegisterSet(GeneralRegisterSet::All()).getAny();
AllocatableGeneralRegisterSet temps(GeneralRegisterSet::All());
Register temp1 = temps.takeAny();
Register temp2 = temps.takeAny();
const CompileInfo& info = gen->info();
@ -5103,7 +5109,8 @@ CodeGenerator::generateArgumentsChecks(bool assert)
// ... * sizeof(Value) - Scale by value size.
// ArgToStackOffset(...) - Compute displacement within arg vector.
int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
masm.guardTypeSet(Address(masm.getStackPointer(), offset), types, BarrierKind::TypeSet, temp, &miss);
Address argAddr(masm.getStackPointer(), offset);
masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2, &miss);
}
if (miss.used()) {
@ -5124,8 +5131,8 @@ CodeGenerator::generateArgumentsChecks(bool assert)
Label skip;
Address addr(masm.getStackPointer(), ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)));
masm.branchTestObject(Assembler::NotEqual, addr, &skip);
Register obj = masm.extractObject(addr, temp);
masm.guardTypeSetMightBeIncomplete(types, obj, temp, &success);
Register obj = masm.extractObject(addr, temp1);
masm.guardTypeSetMightBeIncomplete(types, obj, temp1, &success);
masm.bind(&skip);
}
@ -5469,7 +5476,7 @@ CodeGenerator::emitAssertResultV(const ValueOperand input, const TemporaryTypeSe
if (typeset && !typeset->unknown()) {
// We have a result TypeSet, assert this value is in it.
Label miss, ok;
masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, &miss);
masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, temp2, &miss);
masm.jump(&ok);
masm.bind(&miss);

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

@ -1439,6 +1439,22 @@ EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCh
masm.Push(obj);
Register scratch1 = obj;
// We may also need a scratch register for guardTypeSet.
Register objScratch = InvalidReg;
if (propTypes && !propTypes->unknownObject() && propTypes->getObjectCount() > 0) {
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
if (!val.constant()) {
TypedOrValueRegister valReg = val.reg();
if (valReg.hasValue())
regs.take(valReg.valueReg());
else if (!valReg.typedReg().isFloat())
regs.take(valReg.typedReg().gpr());
}
regs.take(scratch1);
objScratch = regs.takeAny();
masm.Push(objScratch);
}
bool checkTypeSet = true;
Label failedFastPath;
@ -1468,7 +1484,8 @@ EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCh
if (propTypes) {
// guardTypeSet can read from type sets without triggering read barriers.
TypeSet::readBarrier(propTypes);
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch1, &failedFastPath);
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch1, objScratch,
&failedFastPath);
masm.jump(&done);
} else {
masm.jump(&failedFastPath);
@ -1511,11 +1528,15 @@ EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCh
masm.PopRegsInMaskIgnore(save, ignore);
masm.branchIfTrueBool(scratch1, &done);
if (objScratch != InvalidReg)
masm.pop(objScratch);
masm.pop(obj);
masm.jump(failures);
}
masm.bind(&done);
if (objScratch != InvalidReg)
masm.Pop(objScratch);
masm.Pop(obj);
}

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

@ -2792,7 +2792,6 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
// from inside a type barrier test.
const TemporaryTypeSet* types = ins->resultTypeSet();
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
MIRType inputType = ins->getOperand(0)->type();
MOZ_ASSERT(inputType == ins->type());
@ -2807,10 +2806,13 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
return;
}
bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
// Handle typebarrier with Value as input.
if (inputType == MIRType::Value) {
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tmp);
LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tempToUnbox(),
objTemp);
assignSnapshot(barrier, Bailout_TypeBarrierV);
add(barrier, ins);
redefine(ins, ins->input());
@ -2829,7 +2831,7 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
}
if (needsObjectBarrier) {
LDefinition tmp = needTemp ? temp() : LDefinition::BogusTemp();
LDefinition tmp = needObjTemp ? temp() : LDefinition::BogusTemp();
LTypeBarrierO* barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
assignSnapshot(barrier, Bailout_TypeBarrierO);
add(barrier, ins);
@ -2848,10 +2850,11 @@ LIRGenerator::visitMonitorTypes(MMonitorTypes* ins)
// from inside a type check.
const TemporaryTypeSet* types = ins->typeSet();
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tmp);
bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tempToUnbox(), objTemp);
assignSnapshot(lir, Bailout_MonitorTypes);
add(lir, ins);
}
@ -3188,8 +3191,7 @@ LIRGenerator::visitSpectreMaskIndex(MSpectreMaskIndex* ins)
MOZ_ASSERT(ins->length()->type() == MIRType::Int32);
MOZ_ASSERT(ins->type() == MIRType::Int32);
LSpectreMaskIndex* lir =
new(alloc()) LSpectreMaskIndex(useRegister(ins->index()), useAny(ins->length()));
auto* lir = new(alloc()) LSpectreMaskIndex(useRegister(ins->index()), useAny(ins->length()));
define(lir, ins);
}

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

@ -4306,9 +4306,9 @@ SimdTypeToArrayElementType(SimdType type)
}
bool
IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements,
MDefinition** index, Scalar::Type* arrayType,
BoundsCheckKind boundsCheckKind)
IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
MInstruction** elements, MDefinition** index,
Scalar::Type* arrayType, BoundsCheckKind boundsCheckKind)
{
MDefinition* array = callInfo.getArg(0);
*index = callInfo.getArg(1);
@ -4320,31 +4320,35 @@ IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, M
current->add(indexAsInt32);
*index = indexAsInt32;
MDefinition* indexForBoundsCheck = *index;
MDefinition* indexLoadEnd = *index;
// Artificially make sure the index is in bounds by adding the difference
// number of slots needed (e.g. reading from Float32Array we need to make
// sure to be in bounds for 4 slots, so add 3, etc.).
MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
int32_t suppSlotsNeeded = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType) - 1;
if (suppSlotsNeeded) {
MConstant* suppSlots = constant(Int32Value(suppSlotsNeeded));
MAdd* addedIndex = MAdd::New(alloc(), *index, suppSlots);
// We're fine even with the add overflows, as long as the generated code
// for the bounds check uses an unsigned comparison.
int32_t byteLoadSize = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType);
if (byteLoadSize > 1) {
// Add the number of supplementary needed slots. Overflows are fine
// because the bounds check code uses an unsigned comparison.
MAdd* addedIndex = MAdd::New(alloc(), *index, constant(Int32Value(byteLoadSize - 1)));
addedIndex->setInt32Specialization();
current->add(addedIndex);
indexForBoundsCheck = addedIndex;
indexLoadEnd = addedIndex;
}
MInstruction* length;
addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements, boundsCheckKind);
// It can be that the index is out of bounds, while the added index for the
// bounds check is in bounds, so we actually need two bounds checks here.
// If the index+size addition overflows, then indexLoadEnd might be
// in bounds while the actual index isn't, so we need two bounds checks
// here.
if (byteLoadSize > 1) {
indexLoadEnd = addBoundsCheck(indexLoadEnd, length, BoundsCheckKind::UnusedIndex);
auto* sub = MSub::New(alloc(), indexLoadEnd, constant(Int32Value(byteLoadSize - 1)));
sub->setInt32Specialization();
current->add(sub);
*index = sub;
}
*index = addBoundsCheck(*index, length, boundsCheckKind);
addBoundsCheck(indexForBoundsCheck, length, BoundsCheckKind::UnusedIndex);
return true;
}

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

@ -80,8 +80,14 @@ EmitTypeCheck(MacroAssembler& masm, Assembler::Condition cond, const T& src, Typ
template <typename Source> void
MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
Register scratch, Label* miss)
Register unboxScratch, Register objScratch, Label* miss)
{
// unboxScratch may be InvalidReg on 32-bit platforms. It should only be
// used for extracting the Value tag or payload.
//
// objScratch may be InvalidReg if the TypeSet does not contain specific
// objects to guard on. It should only be used for guardObjectType.
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
MOZ_ASSERT(!types->unknown());
@ -119,7 +125,7 @@ MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, Barrie
return;
}
Register tag = extractTag(address, scratch);
Register tag = extractTag(address, unboxScratch);
// Emit all typed tests.
for (size_t i = 0; i < mozilla::ArrayLength(tests); i++) {
@ -140,26 +146,24 @@ MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, Barrie
}
// Test specific objects.
MOZ_ASSERT(scratch != InvalidReg);
MOZ_ASSERT(objScratch != InvalidReg);
MOZ_ASSERT(objScratch != unboxScratch);
MOZ_ASSERT(numBranches == 1);
branchTestObject(NotEqual, tag, miss);
if (kind != BarrierKind::TypeTagOnly) {
Register obj = extractObject(address, scratch);
guardObjectType(obj, types, scratch, miss);
Register obj = extractObject(address, unboxScratch);
guardObjectType(obj, types, objScratch, miss);
} else {
#ifdef DEBUG
Label fail;
Register obj = extractObject(address, scratch);
guardObjectType(obj, types, scratch, &fail);
Register obj = extractObject(address, unboxScratch);
guardObjectType(obj, types, objScratch, &fail);
jump(&matched);
bind(&fail);
if (obj == scratch)
extractObject(address, scratch);
guardTypeSetMightBeIncomplete(types, obj, scratch, &matched);
guardTypeSetMightBeIncomplete(types, obj, objScratch, &matched);
assumeUnreachable("Unexpected object type");
#endif
}
@ -209,6 +213,7 @@ void
MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
Register scratch, Label* miss)
{
MOZ_ASSERT(obj != scratch);
MOZ_ASSERT(!types->unknown());
MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType()));
MOZ_ASSERT_IF(types->getObjectCount() > 0, scratch != InvalidReg);
@ -257,8 +262,6 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
if (hasObjectGroups) {
comment("has object groups");
// Note: Some platforms give the same register for obj and scratch.
// Make sure when writing to scratch, the obj register isn't used anymore!
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
for (unsigned i = 0; i < count; i++) {
@ -279,11 +282,14 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
}
template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types,
BarrierKind kind, Register scratch, Label* miss);
BarrierKind kind, Register unboxScratch,
Register objScratch, Label* miss);
template void MacroAssembler::guardTypeSet(const ValueOperand& value, const TypeSet* types,
BarrierKind kind, Register scratch, Label* miss);
BarrierKind kind, Register unboxScratch,
Register objScratch, Label* miss);
template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, const TypeSet* types,
BarrierKind kind, Register scratch, Label* miss);
BarrierKind kind, Register unboxScratch,
Register objScratch, Label* miss);
template<typename S, typename T>
static void

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

@ -1933,7 +1933,8 @@ class MacroAssembler : public MacroAssemblerSpecific
// Emits a test of a value against all types in a TypeSet. A scratch
// register is required.
template <typename Source>
void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
Register unboxScratch, Register objScratch, Label* miss);
void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss);

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

@ -833,6 +833,14 @@ class AssemblerMIPSShared : public AssemblerShared
FCSR = 31
};
enum FCSRBit {
CauseI = 12,
CauseU,
CauseO,
CauseZ,
CauseV
};
enum FloatFormat {
SingleFloat,
DoubleFloat

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

@ -1481,17 +1481,19 @@ CodeGeneratorMIPSShared::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input);
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output);
addOutOfLineCode(ool, mir);
Label* oolEntry = ool->entry();
if (mir->isUnsigned()) {
if (fromType == MIRType::Double)
masm.wasmTruncateDoubleToUInt32(input, output, oolEntry);
masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(), oolEntry);
else if (fromType == MIRType::Float32)
masm.wasmTruncateFloat32ToUInt32(input, output, oolEntry);
masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(), oolEntry);
else
MOZ_CRASH("unexpected type");
masm.bind(ool->rejoin());
return;
}
@ -1508,9 +1510,15 @@ CodeGeneratorMIPSShared::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
void
CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
{
masm.outOfLineWasmTruncateToIntCheck(ool->input(), ool->fromType(), ool->toType(),
ool->isUnsigned(), ool->rejoin(),
ool->bytecodeOffset());
if(ool->toType() == MIRType::Int32)
{
masm.outOfLineWasmTruncateToInt32Check(ool->input(), ool->output(), ool->fromType(),
ool->flags(), ool->rejoin(), ool->bytecodeOffset());
} else {
MOZ_ASSERT(ool->toType() == MIRType::Int64);
masm.outOfLineWasmTruncateToInt64Check(ool->input(), ool->output64(), ool->fromType(),
ool->flags(), ool->rejoin(), ool->bytecodeOffset());
}
}
void

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

@ -610,20 +610,10 @@ MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegist
ma_bc1s(lhs, rhs, label, cond);
}
void
MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
Label test, success;
as_truncws(ScratchFloat32Reg, src);
as_mfc1(dest, ScratchFloat32Reg);
ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
}
void
MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
{
convertFloat32ToInt32(src, dest, fail);
MOZ_CRASH();
}
void
@ -633,25 +623,10 @@ MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegis
ma_bc1d(lhs, rhs, label, cond);
}
// Convert the floating point value to an integer, if it did not fit, then it
// was clamped to INT32_MIN/INT32_MAX, and we can test it.
// NOTE: if the value really was supposed to be INT32_MAX / INT32_MIN then it
// will be wrong.
void
MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
Label test, success;
as_truncwd(ScratchDoubleReg, src);
as_mfc1(dest, ScratchDoubleReg);
ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
}
void
MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
{
convertDoubleToInt32(src, dest, fail);
MOZ_CRASH();
}
template <typename T, typename L>

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

@ -1110,8 +1110,12 @@ MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value)
{
Imm32 imm(mozilla::BitwiseCast<uint32_t>(value));
ma_li(ScratchRegister, imm);
moveToFloat32(ScratchRegister, dest);
if(imm.value != 0) {
ma_li(ScratchRegister, imm);
moveToFloat32(ScratchRegister, dest);
} else {
moveToFloat32(zero, dest);
}
}
void
@ -1654,7 +1658,7 @@ MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output,
as_truncwd(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
@ -1666,113 +1670,198 @@ MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output,
as_truncws(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void
MacroAssembler::oolWasmTruncateCheckF32ToI32(FloatRegister input, Register, TruncFlags flags,
wasm::BytecodeOffset off, Label* rejoin)
MacroAssembler::oolWasmTruncateCheckF32ToI32(FloatRegister input, Register output,
TruncFlags flags, wasm::BytecodeOffset off,
Label* rejoin)
{
outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int32, flags & TRUNC_UNSIGNED,
rejoin, off);
outOfLineWasmTruncateToInt32Check(input, output, MIRType::Float32, flags, rejoin, off);
}
void
MacroAssembler::oolWasmTruncateCheckF64ToI32(FloatRegister input, Register, TruncFlags flags,
wasm::BytecodeOffset off, Label* rejoin)
MacroAssembler::oolWasmTruncateCheckF64ToI32(FloatRegister input, Register output,
TruncFlags flags, wasm::BytecodeOffset off,
Label* rejoin)
{
outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int32, flags & TRUNC_UNSIGNED,
rejoin, off);
outOfLineWasmTruncateToInt32Check(input, output, MIRType::Double, flags, rejoin, off);
}
void
MacroAssembler::oolWasmTruncateCheckF32ToI64(FloatRegister input, Register64, TruncFlags flags,
wasm::BytecodeOffset off, Label* rejoin)
MacroAssembler::oolWasmTruncateCheckF32ToI64(FloatRegister input, Register64 output,
TruncFlags flags, wasm::BytecodeOffset off,
Label* rejoin)
{
outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int64, flags & TRUNC_UNSIGNED,
rejoin, off);
outOfLineWasmTruncateToInt64Check(input, output, MIRType::Float32, flags, rejoin, off);
}
void
MacroAssembler::oolWasmTruncateCheckF64ToI64(FloatRegister input, Register64, TruncFlags flags,
wasm::BytecodeOffset off, Label* rejoin)
MacroAssembler::oolWasmTruncateCheckF64ToI64(FloatRegister input, Register64 output,
TruncFlags flags, wasm::BytecodeOffset off,
Label* rejoin)
{
outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int64, flags & TRUNC_UNSIGNED,
rejoin, off);
outOfLineWasmTruncateToInt64Check(input, output, MIRType::Double, flags, rejoin, off);
}
void
MacroAssemblerMIPSShared::outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType,
MIRType toType, bool isUnsigned,
Label* rejoin,
wasm::BytecodeOffset trapOffset)
MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt32Check(FloatRegister input, Register output,
MIRType fromType, TruncFlags flags,
Label* rejoin,
wasm::BytecodeOffset trapOffset)
{
// Eagerly take care of NaNs.
bool isUnsigned = flags & TRUNC_UNSIGNED;
bool isSaturating = flags & TRUNC_SATURATING;
if(isSaturating) {
if(fromType == MIRType::Double)
asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
else
asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
if(isUnsigned) {
ma_li(output, Imm32(UINT32_MAX));
FloatTestKind moveCondition;
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThanOrUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
} else {
// Positive overflow is already saturated to INT32_MAX, so we only have
// to handle NaN and negative overflow here.
FloatTestKind moveCondition;
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
input,
Assembler::DoubleUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThan, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
ma_li(ScratchRegister, Imm32(INT32_MIN));
as_movt(output, ScratchRegister);
}
MOZ_ASSERT(rejoin->bound());
asMasm().jump(rejoin);
return;
}
Label inputIsNaN;
if (fromType == MIRType::Double)
asMasm().branchDouble(Assembler::DoubleUnordered, input, input, &inputIsNaN);
else if (fromType == MIRType::Float32)
asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
else
MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
// By default test for the following inputs and bail:
// signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
// unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
// Note: we cannot always represent those exact values. As a result
// this changes the actual comparison a bit.
double minValue, maxValue;
Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
if (toType == MIRType::Int64) {
if (isUnsigned) {
minValue = -1;
maxValue = double(UINT64_MAX) + 1.0;
asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
asMasm().bind(&inputIsNaN);
asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);
}
void
MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt64Check(FloatRegister input, Register64 output_,
MIRType fromType, TruncFlags flags,
Label* rejoin,
wasm::BytecodeOffset trapOffset)
{
bool isUnsigned = flags & TRUNC_UNSIGNED;
bool isSaturating = flags & TRUNC_SATURATING;
if(isSaturating) {
#if defined(JS_CODEGEN_MIPS32)
// Saturating callouts don't use ool path.
return;
#else
Register output = output_.reg;
if(fromType == MIRType::Double)
asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
else
asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
if(isUnsigned) {
asMasm().ma_li(output, ImmWord(UINT64_MAX));
FloatTestKind moveCondition;
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThanOrUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
} else {
// In the float32/double range there exists no value between
// INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
minValue = double(INT64_MIN);
minCond = Assembler::DoubleLessThan;
maxValue = double(INT64_MAX) + 1.0;
}
} else {
if (isUnsigned) {
minValue = -1;
maxValue = double(UINT32_MAX) + 1.0;
} else {
if (fromType == MIRType::Float32) {
// In the float32 range there exists no value between
// INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
minValue = double(INT32_MIN);
minCond = Assembler::DoubleLessThan;
} else {
minValue = double(INT32_MIN) - 1.0;
}
maxValue = double(INT32_MAX) + 1.0;
// Positive overflow is already saturated to INT64_MAX, so we only have
// to handle NaN and negative overflow here.
FloatTestKind moveCondition;
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
input,
Assembler::DoubleUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
compareFloatingPoint(fromType == MIRType::Double ? DoubleFloat : SingleFloat,
input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThan, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
asMasm().ma_li(ScratchRegister, ImmWord(INT64_MIN));
as_movt(output, ScratchRegister);
}
MOZ_ASSERT(rejoin->bound());
asMasm().jump(rejoin);
return;
#endif
}
Label fail;
Label inputIsNaN;
if (fromType == MIRType::Double)
asMasm().branchDouble(Assembler::DoubleUnordered, input, input, &inputIsNaN);
else if (fromType == MIRType::Float32)
asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
#if defined(JS_CODEGEN_MIPS32)
// Only possible valid input that produces INT64_MIN result.
double validInput = isUnsigned ? double(uint64_t(INT64_MIN)) : double(int64_t(INT64_MIN));
if (fromType == MIRType::Double) {
asMasm().loadConstantDouble(minValue, ScratchDoubleReg);
asMasm().branchDouble(minCond, input, ScratchDoubleReg, &fail);
asMasm().loadConstantDouble(maxValue, ScratchDoubleReg);
asMasm().branchDouble(maxCond, input, ScratchDoubleReg, &fail);
asMasm().loadConstantDouble(validInput, ScratchDoubleReg);
asMasm().branchDouble(Assembler::DoubleEqual, input, ScratchDoubleReg, rejoin);
} else {
asMasm().loadConstantFloat32(float(minValue), ScratchFloat32Reg);
asMasm().branchFloat(minCond, input, ScratchFloat32Reg, &fail);
asMasm().loadConstantFloat32(float(maxValue), ScratchFloat32Reg);
asMasm().branchFloat(maxCond, input, ScratchFloat32Reg, &fail);
asMasm().loadConstantFloat32(float(validInput), ScratchFloat32Reg);
asMasm().branchFloat(Assembler::DoubleEqual, input, ScratchDoubleReg, rejoin);
}
asMasm().jump(rejoin);
#endif
// Handle errors.
asMasm().bind(&fail);
asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
asMasm().bind(&inputIsNaN);
asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);

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

@ -217,9 +217,12 @@ class MacroAssemblerMIPSShared : public Assembler
void minMaxDouble(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
void minMaxFloat32(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
void outOfLineWasmTruncateToIntCheck(FloatRegister input, MIRType fromType,
MIRType toType, bool isUnsigned, Label* rejoin,
wasm::BytecodeOffset trapOffset);
void outOfLineWasmTruncateToInt32Check(FloatRegister input, Register output, MIRType fromType,
TruncFlags flags, Label* rejoin,
wasm::BytecodeOffset trapOffset);
void outOfLineWasmTruncateToInt64Check(FloatRegister input, Register64 output, MIRType fromType,
TruncFlags flags, Label* rejoin,
wasm::BytecodeOffset trapOffset);
protected:
void wasmLoadImpl(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,

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

@ -69,7 +69,7 @@ class FloatRegisters : public FloatRegistersMIPSShared
static const uint32_t TotalDouble = 16;
static const uint32_t TotalSingle = 16;
static const uint32_t Allocatable = 28;
static const uint32_t Allocatable = 30;
static const SetType AllSingleMask = (1ULL << TotalSingle) - 1;
static const SetType AllDoubleMask = ((1ULL << TotalDouble) - 1) << TotalSingle;
@ -95,8 +95,7 @@ class FloatRegisters : public FloatRegistersMIPSShared
static const SetType WrapperMask = VolatileMask;
static const SetType NonAllocatableMask =
((SetType(1) << (FloatRegisters::f16 >> 1)) |
(SetType(1) << (FloatRegisters::f18 >> 1))) * ((1 << TotalSingle) + 1);
(SetType(1) << (FloatRegisters::f18 >> 1)) * ((1 << TotalSingle) + 1);
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
};

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

@ -83,8 +83,6 @@ static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatReg
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegister::Double };
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f18, FloatRegister::Single };
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::f18, FloatRegister::Double };
static constexpr FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f16, FloatRegister::Single };
static constexpr FloatRegister SecondScratchDoubleReg = { FloatRegisters::f16, FloatRegister::Double };
struct ScratchFloat32Scope : public AutoFloatRegisterScope
{

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

@ -619,40 +619,45 @@ void
CodeGeneratorMIPS::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
FloatRegister scratch = input;
FloatRegister arg = input;
Register64 output = ToOutRegister64(lir);
MWasmTruncateToInt64* mir = lir->mir();
MIRType fromType = mir->input()->type();
auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input, Register64::Invalid());
addOutOfLineCode(ool, mir);
if (fromType == MIRType::Double) {
masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
} else if (fromType == MIRType::Float32) {
masm.branchFloat(Assembler::DoubleUnordered, input, input, ool->entry());
scratch = ScratchDoubleReg;
masm.convertFloat32ToDouble(input, scratch);
} else {
MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
if (fromType == MIRType::Float32) {
arg = ScratchDoubleReg;
masm.convertFloat32ToDouble(input, arg);
}
masm.Push(input);
if (!lir->mir()->isSaturating()) {
masm.Push(input);
masm.setupWasmABICall();
masm.passABIArg(scratch, MoveOp::DOUBLE);
if (lir->mir()->isUnsigned())
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToUint64);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToInt64);
masm.setupWasmABICall();
masm.passABIArg(arg, MoveOp::DOUBLE);
masm.Pop(input);
if (lir->mir()->isUnsigned())
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToUint64);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToInt64);
masm.ma_b(output.high, Imm32(0x80000000), ool->rejoin(), Assembler::NotEqual);
masm.ma_b(output.low, Imm32(0x00000000), ool->rejoin(), Assembler::NotEqual);
masm.ma_b(ool->entry());
masm.Pop(input);
masm.bind(ool->rejoin());
masm.ma_xor(ScratchRegister, output.high, Imm32(0x80000000));
masm.ma_or(ScratchRegister, output.low);
masm.ma_b(ScratchRegister, Imm32(0), ool->entry(), Assembler::Equal);
masm.bind(ool->rejoin());
} else {
masm.setupWasmABICall();
masm.passABIArg(arg, MoveOp::DOUBLE);
if (lir->mir()->isUnsigned())
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::SaturatingTruncateDoubleToUint64);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::SaturatingTruncateDoubleToInt64);
}
MOZ_ASSERT(ReturnReg64 == output);
}
@ -661,7 +666,7 @@ void
CodeGeneratorMIPS::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
{
Register64 input = ToRegister64(lir->getInt64Operand(0));
FloatRegister output = ToFloatRegister(lir->output());
mozilla::DebugOnly<FloatRegister> output = ToFloatRegister(lir->output());
MInt64ToFloatingPoint* mir = lir->mir();
MIRType toType = mir->type();
@ -686,8 +691,8 @@ CodeGeneratorMIPS::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Int64ToFloat32, MoveOp::FLOAT32);
MOZ_ASSERT_IF(toType == MIRType::Double, output == ReturnDoubleReg);
MOZ_ASSERT_IF(toType == MIRType::Float32, output == ReturnFloat32Reg);
MOZ_ASSERT_IF(toType == MIRType::Double, *(&output) == ReturnDoubleReg);
MOZ_ASSERT_IF(toType == MIRType::Float32, *(&output) == ReturnFloat32Reg);
}
void

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

@ -996,6 +996,26 @@ MacroAssembler::branchToComputedAddress(const BaseIndex& addr)
as_nop();
}
void
MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void
MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
// ========================================================================
// Memory access primitives.
void

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

@ -62,20 +62,26 @@ MacroAssemblerMIPSCompat::convertInt32ToDouble(const BaseIndex& src, FloatRegist
void
MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src, FloatRegister dest)
{
// We use SecondScratchDoubleReg because MacroAssembler::loadFromTypedArray
// calls with ScratchDoubleReg as dest.
MOZ_ASSERT(dest != SecondScratchDoubleReg);
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
// Subtract INT32_MIN to get a positive number
ma_subu(ScratchRegister, src, Imm32(INT32_MIN));
const uint32_t kExponentShift = mozilla::FloatingPoint<double>::kExponentShift - 32;
const uint32_t kExponent = (31 + mozilla::FloatingPoint<double>::kExponentBias);
// Convert value
as_mtc1(ScratchRegister, dest);
as_cvtdw(dest, dest);
ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
ma_or(SecondScratchReg, ScratchRegister);
ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
moveToDoubleHi(SecondScratchReg, dest);
moveToDoubleLo(ScratchRegister, dest);
ma_b(&done, ShortJump);
bind(&positive);
convertInt32ToDouble(src, dest);
bind(&done);
// Add unsigned value of INT32_MIN
ma_lid(SecondScratchDoubleReg, 2147483648.0);
as_addd(dest, dest, SecondScratchDoubleReg);
}
void
@ -84,10 +90,19 @@ MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister des
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
// We cannot do the same as convertUInt32ToDouble because float32 doesn't
// have enough precision.
convertUInt32ToDouble(src, dest);
convertDoubleToFloat32(dest, dest);
const uint32_t kExponentShift = mozilla::FloatingPoint<double>::kExponentShift - 32;
const uint32_t kExponent = (31 + mozilla::FloatingPoint<double>::kExponentBias);
ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
ma_or(SecondScratchReg, ScratchRegister);
ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
FloatRegister destDouble = dest.asDouble();
moveToDoubleHi(SecondScratchReg, destDouble);
moveToDoubleLo(ScratchRegister, destDouble);
convertDoubleToFloat32(destDouble, dest);
ma_b(&done, ShortJump);
bind(&positive);
@ -111,17 +126,18 @@ MacroAssemblerMIPSCompat::convertDoubleToInt32(FloatRegister src, Register dest,
{
if (negativeZeroCheck) {
moveFromDoubleHi(src, dest);
moveFromDoubleLo(src, ScratchRegister);
as_movn(dest, zero, ScratchRegister);
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
moveFromDoubleLo(src, SecondScratchReg);
ma_xor(dest, Imm32(INT32_MIN));
ma_or(dest, SecondScratchReg);
ma_b(dest, Imm32(0), fail, Assembler::Equal);
}
// Convert double to int, then convert back and check if we have the
// same number.
as_cvtwd(ScratchDoubleReg, src);
as_mfc1(dest, ScratchDoubleReg);
as_cvtdw(ScratchDoubleReg, ScratchDoubleReg);
ma_bc1d(src, ScratchDoubleReg, fail, Assembler::DoubleNotEqualOrUnordered);
// Truncate double to int ; if result is inexact fail
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
// Checks whether a float32 is representable as a 32-bit integer. If so, the
@ -136,18 +152,11 @@ MacroAssemblerMIPSCompat::convertFloat32ToInt32(FloatRegister src, Register dest
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
}
// Converting the floating point value to an integer and then converting it
// back to a float32 would not work, as float to int32 conversions are
// clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
// and then back to float(INT32_MAX + 1)). If this ever happens, we just
// bail out.
as_cvtws(ScratchFloat32Reg, src);
as_mfc1(dest, ScratchFloat32Reg);
as_cvtsw(ScratchFloat32Reg, ScratchFloat32Reg);
ma_bc1s(src, ScratchFloat32Reg, fail, Assembler::DoubleNotEqualOrUnordered);
// Bail out in the clamped cases.
ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void
@ -1377,49 +1386,19 @@ MacroAssemblerMIPSCompat::storeUnalignedDouble(const wasm::MemoryAccessDesc& acc
append(access, store.getOffset(), framePushed);
}
// Note: this function clobbers the input register.
void
MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
{
MOZ_ASSERT(input != ScratchDoubleReg);
Label positive, done;
// <= 0 or NaN --> 0
zeroDouble(ScratchDoubleReg);
branchDouble(DoubleGreaterThan, input, ScratchDoubleReg, &positive);
{
move32(Imm32(0), output);
jump(&done);
}
bind(&positive);
// Add 0.5 and truncate.
loadConstantDouble(0.5, ScratchDoubleReg);
addDouble(ScratchDoubleReg, input);
Label outOfRange;
branchTruncateDoubleMaybeModUint32(input, output, &outOfRange);
asMasm().branch32(Assembler::Above, output, Imm32(255), &outOfRange);
{
// Check if we had a tie.
convertInt32ToDouble(output, ScratchDoubleReg);
branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
// It was a tie. Mask out the ones bit to get an even value.
// See also js_TypedArray_uint8_clamp_double.
and32(Imm32(~1), output);
jump(&done);
}
// > 255 --> 255
bind(&outOfRange);
{
move32(Imm32(255), output);
}
bind(&done);
as_roundwd(ScratchDoubleReg, input);
ma_li(ScratchRegister, Imm32(255));
as_mfc1(output, ScratchDoubleReg);
zeroDouble(ScratchDoubleReg);
as_sltiu(SecondScratchReg, output, 255);
as_colt(DoubleFloat, ScratchDoubleReg, input);
// if res > 255; res = 255;
as_movz(output, ScratchRegister, SecondScratchReg);
// if !(input > 0); res = 0;
as_movf(output, zero);
}
// higher level tag testing code
@ -2549,25 +2528,26 @@ void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating,
Label* oolEntry)
{
MOZ_ASSERT(!isSaturating, "NYI");
Label done;
loadConstantDouble(double(-1.0), ScratchDoubleReg);
branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, oolEntry);
as_truncwd(ScratchFloat32Reg, input);
ma_li(ScratchRegister, Imm32(INT32_MAX));
moveFromFloat32(ScratchFloat32Reg, output);
loadConstantDouble(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
branchDouble(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
Label done, simple;
loadConstantDouble(double(0x80000000UL), ScratchDoubleReg);
branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
// For numbers in -1.[ : ]INT32_MAX range do nothing more
ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump);
loadConstantDouble(double(INT32_MAX + 1ULL), ScratchDoubleReg);
ma_li(ScratchRegister, Imm32(INT32_MIN));
as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
as_truncwd(ScratchDoubleReg, ScratchDoubleReg);
moveFromFloat32(ScratchDoubleReg, output);
ma_li(ScratchRegister, Imm32(0x80000000UL));
ma_or(output, ScratchRegister);
ma_b(&done);
bind(&simple);
as_truncwd(ScratchDoubleReg, input);
moveFromFloat32(ScratchDoubleReg, output);
as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
as_cfc1(SecondScratchReg, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1);
ma_addu(output, ScratchRegister);
ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
bind(&done);
}
@ -2575,25 +2555,29 @@ void
MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, bool isSaturating,
Label* oolEntry)
{
MOZ_ASSERT(!isSaturating, "NYI");
Label done;
loadConstantFloat32(double(-1.0), ScratchDoubleReg);
branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
as_truncws(ScratchFloat32Reg, input);
ma_li(ScratchRegister, Imm32(INT32_MAX));
moveFromFloat32(ScratchFloat32Reg, output);
// For numbers in -1.[ : ]INT32_MAX range do nothing more
ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump);
loadConstantFloat32(float(INT32_MAX + 1ULL), ScratchFloat32Reg);
ma_li(ScratchRegister, Imm32(INT32_MIN));
as_subs(ScratchFloat32Reg, input, ScratchFloat32Reg);
as_truncws(ScratchFloat32Reg, ScratchFloat32Reg);
as_cfc1(SecondScratchReg, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1);
ma_addu(output, ScratchRegister);
// Guard against negative values that result in 0 due the precision loss.
as_sltiu(ScratchRegister, output, 1);
ma_or(SecondScratchReg, ScratchRegister);
ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
loadConstantFloat32(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
branchFloat(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
Label done, simple;
loadConstantFloat32(double(0x80000000UL), ScratchDoubleReg);
branchFloat(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
as_subs(ScratchDoubleReg, input, ScratchDoubleReg);
as_truncws(ScratchDoubleReg, ScratchDoubleReg);
moveFromFloat32(ScratchDoubleReg, output);
ma_li(ScratchRegister, Imm32(0x80000000UL));
ma_or(output, ScratchRegister);
ma_b(&done);
bind(&simple);
as_truncws(ScratchDoubleReg, input);
moveFromFloat32(ScratchDoubleReg, output);
bind(&done);
}

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

@ -1576,24 +1576,34 @@ Simulator::setFCSRRoundError(double original, double rounded)
{
bool ret = false;
setFCSRBit(kFCSRInexactCauseBit, false);
setFCSRBit(kFCSRUnderflowCauseBit, false);
setFCSRBit(kFCSROverflowCauseBit, false);
setFCSRBit(kFCSRInvalidOpCauseBit, false);
if (!std::isfinite(original) || !std::isfinite(rounded)) {
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
if (original != rounded) {
setFCSRBit(kFCSRInexactFlagBit, true);
setFCSRBit(kFCSRInexactCauseBit, true);
}
if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
setFCSRBit(kFCSRUnderflowFlagBit, true);
setFCSRBit(kFCSRUnderflowCauseBit, true);
ret = true;
}
if (rounded > INT_MAX || rounded < INT_MIN) {
setFCSRBit(kFCSROverflowFlagBit, true);
setFCSRBit(kFCSROverflowCauseBit, true);
// The reference is not really clear but it seems this is required:
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
@ -1621,15 +1631,15 @@ Simulator::get_pc() const
return registers_[pc];
}
void
Simulator::startInterrupt(JitActivation* activation)
JS::ProfilingFrameIterator::RegisterState
Simulator::registerState()
{
JS::ProfilingFrameIterator::RegisterState state;
wasm::RegisterState state;
state.pc = (void*) get_pc();
state.fp = (void*) getRegister(fp);
state.sp = (void*) getRegister(sp);
state.lr = (void*) getRegister(ra);
activation->startWasmInterrupt(state);
return state;
}
// The signal handler only redirects the PC to the interrupt stub when the PC is
@ -1651,7 +1661,9 @@ Simulator::handleWasmInterrupt()
if (!segment || !segment->isModule() || !segment->containsCodePC(pc))
return;
startInterrupt(activation);
if (!activation->startWasmInterrupt(registerState()))
return;
set_pc(int32_t(segment->asModule()->interruptCode()));
}
@ -1682,14 +1694,19 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
const wasm::ModuleSegment* moduleSegment = segment->asModule();
wasm::Instance* instance = wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
if (!instance || !instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
if (!instance)
return false;
MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
return false;
LLBit_ = false;
const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
if (!memoryAccess) {
startInterrupt(act);
MOZ_ALWAYS_TRUE(act->startWasmInterrupt(registerState()));
if (!instance->code().containsCodePC(pc))
MOZ_CRASH("Cannot map PC to trap handler");
set_pc(int32_t(moduleSegment->outOfBoundsCode()));
@ -1713,7 +1730,6 @@ Simulator::handleWasmTrapFault()
JitActivation* act = cx->activation()->asJit();
void* pc = reinterpret_cast<void*>(get_pc());
uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));
const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
if (!segment || !segment->isModule())
@ -1725,7 +1741,7 @@ Simulator::handleWasmTrapFault()
if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
return false;
act->startWasmTrap(trap, bytecode.offset, pc, fp);
act->startWasmTrap(trap, bytecode.offset, registerState());
set_pc(int32_t(moduleSegment->trapCode()));
return true;
}

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

@ -34,6 +34,7 @@
#include "mozilla/Atomics.h"
#include "jit/IonTypes.h"
#include "js/ProfilingFrameIterator.h"
#include "threading/Thread.h"
#include "vm/MutexIDs.h"
#include "wasm/WasmCode.h"
@ -77,6 +78,12 @@ const uint32_t kFCSROverflowFlagBit = 4;
const uint32_t kFCSRDivideByZeroFlagBit = 5;
const uint32_t kFCSRInvalidOpFlagBit = 6;
const uint32_t kFCSRInexactCauseBit = 12;
const uint32_t kFCSRUnderflowCauseBit = 13;
const uint32_t kFCSROverflowCauseBit = 14;
const uint32_t kFCSRDivideByZeroCauseBit = 15;
const uint32_t kFCSRInvalidOpCauseBit = 16;
const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit;
const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit;
const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit;
@ -299,7 +306,7 @@ class Simulator {
// Handle a wasm interrupt triggered by an async signal handler.
void handleWasmInterrupt();
void startInterrupt(JitActivation* act);
JS::ProfilingFrameIterator::RegisterState registerState();
// Handle any wasm faults, returning true if the fault was handled.
bool handleWasmFault(int32_t addr, unsigned numBytes);

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

@ -41,7 +41,7 @@ class FloatRegisters : public FloatRegistersMIPSShared
static Encoding FromName(const char* name);
static const uint32_t Total = 32 * NumTypes;
static const uint32_t Allocatable = 60;
static const uint32_t Allocatable = 62;
// When saving all registers we only need to do is save double registers.
static const uint32_t TotalPhys = 32;
@ -79,10 +79,7 @@ class FloatRegisters : public FloatRegistersMIPSShared
static const SetType WrapperMask = VolatileMask;
static const SetType NonAllocatableMask =
( // f21 and f23 are MIPS scratch float registers.
(1U << FloatRegisters::f21) |
(1U << FloatRegisters::f23)
) * Spread;
(1U << FloatRegisters::f23) * Spread;
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
};

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

@ -77,8 +77,6 @@ static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatReg
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegisters::Double };
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f23, FloatRegisters::Single };
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::f23, FloatRegisters::Double };
static constexpr FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f21, FloatRegisters::Single };
static constexpr FloatRegister SecondScratchDoubleReg = { FloatRegisters::f21, FloatRegisters::Double };
struct ScratchFloat32Scope : public AutoFloatRegisterScope
{

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

@ -594,18 +594,35 @@ void
CodeGeneratorMIPS64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
{
FloatRegister input = ToFloatRegister(lir->input());
Register output = ToRegister(lir->output());
Register64 output = ToOutRegister64(lir);
MWasmTruncateToInt64* mir = lir->mir();
MIRType fromType = mir->input()->type();
MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input);
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output);
addOutOfLineCode(ool, mir);
masm.wasmTruncateToI64(input, output, fromType, mir->isUnsigned(),
ool->entry(), ool->rejoin());
Label* oolEntry = ool->entry();
Label* oolRejoin = ool->rejoin();
bool isSaturating = mir->isSaturating();
if (fromType == MIRType::Double) {
if (mir->isUnsigned())
masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, oolRejoin,
InvalidFloatReg);
else
masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, oolRejoin,
InvalidFloatReg);
} else {
if (mir->isUnsigned())
masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, oolRejoin,
InvalidFloatReg);
else
masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, oolRejoin,
InvalidFloatReg);
}
}
void

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

@ -744,6 +744,30 @@ MacroAssembler::branchToComputedAddress(const BaseIndex& addr)
as_nop();
}
void
MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
as_truncld(ScratchDoubleReg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
as_sll(dest, dest, 0);
}
void
MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
{
as_truncls(ScratchDoubleReg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
as_sll(dest, dest, 0);
}
// ========================================================================
// Memory access primitives.
void

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

@ -58,20 +58,8 @@ MacroAssemblerMIPS64Compat::convertInt32ToDouble(const BaseIndex& src, FloatRegi
void
MacroAssemblerMIPS64Compat::convertUInt32ToDouble(Register src, FloatRegister dest)
{
// We use SecondScratchDoubleReg because MacroAssembler::loadFromTypedArray
// calls with ScratchDoubleReg as dest.
MOZ_ASSERT(dest != SecondScratchDoubleReg);
// Subtract INT32_MIN to get a positive number
ma_subu(ScratchRegister, src, Imm32(INT32_MIN));
// Convert value
as_mtc1(ScratchRegister, dest);
as_cvtdw(dest, dest);
// Add unsigned value of INT32_MIN
ma_lid(SecondScratchDoubleReg, 2147483648.0);
as_addd(dest, dest, SecondScratchDoubleReg);
ma_dext(ScratchRegister, src, Imm32(0), Imm32(32));
asMasm().convertInt64ToDouble(Register64(ScratchRegister), dest);
}
void
@ -101,19 +89,8 @@ MacroAssemblerMIPS64Compat::convertUInt64ToDouble(Register src, FloatRegister de
void
MacroAssemblerMIPS64Compat::convertUInt32ToFloat32(Register src, FloatRegister dest)
{
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
// We cannot do the same as convertUInt32ToDouble because float32 doesn't
// have enough precision.
convertUInt32ToDouble(src, dest);
convertDoubleToFloat32(dest, dest);
ma_b(&done, ShortJump);
bind(&positive);
convertInt32ToFloat32(src, dest);
bind(&done);
ma_dext(ScratchRegister, src, Imm32(0), Imm32(32));
asMasm().convertInt64ToFloat32(Register64(ScratchRegister), dest);
}
void
@ -135,12 +112,12 @@ MacroAssemblerMIPS64Compat::convertDoubleToInt32(FloatRegister src, Register des
ma_b(dest, Imm32(1), fail, Assembler::Equal);
}
// Convert double to int, then convert back and check if we have the
// same number.
as_cvtwd(ScratchDoubleReg, src);
as_mfc1(dest, ScratchDoubleReg);
as_cvtdw(ScratchDoubleReg, ScratchDoubleReg);
ma_bc1d(src, ScratchDoubleReg, fail, Assembler::DoubleNotEqualOrUnordered);
// Truncate double to int ; if result is inexact fail
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
// Checks whether a float32 is representable as a 32-bit integer. If so, the
@ -155,18 +132,11 @@ MacroAssemblerMIPS64Compat::convertFloat32ToInt32(FloatRegister src, Register de
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
}
// Converting the floating point value to an integer and then converting it
// back to a float32 would not work, as float to int32 conversions are
// clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
// and then back to float(INT32_MAX + 1)). If this ever happens, we just
// bail out.
as_cvtws(ScratchFloat32Reg, src);
as_mfc1(dest, ScratchFloat32Reg);
as_cvtsw(ScratchFloat32Reg, ScratchFloat32Reg);
ma_bc1s(src, ScratchFloat32Reg, fail, Assembler::DoubleNotEqualOrUnordered);
// Bail out in the clamped cases.
ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal);
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void
@ -855,8 +825,13 @@ MacroAssemblerMIPS64::ma_lid(FloatRegister dest, double value)
{
ImmWord imm(mozilla::BitwiseCast<uint64_t>(value));
ma_li(ScratchRegister, imm);
moveToDouble(ScratchRegister, dest);
if(imm.value != 0){
ma_li(ScratchRegister, imm);
moveToDouble(ScratchRegister, dest);
} else {
moveToDouble(zero, dest);
}
}
void
@ -1363,49 +1338,19 @@ MacroAssemblerMIPS64Compat::storeUnalignedDouble(const wasm::MemoryAccessDesc& a
append(access, store.getOffset(), asMasm().framePushed());
}
// Note: this function clobbers the input register.
void
MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
{
MOZ_ASSERT(input != ScratchDoubleReg);
Label positive, done;
// <= 0 or NaN --> 0
zeroDouble(ScratchDoubleReg);
branchDouble(DoubleGreaterThan, input, ScratchDoubleReg, &positive);
{
move32(Imm32(0), output);
jump(&done);
}
bind(&positive);
// Add 0.5 and truncate.
loadConstantDouble(0.5, ScratchDoubleReg);
addDouble(ScratchDoubleReg, input);
Label outOfRange;
branchTruncateDoubleMaybeModUint32(input, output, &outOfRange);
asMasm().branch32(Assembler::Above, output, Imm32(255), &outOfRange);
{
// Check if we had a tie.
convertInt32ToDouble(output, ScratchDoubleReg);
branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
// It was a tie. Mask out the ones bit to get an even value.
// See also js_TypedArray_uint8_clamp_double.
and32(Imm32(~1), output);
jump(&done);
}
// > 255 --> 255
bind(&outOfRange);
{
move32(Imm32(255), output);
}
bind(&done);
as_roundwd(ScratchDoubleReg, input);
ma_li(ScratchRegister, Imm32(255));
as_mfc1(output, ScratchDoubleReg);
zeroDouble(ScratchDoubleReg);
as_sltiu(SecondScratchReg, output, 255);
as_colt(DoubleFloat, ScratchDoubleReg, input);
// if res > 255; res = 255;
as_movz(output, ScratchRegister, SecondScratchReg);
// if !(input > 0); res = 0;
as_movf(output, zero);
}
void
@ -2441,33 +2386,22 @@ void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating,
Label* oolEntry)
{
MOZ_ASSERT(!isSaturating, "NYI");
as_truncld(ScratchDoubleReg, input);
moveFromDoubleHi(ScratchDoubleReg, output);
as_cfc1(ScratchRegister, Assembler::FCSR);
ma_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_or(ScratchRegister, output);
moveFromFloat32(ScratchDoubleReg, output);
moveFromDouble(ScratchDoubleReg, output);
ma_dsrl(ScratchRegister, output, Imm32(32));
as_sll(output, output, 0);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void
MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, bool isSaturating,
Label* oolEntry)
{
MOZ_ASSERT(!isSaturating, "NYI");
as_truncls(ScratchDoubleReg, input);
moveFromDoubleHi(ScratchDoubleReg, output);
as_cfc1(ScratchRegister, Assembler::FCSR);
ma_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_or(ScratchRegister, output);
moveFromFloat32(ScratchDoubleReg, output);
moveFromDouble(ScratchDoubleReg, output);
ma_dsrl(ScratchRegister, output, Imm32(32));
as_sll(output, output, 0);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void
@ -2501,113 +2435,117 @@ MacroAssembler::wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access, Regi
}
void
MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, bool,
Label* oolEntry, Label* oolRejoin,
FloatRegister tempDouble)
MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output,
bool isSaturating, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_ASSERT(tempDouble.isInvalid());
wasmTruncateToI64(input, output.reg, MIRType::Double, false, oolEntry, oolRejoin);
as_truncld(ScratchDoubleReg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, output.reg);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
if (isSaturating)
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, bool,
Label* oolEntry, Label* oolRejoin,
FloatRegister tempDouble)
MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output_,
bool isSaturating, Label* oolEntry,
Label* oolRejoin, FloatRegister tempDouble)
{
MOZ_ASSERT(tempDouble.isInvalid());
wasmTruncateToI64(input, output.reg, MIRType::Double, true, oolEntry, oolRejoin);
Register output = output_.reg;
Label done;
as_truncld(ScratchDoubleReg, input);
// ma_li INT64_MAX
ma_li(SecondScratchReg, Imm32(-1));
ma_dext(SecondScratchReg, SecondScratchReg, Imm32(0), Imm32(63));
moveFromDouble(ScratchDoubleReg, output);
// For numbers in -1.[ : ]INT64_MAX range do nothing more
ma_b(output, SecondScratchReg, &done, Assembler::Below, ShortJump);
loadConstantDouble(double(INT64_MAX + 1ULL), ScratchDoubleReg);
// ma_li INT64_MIN
ma_daddu(SecondScratchReg, Imm32(1));
as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
as_truncld(ScratchDoubleReg, ScratchDoubleReg);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, output);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_daddu(output, SecondScratchReg);
// Guard against negative values that result in 0 due the precision loss.
as_sltiu(SecondScratchReg, output, 1);
ma_or(ScratchRegister, SecondScratchReg);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
bind(&done);
if (isSaturating)
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, bool,
Label* oolEntry, Label* oolRejoin,
FloatRegister tempFloat)
MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output,
bool isSaturating, Label* oolEntry,
Label* oolRejoin, FloatRegister tempFloat)
{
MOZ_ASSERT(tempFloat.isInvalid());
wasmTruncateToI64(input, output.reg, MIRType::Float32, false, oolEntry, oolRejoin);
as_truncls(ScratchDoubleReg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, output.reg);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
if (isSaturating)
bind(oolRejoin);
}
void
MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, bool,
Label* oolEntry, Label* oolRejoin,
FloatRegister tempFloat)
MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output_,
bool isSaturating, Label* oolEntry,
Label* oolRejoin, FloatRegister tempFloat)
{
MOZ_ASSERT(tempFloat.isInvalid());
wasmTruncateToI64(input, output.reg, MIRType::Float32, true, oolEntry, oolRejoin);
}
Register output = output_.reg;
void
MacroAssemblerMIPS64Compat::wasmTruncateToI64(FloatRegister input, Register output, MIRType fromType,
bool isUnsigned, Label* oolEntry, Label* oolRejoin)
{
if (isUnsigned) {
Label isLarge, done;
Label done;
if (fromType == MIRType::Double) {
asMasm().loadConstantDouble(double(INT64_MAX), ScratchDoubleReg);
asMasm().ma_bc1d(ScratchDoubleReg, input, &isLarge,
Assembler::DoubleLessThanOrEqual, ShortJump);
as_truncls(ScratchDoubleReg, input);
// ma_li INT64_MAX
ma_li(SecondScratchReg, Imm32(-1));
ma_dext(SecondScratchReg, SecondScratchReg, Imm32(0), Imm32(63));
moveFromDouble(ScratchDoubleReg, output);
// For numbers in -1.[ : ]INT64_MAX range do nothing more
ma_b(output, SecondScratchReg, &done, Assembler::Below, ShortJump);
asMasm().as_truncld(ScratchDoubleReg, input);
} else {
asMasm().loadConstantFloat32(float(INT64_MAX), ScratchFloat32Reg);
asMasm().ma_bc1s(ScratchFloat32Reg, input, &isLarge,
Assembler::DoubleLessThanOrEqual, ShortJump);
loadConstantFloat32(float(INT64_MAX + 1ULL), ScratchFloat32Reg);
// ma_li INT64_MIN
ma_daddu(SecondScratchReg, Imm32(1));
as_subs(ScratchFloat32Reg, input, ScratchFloat32Reg);
as_truncls(ScratchDoubleReg, ScratchFloat32Reg);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, output);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_daddu(output, SecondScratchReg);
asMasm().as_truncls(ScratchDoubleReg, input);
}
// Guard against negative values that result in 0 due the precision loss.
as_sltiu(SecondScratchReg, output, 1);
ma_or(ScratchRegister, SecondScratchReg);
// Check that the result is in the uint64_t range.
asMasm().moveFromDouble(ScratchDoubleReg, output);
asMasm().as_cfc1(ScratchRegister, Assembler::FCSR);
// extract invalid operation flag (bit 6) from FCSR
asMasm().ma_ext(ScratchRegister, ScratchRegister, 16, 1);
asMasm().ma_dsrl(SecondScratchReg, output, Imm32(63));
asMasm().ma_or(SecondScratchReg, ScratchRegister);
asMasm().ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
asMasm().ma_b(&done, ShortJump);
bind(&done);
// The input is greater than double(INT64_MAX).
asMasm().bind(&isLarge);
if (fromType == MIRType::Double) {
asMasm().as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
asMasm().as_truncld(ScratchDoubleReg, ScratchDoubleReg);
} else {
asMasm().as_subs(ScratchDoubleReg, input, ScratchDoubleReg);
asMasm().as_truncls(ScratchDoubleReg, ScratchDoubleReg);
}
// Check that the result is in the uint64_t range.
asMasm().moveFromDouble(ScratchDoubleReg, output);
asMasm().as_cfc1(ScratchRegister, Assembler::FCSR);
asMasm().ma_ext(ScratchRegister, ScratchRegister, 16, 1);
asMasm().ma_dsrl(SecondScratchReg, output, Imm32(63));
asMasm().ma_or(SecondScratchReg, ScratchRegister);
asMasm().ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual);
asMasm().ma_li(ScratchRegister, Imm32(1));
asMasm().ma_dins(output, ScratchRegister, Imm32(63), Imm32(1));
asMasm().bind(&done);
asMasm().bind(oolRejoin);
return;
}
// When the input value is Infinity, NaN, or rounds to an integer outside the
// range [INT64_MIN; INT64_MAX + 1[, the Invalid Operation flag is set in the FCSR.
if (fromType == MIRType::Double)
asMasm().as_truncld(ScratchDoubleReg, input);
else
asMasm().as_truncls(ScratchDoubleReg, input);
// Check that the result is in the int64_t range.
asMasm().as_cfc1(output, Assembler::FCSR);
asMasm().ma_ext(output, output, 16, 1);
asMasm().ma_b(output, Imm32(0), oolEntry, Assembler::NotEqual);
asMasm().bind(oolRejoin);
asMasm().moveFromDouble(ScratchDoubleReg, output);
if (isSaturating)
bind(oolRejoin);
}
void

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

@ -733,8 +733,6 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
void convertUInt64ToDouble(Register src, FloatRegister dest);
void wasmTruncateToI64(FloatRegister input, Register output, MIRType fromType,
bool isUnsigned, Label* oolEntry, Label* oolRejoin);
void breakpoint();

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

@ -1573,23 +1573,34 @@ Simulator::setFCSRRoundError(double original, double rounded)
{
bool ret = false;
setFCSRBit(kFCSRInexactCauseBit, false);
setFCSRBit(kFCSRUnderflowCauseBit, false);
setFCSRBit(kFCSROverflowCauseBit, false);
setFCSRBit(kFCSRInvalidOpCauseBit, false);
if (!std::isfinite(original) || !std::isfinite(rounded)) {
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
if (original != rounded)
if (original != rounded) {
setFCSRBit(kFCSRInexactFlagBit, true);
setFCSRBit(kFCSRInexactCauseBit, true);
}
if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
setFCSRBit(kFCSRUnderflowFlagBit, true);
setFCSRBit(kFCSRUnderflowCauseBit, true);
ret = true;
}
if (rounded > INT_MAX || rounded < INT_MIN) {
setFCSRBit(kFCSROverflowFlagBit, true);
setFCSRBit(kFCSROverflowCauseBit, true);
// The reference is not really clear but it seems this is required:
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
@ -1617,15 +1628,15 @@ Simulator::get_pc() const
return registers_[pc];
}
void
Simulator::startInterrupt(JitActivation* activation)
JS::ProfilingFrameIterator::RegisterState
Simulator::registerState()
{
JS::ProfilingFrameIterator::RegisterState state;
wasm::RegisterState state;
state.pc = (void*) get_pc();
state.fp = (void*) getRegister(fp);
state.sp = (void*) getRegister(sp);
state.lr = (void*) getRegister(ra);
activation->startWasmInterrupt(state);
return state;
}
// The signal handler only redirects the PC to the interrupt stub when the PC is
@ -1651,7 +1662,9 @@ Simulator::handleWasmInterrupt()
if (!fp)
return;
startInterrupt(activation);
if (!activation->startWasmInterrupt(registerState()))
return;
set_pc(int64_t(segment->asModule()->interruptCode()));
}
@ -1681,14 +1694,19 @@ Simulator::handleWasmFault(uint64_t addr, unsigned numBytes)
const wasm::ModuleSegment* moduleSegment = segment->asModule();
wasm::Instance* instance = wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
if (!instance || !instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
if (!instance)
return false;
MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
return false;
LLBit_ = false;
const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
if (!memoryAccess) {
startInterrupt(act);
MOZ_ALWAYS_TRUE(act->startWasmInterrupt(registerState()));
if (!instance->code().containsCodePC(pc))
MOZ_CRASH("Cannot map PC to trap handler");
set_pc(int64_t(moduleSegment->outOfBoundsCode()));
@ -1712,7 +1730,6 @@ Simulator::handleWasmTrapFault()
JitActivation* act = cx->activation()->asJit();
void* pc = reinterpret_cast<void*>(get_pc());
uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));
const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
if (!segment || !segment->isModule())
@ -1724,7 +1741,7 @@ Simulator::handleWasmTrapFault()
if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
return false;
act->startWasmTrap(trap, bytecode.offset, pc, fp);
act->startWasmTrap(trap, bytecode.offset, registerState());
set_pc(int64_t(moduleSegment->trapCode()));
return true;
}
@ -4007,9 +4024,6 @@ Simulator::instructionDecode(SimInstruction* instr)
void
Simulator::branchDelayInstructionDecode(SimInstruction* instr)
{
if (single_stepping_)
single_step_callback_(single_step_callback_arg_, this, (void*)instr);
if (instr->instructionBits() == NopInst) {
// Short-cut generic nop instructions. They are always valid and they
// never change the simulator state.

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

@ -35,6 +35,7 @@
#include "mozilla/Atomics.h"
#include "jit/IonTypes.h"
#include "js/ProfilingFrameIterator.h"
#include "threading/Thread.h"
#include "vm/MutexIDs.h"
@ -82,6 +83,12 @@ const uint32_t kFCSROverflowFlagBit = 4;
const uint32_t kFCSRDivideByZeroFlagBit = 5;
const uint32_t kFCSRInvalidOpFlagBit = 6;
const uint32_t kFCSRInexactCauseBit = 12;
const uint32_t kFCSRUnderflowCauseBit = 13;
const uint32_t kFCSROverflowCauseBit = 14;
const uint32_t kFCSRDivideByZeroCauseBit = 15;
const uint32_t kFCSRInvalidOpCauseBit = 16;
const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit;
const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit;
const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit;
@ -314,7 +321,7 @@ class Simulator {
// Handle a wasm interrupt triggered by an async signal handler.
void handleWasmInterrupt();
void startInterrupt(JitActivation* act);
JS::ProfilingFrameIterator::RegisterState registerState();
// Handle any wasm faults, returning true if the fault was handled.
bool handleWasmFault(uint64_t addr, unsigned numBytes);

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

@ -7788,14 +7788,16 @@ class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0>
};
// Guard that a value is in a TypeSet.
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 2>
{
public:
LIR_HEADER(TypeBarrierV)
LTypeBarrierV(const LBoxAllocation& input, const LDefinition& temp) {
LTypeBarrierV(const LBoxAllocation& input, const LDefinition& unboxTemp,
const LDefinition& objTemp) {
setBoxOperand(Input, input);
setTemp(0, temp);
setTemp(0, unboxTemp);
setTemp(1, objTemp);
}
static const size_t Input = 0;
@ -7803,9 +7805,12 @@ class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
const MTypeBarrier* mir() const {
return mir_->toTypeBarrier();
}
const LDefinition* temp() {
const LDefinition* unboxTemp() {
return getTemp(0);
}
const LDefinition* objTemp() {
return getTemp(1);
}
};
// Guard that a object is in a TypeSet.
@ -7830,14 +7835,16 @@ class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
};
// Guard that a value is in a TypeSet.
class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 2>
{
public:
LIR_HEADER(MonitorTypes)
LMonitorTypes(const LBoxAllocation& input, const LDefinition& temp) {
LMonitorTypes(const LBoxAllocation& input, const LDefinition& unboxTemp,
const LDefinition& objTemp) {
setBoxOperand(Input, input);
setTemp(0, temp);
setTemp(0, unboxTemp);
setTemp(1, objTemp);
}
static const size_t Input = 0;
@ -7845,9 +7852,12 @@ class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
const MMonitorTypes* mir() const {
return mir_->toMonitorTypes();
}
const LDefinition* temp() {
const LDefinition* unboxTemp() {
return getTemp(0);
}
const LDefinition* objTemp() {
return getTemp(1);
}
};
// Generational write barrier used when writing an object to another object.

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

@ -1427,6 +1427,14 @@ static const LiveRegisterSet AllRegsExceptPCSP(
(uint32_t(1) << Registers::pc))),
FloatRegisterSet(FloatRegisters::AllDoubleMask));
static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
static const LiveRegisterSet AllUserRegsExceptSP(
GeneralRegisterSet(Registers::AllMask & ~((uint32_t(1) << Registers::k0) |
(uint32_t(1) << Registers::k1) |
(uint32_t(1) << Registers::sp) |
(uint32_t(1) << Registers::zero))),
FloatRegisterSet(FloatRegisters::AllDoubleMask));
static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
#else
static const LiveRegisterSet AllRegsExceptSP(
GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
@ -1487,14 +1495,16 @@ GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
// Reserve space to store resumePC and HeapReg.
masm.subFromStackPtr(Imm32(2 * sizeof(intptr_t)));
// set to zero so we can use masm.framePushed() below.
// Set to zero so we can use masm.framePushed() below.
masm.setFramePushed(0);
static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
// save all registers,except sp. After this stack is alligned.
masm.PushRegsInMask(AllRegsExceptSP);
// Save the stack pointer in a non-volatile register.
// Save all registers, except sp.
masm.PushRegsInMask(AllUserRegsExceptSP);
// Save the stack pointer and FCSR in a non-volatile registers.
masm.moveStackPtrTo(s0);
masm.as_cfc1(s1, Assembler::FCSR);
// Align the stack.
masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
@ -1509,19 +1519,18 @@ GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::HandleExecutionInterrupt);
# ifdef USES_O32_ABI
masm.addToStackPtr(Imm32(4 * sizeof(intptr_t)));
# endif
masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
// This will restore stack to the address before the call.
masm.moveToStackPtr(s0);
// Restore FCSR.
masm.as_ctc1(s1, Assembler::FCSR);
// Store resumePC into the reserved space.
masm.storePtr(ReturnReg, Address(s0, masm.framePushed()));
masm.PopRegsInMask(AllRegsExceptSP);
masm.PopRegsInMask(AllUserRegsExceptSP);
// Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
// during jump delay slot.

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

@ -137,7 +137,7 @@ class ReftestRunner(MozbuildObject):
args.printDeviceInfo = False
from mozrunner.devices.android_device import grant_runtime_permissions, get_adb_path
grant_runtime_permissions(self)
grant_runtime_permissions(self, args.app)
if not args.adb_path:
args.adb_path = get_adb_path(self)

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

@ -860,6 +860,10 @@ public abstract class GeckoApp extends GeckoActivity
public void onFocusRequest(final GeckoSession session) {
}
@Override // GeckoSession.ContentListener
public void onCloseRequest(final GeckoSession session) {
}
@Override // GeckoSession.ContentListener
public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
if (fullScreen) {

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

@ -649,6 +649,13 @@ public class CustomTabsActivity extends AppCompatActivity
return true;
}
@Override
public void onNewSession(final GeckoSession session, final String uri,
final GeckoSession.Response<GeckoSession> response) {
// We should never get here because we abort loads that need a new session in onLoadUri()
throw new IllegalStateException("Unexpected new session");
}
/* GeckoSession.ProgressListener */
@Override
public void onPageStart(GeckoSession session, String url) {
@ -686,6 +693,11 @@ public class CustomTabsActivity extends AppCompatActivity
startActivity(intent);
}
@Override
public void onCloseRequest(GeckoSession session) {
// Ignore
}
@Override
public void onFullScreen(GeckoSession session, boolean fullScreen) {
ActivityUtils.setFullScreen(this, fullScreen);

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

@ -356,6 +356,11 @@ public class WebAppActivity extends AppCompatActivity
startActivity(intent);
}
@Override // GeckoSession.ContentListener
public void onCloseRequest(GeckoSession session) {
// Ignore
}
@Override // GeckoSession.ContentListener
public void onContextMenu(GeckoSession session, int screenX, int screenY,
String uri, String elementSrc) {
@ -422,6 +427,13 @@ public class WebAppActivity extends AppCompatActivity
return true;
}
@Override
public void onNewSession(final GeckoSession session, final String uri,
final GeckoSession.Response<GeckoSession> response) {
// We should never get here because we abort loads that need a new session in onLoadUri()
throw new IllegalStateException("Unexpected new session");
}
private void updateFullScreen() {
boolean fullScreen = mIsFullScreenContent || mIsFullScreenMode;
if (ActivityUtils.isFullScreen(this) == fullScreen) {

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

@ -24,6 +24,7 @@ class GeckoViewContent extends GeckoViewContentModule {
addEventListener("DOMTitleChanged", this, false);
addEventListener("DOMWindowFocus", this, false);
addEventListener("DOMWindowClose", this, false);
addEventListener("MozDOMFullscreen:Entered", this, false);
addEventListener("MozDOMFullscreen:Exit", this, false);
addEventListener("MozDOMFullscreen:Exited", this, false);
@ -43,6 +44,7 @@ class GeckoViewContent extends GeckoViewContentModule {
removeEventListener("DOMTitleChanged", this);
removeEventListener("DOMWindowFocus", this);
removeEventListener("DOMWindowClose", this);
removeEventListener("MozDOMFullscreen:Entered", this);
removeEventListener("MozDOMFullscreen:Exit", this);
removeEventListener("MozDOMFullscreen:Exited", this);
@ -179,6 +181,16 @@ class GeckoViewContent extends GeckoViewContentModule {
type: "GeckoView:DOMWindowFocus"
});
break;
case "DOMWindowClose":
if (!aEvent.isTrusted) {
return;
}
aEvent.preventDefault();
this.eventDispatcher.sendRequest({
type: "GeckoView:DOMWindowClose"
});
break;
}
}
}

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

@ -9,6 +9,8 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "EventDispatcher",
"resource://gre/modules/Messaging.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "WindowEventDispatcher",
() => EventDispatcher.for(window));
@ -23,8 +25,8 @@ XPCOMUtils.defineLazyGetter(this, "dump", () =>
// and remove by calling
// remove(<type name>)
var ModuleManager = {
init: function() {
this.browser = document.getElementById("content");
init: function(aBrowser) {
this.browser = aBrowser;
this.modules = {};
},
@ -45,8 +47,24 @@ var ModuleManager = {
}
};
function createBrowser() {
const browser = window.browser = document.createElement("browser");
browser.setAttribute("type", "content");
browser.setAttribute("primary", "true");
browser.setAttribute("flex", "1");
// There may be a GeckoViewNavigation module in another window waiting for us to
// create a browser so it can call presetOpenerWindow(), so allow them to do that now.
Services.obs.notifyObservers(window, "geckoview-window-created");
window.document.getElementById("main-window").appendChild(browser);
browser.stop();
return browser;
}
function startup() {
ModuleManager.init();
const browser = createBrowser();
ModuleManager.init(browser);
// GeckoViewNavigation needs to go first because nsIDOMBrowserWindow must set up
// before the first remote browser. Bug 1365364.
@ -69,5 +87,5 @@ function startup() {
// Move focus to the content window at the end of startup,
// so things like text selection can work properly.
document.getElementById("content").focus();
browser.focus();
}

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

@ -3,14 +3,9 @@
- 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/. -->
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<window id="main-window"
onload="startup();"
windowtype="navigator:geckoview"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<browser id="content" type="content" primary="true" src="about:blank" flex="1"/>
<script type="application/javascript" src="chrome://geckoview/content/geckoview.js"/>
</window>

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

@ -17,10 +17,28 @@ GeckoViewStartup.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
/**
* Register resource://android as the APK root.
*
* Consumers can access Android assets using resource://android/assets/FILENAME.
*/
setResourceSubstitutions: function() {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
// Like jar:jar:file:///data/app/org.mozilla.geckoview.test.apk!/assets/omni.ja!/chrome/geckoview/content/geckoview.js
let url = registry.convertChromeURL(Services.io.newURI("chrome://geckoview/content/geckoview.js")).spec;
// Like jar:file:///data/app/org.mozilla.geckoview.test.apk!/
url = url.substring(4, url.indexOf("!/") + 2);
let protocolHandler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
protocolHandler.setSubstitution("android", Services.io.newURI(url));
},
/* ---------- nsIObserver ---------- */
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case "app-startup": {
this.setResourceSubstitutions();
// Parent and content process.
Services.obs.addObserver(this, "chrome-document-global-created");
Services.obs.addObserver(this, "content-document-global-created");

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

@ -50,6 +50,8 @@ android {
versionName "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// TODO: ensure these fields always agree with mobile/android/geckoview/BuildConfig.java.in,
// either by diffing the processed files or by generating the output from a single source.
buildConfigField 'String', "GRE_MILESTONE", "\"${mozconfig.substs.GRE_MILESTONE}\""
@ -152,6 +154,11 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.5.1'
testImplementation 'org.mockito:mockito-core:1.10.19'
androidTestImplementation 'com.android.support.test:runner:0.5'
androidTestImplementation 'com.android.support.test:rules:0.5'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestImplementation "com.android.support:support-annotations:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
}
apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"

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

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mozilla.geckoview.test">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".TestRunnerActivity" android:exported="true"/>
<activity-alias android:name=".App" android:targetActivity=".TestRunnerActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
</application>
</manifest>

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

@ -0,0 +1,6 @@
<html>
<head><title>Hello, world!</title></head>
<body>
<p>Hello, world!</p>
</body>
</html>

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

@ -0,0 +1,6 @@
<html>
<head><title>Hello, world! Again!</title></head>
<body>
<p>Hello, world! Again!</p>
</body>
</html>

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

@ -0,0 +1,20 @@
<html>
<head><title>Hello, world!</title></head>
<body>
<p id="message"></p>
<script>
const msg = document.getElementById("message");
msg.innerText = "Waiting for click...";
window.addEventListener("click", function() {
msg.innerText = "Opening window....";
try {
const win = window.open("newSession_child.html");
msg.innerText = "Opened window: " + win;
} catch (e) {
msg.innerText = "Failed to open window: " + e;
}
});
</script>
</body>
</html>

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

@ -0,0 +1,11 @@
<html>
<head><title>Hello, world!</title></head>
<body>
<p>I'm the child</p>
<script>
setTimeout(function() {
window.close();
}, 1000);
</script>
</body>
</html>

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

@ -0,0 +1,118 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.geckoview.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.mozilla.gecko.GeckoSession;
import org.mozilla.gecko.GeckoView;
import android.net.Uri;
import android.os.SystemClock;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingResource;
import android.support.test.espresso.assertion.ViewAssertions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.view.MotionEvent;
import static org.junit.Assert.assertTrue;
public class BaseGeckoViewTest {
protected GeckoSession mSession;
protected GeckoView mView;
private ExplicitIdlingResource mIdlingResource;
@Before
public void setup() {
mView = mActivityRule.getActivity().getGeckoView();
mSession = mActivityRule.getActivity().getGeckoSession();
mIdlingResource = new ExplicitIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
@After
public void tearDown() {
Espresso.unregisterIdlingResources(mIdlingResource);
}
@Rule
public ActivityTestRule<TestRunnerActivity> mActivityRule = new ActivityTestRule<>(
TestRunnerActivity.class);
protected void waitUntilDone() {
Espresso.onView(ViewMatchers.isRoot()).check(ViewAssertions.matches(ViewMatchers.isRoot()));
}
protected void done() {
mIdlingResource.done();
}
protected String buildAssetUrl(String path) {
return "resource://android/assets/www/" + path;
}
protected void loadTestPath(final String path, Runnable finished) {
loadTestPage(buildAssetUrl(path), finished);
}
protected void loadTestPage(final String url, final Runnable finished) {
final String path = Uri.parse(url).getPath();
mSession.setProgressListener(new GeckoSession.ProgressListener() {
@Override
public void onPageStart(GeckoSession session, String loadingUrl) {
assertTrue("Loaded url should end with " + path, loadingUrl.endsWith(path));
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
assertTrue("Load should succeed", success);
mSession.setProgressListener(null);
finished.run();
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
}
});
mSession.loadUri(url);
}
protected void sendClick(int x, int y) {
mSession.getPanZoomController().onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 5, 5, 0));
mSession.getPanZoomController().onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 5, 5, 0));
}
private class ExplicitIdlingResource implements IdlingResource {
private boolean mIsIdle;
private ResourceCallback mCallback;
@Override
public String getName() {
return "Explicit Idling Resource";
}
@Override
public boolean isIdleNow() {
return mIsIdle;
}
public void done() {
mIsIdle = true;
if (mCallback != null) {
mCallback.onTransitionToIdle();
}
}
@Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
}
}

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

@ -0,0 +1,298 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.geckoview.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.GeckoSession;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
public class NavigationTests extends BaseGeckoViewTest {
@Test
public void testLoadUri() {
loadTestPath("hello.html", new Runnable() {
@Override public void run() {
done();
}
});
waitUntilDone();
}
@Test
public void testGoBack() {
final String startPath = "hello.html";
loadTestPath(startPath, new Runnable() {
@Override public void run() {
loadTestPath("hello2.html", new Runnable() {
@Override public void run() {
mSession.setNavigationListener(new GeckoSession.NavigationListener() {
@Override
public void onLocationChange(GeckoSession session, String url) {
assertTrue("URL should end with " + startPath + ", got " + url, url.endsWith(startPath));
done();
}
@Override
public void onCanGoBack(GeckoSession session, boolean canGoBack) {
assertFalse("Should not be able to go back", canGoBack);
}
@Override
public void onCanGoForward(GeckoSession session, boolean canGoForward) {
assertTrue("Should be able to go forward", canGoForward);
}
@Override
public boolean onLoadUri(GeckoSession session, String uri, TargetWindow where) {
return false;
}
@Override
public void onNewSession(GeckoSession session, String uri, GeckoSession.Response<GeckoSession> response) {
response.respond(null);
}
});
mSession.goBack();
}
});
}
});
waitUntilDone();
}
@Test
public void testReload() {
loadTestPath("hello.html", new Runnable() {
@Override public void run() {
mSession.setProgressListener(new GeckoSession.ProgressListener() {
@Override
public void onPageStart(GeckoSession session, String url) {
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
assertTrue(success);
done();
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
}
});
mSession.reload();
}
});
waitUntilDone();
}
@Test
public void testExpiredCert() {
mSession.setProgressListener(new GeckoSession.ProgressListener() {
private boolean mNotBlank;
@Override
public void onPageStart(GeckoSession session, String url) {
mNotBlank = !url.equals("about:blank");
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
if (mNotBlank) {
assertFalse("Expected unsuccessful page load", success);
done();
}
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
assertFalse(securityInfo.isSecure);
assertEquals(securityInfo.securityMode, SecurityInformation.SECURITY_MODE_UNKNOWN);
}
});
mSession.loadUri("https://expired.badssl.com/");
waitUntilDone();
}
@Test
public void testValidTLS() {
mSession.setProgressListener(new GeckoSession.ProgressListener() {
private boolean mNotBlank;
@Override
public void onPageStart(GeckoSession session, String url) {
mNotBlank = !url.equals("about:blank");
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
if (mNotBlank) {
assertTrue("Expected successful page load", success);
done();
}
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
assertTrue(securityInfo.isSecure);
assertEquals(securityInfo.securityMode, SecurityInformation.SECURITY_MODE_IDENTIFIED);
}
});
mSession.loadUri("https://mozilla-modern.badssl.com/");
waitUntilDone();
}
@Test
public void testOnNewSession() {
mSession.setNavigationListener(new GeckoSession.NavigationListener() {
@Override
public void onLocationChange(GeckoSession session, String url) {
}
@Override
public void onCanGoBack(GeckoSession session, boolean canGoBack) {
}
@Override
public void onCanGoForward(GeckoSession session, boolean canGoForward) {
}
@Override
public boolean onLoadUri(GeckoSession session, String uri, TargetWindow where) {
return false;
}
@Override
public void onNewSession(GeckoSession session, String uri, GeckoSession.Response<GeckoSession> response) {
final GeckoSession newSession = new GeckoSession(session.getSettings());
newSession.setContentListener(new GeckoSession.ContentListener() {
@Override
public void onTitleChange(GeckoSession session, String title) {
}
@Override
public void onFocusRequest(GeckoSession session) {
}
@Override
public void onCloseRequest(GeckoSession session) {
session.closeWindow();
done();
}
@Override
public void onFullScreen(GeckoSession session, boolean fullScreen) {
}
@Override
public void onContextMenu(GeckoSession session, int screenX, int screenY, String uri, String elementSrc) {
}
});
newSession.openWindow(InstrumentationRegistry.getTargetContext());
response.respond(newSession);
}
});
mSession.setProgressListener(new GeckoSession.ProgressListener() {
@Override
public void onPageStart(GeckoSession session, String url) {
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
// Send a click to open the window
sendClick(100, 100);
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
}
});
mSession.loadUri(buildAssetUrl("newSession.html"));
waitUntilDone();
}
@Test(expected = IllegalArgumentException.class)
public void testOnNewSessionNoExisting() {
// This makes sure that we get an exception if you try to return
// an existing GeckoSession instance from the NavigationListener.onNewSession()
// implementation.
mSession.setNavigationListener(new GeckoSession.NavigationListener() {
@Override
public void onLocationChange(GeckoSession session, String url) {
}
@Override
public void onCanGoBack(GeckoSession session, boolean canGoBack) {
}
@Override
public void onCanGoForward(GeckoSession session, boolean canGoForward) {
}
@Override
public boolean onLoadUri(GeckoSession session, String uri, TargetWindow where) {
return false;
}
@Override
public void onNewSession(GeckoSession session, String uri, GeckoSession.Response<GeckoSession> response) {
// This is where the throw should occur
response.respond(mSession);
}
});
mSession.setProgressListener(new GeckoSession.ProgressListener() {
@Override
public void onPageStart(GeckoSession session, String url) {
}
@Override
public void onPageStop(GeckoSession session, boolean success) {
sendClick(100, 100);
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
}
});
mSession.loadUri(buildAssetUrl("newSession.html"));
waitUntilDone();
}
}

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

@ -0,0 +1,139 @@
package org.mozilla.geckoview.test;
import org.mozilla.gecko.GeckoSession;
import org.mozilla.gecko.GeckoSessionSettings;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.mozglue.SafeIntent;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class TestRunnerActivity extends Activity {
private static final String LOGTAG = "TestRunnerActivity";
GeckoSession mSession;
GeckoView mView;
private GeckoSession.NavigationListener mNavigationListener = new GeckoSession.NavigationListener() {
@Override
public void onLocationChange(GeckoSession session, String url) {
getActionBar().setSubtitle(url);
}
@Override
public void onCanGoBack(GeckoSession session, boolean canGoBack) {
}
@Override
public void onCanGoForward(GeckoSession session, boolean canGoForward) {
}
@Override
public boolean onLoadUri(GeckoSession session, String uri, TargetWindow where) {
// Allow Gecko to load all URIs
return false;
}
@Override
public void onNewSession(GeckoSession session, String uri, GeckoSession.Response<GeckoSession> response) {
response.respond(createSession(session.getSettings()));
}
};
private GeckoSession.ContentListener mContentListener = new GeckoSession.ContentListener() {
@Override
public void onTitleChange(GeckoSession session, String title) {
}
@Override
public void onFocusRequest(GeckoSession session) {
}
@Override
public void onCloseRequest(GeckoSession session) {
session.closeWindow();
}
@Override
public void onFullScreen(GeckoSession session, boolean fullScreen) {
}
@Override
public void onContextMenu(GeckoSession session, int screenX, int screenY, String uri, String elementSrc) {
}
};
private GeckoSession createSession() {
return createSession(null);
}
private GeckoSession createSession(GeckoSessionSettings settings) {
if (settings == null) {
settings = new GeckoSessionSettings();
// We can't use e10s because we get deadlocked when quickly creating and
// destroying sessions. Bug 1348361.
settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
}
final GeckoSession session = new GeckoSession(settings);
session.setNavigationListener(mNavigationListener);
session.openWindow(this);
return session;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
GeckoLoader.setLastIntent(new SafeIntent(getIntent()));
final String intentArgs = intent.getStringExtra("args");
final String args = intentArgs != null ? "-purgecaches " + intentArgs : "-purgecaches";
GeckoSession.preload(this, args, false /* no multiprocess, see below */);
// We can't use e10s because we get deadlocked when quickly creating and
// destroying sessions. Bug 1348361.
mSession = createSession();
// If we were passed a URI in the Intent, open it
final Uri uri = intent.getData();
if (uri != null) {
mSession.loadUri(uri);
}
mView = new GeckoView(this);
mView.setSession(mSession);
setContentView(mView);
}
@Override
protected void onDestroy() {
mSession.closeWindow();
super.onDestroy();
}
public GeckoView getGeckoView() {
return mView;
}
public GeckoSession getGeckoSession() {
return mSession;
}
}

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

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.9 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.9 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 10 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 9.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB

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

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<resources>
<string name="app_name">GeckoView Test Runner</string>
</resources>

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

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

@ -83,6 +83,11 @@ public class GeckoThread extends Thread {
}
return false;
}
@Override
public String toString() {
return name();
}
}
private static final NativeQueue sNativeQueue =
@ -495,13 +500,17 @@ public class GeckoThread extends Thread {
@WrapForJNI(calledFrom = "gecko")
private static void setState(final State newState) {
sNativeQueue.setState(newState);
checkAndSetState(null, newState);
}
@WrapForJNI(calledFrom = "gecko")
private static boolean checkAndSetState(final State expectedState,
final State newState) {
return sNativeQueue.checkAndSetState(expectedState, newState);
final boolean result = sNativeQueue.checkAndSetState(expectedState, newState);
if (result) {
Log.d(LOGTAG, "State changed to " + newState);
}
return result;
}
@WrapForJNI(stubName = "SpeculativeConnect")

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

@ -9,6 +9,7 @@ package org.mozilla.geckoview;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.EventDispatcher;
@ -73,6 +74,9 @@ public class GeckoSession extends LayerSession
private final TextInputController mTextInput = new TextInputController(this, mNativeQueue);
private String mId = UUID.randomUUID().toString().replace("-", "");
/* package */ String getId() { return mId; }
private final GeckoSessionHandler<ContentListener> mContentHandler =
new GeckoSessionHandler<ContentListener>(
"GeckoViewContent", this,
@ -80,6 +84,7 @@ public class GeckoSession extends LayerSession
"GeckoView:ContextMenu",
"GeckoView:DOMTitleChanged",
"GeckoView:DOMWindowFocus",
"GeckoView:DOMWindowClose",
"GeckoView:FullScreenEnter",
"GeckoView:FullScreenExit"
}
@ -101,6 +106,8 @@ public class GeckoSession extends LayerSession
message.getString("title"));
} else if ("GeckoView:DOMWindowFocus".equals(event)) {
listener.onFocusRequest(GeckoSession.this);
} else if ("GeckoView:DOMWindowClose".equals(event)) {
listener.onCloseRequest(GeckoSession.this);
} else if ("GeckoView:FullScreenEnter".equals(event)) {
listener.onFullScreen(GeckoSession.this, true);
} else if ("GeckoView:FullScreenExit".equals(event)) {
@ -114,7 +121,8 @@ public class GeckoSession extends LayerSession
"GeckoViewNavigation", this,
new String[]{
"GeckoView:LocationChange",
"GeckoView:OnLoadUri"
"GeckoView:OnLoadUri",
"GeckoView:OnNewSession"
}
) {
@Override
@ -137,6 +145,19 @@ public class GeckoSession extends LayerSession
final boolean result =
listener.onLoadUri(GeckoSession.this, uri, where);
callback.sendSuccess(result);
} else if ("GeckoView:OnNewSession".equals(event)) {
final String uri = message.getString("uri");
listener.onNewSession(GeckoSession.this, uri,
new Response<GeckoSession>() {
@Override
public void respond(GeckoSession session) {
if (session != null && session.isOpen() && session.isReady()) {
throw new IllegalArgumentException("Must use a new GeckoSession instance");
}
callback.sendSuccess(session != null ? session.getId() : null);
}
});
}
}
};
@ -353,7 +374,7 @@ public class GeckoSession extends LayerSession
public static native void open(Window instance, Compositor compositor,
EventDispatcher dispatcher,
GeckoBundle settings, String chromeUri,
int screenId, boolean privateMode);
int screenId, boolean privateMode, String id);
@Override // JNIObject
protected void disposeNative() {
@ -434,7 +455,7 @@ public class GeckoSession extends LayerSession
private GeckoSessionSettings mSettings;
public GeckoSession() {
this(/* settings */ null);
this(null);
}
public GeckoSession(final GeckoSessionSettings settings) {
@ -447,13 +468,15 @@ public class GeckoSession extends LayerSession
mListener.registerListeners();
}
private void transferFrom(final Window window, final GeckoSessionSettings settings) {
private void transferFrom(final Window window, final GeckoSessionSettings settings,
final String id) {
if (isOpen()) {
throw new IllegalStateException("Session is open");
}
mWindow = window;
mSettings = new GeckoSessionSettings(settings, this);
mId = id;
if (mWindow != null) {
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
@ -471,7 +494,7 @@ public class GeckoSession extends LayerSession
}
/* package */ void transferFrom(final GeckoSession session) {
transferFrom(session.mWindow, session.mSettings);
transferFrom(session.mWindow, session.mSettings, session.mId);
session.mWindow = null;
session.onWindowChanged();
}
@ -485,6 +508,7 @@ public class GeckoSession extends LayerSession
public void writeToParcel(Parcel out, int flags) {
out.writeStrongInterface(mWindow);
out.writeParcelable(mSettings, flags);
out.writeString(mId);
}
// AIDL code may call readFromParcel even though it's not part of Parcelable.
@ -495,7 +519,8 @@ public class GeckoSession extends LayerSession
final Window window = (ifce instanceof Window) ? (Window) ifce : null;
final GeckoSessionSettings settings =
source.readParcelable(getClass().getClassLoader());
transferFrom(window, settings);
final String id = source.readString();
transferFrom(window, settings, id);
}
public static final Creator<GeckoSession> CREATOR = new Creator<GeckoSession>() {
@ -546,6 +571,10 @@ public class GeckoSession extends LayerSession
return mWindow != null;
}
/* package */ boolean isReady() {
return mNativeQueue.isReady();
}
public void openWindow(final Context appContext) {
ThreadUtils.assertOnUiThread();
@ -567,7 +596,7 @@ public class GeckoSession extends LayerSession
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
Window.open(mWindow, mCompositor, mEventDispatcher,
mSettings.asBundle(), chromeUri, screenId, isPrivate);
mSettings.asBundle(), chromeUri, screenId, isPrivate, mId);
} else {
GeckoThread.queueNativeCallUntil(
GeckoThread.State.PROFILE_READY,
@ -577,7 +606,7 @@ public class GeckoSession extends LayerSession
EventDispatcher.class, mEventDispatcher,
GeckoBundle.class, mSettings.asBundle(),
String.class, chromeUri,
screenId, isPrivate);
screenId, isPrivate, mId);
}
onWindowChanged();
@ -762,7 +791,7 @@ public class GeckoSession extends LayerSession
/**
* Set the tracking protection callback handler.
* This will replace the current handler.
* @param listener An implementation of TrackingProtectionDelegate.
* @param delegate An implementation of TrackingProtectionDelegate.
*/
public void setTrackingProtectionDelegate(TrackingProtectionDelegate delegate) {
mTrackingProtectionHandler.setListener(delegate, this);
@ -1278,6 +1307,12 @@ public class GeckoSession extends LayerSession
*/
void onFocusRequest(GeckoSession session);
/**
* A page has requested to close
* @param session The GeckoSession that initiated the callback.
*/
void onCloseRequest(GeckoSession session);
/**
* A page has entered or exited full screen mode. Typically, the implementation
* would set the Activity containing the GeckoSession to full screen when the page is
@ -1306,6 +1341,16 @@ public class GeckoSession extends LayerSession
String uri, String elementSrc);
}
/**
* This is used to send responses in delegate methods that have asynchronous responses.
*/
public interface Response<T> {
/**
* @param val The value contained in the response
*/
void respond(T val);
}
public interface NavigationListener {
/**
* A view has started loading content from the network.
@ -1378,10 +1423,22 @@ public class GeckoSession extends LayerSession
* @param uri The URI to be loaded.
* @param where The target window.
*
* @return True if the URI loading has been handled, false if Gecko
* should handle the loading.
* @return Whether or not the load was handled. Returning false will allow Gecko
* to continue the load as normal.
*/
boolean onLoadUri(GeckoSession session, String uri, TargetWindow where);
/**
* A request has been made to open a new session. The URI is provided only for
* informational purposes. Do not call GeckoSession.loadUri() here. Additionally, the
* returned GeckoSession must be a newly-created one.
*
* @param session The GeckoSession that initiated the callback.
* @param uri The URI to be loaded.
*
* @param response A Response which will hold the returned GeckoSession
*/
void onNewSession(GeckoSession session, String uri, Response<GeckoSession> response);
}
/**
@ -1877,7 +1934,7 @@ public class GeckoSession extends LayerSession
* @param session The GeckoSession that initiated the callback.
* @param uri The URI of the blocked element.
* @param categories The tracker categories of the blocked element.
* One or more of the {@link #CATEGORY_AD CATEGORY_*}
* One or more of the {@link TrackingProtectionDelegate#CATEGORY_AD}
* flags.
*/
void onTrackerBlocked(GeckoSession session, String uri, int categories);
@ -1886,7 +1943,7 @@ public class GeckoSession extends LayerSession
/**
* Enable tracking protection.
* @param categories The categories of trackers that should be blocked.
* Use one or more of the {@link #CATEGORY_AD CATEGORY_*}
* Use one or more of the {@link TrackingProtectionDelegate#CATEGORY_AD}
* flags.
**/
public void enableTrackingProtection(int categories) {

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

@ -89,6 +89,8 @@ import android.util.Log;
if (mListener != null) {
handleMessage(mListener, event, message, callback);
} else {
callback.sendError("No listener registered");
}
}

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

@ -23,6 +23,7 @@ import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.geckoview.GeckoSession;
import org.mozilla.geckoview.GeckoSessionSettings;
import org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource;
import org.mozilla.geckoview.GeckoSession.Response;
import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
import org.mozilla.geckoview.GeckoView;
@ -87,8 +88,8 @@ public class GeckoViewActivity extends Activity {
permission.androidPermissionRequestCode = REQUEST_PERMISSIONS;
mGeckoSession.setPermissionDelegate(permission);
mGeckoView.getSettings().setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
useMultiprocess);
mGeckoSession.getSettings().setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
useMultiprocess);
mGeckoSession.enableTrackingProtection(
TrackingProtectionDelegate.CATEGORY_AD |
@ -173,11 +174,6 @@ public class GeckoViewActivity extends Activity {
Log.i(LOGTAG, "Content title changed to " + title);
}
@Override
public void onFocusRequest(GeckoSession session) {
Log.i(LOGTAG, "Content requesting focus");
}
@Override
public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
getWindow().setFlags(fullScreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
@ -189,6 +185,18 @@ public class GeckoViewActivity extends Activity {
}
}
@Override
public void onFocusRequest(final GeckoSession session) {
Log.i(LOGTAG, "Content requesting focus");
}
@Override
public void onCloseRequest(final GeckoSession session) {
if (session != mGeckoSession) {
session.closeWindow();
}
}
@Override
public void onContextMenu(GeckoSession session, int screenX, int screenY,
String uri, String elementSrc) {
@ -346,13 +354,13 @@ public class GeckoViewActivity extends Activity {
@Override
public boolean onLoadUri(final GeckoSession session, final String uri,
final TargetWindow where) {
Log.d(LOGTAG, "onLoadUri=" + uri +
" where=" + where);
if (where != TargetWindow.NEW) {
return false;
}
session.loadUri(uri);
return true;
Log.d(LOGTAG, "onLoadUri=" + uri + " where=" + where);
return false;
}
@Override
public void onNewSession(final GeckoSession session, final String uri, Response<GeckoSession> response) {
response.respond(null);
}
}

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

@ -43,10 +43,11 @@ ext.configureVariantWithGeckoBinaries = { variant ->
def distDir = "${topobjdir}/dist/${omnijar_dir}"
def syncOmnijarFromDistDir = task("syncOmnijarFromDistDirFor${variant.name.capitalize()}", type: Sync) {
doFirst {
onlyIf {
if (source.empty) {
throw new GradleException("Required omnijar not found in ${source.asPath}. Have you built and packaged?")
throw new StopExecutionException("Required omnijar not found in ${distDir}/{omni.ja,assets/omni.ja}. Have you built and packaged?")
}
return true
}
into("${project.buildDir}/moz.build/src/${variant.name}/omnijar")
@ -58,10 +59,11 @@ ext.configureVariantWithGeckoBinaries = { variant ->
}
def syncLibsFromDistDir = task("syncLibsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
doFirst {
onlyIf {
if (source.empty) {
throw new GradleException("Required JNI libraries not found in ${source.asPath}. Have you built and packaged?")
throw new StopExecutionException("Required JNI libraries not found in ${distDir}/lib. Have you built and packaged?")
}
return true
}
into("${project.buildDir}/moz.build/src/${variant.name}/jniLibs")
@ -69,10 +71,11 @@ ext.configureVariantWithGeckoBinaries = { variant ->
}
def syncAssetsFromDistDir = task("syncAssetsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
doFirst {
onlyIf {
if (source.empty) {
throw new GradleException("Required assets not found in ${source.asPath}. Have you built and packaged?")
throw new StopExecutionException("Required assets not found in ${distDir}/assets. Have you built and packaged?")
}
return true
}
into("${project.buildDir}/moz.build/src/${variant.name}/assets")

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше