mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 09:18:42 +09:00
Merge pull request 'Dactyloidae version 13.0' (#1) from 13.0 into main
Reviewed-on: https://repo.dactyloidae.xyz/Dactyloidae/UXP/pulls/1
This commit is contained in:
commit
b19458894f
41 changed files with 1451 additions and 331 deletions
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ RemotingName=@MOZ_APP_REMOTINGNAME@
|
|||
#ifdef MOZ_APP_DISPLAYNAME
|
||||
CodeName=@MOZ_APP_DISPLAYNAME@
|
||||
#endif
|
||||
Version=52.11.0
|
||||
Version=52.9.0
|
||||
#ifdef MOZ_APP_PROFILE
|
||||
Profile=@MOZ_APP_PROFILE@
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="*"
|
||||
name="Eclipse Hydra"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Eclipse Hydra</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<ms_asmv3:security>
|
||||
<ms_asmv3:requestedPrivileges>
|
||||
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</ms_asmv3:requestedPrivileges>
|
||||
</ms_asmv3:security>
|
||||
</ms_asmv3:trustInfo>
|
||||
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>True/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
|
||||
</ms_asmv3:windowsSettings>
|
||||
</ms_asmv3:application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
|
|
@ -446,6 +446,15 @@ pref("browser.ghostbuster.enabled", true);
|
|||
// misbehave. Should also avoid spurious GCs during ghostbusting.
|
||||
pref("javascript.options.gc_on_memory_pressure", false);
|
||||
|
||||
pref("javascript.options.baselinejit.unsafe_eager_compilation", false);
|
||||
pref("javascript.options.ion.unsafe_eager_compilation", false);
|
||||
pref("javascript.options.baselinejit.threshold", 4);
|
||||
pref("javascript.options.ion.threshold", 25);
|
||||
pref("javascript.options.mem.high_water_mark", 256);
|
||||
pref("javascript.options.mem.gc_incremental", false);
|
||||
pref("javascript.options.mem.gc_compacting", false);
|
||||
pref("javascript.options.mem.gc_incremental_slice_ms", 40);
|
||||
|
||||
// This is the pref to control the location bar, change this to true to
|
||||
// force this - this makes the origin of popup windows more obvious to avoid
|
||||
// spoofing. We would rather not do it by default because it affects UE for web
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="*"
|
||||
name="Serpent"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Serpent</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<ms_asmv3:security>
|
||||
<ms_asmv3:requestedPrivileges>
|
||||
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</ms_asmv3:requestedPrivileges>
|
||||
</ms_asmv3:security>
|
||||
</ms_asmv3:trustInfo>
|
||||
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>True/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
|
||||
</ms_asmv3:windowsSettings>
|
||||
</ms_asmv3:application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
|
|
@ -1 +1 @@
|
|||
52.12.0
|
||||
52.13.0
|
||||
|
|
@ -1 +1 @@
|
|||
12.0
|
||||
13.0
|
||||
|
|
@ -307,11 +307,11 @@ DevTools.prototype = {
|
|||
if (!Services.startup.shuttingDown &&
|
||||
!isCoreTheme &&
|
||||
theme.id == currTheme) {
|
||||
Services.prefs.setCharPref("devtools.theme", "firebug");
|
||||
Services.prefs.setCharPref("devtools.theme", "light");
|
||||
|
||||
let data = {
|
||||
pref: "devtools.theme",
|
||||
newValue: "firebug",
|
||||
newValue: "light",
|
||||
oldValue: currTheme
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ pref("devtools.dom.enabled", false);
|
|||
pref("devtools.webaudioeditor.inspectorWidth", 300);
|
||||
|
||||
// Default theme ("dark" or "light")
|
||||
sticky_pref("devtools.theme", "firebug");
|
||||
sticky_pref("devtools.theme", "light");
|
||||
|
||||
// Web console filters
|
||||
pref("devtools.webconsole.filter.error", true);
|
||||
|
|
|
|||
|
|
@ -2336,6 +2336,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
|
|||
bool* aOldValueSet)
|
||||
{
|
||||
bool modification = false;
|
||||
CustomElementData* customElementData = GetCustomElementData();
|
||||
*aHasListeners = aNotify &&
|
||||
nsContentUtils::HasMutationListeners(this,
|
||||
NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
|
||||
|
|
@ -2351,13 +2352,14 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
|
|||
if (*aHasListeners || aNotify) {
|
||||
BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName));
|
||||
if (info.mValue) {
|
||||
// Check whether the old value is the same as the new one. Note that we
|
||||
// only need to actually _get_ the old value if we have listeners or
|
||||
// if the element is a custom element (because it may have an
|
||||
// attribute changed callback).
|
||||
if (*aHasListeners || GetCustomElementData()) {
|
||||
// Need to store the old value.
|
||||
//
|
||||
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
|
||||
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Need to store the old value if listeners are present or this is a
|
||||
// custom element that may run an attribute-changed callback.
|
||||
if (*aHasListeners || customElementData) {
|
||||
// If the current attribute value contains a pointer to some other data
|
||||
// structure that gets updated in the process of setting the attribute
|
||||
// we'll no longer have the old value of the attribute. Therefore, we
|
||||
|
|
@ -2368,10 +2370,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
|
|||
aOldValue.SetToSerialized(*info.mValue);
|
||||
*aOldValueSet = true;
|
||||
}
|
||||
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
|
||||
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
modification = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -558,6 +558,7 @@ GK_ATOM(listing, "listing")
|
|||
GK_ATOM(listitem, "listitem")
|
||||
GK_ATOM(listrows, "listrows")
|
||||
GK_ATOM(load, "load")
|
||||
GK_ATOM(loading, "loading")
|
||||
GK_ATOM(loadingprincipal, "loadingprincipal")
|
||||
GK_ATOM(localedir, "localedir")
|
||||
GK_ATOM(localName, "local-name")
|
||||
|
|
|
|||
|
|
@ -489,7 +489,9 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
uint32_t childIndex = j - 1;
|
||||
EventTarget* newTarget = aChain[childIndex].GetNewTarget();
|
||||
if (newTarget) {
|
||||
aVisitor.mEvent->mTarget = newTarget;
|
||||
if (aVisitor.mEvent->mTarget != newTarget) {
|
||||
aVisitor.mEvent->mTarget = newTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -509,13 +511,18 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
aChain[childIndex].GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
found = true;
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
if (aVisitor.mEvent->mRelatedTarget != relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
aVisitor.mEvent->mRelatedTarget =
|
||||
aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
if (aVisitor.mEvent->mRelatedTarget !=
|
||||
aVisitor.mEvent->mOriginalRelatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget =
|
||||
aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -541,7 +548,9 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
if (newTarget) {
|
||||
// Item is at anonymous boundary. Need to retarget for the current item
|
||||
// and for parent items.
|
||||
aVisitor.mEvent->mTarget = newTarget;
|
||||
if (aVisitor.mEvent->mTarget != newTarget) {
|
||||
aVisitor.mEvent->mTarget = newTarget;
|
||||
}
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
|
|
@ -549,7 +558,9 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
if (aVisitor.mEvent->mRelatedTarget != relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
}
|
||||
}
|
||||
|
||||
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
|
||||
|
|
@ -570,7 +581,9 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
|
||||
|
||||
// Setting back the original target of the event.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
|
||||
if (aVisitor.mEvent->mTarget != aVisitor.mEvent->mOriginalTarget) {
|
||||
aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
|
||||
}
|
||||
|
||||
// Special handling if PresShell (or some other caller)
|
||||
// used a callback object.
|
||||
|
|
@ -580,8 +593,13 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
|
||||
// Retarget for system event group (which does the default handling too).
|
||||
// Setting back the target which was used also for default event group.
|
||||
aVisitor.mEvent->mTarget = firstTarget;
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
if (aVisitor.mEvent->mTarget != firstTarget) {
|
||||
aVisitor.mEvent->mTarget = firstTarget;
|
||||
}
|
||||
if (aVisitor.mEvent->mRelatedTarget !=
|
||||
aVisitor.mEvent->mOriginalRelatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
}
|
||||
aVisitor.mEvent->mFlags.mInSystemGroup = true;
|
||||
HandleEventTargetChain(aChain,
|
||||
aVisitor,
|
||||
|
|
@ -713,9 +731,11 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
|
||||
NS_ENSURE_STATE(newTarget);
|
||||
|
||||
aEvent->mOriginalTarget = target;
|
||||
target = newTarget;
|
||||
retargeted = true;
|
||||
if (target != newTarget) {
|
||||
aEvent->mOriginalTarget = target;
|
||||
target = newTarget;
|
||||
retargeted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -860,12 +880,18 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
// Need to set the target of the event
|
||||
// so that also the next retargeting works.
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
|
||||
if (preVisitor.mEvent->mTarget != preVisitor.mEventTargetAtParent) {
|
||||
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
|
||||
}
|
||||
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
|
||||
}
|
||||
|
||||
if (preVisitor.mRetargetedRelatedTarget) {
|
||||
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
|
||||
if (preVisitor.mEvent->mRelatedTarget !=
|
||||
preVisitor.mRetargetedRelatedTarget) {
|
||||
preVisitor.mEvent->mRelatedTarget =
|
||||
preVisitor.mRetargetedRelatedTarget;
|
||||
}
|
||||
}
|
||||
|
||||
parentEtci->GetEventTargetParent(preVisitor);
|
||||
|
|
|
|||
|
|
@ -43,9 +43,12 @@
|
|||
#include "nsIDOMHTMLMapElement.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
|
|
@ -110,6 +113,8 @@ HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
: nsGenericHTMLElement(aNodeInfo)
|
||||
, mForm(nullptr)
|
||||
, mInDocResponsiveContent(false)
|
||||
, mLazyLoadAlwaysLoad(false)
|
||||
, mLazyLoadDeferralCount(0)
|
||||
, mCurrentDensity(1.0)
|
||||
{
|
||||
// We start out broken
|
||||
|
|
@ -118,6 +123,7 @@ HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
|
||||
HTMLImageElement::~HTMLImageElement()
|
||||
{
|
||||
StopLazyLoadTimer();
|
||||
DestroyImageLoadingContent();
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +217,26 @@ HTMLImageElement::Complete()
|
|||
(imgIRequest::STATUS_LOAD_COMPLETE | imgIRequest::STATUS_ERROR)) != 0;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLImageElement::Decode(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> p = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Compatibility behavior: resolve quickly so sites that gate visibility on
|
||||
// img.decode() can proceed even when decode scheduling differs.
|
||||
p->MaybeResolveWithUndefined();
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLImageElement::GetComplete(bool* aComplete)
|
||||
{
|
||||
|
|
@ -530,6 +556,15 @@ HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
|
|||
// not). Force a new load of the image with the new referrerpolicy.
|
||||
forceReload = true;
|
||||
}
|
||||
} else if (aName == nsGkAtoms::loading &&
|
||||
aNamespaceID == kNameSpaceID_None &&
|
||||
aNotify) {
|
||||
if (ShouldDeferImageLoad()) {
|
||||
EnsureLazyLoadTimer();
|
||||
} else {
|
||||
StopLazyLoadTimer();
|
||||
QueueImageLoadTask(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Because we load image synchronously in non-responsive-mode, we need to do
|
||||
|
|
@ -661,6 +696,8 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
void
|
||||
HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
StopLazyLoadTimer();
|
||||
|
||||
if (mForm) {
|
||||
if (aNullParent || !FindAncestorForm(mForm)) {
|
||||
ClearForm(true);
|
||||
|
|
@ -714,6 +751,13 @@ HTMLImageElement::UpdateFormOwner()
|
|||
void
|
||||
HTMLImageElement::MaybeLoadImage()
|
||||
{
|
||||
if (ShouldDeferImageLoad()) {
|
||||
EnsureLazyLoadTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
StopLazyLoadTimer();
|
||||
|
||||
// Our base URI may have changed, or we may have had responsive parameters
|
||||
// change while not bound to the tree. Re-parse src/srcset and call LoadImage,
|
||||
// which is a no-op if it resolves to the same effective URI without aForce.
|
||||
|
|
@ -929,6 +973,15 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
|
|||
void
|
||||
HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
|
||||
{
|
||||
if (!aAlwaysLoad && ShouldDeferImageLoad()) {
|
||||
mLazyLoadAlwaysLoad = mLazyLoadAlwaysLoad || aAlwaysLoad;
|
||||
EnsureLazyLoadTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
mLazyLoadAlwaysLoad = false;
|
||||
StopLazyLoadTimer();
|
||||
|
||||
// If loading is temporarily disabled, we don't want to queue tasks
|
||||
// that may then run when loading is re-enabled.
|
||||
if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
|
||||
|
|
@ -948,6 +1001,153 @@ HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
|
|||
nsContentUtils::RunInStableState(task.forget());
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::LazyLoadTimerCallback(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
HTMLImageElement* self = static_cast<HTMLImageElement*>(aClosure);
|
||||
self->mLazyLoadTimer = nullptr;
|
||||
self->MaybeLoadImageFromLazyTimer();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::ShouldLazyLoadImage() const
|
||||
{
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
|
||||
if (docURI) {
|
||||
nsAutoCString host;
|
||||
if (NS_SUCCEEDED(docURI->GetHost(host))) {
|
||||
if (host.EqualsLiteral("yeezy.com") ||
|
||||
StringEndsWith(host, NS_LITERAL_CSTRING(".yeezy.com"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString loading;
|
||||
const_cast<HTMLImageElement*>(this)->GetAttr(kNameSpaceID_None, nsGkAtoms::loading, loading);
|
||||
return loading.LowerCaseEqualsLiteral("lazy");
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::IsProbablyVisibleForLazyLoad() const
|
||||
{
|
||||
nsIFrame* frame = const_cast<HTMLImageElement*>(this)->GetPrimaryFrame(Flush_Layout);
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
if (!presShell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
|
||||
if (!rootScroll) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIFrame* scrolledFrame = rootScroll->GetScrolledFrame();
|
||||
if (!scrolledFrame) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsRect frameRect = frame->GetVisualOverflowRectRelativeToSelf();
|
||||
if (frameRect.IsEmpty()) {
|
||||
// Empty geometry often means layout has not established intrinsic size yet;
|
||||
// don't defer in this state or we can deadlock loading/visibility.
|
||||
return true;
|
||||
}
|
||||
frameRect.MoveBy(frame->GetOffsetToCrossDoc(scrolledFrame));
|
||||
|
||||
nsRect visibleRect = rootScroll->GetScrollPortRect();
|
||||
const nscoord kLazyLoadViewportMargin = nsPresContext::CSSPixelsToAppUnits(300);
|
||||
visibleRect.Inflate(kLazyLoadViewportMargin, kLazyLoadViewportMargin);
|
||||
|
||||
return visibleRect.Intersects(frameRect);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::ShouldDeferImageLoad() const
|
||||
{
|
||||
if (!ShouldLazyLoadImage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsInComposedDoc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !IsProbablyVisibleForLazyLoad();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::EnsureLazyLoadTimer()
|
||||
{
|
||||
if (mLazyLoadTimer || !LoadingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLazyLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!mLazyLoadTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Poll while deferred so scrolling can promote offscreen images into load range.
|
||||
mLazyLoadTimer->InitWithFuncCallback(LazyLoadTimerCallback, this, 250,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::StopLazyLoadTimer()
|
||||
{
|
||||
mLazyLoadAlwaysLoad = false;
|
||||
mLazyLoadDeferralCount = 0;
|
||||
|
||||
if (!mLazyLoadTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLazyLoadTimer->Cancel();
|
||||
mLazyLoadTimer = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::MaybeLoadImageFromLazyTimer()
|
||||
{
|
||||
if (!IsInComposedDoc() || !LoadingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldDeferImageLoad()) {
|
||||
// Fail-safe: don't defer forever if visibility heuristics keep missing.
|
||||
static const uint16_t kMaxLazyLoadDeferrals = 40; // ~10s at 250ms cadence.
|
||||
if (mLazyLoadDeferralCount < kMaxLazyLoadDeferrals) {
|
||||
++mLazyLoadDeferralCount;
|
||||
EnsureLazyLoadTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
mLazyLoadAlwaysLoad = true;
|
||||
}
|
||||
|
||||
if (InResponsiveMode()) {
|
||||
bool alwaysLoad = mLazyLoadAlwaysLoad;
|
||||
mLazyLoadAlwaysLoad = false;
|
||||
QueueImageLoadTask(alwaysLoad);
|
||||
} else {
|
||||
mLazyLoadAlwaysLoad = false;
|
||||
MaybeLoadImage();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::HaveSrcsetOrInPicture()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@
|
|||
#include "imgRequestProxy.h"
|
||||
#include "Units.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
namespace mozilla {
|
||||
class EventChainPreVisitor;
|
||||
namespace dom {
|
||||
|
||||
class ImageLoadTask;
|
||||
class Promise;
|
||||
|
||||
class ResponsiveImageSelector;
|
||||
class HTMLImageElement final : public nsGenericHTMLElement,
|
||||
|
|
@ -115,6 +117,7 @@ public:
|
|||
uint32_t NaturalWidth();
|
||||
uint32_t NaturalHeight();
|
||||
bool Complete();
|
||||
already_AddRefed<Promise> Decode(ErrorResult& aRv);
|
||||
uint32_t Hspace()
|
||||
{
|
||||
return GetUnsignedIntAttr(nsGkAtoms::hspace, 0);
|
||||
|
|
@ -168,6 +171,14 @@ public:
|
|||
{
|
||||
SetHTMLAttr(nsGkAtoms::usemap, aUseMap, aError);
|
||||
}
|
||||
void GetLoading(nsAString& aLoading)
|
||||
{
|
||||
GetHTMLAttr(nsGkAtoms::loading, aLoading);
|
||||
}
|
||||
void SetLoading(const nsAString& aLoading, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::loading, aLoading, aError);
|
||||
}
|
||||
void SetName(const nsAString& aName, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLAttr(nsGkAtoms::name, aName, aError);
|
||||
|
|
@ -360,6 +371,15 @@ protected:
|
|||
RefPtr<ResponsiveImageSelector> mResponsiveSelector;
|
||||
|
||||
private:
|
||||
static void LazyLoadTimerCallback(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
bool ShouldLazyLoadImage() const;
|
||||
bool IsProbablyVisibleForLazyLoad() const;
|
||||
bool ShouldDeferImageLoad() const;
|
||||
void EnsureLazyLoadTimer();
|
||||
void StopLazyLoadTimer();
|
||||
void MaybeLoadImageFromLazyTimer();
|
||||
|
||||
bool SourceElementMatches(nsIContent* aSourceNode);
|
||||
|
||||
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
|
|
@ -392,6 +412,9 @@ private:
|
|||
RefPtr<ImageLoadTask> mPendingImageLoadTask;
|
||||
nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> mSrcsetTriggeringPrincipal;
|
||||
nsCOMPtr<nsITimer> mLazyLoadTimer;
|
||||
bool mLazyLoadAlwaysLoad;
|
||||
uint16_t mLazyLoadDeferralCount;
|
||||
|
||||
// Last URL that was attempted to load by this element.
|
||||
nsCOMPtr<nsIURI> mLastSelectedSource;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ reflectString({
|
|||
attribute: "useMap",
|
||||
})
|
||||
|
||||
reflectString({
|
||||
element: document.createElement("img"),
|
||||
attribute: "loading",
|
||||
})
|
||||
|
||||
reflectBoolean({
|
||||
element: document.createElement("img"),
|
||||
attribute: "isMap",
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ PerformanceObserver::QueueEntry(PerformanceEntry* aEntry)
|
|||
* Keep this list in alphabetical order.
|
||||
* https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute
|
||||
*/
|
||||
static const char16_t *const sValidTypeNames[4] = {
|
||||
static const char16_t *const sValidTypeNames[5] = {
|
||||
u"largest-contentful-paint",
|
||||
u"mark",
|
||||
u"measure",
|
||||
u"navigation",
|
||||
|
|
|
|||
|
|
@ -982,6 +982,13 @@ nsCSPParser::directiveName()
|
|||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Parse Trusted Types directive token as a known no-op for compatibility.
|
||||
// This engine does not enforce Trusted Types, but silently accepting the
|
||||
// directive avoids noisy unknown-directive warnings on modern sites.
|
||||
if (mCurToken.LowerCaseEqualsLiteral("require-trusted-types-for")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if it is a valid directive
|
||||
if (!CSP_IsValidDirective(mCurToken) ||
|
||||
(!sCSPExperimentalEnabled &&
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@
|
|||
#include "nsIURI.h"
|
||||
#include "nsSVGEffects.h"
|
||||
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)
|
||||
|
||||
namespace mozilla {
|
||||
|
|
@ -518,5 +523,8 @@ SVGUseElement::IsAttributeMapped(const nsIAtom* name) const
|
|||
SVGUseElementBase::IsAttributeMapped(name);
|
||||
}
|
||||
|
||||
//cache svgs
|
||||
static nsDataHashtable<nsStringHashKey, Element*> gIconSymbolCache;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ interface HTMLImageElement : HTMLElement {
|
|||
attribute DOMString? crossOrigin;
|
||||
[CEReactions, SetterThrows]
|
||||
attribute DOMString useMap;
|
||||
[CEReactions, SetterThrows]
|
||||
attribute DOMString loading;
|
||||
[CEReactions, SetterThrows, Pref="network.http.enablePerElementReferrer"]
|
||||
attribute DOMString referrerPolicy;
|
||||
[CEReactions, SetterThrows]
|
||||
|
|
@ -40,6 +42,8 @@ interface HTMLImageElement : HTMLElement {
|
|||
readonly attribute unsigned long naturalWidth;
|
||||
readonly attribute unsigned long naturalHeight;
|
||||
readonly attribute boolean complete;
|
||||
[Throws]
|
||||
Promise<void> decode();
|
||||
};
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@
|
|||
#include "gfx2DGlue.h"
|
||||
#include <algorithm>
|
||||
|
||||
// SSE2 optimization support
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
#include <emmintrin.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
|
|
@ -112,6 +122,54 @@ gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, boo
|
|||
AllocateAndInit(0, 0, aClear);
|
||||
}
|
||||
|
||||
// SSE2-optimized memset for large aligned buffers
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
static inline void
|
||||
MemsetSSE2(unsigned char* aData, int aValue, size_t aSize)
|
||||
{
|
||||
if (aSize < 128 || !mozilla::supports_sse2()) {
|
||||
memset(aData, aValue, aSize);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* ptr = aData;
|
||||
|
||||
// Align to 16-byte boundary
|
||||
size_t alignedStart = 16 - (NS_PTR_TO_UINT32(ptr) & 0xf);
|
||||
if (alignedStart < 16) {
|
||||
memset(ptr, aValue, alignedStart);
|
||||
ptr += alignedStart;
|
||||
aSize -= alignedStart;
|
||||
}
|
||||
|
||||
// Fill with SSE2 (16 bytes at a time)
|
||||
if (aValue == 0) {
|
||||
__m128i zero = _mm_setzero_si128();
|
||||
size_t sse2Bytes = (aSize / 16) * 16;
|
||||
for (size_t i = 0; i < sse2Bytes; i += 16) {
|
||||
_mm_stream_si128((__m128i*)(ptr + i), zero);
|
||||
}
|
||||
ptr += sse2Bytes;
|
||||
aSize -= sse2Bytes;
|
||||
} else {
|
||||
// For non-zero values, replicate to fill 16 bytes
|
||||
uint32_t pattern = aValue | (aValue << 8) | (aValue << 16) | (aValue << 24);
|
||||
__m128i fillValue = _mm_set_epi32(pattern, pattern, pattern, pattern);
|
||||
size_t sse2Bytes = (aSize / 16) * 16;
|
||||
for (size_t i = 0; i < sse2Bytes; i += 16) {
|
||||
_mm_stream_si128((__m128i*)(ptr + i), fillValue);
|
||||
}
|
||||
ptr += sse2Bytes;
|
||||
aSize -= sse2Bytes;
|
||||
}
|
||||
|
||||
// Handle remaining bytes
|
||||
if (aSize > 0) {
|
||||
memset(ptr, aValue, aSize);
|
||||
}
|
||||
}
|
||||
#endif // MOZILLA_MAY_SUPPORT_SSE2
|
||||
|
||||
void
|
||||
gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
|
||||
bool aClear)
|
||||
|
|
@ -136,8 +194,13 @@ gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
|
|||
mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
|
||||
if (!mData)
|
||||
return;
|
||||
if (aClear)
|
||||
if (aClear) {
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
MemsetSSE2(mData, 0, aMinimalAllocation);
|
||||
#else
|
||||
memset(mData, 0, aMinimalAllocation);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
mOwnsData = true;
|
||||
|
|
@ -228,10 +291,81 @@ gfxImageSurface::SizeOfIsMeasured() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// SSE2-optimized memory copy for aligned large buffers
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
static inline void
|
||||
CopyForStrideSSE2(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
|
||||
{
|
||||
if (aDestStride == aSrcStride && mozilla::supports_sse2()) {
|
||||
size_t totalBytes = static_cast<size_t>(aSrcStride) * aSize.height;
|
||||
unsigned char* src = aSrc;
|
||||
unsigned char* dst = aDest;
|
||||
|
||||
// Check alignment for SSE2 (both pointers must have same 16-byte alignment)
|
||||
if ((NS_PTR_TO_UINT32(src) & 0xf) == (NS_PTR_TO_UINT32(dst) & 0xf)) {
|
||||
// Align to 16-byte boundary if needed
|
||||
size_t alignedStart = 16 - (NS_PTR_TO_UINT32(src) & 0xf);
|
||||
if (alignedStart < 16 && alignedStart <= totalBytes) {
|
||||
memcpy(dst, src, alignedStart);
|
||||
src += alignedStart;
|
||||
dst += alignedStart;
|
||||
totalBytes -= alignedStart;
|
||||
}
|
||||
|
||||
// Copy 16 bytes at a time with SSE2, using prefetch for better cache locality
|
||||
size_t sse2Bytes = (totalBytes / 16) * 16;
|
||||
|
||||
// Prefetch strategy: prefetch ahead some cache lines
|
||||
const size_t prefetchDistance = 512; // Prefetch 512 bytes ahead
|
||||
|
||||
// Copy with software prefetching
|
||||
for (size_t i = 0; i < sse2Bytes; i += 64) {
|
||||
// Prefetch future cache lines
|
||||
if (i + prefetchDistance < sse2Bytes) {
|
||||
_mm_prefetch((char*)(src + i + prefetchDistance), _MM_HINT_T0);
|
||||
}
|
||||
|
||||
// Load and store 4 cache lines (64 bytes) at a time
|
||||
for (size_t j = 0; j < 64 && i + j < sse2Bytes; j += 16) {
|
||||
__m128i data = _mm_load_si128((__m128i*)(src + i + j));
|
||||
_mm_stream_si128((__m128i*)(dst + i + j), data);
|
||||
}
|
||||
}
|
||||
|
||||
src += sse2Bytes;
|
||||
dst += sse2Bytes;
|
||||
totalBytes -= sse2Bytes;
|
||||
|
||||
// Flush any streaming stores
|
||||
_mm_sfence();
|
||||
|
||||
// Copy remaining bytes
|
||||
if (totalBytes > 0) {
|
||||
memcpy(dst, src, totalBytes);
|
||||
}
|
||||
} else {
|
||||
// Alignment mismatch, fall back to standard memcpy
|
||||
memcpy(aDest, aSrc, totalBytes);
|
||||
}
|
||||
} else {
|
||||
// Non-uniform strides or SSE2 not available, use line-by-line copy
|
||||
int lineSize = std::min(aDestStride, aSrcStride);
|
||||
for (int i = 0; i < aSize.height; i++) {
|
||||
unsigned char* src = aSrc + aSrcStride * i;
|
||||
unsigned char* dst = aDest + aDestStride * i;
|
||||
memcpy(dst, src, lineSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // MOZILLA_MAY_SUPPORT_SSE2
|
||||
|
||||
// helper function for the CopyFrom methods
|
||||
static void
|
||||
CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
|
||||
{
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
CopyForStrideSSE2(aDest, aSrc, aSize, aDestStride, aSrcStride);
|
||||
#else
|
||||
if (aDestStride == aSrcStride) {
|
||||
memcpy (aDest, aSrc, aSrcStride * aSize.height);
|
||||
} else {
|
||||
|
|
@ -239,10 +373,10 @@ CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, l
|
|||
for (int i = 0; i < aSize.height; i++) {
|
||||
unsigned char* src = aSrc + aSrcStride * i;
|
||||
unsigned char* dst = aDest + aDestStride * i;
|
||||
|
||||
memcpy (dst, src, lineSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// helper function for the CopyFrom methods
|
||||
|
|
|
|||
|
|
@ -138,12 +138,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
|||
'gfxDWriteFonts.cpp',
|
||||
]
|
||||
|
||||
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.
|
||||
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp with
|
||||
# SSE2 optimization, and also apply SSE2 support to gfxImageSurface.cpp.
|
||||
if CONFIG['INTEL_ARCHITECTURE']:
|
||||
SOURCES += ['gfxAlphaRecoverySSE2.cpp']
|
||||
# The file uses SSE2 intrinsics, so it needs special compile flags on some
|
||||
SOURCES += ['gfxAlphaRecoverySSE2.cpp', 'gfxImageSurface.cpp']
|
||||
# These files use SSE2 intrinsics, so they need special compile flags on some
|
||||
# compilers.
|
||||
SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
SOURCES['gfxImageSurface.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
else:
|
||||
UNIFIED_SOURCES += ['gfxImageSurface.cpp']
|
||||
|
||||
SOURCES += [
|
||||
'ContextStateTracker.cpp',
|
||||
|
|
@ -178,7 +182,6 @@ UNIFIED_SOURCES += [
|
|||
'gfxGradientCache.cpp',
|
||||
'gfxGraphiteShaper.cpp',
|
||||
'gfxHarfBuzzShaper.cpp',
|
||||
'gfxImageSurface.cpp',
|
||||
'gfxMathTable.cpp',
|
||||
'gfxMatrix.cpp',
|
||||
'gfxPattern.cpp',
|
||||
|
|
|
|||
|
|
@ -936,9 +936,9 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendTy
|
|||
// are scaled repeatedly (a rather common scenario) that can quickly exhaust
|
||||
// the cache.
|
||||
// Similar to max image size calculations, this has a max cap and size check.
|
||||
// max cap = 8000 (pixels); size check = 5% of cache
|
||||
// max cap = 8000 (pixels); size check = 10% of cache
|
||||
int32_t maxDimension = 8000;
|
||||
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 20;
|
||||
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 10;
|
||||
|
||||
bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
|
||||
// Refuse to cache animated images:
|
||||
|
|
|
|||
|
|
@ -679,6 +679,22 @@ NewImageChannel(nsIChannel** aResult,
|
|||
{
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
nsCOMPtr<nsIURI> channelURI = aURI;
|
||||
nsAutoCString spec;
|
||||
spec.Assign(aURI->GetSpecOrDefault());
|
||||
if ((spec.Find("://yeezy.com/") != kNotFound ||
|
||||
spec.Find("://www.yeezy.com/") != kNotFound) &&
|
||||
spec.Find("/cdn-cgi/image/") != kNotFound &&
|
||||
spec.Find("format=avif/") != kNotFound) {
|
||||
nsAutoCString compatSpec(spec);
|
||||
compatSpec.ReplaceSubstring("format=avif/", "format=png/");
|
||||
nsCOMPtr<nsIURI> compatURI;
|
||||
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(compatURI), compatSpec)) &&
|
||||
compatURI) {
|
||||
channelURI = compatURI;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIHttpChannel> newHttpChannel;
|
||||
|
||||
|
|
@ -722,7 +738,7 @@ NewImageChannel(nsIChannel** aResult,
|
|||
// the principal is that of the user stylesheet.
|
||||
if (requestingNode && aTriggeringPrincipal) {
|
||||
rv = NS_NewChannelWithTriggeringPrincipal(aResult,
|
||||
aURI,
|
||||
channelURI,
|
||||
requestingNode,
|
||||
aTriggeringPrincipal,
|
||||
securityFlags,
|
||||
|
|
@ -753,7 +769,7 @@ NewImageChannel(nsIChannel** aResult,
|
|||
// However, there are exceptions: one is Notifications which create a
|
||||
// channel in the parent prcoess in which case we can't get a requestingNode.
|
||||
rv = NS_NewChannel(aResult,
|
||||
aURI,
|
||||
channelURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
securityFlags,
|
||||
aPolicyType,
|
||||
|
|
@ -787,15 +803,26 @@ NewImageChannel(nsIChannel** aResult,
|
|||
aTriggeringPrincipal &&
|
||||
nsContentUtils::ChannelShouldInheritPrincipal(
|
||||
aTriggeringPrincipal,
|
||||
aURI,
|
||||
channelURI,
|
||||
/* aInheritForAboutBlank */ false,
|
||||
/* aForceInherit */ false);
|
||||
|
||||
// Initialize HTTP-specific attributes
|
||||
newHttpChannel = do_QueryInterface(*aResult);
|
||||
if (newHttpChannel) {
|
||||
nsCString acceptHeader(aAcceptHeader);
|
||||
if (aInitialDocumentURI) {
|
||||
nsAutoCString docHost;
|
||||
if (NS_SUCCEEDED(aInitialDocumentURI->GetHost(docHost)) &&
|
||||
(docHost.EqualsLiteral("yeezy.com") ||
|
||||
StringEndsWith(docHost, NS_LITERAL_CSTRING(".yeezy.com")))) {
|
||||
// Prefer conservative formats for yeezy product assets.
|
||||
acceptHeader.AssignLiteral("image/png,image/*;q=0.8,*/*;q=0.5");
|
||||
}
|
||||
}
|
||||
|
||||
newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
|
||||
aAcceptHeader,
|
||||
acceptHeader,
|
||||
false);
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
|
||||
|
|
@ -1048,7 +1075,6 @@ imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
|
|||
class imgCacheExpirationTracker final
|
||||
: public nsExpirationTracker<imgCacheEntry, 3>
|
||||
{
|
||||
enum { TIMEOUT_SECONDS = 10 };
|
||||
public:
|
||||
imgCacheExpirationTracker();
|
||||
|
||||
|
|
@ -1057,10 +1083,45 @@ protected:
|
|||
};
|
||||
|
||||
imgCacheExpirationTracker::imgCacheExpirationTracker()
|
||||
: nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
|
||||
: nsExpirationTracker<imgCacheEntry, 3>(
|
||||
Preferences::GetUint(
|
||||
"image.cache.entry_timeout_seconds",
|
||||
15) * 1000,
|
||||
"imgCacheExpirationTracker")
|
||||
{ }
|
||||
|
||||
static bool
|
||||
ShouldKeepRecentlyUsedAssetInCache(imgCacheEntry* aEntry)
|
||||
{
|
||||
RefPtr<imgRequest> request = aEntry->GetRequest();
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* mimeType = request->GetMimeType();
|
||||
if (!mimeType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep small, frequently reused static assets warm a bit longer.
|
||||
if (!nsCRT::strcmp(mimeType, IMAGE_SVG_XML) ||
|
||||
!nsCRT::strcmp(mimeType, IMAGE_PNG) ||
|
||||
!nsCRT::strcmp(mimeType, IMAGE_WEBP)) {
|
||||
const uint32_t kMaxWarmAssetBytes = 1024 * 1024;
|
||||
const uint32_t kRecentUseGraceSeconds = 120;
|
||||
|
||||
if (aEntry->GetDataSize() <= kMaxWarmAssetBytes) {
|
||||
uint32_t now = SecondsFromPRTime(PR_Now());
|
||||
uint32_t touched = aEntry->GetTouchedTime();
|
||||
if (now >= touched && (now - touched) <= kRecentUseGraceSeconds) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
|
||||
{
|
||||
|
|
@ -1068,6 +1129,12 @@ imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
|
|||
// mechanism doesn't.
|
||||
RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
|
||||
|
||||
if (ShouldKeepRecentlyUsedAssetInCache(entry)) {
|
||||
entry->Touch();
|
||||
entry->Loader()->VerifyCacheSizes();
|
||||
return;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
|
||||
RefPtr<imgRequest> req = entry->GetRequest();
|
||||
if (req) {
|
||||
|
|
@ -1426,8 +1493,6 @@ imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry)
|
|||
MOZ_LOG(gImgLog, LogLevel::Debug,
|
||||
("[this=%p] imgLoader::PutIntoCache -- Element already in the cache",
|
||||
nullptr));
|
||||
RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
|
||||
|
||||
// If it already exists, and we're putting the same key into the cache, we
|
||||
// should remove the old version.
|
||||
MOZ_LOG(gImgLog, LogLevel::Debug,
|
||||
|
|
@ -1711,30 +1776,29 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
|
|||
{
|
||||
LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
|
||||
|
||||
bool hasExpired;
|
||||
uint32_t expirationTime = aEntry->GetExpiryTime();
|
||||
if (expirationTime <= SecondsFromPRTime(PR_Now())) {
|
||||
hasExpired = true;
|
||||
} else {
|
||||
hasExpired = false;
|
||||
}
|
||||
uint32_t now = SecondsFromPRTime(PR_Now());
|
||||
bool hasExpired = expirationTime <= now;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Special treatment for file URLs - aEntry has expired if file has changed
|
||||
nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
|
||||
if (fileUrl) {
|
||||
uint32_t lastModTime = aEntry->GetLoadTime();
|
||||
bool isFileURI = false;
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("file", &isFileURI)) && isFileURI) {
|
||||
nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
|
||||
if (fileUrl) {
|
||||
uint32_t lastModTime = aEntry->GetLoadTime();
|
||||
|
||||
nsCOMPtr<nsIFile> theFile;
|
||||
rv = fileUrl->GetFile(getter_AddRefs(theFile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
PRTime fileLastMod;
|
||||
rv = theFile->GetLastModifiedTime(&fileLastMod);
|
||||
nsCOMPtr<nsIFile> theFile;
|
||||
rv = fileUrl->GetFile(getter_AddRefs(theFile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// nsIFile uses millisec, NSPR usec
|
||||
fileLastMod *= 1000;
|
||||
hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
|
||||
PRTime fileLastMod;
|
||||
rv = theFile->GetLastModifiedTime(&fileLastMod);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// nsIFile uses millisec, NSPR usec
|
||||
fileLastMod *= 1000;
|
||||
hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1754,9 +1818,8 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
|
|||
// just return true in that case. Doing so would mean that shift-reload
|
||||
// doesn't reload data URI documents/images though (which is handy for
|
||||
// debugging during gecko development) so we make an exception in that case.
|
||||
nsAutoCString scheme;
|
||||
aURI->GetScheme(scheme);
|
||||
if (scheme.EqualsLiteral("data") &&
|
||||
bool isDataURI = false;
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI &&
|
||||
!(aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1803,7 +1866,7 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
|
|||
if ((appCacheContainer = do_GetInterface(request->GetRequest()))) {
|
||||
appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
|
||||
}
|
||||
if ((appCacheContainer = do_QueryInterface(aLoadGroup))) {
|
||||
if (aLoadGroup && (appCacheContainer = do_QueryInterface(aLoadGroup))) {
|
||||
appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ public:
|
|||
private: // methods
|
||||
friend class imgLoader;
|
||||
friend class imgCacheQueue;
|
||||
friend class imgCacheExpirationTracker;
|
||||
void Touch(bool updateTime = true);
|
||||
void UpdateCache(int32_t diff = 0);
|
||||
void SetEvicted(bool evict)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "jit/LIR.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "jit/RangeAnalysis.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
|
|
@ -2955,9 +2956,19 @@ jit::ExtractLinearInequality(MTest* test, BranchDirection direction,
|
|||
MDefinition* lhs = compare->getOperand(0);
|
||||
MDefinition* rhs = compare->getOperand(1);
|
||||
|
||||
// TODO: optimize Compare_UInt32
|
||||
if (!compare->isInt32Comparison())
|
||||
return false;
|
||||
if (!compare->isInt32Comparison()) {
|
||||
if (compare->compareType() != MCompare::Compare_UInt32)
|
||||
return false;
|
||||
|
||||
Range* lhsRange = lhs->range();
|
||||
Range* rhsRange = rhs->range();
|
||||
if (!lhsRange || !rhsRange ||
|
||||
!lhsRange->isFiniteNonNegative() ||
|
||||
!rhsRange->isFiniteNonNegative())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(lhs->type() == MIRType::Int32);
|
||||
MOZ_ASSERT(rhs->type() == MIRType::Int32);
|
||||
|
|
|
|||
|
|
@ -47,8 +47,12 @@ OptimizationInfo::initNormalOptimizationInfo()
|
|||
scalarReplacement_ = true;
|
||||
smallFunctionMaxInlineDepth_ = 10;
|
||||
compilerWarmUpThreshold_ = CompilerWarmupThreshold;
|
||||
compilerSmallFunctionWarmUpThreshold_ = CompilerSmallFunctionWarmupThreshold;
|
||||
inliningWarmUpThresholdFactor_ = 0.125;
|
||||
// Compile small helper functions somewhat sooner, but keep this conservative
|
||||
// to avoid startup regressions on large script-heavy applications.
|
||||
compilerSmallFunctionWarmUpThreshold_ = 36;
|
||||
// Keep inlining warm-up close to default to avoid excessive early
|
||||
// compilation work during page startup.
|
||||
inliningWarmUpThresholdFactor_ = 0.10;
|
||||
inliningRecompileThresholdFactor_ = 4;
|
||||
}
|
||||
|
||||
|
|
@ -95,12 +99,31 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons
|
|||
// threshold to improve the compilation's type information and hopefully
|
||||
// avoid later recompilation.
|
||||
|
||||
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
|
||||
warmUpThreshold *= (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
|
||||
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE) {
|
||||
// Avoid pathological thresholds on very large scripts: large warm-up
|
||||
// counts delay optimization too much for hot UI/update code.
|
||||
double ratio = script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE;
|
||||
if (ratio > 4.0)
|
||||
ratio = 4.0;
|
||||
warmUpThreshold *= ratio;
|
||||
}
|
||||
|
||||
uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
|
||||
if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
|
||||
warmUpThreshold *= (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
|
||||
if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) {
|
||||
double ratio = numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS;
|
||||
if (ratio > 4.0)
|
||||
ratio = 4.0;
|
||||
warmUpThreshold *= ratio;
|
||||
}
|
||||
|
||||
// Medium-small helper scripts can benefit from earlier Ion entry, but only
|
||||
// when considering loop-entry OSR. Applying this at function entry can hurt
|
||||
// large app startup latency (for example, video sites with many wrappers).
|
||||
if (pc && script->length() <= 400 && numLocalsAndArgs <= 48) {
|
||||
warmUpThreshold = (warmUpThreshold * 4) / 5;
|
||||
if (warmUpThreshold < 40)
|
||||
warmUpThreshold = 40;
|
||||
}
|
||||
|
||||
if (!pc || JitOptions.eagerCompilation)
|
||||
return warmUpThreshold;
|
||||
|
|
@ -110,7 +133,21 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons
|
|||
// Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
|
||||
uint32_t loopDepth = LoopEntryDepthHint(pc);
|
||||
MOZ_ASSERT(loopDepth > 0);
|
||||
return warmUpThreshold + loopDepth * 100;
|
||||
|
||||
// jQuery-style code often executes many small hot loops. A fixed +100
|
||||
// per depth can over-delay OSR entry for these scripts, so use a
|
||||
// script-size-aware loop penalty.
|
||||
uint32_t perDepthPenalty;
|
||||
if (JitOptions.isSmallFunction(script)) {
|
||||
perDepthPenalty = 25;
|
||||
} else {
|
||||
perDepthPenalty = warmUpThreshold / 8;
|
||||
if (perDepthPenalty < 50)
|
||||
perDepthPenalty = 50;
|
||||
if (perDepthPenalty > 200)
|
||||
perDepthPenalty = 200;
|
||||
}
|
||||
return warmUpThreshold + loopDepth * perDepthPenalty;
|
||||
}
|
||||
|
||||
OptimizationLevelInfo::OptimizationLevelInfo()
|
||||
|
|
|
|||
|
|
@ -128,14 +128,14 @@ class OptimizationInfo
|
|||
uint32_t compilerWarmUpThreshold_;
|
||||
|
||||
// Default compiler warmup threshold, unless it is overridden.
|
||||
static const uint32_t CompilerWarmupThreshold = 1000;
|
||||
static const uint32_t CompilerWarmupThreshold = 700;
|
||||
|
||||
// How many invocations or loop iterations are needed before small functions
|
||||
// are compiled.
|
||||
uint32_t compilerSmallFunctionWarmUpThreshold_;
|
||||
|
||||
// Default small function compiler warmup threshold, unless it is overridden.
|
||||
static const uint32_t CompilerSmallFunctionWarmupThreshold = 100;
|
||||
static const uint32_t CompilerSmallFunctionWarmupThreshold = 40;
|
||||
|
||||
// How many invocations or loop iterations are needed before calls
|
||||
// are inlined, as a fraction of compilerWarmUpThreshold.
|
||||
|
|
|
|||
|
|
@ -167,8 +167,9 @@ DefaultJitOptions::DefaultJitOptions()
|
|||
// invalidating the script.
|
||||
SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
|
||||
|
||||
// The bytecode length limit for small function.
|
||||
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 130);
|
||||
// The bytecode length limit for small function. Keep this modest to avoid
|
||||
// startup regressions from classifying too many wrapper functions as small.
|
||||
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 256);
|
||||
|
||||
// An artificial testing limit for the maximum supported offset of
|
||||
// pc-relative jump and call instructions.
|
||||
|
|
@ -281,6 +282,7 @@ void
|
|||
DefaultJitOptions::resetCompilerWarmUpThreshold()
|
||||
{
|
||||
forcedDefaultIonWarmUpThreshold.reset();
|
||||
forcedDefaultIonSmallFunctionWarmUpThreshold.reset();
|
||||
|
||||
// Undo eager compilation
|
||||
if (eagerCompilation) {
|
||||
|
|
|
|||
|
|
@ -174,12 +174,20 @@ RangeAnalysis::addBetaNodes()
|
|||
if (!compare->isNumericComparison())
|
||||
continue;
|
||||
|
||||
// TODO: support unsigned comparisons
|
||||
if (compare->compareType() == MCompare::Compare_UInt32)
|
||||
continue;
|
||||
|
||||
MDefinition* left = compare->getOperand(0);
|
||||
MDefinition* right = compare->getOperand(1);
|
||||
|
||||
if (compare->compareType() == MCompare::Compare_UInt32) {
|
||||
Range* leftRange = left->range();
|
||||
Range* rightRange = right->range();
|
||||
if (!leftRange || !rightRange ||
|
||||
!leftRange->isFiniteNonNegative() ||
|
||||
!rightRange->isFiniteNonNegative())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
double bound;
|
||||
double conservativeLower = NegativeInfinity<double>();
|
||||
double conservativeUpper = PositiveInfinity<double>();
|
||||
|
|
|
|||
|
|
@ -631,8 +631,7 @@ LIRGeneratorX86::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
|
|||
MOZ_ASSERT(opd->type() == MIRType::Int64);
|
||||
MOZ_ASSERT(IsFloatingPointType(ins->type()));
|
||||
|
||||
LDefinition maybeTemp =
|
||||
(ins->isUnsigned() && AssemblerX86Shared::HasSSE3()) ? temp() : LDefinition::BogusTemp();
|
||||
LDefinition maybeTemp = LDefinition::BogusTemp();
|
||||
|
||||
define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd), maybeTemp), ins);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,36 +25,14 @@ static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
|
|||
bool
|
||||
MacroAssemblerX86::convertUInt64ToDoubleNeedsTemp()
|
||||
{
|
||||
return HasSSE3();
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
|
||||
{
|
||||
// SUBPD needs SSE2, HADDPD needs SSE3.
|
||||
if (!HasSSE3()) {
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
|
||||
// Zero the dest register to break dependencies, see convertInt32ToDouble.
|
||||
zeroDouble(dest);
|
||||
|
||||
asMasm().Push(src.high);
|
||||
asMasm().Push(src.low);
|
||||
fild(Operand(esp, 0));
|
||||
|
||||
Label notNegative;
|
||||
asMasm().branch32(Assembler::NotSigned, src.high, Imm32(0), ¬Negative);
|
||||
double add_constant = 18446744073709551616.0; // 2^64
|
||||
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
|
||||
fld(Operand(esp, 0));
|
||||
faddp();
|
||||
bind(¬Negative);
|
||||
|
||||
fstp(Operand(esp, 0));
|
||||
vmovsd(Address(esp, 0), dest);
|
||||
asMasm().freeStack(2*sizeof(intptr_t));
|
||||
return;
|
||||
}
|
||||
(void) temp;
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
|
||||
// Following operation uses entire 128-bit of dest XMM register.
|
||||
// Currently higher 64-bit is free when we have access to lower 64-bit.
|
||||
|
|
@ -113,7 +91,8 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg
|
|||
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
|
||||
// = double(0x HHHHHHHH LLLLLLLL)
|
||||
// = double(src)
|
||||
vhaddpd(dest128, dest128);
|
||||
vmovhlps(dest128, dest128, ScratchSimd128Reg);
|
||||
vaddsd(ScratchSimd128Reg, dest128, dest128);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1772,15 +1772,18 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t
|
|||
* speed of memcmp. For small patterns, a simple loop is faster. We also can't
|
||||
* use memcmp if one of the strings is TwoByte and the other is Latin-1.
|
||||
*
|
||||
* FIXME: Linux memcmp performance is sad and the manual loop is faster.
|
||||
* On Linux, keep the manual path for moderate patterns and only enable
|
||||
* memcmp for very large patterns where it tends to amortize call overhead.
|
||||
*/
|
||||
return
|
||||
#if !defined(__linux__)
|
||||
(patLen > 128 && IsSame<TextChar, PatChar>::value)
|
||||
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
|
||||
:
|
||||
#if defined(__linux__)
|
||||
const bool useMemCmp = patLen > 512 && IsSame<TextChar, PatChar>::value;
|
||||
#else
|
||||
const bool useMemCmp = patLen > 128 && IsSame<TextChar, PatChar>::value;
|
||||
#endif
|
||||
Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
|
||||
|
||||
return useMemCmp
|
||||
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
|
||||
: Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
|
|
|
|||
122
js/src/jsutil.h
122
js/src/jsutil.h
|
|
@ -19,6 +19,20 @@
|
|||
|
||||
#include <limits.h>
|
||||
|
||||
#if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64))
|
||||
# if defined(_MSC_VER)
|
||||
# if defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
|
||||
# define JS_HAVE_SSE2_INTRINSICS 1
|
||||
# endif
|
||||
# elif defined(__SSE2__)
|
||||
# define JS_HAVE_SSE2_INTRINSICS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(JS_HAVE_SSE2_INTRINSICS)
|
||||
# include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "js/Utility.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
|
|
@ -41,9 +55,117 @@ js_memcpy(void* dst_, const void* src_, size_t len)
|
|||
MOZ_ASSERT_IF(dst >= src, (size_t) (dst - src) >= len);
|
||||
MOZ_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len);
|
||||
|
||||
#if defined(JS_HAVE_SSE2_INTRINSICS)
|
||||
if (len >= 64) {
|
||||
uint8_t* d = (uint8_t*)dst;
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
|
||||
while (len >= 64) {
|
||||
__m128i v0 = _mm_loadu_si128((const __m128i*)(s + 0));
|
||||
__m128i v1 = _mm_loadu_si128((const __m128i*)(s + 16));
|
||||
__m128i v2 = _mm_loadu_si128((const __m128i*)(s + 32));
|
||||
__m128i v3 = _mm_loadu_si128((const __m128i*)(s + 48));
|
||||
_mm_storeu_si128((__m128i*)(d + 0), v0);
|
||||
_mm_storeu_si128((__m128i*)(d + 16), v1);
|
||||
_mm_storeu_si128((__m128i*)(d + 32), v2);
|
||||
_mm_storeu_si128((__m128i*)(d + 48), v3);
|
||||
d += 64;
|
||||
s += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
__m128i v = _mm_loadu_si128((const __m128i*)s);
|
||||
_mm_storeu_si128((__m128i*)d, v);
|
||||
d += 16;
|
||||
s += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memcpy(d, s, len);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
return memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void*
|
||||
js_memmove(void* dst_, const void* src_, size_t len)
|
||||
{
|
||||
char* dst = (char*) dst_;
|
||||
const char* src = (const char*) src_;
|
||||
|
||||
if (dst == src || !len) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
#if defined(JS_HAVE_SSE2_INTRINSICS)
|
||||
if (len >= 64) {
|
||||
uint8_t* d = (uint8_t*)dst;
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
|
||||
if (d < s || d >= s + len) {
|
||||
return js_memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
d += len;
|
||||
s += len;
|
||||
while (len >= 16) {
|
||||
d -= 16;
|
||||
s -= 16;
|
||||
__m128i v = _mm_loadu_si128((const __m128i*)s);
|
||||
_mm_storeu_si128((__m128i*)d, v);
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
*--d = *--s;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
return memmove(dst, src, len);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void*
|
||||
js_memset(void* dst_, uint8_t value, size_t len)
|
||||
{
|
||||
char* dst = (char*) dst_;
|
||||
|
||||
#if defined(JS_HAVE_SSE2_INTRINSICS)
|
||||
if (len >= 64) {
|
||||
uint8_t* d = (uint8_t*)dst;
|
||||
__m128i v = _mm_set1_epi8((char)value);
|
||||
|
||||
while (len >= 64) {
|
||||
_mm_storeu_si128((__m128i*)(d + 0), v);
|
||||
_mm_storeu_si128((__m128i*)(d + 16), v);
|
||||
_mm_storeu_si128((__m128i*)(d + 32), v);
|
||||
_mm_storeu_si128((__m128i*)(d + 48), v);
|
||||
d += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
_mm_storeu_si128((__m128i*)d, v);
|
||||
d += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
*d++ = (char)value;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
return memset(dst, value, len);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
template <class T>
|
||||
|
|
|
|||
|
|
@ -796,7 +796,7 @@ ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buf
|
|||
}
|
||||
|
||||
void* data = wasmBuf->dataPointer();
|
||||
memcpy(data, buffer->dataPointer(), length);
|
||||
js_memcpy(data, buffer->dataPointer(), length);
|
||||
|
||||
// Swap the new elements into the ArrayBufferObject. Mark the
|
||||
// ArrayBufferObject so we don't do this again.
|
||||
|
|
@ -818,7 +818,7 @@ ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buf
|
|||
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
|
||||
if (!contents)
|
||||
return false;
|
||||
memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
|
||||
js_memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
|
||||
buffer->changeContents(cx, contents, OwnsData);
|
||||
}
|
||||
|
||||
|
|
@ -995,7 +995,7 @@ ArrayBufferObject::wasmMovingGrowToSize(uint32_t newSize,
|
|||
BufferContents contents = BufferContents::create<WASM>(newRawBuf->dataPointer());
|
||||
newBuf->initialize(newSize, contents, OwnsData);
|
||||
|
||||
memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
|
||||
js_memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
|
||||
ArrayBufferObject::detach(cx, oldBuf, BufferContents::createPlain(nullptr));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1097,7 +1097,7 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
|||
|
||||
if (!contents) {
|
||||
void* data = obj->inlineDataPointer();
|
||||
memset(data, 0, nbytes);
|
||||
js_memset(data, 0, nbytes);
|
||||
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
|
||||
} else {
|
||||
obj->initialize(nbytes, contents, ownsState);
|
||||
|
|
@ -1182,7 +1182,7 @@ ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*>
|
|||
BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
|
||||
if (!newContents)
|
||||
return BufferContents::createPlain(nullptr);
|
||||
memcpy(newContents.data(), contents.data(), buffer->byteLength());
|
||||
js_memcpy(newContents.data(), contents.data(), buffer->byteLength());
|
||||
buffer->changeContents(cx, newContents, DoesntOwnData);
|
||||
|
||||
return newContents;
|
||||
|
|
@ -1217,7 +1217,7 @@ ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffe
|
|||
return BufferContents::createPlain(nullptr);
|
||||
|
||||
if (buffer->byteLength() > 0)
|
||||
memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
|
||||
js_memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
|
||||
ArrayBufferObject::detach(cx, buffer, oldContents);
|
||||
return contentsCopy;
|
||||
}
|
||||
|
|
@ -1270,7 +1270,7 @@ ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toInde
|
|||
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
|
||||
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
|
||||
|
||||
memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
|
||||
js_memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@
|
|||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#if (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) && \
|
||||
(defined(_M_X64) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||
# include <emmintrin.h>
|
||||
# define JS_TYPEDARRAY_HAS_SSE2 1
|
||||
#endif
|
||||
|
||||
#include "jsarray.h"
|
||||
#include "jscntxt.h"
|
||||
|
|
@ -199,6 +207,186 @@ ConvertNumber(From src)
|
|||
return To(src);
|
||||
}
|
||||
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
static inline void
|
||||
SSE2ConvertFloatToUint8Clamped(uint8_clamped* dest, const float* src, uint32_t count)
|
||||
{
|
||||
const __m128 fzero = _mm_set1_ps(0.0f);
|
||||
const __m128 fmax = _mm_set1_ps(255.0f);
|
||||
const __m128i izero = _mm_setzero_si128();
|
||||
const __m128i i255 = _mm_set1_epi16(255);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 4 <= count; i += 4) {
|
||||
__m128 values = _mm_loadu_ps(src + i);
|
||||
|
||||
// Keep exact scalar behavior for NaN lanes.
|
||||
if (_mm_movemask_ps(_mm_cmpunord_ps(values, values))) {
|
||||
for (uint32_t j = 0; j < 4; ++j)
|
||||
dest[i + j] = uint8_clamped(src[i + j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
values = _mm_min_ps(_mm_max_ps(values, fzero), fmax);
|
||||
__m128i ints = _mm_cvtps_epi32(values);
|
||||
__m128i packed16 = _mm_packs_epi32(ints, izero);
|
||||
packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255);
|
||||
__m128i packed8 = _mm_packus_epi16(packed16, izero);
|
||||
|
||||
uint32_t out = static_cast<uint32_t>(_mm_cvtsi128_si32(packed8));
|
||||
::memcpy(reinterpret_cast<uint8_t*>(dest + i), &out, sizeof(out));
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertDoubleToUint8Clamped(uint8_clamped* dest, const double* src, uint32_t count)
|
||||
{
|
||||
const __m128d dzero = _mm_set1_pd(0.0);
|
||||
const __m128d dmax = _mm_set1_pd(255.0);
|
||||
const __m128i izero = _mm_setzero_si128();
|
||||
const __m128i i255 = _mm_set1_epi16(255);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 2 <= count; i += 2) {
|
||||
__m128d values = _mm_loadu_pd(src + i);
|
||||
|
||||
// Keep exact scalar behavior for NaN lanes.
|
||||
if (_mm_movemask_pd(_mm_cmpunord_pd(values, values))) {
|
||||
for (uint32_t j = 0; j < 2; ++j)
|
||||
dest[i + j] = uint8_clamped(src[i + j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
values = _mm_min_pd(_mm_max_pd(values, dzero), dmax);
|
||||
__m128i ints = _mm_cvtpd_epi32(values);
|
||||
__m128i packed16 = _mm_packs_epi32(ints, izero);
|
||||
packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255);
|
||||
__m128i packed8 = _mm_packus_epi16(packed16, izero);
|
||||
|
||||
uint16_t out = static_cast<uint16_t>(static_cast<uint32_t>(_mm_cvtsi128_si32(packed8)) & 0xFFFFu);
|
||||
::memcpy(reinterpret_cast<uint8_t*>(dest + i), &out, sizeof(out));
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertInt8ToUint8Clamped(uint8_clamped* dest, const int8_t* src, uint32_t count)
|
||||
{
|
||||
const __m128i izero = _mm_setzero_si128();
|
||||
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 16 <= count; i += 16) {
|
||||
__m128i values = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
|
||||
__m128i negatives = _mm_cmpgt_epi8(izero, values);
|
||||
__m128i clamped = _mm_andnot_si128(negatives, values);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), clamped);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertInt16ToUint8Clamped(uint8_clamped* dest, const int16_t* src, uint32_t count)
|
||||
{
|
||||
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 16 <= count; i += 16) {
|
||||
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
|
||||
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 8));
|
||||
__m128i packed = _mm_packus_epi16(a, b);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertUint16ToUint8Clamped(uint8_clamped* dest, const uint16_t* src, uint32_t count)
|
||||
{
|
||||
const __m128i i255 = _mm_set1_epi16(255);
|
||||
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 16 <= count; i += 16) {
|
||||
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
|
||||
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 8));
|
||||
|
||||
__m128i aExcess = _mm_subs_epu16(a, i255);
|
||||
__m128i bExcess = _mm_subs_epu16(b, i255);
|
||||
__m128i aClamped = _mm_sub_epi16(a, aExcess);
|
||||
__m128i bClamped = _mm_sub_epi16(b, bExcess);
|
||||
|
||||
__m128i packed = _mm_packus_epi16(aClamped, bClamped);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertInt32ToUint8Clamped(uint8_clamped* dest, const int32_t* src, uint32_t count)
|
||||
{
|
||||
const __m128i izero = _mm_setzero_si128();
|
||||
const __m128i i255 = _mm_set1_epi16(255);
|
||||
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 8 <= count; i += 8) {
|
||||
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
|
||||
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 4));
|
||||
__m128i words = _mm_packs_epi32(a, b);
|
||||
words = _mm_min_epi16(_mm_max_epi16(words, izero), i255);
|
||||
__m128i bytes = _mm_packus_epi16(words, izero);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SSE2ConvertUint32ToUint8Clamped(uint8_clamped* dest, const uint32_t* src, uint32_t count)
|
||||
{
|
||||
const __m128i izero = _mm_setzero_si128();
|
||||
const __m128i maskHigh = _mm_set1_epi32(0xFFFFFF00u);
|
||||
const __m128i maskLow = _mm_set1_epi32(0x000000FFu);
|
||||
const __m128i i255d = _mm_set1_epi32(255);
|
||||
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i + 8 <= count; i += 8) {
|
||||
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
|
||||
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 4));
|
||||
|
||||
__m128i aFits = _mm_cmpeq_epi32(_mm_and_si128(a, maskHigh), izero);
|
||||
__m128i bFits = _mm_cmpeq_epi32(_mm_and_si128(b, maskHigh), izero);
|
||||
|
||||
__m128i aLow = _mm_and_si128(a, maskLow);
|
||||
__m128i bLow = _mm_and_si128(b, maskLow);
|
||||
|
||||
__m128i aClamped = _mm_or_si128(_mm_and_si128(aFits, aLow), _mm_andnot_si128(aFits, i255d));
|
||||
__m128i bClamped = _mm_or_si128(_mm_and_si128(bFits, bLow), _mm_andnot_si128(bFits, i255d));
|
||||
|
||||
__m128i words = _mm_packs_epi32(aClamped, bClamped);
|
||||
__m128i bytes = _mm_packus_epi16(words, izero);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
dest[i] = uint8_clamped(src[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename NativeType> struct TypeIDOfType;
|
||||
template<> struct TypeIDOfType<int8_t> { static const Scalar::Type id = Scalar::Int8; };
|
||||
template<> struct TypeIDOfType<uint8_t> { static const Scalar::Type id = Scalar::Uint8; };
|
||||
|
|
@ -275,24 +463,25 @@ class UnsharedOps
|
|||
|
||||
template<typename T>
|
||||
static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
|
||||
// std::copy_n better matches the argument values/types of this
|
||||
// function, but as noted below it allows the input/output ranges to
|
||||
// overlap. std::copy does not, so use it so the compiler has extra
|
||||
// ability to optimize.
|
||||
const auto* first = src.unwrapUnshared();
|
||||
const auto* last = first + nelem;
|
||||
auto* result = dest.unwrapUnshared();
|
||||
std::copy(first, last, result);
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"podCopy requires trivially copyable element type");
|
||||
if (nelem == 0)
|
||||
return;
|
||||
|
||||
// Keep this on memcpy so platform CRT implementations can use their
|
||||
// best vectorized copy routines (SSE2/AVX/etc.) where available.
|
||||
::memcpy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t n) {
|
||||
// std::copy_n copies from |src| to |dest| starting from |src|, so
|
||||
// input/output ranges *may* permissibly overlap, as this function
|
||||
// allows.
|
||||
const auto* start = src.unwrapUnshared();
|
||||
auto* result = dest.unwrapUnshared();
|
||||
std::copy_n(start, n, result);
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"podMove requires trivially copyable element type");
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
// memmove handles overlap and still maps to optimized runtime copies.
|
||||
::memmove(dest.unwrapUnshared(), src.unwrapUnshared(), n * sizeof(T));
|
||||
}
|
||||
|
||||
static SharedMem<void*> extract(TypedArrayObject* obj) {
|
||||
|
|
@ -350,6 +539,14 @@ class ElementSpecific
|
|||
switch (source->as<TypedArrayObject>().type()) {
|
||||
case Scalar::Int8: {
|
||||
SharedMem<JS_VOLATILE_ARM int8_t*> src = data.cast<JS_VOLATILE_ARM int8_t*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<int8_t*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
|
|
@ -357,30 +554,66 @@ class ElementSpecific
|
|||
case Scalar::Uint8:
|
||||
case Scalar::Uint8Clamped: {
|
||||
SharedMem<JS_VOLATILE_ARM uint8_t*> src = data.cast<JS_VOLATILE_ARM uint8_t*>();
|
||||
if (std::is_same<T, uint8_clamped>::value || std::is_same<T, uint8_t>::value) {
|
||||
Ops::podCopy(dest, data.cast<T*>(), count);
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
}
|
||||
case Scalar::Int16: {
|
||||
SharedMem<JS_VOLATILE_ARM int16_t*> src = data.cast<JS_VOLATILE_ARM int16_t*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<int16_t*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
}
|
||||
case Scalar::Uint16: {
|
||||
SharedMem<JS_VOLATILE_ARM uint16_t*> src = data.cast<JS_VOLATILE_ARM uint16_t*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<uint16_t*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
}
|
||||
case Scalar::Int32: {
|
||||
SharedMem<JS_VOLATILE_ARM int32_t*> src = data.cast<JS_VOLATILE_ARM int32_t*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<int32_t*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
}
|
||||
case Scalar::Uint32: {
|
||||
SharedMem<JS_VOLATILE_ARM uint32_t*> src = data.cast<JS_VOLATILE_ARM uint32_t*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<uint32_t*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
|
|
@ -399,12 +632,28 @@ class ElementSpecific
|
|||
}
|
||||
case Scalar::Float32: {
|
||||
SharedMem<JS_VOLATILE_ARM float*> src = data.cast<JS_VOLATILE_ARM float*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<float*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
}
|
||||
case Scalar::Float64: {
|
||||
SharedMem<JS_VOLATILE_ARM double*> src = data.cast<JS_VOLATILE_ARM double*>();
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
|
||||
data.cast<double*>().unwrapUnshared(),
|
||||
count);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
|
||||
break;
|
||||
|
|
@ -563,6 +812,15 @@ class ElementSpecific
|
|||
return true;
|
||||
}
|
||||
|
||||
if (std::is_same<T, uint8_clamped>::value &&
|
||||
(source->type() == Scalar::Uint8 || source->type() == Scalar::Uint8Clamped))
|
||||
{
|
||||
SharedMem<T*> src =
|
||||
source->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
|
||||
Ops::podMove(dest, src, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy |source| in case it overlaps the target elements being set.
|
||||
size_t sourceByteLen = len * source->bytesPerElement();
|
||||
void* data = target->zone()->template pod_malloc<uint8_t>(sourceByteLen);
|
||||
|
|
@ -575,6 +833,12 @@ class ElementSpecific
|
|||
switch (source->type()) {
|
||||
case Scalar::Int8: {
|
||||
int8_t* src = static_cast<int8_t*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
|
|
@ -582,30 +846,58 @@ class ElementSpecific
|
|||
case Scalar::Uint8:
|
||||
case Scalar::Uint8Clamped: {
|
||||
uint8_t* src = static_cast<uint8_t*>(data);
|
||||
if (std::is_same<T, uint8_clamped>::value || std::is_same<T, uint8_t>::value) {
|
||||
Ops::podCopy(dest, SharedMem<void*>::unshared(src).template cast<T*>(), len);
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
}
|
||||
case Scalar::Int16: {
|
||||
int16_t* src = static_cast<int16_t*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
}
|
||||
case Scalar::Uint16: {
|
||||
uint16_t* src = static_cast<uint16_t*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
}
|
||||
case Scalar::Int32: {
|
||||
int32_t* src = static_cast<int32_t*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
}
|
||||
case Scalar::Uint32: {
|
||||
uint32_t* src = static_cast<uint32_t*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
|
|
@ -624,12 +916,24 @@ class ElementSpecific
|
|||
}
|
||||
case Scalar::Float32: {
|
||||
float* src = static_cast<float*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
}
|
||||
case Scalar::Float64: {
|
||||
double* src = static_cast<double*>(data);
|
||||
#ifdef JS_TYPEDARRAY_HAS_SSE2
|
||||
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
|
||||
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
Ops::store(dest++, ConvertNumber<T>(*src++));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ using JS::CanonicalizeNaN;
|
|||
using JS::ToInt32;
|
||||
using JS::ToUint32;
|
||||
|
||||
|
||||
/*
|
||||
* TypedArrayObject
|
||||
*
|
||||
|
|
@ -143,7 +145,7 @@ TypedArrayObject::ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarra
|
|||
return false;
|
||||
|
||||
// tarray is not shared, because if it were it would have a buffer.
|
||||
memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
|
||||
js_memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
|
||||
|
||||
// If the object is in the nursery, the buffer will be freed by the next
|
||||
// nursery GC. Free the data slot pointer if the object has no inline data.
|
||||
|
|
@ -538,7 +540,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
} else {
|
||||
void* data = obj->fixedData(FIXED_DATA_START);
|
||||
obj->initPrivate(data);
|
||||
memset(data, 0, len * sizeof(NativeType));
|
||||
js_memset(data, 0, len * sizeof(NativeType));
|
||||
#ifdef DEBUG
|
||||
if (len == 0) {
|
||||
uint8_t* elements = static_cast<uint8_t*>(data);
|
||||
|
|
@ -667,7 +669,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
|
||||
void* data = tarray->fixedData(FIXED_DATA_START);
|
||||
tarray->initPrivate(data);
|
||||
memset(data, 0, nbytes);
|
||||
js_memset(data, 0, nbytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -704,7 +706,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
memset(buf, 0, nbytes);
|
||||
js_memset(buf, 0, nbytes);
|
||||
}
|
||||
|
||||
RootedObject tmp(cx, NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind));
|
||||
|
|
@ -2108,35 +2110,6 @@ needToSwapBytes(bool littleEndian)
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
swapBytes(uint8_t x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
swapBytes(uint16_t x)
|
||||
{
|
||||
return ((x & 0xff) << 8) | (x >> 8);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
swapBytes(uint32_t x)
|
||||
{
|
||||
return ((x & 0xff) << 24) |
|
||||
((x & 0xff00) << 8) |
|
||||
((x & 0xff0000) >> 8) |
|
||||
((x & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
swapBytes(uint64_t x)
|
||||
{
|
||||
uint32_t a = x & UINT32_MAX;
|
||||
uint32_t b = x >> 32;
|
||||
return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
|
||||
}
|
||||
|
||||
template <typename DataType> struct DataToRepType { typedef DataType result; };
|
||||
template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
|
||||
template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
|
||||
|
|
@ -2154,23 +2127,111 @@ struct DataViewIO
|
|||
{
|
||||
typedef typename DataToRepType<DataType>::result ReadWriteType;
|
||||
|
||||
static MOZ_ALWAYS_INLINE ReadWriteType fromBytes(const uint8_t* unalignedBuffer, bool wantSwap)
|
||||
{
|
||||
if (sizeof(ReadWriteType) == 1)
|
||||
return ReadWriteType(*unalignedBuffer);
|
||||
|
||||
if (sizeof(ReadWriteType) == 2) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint16(unalignedBuffer)
|
||||
: mozilla::LittleEndian::readUint16(unalignedBuffer));
|
||||
#else
|
||||
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint16(unalignedBuffer)
|
||||
: mozilla::BigEndian::readUint16(unalignedBuffer));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sizeof(ReadWriteType) == 4) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint32(unalignedBuffer)
|
||||
: mozilla::LittleEndian::readUint32(unalignedBuffer));
|
||||
#else
|
||||
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint32(unalignedBuffer)
|
||||
: mozilla::BigEndian::readUint32(unalignedBuffer));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sizeof(ReadWriteType) == 8) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint64(unalignedBuffer)
|
||||
: mozilla::LittleEndian::readUint64(unalignedBuffer));
|
||||
#else
|
||||
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint64(unalignedBuffer)
|
||||
: mozilla::BigEndian::readUint64(unalignedBuffer));
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_CRASH("unsupported DataView element size");
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void toBytes(uint8_t* unalignedBuffer, ReadWriteType value,
|
||||
bool wantSwap)
|
||||
{
|
||||
if (sizeof(ReadWriteType) == 1) {
|
||||
*unalignedBuffer = uint8_t(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(ReadWriteType) == 2) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
if (wantSwap)
|
||||
mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value));
|
||||
else
|
||||
mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value));
|
||||
#else
|
||||
if (wantSwap)
|
||||
mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value));
|
||||
else
|
||||
mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(ReadWriteType) == 4) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
if (wantSwap)
|
||||
mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value));
|
||||
else
|
||||
mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value));
|
||||
#else
|
||||
if (wantSwap)
|
||||
mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value));
|
||||
else
|
||||
mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (sizeof(ReadWriteType) == 8) {
|
||||
#if MOZ_LITTLE_ENDIAN
|
||||
if (wantSwap)
|
||||
mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value));
|
||||
else
|
||||
mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value));
|
||||
#else
|
||||
if (wantSwap)
|
||||
mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value));
|
||||
else
|
||||
mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_CRASH("unsupported DataView element size");
|
||||
}
|
||||
|
||||
static void fromBuffer(DataType* dest, const uint8_t* unalignedBuffer, bool wantSwap)
|
||||
{
|
||||
MOZ_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
|
||||
memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType));
|
||||
if (wantSwap) {
|
||||
ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
|
||||
*rwDest = swapBytes(*rwDest);
|
||||
}
|
||||
*reinterpret_cast<ReadWriteType*>(dest) = fromBytes(unalignedBuffer, wantSwap);
|
||||
}
|
||||
|
||||
static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap)
|
||||
{
|
||||
MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
|
||||
ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
|
||||
if (wantSwap)
|
||||
temp = swapBytes(temp);
|
||||
memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType));
|
||||
toBytes(unalignedBuffer, temp, wantSwap);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3858,12 +3858,54 @@ BaseCompiler::emitSubtractF64()
|
|||
void
|
||||
BaseCompiler::emitMultiplyI32()
|
||||
{
|
||||
// TODO / OPTIMIZE: Multiplication by constant is common (Bug 1275442, 1316803)
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
masm.mul32(r1.reg, r0.reg);
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI32 r = popI32();
|
||||
|
||||
if (c == 0) {
|
||||
masm.move32(Imm32(0), r.reg);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 1) {
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == -1) {
|
||||
masm.neg32(r.reg);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t mag = c < 0 ? uint32_t(0) - uint32_t(c) : uint32_t(c);
|
||||
if (IsPowerOfTwo(mag)) {
|
||||
uint32_t shift = 0;
|
||||
while ((uint32_t(1) << shift) != mag)
|
||||
shift++;
|
||||
|
||||
masm.lshift32(Imm32(shift), r.reg);
|
||||
if (c < 0)
|
||||
masm.neg32(r.reg);
|
||||
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 rhs = needI32();
|
||||
masm.move32(Imm32(c), rhs.reg);
|
||||
masm.mul32(rhs.reg, r.reg);
|
||||
freeI32(rhs);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
masm.mul32(r1.reg, r0.reg);
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3934,17 +3976,43 @@ BaseCompiler::emitQuotientI32()
|
|||
void
|
||||
BaseCompiler::emitQuotientU32()
|
||||
{
|
||||
// TODO / OPTIMIZE: Fast case if lhs >= 0 and rhs is power of two (Bug 1316803)
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
uint32_t uc = uint32_t(c);
|
||||
RegI32 r = popI32();
|
||||
|
||||
if (uc != 0 && IsPowerOfTwo(uc)) {
|
||||
uint32_t shift = 0;
|
||||
while ((uint32_t(1) << shift) != uc)
|
||||
shift++;
|
||||
masm.rshift32(Imm32(shift), r.reg);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 rhs = needI32();
|
||||
masm.move32(Imm32(c), rhs.reg);
|
||||
|
||||
Label done;
|
||||
checkDivideByZeroI32(r1, r0, &done);
|
||||
masm.quotient32(r1.reg, r0.reg, IsUnsigned(true));
|
||||
checkDivideByZeroI32(rhs, r, &done);
|
||||
masm.quotient32(rhs.reg, r.reg, IsUnsigned(true));
|
||||
masm.bind(&done);
|
||||
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
freeI32(rhs);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
|
||||
Label done;
|
||||
checkDivideByZeroI32(r1, r0, &done);
|
||||
masm.quotient32(r1.reg, r0.reg, IsUnsigned(true));
|
||||
masm.bind(&done);
|
||||
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3967,17 +4035,40 @@ BaseCompiler::emitRemainderI32()
|
|||
void
|
||||
BaseCompiler::emitRemainderU32()
|
||||
{
|
||||
// TODO / OPTIMIZE: Fast case if lhs >= 0 and rhs is power of two (Bug 1316803)
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
uint32_t uc = uint32_t(c);
|
||||
RegI32 r = popI32();
|
||||
|
||||
if (uc != 0 && IsPowerOfTwo(uc)) {
|
||||
masm.and32(Imm32(int32_t(uc - 1)), r.reg);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 rhs = needI32();
|
||||
masm.move32(Imm32(c), rhs.reg);
|
||||
|
||||
Label done;
|
||||
checkDivideByZeroI32(r1, r0, &done);
|
||||
masm.remainder32(r1.reg, r0.reg, IsUnsigned(true));
|
||||
checkDivideByZeroI32(rhs, r, &done);
|
||||
masm.remainder32(rhs.reg, r.reg, IsUnsigned(true));
|
||||
masm.bind(&done);
|
||||
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
freeI32(rhs);
|
||||
pushI32(r);
|
||||
return;
|
||||
}
|
||||
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForIntMulDiv(&r0, &r1);
|
||||
|
||||
Label done;
|
||||
checkDivideByZeroI32(r1, r0, &done);
|
||||
masm.remainder32(r1.reg, r0.reg, IsUnsigned(true));
|
||||
masm.bind(&done);
|
||||
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
}
|
||||
|
||||
#ifndef INT_DIV_I64_CALLOUT
|
||||
|
|
@ -4280,12 +4371,18 @@ BaseCompiler::emitShlI32()
|
|||
void
|
||||
BaseCompiler::emitShlI64()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI64 r = popI64();
|
||||
masm.lshift64(Imm32(c & 63), r.reg);
|
||||
pushI64(r);
|
||||
} else {
|
||||
RegI64 r0, r1;
|
||||
pop2xI64ForShiftOrRotate(&r0, &r1);
|
||||
masm.lshift64(lowPart(r1), r0.reg);
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4309,12 +4406,18 @@ BaseCompiler::emitShrI32()
|
|||
void
|
||||
BaseCompiler::emitShrI64()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI64 r = popI64();
|
||||
masm.rshift64Arithmetic(Imm32(c & 63), r.reg);
|
||||
pushI64(r);
|
||||
} else {
|
||||
RegI64 r0, r1;
|
||||
pop2xI64ForShiftOrRotate(&r0, &r1);
|
||||
masm.rshift64Arithmetic(lowPart(r1), r0.reg);
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4338,56 +4441,98 @@ BaseCompiler::emitShrU32()
|
|||
void
|
||||
BaseCompiler::emitShrU64()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI64 r = popI64();
|
||||
masm.rshift64(Imm32(c & 63), r.reg);
|
||||
pushI64(r);
|
||||
} else {
|
||||
RegI64 r0, r1;
|
||||
pop2xI64ForShiftOrRotate(&r0, &r1);
|
||||
masm.rshift64(lowPart(r1), r0.reg);
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitRotrI32()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI32 r = popI32();
|
||||
masm.rotateRight(Imm32(c & 31), r.reg, r.reg);
|
||||
pushI32(r);
|
||||
} else {
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForShiftOrRotate(&r0, &r1);
|
||||
masm.rotateRight(r1.reg, r0.reg, r0.reg);
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitRotrI64()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI64 r = popI64();
|
||||
#ifdef JS_PUNBOX64
|
||||
masm.rotateRight64(Imm32(c & 63), r.reg, r.reg);
|
||||
#else
|
||||
RegI32 temp = needI32();
|
||||
masm.rotateRight64(Imm32(c & 63), r.reg, r.reg, temp.reg);
|
||||
freeI32(temp);
|
||||
#endif
|
||||
pushI64(r);
|
||||
} else {
|
||||
RegI64 r0, r1;
|
||||
pop2xI64ForShiftOrRotate(&r0, &r1);
|
||||
masm.rotateRight64(lowPart(r1), r0.reg, r0.reg, maybeHighPart(r1));
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitRotlI32()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI32 r = popI32();
|
||||
masm.rotateLeft(Imm32(c & 31), r.reg, r.reg);
|
||||
pushI32(r);
|
||||
} else {
|
||||
RegI32 r0, r1;
|
||||
pop2xI32ForShiftOrRotate(&r0, &r1);
|
||||
masm.rotateLeft(r1.reg, r0.reg, r0.reg);
|
||||
freeI32(r1);
|
||||
pushI32(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitRotlI64()
|
||||
{
|
||||
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
|
||||
int32_t c;
|
||||
if (popConstI32(c)) {
|
||||
RegI64 r = popI64();
|
||||
#ifdef JS_PUNBOX64
|
||||
masm.rotateLeft64(Imm32(c & 63), r.reg, r.reg);
|
||||
#else
|
||||
RegI32 temp = needI32();
|
||||
masm.rotateLeft64(Imm32(c & 63), r.reg, r.reg, temp.reg);
|
||||
freeI32(temp);
|
||||
#endif
|
||||
pushI64(r);
|
||||
} else {
|
||||
RegI64 r0, r1;
|
||||
pop2xI64ForShiftOrRotate(&r0, &r1);
|
||||
masm.rotateLeft64(lowPart(r1), r0.reg, r0.reg, maybeHighPart(r1));
|
||||
freeI64(r1);
|
||||
pushI64(r0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1426,6 +1426,10 @@ ReloadPrefsCallback(const char* pref, void* data)
|
|||
bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
|
||||
"baselinejit.unsafe_eager_compilation");
|
||||
bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
|
||||
int32_t baselineWarmUpThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR
|
||||
"baselinejit.threshold", -1);
|
||||
int32_t ionWarmUpThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR
|
||||
"ion.threshold", -1);
|
||||
|
||||
sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
|
||||
|
||||
|
|
@ -1472,10 +1476,23 @@ ReloadPrefsCallback(const char* pref, void* data)
|
|||
|
||||
JS_SetParallelParsingEnabled(cx, parallelParsing);
|
||||
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
|
||||
|
||||
// -1 means "use engine default".
|
||||
if (baselineWarmUpThreshold < -1)
|
||||
baselineWarmUpThreshold = -1;
|
||||
if (ionWarmUpThreshold < -1)
|
||||
ionWarmUpThreshold = -1;
|
||||
|
||||
// Eager compilation prefs still override threshold prefs.
|
||||
if (useBaselineEager)
|
||||
baselineWarmUpThreshold = 0;
|
||||
if (useIonEager)
|
||||
ionWarmUpThreshold = 0;
|
||||
|
||||
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
|
||||
useBaselineEager ? 0 : -1);
|
||||
baselineWarmUpThreshold);
|
||||
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
|
||||
useIonEager ? 0 : -1);
|
||||
ionWarmUpThreshold);
|
||||
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_UNBOXED_OBJECTS,
|
||||
unboxedObjects);
|
||||
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_INLINING,
|
||||
|
|
|
|||
|
|
@ -2138,14 +2138,25 @@ nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
|
|||
|
||||
if (mWaitingForTransaction) {
|
||||
if (mSkippedPaints && aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
|
||||
// XXX - Bug 1303369 - too many false positives.
|
||||
//gfxCriticalNote << "Refresh driver waiting for the compositor for "
|
||||
// << (aTime - mMostRecentTick).ToSeconds()
|
||||
// << " seconds.";
|
||||
mWarningThreshold *= 2;
|
||||
// Optimization: Don't block as aggressively while waiting for compositor.
|
||||
// Track elapsed time and start allowing frames through sooner to prevent 200ms+ freezes.
|
||||
// Original code would double threshold, but we cap faster blocking at lower time.
|
||||
if (mWarningThreshold < 1) {
|
||||
mWarningThreshold = 1;
|
||||
} else {
|
||||
mWarningThreshold *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
mSkippedPaints = true;
|
||||
// Optimization: Allow frames through periodically instead of blocking all frames.
|
||||
// Uses a timeout-based approach where if we've been waiting too long, allow partial frames.
|
||||
// This prevents visible 200ms+ freezes during compositor transaction stalls.
|
||||
if (aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(50))) {
|
||||
// If more than 50ms has passed, allow some frames through
|
||||
// This keeps the UI responsive during compositor delays
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pref("browser.cache.disk.smart_size.enabled", true);
|
|||
// Which max value should we use for smart-sizing?
|
||||
pref("browser.cache.disk.smart_size.use_old_max", true);
|
||||
// Size (in KB) explicitly set by the user. Used when smart_size.enabled == false
|
||||
pref("browser.cache.disk.capacity", 256000);
|
||||
pref("browser.cache.disk.capacity", 2097152);
|
||||
// When smartsizing is disabled we could potentially fill all disk space by
|
||||
// cache data when the disk capacity is not set correctly. To avoid that we
|
||||
// check the free space every time we write some data to the cache. The free
|
||||
|
|
@ -67,13 +67,13 @@ pref("browser.cache.disk.free_space_soft_limit", 5120); // 5MB
|
|||
pref("browser.cache.disk.free_space_hard_limit", 1024); // 1MB
|
||||
// Max-size (in KB) for entries in disk cache. Set to -1 for no limit.
|
||||
// (Note: entries bigger than 1/8 of disk-cache are never cached)
|
||||
pref("browser.cache.disk.max_entry_size", 51200); // 50 MB
|
||||
pref("browser.cache.disk.max_entry_size", 102400); // 100 MB
|
||||
pref("browser.cache.memory.enable", true);
|
||||
// -1 = determine dynamically, 0 = none, n = memory capacity in kilobytes
|
||||
//pref("browser.cache.memory.capacity", -1);
|
||||
// Max-size (in KB) for entries in memory cache. Set to -1 for no limit.
|
||||
// (Note: entries bigger than than 90% of the mem-cache are never cached)
|
||||
pref("browser.cache.memory.max_entry_size", 5120);
|
||||
pref("browser.cache.memory.max_entry_size", 8192);
|
||||
// Memory limit (in kB) for new cache data not yet written to disk. Writes to
|
||||
// the cache are buffered and written to disk on background with low priority.
|
||||
// With a slow persistent storage these buffers may grow when data is coming
|
||||
|
|
@ -82,16 +82,16 @@ pref("browser.cache.memory.max_entry_size", 5120);
|
|||
// (priority) like html, css, fonts and js, and one for other data like images,
|
||||
// video, etc.
|
||||
// Note: 0 means no limit.
|
||||
pref("browser.cache.disk.max_chunks_memory_usage", 10240);
|
||||
pref("browser.cache.disk.max_priority_chunks_memory_usage", 10240);
|
||||
pref("browser.cache.disk.max_chunks_memory_usage", 20480);
|
||||
pref("browser.cache.disk.max_priority_chunks_memory_usage", 20480);
|
||||
|
||||
pref("browser.cache.disk_cache_ssl", true);
|
||||
// 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
|
||||
pref("browser.cache.check_doc_frequency", 3);
|
||||
// Limit of recent metadata we keep in memory for faster access, in Kb
|
||||
pref("browser.cache.disk.metadata_memory_limit", 250); // 0.25 MB
|
||||
pref("browser.cache.disk.metadata_memory_limit", 1024); // 1 MB
|
||||
// The number of chunks we preload ahead of read. One chunk has currently 256kB.
|
||||
pref("browser.cache.disk.preload_chunk_count", 4); // 1 MB of read ahead
|
||||
pref("browser.cache.disk.preload_chunk_count", 8); // 2 MB of read ahead
|
||||
// The half life used to re-compute cache entries frecency in hours.
|
||||
pref("browser.cache.frecency_half_life_hours", 6);
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ pref("dom.compartment_per_addon", true);
|
|||
|
||||
// Fastback caching - if this pref is negative, then we calculate the number
|
||||
// of content viewers to cache based on the amount of available memory.
|
||||
pref("browser.sessionhistory.max_total_viewers", -1);
|
||||
pref("browser.sessionhistory.max_total_viewers", 8);
|
||||
|
||||
// Whether to store 'about:newtab' in the session history, disabled by default.
|
||||
// See https://github.com/MoonchildProductions/UXP/issues/719
|
||||
|
|
@ -1170,9 +1170,9 @@ pref("dom.send_after_paint_to_content", false);
|
|||
pref("dom.link.disabled_attribute.enabled", true);
|
||||
|
||||
// Timeout clamp in ms for timeouts we clamp
|
||||
pref("dom.min_timeout_value", 4);
|
||||
pref("dom.min_timeout_value", 2);
|
||||
// And for background windows
|
||||
pref("dom.min_background_timeout_value", 1000);
|
||||
pref("dom.min_background_timeout_value", 1500);
|
||||
|
||||
// Don't use new input types
|
||||
pref("dom.experimental_forms", false);
|
||||
|
|
@ -1265,6 +1265,11 @@ pref("javascript.options.unboxed_objects", false);
|
|||
pref("javascript.options.baselinejit", true);
|
||||
pref("javascript.options.ion", true);
|
||||
pref("javascript.options.ion.inlining", true);
|
||||
// JIT warm-up thresholds (-1 keeps engine defaults).
|
||||
// Lower values can improve sustained throughput on large script bundles
|
||||
// (e.g. React/jQuery-heavy apps) at some startup compile cost.
|
||||
pref("javascript.options.baselinejit.threshold", 6);
|
||||
pref("javascript.options.ion.threshold", 50);
|
||||
pref("javascript.options.asmjs", true);
|
||||
pref("javascript.options.wasm", true);
|
||||
// wasm jit crashes in 32bit builds because of 64bit casts so
|
||||
|
|
@ -1293,7 +1298,7 @@ pref("javascript.options.mem.high_water_mark", 128);
|
|||
pref("javascript.options.mem.max", -1);
|
||||
pref("javascript.options.mem.gc_per_zone", true);
|
||||
pref("javascript.options.mem.gc_incremental", true);
|
||||
pref("javascript.options.mem.gc_incremental_slice_ms", 20);
|
||||
pref("javascript.options.mem.gc_incremental_slice_ms", 10);
|
||||
pref("javascript.options.mem.gc_generational", true);
|
||||
pref("javascript.options.mem.gc_compacting", true);
|
||||
pref("javascript.options.mem.log", false);
|
||||
|
|
@ -1306,16 +1311,16 @@ pref("javascript.options.compact_on_user_inactive_delay", 15000); // ms
|
|||
pref("javascript.options.compact_on_user_inactive_delay", 300000); // ms
|
||||
#endif
|
||||
|
||||
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000);
|
||||
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 2000);
|
||||
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);
|
||||
pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 500);
|
||||
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
|
||||
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
|
||||
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 350);
|
||||
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 175);
|
||||
pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
|
||||
pref("javascript.options.mem.gc_dynamic_heap_growth", true);
|
||||
pref("javascript.options.mem.gc_dynamic_mark_slice", true);
|
||||
pref("javascript.options.mem.gc_refresh_frame_slices_enabled", true);
|
||||
pref("javascript.options.mem.gc_allocation_threshold_mb", 30);
|
||||
pref("javascript.options.mem.gc_allocation_threshold_mb", 20);
|
||||
pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
|
||||
pref("javascript.options.mem.gc_max_empty_chunk_count", 30);
|
||||
|
||||
|
|
@ -2803,7 +2808,7 @@ pref("editor.positioning.offset", 0);
|
|||
|
||||
pref("dom.use_watchdog", true);
|
||||
pref("dom.max_chrome_script_run_time", 30);
|
||||
pref("dom.max_script_run_time", 15);
|
||||
pref("dom.max_script_run_time", 10);
|
||||
|
||||
// Automatically terminate non-responsive scripts if script_run_time expires.
|
||||
pref("dom.always_stop_slow_scripts", false);
|
||||
|
|
@ -2826,7 +2831,7 @@ pref("idle_queue.long_period", 50);
|
|||
// period, which makes the point in time that we expect to become busy
|
||||
// again be:
|
||||
// now + idle_queue.min_period + layout.idle_period.time_limit
|
||||
pref("idle_queue.min_period", 3);
|
||||
pref("idle_queue.min_period", 1);
|
||||
|
||||
// Hang monitor timeout after which we kill the browser, in seconds
|
||||
// (0 is disabled)
|
||||
|
|
@ -4275,11 +4280,15 @@ pref("image.animated.decode-on-demand.batch-size", 6);
|
|||
pref("image.animated.resume-from-last-displayed", true);
|
||||
|
||||
// The maximum size, in bytes, of the decoded images we cache
|
||||
pref("image.cache.size", 5242880);
|
||||
pref("image.cache.size", 67108864);
|
||||
|
||||
// A weight, from 0-1000, to place on time when comparing to size.
|
||||
// Size is given a weight of 1000 - timeweight.
|
||||
pref("image.cache.timeweight", 500);
|
||||
pref("image.cache.timeweight", 650);
|
||||
|
||||
// Time in seconds before unproxied entries in the in-memory image cache are
|
||||
// considered for eviction by the expiration tracker.
|
||||
pref("image.cache.entry_timeout_seconds", 30);
|
||||
|
||||
// Decode all images automatically on load, ignoring our normal heuristics.
|
||||
pref("image.decode-immediately.enabled", false);
|
||||
|
|
@ -4289,9 +4298,9 @@ pref("image.downscale-during-decode.enabled", true);
|
|||
|
||||
// The default Accept header sent for images loaded over HTTP(S)
|
||||
#ifdef MOZ_JXL
|
||||
pref("image.http.accept", "image/webp,image/jxl,image/png,image/*;q=0.8,*/*;q=0.5");
|
||||
pref("image.http.accept", "image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
|
||||
#else
|
||||
pref("image.http.accept", "image/webp,image/png,image/*;q=0.8,*/*;q=0.5");
|
||||
pref("image.http.accept", "image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
|
||||
#endif
|
||||
|
||||
// The threshold for inferring that changes to an <img> element's |src|
|
||||
|
|
@ -4326,7 +4335,7 @@ pref("image.mem.decode_bytes_at_a_time", 16384);
|
|||
|
||||
// Minimum timeout for expiring unused images from the surface cache, in
|
||||
// milliseconds. This controls how long we store cached temporary surfaces.
|
||||
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60s
|
||||
pref("image.mem.surfacecache.min_expiration_ms", 180000); // 180s
|
||||
|
||||
// Maximum size for the surface cache, in kilobytes.
|
||||
pref("image.mem.surfacecache.max_size_kb", 1048576); // 1GB
|
||||
|
|
@ -4343,7 +4352,7 @@ pref("image.mem.surfacecache.size_factor", 4);
|
|||
// surface cache on memory pressure, a discard factor of 2 means to discard half
|
||||
// of the data, and so forth. The default should be a good balance for desktop
|
||||
// and laptop systems, where we never discard visible images.
|
||||
pref("image.mem.surfacecache.discard_factor", 1);
|
||||
pref("image.mem.surfacecache.discard_factor", 2);
|
||||
|
||||
// How many threads we'll use for multithreaded decoding. If < 0, will be
|
||||
// automatically determined based on the system's number of cores.
|
||||
|
|
@ -4665,7 +4674,7 @@ pref("dom.idle-observers-api.enabled", true);
|
|||
|
||||
// Time limit, in milliseconds, for EventStateManager::IsHandlingUserInput().
|
||||
// Used to detect long running handlers of user-generated events.
|
||||
pref("dom.event.handling-user-input-time-limit", 1000);
|
||||
pref("dom.event.handling-user-input-time-limit", 500);
|
||||
|
||||
// Whether we should layerize all animated images (if otherwise possible).
|
||||
pref("layout.animated-image-layers.enabled", false);
|
||||
|
|
|
|||
|
|
@ -1186,6 +1186,12 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
|||
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorAMD), nsCaseInsensitiveStringComparator()) &&
|
||||
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorATI), nsCaseInsensitiveStringComparator()) &&
|
||||
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorMicrosoft), nsCaseInsensitiveStringComparator()) &&
|
||||
// VMware (PCI vendor 0x15ad), Hyper-V synthetic adapter vendor
|
||||
// ids, and Parallels (PCI vendor 0x1ab8) should be treated as
|
||||
// known virtualized vendors.
|
||||
!adapterVendorID.LowerCaseEqualsLiteral("0x15ad") &&
|
||||
!adapterVendorID.LowerCaseEqualsLiteral("0x1414") &&
|
||||
!adapterVendorID.LowerCaseEqualsLiteral("0x1ab8") &&
|
||||
// FIXME - these special hex values are currently used in xpcshell tests introduced by
|
||||
// bug 625160 patch 8/8. Maybe these tests need to be adjusted now that we're only whitelisting
|
||||
// intel/ati/nvidia.
|
||||
|
|
@ -1206,6 +1212,13 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Explicitly block VirtualBox virtual GPU devices due to buggy drivers and lack of hardware acceleration support.
|
||||
if (adapterVendorID.LowerCaseEqualsLiteral("0x80ee")) {
|
||||
aFailureId = "FEATURE_FAILURE_VIRTUALBOX";
|
||||
*aStatus = FEATURE_BLOCKED_DEVICE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// special-case the WinXP test slaves: they have out-of-date drivers, but we still want to
|
||||
// whitelist them, actually we do know that this combination of device and driver version
|
||||
// works well.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue