Merge inbound to mozilla-central. a=merge

This commit is contained in:
shindli 2018-03-03 01:06:35 +02:00
commit 73e569944b
212 changed files with 4079 additions and 740 deletions

View File

@ -2484,18 +2484,19 @@ class TabBrowser {
if ((openerTab &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) ||
Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab || this.mCurrentTab)._tPos + 1;
let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab || this.mCurrentTab)._tPos + 1;
if (lastRelatedTab)
lastRelatedTab.owner = null;
else if (openerTab)
t.owner = openerTab;
this.moveTabTo(t, newTabPos, true);
if (openerTab)
this._lastRelatedTabMap.set(openerTab, t);
}
if (lastRelatedTab) {
lastRelatedTab.owner = null;
} else if (openerTab) {
t.owner = openerTab;
}
this.moveTabTo(t, newTabPos, true);
if (openerTab) {
this._lastRelatedTabMap.set(openerTab, t);
}
}
// This field is updated regardless if we actually animate
// since it's important that we keep this count correct in all cases.

View File

@ -127,6 +127,67 @@ var gTests = [
}
},
{
desc: "getUserMedia audio+video: with two frames sharing at the same time, sharing UI shows all shared devices",
run: async function checkFrameOverridingSharingUI() {
// This tests an edge case discovered in bug 1440356 that works like this
// - Share audio and video in iframe 1.
// - Share only video in iframe 2.
// The WebRTC UI should still show both video and audio indicators.
let promise = promisePopupNotificationShown("webRTC-shareDevices");
await promiseRequestDevice(true, true, "frame1");
await promise;
await expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(true, true);
let indicator = promiseIndicatorWindow();
await promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
Assert.deepEqual((await getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
await indicator;
await checkSharingUI({video: true, audio: true});
await expectNoObserverCalled();
// Check that requesting a new device from a different frame
// doesn't override sharing UI.
promise = promisePopupNotificationShown("webRTC-shareDevices");
await promiseRequestDevice(false, true, "frame2");
await promise;
await expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, true);
await promiseMessage("ok", () => {
PopupNotifications.panel.firstChild.button.click();
});
await expectObserverCalled("getUserMedia:response:allow");
await expectObserverCalled("recording-device-events");
Assert.deepEqual((await getMediaCaptureState()), {audio: true, video: true},
"expected camera and microphone to be shared");
await checkSharingUI({video: true, audio: true});
// Check that ending the stream with the other frame
// doesn't override sharing UI.
promise = promiseObserverCalled("recording-device-events");
await promiseReloadFrame("frame2");
await promise;
await expectObserverCalled("recording-window-ended");
await checkSharingUI({video: true, audio: true});
await expectNoObserverCalled();
await closeStream(false, "frame1");
await expectNoObserverCalled();
await checkNotSharing();
}
},
{
desc: "getUserMedia audio+video: reloading a frame updates the sharing UI",
run: async function checkUpdateWhenReloading() {

View File

@ -4373,7 +4373,10 @@ OverflowableToolbar.prototype = {
this._panel.addEventListener("popupshown", () => {
this._panel.addEventListener("dragover", this);
this._panel.addEventListener("dragend", this);
resolve();
// Wait until the next tick to resolve so all popupshown
// handlers have a chance to run before our promise resolution
// handlers do.
Services.tm.dispatchToMainThread(resolve);
}, {once: true});
});
},

View File

@ -153,6 +153,29 @@ ChannelMediaDecoder::ResourceCallback::NotifyPrincipalChanged()
}
}
void
ChannelMediaDecoder::NotifyPrincipalChanged()
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::NotifyPrincipalChanged();
if (!mInitialChannelPrincipalKnown) {
// We'll receive one notification when the channel's initial principal
// is known, after all HTTP redirects have resolved. This isn't really a
// principal change, so return here to avoid the mSameOriginMedia check
// below.
mInitialChannelPrincipalKnown = true;
return;
}
if (!mSameOriginMedia &&
DecoderTraits::CrossOriginRedirectsProhibited(ContainerType())) {
// For some content types we block mid-flight channel redirects to cross
// origin destinations due to security constraints. See bug 1441153.
LOG("ChannnelMediaDecoder prohibited cross origin redirect blocked.");
NetworkError(MediaResult(NS_ERROR_DOM_BAD_URI,
"Prohibited cross origin redirect blocked"));
}
}
void
ChannelMediaDecoder::ResourceCallback::NotifySuspendedStatusChanged(
bool aSuspendedByCache)

View File

@ -66,6 +66,7 @@ protected:
void MetadataLoaded(UniquePtr<MediaInfo> aInfo,
UniquePtr<MetadataTags> aTags,
MediaDecoderEventVisibility aEventVisibility) override;
void NotifyPrincipalChanged() override;
RefPtr<ResourceCallback> mResourceCallback;
RefPtr<BaseMediaResource> mResource;
@ -158,6 +159,10 @@ private:
int64_t mPlaybackPosition = 0;
bool mCanPlayThrough = false;
// True if we've been notified that the ChannelMediaResource has
// a principal.
bool mInitialChannelPrincipalKnown = false;
};
} // namespace mozilla

View File

@ -325,4 +325,11 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
false;
}
/* static */
bool
DecoderTraits::CrossOriginRedirectsProhibited(const MediaContainerType& aType)
{
return WaveDecoder::IsSupportedType(aType);
}
} // namespace mozilla

View File

@ -57,6 +57,10 @@ public:
static bool IsMatroskaType(const MediaContainerType& aType);
static bool IsSupportedType(const MediaContainerType& aType);
// For some content types we block channel redirects to cross origin
// destinations due to security constraints. See bug 1441153.
static bool CrossOriginRedirectsProhibited(const MediaContainerType& aType);
};
} // namespace mozilla

View File

@ -514,7 +514,7 @@ protected:
// Called by MediaResource when the principal of the resource has
// changed. Called on main thread only.
void NotifyPrincipalChanged();
virtual void NotifyPrincipalChanged();
MozPromiseRequestHolder<SeekPromise> mSeekRequest;

View File

@ -2196,21 +2196,6 @@ int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
}
static void
StopRawIDCallback(MediaManager *aThis,
uint64_t aWindowID,
GetUserMediaWindowListener *aListener,
void *aData)
{
if (!aListener || !aData) {
return;
}
nsString* removedDeviceID = static_cast<nsString*>(aData);
aListener->StopRawID(*removedDeviceID);
}
void MediaManager::OnDeviceChange() {
RefPtr<MediaManager> self(this);
NS_DispatchToMainThread(media::NewRunnableFrom([self]() mutable {
@ -2238,16 +2223,24 @@ void MediaManager::OnDeviceChange() {
}
for (auto& id : self->mDeviceIDs) {
if (!deviceIDs.Contains(id)) {
// Stop the coresponding SourceListener
nsGlobalWindowInner::InnerWindowByIdTable* windowsById =
nsGlobalWindowInner::GetWindowsTable();
if (windowsById) {
for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
nsGlobalWindowInner* window = iter.Data();
self->IterateWindowListeners(window->AsInner(), StopRawIDCallback, &id);
}
}
if (deviceIDs.Contains(id)) {
continue;
}
// Stop the coresponding SourceListener
nsGlobalWindowInner::InnerWindowByIdTable* windowsById =
nsGlobalWindowInner::GetWindowsTable();
if (!windowsById) {
continue;
}
for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
nsGlobalWindowInner* window = iter.Data();
self->IterateWindowListeners(window->AsInner(),
[&id](GetUserMediaWindowListener* aListener)
{
aListener->StopRawID(id);
});
}
}
@ -3094,27 +3087,6 @@ MediaManager::GetBackend(uint64_t aWindowId)
return mBackend;
}
static void
StopSharingCallback(MediaManager *aThis,
uint64_t aWindowID,
GetUserMediaWindowListener *aListener,
void *aData)
{
MOZ_ASSERT(NS_IsMainThread());
// Grab a strong ref since RemoveAll() might destroy the listener mid-way
// when clearing the mActiveWindows reference.
RefPtr<GetUserMediaWindowListener> listener(aListener);
if (!listener) {
return;
}
listener->Stop();
listener->RemoveAll();
MOZ_ASSERT(!aThis->GetWindowListener(aWindowID));
}
void
MediaManager::OnNavigation(uint64_t aWindowID)
{
@ -3136,7 +3108,19 @@ MediaManager::OnNavigation(uint64_t aWindowID)
// be added to from the main-thread
auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
if (window) {
IterateWindowListeners(window->AsInner(), StopSharingCallback, nullptr);
IterateWindowListeners(window->AsInner(),
[self = RefPtr<MediaManager>(this),
windowID = DebugOnly<decltype(aWindowID)>(aWindowID)]
(GetUserMediaWindowListener* aListener)
{
// Grab a strong ref since RemoveAll() might destroy the listener
// mid-way when clearing the mActiveWindows reference.
RefPtr<GetUserMediaWindowListener> listener(aListener);
listener->Stop();
listener->RemoveAll();
MOZ_ASSERT(!self->GetWindowListener(windowID));
});
} else {
RemoveWindowID(aWindowID);
}
@ -3591,63 +3575,52 @@ struct CaptureWindowStateData {
uint16_t* mBrowserShare;
};
static void
CaptureWindowStateCallback(MediaManager *aThis,
uint64_t aWindowID,
GetUserMediaWindowListener *aListener,
void *aData)
{
MOZ_ASSERT(aData);
auto& data = *static_cast<CaptureWindowStateData*>(aData);
if (!aListener) {
return;
}
*data.mCamera =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Camera));
*data.mMicrophone =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Microphone));
*data.mScreenShare =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Screen));
*data.mWindowShare =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Window));
*data.mAppShare =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Application));
*data.mBrowserShare =
FromCaptureState(aListener->CapturingSource(MediaSourceEnum::Browser));
}
NS_IMETHODIMP
MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow,
MediaManager::MediaCaptureWindowState(nsIDOMWindow* aCapturedWindow,
uint16_t* aCamera,
uint16_t* aMicrophone,
uint16_t* aScreenShare,
uint16_t* aWindowShare,
uint16_t* aAppShare,
uint16_t* aBrowserShare)
uint16_t* aScreen,
uint16_t* aWindow,
uint16_t* aApplication,
uint16_t* aBrowser)
{
MOZ_ASSERT(NS_IsMainThread());
struct CaptureWindowStateData data;
data.mCamera = aCamera;
data.mMicrophone = aMicrophone;
data.mScreenShare = aScreenShare;
data.mWindowShare = aWindowShare;
data.mAppShare = aAppShare;
data.mBrowserShare = aBrowserShare;
*aCamera = nsIMediaManagerService::STATE_NOCAPTURE;
*aMicrophone = nsIMediaManagerService::STATE_NOCAPTURE;
*aScreenShare = nsIMediaManagerService::STATE_NOCAPTURE;
*aWindowShare = nsIMediaManagerService::STATE_NOCAPTURE;
*aAppShare = nsIMediaManagerService::STATE_NOCAPTURE;
*aBrowserShare = nsIMediaManagerService::STATE_NOCAPTURE;
CaptureState camera = CaptureState::Off;
CaptureState microphone = CaptureState::Off;
CaptureState screen = CaptureState::Off;
CaptureState window = CaptureState::Off;
CaptureState application = CaptureState::Off;
CaptureState browser = CaptureState::Off;
nsCOMPtr<nsPIDOMWindowInner> piWin = do_QueryInterface(aWindow);
nsCOMPtr<nsPIDOMWindowInner> piWin = do_QueryInterface(aCapturedWindow);
if (piWin) {
IterateWindowListeners(piWin, CaptureWindowStateCallback, &data);
IterateWindowListeners(piWin,
[&camera, &microphone, &screen, &window, &application, &browser]
(GetUserMediaWindowListener* aListener)
{
camera = CombineCaptureState(
camera, aListener->CapturingSource(MediaSourceEnum::Camera));
microphone = CombineCaptureState(
microphone, aListener->CapturingSource(MediaSourceEnum::Microphone));
screen = CombineCaptureState(
screen, aListener->CapturingSource(MediaSourceEnum::Screen));
window = CombineCaptureState(
window, aListener->CapturingSource(MediaSourceEnum::Window));
application = CombineCaptureState(
application, aListener->CapturingSource(MediaSourceEnum::Application));
browser = CombineCaptureState(
browser, aListener->CapturingSource(MediaSourceEnum::Browser));
});
}
*aCamera = FromCaptureState(camera);
*aMicrophone= FromCaptureState(microphone);
*aScreen = FromCaptureState(screen);
*aWindow = FromCaptureState(window);
*aApplication = FromCaptureState(application);
*aBrowser = FromCaptureState(browser);
#ifdef DEBUG
LOG(("%s: window %" PRIu64 " capturing %s %s %s %s %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
*aCamera == nsIMediaManagerService::STATE_CAPTURE_ENABLED
@ -3658,10 +3631,10 @@ MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow,
? "microphone (enabled)"
: (*aMicrophone == nsIMediaManagerService::STATE_CAPTURE_DISABLED
? "microphone (disabled)" : ""),
*aScreenShare ? "screenshare" : "",
*aWindowShare ? "windowshare" : "",
*aAppShare ? "appshare" : "",
*aBrowserShare ? "browsershare" : ""));
*aScreen ? "screenshare" : "",
*aWindow ? "windowshare" : "",
*aApplication ? "appshare" : "",
*aBrowser ? "browsershare" : ""));
#endif
return NS_OK;
}
@ -3676,19 +3649,6 @@ MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
return NS_OK;
}
static void
StopScreensharingCallback(MediaManager *aThis,
uint64_t aWindowID,
GetUserMediaWindowListener *aListener,
void *aData)
{
if (!aListener) {
return;
}
aListener->StopSharing();
}
void
MediaManager::StopScreensharing(uint64_t aWindowID)
{
@ -3699,14 +3659,17 @@ MediaManager::StopScreensharing(uint64_t aWindowID)
if (!window) {
return;
}
IterateWindowListeners(window->AsInner(), &StopScreensharingCallback, nullptr);
IterateWindowListeners(window->AsInner(),
[](GetUserMediaWindowListener* aListener)
{
aListener->StopSharing();
});
}
// lets us do all sorts of things to the listeners
template<typename FunctionType>
void
MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow,
WindowListenerCallback aCallback,
void *aData)
const FunctionType& aCallback)
{
// Iterate the docshell tree to find all the child windows, and for each
// invoke the callback
@ -3714,7 +3677,9 @@ MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow,
{
uint64_t windowID = aWindow->WindowID();
GetUserMediaWindowListener* listener = GetWindowListener(windowID);
(*aCallback)(this, windowID, listener, aData);
if (listener) {
aCallback(listener);
}
// NB: `listener` might have been destroyed.
}
@ -3729,8 +3694,7 @@ MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow,
nsCOMPtr<nsPIDOMWindowOuter> winOuter = item ? item->GetWindow() : nullptr;
if (winOuter) {
IterateWindowListeners(winOuter->GetCurrentInnerWindow(),
aCallback, aData);
IterateWindowListeners(winOuter->GetCurrentInnerWindow(), aCallback);
}
}
}

View File

@ -127,12 +127,6 @@ public:
typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener> WindowTable;
// we could add MediaManager if needed
typedef void (*WindowListenerCallback)(MediaManager *aThis,
uint64_t aWindowID,
GetUserMediaWindowListener *aListener,
void *aData);
class MediaManager final : public nsIMediaManagerService,
public nsIObserver
,public DeviceChangeCallback
@ -253,9 +247,14 @@ private:
void Shutdown();
void StopScreensharing(uint64_t aWindowID);
/**
* Calls aCallback with a GetUserMediaWindowListener argument once for
* each window listener associated with aWindow and its child windows.
*/
template<typename FunctionType>
void IterateWindowListeners(nsPIDOMWindowInner *aWindow,
WindowListenerCallback aCallback,
void *aData);
const FunctionType& aCallback);
void StopMediaStreams();
void RemoveMediaDevicesCallback(uint64_t aWindowID);

View File

@ -1947,7 +1947,7 @@ void
ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mWorkerPrivate);
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
MOZ_ASSERT(!mSupportsArray.Contains(aSupports));
mSupportsArray.AppendElement(aSupports);

View File

@ -25,6 +25,7 @@
#include "nsServiceManagerUtils.h"
#include "ServiceWorker.h"
#include "ServiceWorkerManager.h"
#include "ServiceWorkerPrivate.h"
#include "ServiceWorkerRegistration.h"
#include "nsIDocument.h"
@ -133,7 +134,7 @@ namespace {
void
UpdateInternal(nsIPrincipal* aPrincipal,
const nsAString& aScope,
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
@ -146,7 +147,7 @@ UpdateInternal(nsIPrincipal* aPrincipal,
return;
}
swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
swm->Update(aPrincipal, aScope, aCallback);
}
class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
@ -281,12 +282,52 @@ public:
class SWRUpdateRunnable final : public Runnable
{
class TimerCallback final : public nsITimerCallback
{
RefPtr<ServiceWorkerPrivate> mPrivate;
RefPtr<Runnable> mRunnable;
public:
TimerCallback(ServiceWorkerPrivate* aPrivate,
Runnable* aRunnable)
: mPrivate(aPrivate)
, mRunnable(aRunnable)
{
MOZ_ASSERT(mPrivate);
MOZ_ASSERT(aRunnable);
}
NS_IMETHOD
Notify(nsITimer *aTimer) override
{
mRunnable->Run();
mPrivate->RemoveISupports(aTimer);
return NS_OK;
}
NS_DECL_THREADSAFE_ISUPPORTS
private:
~TimerCallback()
{ }
};
public:
SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope)
explicit SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy)
: Runnable("dom::SWRUpdateRunnable")
, mPromiseProxy(aPromiseProxy)
, mScope(aScope)
{}
, mDescriptor(aPromiseProxy->GetWorkerPrivate()->GetServiceWorkerDescriptor())
, mDelayed(false)
{
MOZ_ASSERT(mPromiseProxy);
// This runnable is used for update calls originating from a worker thread,
// which may be delayed in some cases.
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate()->IsServiceWorker());
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate());
mPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
}
NS_IMETHOD
Run() override
@ -307,20 +348,65 @@ public:
}
MOZ_ASSERT(principal);
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (NS_WARN_IF(!swm)) {
return NS_OK;
}
// This will delay update jobs originating from a service worker thread.
// We don't currently handle ServiceWorkerRegistration.update() from other
// worker types. Also, we assume this registration matches self.registration
// on the service worker global. This is ok for now because service worker globals
// are the only worker contexts where we expose ServiceWorkerRegistration.
RefPtr<ServiceWorkerRegistrationInfo> registration =
swm->GetRegistration(principal, mDescriptor.Scope());
if (NS_WARN_IF(!registration)) {
return NS_OK;
}
RefPtr<ServiceWorkerInfo> worker = registration->GetByDescriptor(mDescriptor);
uint32_t delay = registration->GetUpdateDelay();
// if we have a timer object, it means we've already been delayed once.
if (delay && !mDelayed) {
nsCOMPtr<nsITimerCallback> cb = new TimerCallback(worker->WorkerPrivate(), this);
Result<nsCOMPtr<nsITimer>, nsresult> result =
NS_NewTimerWithCallback(cb, delay, nsITimer::TYPE_ONE_SHOT,
SystemGroup::EventTargetFor(TaskCategory::Other));
nsCOMPtr<nsITimer> timer = result.unwrapOr(nullptr);
if (NS_WARN_IF(!timer)) {
return NS_OK;
}
mDelayed = true;
// We're storing the timer object on the calling service worker's private.
// ServiceWorkerPrivate will drop the reference if the worker terminates,
// which will cancel the timer.
worker->WorkerPrivate()->StoreISupports(timer);
return NS_OK;
}
RefPtr<WorkerThreadUpdateCallback> cb =
new WorkerThreadUpdateCallback(mPromiseProxy);
UpdateInternal(principal, mScope, cb);
UpdateInternal(principal, mDescriptor.Scope(), cb);
return NS_OK;
}
private:
~SWRUpdateRunnable()
{}
{
MOZ_ASSERT(NS_IsMainThread());
}
RefPtr<PromiseWorkerProxy> mPromiseProxy;
const nsString mScope;
const ServiceWorkerDescriptor mDescriptor;
bool mDelayed;
};
NS_IMPL_ISUPPORTS(SWRUpdateRunnable::TimerCallback, nsITimerCallback)
class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
{
PromiseWindowProxy mPromise;
@ -533,7 +619,7 @@ ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
RefPtr<MainThreadUpdateCallback> cb =
new MainThreadUpdateCallback(mOuter->GetOwner(), promise);
UpdateInternal(doc->NodePrincipal(), mScope, cb);
UpdateInternal(doc->NodePrincipal(), NS_ConvertUTF16toUTF8(mScope), cb);
return promise.forget();
}
@ -848,7 +934,7 @@ ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
return nullptr;
}
RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope);
RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy);
MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
return promise.forget();

View File

@ -87,6 +87,7 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
: mPrincipal(aPrincipal)
, mDescriptor(aPrincipal, aScope, aUpdateViaCache)
, mControlledClientsCounter(0)
, mDelayMultiplier(0)
, mUpdateState(NoUpdate)
, mCreationTime(PR_Now())
, mCreationTimeStamp(TimeStamp::Now())
@ -709,5 +710,25 @@ ServiceWorkerRegistrationInfo::Descriptor() const
return mDescriptor;
}
uint32_t
ServiceWorkerRegistrationInfo::GetUpdateDelay()
{
uint32_t delay = Preferences::GetInt("dom.serviceWorkers.update_delay",
1000);
// This can potentially happen if you spam registration->Update(). We don't
// want to wrap to a lower value.
if (mDelayMultiplier >= INT_MAX / (delay ? delay : 1)) {
return INT_MAX;
}
delay *= mDelayMultiplier;
if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
}
return delay;
}
} // namespace dom
} // namespace mozilla

View File

@ -23,6 +23,7 @@ class ServiceWorkerRegistrationInfo final
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
uint32_t mControlledClientsCounter;
uint32_t mDelayMultiplier;
enum
{
@ -94,6 +95,7 @@ public:
StartControllingClient()
{
++mControlledClientsCounter;
mDelayMultiplier = 0;
}
void
@ -211,6 +213,9 @@ public:
const ServiceWorkerRegistrationDescriptor&
Descriptor() const;
uint32_t
GetUpdateDelay();
private:
// Roughly equivalent to [[Update Registration State algorithm]]. Make sure
// this is called *before* updating SW instances' state, otherwise they

View File

@ -232,6 +232,7 @@ support-files =
bug1290951_worker_imported.sjs
sw_storage_not_allow.js
update_worker.sjs
self_update_worker.sjs
[test_bug1151916.html]
[test_bug1240436.html]
@ -348,3 +349,4 @@ tags = openwindow
[test_bad_script_cache.html]
[test_file_upload.html]
support-files = script_file_upload.js sw_file_upload.js server_file_upload.sjs
[test_self_update_worker.html]

View File

@ -0,0 +1,43 @@
/* 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 WORKER_BODY = `
onactivate = function(event) {
let promise = clients.matchAll({includeUncontrolled: true}).then(function(clients) {
for (i = 0; i < clients.length; i++) {
clients[i].postMessage({version: version});
}
}).then(function() {
return self.registration.update();
});
event.waitUntil(promise);
};
`;
function handleRequest(request, response) {
if (request.queryString == 'clearcounter') {
setState('count', "1");
response.write("ok");
return;
}
let count = getState("count");
if (count === "") {
count = 1;
} else {
count = parseInt(count);
}
let worker = "var version = " + count + ";\n";
worker = worker + WORKER_BODY;
// This header is necessary for making this script able to be loaded.
response.setHeader("Content-Type", "application/javascript");
// If this is the first request, return the first source.
response.write(worker);
setState("count", "" + (count + 1));
}

View File

@ -0,0 +1,79 @@
<!DOCTYPE HTML>
<html>
<!--
Test that a self updating service worker can't keep running forever when the
script changes.
-->
<head>
<title>Test for Bug 1432846</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/SpawnTask.js"></script>
<script src="error_reporting_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1432846">Mozilla Bug 1432846</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script src="utils.js"></script>
<script class="testbody" type="text/javascript">
add_task(function setupPrefs() {
return SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
]});
});
function activateDummyWorker() {
return navigator.serviceWorker.register("empty.js",
{ scope: "./empty?random=" + Date.now() })
.then(function(registration) {
var worker = registration.installing;
return waitForState(worker, 'activated', registration).then(function() {
ok(true, "got dummy!");
return registration.unregister();
});
});
}
add_task(async function test_update() {
navigator.serviceWorker.onmessage = function(event) {
ok (event.data.version < 3, "Service worker updated too many times." + event.data.version);
}
await SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.idle_timeout", 0],
["dom.serviceWorkers.update_delay", 30000],
["dom.serviceWorkers.idle_extended_timeout", 299999]]});
// clear version counter
await fetch("self_update_worker.sjs?clearcounter");
var worker;
let registration = await navigator.serviceWorker.register(
"self_update_worker.sjs",
{ scope: "./test_self_update_worker.html?random=" + Date.now()})
.then(function(registration) {
worker = registration.installing;
return waitForState(worker, 'activated', registration);
});
// We need to wait a reasonable time to give the self updating worker a chance
// to change to a newer version. Register and activate an empty worker 5 times.
for (i = 0; i < 5; i++) {
await activateDummyWorker();
}
await registration.unregister();
await SpecialPowers.popPrefEnv();
await SpecialPowers.popPrefEnv();
});
</script>
</body>
</html>

View File

@ -19,7 +19,7 @@ There's only a simple example function that tries to register and sign right now
configure with the `RUST_LOG` environment variable:
```
cargo build
cargo build --example main
RUST_LOG=debug cargo run --example main
```
@ -45,6 +45,6 @@ To fuzz, you will need cargo-fuzz (the latest version from GitHub) as well as Ru
rustup install nightly
cargo install --git https://github.com/rust-fuzz/cargo-fuzz/
rustup run nightly cargo fuzz run u2f_read -- -max_len=512
rustup run nightly cargo fuzz run u2f_read_write -- -max_len=512
cargo +nightly fuzz run u2f_read -- -max_len=512
cargo +nightly fuzz run u2f_read_write -- -max_len=512
```

View File

@ -11,8 +11,17 @@ use std::io;
use std::sync::mpsc::channel;
use u2fhid::{AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, U2FManager};
extern crate log;
extern crate env_logger;
extern crate log;
macro_rules! try_or {
($val:expr, $or:expr) => {
match $val {
Ok(v) => { v }
Err(e) => { return $or(e); }
}
}
}
fn u2f_get_key_handle_from_register_response(register_response: &Vec<u8>) -> io::Result<Vec<u8>> {
if register_response[0] != 0x05 {
@ -34,8 +43,7 @@ fn main() {
env_logger::init().expect("Cannot start logger");
println!("Asking a security key to register now...");
let challenge_str =
format!("{}{}",
let challenge_str = format!("{}{}",
r#"{"challenge": "1vQ9mxionq0ngCnjD-wTsv1zUSrGRtFqG2xP09SbZ70","#,
r#" "version": "U2F_V2", "appId": "http://demo.yubico.com"}"#);
let mut challenge = Sha256::new();
@ -59,13 +67,14 @@ fn main() {
chall_bytes.clone(),
app_bytes.clone(),
vec![],
move |rv| { tx.send(rv.unwrap()).unwrap(); },
move |rv| {
tx.send(rv.unwrap()).unwrap();
},
)
.unwrap();
let register_data = try_or!(rx.recv(), |_| {
panic!("Problem receiving, unable to continue");
return;
});
println!("Register result: {}", base64::encode(&register_data));
println!("Asking a security key to sign now, with the data from the register...");
@ -84,13 +93,16 @@ fn main() {
chall_bytes,
vec![app_bytes],
vec![key_handle],
move |rv| { tx.send(rv.unwrap()).unwrap(); },
move |rv| {
tx.send(rv.unwrap()).unwrap();
},
)
.unwrap();
let (_, sign_data) = try_or!(rx.recv(), |_| {
let (_, handle_used, sign_data) = try_or!(rx.recv(), |_| {
println!("Problem receiving");
});
println!("Sign result: {}", base64::encode(&sign_data));
println!("Key handle used: {}", base64::encode(&handle_used));
println!("Done.");
}

View File

@ -53,7 +53,7 @@ pub unsafe extern "C" fn rust_u2f_app_ids_new() -> *mut U2FAppIds {
pub unsafe extern "C" fn rust_u2f_app_ids_add(
ids: *mut U2FAppIds,
id_ptr: *const u8,
id_len: usize
id_len: usize,
) {
(*ids).push(from_raw(id_ptr, id_len));
}
@ -177,7 +177,11 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(
},
);
if res.is_ok() { tid } else { 0 }
if res.is_ok() {
tid
} else {
0
}
}
#[no_mangle]
@ -211,26 +215,23 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
let key_handles = (*khs).clone();
let tid = new_tid();
let res = (*mgr).sign(
flags,
timeout,
challenge,
app_ids,
key_handles,
move |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());
};
},
);
let res = (*mgr).sign(flags, timeout, challenge, app_ids, key_handles, move |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());
};
});
if res.is_ok() { tid } else { 0 }
if res.is_ok() {
tid
} else {
0
}
}
#[no_mangle]

View File

@ -27,11 +27,11 @@ pub mod platform;
#[path = "stub/mod.rs"]
pub mod platform;
extern crate boxfnonce;
extern crate libc;
#[macro_use]
extern crate log;
extern crate rand;
extern crate libc;
extern crate boxfnonce;
extern crate runloop;
#[macro_use]

View File

@ -8,7 +8,7 @@ use std::io;
use std::mem;
use std::os::unix::io::RawFd;
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use consts::{FIDO_USAGE_U2FHID, FIDO_USAGE_PAGE};
use util::{from_unix_result, io_err};
#[allow(non_camel_case_types)]

View File

@ -89,9 +89,10 @@ where
}
fn process_event(&mut self, event: libudev::Event) {
let path = event.device().devnode().map(
|dn| dn.to_owned().into_os_string(),
);
let path = event
.device()
.devnode()
.map(|dn| dn.to_owned().into_os_string());
match (event.event_type(), path) {
(EventType::Add, Some(path)) => {
@ -108,8 +109,10 @@ where
let f = self.new_device_cb.clone();
let key = path.clone();
let runloop = RunLoop::new(move |alive| if alive() {
f(path, alive);
let runloop = RunLoop::new(move |alive| {
if alive() {
f(path, alive);
}
});
if let Ok(runloop) = runloop {

View File

@ -33,7 +33,9 @@ impl Transaction {
timeout,
)?;
Ok(Self { thread: Some(thread) })
Ok(Self {
thread: Some(thread),
})
}
pub fn cancel(&mut self) {

View File

@ -7,7 +7,7 @@
extern crate core_foundation_sys;
extern crate libc;
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use consts::{FIDO_USAGE_U2FHID, FIDO_USAGE_PAGE};
use core_foundation_sys::base::*;
use core_foundation_sys::dictionary::*;
use core_foundation_sys::number::*;
@ -23,19 +23,23 @@ pub type IOReturn = libc::c_int;
pub type IOHIDManagerRef = *mut __IOHIDManager;
pub type IOHIDManagerOptions = IOOptionBits;
pub type IOHIDDeviceCallback = extern "C" fn(context: *mut c_void,
result: IOReturn,
sender: *mut c_void,
device: IOHIDDeviceRef);
pub type IOHIDDeviceCallback = extern "C" fn(
context: *mut c_void,
result: IOReturn,
sender: *mut c_void,
device: IOHIDDeviceRef,
);
pub type IOHIDReportType = IOOptionBits;
pub type IOHIDReportCallback = extern "C" fn(context: *mut c_void,
result: IOReturn,
sender: IOHIDDeviceRef,
report_type: IOHIDReportType,
report_id: u32,
report: *mut u8,
report_len: CFIndex);
pub type IOHIDReportCallback = extern "C" fn(
context: *mut c_void,
result: IOReturn,
sender: IOHIDDeviceRef,
report_type: IOHIDReportType,
report_id: u32,
report: *mut u8,
report_len: CFIndex,
);
pub const kIOHIDManagerOptionNone: IOHIDManagerOptions = 0;

View File

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate log;
extern crate libc;
extern crate log;
use core_foundation_sys::base::*;
use core_foundation_sys::runloop::*;

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io;
use std::sync::mpsc::{channel, Sender, RecvTimeoutError};
use std::sync::mpsc::{channel, RecvTimeoutError, Sender};
use std::time::Duration;
use consts::PARAMETER_SIZE;
@ -47,13 +47,13 @@ impl U2FManager {
while alive() {
match rx.recv_timeout(Duration::from_millis(50)) {
Ok(QueueAction::Register {
flags,
timeout,
challenge,
application,
key_handles,
callback,
}) => {
flags,
timeout,
challenge,
application,
key_handles,
callback,
}) => {
// This must not block, otherwise we can't cancel.
sm.register(
flags,
@ -65,22 +65,15 @@ impl U2FManager {
);
}
Ok(QueueAction::Sign {
flags,
timeout,
challenge,
app_ids,
key_handles,
callback,
}) => {
flags,
timeout,
challenge,
app_ids,
key_handles,
callback,
}) => {
// This must not block, otherwise we can't cancel.
sm.sign(
flags,
timeout,
challenge,
app_ids,
key_handles,
callback,
);
sm.sign(flags, timeout, challenge, app_ids, key_handles, callback);
}
Ok(QueueAction::Cancel) => {
// Cancelling must block so that we don't start a new
@ -173,12 +166,12 @@ impl U2FManager {
}
for app_id in &app_ids {
if app_id.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid app_id size",
));
}
if app_id.len() != PARAMETER_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid app_id size",
));
}
}
for key_handle in &key_handles {

View File

@ -24,16 +24,16 @@ where
{
// 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<_>>();
// 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);
}
// If there's at least one, stop.
if valid_handles.len() > 0 {
return (app_id, valid_handles);
}
}
return (&app_ids[0], vec![]);
@ -87,20 +87,26 @@ impl StateMachine {
}
// Iterate the exclude list and see if there are any matches.
// Abort the state machine if we found a valid key handle.
if key_handles.iter().any(|key_handle| {
is_valid_transport(key_handle.transports) &&
u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
// If so, we'll keep polling the device anyway to test for user
// consent, to be consistent with CTAP2 device behavior.
let excluded = key_handles.iter().any(|key_handle| {
is_valid_transport(key_handle.transports)
&& u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
.unwrap_or(false) /* no match on failure */
})
{
return;
}
});
while alive() {
if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
callback.call(Ok(bytes));
break;
if excluded {
let blank = vec![0u8; PARAMETER_SIZE];
if let Ok(_) = u2f_register(dev, &blank, &blank) {
callback.call(Err(io_err("duplicate registration")));
break;
}
} else {
if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
callback.call(Ok(bytes));
break;
}
}
// Sleep a bit before trying again.
@ -108,9 +114,9 @@ impl StateMachine {
}
});
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
self.transaction = Some(try_or!(transaction, |_| cbc.call(Err(io_err(
"couldn't create transaction"
)))));
}
pub fn sign(
@ -153,18 +159,17 @@ impl StateMachine {
// 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 */
});
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(
::AuthenticatorTransports::empty(),
|t, k| t | k.transports,
);
let transports = key_handles
.iter()
.fold(::AuthenticatorTransports::empty(), |t, k| {
t | k.transports
});
// We currently only support USB. If the RP specifies transports
// and doesn't include USB it's probably lying.
@ -184,16 +189,13 @@ impl StateMachine {
} else {
// Otherwise, try to sign.
for key_handle in &valid_handles {
if let Ok(bytes) = u2f_sign(
dev,
&challenge,
app_id,
&key_handle.credential,
)
if let Ok(bytes) = u2f_sign(dev, &challenge, app_id, &key_handle.credential)
{
callback.call(Ok((app_id.clone(),
key_handle.credential.clone(),
bytes)));
callback.call(Ok((
app_id.clone(),
key_handle.credential.clone(),
bytes,
)));
break;
}
}
@ -204,9 +206,9 @@ impl StateMachine {
}
});
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
self.transaction = Some(try_or!(transaction, |_| cbc.call(Err(io_err(
"couldn't create transaction"
)))));
}
// This blocks.

View File

@ -235,8 +235,8 @@ where
mod tests {
use rand::{thread_rng, Rng};
use super::{U2FDevice, init_device, ping_device, sendrecv, send_apdu};
use consts::{CID_BROADCAST, U2FHID_INIT, U2FHID_PING, U2FHID_MSG, SW_NO_ERROR};
use super::{init_device, ping_device, send_apdu, sendrecv, U2FDevice};
use consts::{U2FHID_INIT, U2FHID_MSG, U2FHID_PING, CID_BROADCAST, SW_NO_ERROR};
mod platform {
use std::io;
@ -358,21 +358,21 @@ mod tests {
// init packet
let mut msg = cid.to_vec();
msg.extend(vec![U2FHID_PING, 0x00, 0xe4]); // cmd + length = 228
// write msg, append [1u8; 57], 171 bytes remaining
// write msg, append [1u8; 57], 171 bytes remain
device.add_write(&msg, 1);
device.add_read(&msg, 1);
// cont packet
let mut msg = cid.to_vec();
msg.push(0x00); // seq = 0
// write msg, append [1u8; 59], 112 bytes remaining
// write msg, append [1u8; 59], 112 bytes remaining
device.add_write(&msg, 1);
device.add_read(&msg, 1);
// cont packet
let mut msg = cid.to_vec();
msg.push(0x01); // seq = 1
// write msg, append [1u8; 59], 53 bytes remaining
// write msg, append [1u8; 59], 53 bytes remaining
device.add_write(&msg, 1);
device.add_read(&msg, 1);
@ -400,7 +400,7 @@ mod tests {
let mut msg = cid.to_vec();
// sendrecv header
msg.extend(vec![U2FHID_MSG, 0x00, 0x0e]); // len = 14
// apdu header
// apdu header
msg.extend(vec![0x00, U2FHID_PING, 0xaa, 0x00, 0x00, 0x00, 0x05]);
// apdu data
msg.extend_from_slice(&data);

View File

@ -135,7 +135,6 @@ impl U2FHIDCont {
}
}
// Reply sent after initialization command. Contains information about U2F USB
// Key versioning, as well as the communication channel to be used for all
// further requests.

View File

@ -64,7 +64,9 @@ impl<T> OnceCallback<T> {
F: Send + 'static,
{
let cb = Some(SendBoxFnOnce::from(cb));
Self { callback: Arc::new(Mutex::new(cb)) }
Self {
callback: Arc::new(Mutex::new(cb)),
}
}
pub fn call(&self, rv: io::Result<T>) {
@ -78,6 +80,8 @@ impl<T> OnceCallback<T> {
impl<T> Clone for OnceCallback<T> {
fn clone(&self) -> Self {
Self { callback: self.callback.clone() }
Self {
callback: self.callback.clone(),
}
}
}

View File

@ -7,7 +7,7 @@ use std::io;
use std::io::{Read, Write};
use std::os::windows::io::AsRawHandle;
use consts::{CID_BROADCAST, HID_RPT_SIZE, FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
use consts::{FIDO_USAGE_U2FHID, CID_BROADCAST, FIDO_USAGE_PAGE, HID_RPT_SIZE};
use super::winapi::DeviceCapabilities;
use u2ftypes::U2FDevice;

View File

@ -65,8 +65,10 @@ where
let path = path.clone();
let key = path.clone();
let runloop = RunLoop::new(move |alive| if alive() {
f(path, alive);
let runloop = RunLoop::new(move |alive| {
if alive() {
f(path, alive);
}
});
if let Ok(runloop) = runloop {

View File

@ -32,7 +32,9 @@ impl Transaction {
timeout,
)?;
Ok(Self { thread: Some(thread) })
Ok(Self {
thread: Some(thread),
})
}
pub fn cancel(&mut self) {

View File

@ -722,6 +722,7 @@ protected:
virtual ~gfxPlatform();
virtual void InitAcceleration();
virtual void InitWebRenderConfig();
/**
* Called immediately before deleting the gfxPlatform object.
@ -839,7 +840,6 @@ private:
void InitCompositorAccelerationPrefs();
void InitGPUProcessPrefs();
void InitWebRenderConfig();
void InitOMTPConfig();
static bool IsDXInterop2Blocked();

View File

@ -370,6 +370,16 @@ gfxWindowsPlatform::InitAcceleration()
UpdateCanUseHardwareVideoDecoding();
}
void
gfxWindowsPlatform::InitWebRenderConfig()
{
gfxPlatform::InitWebRenderConfig();
if (gfxVars::UseWebRender()) {
UpdateBackendPrefs();
}
}
bool
gfxWindowsPlatform::CanUseHardwareVideoDecoding()
{
@ -436,7 +446,8 @@ gfxWindowsPlatform::UpdateBackendPrefs()
uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) |
BackendTypeBit(BackendType::SKIA);
BackendType defaultBackend = BackendType::SKIA;
if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::HasD2D1Device()) {
if (gfxConfig::IsEnabled(Feature::DIRECT2D) &&
Factory::HasD2D1Device() && !gfxVars::UseWebRender()) {
contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
defaultBackend = BackendType::DIRECT2D1_1;

View File

@ -237,6 +237,7 @@ protected:
private:
void Init();
void InitAcceleration() override;
void InitWebRenderConfig() override;
void InitializeDevices();
void InitializeD3D11();

View File

@ -2835,7 +2835,7 @@ void
CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton* lir)
{
pushArg(ToRegister(lir->environmentChain()));
pushArg(ImmGCPtr(lir->mir()->info().fun));
pushArg(ImmGCPtr(lir->mir()->info().funUnsafe()));
callVM(LambdaInfo, lir);
}
@ -2847,18 +2847,16 @@ CodeGenerator::visitLambda(LLambda* lir)
Register tempReg = ToRegister(lir->temp());
const LambdaFunctionInfo& info = lir->mir()->info();
OutOfLineCode* ool = oolCallVM(LambdaInfo, lir, ArgList(ImmGCPtr(info.fun), envChain),
OutOfLineCode* ool = oolCallVM(LambdaInfo, lir, ArgList(ImmGCPtr(info.funUnsafe()), envChain),
StoreRegisterTo(output));
MOZ_ASSERT(!info.singletonType);
masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
masm.createGCObject(output, tempReg, info.funUnsafe(), gc::DefaultHeap, ool->entry());
emitLambdaInit(output, envChain, info);
if (info.flags & JSFunction::EXTENDED) {
MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin() ||
info.fun->isAsync());
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
@ -2908,7 +2906,7 @@ CodeGenerator::visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool)
pushArg(newTarget);
pushArg(envChain);
pushArg(ImmGCPtr(info.fun));
pushArg(ImmGCPtr(info.funUnsafe()));
callVM(LambdaArrowInfo, ool->lir);
StoreRegisterTo(output).generate(this);
@ -2945,7 +2943,7 @@ CodeGenerator::visitLambdaArrow(LLambdaArrow* lir)
Register tempReg = newTarget.scratchReg();
masm.push(newTarget.scratchReg());
masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
masm.createGCObject(output, tempReg, info.funUnsafe(), gc::DefaultHeap, ool->entry());
masm.pop(newTarget.scratchReg());
@ -2986,7 +2984,8 @@ CodeGenerator::emitLambdaInit(Register output, Register envChain,
masm.storePtr(envChain, Address(output, JSFunction::offsetOfEnvironment()));
// No post barrier needed because output is guaranteed to be allocated in
// the nursery.
masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
masm.storePtr(ImmGCPtr(info.funUnsafe()->displayAtom()),
Address(output, JSFunction::offsetOfAtom()));
}
typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);

View File

@ -734,7 +734,6 @@ class LNode
// Returns information about operands.
virtual LAllocation* getOperand(size_t index) = 0;
virtual void setOperand(size_t index, const LAllocation& a) = 0;
bool isCall() const {
return isCall_;
@ -830,6 +829,9 @@ class LInstruction
void setDef(size_t index, const LDefinition& def) {
*getDef(index) = def;
}
void setOperand(size_t index, const LAllocation& a) {
*getOperand(index) = a;
}
// Returns information about temporary registers needed. Each temporary
// register is an LDefinition with a fixed or virtual register and
@ -969,7 +971,7 @@ class LPhi final : public LNode
MOZ_ASSERT(index < numOperands());
return &inputs_[index];
}
void setOperand(size_t index, const LAllocation& a) override {
void setOperand(size_t index, const LAllocation& a) {
MOZ_ASSERT(index < numOperands());
inputs_[index] = a;
}
@ -1166,7 +1168,7 @@ class LInstructionHelper : public details::LInstructionFixedDefsTempsHelper<Defs
LAllocation* getOperand(size_t index) final {
return &operands_[index];
}
void setOperand(size_t index, const LAllocation& a) final {
void setOperand(size_t index, const LAllocation& a) {
operands_[index] = a;
}
void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
@ -1212,7 +1214,7 @@ class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<De
LAllocation* getOperand(size_t index) final {
return &operands_[index];
}
void setOperand(size_t index, const LAllocation& a) final {
void setOperand(size_t index, const LAllocation& a) {
operands_[index] = a;
}
void setBoxOperand(size_t index, const LBoxAllocation& a) {

View File

@ -8798,7 +8798,10 @@ struct LambdaFunctionInfo
// The functions used in lambdas are the canonical original function in
// the script, and are immutable except for delazification. Record this
// information while still on the active thread to avoid races.
CompilerFunction fun;
private:
CompilerFunction fun_;
public:
uint16_t flags;
uint16_t nargs;
gc::Cell* scriptOrLazyScript;
@ -8806,20 +8809,37 @@ struct LambdaFunctionInfo
bool useSingletonForClone;
explicit LambdaFunctionInfo(JSFunction* fun)
: fun(fun), flags(fun->flags()), nargs(fun->nargs()),
: fun_(fun), flags(fun->flags()), nargs(fun->nargs()),
scriptOrLazyScript(fun->hasScript()
? (gc::Cell*) fun->nonLazyScript()
: (gc::Cell*) fun->lazyScript()),
singletonType(fun->isSingleton()),
useSingletonForClone(ObjectGroup::useSingletonForClone(fun))
{}
{
// If this assert fails, make sure CodeGenerator::visitLambda does the
// right thing. We can't assert this off-thread in CodeGenerator,
// because fun->isAsync() accesses the script/lazyScript and can race
// with delazification on the main thread.
MOZ_ASSERT_IF(flags & JSFunction::EXTENDED,
fun->isArrow() ||
fun->allowSuperProperty() ||
fun->isSelfHostedBuiltin() ||
fun->isAsync());
}
// Be careful when calling this off-thread. Don't call any JSFunction*
// methods that depend on script/lazyScript - this can race with
// delazification on the main thread.
JSFunction* funUnsafe() const {
return fun_;
}
bool appendRoots(MRootList& roots) const {
if (!roots.append(fun))
if (!roots.append(fun_))
return false;
if (fun->hasScript())
return roots.append(fun->nonLazyScript());
return roots.append(fun->lazyScript());
if (fun_->hasScript())
return roots.append(fun_->nonLazyScript());
return roots.append(fun_->lazyScript());
}
private:
@ -8839,8 +8859,9 @@ class MLambda
info_(&cst->toObject().as<JSFunction>())
{
setResultType(MIRType::Object);
if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
JSFunction* fun = info().funUnsafe();
if (!fun->isSingleton() && !ObjectGroup::useSingletonForClone(fun))
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, fun));
}
public:
@ -8875,9 +8896,10 @@ class MLambdaArrow
info_(&cst->toObject().as<JSFunction>())
{
setResultType(MIRType::Object);
MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun));
if (!info().fun->isSingleton())
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
JSFunction* fun = info().funUnsafe();
MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
if (!fun->isSingleton())
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, fun));
}
public:

View File

@ -356,12 +356,9 @@ class RegisterAllocator
public:
template<typename TakeableSet>
static void takeWasmRegisters(TakeableSet& regs) {
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
regs.take(HeapReg);
#elif defined(JS_CODEGEN_ARM64)
regs.take(HeapReg);
regs.take(HeapLenReg);
#endif
regs.take(FramePointer);
}

View File

@ -264,7 +264,7 @@ RValueAllocation::layoutFromMode(Mode mode)
}
}
MOZ_CRASH("Wrong mode type?");
MOZ_CRASH_UNSAFE_PRINTF("Unexpected mode: 0x%x", mode);
}
// Pad serialized RValueAllocations by a multiple of X bytes in the allocation

View File

@ -303,6 +303,9 @@ class RValueAllocation
void write(CompactBufferWriter& writer) const;
public:
bool valid() const {
return mode_ != INVALID;
}
Mode mode() const {
return Mode(mode_ & MODE_BITS_MASK);
}

View File

@ -71,5 +71,11 @@ FloatRegister::getRegisterDumpOffsetInBytes()
return encoding() * sizeof(double);
}
uint32_t
GetARM64Flags()
{
return 0;
}
} // namespace jit
} // namespace js

View File

@ -491,6 +491,8 @@ hasMultiAlias()
return false;
}
uint32_t GetARM64Flags();
} // namespace jit
} // namespace js

View File

@ -87,7 +87,6 @@ static constexpr Register IntArgReg5 { Registers::x5 };
static constexpr Register IntArgReg6 { Registers::x6 };
static constexpr Register IntArgReg7 { Registers::x7 };
static constexpr Register HeapReg { Registers::x21 };
static constexpr Register HeapLenReg { Registers::x22 };
// Define unsized Registers.
#define DEFINE_UNSIZED_REGISTERS(N) \

View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2018 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jit/Disassembler.h"
MOZ_COLD uint8_t*
js::jit::Disassembler::DisassembleHeapAccess(uint8_t*, js::jit::Disassembler::HeapAccess*)
{
MOZ_CRASH("NYI - asm.js not supported yet on this platform");
}

View File

@ -2027,29 +2027,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
store32(scratch32.asUnsized(), addr);
}
void BoundsCheck(Register ptrReg, Label* onFail, vixl::CPURegister zeroMe = vixl::NoReg) {
// use tst rather than Tst to *ensure* that a single instrution is generated.
Cmp(ARMRegister(ptrReg, 32), ARMRegister(HeapLenReg, 32));
if (!zeroMe.IsNone()) {
if (zeroMe.IsRegister()) {
Csel(ARMRegister(zeroMe),
ARMRegister(zeroMe),
Operand(zeroMe.Is32Bits() ? vixl::wzr : vixl::xzr),
Assembler::Below);
} else if (zeroMe.Is32Bits()) {
vixl::UseScratchRegisterScope temps(this);
const ARMFPRegister scratchFloat = temps.AcquireS();
Fmov(scratchFloat, JS::GenericNaN());
Fcsel(ARMFPRegister(zeroMe), ARMFPRegister(zeroMe), scratchFloat, Assembler::Below);
} else {
vixl::UseScratchRegisterScope temps(this);
const ARMFPRegister scratchDouble = temps.AcquireD();
Fmov(scratchDouble, JS::GenericNaN());
Fcsel(ARMFPRegister(zeroMe), ARMFPRegister(zeroMe), scratchDouble, Assembler::Below);
}
}
B(onFail, Assembler::AboveOrEqual);
}
void breakpoint();
// Emits a simulator directive to save the current sp on an internal stack.

View File

@ -452,13 +452,15 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir,
JSValueType valueType =
(type == MIRType::ObjectOrNull) ? JSVAL_TYPE_OBJECT : ValueTypeFromMIRType(type);
MOZ_ASSERT(payload->isMemory() || payload->isRegister());
MOZ_DIAGNOSTIC_ASSERT(payload->isMemory() || payload->isRegister());
if (payload->isMemory())
alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
else if (payload->isGeneralReg())
alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
else if (payload->isFloatReg())
alloc = RValueAllocation::Double(ToFloatRegister(payload));
else
MOZ_CRASH("Unexpected payload type.");
break;
}
case MIRType::Float32:
@ -541,6 +543,7 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir,
break;
}
}
MOZ_DIAGNOSTIC_ASSERT(alloc.valid());
// This set an extra bit as part of the RValueAllocation, such that we know
// that recover instruction have to be executed without wrapping the

View File

@ -6939,7 +6939,7 @@ class LGetPropertyPolymorphicV : public LInstructionHelper<BOX_PIECES, 1, 0>
const MGetPropertyPolymorphic* mir() const {
return mir_->toGetPropertyPolymorphic();
}
virtual const char* extraName() const {
const char* extraName() const {
return PropertyNameToExtraName(mir()->name());
}
};
@ -6964,7 +6964,7 @@ class LGetPropertyPolymorphicT : public LInstructionHelper<1, 1, 1>
const MGetPropertyPolymorphic* mir() const {
return mir_->toGetPropertyPolymorphic();
}
virtual const char* extraName() const {
const char* extraName() const {
return PropertyNameToExtraName(mir()->name());
}
};

View File

@ -3685,9 +3685,9 @@ class AssemblerX86Shared : public AssemblerShared
static void PatchDataWithValueCheck(CodeLocationLabel data, PatchedImmPtr newData,
PatchedImmPtr expectedData) {
// The pointer given is a pointer to *after* the data.
uintptr_t* ptr = ((uintptr_t*) data.raw()) - 1;
MOZ_ASSERT(*ptr == (uintptr_t)expectedData.value);
*ptr = (uintptr_t)newData.value;
uint8_t* ptr = data.raw() - sizeof(uintptr_t);
MOZ_ASSERT(mozilla::LittleEndian::readUintptr(ptr) == uintptr_t(expectedData.value));
mozilla::LittleEndian::writeUintptr(ptr, uintptr_t(newData.value));
}
static void PatchDataWithValueCheck(CodeLocationLabel data, ImmPtr newData, ImmPtr expectedData) {
PatchDataWithValueCheck(data, PatchedImmPtr(newData.value), PatchedImmPtr(expectedData.value));

View File

@ -519,6 +519,7 @@ elif CONFIG['JS_CODEGEN_ARM64']:
'jit/arm64/Bailouts-arm64.cpp',
'jit/arm64/BaselineIC-arm64.cpp',
'jit/arm64/CodeGenerator-arm64.cpp',
'jit/arm64/Disassembler-arm64.cpp',
'jit/arm64/Lowering-arm64.cpp',
'jit/arm64/MacroAssembler-arm64.cpp',
'jit/arm64/MoveEmitter-arm64.cpp',
@ -652,11 +653,13 @@ if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_WASM_SATURATING_TRUNC_OPS'] = True
DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
# An experiment we want to run on Nightly: Can we change the JS
# representation of an exported global from the global's value
# to an instance of WebAssembly.Global without breaking existing
# wasm content?
DEFINES['ENABLE_WASM_GLOBAL'] = True
# An experiment we want to run on Nightly and early Beta: Can we change the JS
# representation of an exported global from the global's value to an instance
# of WebAssembly.Global without breaking existing wasm content?
#
# Additionally guarded by EARLY_BETA_OR_EARLIER in the code.
DEFINES['ENABLE_WASM_GLOBAL'] = True
if CONFIG['JS_BUILD_BINAST']:
# Using SOURCES as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.
@ -679,7 +682,7 @@ if CONFIG['JS_BUILD_BINAST']:
# so that it is easy to use the huge-mapping optimization for other
# 64-bit platforms in the future.
if CONFIG['JS_CODEGEN_X64']:
if CONFIG['JS_CODEGEN_X64'] or CONFIG['JS_CODEGEN_ARM64']:
DEFINES['WASM_HUGE_MEMORY'] = True
if CONFIG['MOZ_DEBUG'] or CONFIG['NIGHTLY_BUILD']:

View File

@ -1551,6 +1551,7 @@ dnl ========================================================
MOZ_ARG_HEADER(Misc. Options)
if test -z "$SKIP_COMPILER_CHECKS"; then
dnl ========================================================
dnl =
dnl = Compiler Options
@ -1825,6 +1826,19 @@ if test -n "$MOZ_DEV_EDITION"; then
AC_DEFINE(MOZ_DEV_EDITION)
fi
# Allow influencing configure with a defines.sh script.
. "${srcdir}/build/defines.sh"
# If we're not building a release build, define EARLY_BETA_OR_EARLIER if it is
# set in defines.sh
if test "$BUILDING_RELEASE"; then
# Override value in defines.sh, if any
EARLY_BETA_OR_EARLIER=
elif test "$EARLY_BETA_OR_EARLIER"; then
AC_DEFINE(EARLY_BETA_OR_EARLIER)
fi
AC_SUBST(EARLY_BETA_OR_EARLIER)
dnl ========================================================
dnl JavaScript shell
dnl ========================================================

View File

@ -111,7 +111,9 @@ JSScript::ensureNonLazyCanonicalFunction()
inline JSFunction*
JSScript::getFunction(size_t index)
{
JSFunction* fun = &getObject(index)->as<JSFunction>();
JSObject* obj = getObject(index);
MOZ_RELEASE_ASSERT(obj->is<JSFunction>(), "Script object is not JSFunction");
JSFunction* fun = &obj->as<JSFunction>();
MOZ_ASSERT_IF(fun->isNative(), IsAsmJSModuleNative(fun->native()));
return fun;
}
@ -119,13 +121,17 @@ JSScript::getFunction(size_t index)
inline js::RegExpObject*
JSScript::getRegExp(size_t index)
{
return &getObject(index)->as<js::RegExpObject>();
JSObject* obj = getObject(index);
MOZ_RELEASE_ASSERT(obj->is<js::RegExpObject>(), "Script object is not RegExpObject");
return &obj->as<js::RegExpObject>();
}
inline js::RegExpObject*
JSScript::getRegExp(jsbytecode* pc)
{
return &getObject(pc)->as<js::RegExpObject>();
JSObject* obj = getObject(pc);
MOZ_RELEASE_ASSERT(obj->is<js::RegExpObject>(), "Script object is not RegExpObject");
return &obj->as<js::RegExpObject>();
}
inline js::GlobalObject&

View File

@ -2375,12 +2375,14 @@ JSStructuredCloneReader::readHeader()
return in.reportTruncated();
if (tag != SCTAG_HEADER) {
// Old structured clone buffer. We must have read it from disk or
// somewhere, so we can assume it's scope-compatible.
// Old structured clone buffer. We must have read it from disk.
storedScope = JS::StructuredCloneScope::DifferentProcess;
return true;
}
MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
storedScope = JS::StructuredCloneScope(data);
if (data != uint32_t(JS::StructuredCloneScope::SameProcessSameThread) &&
data != uint32_t(JS::StructuredCloneScope::SameProcessDifferentThread) &&
data != uint32_t(JS::StructuredCloneScope::DifferentProcess))
@ -2389,7 +2391,6 @@ JSStructuredCloneReader::readHeader()
"invalid structured clone scope");
return false;
}
storedScope = JS::StructuredCloneScope(data);
if (storedScope < allowedScope) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
"incompatible structured clone scope");

View File

@ -253,7 +253,7 @@ GetImports(JSContext* cx,
MOZ_ASSERT(global.importIndex() == globalIndex - 1);
MOZ_ASSERT(!global.isMutable());
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
if (v.isObject() && v.toObject().is<WasmGlobalObject>())
v.set(v.toObject().as<WasmGlobalObject>().value());
#endif
@ -1935,7 +1935,7 @@ WasmTableObject::table() const
// ============================================================================
// WebAssembly.global class and methods
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
const ClassOps WasmGlobalObject::classOps_ =
{
@ -2128,7 +2128,7 @@ WasmGlobalObject::value() const
return getReservedSlot(VALUE_SLOT);
}
#endif // ENABLE_WASM_GLOBAL
#endif // ENABLE_WASM_GLOBAL && EARLY_BETA_OR_EARLIER
// ============================================================================
// WebAssembly class and static methods
@ -2968,7 +2968,7 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
return nullptr;
RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
RootedObject globalProto(cx);
#endif
if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
@ -2979,7 +2979,7 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
return nullptr;
if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
return nullptr;
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
if (!InitConstructor<WasmGlobalObject>(cx, wasm, "Global", &globalProto))
return nullptr;
#endif
@ -3002,7 +3002,7 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
global->setPrototype(JSProto_WasmGlobal, ObjectValue(*globalProto));
#endif
global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));

View File

@ -285,7 +285,7 @@ class WasmTableObject : public NativeObject
wasm::Table& table() const;
};
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
// The class of WebAssembly.Global. A WasmGlobalObject holds either the value
// of an immutable wasm global or the cell of a mutable wasm global.
@ -319,7 +319,7 @@ class WasmGlobalObject : public NativeObject
Value value() const;
};
#endif // ENABLE_WASM_GLOBAL
#endif // ENABLE_WASM_GLOBAL && EARLY_BETA_OR_EARLIER
} // namespace js

View File

@ -1016,7 +1016,7 @@ GetGlobalExport(JSContext* cx, const GlobalDescVector& globals, uint32_t globalI
ToJSValue(val, jsval);
#ifdef ENABLE_WASM_GLOBAL
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
Rooted<WasmGlobalObject*> go(cx, WasmGlobalObject::create(cx, ValType::I32, false, jsval));
if (!go)
return false;

View File

@ -625,6 +625,7 @@ StoreValueFromGPImm(SharedMem<void*> addr, size_t size, int32_t imm)
AtomicOperations::memcpySafeWhenRacy(addr, static_cast<void*>(&imm), size);
}
#if defined(JS_CODEGEN_X64)
# if !defined(XP_DARWIN)
MOZ_COLD static void*
AddressOfFPRegisterSlot(CONTEXT* context, FloatRegisters::Encoding encoding)
@ -726,6 +727,19 @@ AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
MOZ_CRASH();
}
# endif // !XP_DARWIN
#elif defined(JS_CODEGEN_ARM64)
MOZ_COLD static void*
AddressOfFPRegisterSlot(EMULATOR_CONTEXT* context, FloatRegisters::Encoding encoding)
{
MOZ_CRASH("NYI - asm.js not supported yet on this platform");
}
MOZ_COLD static void*
AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
{
MOZ_CRASH("NYI - asm.js not supported yet on this platform");
}
#endif
MOZ_COLD static void
SetRegisterToCoercedUndefined(EMULATOR_CONTEXT* context, size_t size,

View File

@ -32,11 +32,18 @@ using namespace js::wasm;
using mozilla::IsPowerOfTwo;
using mozilla::MakeEnumeratedRange;
// A sanity check. We have only tested WASM_HUGE_MEMORY on x64, and only tested
// x64 with WASM_HUGE_MEMORY.
// We have only tested x64 with WASM_HUGE_MEMORY.
#if defined(WASM_HUGE_MEMORY) != defined(JS_CODEGEN_X64)
# error "Not an expected configuration"
#if defined(JS_CODEGEN_X64) && !defined(WASM_HUGE_MEMORY)
# error "Not an expected configuration"
#endif
// We have only tested WASM_HUGE_MEMORY on x64 and arm64.
#if defined(WASM_HUGE_MEMORY)
# if !(defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64))
# error "Not an expected configuration"
# endif
#endif
// Another sanity check.
@ -102,6 +109,7 @@ GetCPUID()
ARM = 0x3,
MIPS = 0x4,
MIPS64 = 0x5,
ARM64 = 0x6,
ARCH_BITS = 3
};
@ -115,7 +123,8 @@ GetCPUID()
MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
return ARM | (jit::GetARMFlags() << ARCH_BITS);
#elif defined(JS_CODEGEN_ARM64)
MOZ_CRASH("not enabled");
MOZ_ASSERT(jit::GetARM64Flags() <= (UINT32_MAX >> ARCH_BITS));
return ARM64 | (jit::GetARM64Flags() << ARCH_BITS);
#elif defined(JS_CODEGEN_MIPS32)
MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
return MIPS | (jit::GetMIPSFlags() << ARCH_BITS);

View File

@ -207,7 +207,7 @@ WhitespaceOnlyChangedOnAppend(const CharT* aBuffer,
size_t aOldLength,
size_t aNewLength)
{
MOZ_ASSERT(aOldLength < aNewLength);
MOZ_ASSERT(aOldLength <= aNewLength);
if (!WhitespaceOnly(aBuffer, aOldLength)) {
// The old text was already not whitespace-only.
return false;

View File

@ -0,0 +1,10 @@
<script>
function go() {
var b = window.getSelection();
var c = document.getSelection();
b.setBaseAndExtent(document.getElementById("a"), 0, document.body.firstChild, 1);
c.deleteFromDocument();
}
</script>
<body onload=go()>
<p id="a">

View File

@ -525,3 +525,4 @@ load 1429961.html
load 1435015.html
load 1429962.html
pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html
load 1442506.html

View File

@ -2911,7 +2911,7 @@ css::URLValueData::GetUTF16String() const
nsDependentCSubstring rust = GetRustString();
nsString converted = NS_ConvertUTF8toUTF16(rust);
Servo_ReleaseArcStringData(&mStrings.mRustString);
mStrings.mString = converted;
new (&mStrings) RustOrGeckoString(converted);
mUsingRustString = false;
}
return mStrings.mString;

View File

@ -134,15 +134,12 @@ MOZ_END_EXTERN_C
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw()
#endif
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS
#elif __cplusplus >= 201103
#else
/*
* C++11 has deprecated exception-specifications in favour of |noexcept|.
*/
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS noexcept(true)
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS noexcept(false)
#else
#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw()
#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS throw(std::bad_alloc)
#endif
#define MOZALLOC_THROW_BAD_ALLOC MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS

View File

@ -9,7 +9,9 @@
/*
* The classes LittleEndian and BigEndian expose static methods for
* reading and writing 16-, 32-, and 64-bit signed and unsigned integers
* in their respective endianness. The naming scheme is:
* in their respective endianness. The addresses read from or written
* to may be misaligned (although misaligned accesses may incur
* architecture-specific performance costs). The naming scheme is:
*
* {Little,Big}Endian::{read,write}{Uint,Int}<bitsize>
*
@ -361,6 +363,12 @@ protected:
return read<uint64_t>(aPtr);
}
/** Read a uintptr_t in ThisEndian endianness from |aPtr| and return it. */
static MOZ_MUST_USE uintptr_t readUintptr(const void* aPtr)
{
return read<uintptr_t>(aPtr);
}
/** Read an int16_t in ThisEndian endianness from |aPtr| and return it. */
static MOZ_MUST_USE int16_t readInt16(const void* aPtr)
{
@ -379,6 +387,12 @@ protected:
return read<int64_t>(aPtr);
}
/** Read an intptr_t in ThisEndian endianness from |aPtr| and return it. */
static MOZ_MUST_USE intptr_t readIntptr(const void* aPtr)
{
return read<intptr_t>(aPtr);
}
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
static void writeUint16(void* aPtr, uint16_t aValue)
{
@ -397,6 +411,12 @@ protected:
write(aPtr, aValue);
}
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
static void writeUintptr(void* aPtr, uintptr_t aValue)
{
write(aPtr, aValue);
}
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
static void writeInt16(void* aPtr, int16_t aValue)
{
@ -415,6 +435,12 @@ protected:
write(aPtr, aValue);
}
/** Write |aValue| to |aPtr| using ThisEndian endianness. */
static void writeIntptr(void* aPtr, intptr_t aValue)
{
write(aPtr, aValue);
}
/*
* Converts a value of type T to little-endian format.
*
@ -630,15 +656,19 @@ public:
using super::readUint16;
using super::readUint32;
using super::readUint64;
using super::readUintptr;
using super::readInt16;
using super::readInt32;
using super::readInt64;
using super::readIntptr;
using super::writeUint16;
using super::writeUint32;
using super::writeUint64;
using super::writeUintptr;
using super::writeInt16;
using super::writeInt32;
using super::writeInt64;
using super::writeIntptr;
};
} /* namespace detail */

View File

@ -325,69 +325,101 @@ int
main()
{
static const uint8_t unsigned_bytes[16] = {
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
};
static const int8_t signed_bytes[16] = {
-0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08,
-0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08
};
static const uint16_t uint16_values[8] = {
0x102, 0x304, 0x506, 0x708, 0x102, 0x304, 0x506, 0x708
0x0102, 0x0304, 0x0506, 0x0708, 0x0102, 0x0304, 0x0506, 0x0708
};
static const int16_t int16_values[8] = {
int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8),
int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8)
};
static const uint32_t uint32_values[4] = {
0x1020304, 0x5060708, 0x1020304, 0x5060708
0x01020304, 0x05060708, 0x01020304, 0x05060708
};
static const int32_t int32_values[4] = {
int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8),
int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8)
};
static const uint64_t uint64_values[2] = {
0x102030405060708, 0x102030405060708
0x0102030405060708, 0x0102030405060708
};
static const int64_t int64_values[2] = {
int64_t(0xf1f2f3f4f5f6f7f8), int64_t(0xf1f2f3f4f5f6f7f8)
};
uint8_t buffer[8];
MOZ_RELEASE_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x201);
MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x102);
MOZ_RELEASE_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x0201);
MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x0102);
MOZ_RELEASE_ASSERT(
LittleEndian::readUint32(&unsigned_bytes[0]) == 0x4030201U);
LittleEndian::readUint32(&unsigned_bytes[0]) == 0x04030201U);
MOZ_RELEASE_ASSERT(
BigEndian::readUint32(&unsigned_bytes[0]) == 0x1020304U);
BigEndian::readUint32(&unsigned_bytes[0]) == 0x01020304U);
MOZ_RELEASE_ASSERT(
LittleEndian::readUint64(&unsigned_bytes[0]) == 0x807060504030201ULL);
LittleEndian::readUint64(&unsigned_bytes[0]) == 0x0807060504030201ULL);
MOZ_RELEASE_ASSERT(
BigEndian::readUint64(&unsigned_bytes[0]) == 0x102030405060708ULL);
BigEndian::readUint64(&unsigned_bytes[0]) == 0x0102030405060708ULL);
LittleEndian::writeUint16(&buffer[0], 0x201);
if (sizeof(uintptr_t) == 8) {
MOZ_RELEASE_ASSERT(
LittleEndian::readUintptr(&unsigned_bytes[0]) == 0x0807060504030201ULL);
MOZ_RELEASE_ASSERT(
BigEndian::readUintptr(&unsigned_bytes[0]) == 0x0102030405060708ULL);
} else {
MOZ_RELEASE_ASSERT(
LittleEndian::readUintptr(&unsigned_bytes[0]) == 0x04030201U);
MOZ_RELEASE_ASSERT(
BigEndian::readUintptr(&unsigned_bytes[0]) == 0x01020304U);
}
LittleEndian::writeUint16(&buffer[0], 0x0201);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0);
BigEndian::writeUint16(&buffer[0], 0x102);
BigEndian::writeUint16(&buffer[0], 0x0102);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0);
LittleEndian::writeUint32(&buffer[0], 0x4030201U);
LittleEndian::writeUint32(&buffer[0], 0x04030201U);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0);
BigEndian::writeUint32(&buffer[0], 0x1020304U);
BigEndian::writeUint32(&buffer[0], 0x01020304U);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0);
LittleEndian::writeUint64(&buffer[0], 0x807060504030201ULL);
LittleEndian::writeUint64(&buffer[0], 0x0807060504030201ULL);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0);
BigEndian::writeUint64(&buffer[0], 0x102030405060708ULL);
BigEndian::writeUint64(&buffer[0], 0x0102030405060708ULL);
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0);
memset(&buffer[0], 0xff, sizeof(buffer));
LittleEndian::writeUintptr(&buffer[0], uintptr_t(0x0807060504030201ULL));
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_t)) == 0);
if (sizeof(uintptr_t) == 4) {
MOZ_RELEASE_ASSERT(
LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
}
memset(&buffer[0], 0xff, sizeof(buffer));
if (sizeof(uintptr_t) == 8) {
BigEndian::writeUintptr(&buffer[0], uintptr_t(0x0102030405060708ULL));
} else {
BigEndian::writeUintptr(&buffer[0], uintptr_t(0x01020304U));
MOZ_RELEASE_ASSERT(
LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
}
MOZ_RELEASE_ASSERT(
memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_t)) == 0);
MOZ_RELEASE_ASSERT(
LittleEndian::readInt16(&signed_bytes[0]) == int16_t(0xf2f1));
MOZ_RELEASE_ASSERT(
@ -403,6 +435,18 @@ main()
MOZ_RELEASE_ASSERT(
BigEndian::readInt64(&signed_bytes[0]) == int64_t(0xf1f2f3f4f5f6f7f8LL));
if (sizeof(uintptr_t) == 8) {
MOZ_RELEASE_ASSERT(
LittleEndian::readIntptr(&signed_bytes[0]) == intptr_t(0xf8f7f6f5f4f3f2f1LL));
MOZ_RELEASE_ASSERT(
BigEndian::readIntptr(&signed_bytes[0]) == intptr_t(0xf1f2f3f4f5f6f7f8LL));
} else {
MOZ_RELEASE_ASSERT(
LittleEndian::readIntptr(&signed_bytes[0]) == intptr_t(0xf4f3f2f1));
MOZ_RELEASE_ASSERT(
BigEndian::readIntptr(&signed_bytes[0]) == intptr_t(0xf1f2f3f4));
}
LittleEndian::writeInt16(&buffer[0], int16_t(0xf2f1));
MOZ_RELEASE_ASSERT(
memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0);
@ -424,6 +468,26 @@ main()
MOZ_RELEASE_ASSERT(
memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0);
memset(&buffer[0], 0xff, sizeof(buffer));
LittleEndian::writeIntptr(&buffer[0], intptr_t(0xf8f7f6f5f4f3f2f1LL));
MOZ_RELEASE_ASSERT(
memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_t)) == 0);
if (sizeof(intptr_t) == 4) {
MOZ_RELEASE_ASSERT(
LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
}
memset(&buffer[0], 0xff, sizeof(buffer));
if (sizeof(intptr_t) == 8) {
BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4f5f6f7f8LL));
} else {
BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4));
MOZ_RELEASE_ASSERT(
LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
}
MOZ_RELEASE_ASSERT(
memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_t)) == 0);
TestSingleSwap(uint16_t(0xf2f1), uint16_t(0xf1f2));
TestSingleSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf1f2f3f4));
TestSingleSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf1f2f3f4f5f6f7f8));

View File

@ -174,6 +174,10 @@ pref("dom.serviceWorkers.idle_timeout", 30000);
// The amount of time (milliseconds) service workers can be kept running using waitUntil promises.
pref("dom.serviceWorkers.idle_extended_timeout", 300000);
// The amount of time (milliseconds) an update request is delayed when triggered
// by a service worker that doesn't control any clients.
pref("dom.serviceWorkers.update_delay", 1000);
// Enable test for 24 hours update, service workers will always treat last update check time is over 24 hours
pref("dom.serviceWorkers.testUpdateOverOneDay", false);

View File

@ -14,3 +14,9 @@ kind-dependencies:
job-template:
shipping-phase: promote
worker-type:
by-project:
mozilla-central: scriptworker-prov-v1/beetmoverworker-v1
mozilla-beta: scriptworker-prov-v1/beetmoverworker-v1
mozilla-release: scriptworker-prov-v1/beetmoverworker-v1
default: scriptworker-prov-v1/beetmoverworker-dev

View File

@ -202,7 +202,7 @@ talos-g4:
default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
max-run-time:
by-test-platform:
linux64.*: 900
linux64.*: 1500
default: 1800
mozharness:
extra-options:
@ -223,7 +223,7 @@ talos-g4-profiling:
- --webServer,localhost
max-run-time:
by-test-platform:
linux64.*: 900
linux64.*: 1500
default: 1800
talos-g5:

View File

@ -368,6 +368,9 @@ def mozharness_test_on_native_engine(config, job, taskdesc):
if test['reboot']:
worker['reboot'] = test['reboot']
if test['max-run-time']:
worker['max-run-time'] = test['max-run-time']
worker['env'] = env = {
'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
'GECKO_HEAD_REV': config.params['head_rev'],

View File

@ -8,7 +8,7 @@ from __future__ import absolute_import, print_function, unicode_literals
from taskgraph.transforms.base import TransformSequence
from taskgraph.util.attributes import copy_attributes_from_dependent_job
from taskgraph.util.schema import validate_schema, Schema
from taskgraph.util.schema import validate_schema, Schema, resolve_keyed_by, optionally_keyed_by
from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
get_beetmover_action_scope,
get_phase)
@ -54,6 +54,7 @@ release_generate_checksums_beetmover_schema = Schema({
Optional('shipping-phase'): task_description_schema['shipping-phase'],
Optional('shipping-product'): task_description_schema['shipping-product'],
Required('worker-type'): optionally_keyed_by('project', basestring),
})
@ -102,10 +103,14 @@ def make_task_description(config, jobs):
action_scope = get_beetmover_action_scope(config)
phase = get_phase(config)
resolve_keyed_by(
job, 'worker-type', item_name=label, project=config.params['project']
)
task = {
'label': label,
'description': description,
'worker-type': 'scriptworker-prov-v1/beetmoverworker-dev',
'worker-type': job['worker-type'],
'scopes': [bucket_scope, action_scope],
'dependencies': dependencies,
'attributes': attributes,

View File

@ -77,8 +77,6 @@ def make_release_generate_checksums_signing_description(config, jobs):
}]
signing_cert_scope = get_signing_cert_scope(config)
# XXX hardcode on maple to get a green graph. DO NOT UPLIFT
signing_cert_scope = 'project:releng:signing:cert:dep-signing'
task = {
'label': label,

View File

@ -449,6 +449,9 @@ task_description_schema = Schema({
Required('implementation'): 'native-engine',
Required('os'): Any('macosx', 'linux'),
# the maximum time to run, in seconds
Required('max-run-time'): int,
# A link for an executable to download
Optional('context'): basestring,
@ -1221,6 +1224,7 @@ def build_macosx_engine_payload(config, task, task_def):
'command': worker['command'],
'env': worker['env'],
'artifacts': artifacts,
'maxRunTime': worker['max-run-time'],
}
if worker.get('reboot'):
task_def['payload'] = worker['reboot']

View File

@ -1581,46 +1581,45 @@ or run without that action (ie: --no-{action})"
else:
self.fatal("could not determine packageName")
interests = ['libxul.so', 'classes.dex', 'omni.ja']
installer = os.path.join(dirs['abs_obj_dir'], 'dist', packageName)
interests = ['libxul.so', 'classes.dex', 'omni.ja', 'xul.dll']
installer = os.path.join(dist_dir, packageName)
installer_size = 0
size_measurements = []
def paths_with_sizes(installer):
if zipfile.is_zipfile(installer):
with zipfile.ZipFile(installer, 'r') as zf:
for zi in zf.infolist():
yield zi.filename, zi.file_size
elif tarfile.is_tarfile(installer):
with tarfile.open(installer, 'r:*') as tf:
for ti in tf:
yield ti.name, ti.size
if os.path.exists(installer):
installer_size = self.query_filesize(installer)
self.info('Size of %s: %s bytes' % (packageName, installer_size))
try:
subtests = {}
if zipfile.is_zipfile(installer):
with zipfile.ZipFile(installer, 'r') as zf:
for zi in zf.infolist():
name = os.path.basename(zi.filename)
size = zi.file_size
if name in interests:
if name in subtests:
# File seen twice in same archive;
# ignore to avoid confusion.
subtests[name] = None
else:
subtests[name] = size
elif tarfile.is_tarfile(installer):
with tarfile.open(installer, 'r:*') as tf:
for ti in tf:
name = os.path.basename(ti.name)
size = ti.size
if name in interests:
if name in subtests:
# File seen twice in same archive;
# ignore to avoid confusion.
subtests[name] = None
else:
subtests[name] = size
for path, size in paths_with_sizes(installer):
name = os.path.basename(path)
if name in interests:
# We have to be careful here: desktop Firefox installers
# contain two omni.ja files: one for the general runtime,
# and one for the browser proper.
if name == 'omni.ja':
containing_dir = os.path.basename(os.path.dirname(path))
if containing_dir == 'browser':
name = 'browser-omni.ja'
if name in subtests:
self.fatal('should not see %s (%s) multiple times!'
% (name, path))
subtests[name] = size
for name in subtests:
if subtests[name] is not None:
self.info('Size of %s: %s bytes' % (name,
subtests[name]))
size_measurements.append(
{'name': name, 'value': subtests[name]})
self.info('Size of %s: %s bytes' % (name,
subtests[name]))
size_measurements.append(
{'name': name, 'value': subtests[name]})
except:
self.info('Unable to search %s for component sizes.' % installer)
size_measurements = []

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
[cssSkewX.tentative.html]
[CSSSkewX tests]
expected: FAIL

View File

@ -0,0 +1,4 @@
[display.html]
['display' property]
expected: FAIL

View File

@ -0,0 +1,4 @@
[margin-top.html]
['margin-top' property]
expected: FAIL

View File

@ -0,0 +1,97 @@
[pseudo-class-defined.html]
[<div> should be :defined]
expected: FAIL
[createElement("div") should be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/1999/xhtml", "div") should be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/2000/svg", "div") should be :defined]
expected: FAIL
[Without browsing context: createElement("div") should be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "div") should be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/2000/svg", "div") should be :defined]
expected: FAIL
[<a-a> should not be :defined]
expected: FAIL
[createElement("a-a") should not be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/1999/xhtml", "a-a") should not be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/2000/svg", "a-a") should be :defined]
expected: FAIL
[Upgraded createElement("a-a") should be :defined]
expected: FAIL
[Upgraded createElementNS("http://www.w3.org/1999/xhtml", "a-a") should be :defined]
expected: FAIL
[Without browsing context: createElement("a-a") should not be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "a-a") should not be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/2000/svg", "a-a") should be :defined]
expected: FAIL
[<font-face> should be :defined]
expected: FAIL
[createElement("font-face") should be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/1999/xhtml", "font-face") should be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/2000/svg", "font-face") should be :defined]
expected: FAIL
[Without browsing context: createElement("font-face") should be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "font-face") should be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/2000/svg", "font-face") should be :defined]
expected: FAIL
[<abbr is=my-abbr> should not be :defined]
expected: FAIL
[createElement("abbr", { is: "my-abbr" }) should not be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should not be :defined]
expected: FAIL
[createElementNS("http://www.w3.org/2000/svg", "abbr", { is: "my-abbr" }) should be :defined]
expected: FAIL
[Upgraded createElement("abbr", { is: "my-abbr" }) should be :defined]
expected: FAIL
[Upgraded createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should be :defined]
expected: FAIL
[Without browsing context: createElement("abbr", { is: "my-abbr" }) should not be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should not be :defined]
expected: FAIL
[Without browsing context: createElementNS("http://www.w3.org/2000/svg", "abbr", { is: "my-abbr" }) should be :defined]
expected: FAIL

View File

@ -2,7 +2,6 @@
prefs: [dom.webcomponents.shadowdom.enabled:true]
disabled:
if not stylo: Shadow DOM is stylo only.
[removeAttribute on Element must not enqueue an attributeChanged reaction when removing an attribute that does not exist]
expected: FAIL

View File

@ -1,2 +1,2 @@
local: c32b6b173c0008be57ce56cc78878c2e29ef5472
upstream: bb1f35100ad0aedeeb6897dd640b360f80498027
local: 001cf1ba27c3446ae6e4650a773a7022f4ce48b9
upstream: 3197bbb8a67f73a36230d3c2deab7f70cd660727

View File

@ -0,0 +1,2 @@
[panner-automation-basic.html]
expected: ERROR

View File

@ -0,0 +1,2 @@
[panner-automation-position.html]
expected: TIMEOUT

View File

@ -0,0 +1,28 @@
[panner-distance-clamping.html]
[X new PannerNode(c, {refDistance: -1}) did not throw an exception.]
expected: FAIL
[X panner.refDistance = -1 did not throw an exception.]
expected: FAIL
[< [ref-distance-error\] 2 out of 6 assertions were failed.]
expected: FAIL
[X new PannerNode(c, {maxDistance: -1}) did not throw an exception.]
expected: FAIL
[X new PannerNode(c, {maxDistance: 0}) did not throw an exception.]
expected: FAIL
[X panner.maxDistance = -1 did not throw an exception.]
expected: FAIL
[X panner.maxDistance = 0 did not throw an exception.]
expected: FAIL
[< [max-distance-error\] 4 out of 6 assertions were failed.]
expected: FAIL
[# AUDIT TASK RUNNER FINISHED: 2 out of 4 tasks were failed.]
expected: FAIL

View File

@ -0,0 +1,40 @@
[panner-rolloff-clamping.html]
[X Panner distanceModel: "linear", rolloffFactor: -1 expected to be equal to the array [0,0.018875712528824806,0.05621177703142166,0.06847958266735077,0.09856243431568146,0.12242205440998077,0.1495940238237381,0.16251686215400696,0.20651382207870483,0.21217726171016693,0.2435699999332428,0.2771494388580322,0.28300273418426514,0.32210540771484375,0.344368577003479,0.36553990840911865...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t2.8312625363469124e-2\t1.8875712528824806e-2\n\t[2\]\t8.4314860403537750e-2\t5.6211777031421661e-2\n\t[3\]\t1.0271595418453217e-1\t6.8479582667350769e-2\n\t[4\]\t1.4783872663974762e-1\t9.8562434315681458e-2\n\t...and 2043 more errors.]
expected: FAIL
[< [linear-clamp-low\] 1 out of 1 assertions were failed.]
expected: FAIL
[X Panner distanceModel: "linear", rolloffFactor: 2 expected to be equal to the array [0,0.009438800625503063,0.02810869924724102,0.03424321487545967,0.049286145716905594,0.06121714785695076,0.07480449229478836,0.08126655966043472,0.10326723754405975,0.10609924048185349,0.12179718166589737,0.1385885775089264,0.14151552319526672,0.16106881201267242,0.1722015142440796,0.18278823792934418...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t1.8877600496125524e-6\t9.4388006255030632e-3\n\t[2\]\t5.6217400015157182e-6\t2.8108699247241020e-2\n\t[3\]\t6.8486433519865386e-6\t3.4243214875459671e-2\n\t[4\]\t9.8572290880838409e-6\t4.9286145716905594e-2\n\t...and 2043 more errors.]
expected: FAIL
[< [linear-clamp-high\] 1 out of 1 assertions were failed.]
expected: FAIL
[X Panner distanceModel: "inverse", rolloffFactor: -1 expected to be equal to the array [0,0.018875712528824806,0.05621177703142166,0.06847958266735077,0.09856243431568146,0.12242205440998077,0.1495940238237381,0.16251686215400696,0.20651382207870483,0.21217726171016693,0.2435699999332428,0.2771494388580322,0.28300273418426514,0.32210540771484375,0.344368577003479,0.36553990840911865...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t0.0000000000000000e+0\t1.8875712528824806e-2\n\t[2\]\t0.0000000000000000e+0\t5.6211777031421661e-2\n\t[3\]\t0.0000000000000000e+0\t6.8479582667350769e-2\n\t[4\]\t0.0000000000000000e+0\t9.8562434315681458e-2\n\t...and 2043 more errors.]
expected: FAIL
[< [inverse-clamp\] 1 out of 1 assertions were failed.]
expected: FAIL
[X Panner distanceModel: "exponential", rolloffFactor: -2 expected to be equal to the array [0,0.018875712528824806,0.05621177703142166,0.06847958266735077,0.09856243431568146,0.12242205440998077,0.1495940238237381,0.16251686215400696,0.20651382207870483,0.21217726171016693,0.2435699999332428,0.2771494388580322,0.28300273418426514,0.32210540771484375,0.344368577003479,0.36553990840911865...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t4.7189281250000000e+5\t1.8875712528824806e-2\n\t[2\]\t1.4052943750000000e+6\t5.6211777031421661e-2\n\t[3\]\t1.7119896250000000e+6\t6.8479582667350769e-2\n\t[4\]\t2.4640607500000000e+6\t9.8562434315681458e-2\n\t...and 2043 more errors.]
expected: FAIL
[< [exponential-clamp\] 1 out of 1 assertions were failed.]
expected: FAIL
[# AUDIT TASK RUNNER FINISHED: 4 out of 4 tasks were failed.]
expected: FAIL
[X Panner distanceModel: "linear", rolloffFactor: -1 expected to be equal to the array [0,0.0188757237046957,0.056211769580841064,0.06847962737083435,0.09856244176626205,0.12242206931114197,0.1495940387248993,0.16251686215400696,0.2065138816833496,0.21217726171016693,0.24357002973556519,0.2771494686603546,0.2830027937889099,0.3221054673194885,0.344368577003479,0.36553993821144104...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t2.8312642127275467e-2\t1.8875723704695702e-2\n\t[2\]\t8.4314845502376556e-2\t5.6211769580841064e-2\n\t[3\]\t1.0271602123975754e-1\t6.8479627370834351e-2\n\t[4\]\t1.4783874154090881e-1\t9.8562441766262054e-2\n\t...and 2043 more errors.]
expected: FAIL
[X Panner distanceModel: "linear", rolloffFactor: 2 expected to be equal to the array [0,0.009438806213438511,0.028108695521950722,0.03424323722720146,0.04928614944219589,0.06121715530753136,0.07480449974536896,0.08126655966043472,0.10326726734638214,0.10609924048185349,0.12179719656705856,0.13858859241008759,0.1415155529975891,0.1610688418149948,0.1722015142440796,0.18278825283050537...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t1.8877611864809296e-6\t9.4388062134385109e-3\n\t[2\]\t5.6217390920210164e-6\t2.8108695521950722e-2\n\t[3\]\t6.8486474447126966e-6\t3.4243237227201462e-2\n\t[4\]\t9.8572299975785427e-6\t4.9286149442195892e-2\n\t...and 2043 more errors.]
expected: FAIL
[X Panner distanceModel: "inverse", rolloffFactor: -1 expected to be equal to the array [0,0.0188757237046957,0.056211769580841064,0.06847962737083435,0.09856244176626205,0.12242206931114197,0.1495940387248993,0.16251686215400696,0.2065138816833496,0.21217726171016693,0.24357002973556519,0.2771494686603546,0.2830027937889099,0.3221054673194885,0.344368577003479,0.36553993821144104...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t0.0000000000000000e+0\t1.8875723704695702e-2\n\t[2\]\t0.0000000000000000e+0\t5.6211769580841064e-2\n\t[3\]\t0.0000000000000000e+0\t6.8479627370834351e-2\n\t[4\]\t0.0000000000000000e+0\t9.8562441766262054e-2\n\t...and 2043 more errors.]
expected: FAIL
[X Panner distanceModel: "exponential", rolloffFactor: -2 expected to be equal to the array [0,0.0188757237046957,0.056211769580841064,0.06847962737083435,0.09856244176626205,0.12242206931114197,0.1495940387248993,0.16251686215400696,0.2065138816833496,0.21217726171016693,0.24357002973556519,0.2771494686603546,0.2830027937889099,0.3221054673194885,0.344368577003479,0.36553993821144104...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t4.7189309375000000e+5\t1.8875723704695702e-2\n\t[2\]\t1.4052942500000000e+6\t5.6211769580841064e-2\n\t[3\]\t1.7119906250000000e+6\t6.8479627370834351e-2\n\t[4\]\t2.4640610000000000e+6\t9.8562441766262054e-2\n\t...and 2043 more errors.]
expected: FAIL

View File

@ -0,0 +1,2 @@
[pannernode-basic.html]
expected: ERROR

View File

@ -54,6 +54,9 @@ function assert_style_value_equals(a, b) {
assert_style_value_equals(a.ax, b.ax);
assert_style_value_equals(a.ay, b.ay);
break;
case 'CSSSkewX':
assert_style_value_equals(a.ax, b.ax);
break;
case 'CSSPerspective':
assert_style_value_equals(a.length, b.length);
break;

View File

@ -123,6 +123,11 @@ const gTestCases = [
expected: new CSSSkew(CSS.deg(90), CSS.deg(0)),
desc: 'skew() with only X'
},
{
cssText: 'skew(90deg, 0deg)',
expected: new CSSSkew(CSS.deg(90), CSS.deg(0)),
desc: 'skew() with X and Y which is 0 value'
},
{
cssText: 'skew(90deg, 45deg)',
expected: new CSSSkew(CSS.deg(90), CSS.deg(45)),
@ -130,7 +135,7 @@ const gTestCases = [
},
{
cssText: 'skewX(90deg)',
expected: new CSSSkew(CSS.deg(90), CSS.deg(0)),
expected: new CSSSkewX(CSS.deg(90)),
desc: 'skewX()'
},
{
@ -153,12 +158,13 @@ for (const {cssText, expected, desc} of gTestCases) {
test(t => {
test_transform_normalization(t,
'translate(1px) rotateX(90deg) perspective(1px) skew(90deg) scale3d(1, 2, 3)',
'translate(1px) rotateX(90deg) perspective(1px) skew(90deg) skewX(20deg) scale3d(1, 2, 3)',
new CSSTransformValue([
new CSSTranslate(CSS.px(1), CSS.px(0)),
new CSSRotate(CSS.number(1), CSS.number(0), CSS.number(0), CSS.deg(90)),
new CSSPerspective(CSS.px(1)),
new CSSSkew(CSS.deg(90), CSS.deg(0)),
new CSSSkewX(CSS.deg(20)),
new CSSScale(CSS.number(1), CSS.number(2), CSS.number(3)),
]));
}, 'Normalizing a <transform-list> returns a CSSTransformValue containing all the transforms');

View File

@ -49,6 +49,11 @@ const gTestCases = [
cssText: 'skew(90deg)',
desc: 'CSSSkew with Y which is 0 value'
},
{
value: new CSSSkewX(CSS.deg(90)),
cssText: 'skewX(90deg)',
desc: 'CSSSkewX'
},
{
value: new CSSPerspective(CSS.px(1)),
cssText: 'perspective(1px)',

View File

@ -0,0 +1,62 @@
<!doctype html>
<meta charset="utf-8">
<title>CSSSkewX tests</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#cssskewx">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/testhelper.js"></script>
<script>
'use strict';
const gInvalidTestCases = [
{ value: 'auto', desc: 'a keyword'},
{ value: 3.14, desc: 'a double'},
{ value: 0, desc: 'a unitless zero'},
{ value: '10deg', desc: 'a string angle'},
{ value: CSS.number(10), desc: 'a number CSSUnitValue'},
{ value: CSS.s(10), desc: 'a time dimension CSSUnitValue'},
{ value: new CSSMathSum(CSS.px(1)), desc: 'a CSSMathValue of length type' },
];
for (const {value, desc} of gInvalidTestCases) {
test(() => {
assert_throws(new TypeError(), () => new CSSSkewX(value));
}, 'Constructing a CSSSkewX with ' + desc + ' throws a TypeError');
}
for (const {value, desc} of gInvalidTestCases) {
test(() => {
let skewX = new CSSSkewX(CSS.deg(0));
assert_throws(new TypeError(), () => skewX.ax = value);
assert_style_value_equals(skewX.ax, CSS.deg(0));
}, 'Updating CSSSkewX.ax with ' + desc + ' throws a TypeError');
}
const gValidTestCases = [
{ value: CSS.deg(-3.14), desc: 'an angle CSSUnitValue' },
{ value: new CSSMathSum(CSS.deg(1)), desc: 'a CSSMathValue of angle type' },
];
for (const {value: ax, desc: axDesc} of gValidTestCases) {
test(() => {
const skewX = new CSSSkewX(ax);
assert_equals(skewX.ax, ax);
assert_true(skewX.is2D);
}, 'CSSSkewX can be constructed from ' + axDesc);
}
for (const {value, desc} of gValidTestCases) {
test(() => {
let skewX = new CSSSkewX(CSS.deg(0));
skewX.ax = value;
assert_style_value_equals(skewX.ax, value);
}, 'CSSSkew.ax can be updated to ' + desc);
}
test(() => {
let skewX = new CSSSkewX(CSS.deg(0));
skewX.is2D = false;
assert_true(skewX.is2D);
}, 'Modifying skewX.is2D is a no-op');
</script>

View File

@ -0,0 +1,23 @@
<!doctype html>
<meta charset="utf-8">
<title>'display' property</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<script src="resources/testsuite.js"></script>
<body>
<div id="log">
<script>
'use strict';
runPropertyTests('display', [
{
specified: '<ident>',
examples: [new CSSKeywordValue('none'), new CSSKeywordValue('block')]
},
]);
</script>

View File

@ -0,0 +1,23 @@
<!doctype html>
<meta charset="utf-8">
<title>'margin-top' property</title>
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get">
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set">
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../resources/testhelper.js"></script>
<script src="resources/testsuite.js"></script>
<body>
<div id="log">
<script>
'use strict';
runPropertyTests('margin-top', [
{ specified: '0' },
{ specified: '<ident>', examples: [new CSSKeywordValue('auto')] },
{ specified: '<percentage>' },
{ specified: '<length>' },
]);
</script>

View File

@ -0,0 +1,62 @@
function testGet(propertyName, values, description) {
test(t => {
let element = createDivWithStyle(t);
let styleMap = element.attributeStyleMap;
for (const styleValue of values) {
element.style[propertyName] = styleValue.toString();
getComputedStyle(element); // Force a style recalc.
const result = styleMap.get(propertyName);
assert_style_value_equals(result, styleValue);
}
}, `Can get ${description} from '${propertyName}'`);
}
function testSet(propertyName, values, description) {
test(t => {
let element = createDivWithStyle(t);
let styleMap = element.attributeStyleMap;
for (const styleValue of values) {
styleMap.set(propertyName, styleValue);
getComputedStyle(element); // Force a style recalc.
assert_equals(element.style[propertyName], styleValue.toString());
}
}, `Can set '${propertyName}' to ${description}`);
}
function testGetSet(propertyName, values, description) {
testGet(propertyName, values, description);
testSet(propertyName, values, description);
}
function runPropertyTests(propertyName, testCases) {
for (const testCase of testCases) {
if (testCase.specified == '0') {
testSet(propertyName, [
new CSSUnitValue(0, 'number'),
], 'unitless zero');
} else if (testCase.specified === '<length>') {
testGetSet(propertyName, [
new CSSUnitValue(0, 'px'),
new CSSUnitValue(-3.14, 'em'),
new CSSUnitValue(3.14, 'cm'),
], 'a length CSSUnitValue');
} else if (testCase.specified == '<percentage>') {
testGetSet(propertyName, [
new CSSUnitValue(0, 'percent'),
new CSSUnitValue(-3.14, 'percent'),
new CSSUnitValue(3.14, 'percent'),
], 'a percent CSSUnitValue');
} else if (testCase.specified == '<ident>') {
if (!testCase.examples) {
throw new Error('<ident> tests require examples');
}
testGetSet(propertyName, testCase.examples,
'a CSSKeywordValue');
}
}
}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Basic User Interface Test Reference</title>
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
<style>
div { font-family: monospace; }
</style>
<p>The test passes if the following text is visible below: 123456 FE…</p>
<div>123456 FE…</bdo></div>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Basic User Interface Test Reference</title>
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
<style>
div { font-family: monospace; }
</style>
<p>The test passes if the following text is visible below: …56 FEDCBA</p>
<div>…56 FEDCBA</div>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>CSS Basic User Interface Reference</title>
<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
<style>
div { font: 20px monospace; }
</style>
<div>Test passed…</div>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Basic User Interface Test: text-overflow applies visually to bidi</title>
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
<link rel="match" href="reference/text-overflow-027-ref.html">
<meta name="flags" content="">
<meta name="assert" content="text-overflow is a visual operation that occurs after layout, and therfore ellides text from the visual end of the line, even in bidi situations">
<style>
div {
font-family: monospace;
width: 10ch;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
</style>
<p>The test passes if the following text is visible below: 123456 FE…</p>
<div>123456 <bdo dir=rtl>ABCDEF</bdo></div>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Basic User Interface Test: text-overflow applies visually to bidi</title>
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
<link rel="match" href="reference/text-overflow-028-ref.html">
<meta name="flags" content="">
<meta name="assert" content="text-overflow is a visual operation that occurs after layout, and therfore ellides text from the visual end of the line, even in bidi situations">
<style>
div {
font-family: monospace;
width: 10ch;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
}
</style>
<p>The test passes if the following text is visible below: …56 FEDCBA</p>
<div dir=rtl><bdo dir=rtl>ABCDEF</bdo> 123456</div>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>CSS Basic User Interface Test: text-overflow and bidi interaction</title>
<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
<link rel="help" href="http://www.w3.org/TR/css-ui-3/#text-overflow">
<link rel="help" href="http://www.w3.org/TR/css-ui-4/#text-overflow">
<link rel="match" href="reference/text-overflow-029-ref.html">
<meta name="assert" content="When there's content of mixed directionality, text-overflow ellides the characters at the physical end of the line.">
<meta name="flags" content="">
<style>
div {
font: 20px monospace;
width: 12.3ch; /* slightly more than 12ch because in some browsers (safari) the ellipsis is slightly large than other characters, even in monospace fonts. */
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
</style>
<div>Test &#x202E;deliafdessap&#x202C;</div>

Some files were not shown because too many files have changed in this diff Show More