зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
93215ef261
|
@ -19,7 +19,7 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
|
|||
if (mShutdown)
|
||||
return true;
|
||||
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
|
||||
if (aData.NewTree().IsEmpty()) {
|
||||
NS_ERROR("no children being added");
|
||||
|
@ -50,7 +50,7 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
|
|||
}
|
||||
#endif
|
||||
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
|
|||
if (mShutdown)
|
||||
return true;
|
||||
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
|
||||
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
|
||||
if (!rootEntry) {
|
||||
|
@ -122,7 +122,7 @@ DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
|
|||
parent->RemoveChild(root);
|
||||
root->Shutdown();
|
||||
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -196,12 +196,12 @@ DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uin
|
|||
if (!aID)
|
||||
return false;
|
||||
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
|
||||
auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
|
||||
bool result = AddChildDoc(childDoc, aID, false);
|
||||
MOZ_ASSERT(result);
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -265,16 +265,20 @@ DocAccessibleParent::Destroy()
|
|||
GetAccService()->RemoteDocShutdown(this);
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
DocAccessibleParent::CheckDocTree() const
|
||||
{
|
||||
size_t childDocs = mChildDocs.Length();
|
||||
for (size_t i = 0; i < childDocs; i++) {
|
||||
if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
|
||||
MOZ_CRASH("document tree is broken!");
|
||||
return false;
|
||||
|
||||
mChildDocs[i]->CheckDocTree();
|
||||
if (!mChildDocs[i]->CheckDocTree()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // a11y
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
void Destroy();
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override
|
||||
{
|
||||
CheckDocTree();
|
||||
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
|
||||
if (!mShutdown)
|
||||
Destroy();
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ private:
|
|||
uint32_t AddSubtree(ProxyAccessible* aParent,
|
||||
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
|
||||
uint32_t aIdxInParent);
|
||||
void CheckDocTree() const;
|
||||
MOZ_WARN_UNUSED_RESULT bool CheckDocTree() const;
|
||||
|
||||
nsTArray<DocAccessibleParent*> mChildDocs;
|
||||
DocAccessibleParent* mParentDoc;
|
||||
|
|
|
@ -70,6 +70,12 @@ function* test_mute_tab(tab, icon, expectMuted) {
|
|||
return mutedPromise;
|
||||
}
|
||||
|
||||
function get_tab_attributes(tab) {
|
||||
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
let {attributes} = JSON.parse(ss.getTabState(tab));
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function* test_playing_icon_on_tab(tab, browser, isPinned) {
|
||||
let icon = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
isPinned ? "overlay-icon" : "soundplaying-icon");
|
||||
|
@ -83,12 +89,18 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) {
|
|||
|
||||
yield test_tooltip(icon, "Mute tab");
|
||||
|
||||
ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
|
||||
|
||||
yield test_mute_tab(tab, icon, true);
|
||||
|
||||
ok("muted" in get_tab_attributes(tab), "Muted attribute should be persisted");
|
||||
|
||||
yield test_tooltip(icon, "Unmute tab");
|
||||
|
||||
yield test_mute_tab(tab, icon, false);
|
||||
|
||||
ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
|
||||
|
||||
yield test_tooltip(icon, "Mute tab");
|
||||
|
||||
yield test_mute_tab(tab, icon, true);
|
||||
|
|
|
@ -24,7 +24,7 @@ this.TabAttributes = Object.freeze({
|
|||
});
|
||||
|
||||
let TabAttributesInternal = {
|
||||
_attrs: new Set(),
|
||||
_attrs: new Set(["muted"]),
|
||||
|
||||
// We never want to directly read or write those attributes.
|
||||
// 'image' should not be accessed directly but handled by using the
|
||||
|
|
|
@ -455,6 +455,7 @@ frame/log.h
|
|||
frame/req.h
|
||||
freetype/freetype.h
|
||||
freetype/ftcache.h
|
||||
freetype/ftfntfmt.h
|
||||
freetype/ftglyph.h
|
||||
freetype/ftsynth.h
|
||||
freetype/ftoutln.h
|
||||
|
|
24
configure.in
24
configure.in
|
@ -2317,6 +2317,7 @@ ia64*-hpux*)
|
|||
dnl Probably also a compiler bug, but what can you do?
|
||||
PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
|
||||
LDFLAGS="$LDFLAGS -DYNAMICBASE"
|
||||
RCFLAGS="-nologo"
|
||||
if test "$_CC_MAJOR_VERSION" = "18" -a "$_CC_BUILD_VERSION" = "31101"; then
|
||||
dnl Use MaxILKSize as a workaround for LNK1248 in VS2013update4
|
||||
dnl See https://connect.microsoft.com/VisualStudio/feedback/details/1044914/fatal-error-lnk1248
|
||||
|
@ -9133,6 +9134,11 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
|
|||
if test -n "$MOZ_REPLACE_MALLOC"; then
|
||||
# When using replace_malloc, we always want valloc exported from jemalloc.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes"
|
||||
if test "${OS_ARCH}" = Darwin; then
|
||||
# We also need to enable pointer validation on Mac because jemalloc's
|
||||
# zone allocator is not used.
|
||||
ac_configure_args="$ac_configure_args --enable-ivsalloc"
|
||||
fi
|
||||
fi
|
||||
if test -n "$MOZ_JEMALLOC3"; then
|
||||
case "${OS_ARCH}" in
|
||||
|
@ -9142,6 +9148,11 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
|
|||
ac_configure_args="$ac_configure_args --without-export"
|
||||
;;
|
||||
esac
|
||||
if test "${OS_ARCH}" = WINNT; then
|
||||
# Lazy lock initialization doesn't play well with lazy linking of
|
||||
# mozglue.dll on Windows XP (leads to startup crash), so disable it.
|
||||
ac_configure_args="$ac_configure_args --disable-lazy-lock"
|
||||
fi
|
||||
elif test "${OS_ARCH}" = Darwin; then
|
||||
# When building as a replace-malloc lib, disabling the zone allocator
|
||||
# forces to use pthread_atfork.
|
||||
|
@ -9178,20 +9189,27 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
|
|||
for var in AS CC CXX CPP LD AR RANLIB STRIP CPPFLAGS EXTRA_CFLAGS LDFLAGS; do
|
||||
ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'"
|
||||
done
|
||||
if test "$CROSS_COMPILE"; then
|
||||
ac_configure_args="$ac_configure_args je_cv_static_page_shift=12"
|
||||
fi
|
||||
# Force disable DSS support in jemalloc.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
|
||||
|
||||
# Make Linux builds munmap freed chunks instead of recycling them.
|
||||
ac_configure_args="$ac_configure_args --enable-munmap"
|
||||
|
||||
# Disable cache oblivious behavior that appears to have a performance
|
||||
# impact on Firefox.
|
||||
ac_configure_args="$ac_configure_args --disable-cache-oblivious"
|
||||
|
||||
if ! test -e memory/jemalloc; then
|
||||
mkdir -p memory/jemalloc
|
||||
fi
|
||||
|
||||
# jemalloc's configure runs git to determine the version. But when building
|
||||
# from a gecko git clone, the git commands it uses is going to pick gecko's
|
||||
# information, not jemalloc's, which is useless. So pretend we don't have git
|
||||
# at all. That will make jemalloc's configure pick the in-tree VERSION file.
|
||||
(PATH="$srcdir/memory/jemalloc/helper:$PATH";
|
||||
AC_OUTPUT_SUBDIRS(memory/jemalloc/src)
|
||||
) || exit 1
|
||||
ac_configure_args="$_SUBDIR_CONFIG_ARGS"
|
||||
fi
|
||||
|
||||
|
|
|
@ -665,7 +665,7 @@ nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
|
|||
|
||||
#if defined(XP_WIN)
|
||||
// Check for \ in the url-string or just a drive (PC)
|
||||
if (kNotFound != aIn.FindChar('\\') ||
|
||||
if (aIn.Contains('\\') ||
|
||||
(aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
|
||||
attemptFixup = true;
|
||||
}
|
||||
|
|
|
@ -374,6 +374,16 @@ Animation::Tick()
|
|||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
// FIXME: Detect the no-change case and don't request a restyle at all
|
||||
// FIXME: Detect changes to IsPlaying() state and request RestyleType::Layer
|
||||
// so that layers get updated immediately
|
||||
AnimationCollection* collection = GetCollection();
|
||||
if (collection) {
|
||||
collection->RequestRestyle(CanThrottle() ?
|
||||
AnimationCollection::RestyleType::Throttled :
|
||||
AnimationCollection::RestyleType::Standard);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -945,7 +955,7 @@ Animation::PostUpdate()
|
|||
{
|
||||
AnimationCollection* collection = GetCollection();
|
||||
if (collection) {
|
||||
collection->NotifyAnimationUpdated();
|
||||
collection->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1262,7 +1262,7 @@ BlobSet::AppendString(const nsAString& aString, bool nativeEOL, JSContext* aCx)
|
|||
nsCString utf8Str = NS_ConvertUTF16toUTF8(aString);
|
||||
|
||||
if (nativeEOL) {
|
||||
if (utf8Str.FindChar('\r') != kNotFound) {
|
||||
if (utf8Str.Contains('\r')) {
|
||||
utf8Str.ReplaceSubstring("\r\n", "\n");
|
||||
utf8Str.ReplaceSubstring("\r", "\n");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ class nsISupports;
|
|||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class PerformanceResourceTiming;
|
||||
|
||||
// http://www.w3.org/TR/performance-timeline/#performanceentry
|
||||
class PerformanceEntry : public nsISupports,
|
||||
|
@ -78,6 +79,11 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
virtual const PerformanceResourceTiming* ToResourceTiming() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsString mName;
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "PerformanceObserver.h"
|
||||
|
||||
#include "mozilla/dom/PerformanceBinding.h"
|
||||
#include "mozilla/dom/PerformanceEntryBinding.h"
|
||||
#include "mozilla/dom/PerformanceObserverBinding.h"
|
||||
#include "mozilla/dom/workers/bindings/Performance.h"
|
||||
#include "nsPerformance.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsString.h"
|
||||
#include "PerformanceEntry.h"
|
||||
#include "PerformanceObserverEntryList.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserver,
|
||||
mOwner,
|
||||
mPerformance,
|
||||
mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserver)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserver)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PerformanceObserver::PerformanceObserver(nsPIDOMWindow* aOwner,
|
||||
PerformanceObserverCallback& aCb)
|
||||
: mOwner(aOwner)
|
||||
, mCallback(&aCb)
|
||||
{
|
||||
MOZ_ASSERT(mOwner);
|
||||
mPerformance = aOwner->GetPerformance();
|
||||
}
|
||||
|
||||
PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate,
|
||||
PerformanceObserverCallback& aCb)
|
||||
: mCallback(&aCb)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
mPerformance = aWorkerPrivate->GlobalScope()->GetPerformance();
|
||||
}
|
||||
|
||||
PerformanceObserver::~PerformanceObserver()
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<PerformanceObserver>
|
||||
PerformanceObserver::Constructor(const GlobalObject& aGlobal,
|
||||
PerformanceObserverCallback& aCb,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindow> ownerWindow =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!ownerWindow) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(ownerWindow->IsInnerWindow());
|
||||
|
||||
nsRefPtr<PerformanceObserver> observer =
|
||||
new PerformanceObserver(ownerWindow, aCb);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
nsRefPtr<PerformanceObserver> observer =
|
||||
new PerformanceObserver(workerPrivate, aCb);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceObserverBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserver::Notify(PerformanceEntry* aEntry)
|
||||
{
|
||||
MOZ_ASSERT(aEntry);
|
||||
|
||||
nsAutoString entryType;
|
||||
aEntry->GetEntryType(entryType);
|
||||
if (!mEntryTypes.Contains<nsString>(entryType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<PerformanceObserverEntryList> list = new PerformanceObserverEntryList(this);
|
||||
list->AppendEntry(aEntry);
|
||||
|
||||
ErrorResult rv;
|
||||
mCallback->Call(this, *list, *this, rv);
|
||||
NS_WARN_IF(rv.Failed());
|
||||
}
|
||||
|
||||
static nsString sValidTypeNames[7] = {
|
||||
NS_LITERAL_STRING("composite"),
|
||||
NS_LITERAL_STRING("mark"),
|
||||
NS_LITERAL_STRING("measure"),
|
||||
NS_LITERAL_STRING("navigation"),
|
||||
NS_LITERAL_STRING("render"),
|
||||
NS_LITERAL_STRING("resource"),
|
||||
NS_LITERAL_STRING("server")
|
||||
};
|
||||
|
||||
void
|
||||
PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aOptions.mEntryTypes.IsEmpty()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsString> validEntryTypes;
|
||||
|
||||
for (const nsString& validTypeName : sValidTypeNames) {
|
||||
if (aOptions.mEntryTypes.Contains<nsString>(validTypeName) &&
|
||||
!validEntryTypes.Contains<nsString>(validTypeName)) {
|
||||
validEntryTypes.AppendElement(validTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
if (validEntryTypes.IsEmpty()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mEntryTypes = validEntryTypes;
|
||||
|
||||
mPerformance->AddObserver(this);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserver::Disconnect()
|
||||
{
|
||||
mPerformance->RemoveObserver(this);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_PerformanceObserver_h__
|
||||
#define mozilla_dom_PerformanceObserver_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
class PerformanceBase;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
class PerformanceEntry;
|
||||
class PerformanceObserverCallback;
|
||||
struct PerformanceObserverInit;
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
class PerformanceObserver final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserver)
|
||||
|
||||
static already_AddRefed<PerformanceObserver>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
PerformanceObserverCallback& aCb,
|
||||
ErrorResult& aRv);
|
||||
|
||||
PerformanceObserver(nsPIDOMWindow* aOwner,
|
||||
PerformanceObserverCallback& aCb);
|
||||
|
||||
PerformanceObserver(workers::WorkerPrivate* aWorkerPrivate,
|
||||
PerformanceObserverCallback& aCb);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
void Observe(const PerformanceObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
void Notify(PerformanceEntry* entry);
|
||||
|
||||
private:
|
||||
~PerformanceObserver();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsRefPtr<PerformanceObserverCallback> mCallback;
|
||||
nsRefPtr<PerformanceBase> mPerformance;
|
||||
nsTArray<nsString> mEntryTypes;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "PerformanceObserverEntryList.h"
|
||||
|
||||
#include "mozilla/dom/PerformanceObserverEntryListBinding.h"
|
||||
#include "nsPerformance.h"
|
||||
#include "nsString.h"
|
||||
#include "PerformanceResourceTiming.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserverEntryList,
|
||||
mOwner,
|
||||
mEntries)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserverEntryList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserverEntryList)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserverEntryList)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PerformanceObserverEntryList::~PerformanceObserverEntryList()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceObserverEntryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceObserverEntryListBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntries(
|
||||
const PerformanceEntryFilterOptions& aFilter,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (aFilter.mInitiatorType.WasPassed()) {
|
||||
const PerformanceResourceTiming* resourceEntry =
|
||||
entry->ToResourceTiming();
|
||||
if (!resourceEntry) {
|
||||
continue;
|
||||
}
|
||||
nsAutoString initiatorType;
|
||||
resourceEntry->GetInitiatorType(initiatorType);
|
||||
if (!initiatorType.Equals(aFilter.mInitiatorType.Value())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (aFilter.mName.WasPassed() &&
|
||||
!entry->GetName().Equals(aFilter.mName.Value())) {
|
||||
continue;
|
||||
}
|
||||
if (aFilter.mEntryType.WasPassed() &&
|
||||
!entry->GetEntryType().Equals(aFilter.mEntryType.Value())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntriesByType(
|
||||
const nsAString& aEntryType,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (entry->GetEntryType().Equals(aEntryType)) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntriesByName(
|
||||
const nsAString& aName,
|
||||
const Optional<nsAString>& aEntryType,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (entry->GetName().Equals(aName)) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::AppendEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
mEntries.AppendElement(aEntry);
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_PerformanceObserverEntryList_h__
|
||||
#define mozilla_dom_PerformanceObserverEntryList_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/PerformanceEntryBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct PerformanceEntryFilterOptions;
|
||||
class PerformanceEntry;
|
||||
template<typename T> class Optional;
|
||||
|
||||
class PerformanceObserverEntryList final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
~PerformanceObserverEntryList();
|
||||
|
||||
public:
|
||||
explicit PerformanceObserverEntryList(nsISupports* aOwner)
|
||||
: mOwner(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserverEntryList)
|
||||
|
||||
void GetEntries(const PerformanceEntryFilterOptions& aFilter,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
|
||||
void GetEntriesByType(const nsAString& aEntryType,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
|
||||
void GetEntriesByName(const nsAString& aName,
|
||||
const Optional<nsAString>& aEntryType,
|
||||
nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
|
||||
|
||||
void AppendEntry(PerformanceEntry* aEntry);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsTArray<nsRefPtr<PerformanceEntry>> mEntries;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif
|
|
@ -123,6 +123,11 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
virtual const PerformanceResourceTiming* ToResourceTiming() const override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PerformanceResourceTiming();
|
||||
|
||||
|
|
|
@ -189,6 +189,8 @@ EXPORTS.mozilla.dom += [
|
|||
'PerformanceEntry.h',
|
||||
'PerformanceMark.h',
|
||||
'PerformanceMeasure.h',
|
||||
'PerformanceObserver.h',
|
||||
'PerformanceObserverEntryList.h',
|
||||
'PerformanceResourceTiming.h',
|
||||
'ProcessGlobal.h',
|
||||
'ResponsiveImageSelector.h',
|
||||
|
@ -328,6 +330,8 @@ UNIFIED_SOURCES += [
|
|||
'PerformanceEntry.cpp',
|
||||
'PerformanceMark.cpp',
|
||||
'PerformanceMeasure.cpp',
|
||||
'PerformanceObserver.cpp',
|
||||
'PerformanceObserverEntryList.cpp',
|
||||
'PerformanceResourceTiming.cpp',
|
||||
'PostMessageEvent.cpp',
|
||||
'ProcessGlobal.cpp',
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
#include "nsIDocument.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef LoadImage
|
||||
// Undefine LoadImage to prevent naming conflict with Windows.
|
||||
#undef LoadImage
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#define MISC_STR_PTR(_cont) \
|
||||
|
|
|
@ -1729,7 +1729,7 @@ nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
|
|||
|
||||
// Manifest URIs can't have fragment identifiers.
|
||||
if (manifestSpec.IsEmpty() ||
|
||||
manifestSpec.FindChar('#') != kNotFound) {
|
||||
manifestSpec.Contains('#')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ class nsICSSLoaderObserver;
|
|||
class nsIURI;
|
||||
|
||||
#define NS_ISTYLESHEETLINKINGELEMENT_IID \
|
||||
{ 0xe5855604, 0x8a9a, 0x4181, \
|
||||
{ 0xbe, 0x41, 0xdd, 0xf7, 0x08, 0x70, 0x3f, 0xbe } }
|
||||
{ 0xa8b79f3b, 0x9d18, 0x4f9c, \
|
||||
{ 0xb1, 0xaa, 0x8c, 0x9b, 0x1b, 0xaa, 0xac, 0xad } }
|
||||
|
||||
namespace mozilla {
|
||||
class CSSStyleSheet;
|
||||
|
@ -97,6 +97,14 @@ public:
|
|||
// some types of linking elements, but it's a better place than
|
||||
// anywhere else.
|
||||
virtual void SetLineNumber(uint32_t aLineNumber) = 0;
|
||||
|
||||
/**
|
||||
* Get the line number, as previously set by SetLineNumber.
|
||||
*
|
||||
* @return the line number of this element; or 1 if no line number
|
||||
* was set
|
||||
*/
|
||||
virtual uint32_t GetLineNumber() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheetLinkingElement,
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
#include "PerformanceEntry.h"
|
||||
#include "PerformanceMark.h"
|
||||
#include "PerformanceMeasure.h"
|
||||
#include "PerformanceObserver.h"
|
||||
#include "PerformanceResourceTiming.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/PerformanceBinding.h"
|
||||
#include "mozilla/dom/PerformanceEntryEvent.h"
|
||||
#include "mozilla/dom/PerformanceTimingBinding.h"
|
||||
#include "mozilla/dom/PerformanceNavigationBinding.h"
|
||||
#include "mozilla/dom/PerformanceObserverBinding.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
@ -692,15 +694,17 @@ public:
|
|||
class PrefEnabledRunnable final : public WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsCString& aPrefName)
|
||||
: WorkerMainThreadRunnable(aWorkerPrivate)
|
||||
, mEnabled(false)
|
||||
, mPrefName(aPrefName)
|
||||
{ }
|
||||
|
||||
bool MainThreadRun() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mEnabled = Preferences::GetBool("dom.enable_user_timing", false);
|
||||
mEnabled = Preferences::GetBool(mPrefName.get(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -711,6 +715,7 @@ public:
|
|||
|
||||
private:
|
||||
bool mEnabled;
|
||||
nsCString mPrefName;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -727,7 +732,27 @@ nsPerformance::IsEnabled(JSContext* aCx, JSObject* aGlobal)
|
|||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<PrefEnabledRunnable> runnable =
|
||||
new PrefEnabledRunnable(workerPrivate);
|
||||
new PrefEnabledRunnable(workerPrivate,
|
||||
NS_LITERAL_CSTRING("dom.enable_user_timing"));
|
||||
runnable->Dispatch(workerPrivate->GetJSContext());
|
||||
|
||||
return runnable->IsEnabled();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsPerformance::IsObserverEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.enable_performance_observer", false);
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<PrefEnabledRunnable> runnable =
|
||||
new PrefEnabledRunnable(workerPrivate,
|
||||
NS_LITERAL_CSTRING("dom.enable_performance_observer"));
|
||||
runnable->Dispatch(workerPrivate->GetJSContext());
|
||||
|
||||
return runnable->IsEnabled();
|
||||
|
@ -1024,6 +1049,10 @@ PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry)
|
|||
{
|
||||
mUserEntries.InsertElementSorted(aEntry,
|
||||
PerformanceEntryComparator());
|
||||
|
||||
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
|
||||
PerformanceObserver,
|
||||
Notify, (aEntry));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1047,4 +1076,19 @@ PerformanceBase::InsertResourceEntry(PerformanceEntry* aEntry)
|
|||
// call onresourcetimingbufferfull
|
||||
DispatchBufferFullEvent();
|
||||
}
|
||||
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
|
||||
PerformanceObserver,
|
||||
Notify, (aEntry));
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceBase::AddObserver(PerformanceObserver* aObserver)
|
||||
{
|
||||
mObservers.AppendElementUnlessExists(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceBase::RemoveObserver(PerformanceObserver* aObserver)
|
||||
{
|
||||
mObservers.RemoveElement(aObserver);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace mozilla {
|
|||
class ErrorResult;
|
||||
namespace dom {
|
||||
class PerformanceEntry;
|
||||
class PerformanceObserver;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -300,6 +301,7 @@ public:
|
|||
explicit PerformanceBase(nsPIDOMWindow* aWindow);
|
||||
|
||||
typedef mozilla::dom::PerformanceEntry PerformanceEntry;
|
||||
typedef mozilla::dom::PerformanceObserver PerformanceObserver;
|
||||
|
||||
void GetEntries(nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
|
||||
void GetEntriesByType(const nsAString& aEntryType,
|
||||
|
@ -321,6 +323,9 @@ public:
|
|||
|
||||
void SetResourceTimingBufferSize(uint64_t aMaxSize);
|
||||
|
||||
void AddObserver(PerformanceObserver* aObserver);
|
||||
void RemoveObserver(PerformanceObserver* aObserver);
|
||||
|
||||
protected:
|
||||
virtual ~PerformanceBase();
|
||||
|
||||
|
@ -353,6 +358,8 @@ protected:
|
|||
void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
|
||||
void TimingNotification(PerformanceEntry* aEntry, const nsACString& aOwner, uint64_t epoch);
|
||||
|
||||
nsTObserverArray<PerformanceObserver*> mObservers;
|
||||
|
||||
private:
|
||||
nsTArray<nsRefPtr<PerformanceEntry>> mUserEntries;
|
||||
nsTArray<nsRefPtr<PerformanceEntry>> mResourceEntries;
|
||||
|
@ -376,6 +383,8 @@ public:
|
|||
|
||||
static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
static bool IsObserverEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
nsDOMNavigationTiming* GetDOMTiming() const
|
||||
{
|
||||
return mDOMTiming;
|
||||
|
|
|
@ -119,6 +119,12 @@ nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
|
|||
mLineNumber = aLineNumber;
|
||||
}
|
||||
|
||||
/* virtual */ uint32_t
|
||||
nsStyleLinkElement::GetLineNumber()
|
||||
{
|
||||
return mLineNumber;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsStyleLinkElement::IsImportEnabled()
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
|
||||
virtual void OverrideBaseURI(nsIURI* aNewBaseURI) override;
|
||||
virtual void SetLineNumber(uint32_t aLineNumber) override;
|
||||
virtual uint32_t GetLineNumber() override;
|
||||
|
||||
enum RelValue {
|
||||
ePREFETCH = 0x00000001,
|
||||
|
|
|
@ -247,6 +247,8 @@ support-files =
|
|||
referrer_testserver.sjs
|
||||
script_postmessages_fileList.js
|
||||
iframe_postMessages.html
|
||||
test_performance_observer.js
|
||||
performance_observer.html
|
||||
|
||||
[test_anonymousContent_api.html]
|
||||
[test_anonymousContent_append_after_reflow.html]
|
||||
|
@ -805,6 +807,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
|
|||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
|
||||
[test_window_define_nonconfigurable.html]
|
||||
[test_root_iframe.html]
|
||||
[test_performance_observer.html]
|
||||
[test_performance_user_timing.html]
|
||||
[test_bug1126851.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for performance observer</title>
|
||||
<script>
|
||||
'use strict';
|
||||
[
|
||||
"async_test", "test", "setup",
|
||||
"assert_true", "assert_equals", "assert_array_equals",
|
||||
"assert_throws"
|
||||
].forEach(func => {
|
||||
window[func] = opener[func].bind(opener);
|
||||
});
|
||||
function done() {
|
||||
opener.add_completion_callback(() => {
|
||||
self.close();
|
||||
});
|
||||
opener.done();
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src="test_performance_observer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
function promiseXHR(aUrl) {
|
||||
return new Promise(resolve => {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onload = resolve;
|
||||
xmlhttp.open("get", aUrl, true);
|
||||
xmlhttp.send();
|
||||
});
|
||||
}
|
||||
|
||||
async_test(t => {
|
||||
performance.clearResourceTimings();
|
||||
|
||||
var observedEntryList;
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
observedEntryList = list;
|
||||
});
|
||||
observer.observe({entryTypes: ['resource']});
|
||||
|
||||
assert_equals(observedEntries.length, 0);
|
||||
|
||||
promiseXHR("test-data.json").then(t.step_func_done(() => {
|
||||
assert_equals(observedEntries.length, 1);
|
||||
assert_array_equals(observedEntries,
|
||||
performance.getEntriesByType("resource"),
|
||||
"Observed 'resource' entries should equal to entries obtained by getEntriesByType.");
|
||||
|
||||
// getEntries filtering tests
|
||||
assert_array_equals(observedEntryList.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}),
|
||||
performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"),
|
||||
"getEntries with name filter should return correct results.");
|
||||
assert_array_equals(observedEntryList.getEntries({entryType: "resource"}),
|
||||
performance.getEntriesByType("resource"),
|
||||
"getEntries with entryType filter should return correct results.");
|
||||
assert_array_equals(observedEntryList.getEntries({initiatorType: "xmlhttprequest"}),
|
||||
performance.getEntriesByType("resource"),
|
||||
"getEntries with initiatorType filter should return correct results.");
|
||||
assert_array_equals(observedEntryList.getEntries({initiatorType: "link"}),
|
||||
[],
|
||||
"getEntries with non-existent initiatorType filter should return an empty array.");
|
||||
}));
|
||||
}, "resource-timing test");
|
||||
|
||||
done();
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for performance observer</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
'use strict';
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
|
||||
function() {
|
||||
window.open("performance_observer.html");
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,240 @@
|
|||
setup({ explicit_done: true });
|
||||
|
||||
test(t => {
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
new PerformanceObserver();
|
||||
}, "PerformanceObserver constructor should throw TypeError if no argument is specified.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
new PerformanceObserver({});
|
||||
}, "PerformanceObserver constructor should throw TypeError if the argument is not a function.");
|
||||
}, "Test that PerformanceObserver constructor throws exception");
|
||||
|
||||
test(t => {
|
||||
var observer = new PerformanceObserver(() => {
|
||||
});
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe();
|
||||
}, "observe() should throw TypeError exception if no option specified.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ unsupportedAttribute: "unsupported" });
|
||||
}, "obsrve() should throw TypeError exception if the option has no 'entryTypes' attribute.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: [] });
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute is an empty sequence.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: null });
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute is null.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: ["invalid"]});
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute value is invalid.");
|
||||
}, "Test that PerformanceObserver.observe throws exception");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
|
||||
assert_equals(observedEntries.length, 0,
|
||||
"User timing entries should never be observed.");
|
||||
assert_equals(performance.getEntriesByType("mark").length, 0,
|
||||
"There should be no 'mark' entries.");
|
||||
assert_equals(performance.getEntriesByType("measure").length, 0,
|
||||
"There should be no 'measure' entries.");
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
assert_equals(observedEntries.length, 3,
|
||||
"There should be three observed entries.");
|
||||
|
||||
var markEntries = observedEntries.filter(entry => {
|
||||
return entry.entryType == "mark";
|
||||
});
|
||||
assert_array_equals(markEntries, performance.getEntriesByType("mark"),
|
||||
"Observed 'mark' entries should equal to entries obtained by getEntriesByType.");
|
||||
|
||||
var measureEntries = observedEntries.filter(entry => {
|
||||
return entry.entryType == "measure";
|
||||
});
|
||||
assert_array_equals(measureEntries, performance.getEntriesByType("measure"),
|
||||
"Observed 'measure' entries should equal to entries obtained by getEntriesByType.");
|
||||
}, "Test for user-timing with PerformanceObserver");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
|
||||
observer.disconnect();
|
||||
|
||||
assert_equals(observedEntries.length, 0,
|
||||
"User timing entries should be never observed.");
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
assert_equals(performance.getEntriesByType("mark").length, 2);
|
||||
assert_equals(performance.getEntriesByType("measure").length, 1);
|
||||
|
||||
assert_equals(observedEntries.length, 0,
|
||||
"User timing entries should be never observed after disconnecting observer.");
|
||||
}, "Nothing should be notified after disconnecting observer");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntryList;
|
||||
var observer = new PerformanceObserver(list => {
|
||||
observedEntryList = list;
|
||||
});
|
||||
observer.observe({entryTypes: ['mark']});
|
||||
|
||||
performance.mark("test");
|
||||
assert_array_equals(observedEntryList.getEntries({"entryType": "mark"}),
|
||||
performance.getEntriesByType("mark"),
|
||||
"getEntries with entryType filter should return correct results.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({"name": "test"}),
|
||||
performance.getEntriesByName("test"),
|
||||
"getEntries with name filter should return correct results.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({"name": "test",
|
||||
"entryType": "mark"}),
|
||||
performance.getEntriesByName("test"),
|
||||
"getEntries with name and entryType filter should return correct results.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({"name": "invalid"}),
|
||||
[],
|
||||
"getEntries with non-existent name filter should return an empty array.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({"name": "test",
|
||||
"entryType": "measure"}),
|
||||
[],
|
||||
"getEntries with name filter and non-existent entryType should return an empty array.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({"name": "invalid",
|
||||
"entryType": "mark"}),
|
||||
[],
|
||||
"getEntries with non-existent name and entryType filter should return an empty array.");
|
||||
|
||||
assert_array_equals(observedEntryList.getEntries({initiatorType: "xmlhttprequest"}),
|
||||
[],
|
||||
"getEntries with initiatorType filter should return an empty array.");
|
||||
}, "Test for PerformanceObserverEntryList.getEntries");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntryList;
|
||||
var observer = new PerformanceObserver(list => {
|
||||
observedEntryList = list;
|
||||
});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test");
|
||||
assert_array_equals(observedEntryList.getEntriesByType("mark"),
|
||||
performance.getEntriesByType("mark"));
|
||||
|
||||
performance.measure("test-measure", "test", "test");
|
||||
assert_array_equals(observedEntryList.getEntriesByType("measure"),
|
||||
performance.getEntriesByType("measure"));
|
||||
}, "Test for PerformanceObserverEntryList.getEntriesByType");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntryList;
|
||||
var observer = new PerformanceObserver(list => {
|
||||
observedEntryList = list;
|
||||
});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test");
|
||||
assert_array_equals(observedEntryList.getEntriesByName("test"),
|
||||
performance.getEntriesByName("test"));
|
||||
|
||||
performance.measure("test-measure", "test", "test");
|
||||
assert_array_equals(observedEntryList.getEntriesByName("test-measure"),
|
||||
performance.getEntriesByName("test-measure"));
|
||||
}, "Test for PerformanceObserverEntryList.getEntriesByName");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
});
|
||||
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
assert_equals(observedEntries.length, 3,
|
||||
"Observed user timing entries should have only three entries.");
|
||||
}, "Test that invoking observe method twice affects nothing");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
});
|
||||
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
observer.observe({entryTypes: ['mark']});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
assert_equals(observedEntries.length, 2,
|
||||
"Observed user timing entries should have only two entries.");
|
||||
}, "Test that observing filter is replaced by a new filter");
|
||||
|
||||
test(t => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observedEntries = [];
|
||||
var observer = new PerformanceObserver(list => {
|
||||
list.getEntries().forEach(entry => observedEntries.push(entry));
|
||||
});
|
||||
|
||||
observer.observe({entryTypes: ['mark']});
|
||||
observer.observe({entryTypes: ['measure']});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
assert_equals(observedEntries.length, 1,
|
||||
"Observed user timing entries should have only 1 entries.");
|
||||
}, "Test that observing filter is replaced by a new filter");
|
|
@ -672,6 +672,7 @@ PopulateCapFallbackQueue(const SurfaceCaps& baseCaps,
|
|||
static bool
|
||||
CreateOffscreen(GLContext* gl, const WebGLContextOptions& options,
|
||||
const nsCOMPtr<nsIGfxInfo>& gfxInfo, WebGLContext* webgl,
|
||||
layers::LayersBackend layersBackend,
|
||||
layers::ISurfaceAllocator* surfAllocator)
|
||||
{
|
||||
SurfaceCaps baseCaps;
|
||||
|
@ -689,7 +690,7 @@ CreateOffscreen(GLContext* gl, const WebGLContextOptions& options,
|
|||
|
||||
if (gl->IsANGLE() ||
|
||||
(gl->GetContextType() == GLContextType::GLX &&
|
||||
gfxPlatform::GetPlatform()->GetCompositorBackend() == LayersBackend::LAYERS_OPENGL))
|
||||
layersBackend == LayersBackend::LAYERS_OPENGL))
|
||||
{
|
||||
// We can't use no-alpha formats on ANGLE yet because of:
|
||||
// https://code.google.com/p/angleproject/issues/detail?id=764
|
||||
|
@ -763,7 +764,8 @@ WebGLContext::CreateOffscreenGL(bool forceEnabled)
|
|||
if (!gl)
|
||||
break;
|
||||
|
||||
if (!CreateOffscreen(gl, mOptions, gfxInfo, this, surfAllocator))
|
||||
if (!CreateOffscreen(gl, mOptions, gfxInfo, this,
|
||||
GetCompositorBackendType(), surfAllocator))
|
||||
break;
|
||||
|
||||
if (!InitAndValidateGL())
|
||||
|
@ -1292,6 +1294,17 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
|||
return canvasLayer.forget();
|
||||
}
|
||||
|
||||
layers::LayersBackend
|
||||
WebGLContext::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
}
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
|
||||
{
|
||||
|
|
|
@ -363,6 +363,8 @@ public:
|
|||
return IsContextLost() ? 0 : mHeight;
|
||||
}
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const;
|
||||
|
||||
void
|
||||
GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
|
||||
|
||||
|
|
|
@ -675,10 +675,10 @@ HangMonitoredProcess::GetHangType(uint32_t* aHangType)
|
|||
*aHangType = PLUGIN_HANG;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,8 +197,7 @@ DecodedStreamData::SetPlaying(bool aPlaying)
|
|||
class OutputStreamListener : public MediaStreamListener {
|
||||
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
|
||||
public:
|
||||
OutputStreamListener(DecodedStream* aDecodedStream, MediaStream* aStream)
|
||||
: mDecodedStream(aDecodedStream), mStream(aStream) {}
|
||||
explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {}
|
||||
|
||||
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
|
||||
{
|
||||
|
@ -212,37 +211,142 @@ public:
|
|||
void Forget()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mDecodedStream = nullptr;
|
||||
mOwner = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void DoNotifyFinished()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mDecodedStream) {
|
||||
if (mOwner) {
|
||||
// Remove the finished stream so it won't block the decoded stream.
|
||||
mDecodedStream->Remove(mStream);
|
||||
mOwner->Remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Main thread only
|
||||
DecodedStream* mDecodedStream;
|
||||
nsRefPtr<MediaStream> mStream;
|
||||
OutputStreamData* mOwner;
|
||||
};
|
||||
|
||||
OutputStreamData::~OutputStreamData()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mListener->Forget();
|
||||
// Break the connection to the input stream if necessary.
|
||||
if (mPort) {
|
||||
mPort->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream)
|
||||
OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
|
||||
{
|
||||
mOwner = aOwner;
|
||||
mStream = aStream;
|
||||
mListener = new OutputStreamListener(aDecodedStream, aStream);
|
||||
mListener = new OutputStreamListener(this);
|
||||
aStream->AddListener(mListener);
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamData::Connect(MediaStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mPort, "Already connected?");
|
||||
MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
|
||||
|
||||
// The output stream must stay in sync with the input stream, so if
|
||||
// either stream is blocked, we block the other.
|
||||
mPort = mStream->AllocateInputPort(aStream,
|
||||
MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||
// Unblock the output stream now. The input stream is responsible for
|
||||
// controlling blocking from now on.
|
||||
mStream->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
|
||||
bool
|
||||
OutputStreamData::Disconnect()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// During cycle collection, DOMMediaStream can be destroyed and send
|
||||
// its Destroy message before this decoder is destroyed. So we have to
|
||||
// be careful not to send any messages after the Destroy().
|
||||
if (mStream->IsDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disconnect the existing port if necessary.
|
||||
if (mPort) {
|
||||
mPort->Destroy();
|
||||
mPort = nullptr;
|
||||
}
|
||||
// Block the stream again. It will be unlocked when connecting
|
||||
// to the input stream.
|
||||
mStream->ChangeExplicitBlockerCount(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamData::Remove()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mOwner->Remove(mStream);
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Ensure that aStream finishes the moment mDecodedStream does.
|
||||
if (aFinishWhenEnded) {
|
||||
aStream->SetAutofinish(true);
|
||||
}
|
||||
|
||||
OutputStreamData* p = mStreams.AppendElement();
|
||||
p->Init(this, aStream);
|
||||
|
||||
// Connect to the input stream if we have one. Otherwise the output stream
|
||||
// will be connected in Connect().
|
||||
if (mInputStream) {
|
||||
p->Connect(mInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamManager::Remove(MediaStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
|
||||
if (mStreams[i].Equals(aStream)) {
|
||||
mStreams.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamManager::Connect(MediaStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mInputStream = aStream;
|
||||
for (auto&& os : mStreams) {
|
||||
os.Connect(aStream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OutputStreamManager::Disconnect()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mInputStream = nullptr;
|
||||
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
|
||||
if (!mStreams[i].Disconnect()) {
|
||||
// Probably the DOMMediaStream was GCed. Clean up.
|
||||
mStreams.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
|
||||
MediaQueue<MediaData>& aVideoQueue)
|
||||
: mMonitor("DecodedStream::mMonitor")
|
||||
|
@ -289,27 +393,7 @@ DecodedStream::DestroyData()
|
|||
return;
|
||||
}
|
||||
|
||||
// All streams are having their SourceMediaStream disconnected, so they
|
||||
// need to be explicitly blocked again.
|
||||
auto& outputStreams = OutputStreams();
|
||||
for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) {
|
||||
OutputStreamData& os = outputStreams[i];
|
||||
// Explicitly remove all existing ports.
|
||||
// This is not strictly necessary but it's good form.
|
||||
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
|
||||
os.mPort->Destroy();
|
||||
os.mPort = nullptr;
|
||||
// During cycle collection, nsDOMMediaStream can be destroyed and send
|
||||
// its Destroy message before this decoder is destroyed. So we have to
|
||||
// be careful not to send any messages after the Destroy().
|
||||
if (os.mStream->IsDestroyed()) {
|
||||
// Probably the DOM MediaStream was GCed. Clean up.
|
||||
outputStreams.RemoveElementAt(i);
|
||||
} else {
|
||||
os.mStream->ChangeExplicitBlockerCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
mOutputStreamManager.Disconnect();
|
||||
mData = nullptr;
|
||||
}
|
||||
|
||||
|
@ -328,7 +412,7 @@ DecodedStream::RecreateData(MediaStreamGraph* aGraph)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT((aGraph && !mData && OutputStreams().IsEmpty()) || // first time
|
||||
MOZ_ASSERT((aGraph && !mData && !HasConsumers()) || // first time
|
||||
(!aGraph && mData)); // 2nd time and later
|
||||
|
||||
if (!aGraph) {
|
||||
|
@ -338,31 +422,16 @@ DecodedStream::RecreateData(MediaStreamGraph* aGraph)
|
|||
DestroyData();
|
||||
mData.reset(new DecodedStreamData(source, mPlaying));
|
||||
|
||||
// Note that the delay between removing ports in DestroyDecodedStream
|
||||
// Note that the delay between removing ports in DestroyData
|
||||
// and adding new ones won't cause a glitch since all graph operations
|
||||
// between main-thread stable states take effect atomically.
|
||||
auto& outputStreams = OutputStreams();
|
||||
for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) {
|
||||
OutputStreamData& os = outputStreams[i];
|
||||
MOZ_ASSERT(!os.mStream->IsDestroyed(), "Should've been removed in DestroyData()");
|
||||
Connect(&os);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<OutputStreamData>&
|
||||
DecodedStream::OutputStreams()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
return mOutputStreams;
|
||||
mOutputStreamManager.Connect(mData->mStream);
|
||||
}
|
||||
|
||||
bool
|
||||
DecodedStream::HasConsumers() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
return mOutputStreams.IsEmpty();
|
||||
return !mOutputStreamManager.IsEmpty();
|
||||
}
|
||||
|
||||
ReentrantMonitor&
|
||||
|
@ -371,22 +440,6 @@ DecodedStream::GetReentrantMonitor() const
|
|||
return mMonitor;
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::Connect(OutputStreamData* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
NS_ASSERTION(!aStream->mPort, "Already connected?");
|
||||
|
||||
// The output stream must stay in sync with the decoded stream, so if
|
||||
// either stream is blocked, we block the other.
|
||||
aStream->mPort = aStream->mStream->AllocateInputPort(mData->mStream,
|
||||
MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||
// Unblock the output stream now. While it's connected to DecodedStream,
|
||||
// DecodedStream is responsible for controlling blocking.
|
||||
aStream->mStream->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
|
||||
{
|
||||
|
@ -397,34 +450,13 @@ DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
|
|||
RecreateData(aStream->Graph());
|
||||
}
|
||||
|
||||
OutputStreamData* os = OutputStreams().AppendElement();
|
||||
os->Init(this, aStream);
|
||||
Connect(os);
|
||||
if (aFinishWhenEnded) {
|
||||
// Ensure that aStream finishes the moment mDecodedStream does.
|
||||
aStream->SetAutofinish(true);
|
||||
}
|
||||
mOutputStreamManager.Add(aStream, aFinishWhenEnded);
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::Remove(MediaStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
auto& streams = OutputStreams();
|
||||
for (int32_t i = streams.Length() - 1; i >= 0; --i) {
|
||||
auto& os = streams[i];
|
||||
MediaStream* p = os.mStream.get();
|
||||
if (p == aStream) {
|
||||
if (os.mPort) {
|
||||
os.mPort->Destroy();
|
||||
os.mPort = nullptr;
|
||||
}
|
||||
streams.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mOutputStreamManager.Remove(aStream);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -27,6 +27,7 @@ class MediaInputPort;
|
|||
class MediaStream;
|
||||
class MediaStreamGraph;
|
||||
class OutputStreamListener;
|
||||
class OutputStreamManager;
|
||||
class ProcessedMediaStream;
|
||||
class ReentrantMonitor;
|
||||
|
||||
|
@ -39,13 +40,55 @@ class Image;
|
|||
class OutputStreamData {
|
||||
public:
|
||||
~OutputStreamData();
|
||||
void Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream);
|
||||
void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
|
||||
|
||||
// Connect mStream to the input stream.
|
||||
void Connect(MediaStream* aStream);
|
||||
// Disconnect mStream from its input stream.
|
||||
// Return false is mStream is already destroyed, otherwise true.
|
||||
bool Disconnect();
|
||||
// Called by OutputStreamListener to remove self from the output streams
|
||||
// managed by OutputStreamManager.
|
||||
void Remove();
|
||||
// Return true if aStream points to the same object as mStream.
|
||||
// Used by OutputStreamManager to remove an output stream.
|
||||
bool Equals(MediaStream* aStream)
|
||||
{
|
||||
return mStream == aStream;
|
||||
}
|
||||
|
||||
private:
|
||||
OutputStreamManager* mOwner;
|
||||
nsRefPtr<ProcessedMediaStream> mStream;
|
||||
// mPort connects DecodedStreamData::mStream to our mStream.
|
||||
// mPort connects our mStream to an input stream.
|
||||
nsRefPtr<MediaInputPort> mPort;
|
||||
nsRefPtr<OutputStreamListener> mListener;
|
||||
};
|
||||
|
||||
class OutputStreamManager {
|
||||
public:
|
||||
// Add the output stream to the collection.
|
||||
void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
|
||||
// Remove the output stream from the collection.
|
||||
void Remove(MediaStream* aStream);
|
||||
// Return true if the collection empty.
|
||||
bool IsEmpty() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mStreams.IsEmpty();
|
||||
}
|
||||
// Connect all output streams in the collection to the input stream.
|
||||
void Connect(MediaStream* aStream);
|
||||
// Disconnect all output streams from the input stream.
|
||||
void Disconnect();
|
||||
|
||||
private:
|
||||
// Keep the input stream so we can connect the output streams that
|
||||
// are added after Connect().
|
||||
nsRefPtr<MediaStream> mInputStream;
|
||||
nsTArray<OutputStreamData> mStreams;
|
||||
};
|
||||
|
||||
class DecodedStream {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
|
||||
public:
|
||||
|
@ -84,8 +127,6 @@ protected:
|
|||
private:
|
||||
ReentrantMonitor& GetReentrantMonitor() const;
|
||||
void RecreateData(MediaStreamGraph* aGraph);
|
||||
void Connect(OutputStreamData* aStream);
|
||||
nsTArray<OutputStreamData>& OutputStreams();
|
||||
void InitTracks();
|
||||
void AdvanceTracks();
|
||||
void SendAudio(double aVolume, bool aIsSameOrigin);
|
||||
|
@ -93,7 +134,7 @@ private:
|
|||
|
||||
UniquePtr<DecodedStreamData> mData;
|
||||
// Data about MediaStreams that are being fed by the decoder.
|
||||
nsTArray<OutputStreamData> mOutputStreams;
|
||||
OutputStreamManager mOutputStreamManager;
|
||||
|
||||
// TODO: This is a temp solution to get rid of decoder monitor on the main
|
||||
// thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
|
||||
|
|
|
@ -619,9 +619,9 @@ void MediaDecoder::CallSeek(const SeekTarget& aTarget)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->OwnerThread(),
|
||||
mDecoderStateMachine.get(), __func__,
|
||||
&MediaDecoderStateMachine::Seek, aTarget)
|
||||
mSeekRequest.Begin(InvokeAsync(mDecoderStateMachine->OwnerThread(),
|
||||
mDecoderStateMachine.get(), __func__,
|
||||
&MediaDecoderStateMachine::Seek, aTarget)
|
||||
->Then(AbstractThread::MainThread(), __func__, this,
|
||||
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
|
||||
}
|
||||
|
|
|
@ -719,8 +719,8 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
|||
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
|
||||
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
|
||||
nsRefPtr<MediaDecoderStateMachine> self = this;
|
||||
WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::WaitForData, aType)
|
||||
WaitRequestRef(aType).Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::WaitForData, aType)
|
||||
->Then(OwnerThread(), __func__,
|
||||
[self] (MediaData::Type aType) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
|
@ -1277,7 +1277,7 @@ void MediaDecoderStateMachine::Shutdown()
|
|||
|
||||
// Put a task in the decode queue to shutdown the reader.
|
||||
// the queue to spin down.
|
||||
ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown)
|
||||
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::FinishShutdown,
|
||||
&MediaDecoderStateMachine::FinishShutdown);
|
||||
|
@ -1578,9 +1578,9 @@ MediaDecoderStateMachine::InitiateSeek()
|
|||
|
||||
// Do the seek.
|
||||
nsRefPtr<MediaDecoderStateMachine> self = this;
|
||||
mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
|
||||
Duration().ToMicroseconds())
|
||||
mSeekRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
|
||||
Duration().ToMicroseconds())
|
||||
->Then(OwnerThread(), __func__,
|
||||
[self] (int64_t) -> void {
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
|
@ -1648,15 +1648,15 @@ MediaDecoderStateMachine::RequestAudioData()
|
|||
AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
|
||||
|
||||
if (mSentFirstFrameLoadedEvent) {
|
||||
mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
|
||||
__func__, &MediaDecoderReader::RequestAudioData)
|
||||
mAudioDataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(),
|
||||
__func__, &MediaDecoderReader::RequestAudioData)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded));
|
||||
} else {
|
||||
mAudioDataRequest.Begin(
|
||||
ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestAudioData)
|
||||
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestAudioData)
|
||||
->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
|
||||
&StartTimeRendezvous::ProcessFirstSample<AudioDataPromise>,
|
||||
&StartTimeRendezvous::FirstSampleRejected<AudioData>)
|
||||
|
@ -1732,17 +1732,17 @@ MediaDecoderStateMachine::RequestVideoData()
|
|||
|
||||
if (mSentFirstFrameLoadedEvent) {
|
||||
mVideoDataRequest.Begin(
|
||||
ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime, forceDecodeAhead)
|
||||
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime, forceDecodeAhead)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
} else {
|
||||
mVideoDataRequest.Begin(
|
||||
ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime, forceDecodeAhead)
|
||||
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime, forceDecodeAhead)
|
||||
->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
|
||||
&StartTimeRendezvous::ProcessFirstSample<VideoDataPromise>,
|
||||
&StartTimeRendezvous::FirstSampleRejected<VideoData>)
|
||||
|
@ -2278,8 +2278,8 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||
DECODER_LOG("Dispatching AsyncReadMetadata");
|
||||
// Set mode to METADATA since we are about to read metadata.
|
||||
mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
|
||||
mMetadataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMetadataRead,
|
||||
&MediaDecoderStateMachine::OnMetadataNotRead));
|
||||
|
|
|
@ -280,7 +280,12 @@ MediaFormatReader::AsyncReadMetadata()
|
|||
return p;
|
||||
}
|
||||
MOZ_ASSERT(!mDecodersInitRequest.Exists());
|
||||
EnsureDecodersInitialized();
|
||||
if (EnsureDecodersInitialized()) {
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = nullptr;
|
||||
mMetadataPromise.Resolve(metadata, __func__);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/CDMProxy.h"
|
||||
#endif
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsMimeTypes.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
@ -141,6 +142,12 @@ MP4Decoder::CanHandleMediaType(const nsACString& aType,
|
|||
aOutContainsMP3));
|
||||
}
|
||||
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
if (aType.EqualsASCII(VIDEO_3GPP)) {
|
||||
return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!aType.EqualsASCII("video/mp4") ||
|
||||
!MP4Decoder::CanCreateH264Decoder()) {
|
||||
return false;
|
||||
|
|
|
@ -873,6 +873,9 @@ GStreamerReader::Seek(int64_t aTarget, int64_t aEndTime)
|
|||
media::TimeIntervals GStreamerReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
if (!HaveStartTime()) {
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
media::TimeIntervals buffered;
|
||||
if (!mInfo.HasValidMedia()) {
|
||||
return buffered;
|
||||
|
@ -885,9 +888,10 @@ media::TimeIntervals GStreamerReader::GetBuffered()
|
|||
nsTArray<MediaByteRange> ranges;
|
||||
resource->GetCachedRanges(ranges);
|
||||
|
||||
if (resource->IsDataCachedToEndOfResource(0) && mDuration.Ref().isSome()) {
|
||||
if (resource->IsDataCachedToEndOfResource(0)) {
|
||||
/* fast path for local or completely cached files */
|
||||
gint64 duration = mDuration.Ref().ref().ToMicroseconds();
|
||||
gint64 duration =
|
||||
mDuration.Ref().refOr(media::TimeUnit::FromMicroseconds(0)).ToMicroseconds();
|
||||
LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]",
|
||||
(double) duration / GST_MSECOND, GetDataLength());
|
||||
buffered +=
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "MP4Demuxer.h"
|
||||
#include "MP4Stream.h"
|
||||
#include "MozPromise.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "MediaDataDemuxer.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
|
|
|
@ -35,8 +35,8 @@ MediaSourceDemuxer::MediaSourceDemuxer()
|
|||
nsRefPtr<MediaSourceDemuxer::InitPromise>
|
||||
MediaSourceDemuxer::Init()
|
||||
{
|
||||
return ProxyMediaCall(GetTaskQueue(), this, __func__,
|
||||
&MediaSourceDemuxer::AttemptInit);
|
||||
return InvokeAsync(GetTaskQueue(), this, __func__,
|
||||
&MediaSourceDemuxer::AttemptInit);
|
||||
}
|
||||
|
||||
nsRefPtr<MediaSourceDemuxer::InitPromise>
|
||||
|
@ -272,16 +272,16 @@ nsRefPtr<MediaSourceTrackDemuxer::SeekPromise>
|
|||
MediaSourceTrackDemuxer::Seek(media::TimeUnit aTime)
|
||||
{
|
||||
MOZ_ASSERT(mParent, "Called after BreackCycle()");
|
||||
return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoSeek, aTime);
|
||||
return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoSeek, aTime);
|
||||
}
|
||||
|
||||
nsRefPtr<MediaSourceTrackDemuxer::SamplesPromise>
|
||||
MediaSourceTrackDemuxer::GetSamples(int32_t aNumSamples)
|
||||
{
|
||||
MOZ_ASSERT(mParent, "Called after BreackCycle()");
|
||||
return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoGetSamples, aNumSamples);
|
||||
return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoGetSamples, aNumSamples);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -312,9 +312,9 @@ MediaSourceTrackDemuxer::GetNextRandomAccessPoint(media::TimeUnit* aTime)
|
|||
nsRefPtr<MediaSourceTrackDemuxer::SkipAccessPointPromise>
|
||||
MediaSourceTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
|
||||
{
|
||||
return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint,
|
||||
aTimeThreshold);
|
||||
return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
|
||||
&MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint,
|
||||
aTimeThreshold);
|
||||
}
|
||||
|
||||
int64_t
|
||||
|
|
|
@ -268,9 +268,9 @@ TrackBuffer::BufferAppend()
|
|||
|
||||
nsRefPtr<TrackBuffer> self = this;
|
||||
|
||||
ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
mLastAppendRange, /* aNotifyParent */ true)
|
||||
InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
mLastAppendRange, /* aNotifyParent */ true)
|
||||
->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
|
||||
[self] {
|
||||
self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
|
||||
|
@ -942,9 +942,9 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
MSE_DEBUG("Reader %p activated",
|
||||
aDecoder->GetReader());
|
||||
nsRefPtr<TrackBuffer> self = this;
|
||||
ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
Interval<int64_t>(), /* aNotifyParent */ true)
|
||||
InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
Interval<int64_t>(), /* aNotifyParent */ true)
|
||||
->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
|
||||
[self] {
|
||||
self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
|
||||
|
@ -1274,9 +1274,9 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
|||
|
||||
// Make sure our buffered ranges got updated before resolving promise.
|
||||
nsRefPtr<TrackBuffer> self = this;
|
||||
ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
Interval<int64_t>(), /* aNotifyParent */ false)
|
||||
InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
|
||||
&TrackBuffer::UpdateBufferedRanges,
|
||||
Interval<int64_t>(), /* aNotifyParent */ false)
|
||||
->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
|
||||
[self] {
|
||||
self->mRangeRemovalPromise.ResolveIfExists(true, __func__);
|
||||
|
|
|
@ -156,8 +156,8 @@ TrackBuffersManager::BufferAppend()
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
return ProxyMediaCall(GetTaskQueue(), this,
|
||||
__func__, &TrackBuffersManager::InitSegmentParserLoop);
|
||||
return InvokeAsync(GetTaskQueue(), this,
|
||||
__func__, &TrackBuffersManager::InitSegmentParserLoop);
|
||||
}
|
||||
|
||||
// Abort any pending AppendData.
|
||||
|
@ -211,9 +211,9 @@ TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
|||
|
||||
mEnded = false;
|
||||
|
||||
return ProxyMediaCall(GetTaskQueue(), this, __func__,
|
||||
&TrackBuffersManager::CodedFrameRemovalWithPromise,
|
||||
TimeInterval(aStart, aEnd));
|
||||
return InvokeAsync(GetTaskQueue(), this, __func__,
|
||||
&TrackBuffersManager::CodedFrameRemovalWithPromise,
|
||||
TimeInterval(aStart, aEnd));
|
||||
}
|
||||
|
||||
TrackBuffersManager::EvictDataResult
|
||||
|
|
|
@ -152,7 +152,6 @@ EXPORTS += [
|
|||
|
||||
EXPORTS.mozilla += [
|
||||
'MediaManager.h',
|
||||
'MozPromise.h',
|
||||
'StateMirroring.h',
|
||||
'StateWatching.h',
|
||||
]
|
||||
|
|
|
@ -34,8 +34,8 @@ MediaDataDecoderProxy::Init()
|
|||
{
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
return ProxyMediaCall(mProxyThreadWrapper, this, __func__,
|
||||
&MediaDataDecoderProxy::InternalInit);
|
||||
return InvokeAsync(mProxyThreadWrapper, this, __func__,
|
||||
&MediaDataDecoderProxy::InternalInit);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -737,6 +737,8 @@ skip-if = toolkit == 'gonk' # bug 1128845
|
|||
skip-if = (toolkit == 'android' && processor == 'x86') #bug 845162
|
||||
[test_playback_rate_playpause.html]
|
||||
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_playback_reactivate.html]
|
||||
#skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_played.html]
|
||||
[test_preload_actions.html]
|
||||
[test_preload_attribute.html]
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test playback with dormant of media files that should play OK</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/* This testcase wants to test a video element's playback is not break
|
||||
by dormant.
|
||||
When the metadata is loaded, we remove the video element to trigger dormant.
|
||||
Then set a timer to append the video element back and play it.
|
||||
Test pass if the video plays to the end.
|
||||
*/
|
||||
|
||||
// longer timeout for slow platforms
|
||||
if (isSlowPlatform()) {
|
||||
SimpleTest.requestLongerTimeout(1.5);
|
||||
SimpleTest.requestCompleteLog();
|
||||
}
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(test, token) {
|
||||
var v = document.createElement('video');
|
||||
v.preload = "metadata";
|
||||
v.token = token;
|
||||
|
||||
var handler = {
|
||||
"ontimeout": function() {
|
||||
Log(token, "timed out: ended=" + v.seenEnded + ", suspend=" + v.seenSuspend);
|
||||
}
|
||||
};
|
||||
manager.started(token, handler);
|
||||
|
||||
v.src = test.name;
|
||||
v.name = test.name;
|
||||
|
||||
var check = function(test, v) { return function() {
|
||||
is(test.name, v.name, test.name + ": Name should match #1");
|
||||
Log(v.token, "removeChild: " + v.name);
|
||||
document.body.removeChild(v);
|
||||
var appendAndPlayElement = function() {
|
||||
Log(v.token, "appendChild: " + v.name);
|
||||
document.body.appendChild(v);
|
||||
Log(v.token, "Element play: " + v.name);
|
||||
v.play();
|
||||
}
|
||||
setTimeout(appendAndPlayElement, 2000);
|
||||
}}(test, v);
|
||||
|
||||
var finish = function() {
|
||||
v.finished = true;
|
||||
removeNodeAndSource(v);
|
||||
manager.finished(v.token);
|
||||
}
|
||||
|
||||
var checkEnded = function(test, v) { return function() {
|
||||
is(test.name, v.name, test.name + ": Name should match #2");
|
||||
checkMetadata(test.name, v, test);
|
||||
is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
|
||||
ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
|
||||
ok(v.ended, test.name + " checking playback has ended");
|
||||
|
||||
finish();
|
||||
}}(test, v);
|
||||
|
||||
|
||||
v.addEventListener("loadedmetadata", check, false);
|
||||
v.addEventListener("ended", checkEnded, false);
|
||||
|
||||
document.body.appendChild(v);
|
||||
|
||||
// Debug timeouts on slow platforms.
|
||||
if (isSlowPlatform()) {
|
||||
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
|
||||
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
|
||||
"waiting", "pause"];
|
||||
function logEvent(e) {
|
||||
var v = e.target;
|
||||
Log(e.target.token, "got " + e.type);
|
||||
}
|
||||
events.forEach(function(e) {
|
||||
v.addEventListener(e, logEvent, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
manager.runTests(gSmallTests, startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1038,7 +1038,7 @@ NPError
|
|||
_destroystream(NPP npp, NPStream *pstream, NPError reason)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_write called from the wrong thread\n"));
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_destroystream called from the wrong thread\n"));
|
||||
return NPERR_INVALID_PARAM;
|
||||
}
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
|
||||
|
@ -2624,6 +2624,11 @@ NPError
|
|||
_getvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
|
||||
char **value, uint32_t *len)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getvalueforurl called from the wrong thread\n"));
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
return NPERR_INVALID_PARAM;
|
||||
}
|
||||
|
@ -2670,7 +2675,6 @@ _getvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
|
|||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// Fall through and return an error...
|
||||
;
|
||||
|
@ -2683,6 +2687,11 @@ NPError
|
|||
_setvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
|
||||
const char *value, uint32_t len)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setvalueforurl called from the wrong thread\n"));
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
return NPERR_INVALID_PARAM;
|
||||
}
|
||||
|
@ -2694,7 +2703,7 @@ _setvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
|
|||
switch (variable) {
|
||||
case NPNURLVCookie:
|
||||
{
|
||||
if (!url || !value || (0 >= len))
|
||||
if (!value || 0 == len)
|
||||
return NPERR_INVALID_PARAM;
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
@ -2739,6 +2748,11 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host,
|
|||
char **username, uint32_t *ulen, char **password,
|
||||
uint32_t *plen)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getauthenticationinfo called from the wrong thread\n"));
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
if (!instance || !protocol || !host || !scheme || !realm || !username ||
|
||||
!ulen || !password || !plen)
|
||||
return NPERR_INVALID_PARAM;
|
||||
|
@ -2795,6 +2809,11 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host,
|
|||
uint32_t
|
||||
_scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc timerFunc)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_scheduletimer called from the wrong thread\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
|
||||
if (!inst)
|
||||
return 0;
|
||||
|
@ -2805,6 +2824,11 @@ _scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc t
|
|||
void
|
||||
_unscheduletimer(NPP instance, uint32_t timerID)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_unscheduletimer called from the wrong thread\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Sometimes Flash calls this with a dead NPP instance. Ensure the one we have
|
||||
// here is valid and maps to a nsNPAPIPluginInstance.
|
||||
|
@ -2821,6 +2845,11 @@ _unscheduletimer(NPP instance, uint32_t timerID)
|
|||
NPError
|
||||
_popupcontextmenu(NPP instance, NPMenu* menu)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_popupcontextmenu called from the wrong thread\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
|
||||
|
||||
|
@ -2870,6 +2899,11 @@ _popupcontextmenu(NPP instance, NPMenu* menu)
|
|||
NPBool
|
||||
_convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
|
||||
if (!inst)
|
||||
return false;
|
||||
|
@ -2880,6 +2914,11 @@ _convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace so
|
|||
void
|
||||
_urlredirectresponse(NPP instance, void* notifyData, NPBool allow)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
|
||||
if (!inst) {
|
||||
return;
|
||||
|
|
|
@ -3564,7 +3564,6 @@ nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
|
|||
return rv;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -3610,10 +3610,9 @@ PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect)
|
|||
#elif defined(XP_WIN)
|
||||
if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_X11) || defined(XP_WIN)
|
||||
if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType())
|
||||
return false;
|
||||
|
||||
|
@ -3639,6 +3638,9 @@ PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect)
|
|||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -42,5 +42,5 @@ LOCAL_INCLUDES += [
|
|||
]
|
||||
|
||||
if CONFIG['GNU_CC']:
|
||||
CFLAGS += ['-Wshadow']
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
CFLAGS += ['-Wshadow', '-Wformat-security']
|
||||
CXXFLAGS += ['-Wshadow', '-Wformat-security']
|
||||
|
|
|
@ -465,6 +465,9 @@ nsresult
|
|||
DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
|
||||
const nsString& aValue, nsString& aOld)
|
||||
{
|
||||
// Size of the cache that will change after this action.
|
||||
int64_t delta = 0;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
|
@ -475,11 +478,14 @@ DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
|
|||
Data& data = DataSet(aStorage);
|
||||
if (!data.mKeys.Get(aKey, &aOld)) {
|
||||
SetDOMStringToNull(aOld);
|
||||
|
||||
// We only consider key size if the key doesn't exist before.
|
||||
delta += static_cast<int64_t>(aKey.Length());
|
||||
}
|
||||
|
||||
// Check the quota first
|
||||
const int64_t delta = static_cast<int64_t>(aValue.Length()) -
|
||||
static_cast<int64_t>(aOld.Length());
|
||||
delta += static_cast<int64_t>(aValue.Length()) -
|
||||
static_cast<int64_t>(aOld.Length());
|
||||
|
||||
if (!ProcessUsageDelta(aStorage, delta)) {
|
||||
return NS_ERROR_DOM_QUOTA_REACHED;
|
||||
}
|
||||
|
@ -525,7 +531,8 @@ DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
|
|||
}
|
||||
|
||||
// Recalculate the cached data size
|
||||
const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
|
||||
const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
|
||||
static_cast<int64_t>(aKey.Length()));
|
||||
unused << ProcessUsageDelta(aStorage, delta);
|
||||
data.mKeys.Remove(aKey);
|
||||
|
||||
|
|
|
@ -734,7 +734,7 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MediaRecorder",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "MediaSource", linux: false, release: false},
|
||||
"MediaSource",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MediaStream",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -988,9 +988,9 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "SimpleTest", xbl: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "SourceBuffer", linux: false, release: false},
|
||||
"SourceBuffer",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "SourceBufferList", linux: false, release: false},
|
||||
"SourceBufferList",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "SpeechSynthesisErrorEvent", b2g: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -1346,7 +1346,7 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ValidityState",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "VideoPlaybackQuality", linux: false, release: false},
|
||||
"VideoPlaybackQuality",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"VideoStreamTrack",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -71,3 +71,4 @@ partial interface Performance {
|
|||
[Func="nsPerformance::IsEnabled"]
|
||||
void clearMeasures(optional DOMString measureName);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://w3c.github.io/performance-timeline/#the-performance-observer-interface
|
||||
*/
|
||||
|
||||
dictionary PerformanceObserverInit {
|
||||
required sequence<DOMString> entryTypes;
|
||||
};
|
||||
|
||||
callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer);
|
||||
|
||||
[Func="nsPerformance::IsObserverEnabled",
|
||||
Constructor(PerformanceObserverCallback callback),
|
||||
Exposed=(Window,Worker)]
|
||||
interface PerformanceObserver {
|
||||
[Throws]
|
||||
void observe(PerformanceObserverInit options);
|
||||
void disconnect();
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://w3c.github.io/performance-timeline/#the-performanceobserverentrylist-interface
|
||||
*/
|
||||
|
||||
// XXX should be moved into Performance.webidl.
|
||||
dictionary PerformanceEntryFilterOptions {
|
||||
DOMString name;
|
||||
DOMString entryType;
|
||||
DOMString initiatorType;
|
||||
};
|
||||
|
||||
[Func="nsPerformance::IsObserverEnabled", Exposed=(Window,Worker)]
|
||||
interface PerformanceObserverEntryList {
|
||||
PerformanceEntryList getEntries(optional PerformanceEntryFilterOptions filter);
|
||||
PerformanceEntryList getEntriesByType(DOMString entryType);
|
||||
PerformanceEntryList getEntriesByName(DOMString name,
|
||||
optional DOMString entryType);
|
||||
};
|
||||
|
|
@ -353,6 +353,8 @@ WEBIDL_FILES = [
|
|||
'PerformanceMark.webidl',
|
||||
'PerformanceMeasure.webidl',
|
||||
'PerformanceNavigation.webidl',
|
||||
'PerformanceObserver.webidl',
|
||||
'PerformanceObserverEntryList.webidl',
|
||||
'PerformanceResourceTiming.webidl',
|
||||
'PerformanceTiming.webidl',
|
||||
'PeriodicWave.webidl',
|
||||
|
|
|
@ -106,8 +106,10 @@ support-files =
|
|||
bug1132924_worker.js
|
||||
empty.html
|
||||
worker_performance_user_timing.js
|
||||
worker_performance_observer.js
|
||||
sharedworker_performance_user_timing.js
|
||||
referrer.sjs
|
||||
performance_observer.html
|
||||
|
||||
[test_404.html]
|
||||
[test_atob.html]
|
||||
|
@ -168,6 +170,7 @@ skip-if = buildapp == 'mulet'
|
|||
[test_onLine.html]
|
||||
skip-if = (toolkit == 'gonk' && debug) #debug-only failure
|
||||
[test_performance_user_timing.html]
|
||||
[test_performance_observer.html]
|
||||
[test_promise.html]
|
||||
[test_promise_resolved_with_string.html]
|
||||
[test_recursion.html]
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for performance observer in worker</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
[
|
||||
"async_test", "test", "setup",
|
||||
"assert_true", "assert_equals", "assert_array_equals",
|
||||
"assert_throws", "fetch_tests_from_worker"
|
||||
].forEach(func => {
|
||||
window[func] = opener[func].bind(opener);
|
||||
});
|
||||
|
||||
function done() {
|
||||
opener.add_completion_callback(() => {
|
||||
self.close();
|
||||
});
|
||||
opener.done();
|
||||
}
|
||||
|
||||
fetch_tests_from_worker(new Worker("worker_performance_observer.js"));
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for performance observer in worker</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
'use strict';
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
|
||||
function() {
|
||||
window.open("performance_observer.html");
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
importScripts(['/resources/testharness.js']);
|
||||
importScripts(['../../../dom/base/test/test_performance_observer.js']);
|
||||
|
||||
done();
|
|
@ -79,7 +79,7 @@ txLiteralExpr::toString(nsAString& aStr)
|
|||
static_cast<StringResult*>(static_cast<txAExprResult*>
|
||||
(mValue));
|
||||
char16_t ch = '\'';
|
||||
if (strRes->mValue.FindChar(ch) != kNotFound) {
|
||||
if (strRes->mValue.Contains(ch)) {
|
||||
ch = '\"';
|
||||
}
|
||||
aStr.Append(ch);
|
||||
|
|
|
@ -377,7 +377,7 @@ txExecutionState::getEvalContext()
|
|||
const txXPathNode*
|
||||
txExecutionState::retrieveDocument(const nsAString& aUri)
|
||||
{
|
||||
NS_ASSERTION(aUri.FindChar(char16_t('#')) == kNotFound,
|
||||
NS_ASSERTION(!aUri.Contains(char16_t('#')),
|
||||
"Remove the fragment.");
|
||||
|
||||
if (mDisableLoads) {
|
||||
|
|
|
@ -316,7 +316,7 @@ nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri)
|
|||
return false;
|
||||
|
||||
// return true if host does not contain a dot and is not an ip address
|
||||
return !host.IsEmpty() && host.FindChar('.') == kNotFound &&
|
||||
return !host.IsEmpty() && !host.Contains('.') &&
|
||||
PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ class SharedDIB
|
|||
public:
|
||||
typedef base::SharedMemoryHandle Handle;
|
||||
|
||||
static const uint32_t kBytesPerPixel = 4;
|
||||
|
||||
public:
|
||||
SharedDIB();
|
||||
~SharedDIB();
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace gfx {
|
|||
|
||||
static const cairo_user_data_key_t SHAREDDIB_KEY = {0};
|
||||
|
||||
static const long kBytesPerPixel = 4;
|
||||
|
||||
bool
|
||||
SharedDIBSurface::Create(HDC adc, uint32_t aWidth, uint32_t aHeight,
|
||||
bool aTransparent)
|
||||
|
@ -42,7 +40,7 @@ void
|
|||
SharedDIBSurface::InitSurface(uint32_t aWidth, uint32_t aHeight,
|
||||
bool aTransparent)
|
||||
{
|
||||
long stride = long(aWidth * kBytesPerPixel);
|
||||
long stride = long(aWidth * SharedDIB::kBytesPerPixel);
|
||||
unsigned char* data = reinterpret_cast<unsigned char*>(mSharedDIB.GetBits());
|
||||
|
||||
gfxImageFormat format = aTransparent ? gfxImageFormat::ARGB32 : gfxImageFormat::RGB24;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static const uint32_t kBytesPerPixel = 4;
|
||||
static const uint32_t kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2();
|
||||
static const uint32_t kHeaderBytes =
|
||||
(sizeof(BITMAPV4HEADER) + kByteAlign - 1) & ~(kByteAlign - 1);
|
||||
|
@ -136,6 +135,5 @@ SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
|
|
|
@ -17,12 +17,12 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
|||
'SharedDIBSurface.h',
|
||||
'SharedDIBWin.h',
|
||||
]
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'SharedDIBSurface.cpp',
|
||||
'SharedDIBWin.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'SharedDIB.cpp',
|
||||
]
|
||||
|
||||
|
@ -36,3 +36,5 @@ FINAL_LIBRARY = 'xul'
|
|||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -483,11 +483,15 @@ CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */)
|
|||
return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
|
||||
case Layer::TYPE_IMAGE:
|
||||
return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
|
||||
default:
|
||||
case Layer::TYPE_CANVAS:
|
||||
case Layer::TYPE_READBACK:
|
||||
case Layer::TYPE_SHADOW:
|
||||
case Layer::TYPE_PAINTED:
|
||||
return MakeUnique<LayerPropertiesBase>(aRoot);
|
||||
}
|
||||
|
||||
return UniquePtr<LayerPropertiesBase>(nullptr);
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
|
||||
return MakeUnique<LayerPropertiesBase>(aRoot);
|
||||
}
|
||||
|
||||
/* static */ UniquePtr<LayerProperties>
|
||||
|
|
|
@ -660,7 +660,11 @@ public:
|
|||
// Can't inline these variables due to short-circuit evaluation.
|
||||
bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
|
||||
bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
|
||||
return continueX || continueY;
|
||||
if (!continueX && !continueY) {
|
||||
mApzc.OverscrollAnimationEnding();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
|
@ -2174,17 +2178,24 @@ void AsyncPanZoomController::AcceptFling(const ParentLayerPoint& aVelocity,
|
|||
}
|
||||
CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom();
|
||||
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f "
|
||||
"predictedDelta: %f, %f position: %f, %f "
|
||||
"predictedDestination: %f, %f\n",
|
||||
this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
|
||||
(float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
|
||||
(float)mFrameMetrics.GetScrollOffset().y,
|
||||
(float)predictedDestination.x, (float)predictedDestination.y);
|
||||
controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
|
||||
predictedDestination);
|
||||
// If the fling will overscroll, don't request a fling snap, because the
|
||||
// resulting content scrollTo() would unnecessarily cancel the overscroll
|
||||
// animation.
|
||||
bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) ||
|
||||
(velocity.y * mY.GetOverscroll() >= 0));
|
||||
if (!flingWillOverscroll) {
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f "
|
||||
"predictedDelta: %f, %f position: %f, %f "
|
||||
"predictedDestination: %f, %f\n",
|
||||
this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
|
||||
(float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
|
||||
(float)mFrameMetrics.GetScrollOffset().y,
|
||||
(float)predictedDestination.x, (float)predictedDestination.y);
|
||||
controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
|
||||
predictedDestination);
|
||||
}
|
||||
}
|
||||
|
||||
StartAnimation(fling);
|
||||
|
@ -3319,5 +3330,19 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() {
|
|||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::OverscrollAnimationEnding() {
|
||||
// If we got into overscroll from a fling, that fling did not request a
|
||||
// fling snap to avoid a resulting scrollTo from cancelling the overscroll
|
||||
// animation too early. We do still want to request a fling snap, though,
|
||||
// in case the end of the axis at which we're overscrolled is not a valid
|
||||
// snap point, so we request one now. If there are no snap points, this will
|
||||
// do nothing. If there are snap points, we'll get a scrollTo that snaps us
|
||||
// back to the nearest valid snap point.
|
||||
if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
|
||||
controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
|
||||
mFrameMetrics.GetScrollOffset());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -644,6 +644,10 @@ protected:
|
|||
// Common processing at the end of a touch block.
|
||||
void OnTouchEndOrCancel();
|
||||
|
||||
// This is called by OverscrollAnimation to notify us when the overscroll
|
||||
// animation is ending.
|
||||
void OverscrollAnimationEnding();
|
||||
|
||||
uint64_t mLayersId;
|
||||
nsRefPtr<CompositorParent> mCompositorParent;
|
||||
TaskThrottler mPaintThrottler;
|
||||
|
|
|
@ -844,6 +844,12 @@ gfxDWriteFontList::MakePlatformFont(const nsAString& aFontName,
|
|||
return entry;
|
||||
}
|
||||
|
||||
enum DWriteInitError {
|
||||
errGDIInterop = 1,
|
||||
errSystemFontCollection = 2,
|
||||
errNoFonts = 3
|
||||
};
|
||||
|
||||
nsresult
|
||||
gfxDWriteFontList::InitFontList()
|
||||
{
|
||||
|
@ -874,6 +880,8 @@ gfxDWriteFontList::InitFontList()
|
|||
hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
|
||||
GetGdiInterop(getter_AddRefs(mGDIInterop));
|
||||
if (FAILED(hr)) {
|
||||
Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
|
||||
uint32_t(errGDIInterop));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -886,6 +894,8 @@ gfxDWriteFontList::InitFontList()
|
|||
NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
|
||||
|
||||
if (FAILED(hr)) {
|
||||
Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
|
||||
uint32_t(errSystemFontCollection));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -897,6 +907,8 @@ gfxDWriteFontList::InitFontList()
|
|||
NS_ASSERTION(mFontFamilies.Count() != 0,
|
||||
"no fonts found in the system fontlist -- holy crap batman!");
|
||||
if (mFontFamilies.Count() == 0) {
|
||||
Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
|
||||
uint32_t(errNoFonts));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -2065,26 +2065,19 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
|
|||
switch (aWhichLog) {
|
||||
case eGfxLog_fontlist:
|
||||
return sFontlistLog;
|
||||
break;
|
||||
case eGfxLog_fontinit:
|
||||
return sFontInitLog;
|
||||
break;
|
||||
case eGfxLog_textrun:
|
||||
return sTextrunLog;
|
||||
break;
|
||||
case eGfxLog_textrunui:
|
||||
return sTextrunuiLog;
|
||||
break;
|
||||
case eGfxLog_cmapdata:
|
||||
return sCmapDataLog;
|
||||
break;
|
||||
case eGfxLog_textperf:
|
||||
return sTextPerfLog;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected log type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -3269,7 +3269,7 @@ gfxMissingFontRecorder::Flush()
|
|||
}
|
||||
mNotifiedFonts[i] |= (1 << j);
|
||||
if (!fontNeeded.IsEmpty()) {
|
||||
fontNeeded.Append(PRUnichar(','));
|
||||
fontNeeded.Append(char16_t(','));
|
||||
}
|
||||
uint32_t tag = GetScriptTagForCode(i * 32 + j);
|
||||
fontNeeded.Append(char16_t(tag >> 24));
|
||||
|
|
|
@ -475,6 +475,21 @@ NextNonEmptyStatement(ParseNode* pn)
|
|||
return SkipEmptyStatements(pn->pn_next);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetToken(AsmJSParser& parser, TokenKind* tkp)
|
||||
{
|
||||
TokenStream& ts = parser.tokenStream;
|
||||
TokenKind tk;
|
||||
while (true) {
|
||||
if (!ts.getToken(&tk, TokenStream::Operand))
|
||||
return false;
|
||||
if (tk != TOK_SEMI)
|
||||
break;
|
||||
}
|
||||
*tkp = tk;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PeekToken(AsmJSParser& parser, TokenKind* tkp)
|
||||
{
|
||||
|
@ -1108,6 +1123,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
Signature sig_;
|
||||
PropertyName* name_;
|
||||
Label* entry_;
|
||||
uint32_t firstUseOffset_;
|
||||
uint32_t funcIndex_;
|
||||
uint32_t srcBegin_;
|
||||
uint32_t srcEnd_;
|
||||
|
@ -1115,12 +1131,14 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
bool defined_;
|
||||
|
||||
public:
|
||||
Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex)
|
||||
: sig_(Move(sig)), name_(name), entry_(entry), funcIndex_(funcIndex), srcBegin_(0),
|
||||
srcEnd_(0), compileTime_(0), defined_(false)
|
||||
Func(PropertyName* name, uint32_t firstUseOffset, Signature&& sig, Label* entry,
|
||||
uint32_t funcIndex)
|
||||
: sig_(Move(sig)), name_(name), entry_(entry), firstUseOffset_(firstUseOffset),
|
||||
funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false)
|
||||
{}
|
||||
|
||||
PropertyName* name() const { return name_; }
|
||||
uint32_t firstUseOffset() const { return firstUseOffset_; }
|
||||
bool defined() const { return defined_; }
|
||||
uint32_t funcIndex() const { return funcIndex_; }
|
||||
|
||||
|
@ -1287,25 +1305,30 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
class FuncPtrTable
|
||||
{
|
||||
Signature sig_;
|
||||
FuncPtrVector elems_;
|
||||
PropertyName* name_;
|
||||
uint32_t firstUseOffset_;
|
||||
uint32_t mask_;
|
||||
uint32_t globalDataOffset_;
|
||||
uint32_t tableIndex_;
|
||||
FuncPtrVector elems_;
|
||||
|
||||
public:
|
||||
FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo,
|
||||
uint32_t tableIndex)
|
||||
: sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex),
|
||||
elems_(cx)
|
||||
FuncPtrTable(ExclusiveContext* cx, PropertyName* name, uint32_t firstUseOffset, Signature&& sig,
|
||||
uint32_t mask, uint32_t gdo, uint32_t tableIndex)
|
||||
: sig_(Move(sig)), elems_(cx), name_(name), firstUseOffset_(firstUseOffset), mask_(mask),
|
||||
globalDataOffset_(gdo), tableIndex_(tableIndex)
|
||||
{}
|
||||
|
||||
FuncPtrTable(FuncPtrTable&& rhs)
|
||||
: sig_(Move(rhs.sig_)), mask_(rhs.mask_), globalDataOffset_(rhs.globalDataOffset_),
|
||||
elems_(Move(rhs.elems_))
|
||||
: sig_(Move(rhs.sig_)), elems_(Move(rhs.elems_)), name_(rhs.name()),
|
||||
firstUseOffset_(rhs.firstUseOffset()), mask_(rhs.mask_),
|
||||
globalDataOffset_(rhs.globalDataOffset_)
|
||||
{}
|
||||
|
||||
Signature& sig() { return sig_; }
|
||||
const Signature& sig() const { return sig_; }
|
||||
PropertyName* name() const { return name_; }
|
||||
uint32_t firstUseOffset() const { return firstUseOffset_; }
|
||||
unsigned mask() const { return mask_; }
|
||||
unsigned globalDataOffset() const { return globalDataOffset_; }
|
||||
unsigned tableIndex() const { return tableIndex_; }
|
||||
|
@ -1596,47 +1619,47 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
}
|
||||
|
||||
bool fail(ParseNode* pn, const char* str) {
|
||||
if (pn)
|
||||
return failOffset(pn->pn_pos.begin, str);
|
||||
|
||||
// The exact rooting static analysis does not perform dataflow analysis, so it believes
|
||||
// that unrooted things on the stack during compilation may still be accessed after this.
|
||||
// Since pn is typically only null under OOM, this suppression simply forces any GC to be
|
||||
// delayed until the compilation is off the stack and more memory can be freed.
|
||||
gc::AutoSuppressGC nogc(cx_);
|
||||
TokenPos pos;
|
||||
TokenStream::Modifier modifier = tokenStream().hasLookahead() ? tokenStream().getLookaheadModifier() : TokenStream::None;
|
||||
if (!tokenStream().peekTokenPos(&pos, modifier))
|
||||
return false;
|
||||
return failOffset(pos.begin, str);
|
||||
return failOffset(pn->pn_pos.begin, str);
|
||||
}
|
||||
|
||||
bool failfVA(ParseNode* pn, const char* fmt, va_list ap) {
|
||||
bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) {
|
||||
MOZ_ASSERT(!errorString_);
|
||||
MOZ_ASSERT(errorOffset_ == UINT32_MAX);
|
||||
MOZ_ASSERT(fmt);
|
||||
errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end;
|
||||
errorOffset_ = offset;
|
||||
errorString_.reset(JS_vsmprintf(fmt, ap));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool failfOffset(uint32_t offset, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
failfVAOffset(offset, fmt, ap);
|
||||
va_end(ap);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool failf(ParseNode* pn, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
failfVA(pn, fmt, ap);
|
||||
failfVAOffset(pn->pn_pos.begin, fmt, ap);
|
||||
va_end(ap);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
|
||||
bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
|
||||
// This function is invoked without the caller properly rooting its locals.
|
||||
gc::AutoSuppressGC suppress(cx_);
|
||||
JSAutoByteString bytes;
|
||||
if (AtomToPrintableString(cx_, name, &bytes))
|
||||
failf(pn, fmt, bytes.ptr());
|
||||
failfOffset(offset, fmt, bytes.ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
|
||||
return failNameOffset(pn->pn_pos.begin, fmt, name);
|
||||
}
|
||||
|
||||
bool failOverRecursed() {
|
||||
errorOverRecursed_ = true;
|
||||
return false;
|
||||
|
@ -1767,7 +1790,8 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
global->u.varOrConst.type_ = VarType(coercion).toType().which();
|
||||
return globalsVector_.append(global) && globals_.putNew(varName, global);
|
||||
}
|
||||
bool addFunction(PropertyName* name, Signature&& sig, Func** func, uint32_t* outFuncIndex) {
|
||||
bool addFunction(PropertyName* name, uint32_t firstUseOffset, Signature&& sig, Func** func,
|
||||
uint32_t* outFuncIndex) {
|
||||
MOZ_ASSERT(!finishedFunctionBodies_);
|
||||
uint32_t funcIndex = functions_.length();
|
||||
if (outFuncIndex)
|
||||
|
@ -1781,13 +1805,13 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
Label* entry = moduleLifo_.new_<Label>();
|
||||
if (!entry)
|
||||
return false;
|
||||
*func = moduleLifo_.new_<Func>(name, Move(sig), entry, funcIndex);
|
||||
*func = moduleLifo_.new_<Func>(name, firstUseOffset, Move(sig), entry, funcIndex);
|
||||
if (!*func)
|
||||
return false;
|
||||
return functions_.append(*func);
|
||||
}
|
||||
bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, FuncPtrTable** table,
|
||||
uint32_t* tableIndexOut)
|
||||
bool addFuncPtrTable(PropertyName* name, uint32_t offset, Signature&& sig, uint32_t mask,
|
||||
FuncPtrTable** table, uint32_t* tableIndexOut)
|
||||
{
|
||||
uint32_t tableIndex = funcPtrTables_.length();
|
||||
if (tableIndexOut)
|
||||
|
@ -1801,7 +1825,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
|||
uint32_t globalDataOffset;
|
||||
if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset))
|
||||
return false;
|
||||
FuncPtrTable tmpTable(cx_, Move(sig), mask, globalDataOffset, tableIndex);
|
||||
FuncPtrTable tmpTable(cx_, name, offset, Move(sig), mask, globalDataOffset, tableIndex);
|
||||
if (!funcPtrTables_.append(Move(tmpTable)))
|
||||
return false;
|
||||
*table = &funcPtrTables_.back();
|
||||
|
@ -2972,7 +2996,7 @@ class FunctionBuilder
|
|||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
m_.failfVA(pn, fmt, ap);
|
||||
m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
|
||||
va_end(ap);
|
||||
return false;
|
||||
}
|
||||
|
@ -4713,6 +4737,11 @@ CheckNewArrayView(ModuleCompiler& m, PropertyName* varName, ParseNode* newExpr)
|
|||
shared = global->viewIsSharedView();
|
||||
}
|
||||
|
||||
#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
|
||||
if (shared)
|
||||
return m.fail(ctorExpr, "shared views not supported by this build");
|
||||
#endif
|
||||
|
||||
if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
|
||||
return false;
|
||||
|
||||
|
@ -4857,6 +4886,11 @@ CheckGlobalDotImport(ModuleCompiler& m, PropertyName* varName, ParseNode* initNo
|
|||
Scalar::Type type;
|
||||
bool shared = false;
|
||||
if (IsArrayViewCtorName(m, field, &type, &shared)) {
|
||||
#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
|
||||
if (shared)
|
||||
return m.fail(initNode, "shared views not supported by this build");
|
||||
#endif
|
||||
|
||||
if (!m.module().isValidViewSharedness(shared))
|
||||
return m.failName(initNode, "'%s' has different sharedness than previous view constructors", field);
|
||||
return m.addArrayViewCtor(varName, type, field, shared);
|
||||
|
@ -4918,12 +4952,15 @@ CheckModuleProcessingDirectives(ModuleCompiler& m)
|
|||
return true;
|
||||
|
||||
if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
|
||||
return m.fail(nullptr, "unsupported processing directive");
|
||||
return m.failOffset(ts.currentToken().pos.begin, "unsupported processing directive");
|
||||
|
||||
if (!ts.matchToken(&matched, TOK_SEMI))
|
||||
TokenKind tt;
|
||||
if (!ts.getToken(&tt))
|
||||
return false;
|
||||
if (!matched)
|
||||
return m.fail(nullptr, "expected semicolon after string literal");
|
||||
if (tt != TOK_SEMI) {
|
||||
return m.failOffset(ts.currentToken().pos.begin,
|
||||
"expected semicolon after string literal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6309,7 +6346,7 @@ CheckFunctionSignature(ModuleCompiler& m, ParseNode* usepn, Signature&& sig, Pro
|
|||
if (!existing) {
|
||||
if (!CheckModuleLevelName(m, usepn, name))
|
||||
return false;
|
||||
return m.addFunction(name, Move(sig), func, funcIndex);
|
||||
return m.addFunction(name, usepn->pn_pos.begin, Move(sig), func, funcIndex);
|
||||
}
|
||||
|
||||
if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig()))
|
||||
|
@ -6411,7 +6448,7 @@ CheckFuncPtrTableAgainstExisting(ModuleCompiler& m, ParseNode* usepn,
|
|||
if (!CheckModuleLevelName(m, usepn, name))
|
||||
return false;
|
||||
|
||||
return m.addFuncPtrTable(name, Move(sig), mask, tableOut, tableIndex);
|
||||
return m.addFuncPtrTable(name, usepn->pn_pos.begin, Move(sig), mask, tableOut, tableIndex);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -9643,12 +9680,12 @@ CheckHeapLengthCondition(ModuleCompiler& m, ParseNode* cond, PropertyName* newBu
|
|||
static bool
|
||||
CheckReturnBoolLiteral(ModuleCompiler& m, ParseNode* stmt, bool retval)
|
||||
{
|
||||
if (!stmt)
|
||||
return m.fail(stmt, "expected return statement");
|
||||
|
||||
if (stmt->isKind(PNK_STATEMENTLIST)) {
|
||||
stmt = SkipEmptyStatements(ListHead(stmt));
|
||||
if (!stmt || NextNonEmptyStatement(stmt))
|
||||
ParseNode* next = SkipEmptyStatements(ListHead(stmt));
|
||||
if (!next)
|
||||
return m.fail(stmt, "expected return statement");
|
||||
stmt = next;
|
||||
if (NextNonEmptyStatement(stmt))
|
||||
return m.fail(stmt, "expected single return statement");
|
||||
}
|
||||
|
||||
|
@ -9665,7 +9702,7 @@ CheckReturnBoolLiteral(ModuleCompiler& m, ParseNode* stmt, bool retval)
|
|||
static bool
|
||||
CheckReassignmentTo(ModuleCompiler& m, ParseNode* stmt, PropertyName* lhsName, ParseNode** rhs)
|
||||
{
|
||||
if (!stmt || !stmt->isKind(PNK_SEMI))
|
||||
if (!stmt->isKind(PNK_SEMI))
|
||||
return m.fail(stmt, "missing reassignment");
|
||||
|
||||
ParseNode* assign = UnaryKid(stmt);
|
||||
|
@ -9729,9 +9766,13 @@ CheckChangeHeap(ModuleCompiler& m, ParseNode* fn, bool* validated)
|
|||
if (!CheckReturnBoolLiteral(m, thenStmt, false))
|
||||
return false;
|
||||
|
||||
stmtIter = NextNonEmptyStatement(stmtIter);
|
||||
ParseNode* next = NextNonEmptyStatement(stmtIter);
|
||||
|
||||
for (unsigned i = 0; i < m.numArrayViews(); i++, next = NextNonEmptyStatement(stmtIter)) {
|
||||
if (!next)
|
||||
return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
|
||||
stmtIter = next;
|
||||
|
||||
for (unsigned i = 0; i < m.numArrayViews(); i++, stmtIter = NextNonEmptyStatement(stmtIter)) {
|
||||
const ModuleCompiler::ArrayView& view = m.arrayView(i);
|
||||
|
||||
ParseNode* rhs;
|
||||
|
@ -9755,13 +9796,20 @@ CheckChangeHeap(ModuleCompiler& m, ParseNode* fn, bool* validated)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
|
||||
stmtIter = next;
|
||||
|
||||
ParseNode* rhs;
|
||||
if (!CheckReassignmentTo(m, stmtIter, bufferName, &rhs))
|
||||
return false;
|
||||
if (!IsUseOfName(rhs, newBufferName))
|
||||
return m.failName(stmtIter, "expecting assignment of new buffer to %s", bufferName);
|
||||
|
||||
stmtIter = NextNonEmptyStatement(stmtIter);
|
||||
next = NextNonEmptyStatement(stmtIter);
|
||||
if (!next)
|
||||
return m.failOffset(stmtIter->pn_pos.end, "expected return statement");
|
||||
stmtIter = next;
|
||||
|
||||
if (!CheckReturnBoolLiteral(m, stmtIter, true))
|
||||
return false;
|
||||
|
@ -9824,7 +9872,7 @@ ParseFunction(ModuleCompiler& m, ParseNode** fnOut)
|
|||
if (tokenStream.hadError() || directives == newDirectives)
|
||||
return false;
|
||||
|
||||
return m.fail(nullptr, "encountered new directive");
|
||||
return m.fail(fn, "encountered new directive in function");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!tokenStream.hadError());
|
||||
|
@ -10378,8 +10426,11 @@ static bool
|
|||
CheckAllFunctionsDefined(ModuleCompiler& m)
|
||||
{
|
||||
for (unsigned i = 0; i < m.numFunctions(); i++) {
|
||||
if (!m.function(i).entry().bound())
|
||||
return m.failName(nullptr, "missing definition of function %s", m.function(i).name());
|
||||
if (!m.function(i).entry().bound()) {
|
||||
ModuleCompiler::Func& f = m.function(i);
|
||||
return m.failNameOffset(f.firstUseOffset(),
|
||||
"missing definition of function %s", f.name());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -10764,8 +10815,12 @@ CheckFuncPtrTables(ModuleCompiler& m)
|
|||
}
|
||||
|
||||
for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
|
||||
if (!m.funcPtrTable(i).initialized())
|
||||
return m.fail(nullptr, "expecting function-pointer table");
|
||||
ModuleCompiler::FuncPtrTable& funcPtrTable = m.funcPtrTable(i);
|
||||
if (!funcPtrTable.initialized()) {
|
||||
return m.failNameOffset(funcPtrTable.firstUseOffset(),
|
||||
"function-pointer table %s wasn't defined",
|
||||
funcPtrTable.name());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -10817,13 +10872,16 @@ static bool
|
|||
CheckModuleReturn(ModuleCompiler& m)
|
||||
{
|
||||
TokenKind tk;
|
||||
if (!PeekToken(m.parser(), &tk))
|
||||
if (!GetToken(m.parser(), &tk))
|
||||
return false;
|
||||
TokenStream& ts = m.parser().tokenStream;
|
||||
if (tk != TOK_RETURN) {
|
||||
if (tk == TOK_RC || tk == TOK_EOF)
|
||||
return m.fail(nullptr, "expecting return statement");
|
||||
return m.fail(nullptr, "invalid asm.js statement");
|
||||
const char* msg = (tk == TOK_RC || tk == TOK_EOF)
|
||||
? "expecting return statement"
|
||||
: "invalid asm.js. statement";
|
||||
return m.failOffset(ts.currentToken().pos.begin, msg);
|
||||
}
|
||||
ts.ungetToken();
|
||||
|
||||
ParseNode* returnStmt = m.parser().statement(YieldIsName);
|
||||
if (!returnStmt)
|
||||
|
@ -12020,8 +12078,7 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
|
|||
m.startFunctionBodies();
|
||||
|
||||
#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
|
||||
if (m.module().hasArrayView() && m.module().isSharedView())
|
||||
return m.fail(nullptr, "shared views not supported by this build");
|
||||
MOZ_ASSERT(!m.module().hasArrayView() || !m.module().isSharedView());
|
||||
#endif
|
||||
|
||||
if (!CheckFunctions(m))
|
||||
|
@ -12036,10 +12093,15 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
|
|||
return false;
|
||||
|
||||
TokenKind tk;
|
||||
if (!PeekToken(m.parser(), &tk))
|
||||
if (!GetToken(m.parser(), &tk))
|
||||
return false;
|
||||
if (tk != TOK_EOF && tk != TOK_RC)
|
||||
return m.fail(nullptr, "top-level export (return) must be the last statement");
|
||||
TokenStream& ts = m.parser().tokenStream;
|
||||
if (tk == TOK_EOF || tk == TOK_RC) {
|
||||
ts.ungetToken();
|
||||
} else {
|
||||
return m.failOffset(ts.currentToken().pos.begin,
|
||||
"top-level export (return) must be the last statement");
|
||||
}
|
||||
|
||||
// Delay flushing until dynamic linking. The inhibited range is set by the
|
||||
// masm.executableCopy() called transitively by FinishModule.
|
||||
|
|
|
@ -1795,6 +1795,7 @@ ia64*-hpux*)
|
|||
dnl Probably also a compiler bug, but what can you do?
|
||||
PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
|
||||
LDFLAGS="$LDFLAGS -DYNAMICBASE"
|
||||
RCFLAGS="-nologo"
|
||||
fi
|
||||
AC_DEFINE(HAVE_SNPRINTF)
|
||||
AC_DEFINE(HAVE__MSIZE)
|
||||
|
|
|
@ -428,17 +428,10 @@ BytecodeCompiler::checkArgumentsWithinEval(JSContext* cx, HandleFunction fun)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Force construction of arguments objects for functions that use
|
||||
// |arguments| within an eval.
|
||||
RootedScript script(cx, fun->getOrCreateScript(cx));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
if (script->argumentsHasVarBinding()) {
|
||||
if (!JSScript::argumentsOptimizationFailed(cx, script))
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's an error to use |arguments| in a legacy generator expression.
|
||||
if (script->isGeneratorExp() && script->isLegacyGenerator()) {
|
||||
parser->report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
|
||||
|
|
|
@ -956,14 +956,8 @@ Parser<FullParseHandler>::checkFunctionArguments()
|
|||
FunctionBox* funbox = pc->sc->asFunctionBox();
|
||||
funbox->setArgumentsHasLocalBinding();
|
||||
|
||||
/*
|
||||
* If a script has both explicit mentions of 'arguments' and dynamic
|
||||
* name lookups which could access the arguments, an arguments object
|
||||
* must be created eagerly. The SSA analysis used for lazy arguments
|
||||
* cannot cope with dynamic name accesses, so any 'arguments' accessed
|
||||
* via a NAME opcode must force construction of the arguments object.
|
||||
*/
|
||||
if (pc->sc->bindingsAccessedDynamically() && maybeArgDef)
|
||||
/* Dynamic scope access destroys all hope of optimization. */
|
||||
if (pc->sc->bindingsAccessedDynamically())
|
||||
funbox->setDefinitelyNeedsArgsObj();
|
||||
|
||||
/*
|
||||
|
@ -991,9 +985,6 @@ Parser<FullParseHandler>::checkFunctionArguments()
|
|||
funbox->setDefinitelyNeedsArgsObj();
|
||||
}
|
||||
}
|
||||
/* Watch for mutation of arguments through e.g. eval(). */
|
||||
if (pc->sc->bindingsAccessedDynamically())
|
||||
funbox->setDefinitelyNeedsArgsObj();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -489,16 +489,6 @@ class MOZ_STACK_CLASS TokenStream
|
|||
#endif
|
||||
}
|
||||
|
||||
bool hasLookahead() { return lookahead > 0; }
|
||||
|
||||
Modifier getLookaheadModifier() {
|
||||
#ifdef DEBUG
|
||||
return nextToken().modifier;
|
||||
#else
|
||||
return None;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
verifyConsistentModifier(Modifier modifier, Token lookaheadToken) {
|
||||
#ifdef DEBUG
|
||||
|
@ -991,6 +981,8 @@ class MOZ_STACK_CLASS TokenStream
|
|||
return tokens[(cursor + 1) & ntokensMask];
|
||||
}
|
||||
|
||||
bool hasLookahead() { return lookahead > 0; }
|
||||
|
||||
// Options used for parsing/tokenizing.
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
|
||||
|
|
|
@ -3500,45 +3500,6 @@ CodeGenerator::visitGetDynamicName(LGetDynamicName* lir)
|
|||
bailoutFrom(&undefined, lir->snapshot());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::emitFilterArgumentsOrEval(LInstruction* lir, Register string,
|
||||
Register temp1, Register temp2)
|
||||
{
|
||||
masm.loadJSContext(temp2);
|
||||
|
||||
masm.setupUnalignedABICall(temp1);
|
||||
masm.passABIArg(temp2);
|
||||
masm.passABIArg(string);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, FilterArgumentsOrEval));
|
||||
|
||||
Label bail;
|
||||
masm.branchIfFalseBool(ReturnReg, &bail);
|
||||
bailoutFrom(&bail, lir->snapshot());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS* lir)
|
||||
{
|
||||
emitFilterArgumentsOrEval(lir, ToRegister(lir->getString()),
|
||||
ToRegister(lir->temp1()),
|
||||
ToRegister(lir->temp2()));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV* lir)
|
||||
{
|
||||
ValueOperand input = ToValue(lir, LFilterArgumentsOrEvalV::Input);
|
||||
|
||||
// Act as nop on non-strings.
|
||||
Label done;
|
||||
masm.branchTestString(Assembler::NotEqual, input, &done);
|
||||
|
||||
emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())),
|
||||
ToRegister(lir->temp1()), ToRegister(lir->temp2()));
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue,
|
||||
HandleString, jsbytecode*, MutableHandleValue);
|
||||
static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
|
||||
|
|
|
@ -149,8 +149,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
void visitUnreachable(LUnreachable* unreachable);
|
||||
void visitEncodeSnapshot(LEncodeSnapshot* lir);
|
||||
void visitGetDynamicName(LGetDynamicName* lir);
|
||||
void visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS* lir);
|
||||
void visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV* lir);
|
||||
void visitCallDirectEval(LCallDirectEval* lir);
|
||||
void visitDoubleToInt32(LDoubleToInt32* lir);
|
||||
void visitFloat32ToInt32(LFloat32ToInt32* lir);
|
||||
|
|
|
@ -236,7 +236,11 @@ class ExecutableAllocator
|
|||
}
|
||||
systemRelease(pool->m_allocation);
|
||||
MOZ_ASSERT(m_pools.initialized());
|
||||
m_pools.remove(m_pools.lookup(pool)); // this asserts if |pool| is not in m_pools
|
||||
|
||||
// Pool may not be present in m_pools if we hit OOM during creation.
|
||||
auto ptr = m_pools.lookup(pool);
|
||||
if (ptr)
|
||||
m_pools.remove(ptr);
|
||||
}
|
||||
|
||||
void addSizeOfCode(JS::CodeSizes* sizes) const;
|
||||
|
@ -299,7 +303,13 @@ class ExecutableAllocator
|
|||
systemRelease(a);
|
||||
return nullptr;
|
||||
}
|
||||
m_pools.put(pool);
|
||||
|
||||
if (!m_pools.put(pool)) {
|
||||
js_delete(pool);
|
||||
systemRelease(a);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
|
|
@ -3470,26 +3470,14 @@ jit::AnalyzeArgumentsUsage(JSContext* cx, JSScript* scriptArg)
|
|||
// and also simplifies handling of early returns.
|
||||
script->setNeedsArgsObj(true);
|
||||
|
||||
// Always construct arguments objects when in debug mode and for generator
|
||||
// scripts (generators can be suspended when speculation fails).
|
||||
// Always construct arguments objects when in debug mode, for generator
|
||||
// scripts (generators can be suspended when speculation fails) or when
|
||||
// direct eval is present.
|
||||
//
|
||||
// FIXME: Don't build arguments for ES6 generator expressions.
|
||||
if (scriptArg->isDebuggee() || script->isGenerator())
|
||||
if (scriptArg->isDebuggee() || script->isGenerator() || script->bindingsAccessedDynamically())
|
||||
return true;
|
||||
|
||||
// If the script has dynamic name accesses which could reach 'arguments',
|
||||
// the parser will already have checked to ensure there are no explicit
|
||||
// uses of 'arguments' in the function. If there are such uses, the script
|
||||
// will be marked as definitely needing an arguments object.
|
||||
//
|
||||
// New accesses on 'arguments' can occur through 'eval' or the debugger
|
||||
// statement. In the former case, we will dynamically detect the use and
|
||||
// mark the arguments optimization as having failed.
|
||||
if (script->bindingsAccessedDynamically()) {
|
||||
script->setNeedsArgsObj(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!jit::IsIonEnabled(cx))
|
||||
return true;
|
||||
|
||||
|
|
|
@ -6599,9 +6599,6 @@ IonBuilder::jsop_eval(uint32_t argc)
|
|||
}
|
||||
}
|
||||
|
||||
MInstruction* filterArguments = MFilterArgumentsOrEval::New(alloc(), string);
|
||||
current->add(filterArguments);
|
||||
|
||||
MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string,
|
||||
thisValue, newTargetValue, pc);
|
||||
current->add(ins);
|
||||
|
|
|
@ -855,7 +855,7 @@ JitcodeGlobalTable::sweep(JSRuntime* rt)
|
|||
if (entry->baseEntry().isJitcodeAboutToBeFinalized())
|
||||
e.removeFront();
|
||||
else
|
||||
entry->sweep(rt);
|
||||
entry->sweepChildren(rt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -895,7 +895,7 @@ JitcodeGlobalEntry::BaselineEntry::mark(JSTracer* trc)
|
|||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::BaselineEntry::sweep()
|
||||
JitcodeGlobalEntry::BaselineEntry::sweepChildren()
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&script_));
|
||||
}
|
||||
|
@ -946,7 +946,7 @@ JitcodeGlobalEntry::IonEntry::mark(JSTracer* trc)
|
|||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonEntry::sweep()
|
||||
JitcodeGlobalEntry::IonEntry::sweepChildren()
|
||||
{
|
||||
for (unsigned i = 0; i < numScripts(); i++)
|
||||
MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&sizedScriptList()->pairs[i].script));
|
||||
|
@ -1004,11 +1004,11 @@ JitcodeGlobalEntry::IonCacheEntry::mark(JSTracer* trc)
|
|||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonCacheEntry::sweep(JSRuntime* rt)
|
||||
JitcodeGlobalEntry::IonCacheEntry::sweepChildren(JSRuntime* rt)
|
||||
{
|
||||
JitcodeGlobalEntry entry;
|
||||
RejoinEntry(rt, *this, nativeStartAddr(), &entry);
|
||||
entry.sweep(rt);
|
||||
entry.sweepChildren(rt);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -369,7 +369,7 @@ class JitcodeGlobalEntry
|
|||
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
|
||||
|
||||
template <class ShouldMarkProvider> bool mark(JSTracer* trc);
|
||||
void sweep();
|
||||
void sweepChildren();
|
||||
bool isMarkedFromAnyThread();
|
||||
};
|
||||
|
||||
|
@ -427,7 +427,7 @@ class JitcodeGlobalEntry
|
|||
JSScript** script, jsbytecode** pc) const;
|
||||
|
||||
template <class ShouldMarkProvider> bool mark(JSTracer* trc);
|
||||
void sweep();
|
||||
void sweepChildren();
|
||||
bool isMarkedFromAnyThread();
|
||||
};
|
||||
|
||||
|
@ -476,7 +476,7 @@ class JitcodeGlobalEntry
|
|||
IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
|
||||
|
||||
template <class ShouldMarkProvider> bool mark(JSTracer* trc);
|
||||
void sweep(JSRuntime* rt);
|
||||
void sweepChildren(JSRuntime* rt);
|
||||
bool isMarkedFromAnyThread(JSRuntime* rt);
|
||||
};
|
||||
|
||||
|
@ -931,16 +931,16 @@ class JitcodeGlobalEntry
|
|||
return markedAny;
|
||||
}
|
||||
|
||||
void sweep(JSRuntime* rt) {
|
||||
void sweepChildren(JSRuntime* rt) {
|
||||
switch (kind()) {
|
||||
case Ion:
|
||||
ionEntry().sweep();
|
||||
ionEntry().sweepChildren();
|
||||
break;
|
||||
case Baseline:
|
||||
baselineEntry().sweep();
|
||||
baselineEntry().sweepChildren();
|
||||
break;
|
||||
case IonCache:
|
||||
ionCacheEntry().sweep(rt);
|
||||
ionCacheEntry().sweepChildren(rt);
|
||||
case Dummy:
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -585,30 +585,6 @@ LIRGenerator::visitGetDynamicName(MGetDynamicName* ins)
|
|||
defineReturn(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitFilterArgumentsOrEval(MFilterArgumentsOrEval* ins)
|
||||
{
|
||||
MDefinition* string = ins->getString();
|
||||
MOZ_ASSERT(string->type() == MIRType_String || string->type() == MIRType_Value);
|
||||
|
||||
LInstruction* lir;
|
||||
if (string->type() == MIRType_String) {
|
||||
lir = new(alloc()) LFilterArgumentsOrEvalS(useFixed(string, CallTempReg0),
|
||||
tempFixed(CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
} else {
|
||||
lir = new(alloc()) LFilterArgumentsOrEvalV(tempFixed(CallTempReg0),
|
||||
tempFixed(CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
useBoxFixed(lir, LFilterArgumentsOrEvalV::Input, string,
|
||||
CallTempReg3, CallTempReg4);
|
||||
}
|
||||
|
||||
assignSnapshot(lir, Bailout_StringArgumentsEval);
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitCallDirectEval(MCallDirectEval* ins)
|
||||
{
|
||||
|
|
|
@ -106,7 +106,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitAssertFloat32(MAssertFloat32* ins);
|
||||
void visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* ins);
|
||||
void visitGetDynamicName(MGetDynamicName* ins);
|
||||
void visitFilterArgumentsOrEval(MFilterArgumentsOrEval* ins);
|
||||
void visitCallDirectEval(MCallDirectEval* ins);
|
||||
void visitTest(MTest* test);
|
||||
void visitGotoWithFake(MGotoWithFake* ins);
|
||||
|
|
|
@ -4051,34 +4051,6 @@ class MGetDynamicName
|
|||
}
|
||||
};
|
||||
|
||||
// Bailout if the input string contains 'arguments' or 'eval'.
|
||||
class MFilterArgumentsOrEval
|
||||
: public MAryInstruction<1>,
|
||||
public BoxExceptPolicy<0, MIRType_String>::Data
|
||||
{
|
||||
protected:
|
||||
explicit MFilterArgumentsOrEval(MDefinition* string)
|
||||
{
|
||||
initOperand(0, string);
|
||||
setGuard();
|
||||
setResultType(MIRType_None);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(FilterArgumentsOrEval)
|
||||
|
||||
static MFilterArgumentsOrEval* New(TempAllocator& alloc, MDefinition* string) {
|
||||
return new(alloc) MFilterArgumentsOrEval(string);
|
||||
}
|
||||
|
||||
MDefinition* getString() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MCallDirectEval
|
||||
: public MAryInstruction<4>,
|
||||
public Mix4Policy<ObjectPolicy<0>,
|
||||
|
|
|
@ -69,7 +69,6 @@ namespace jit {
|
|||
_(AssertFloat32) \
|
||||
_(AssertRecoveredOnBailout) \
|
||||
_(GetDynamicName) \
|
||||
_(FilterArgumentsOrEval) \
|
||||
_(CallDirectEval) \
|
||||
_(BitNot) \
|
||||
_(TypeOf) \
|
||||
|
|
|
@ -588,25 +588,6 @@ GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp)
|
|||
vp->setUndefined();
|
||||
}
|
||||
|
||||
bool
|
||||
FilterArgumentsOrEval(JSContext* cx, JSString* str)
|
||||
{
|
||||
// ensureLinear() is fallible, but cannot GC: it can only allocate a
|
||||
// character buffer for the flattened string. If this call fails then the
|
||||
// calling Ion code will bailout, resume in Baseline and likely fail again
|
||||
// when trying to flatten the string and unwind the stack.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
JSLinearString* linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
return false;
|
||||
|
||||
static const char16_t arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'};
|
||||
static const char16_t eval[] = {'e', 'v', 'a', 'l'};
|
||||
|
||||
return !StringHasPattern(linear, arguments, mozilla::ArrayLength(arguments)) &&
|
||||
!StringHasPattern(linear, eval, mozilla::ArrayLength(eval));
|
||||
}
|
||||
|
||||
void
|
||||
PostWriteBarrier(JSRuntime* rt, JSObject* obj)
|
||||
{
|
||||
|
|
|
@ -637,8 +637,6 @@ bool CreateThis(JSContext* cx, HandleObject callee, MutableHandleValue rval);
|
|||
|
||||
void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
|
||||
|
||||
bool FilterArgumentsOrEval(JSContext* cx, JSString* str);
|
||||
|
||||
void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
|
||||
void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
|
||||
|
||||
|
|
|
@ -1874,64 +1874,6 @@ class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
|
|||
}
|
||||
};
|
||||
|
||||
class LFilterArgumentsOrEvalS : public LCallInstructionHelper<0, 1, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(FilterArgumentsOrEvalS)
|
||||
|
||||
LFilterArgumentsOrEvalS(const LAllocation& string, const LDefinition& temp1,
|
||||
const LDefinition& temp2)
|
||||
{
|
||||
setOperand(0, string);
|
||||
setTemp(0, temp1);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
|
||||
MFilterArgumentsOrEval* mir() const {
|
||||
return mir_->toFilterArgumentsOrEval();
|
||||
}
|
||||
|
||||
const LAllocation* getString() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition* temp1() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
class LFilterArgumentsOrEvalV : public LCallInstructionHelper<0, BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(FilterArgumentsOrEvalV)
|
||||
|
||||
LFilterArgumentsOrEvalV(const LDefinition& temp1, const LDefinition& temp2,
|
||||
const LDefinition& temp3)
|
||||
{
|
||||
setTemp(0, temp1);
|
||||
setTemp(1, temp2);
|
||||
setTemp(2, temp3);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
MFilterArgumentsOrEval* mir() const {
|
||||
return mir_->toFilterArgumentsOrEval();
|
||||
}
|
||||
|
||||
const LDefinition* temp1() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition* temp3() {
|
||||
return getTemp(2);
|
||||
}
|
||||
};
|
||||
|
||||
class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + (2 * BOX_PIECES), 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -77,8 +77,6 @@
|
|||
_(Unreachable) \
|
||||
_(EncodeSnapshot) \
|
||||
_(GetDynamicName) \
|
||||
_(FilterArgumentsOrEvalS) \
|
||||
_(FilterArgumentsOrEvalV) \
|
||||
_(CallDirectEval) \
|
||||
_(StackArgT) \
|
||||
_(StackArgV) \
|
||||
|
|
|
@ -2368,7 +2368,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||
|
||||
bool isPositioned = disp->IsAbsPosContainingBlock(child);
|
||||
bool isStackingContext =
|
||||
(isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY ||
|
||||
(isPositioned && (disp->IsPositionForcingStackingContext() ||
|
||||
pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
|
||||
(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
|
||||
disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsIServiceManager.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
@ -301,6 +302,33 @@ inDOMUtils::GetRuleColumn(nsIDOMCSSRule* aRule, uint32_t* _retval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRule);
|
||||
|
||||
Rule* rule = aRule->GetCSSRule();
|
||||
if (!rule) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint32_t lineNumber = rule->GetLineNumber();
|
||||
CSSStyleSheet* sheet = rule->GetStyleSheet();
|
||||
if (sheet) {
|
||||
nsINode* owningNode = sheet->GetOwnerNode();
|
||||
if (owningNode) {
|
||||
nsCOMPtr<nsIStyleSheetLinkingElement> link =
|
||||
do_QueryInterface(owningNode);
|
||||
if (link) {
|
||||
lineNumber -= link->GetLineNumber() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*_retval = lineNumber;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx,
|
||||
JS::MutableHandleValue aResult)
|
||||
|
|
|
@ -17,7 +17,7 @@ interface nsIDOMFontFaceList;
|
|||
interface nsIDOMRange;
|
||||
interface nsIDOMCSSStyleSheet;
|
||||
|
||||
[scriptable, uuid(60b4cbf7-2a08-4419-8937-6ef495417824)]
|
||||
[scriptable, uuid(d67c0463-592e-4d7c-b67e-923ee3f6c643)]
|
||||
interface inIDOMUtils : nsISupports
|
||||
{
|
||||
// CSS utilities
|
||||
|
@ -28,6 +28,16 @@ interface inIDOMUtils : nsISupports
|
|||
unsigned long getRuleLine(in nsIDOMCSSRule aRule);
|
||||
unsigned long getRuleColumn(in nsIDOMCSSRule aRule);
|
||||
|
||||
/**
|
||||
* Like getRuleLine, but if the rule is in a <style> element,
|
||||
* returns a line number relative to the start of the element.
|
||||
*
|
||||
* @param nsIDOMCSSRule aRule the rule to examine
|
||||
* @return the line number of the rule, possibly relative to the
|
||||
* <style> element
|
||||
*/
|
||||
unsigned long getRelativeRuleLine(in nsIDOMCSSRule aRule);
|
||||
|
||||
[implicit_jscontext]
|
||||
jsval getCSSLexer(in DOMString aText);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ support-files =
|
|||
[test_bug1006595.html]
|
||||
[test_color_to_rgba.html]
|
||||
[test_css_property_is_valid.html]
|
||||
[test_getRelativeRuleLine.html]
|
||||
[test_get_all_style_sheets.html]
|
||||
[test_is_valid_css_color.html]
|
||||
[test_isinheritableproperty.html]
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test inDOMUtils::getRelativeRuleLine</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
@supports (not (whatever: 72 zq)) {
|
||||
#test {
|
||||
background-color: #f0c;
|
||||
}
|
||||
}
|
||||
|
||||
#test {
|
||||
color: #f0c;
|
||||
}
|
||||
</style>
|
||||
<style>#test { color: red; }</style>
|
||||
<style>
|
||||
@invalidatkeyword {
|
||||
}
|
||||
|
||||
#test {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript;version=1.8">
|
||||
let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
|
||||
.getService(SpecialPowers.Ci.inIDOMUtils);
|
||||
|
||||
let tests = [
|
||||
{ sheetNo: 0, ruleNo: 0, lineNo: 1, columnNo: 1 },
|
||||
{ sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 },
|
||||
{ sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 },
|
||||
{ sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 },
|
||||
{ sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 },
|
||||
];
|
||||
|
||||
function doTest() {
|
||||
for (let test of tests) {
|
||||
let sheet = document.styleSheets[test.sheetNo];
|
||||
let rule = sheet.cssRules[test.ruleNo];
|
||||
let line = utils.getRelativeRuleLine(rule);
|
||||
let column = utils.getRuleColumn(rule);
|
||||
info("testing sheet " + test.sheetNo + ", rule " + test.ruleNo);
|
||||
is(line, test.lineNo, "line number is correct");
|
||||
is(column, test.columnNo, "column number is correct");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test inDOMUtils::getRelativeRuleLine</h1>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -61,7 +61,7 @@ struct nsGlyphCode {
|
|||
bool IsGlyphID() const { return font == -1; }
|
||||
|
||||
int32_t Length() const {
|
||||
return (IsGlyphID() || code[1] == PRUnichar('\0') ? 1 : 2);
|
||||
return (IsGlyphID() || code[1] == char16_t('\0') ? 1 : 2);
|
||||
}
|
||||
bool Exists() const
|
||||
{
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<!DOCTYPE HTML>
|
||||
<body>
|
||||
<div style="position:absolute; left:0; top:0; width:100px; height:100px; background:lime"></div>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE HTML>
|
||||
<body>
|
||||
<div style="position:fixed; left:0; top:0">
|
||||
<div style="position:absolute; z-index:2; left:0; top:0; width:100px; height:100px; background:red"></div>
|
||||
</div>
|
||||
<div style="position:absolute; z-index:1; left:0; top:0; width:100px; height:100px; background:lime"></div>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче