Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-10-24 18:36:28 -07:00
Родитель 48c3749942 81ebe540f3
Коммит a3086563dc
197 изменённых файлов: 4607 добавлений и 1674 удалений

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

@ -0,0 +1,46 @@
<html>
<script>
function start() {
try{o33=document.documentElement;}catch(e){}
try{o1.appendChild;}catch(e){}
try{tmp = document.createElement('iframe');}catch(e){}
try{tmp.id = 'id36'}catch(e){}
try{o33.ownerDocument.documentElement.appendChild(tmp);}catch(e){}
try{o51=o33.ownerDocument.getElementById('id36').contentDocument;}catch(e){}
try{o579=document.documentElement;}catch(e){}
try{tmp.id = 'id421'}catch(e){}
try{o619=document.getElementById('id421').contentDocument;}catch(e){}
try{o622=window.document.getElementById('id421').contentWindow.document;}catch(e){}
try{o875.setAttributeNS(null,'letter-spacing','normal');}catch(e){}
try{o884=document.createElementNS('http://www.w3.org/2000/svg','set');;}catch(e){}
try{o887=document.createElementNS('http://www.w3.org/2000/svg','desc');;}catch(e){}
try{o884.appendChild(o887);}catch(e){}
try{o887=document.documentElement;}catch(e){}
try{o1041=o622.createElement('bdi');;}catch(e){}
window.setTimeout('start2()',100);
}
function start2() {
try{o1042=document.createElement('input');;}catch(e){}
try{o1043=o51.createElement('input');;}catch(e){}
try{o884.appendChild(o1043);}catch(e){}
try{o1053=o1043.previousElementSibling;}catch(e){}
try{o1067=o619.createElement('blockquote');;}catch(e){}
try{o1062.appendChild(o1067);}catch(e){}
try{o1074=o619.createElement('ruby');;}catch(e){}
try{o1067.appendChild(o1074);}catch(e){}
try{document.body.appendChild(o1041);}catch(e){}
try{o1041.appendChild(o1042);}catch(e){}
try{o1095=o51.createTextNode(unescape('%uff0f%u017f%u0390%ufffa%u2073%uff4d%uDF53%u0261'));;}catch(e){}
try{o1053.appendChild(o1095);}catch(e){}
try{o1041.appendChild(o1043.parentNode);}catch(e){}
try{o1042.appendChild(o1067);}catch(e){}
try{o1053=null;}catch(e){}
try{o1095=null;}catch(e){}
try{for(var xrn in o884.childNodes) o884.removeChild(o884.childNodes[xrn]);}catch(e){}
//fuzzer.gc();
location.reload();
}
</script>
<body onload="start()">
</body>
</html>

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

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html class="reftest-wait"><head>
<meta charset="utf-8">
<title>Testcase for bug 828903</title>
<script>
function reload() {
this.location.reload();
}
// Run the test for 2 seconds
setTimeout(function() {
document.documentElement.removeChild(document.body);
document.documentElement.className = "";
}, 2000);
</script>
</head>
<body onload="document.body.getBoundingClientRect()">
<iframe onload="this.contentWindow.setTimeout(reload,1113)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1233)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1313)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1433)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1113)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1233)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1313)" src="828903-iframe.html"></iframe>
<iframe onload="this.contentWindow.setTimeout(reload,1433)" src="828903-iframe.html"></iframe>
</body>
</html>

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

@ -128,6 +128,7 @@ load 822723.html
load 824719.html
load 827190.html
load 828054.html
load 828903.html
load 829428.html
load 830098.html
load 831287.html

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

@ -112,6 +112,7 @@ NS_CP_ContentTypeName(uint32_t contentType)
CASE_RETURN( TYPE_XSLT );
CASE_RETURN( TYPE_BEACON );
CASE_RETURN( TYPE_FETCH );
CASE_RETURN( TYPE_IMAGESET );
default:
return "<Unknown Type>";
}

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

@ -30,6 +30,7 @@
#include "mozilla/dom/AutocompleteInfoBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/FloatingPoint.h"
#include "nsIContentPolicy.h"
#if defined(XP_WIN)
// Undefine LoadImage to prevent naming conflict with Windows.
@ -591,6 +592,8 @@ public:
* @param aContext the context the image is loaded in (eg an element)
* @param aLoadingDocument the document we belong to
* @param aLoadingPrincipal the principal doing the load
* @param [aContentPolicyType=nsIContentPolicy::TYPE_IMAGE] (Optional)
* The CP content type to use
* @param aImageBlockingStatus the nsIContentPolicy blocking status for this
* image. This will be set even if a security check fails for the
* image, to some reasonable REJECT_* value. This out param will only
@ -601,10 +604,12 @@ public:
* false is returned.
*/
static bool CanLoadImage(nsIURI* aURI,
nsISupports* aContext,
nsIDocument* aLoadingDocument,
nsIPrincipal* aLoadingPrincipal,
int16_t* aImageBlockingStatus = nullptr);
nsISupports* aContext,
nsIDocument* aLoadingDocument,
nsIPrincipal* aLoadingPrincipal,
int16_t* aImageBlockingStatus = nullptr,
uint32_t aContentPolicyType = nsIContentPolicy::TYPE_IMAGE);
/**
* Method to start an image load. This does not do any security checks.
* This method will attempt to make aURI immutable; a caller that wants to
@ -616,6 +621,8 @@ public:
* @param aReferrer the referrer URI
* @param aObserver the observer for the image load
* @param aLoadFlags the load flags to use. See nsIRequest
* @param [aContentPolicyType=nsIContentPolicy::TYPE_IMAGE] (Optional)
* The CP content type to use
* @return the imgIRequest for the image load
*/
static nsresult LoadImage(nsIURI* aURI,
@ -625,7 +632,8 @@ public:
imgINotificationObserver* aObserver,
int32_t aLoadFlags,
const nsAString& initiatorType,
imgRequestProxy** aRequest);
imgRequestProxy** aRequest,
uint32_t aContentPolicyType = nsIContentPolicy::TYPE_IMAGE);
/**
* Obtain an image loader that respects the given document/channel's privacy status.

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

@ -24,9 +24,14 @@ typedef unsigned long nsContentPolicyType;
* by launching a dialog to prompt the user for something).
*/
[scriptable,uuid(8afe3e5c-f916-48fd-8075-9579d3502e1d)]
[scriptable,uuid(94513f07-d559-480e-8879-6249852365ea)]
interface nsIContentPolicy : nsISupports
{
/**
* Indicates a unset or bogus policy type.
*/
const nsContentPolicyType TYPE_INVALID = 0;
/**
* Gecko/Firefox developers: Do not use TYPE_OTHER under any circumstances.
*
@ -156,6 +161,11 @@ interface nsIContentPolicy : nsISupports
*/
const nsContentPolicyType TYPE_FETCH = 20;
/**
* Indicates a <img srcset> or <picture> request.
*/
const nsContentPolicyType TYPE_IMAGESET = 21;
/* When adding new content types, please update nsContentBlocker,
* NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
* implementations, and other things that are not listed here that are

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

@ -1736,14 +1736,26 @@ public:
bool IsVisibleConsideringAncestors() const;
/**
* Return true when this document is active, i.e., the active document
* Return true when this document is active, i.e., an active document
* in a content viewer. Note that this will return true for bfcached
* documents, so this does NOT match the "active document" concept in
* the WHATWG spec. That would correspond to GetInnerWindow() &&
* GetInnerWindow()->IsCurrentInnerWindow().
* the WHATWG spec - see IsCurrentActiveDocument.
*/
bool IsActive() const { return mDocumentContainer && !mRemovedFromDocShell; }
/**
* Return true if this is the current active document for its
* docshell. Note that a docshell may have multiple active documents
* due to the bfcache -- this should be used when you need to
* differentiate the *current* active document from any active
* documents.
*/
bool IsCurrentActiveDocument() const
{
nsPIDOMWindow *inner = GetInnerWindow();
return inner && inner->IsCurrentInnerWindow() && inner->GetDoc() == this;
}
/**
* Register/Unregister the ActivityObserver into mActivityObservers to listen
* the document's activity changes such as OnPageHide, visibility, activity.

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

@ -37,7 +37,7 @@ interface nsIFrame;
* interface to mirror this interface when changing it.
*/
[scriptable, builtinclass, uuid(256a5283-ebb5-4430-8e15-5ada92156ef7)]
[scriptable, builtinclass, uuid(ce098f6c-baca-4178-a9aa-266e8bfe509b)]
interface nsIImageLoadingContent : imgINotificationObserver
{
/**
@ -149,9 +149,10 @@ interface nsIImageLoadingContent : imgINotificationObserver
/**
* forceReload forces reloading of the image pointed to by currentURI
*
* @param aNotify [optional] request should notify, defaults to true
* @throws NS_ERROR_NOT_AVAILABLE if there is no current URI to reload
*/
void forceReload();
[optional_argc] void forceReload([optional] in boolean aNotify /* = true */);
/**
* Enables/disables image state forcing. When |aForce| is PR_TRUE, we force

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

@ -957,7 +957,7 @@ Element::CreateShadowRoot(ErrorResult& aError)
nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding();
aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"),
docInfo, this, true);
docInfo, nullptr, true);
if (aError.Failed()) {
delete protoBinding;
return nullptr;

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

@ -22,7 +22,10 @@ using namespace mozilla::dom;
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS0(ResponsiveImageSelector);
NS_IMPL_CYCLE_COLLECTION(ResponsiveImageSelector, mContent)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ResponsiveImageSelector, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ResponsiveImageSelector, Release)
static bool
ParseInteger(const nsAString& aString, int32_t& aInt)
@ -263,6 +266,19 @@ ResponsiveImageSelector::GetSelectedImageDensity()
return mCandidates[bestIndex].Density(this);
}
bool
ResponsiveImageSelector::SelectImage(bool aReselect)
{
if (!aReselect && mBestCandidateIndex != -1) {
// Already have selection
return false;
}
int oldBest = mBestCandidateIndex;
mBestCandidateIndex = -1;
return GetBestCandidateIndex() != oldBest;
}
int
ResponsiveImageSelector::GetBestCandidateIndex()
{
@ -336,10 +352,6 @@ bool
ResponsiveImageSelector::ComputeFinalWidthForCurrentViewport(int32_t *aWidth)
{
unsigned int numSizes = mSizeQueries.Length();
if (!numSizes) {
return false;
}
nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
nsIPresShell *presShell = doc ? doc->GetShell() : nullptr;
nsPresContext *pctx = presShell ? presShell->GetPresContext() : nullptr;

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

@ -9,6 +9,7 @@
#include "nsISupports.h"
#include "nsIContent.h"
#include "nsString.h"
#include "nsCycleCollectionParticipant.h"
class nsMediaQuery;
class nsCSSValue;
@ -18,13 +19,27 @@ namespace dom {
class ResponsiveImageCandidate;
class ResponsiveImageSelector : public nsISupports
class ResponsiveImageSelector
{
friend class ResponsiveImageCandidate;
public:
NS_DECL_ISUPPORTS
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ResponsiveImageSelector)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ResponsiveImageSelector)
explicit ResponsiveImageSelector(nsIContent* aContent);
// NOTE ABOUT CURRENT SELECTION
//
// The best candidate is selected lazily when GetSelectedImage*() is
// called, or when SelectImage() is called explicitly. This result
// is then cached until either invalidated by further Set*() calls,
// or explicitly by replaced by SelectImage(aReselect = true).
//
// Because the selected image depends on external variants like
// viewport size and device pixel ratio, the time at which image
// selection occurs can affect the result.
// Given a srcset string, parse and replace current candidates (does not
// replace default source)
bool SetCandidatesFromSourceSet(const nsAString & aSrcSet);
@ -41,10 +56,19 @@ public:
nsIContent *Content() { return mContent; }
// Get the URL for the selected best candidate
// Get the url and density for the selected best candidate. These
// implicitly cause an image to be selected if necessary.
already_AddRefed<nsIURI> GetSelectedImageURL();
double GetSelectedImageDensity();
// Runs image selection now if necessary. If an image has already
// been choosen, takes no action unless aReselect is true.
//
// aReselect - Always re-run selection, replacing the previously
// choosen image.
// return - true if the selected image result changed.
bool SelectImage(bool aReselect = false);
protected:
virtual ~ResponsiveImageSelector();

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

@ -12,6 +12,11 @@ nsAttrValueOrString::String() const
return *mStringPtr;
}
if (!mAttrValue) {
mStringPtr = &mCheapString;
return *mStringPtr;
}
if (mAttrValue->Type() == nsAttrValue::eString) {
mCheapString = mAttrValue->GetStringValue();
mStringPtr = &mCheapString;

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

@ -8,6 +8,9 @@
* because constructing an nsAttrValue from an nsAString can be expensive when
* the buffer of the string is not shared.
*
* This treats nsAttrValueOrString(nullptr) as the empty string,
* to help with contexts where a null pointer denotes an empty value.
*
* Since a raw pointer to the passed-in string is kept, this class should only
* be used on the stack.
*/
@ -26,12 +29,25 @@ public:
, mStringPtr(&aValue)
, mCheapString(nullptr)
{ }
explicit nsAttrValueOrString(const nsAString* aValue)
: mAttrValue(nullptr)
, mStringPtr(aValue)
, mCheapString(nullptr)
{ }
explicit nsAttrValueOrString(const nsAttrValue& aValue)
: mAttrValue(&aValue)
, mStringPtr(nullptr)
, mCheapString(nullptr)
{ }
explicit nsAttrValueOrString(const nsAttrValue* aValue)
: mAttrValue(aValue)
, mStringPtr(nullptr)
, mCheapString(nullptr)
{ }
/**
* Returns a reference to the string value of the contents of this object.
*

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

@ -681,6 +681,7 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
{
switch (aType) {
case nsIContentPolicy::TYPE_IMAGE:
case nsIContentPolicy::TYPE_IMAGESET:
return CSP_IMG_SRC;
case nsIContentPolicy::TYPE_SCRIPT:

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

@ -2861,7 +2861,8 @@ bool
nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
nsIDocument* aLoadingDocument,
nsIPrincipal* aLoadingPrincipal,
int16_t* aImageBlockingStatus)
int16_t* aImageBlockingStatus,
uint32_t aContentType)
{
NS_PRECONDITION(aURI, "Must have a URI");
NS_PRECONDITION(aLoadingDocument, "Must have a document");
@ -2904,7 +2905,7 @@ nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
int16_t decision = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_IMAGE,
rv = NS_CheckContentLoadPolicy(aContentType,
aURI,
aLoadingPrincipal,
aContext,
@ -2985,7 +2986,8 @@ nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
imgINotificationObserver* aObserver, int32_t aLoadFlags,
const nsAString& initiatorType,
imgRequestProxy** aRequest)
imgRequestProxy** aRequest,
uint32_t aContentPolicyType)
{
NS_PRECONDITION(aURI, "Must have a URI");
NS_PRECONDITION(aLoadingDocument, "Must have a document");
@ -3018,7 +3020,8 @@ nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
aObserver, /* imgINotificationObserver */
aLoadingDocument, /* uniquification key */
aLoadFlags, /* load flags */
nullptr, /* cache key */
nullptr, /* cache key */
aContentPolicyType, /* content policy type */
initiatorType, /* the load initiator */
aRequest);
}

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

@ -96,7 +96,8 @@ nsDataDocumentContentPolicy::ShouldLoad(uint32_t aContentType,
aContentLocation);
}
}
} else if (aContentType == nsIContentPolicy::TYPE_IMAGE &&
} else if ((aContentType == nsIContentPolicy::TYPE_IMAGE ||
aContentType == nsIContentPolicy::TYPE_IMAGESET) &&
doc->GetDocumentURI()) {
// Check for (& disallow) recursive image-loads
bool isRecursiveLoad;

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

@ -35,7 +35,7 @@ public:
nsresult Init(imgRequestProxy* aImageRequest)
{
// No need to notify, since we have no frame.
return UseAsPrimaryRequest(aImageRequest, false);
return UseAsPrimaryRequest(aImageRequest, false, eImageLoadType_Normal);
}
// nsIContent overrides

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

@ -522,6 +522,19 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
}
}
/* static */
nsContentPolicyType
nsImageLoadingContent::PolicyTypeForLoad(ImageLoadType aImageLoadType)
{
if (aImageLoadType == eImageLoadType_Imageset) {
return nsIContentPolicy::TYPE_IMAGESET;
}
MOZ_ASSERT(aImageLoadType == eImageLoadType_Normal,
"Unknown ImageLoadType type in PolicyTypeForLoad");
return nsIContentPolicy::TYPE_IMAGE;
}
int32_t
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
ErrorResult& aError)
@ -600,7 +613,7 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
// Do the load.
nsCOMPtr<nsIStreamListener> listener;
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(eImageLoadType_Normal);
nsresult rv = nsContentUtils::GetImgLoaderForChannel(aChannel)->
LoadImageWithChannel(aChannel, this, doc,
getter_AddRefs(listener),
@ -632,7 +645,8 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
}
void
nsImageLoadingContent::ForceReload(ErrorResult& aError)
nsImageLoadingContent::ForceReload(const mozilla::dom::Optional<bool>& aNotify,
mozilla::ErrorResult& aError)
{
nsCOMPtr<nsIURI> currentURI;
GetCurrentURI(getter_AddRefs(currentURI));
@ -641,16 +655,32 @@ nsImageLoadingContent::ForceReload(ErrorResult& aError)
return;
}
nsresult rv = LoadImage(currentURI, true, true, nullptr, nsIRequest::VALIDATE_ALWAYS);
// defaults to true
bool notify = !aNotify.WasPassed() || aNotify.Value();
// We keep this flag around along with the old URI even for failed requests
// without a live request object
ImageLoadType loadType = \
(mCurrentRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
: eImageLoadType_Normal;
nsresult rv = LoadImage(currentURI, true, notify, loadType, nullptr,
nsIRequest::VALIDATE_ALWAYS);
if (NS_FAILED(rv)) {
aError.Throw(rv);
}
}
NS_IMETHODIMP nsImageLoadingContent::ForceReload()
NS_IMETHODIMP
nsImageLoadingContent::ForceReload(bool aNotify /* = true */,
uint8_t aArgc)
{
mozilla::dom::Optional<bool> notify;
if (aArgc >= 1) {
notify.Construct() = aNotify;
}
ErrorResult result;
ForceReload(result);
ForceReload(notify, result);
return result.ErrorCode();
}
@ -735,7 +765,8 @@ nsImageLoadingContent::GetVisibleCount()
nsresult
nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
bool aForce,
bool aNotify)
bool aNotify,
ImageLoadType aImageLoadType)
{
// First, get a document (needed for security checks and the like)
nsIDocument* doc = GetOurOwnerDoc();
@ -758,9 +789,9 @@ nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
// Loading an embedded img from the same URI as the document URI will not work
// as a resource cannot recursively embed itself. Attempting to do so generally
// results in having to pre-emptively close down an in-flight HTTP transaction
// results in having to pre-emptively close down an in-flight HTTP transaction
// and then incurring the significant cost of establishing a new TCP channel.
// This is generally triggered from <img src="">
// This is generally triggered from <img src="">
// In light of that, just skip loading it..
// Do make sure to drop our existing image, if any
CancelImageRequests(aNotify);
@ -769,13 +800,14 @@ nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
NS_TryToSetImmutable(imageURI);
return LoadImage(imageURI, aForce, aNotify, doc);
return LoadImage(imageURI, aForce, aNotify, aImageLoadType, doc);
}
nsresult
nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
bool aForce,
bool aNotify,
ImageLoadType aImageLoadType,
nsIDocument* aDocument,
nsLoadFlags aLoadFlags)
{
@ -829,11 +861,14 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
// Are we blocked?
int16_t cpDecision = nsIContentPolicy::REJECT_REQUEST;
nsContentPolicyType policyType = PolicyTypeForLoad(aImageLoadType);
nsContentUtils::CanLoadImage(aNewURI,
static_cast<nsIImageLoadingContent*>(this),
aDocument,
aDocument->NodePrincipal(),
&cpDecision);
&cpDecision,
policyType);
if (!NS_CP_ACCEPTED(cpDecision)) {
FireEvent(NS_LITERAL_STRING("error"));
SetBlockedRequest(aNewURI, cpDecision);
@ -849,7 +884,7 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
}
// Not blocked. Do the load.
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType);
nsCOMPtr<nsIContent> content =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
nsresult rv;
@ -858,7 +893,8 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
aDocument->GetDocumentURI(),
this, loadFlags,
content->LocalName(),
getter_AddRefs(req));
getter_AddRefs(req),
policyType);
if (NS_SUCCEEDED(rv)) {
TrackImage(req);
@ -982,14 +1018,14 @@ nsImageLoadingContent::UpdateImageState(bool aNotify)
// XXX - This machinery should be removed after bug 521604.
return;
}
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
if (!thisContent) {
return;
}
mLoading = mBroken = mUserDisabled = mSuppressed = false;
// If we were blocked by server-based content policy, we claim to be
// suppressed. If we were blocked by type-based content policy, we claim to
// be user-disabled. Otherwise, claim to be broken.
@ -1024,7 +1060,8 @@ nsImageLoadingContent::CancelImageRequests(bool aNotify)
nsresult
nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest,
bool aNotify)
bool aNotify,
ImageLoadType aImageLoadType)
{
// Our state will change. Watch it.
AutoStateChanger changer(this, aNotify);
@ -1034,7 +1071,7 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest,
ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD);
// Clone the request we were given.
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType);
nsresult rv = aRequest->Clone(this, getter_AddRefs(req));
if (NS_SUCCEEDED(rv)) {
TrackImage(req);
@ -1125,15 +1162,15 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType)
}
nsRefPtr<imgRequestProxy>&
nsImageLoadingContent::PrepareNextRequest()
nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
{
// If we don't have a usable current request, get rid of any half-baked
// request that might be sitting there and make this one current.
if (!HaveSize(mCurrentRequest))
return PrepareCurrentRequest();
return PrepareCurrentRequest(aImageLoadType);
// Otherwise, make it pending.
return PreparePendingRequest();
return PreparePendingRequest(aImageLoadType);
}
void
@ -1155,16 +1192,19 @@ nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision)
if (!HaveSize(mCurrentRequest)) {
mImageBlockingStatus = aContentDecision;
uint32_t keepFlags = mCurrentRequestFlags & REQUEST_IS_IMAGESET;
ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD);
// We still want to remember what URI we were despite not having an actual
// request.
// We still want to remember what URI we were and if it was an imageset,
// despite not having an actual request. These are both cleared as part of
// ClearCurrentRequest() before a new request is started.
mCurrentURI = aURI;
mCurrentRequestFlags = keepFlags;
}
}
nsRefPtr<imgRequestProxy>&
nsImageLoadingContent::PrepareCurrentRequest()
nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType)
{
// Blocked images go through SetBlockedRequest, which is a separate path. For
// everything else, we're unblocked.
@ -1177,12 +1217,16 @@ nsImageLoadingContent::PrepareCurrentRequest()
mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
}
if (aImageLoadType == eImageLoadType_Imageset) {
mCurrentRequestFlags |= REQUEST_IS_IMAGESET;
}
// Return a reference.
return mCurrentRequest;
}
nsRefPtr<imgRequestProxy>&
nsImageLoadingContent::PreparePendingRequest()
nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType)
{
// Get rid of anything that was there previously.
ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD);
@ -1191,6 +1235,10 @@ nsImageLoadingContent::PreparePendingRequest()
mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
}
if (aImageLoadType == eImageLoadType_Imageset) {
mPendingRequestFlags |= REQUEST_IS_IMAGESET;
}
// Return a reference.
return mPendingRequest;
}
@ -1233,7 +1281,11 @@ nsImageLoadingContent::MakePendingRequestCurrent()
// to go to 0 and the image to be discarded!
ImageRequestAutoLock autoLock(mCurrentRequest);
PrepareCurrentRequest() = mPendingRequest;
ImageLoadType loadType = \
(mPendingRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
: eImageLoadType_Normal;
PrepareCurrentRequest(loadType) = mPendingRequest;
mPendingRequest = nullptr;
mCurrentRequestFlags = mPendingRequestFlags;
mPendingRequestFlags = 0;
@ -1246,8 +1298,9 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason,
{
if (!mCurrentRequest) {
// Even if we didn't have a current request, we might have been keeping
// a URI as a placeholder for a failed load. Clear that now.
// a URI and flags as a placeholder for a failed load. Clear that now.
mCurrentURI = nullptr;
mCurrentRequestFlags = 0;
return;
}
NS_ABORT_IF_FALSE(!mCurrentURI,

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

@ -22,6 +22,8 @@
#include "nsIRequest.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
#include "nsIContentPolicy.h"
#include "mozilla/dom/BindingDeclarations.h"
class nsIURI;
class nsIDocument;
@ -63,28 +65,46 @@ public:
int32_t
GetRequestType(imgIRequest* aRequest, mozilla::ErrorResult& aError);
already_AddRefed<nsIURI> GetCurrentURI(mozilla::ErrorResult& aError);
void ForceReload(const mozilla::dom::Optional<bool>& aNotify,
mozilla::ErrorResult& aError);
// XPCOM [optional] syntax helper
nsresult ForceReload(bool aNotify = true) {
return ForceReload(aNotify, 1);
}
/**
* Used to initialize content with a previously opened channel. Assumes
* eImageLoadType_Normal
*/
already_AddRefed<nsIStreamListener>
LoadImageWithChannel(nsIChannel* aChannel, mozilla::ErrorResult& aError);
void ForceReload(mozilla::ErrorResult& aError);
protected:
enum ImageLoadType {
// Most normal image loads
eImageLoadType_Normal,
// From a <img srcset> or <picture> context. Affects type given to content
// policy.
eImageLoadType_Imageset
};
/**
* LoadImage is called by subclasses when the appropriate
* attributes (eg 'src' for <img> tags) change. The string passed
* in is the new uri string; this consolidates the code for getting
* the charset, constructing URI objects, and any other incidentals
* into this superclass.
* into this superclass.
*
* @param aNewURI the URI spec to be loaded (may be a relative URI)
* @param aForce If true, make sure to load the URI. If false, only
* load if the URI is different from the currently loaded URI.
* @param aNotify If true, nsIDocumentObserver state change notifications
* will be sent as needed.
* @param aImageLoadType The ImageLoadType for this request
*/
nsresult LoadImage(const nsAString& aNewURI, bool aForce,
bool aNotify);
bool aNotify, ImageLoadType aImageLoadType);
/**
* ImageState is called by subclasses that are computing their content state.
@ -108,13 +128,14 @@ protected:
* load if the URI is different from the currently loaded URI.
* @param aNotify If true, nsIDocumentObserver state change notifications
* will be sent as needed.
* @param aImageLoadType The ImageLoadType for this request
* @param aDocument Optional parameter giving the document this node is in.
* This is purely a performance optimization.
* @param aLoadFlags Optional parameter specifying load flags to use for
* the image load
*/
nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify,
nsIDocument* aDocument = nullptr,
ImageLoadType aImageLoadType, nsIDocument* aDocument = nullptr,
nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
/**
@ -158,7 +179,8 @@ protected:
* effectively be called instead of LoadImage or LoadImageWithChannel.
* If aNotify is true, this method will notify on state changes.
*/
nsresult UseAsPrimaryRequest(imgRequestProxy* aRequest, bool aNotify);
nsresult UseAsPrimaryRequest(imgRequestProxy* aRequest, bool aNotify,
ImageLoadType aImageLoadType);
/**
* Derived classes of nsImageLoadingContent MUST call
@ -191,6 +213,9 @@ protected:
void OnUnlockedDraw();
nsresult OnImageIsAnimated(imgIRequest *aRequest);
// The nsContentPolicyType we would use for this ImageLoadType
static nsContentPolicyType PolicyTypeForLoad(ImageLoadType aImageLoadType);
private:
/**
* Struct used to manage the image observers.
@ -260,8 +285,10 @@ protected:
* a _usable_ current request (one with SIZE_AVAILABLE), this request is
* "pending" until it becomes usable. Otherwise, this becomes the current
* request.
*
* @param aImageLoadType The ImageLoadType for this request
*/
nsRefPtr<imgRequestProxy>& PrepareNextRequest();
nsRefPtr<imgRequestProxy>& PrepareNextRequest(ImageLoadType aImageLoadType);
/**
* Called when we would normally call PrepareNextRequest(), but the request was
@ -275,9 +302,11 @@ protected:
* to get rid of one of the requests, you should call
* Clear*Request(NS_BINDING_ABORTED) instead, since it passes a more appropriate
* aReason than Prepare*Request() does (NS_ERROR_IMAGE_SRC_CHANGED).
*
* @param aImageLoadType The ImageLoadType for this request
*/
nsRefPtr<imgRequestProxy>& PrepareCurrentRequest();
nsRefPtr<imgRequestProxy>& PreparePendingRequest();
nsRefPtr<imgRequestProxy>& PrepareCurrentRequest(ImageLoadType aImageLoadType);
nsRefPtr<imgRequestProxy>& PreparePendingRequest(ImageLoadType aImageLoadType);
/**
* Switch our pending request to be our current request.
@ -339,7 +368,10 @@ protected:
// Set if the request is blocking onload.
REQUEST_BLOCKS_ONLOAD = 0x00000002U,
// Set if the request is currently tracked with the document.
REQUEST_IS_TRACKED = 0x00000004U
REQUEST_IS_TRACKED = 0x00000004U,
// Set if this is an imageset request, such as from <img srcset> or
// <picture>
REQUEST_IS_IMAGESET = 0x00000008U
};
// If the image was blocked or if there was an error loading, it's nice to

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

@ -381,6 +381,7 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
// Active content (or content with a low value/risk-of-blocking ratio)
// that has been explicitly evaluated; listed here for documentation
// purposes and to avoid the assertion and warning for the default case.
case TYPE_IMAGESET:
case TYPE_CSP_REPORT:
case TYPE_DTD:
case TYPE_FETCH:

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

@ -27,6 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
websocket connections over https require an encrypted websocket protocol (wss:)
case nsIContentPolicy::TYPE_IMAGE:
case nsIContentPolicy::TYPE_IMAGESET:
case nsIContentPolicy::TYPE_MEDIA:
case nsIContentPolicy::TYPE_PING:
our ping implementation is off by default and does not comply with the current spec (bug 786347)
@ -181,18 +182,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
/* Part 2: Mixed Display tests */
// Shorthand for all image test variants
function imgHandlers(img, test) {
img.onload = function () {
parent.postMessage({"test": test, "msg": "insecure image loaded"}, "http://mochi.test:8888");
}
img.onerror = function() {
parent.postMessage({"test": test, "msg": "insecure image blocked"}, "http://mochi.test:8888");
}
}
// Test 2a: insecure image
var img = document.createElement("img");
img.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
img.onload = function() {
parent.postMessage({"test": "image", "msg": "insecure image loaded"}, "http://mochi.test:8888");
}
img.onerror = function() {
parent.postMessage({"test": "image", "msg": "insecure image blocked"}, "http://mochi.test:8888");
}
imgHandlers(img, "image");
// We don't need to append the image to the document. Doing so causes the image test to run twice.
// Test 2b: insecure media
var media = document.createElement("video");
media.src = "http://mochi.test:8888/tests/content/media/test/320x240.ogv?" + Math.floor((Math.random()*1000)+1);
@ -207,6 +212,59 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
}
// We don't need to append the video to the document. Doing so causes the image test to run twice.
/* Part 3: Mixed Active Tests for Image srcset */
// Test 3a: image with srcset
var imgA = document.createElement("img");
imgA.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
imgHandlers(imgA, "imageSrcset");
// Test 3b: image with srcset, using fallback from src, should still use imageset policy
var imgB = document.createElement("img");
imgB.srcset = " ";
imgB.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
imgHandlers(imgB, "imageSrcsetFallback");
// Test 3c: image in <picture>
var imgC = document.createElement("img");
var pictureC = document.createElement("picture");
var sourceC = document.createElement("source");
sourceC.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
pictureC.appendChild(sourceC);
pictureC.appendChild(imgC);
imgHandlers(imgC, "imagePicture");
// Test 3d: Loaded basic image switching to a <picture>, loading
// same source, should still redo the request with new
// policy.
var imgD = document.createElement("img");
imgD.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
imgD.onload = imgD.onerror = function() {
// Whether or not it loads, we want to now append it to a picture and observe
var pictureD = document.createElement("picture");
var sourceD = document.createElement("source");
sourceD.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
pictureD.appendChild(sourceD);
pictureD.appendChild(imgD);
imgHandlers(imgD, "imageJoinPicture");
}
// Test 3e: img load from <picture> source reverts to img.src as it
// is removed -- the new request should revert to mixed
// display policy
var imgE = document.createElement("img");
var pictureE = document.createElement("picture");
var sourceE = document.createElement("source");
sourceE.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
pictureE.appendChild(sourceE);
pictureE.appendChild(imgE);
imgE.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
imgE.onload = imgE.onerror = function() {
// Whether or not it loads, remove it from the picture and observe
pictureE.removeChild(imgE)
imgHandlers(imgE, "imageLeavePicture");
}
</script>
</body>
</html>

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

@ -45,6 +45,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
var testsToRun = {
iframe: false,
image: false,
imageSrcset: false,
imageSrcsetFallback: false,
imagePicture: false,
imageJoinPicture: false,
imageLeavePicture: false,
script: false,
stylesheet: false,
object: false,
@ -56,6 +61,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
document.getElementById("log").textContent += "\n" + msg;
}
function reloadFrame() {
document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/content/base/test/file_mixed_content_main.html"></iframe>';
}
function checkTestsCompleted() {
for (var prop in testsToRun) {
// some test hasn't run yet so we're not done
@ -71,7 +80,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
changePrefs(counter);
counter++;
log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/content/base/test/file_mixed_content_main.html"></iframe>';
reloadFrame();
}
else {
//set the prefs back to what they were set to originally
@ -125,7 +134,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
/* Mixed Display tests */
case "image":
//test that the image load matches the pref for dipslay content
//test that the image load matches the pref for display content
ok(blockDisplay == (event.data.msg == "insecure image blocked"), "image did not follow block_display_content pref");
testsToRun["image"] = true;
break;
@ -134,18 +143,53 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
ok(blockDisplay == (event.data.msg == "insecure media blocked"), "media did not follow block_display_content pref");
testsToRun["media"] = true;
break;
/* Images using the "imageset" policy, from <img srcset> and <picture>, do not get the mixed display exception */
case "imageSrcset":
ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcset did not follow block_active_content pref");
testsToRun["imageSrcset"] = true;
break;
case "imageSrcsetFallback":
ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcsetFallback did not follow block_active_content pref");
testsToRun["imageSrcsetFallback"] = true;
break;
case "imagePicture":
ok(blockActive == (event.data.msg == "insecure image blocked"), "imagePicture did not follow block_active_content pref");
testsToRun["imagePicture"] = true;
break;
case "imageJoinPicture":
ok(blockActive == (event.data.msg == "insecure image blocked"), "imageJoinPicture did not follow block_active_content pref");
testsToRun["imageJoinPicture"] = true;
break;
// Should return to mixed display mode
case "imageLeavePicture":
ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
testsToRun["imageLeavePicture"] = true;
break;
}
checkTestsCompleted();
}
// Enable <picture> and <img srcset> for test
SpecialPowers.pushPrefEnv({'set': [[ "dom.image.srcset.enabled", true ],
[ "dom.image.picture.enabled", true ]] },
function() {
// Kick off test
reloadFrame();
});
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
<div id="framediv">
<iframe id="testHarness" src="https://example.com/tests/content/base/test/file_mixed_content_main.html"></iframe>
</div>
<div id="framediv"></div>
<pre id="log"></pre>
</body>
</html>

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

@ -16,6 +16,8 @@
#include "nsIURL.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "nsContainerFrame.h"
@ -25,6 +27,8 @@
#include "nsIDOMWindow.h"
#include "nsFocusManager.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "nsAttrValueOrString.h"
#include "imgLoader.h"
// Responsive images!
#include "mozilla/dom/HTMLSourceElement.h"
@ -48,8 +52,11 @@
#include "mozilla/Preferences.h"
static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled";
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
#ifdef DEBUG
// Is aSubject a previous sibling of aNode.
static bool IsPreviousSibling(nsINode *aSubject, nsINode *aNode)
{
@ -64,10 +71,35 @@ static bool IsPreviousSibling(nsINode *aSubject, nsINode *aNode)
return false;
}
#endif
namespace mozilla {
namespace dom {
// Calls LoadSelectedImage on host element unless it has been superseded or
// canceled -- this is the synchronous section of "update the image data".
// https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
class ImageLoadTask : public nsRunnable
{
public:
explicit ImageLoadTask(HTMLImageElement *aElement) :
mElement(aElement)
{}
NS_IMETHOD Run()
{
if (mElement->mPendingImageLoadTask == this) {
mElement->mPendingImageLoadTask = nullptr;
mElement->LoadSelectedImage(true, true);
}
return NS_OK;
}
private:
~ImageLoadTask() {}
nsRefPtr<HTMLImageElement> mElement;
};
HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
, mForm(nullptr)
@ -85,6 +117,9 @@ HTMLImageElement::~HTMLImageElement()
NS_IMPL_ADDREF_INHERITED(HTMLImageElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLImageElement, Element)
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLImageElement,
nsGenericHTMLElement,
mResponsiveSelector)
// QueryInterface implementation for HTMLImageElement
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLImageElement)
@ -366,40 +401,43 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// from the parser or some such place; we'll get bound after all the
// attributes have been set, so we'll do the image load from BindToTree.
nsCOMPtr<nsIContent> thisContent = AsContent();
nsAttrValueOrString attrVal(aValue);
if (aName == nsGkAtoms::src &&
aNameSpaceID == kNameSpaceID_None) {
// SetAttr handles setting src in the non-responsive case, so only handle it
// for responsive mode or unsetting
if (!aValue) {
aNameSpaceID == kNameSpaceID_None &&
!aValue) {
// SetAttr handles setting src since it needs to catch img.src =
// img.src, so we only need to handle the unset case
if (InResponsiveMode()) {
if (mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(nullptr);
}
QueueImageLoadTask();
} else {
// Bug 1076583 - We still behave synchronously in the non-responsive case
CancelImageRequests(aNotify);
} else if (mResponsiveSelector) {
mResponsiveSelector->SetDefaultSource(aValue ? aValue->GetStringValue()
: EmptyString());
LoadSelectedImage(false, aNotify);
}
} else if (aName == nsGkAtoms::srcset &&
aNameSpaceID == kNameSpaceID_None &&
aNotify &&
AsContent()->IsInDoc() &&
IsSrcsetEnabled()) {
// We currently don't handle responsive mode until BindToTree
PictureSourceSrcsetChanged(thisContent,
aValue ? aValue->GetStringValue() : EmptyString(),
aNotify);
PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
} else if (aName == nsGkAtoms::sizes &&
aNameSpaceID == kNameSpaceID_None &&
thisContent->IsInDoc() &&
HTMLPictureElement::IsPictureEnabled()) {
PictureSourceSizesChanged(thisContent, aValue->GetStringValue(), aNotify);
PictureSourceSizesChanged(this, attrVal.String(), aNotify);
} else if (aName == nsGkAtoms::crossorigin &&
aNameSpaceID == kNameSpaceID_None &&
aNotify) {
// We want aForce == true in this LoadImage call, because we want to force
// a new load of the image with the new cross origin policy.
nsCOMPtr<nsIURI> currentURI;
if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
LoadImage(currentURI, true, aNotify);
// Force a new load of the image with the new cross origin policy.
if (InResponsiveMode()) {
// per spec, full selection runs when this changes, even though
// it doesn't directly affect the source selection
QueueImageLoadTask();
} else {
// Bug 1076583 - We still use the older synchronous algorithm in
// non-responsive mode. Force a new load of the image with the
// new cross origin policy.
ForceReload(aNotify);
}
}
@ -456,7 +494,7 @@ HTMLImageElement::IsHTMLFocusable(bool aWithMouse,
*aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1;
}
*aIsFocusable =
*aIsFocusable =
#ifdef XP_MACOSX
(!aWithMouse || nsFocusManager::sMouseFocusesFormControl) &&
#endif
@ -475,32 +513,45 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// being set to its existing value, which is normally optimized away as a
// no-op.
//
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// image load from BindToTree. Skip the LoadImage call in that case.
// If we are in responsive mode, we drop the forced reload behavior,
// but still trigger a image load task for img.src = img.src per
// spec.
//
// If we are in responsive mode, we drop the forced reload behavior, and
// handle updates in AfterSetAttr
if (aNotify && !mResponsiveSelector &&
aNameSpaceID == kNameSpaceID_None &&
// Both cases handle unsetting src in AfterSetAttr
if (aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::src) {
// Prevent setting image.src by exiting early
// This is for dom.disable_image_src_set, which predates "srcset"
// as an attribute. See Bug 773429
if (nsContentUtils::IsImageSrcSetDisabled()) {
return NS_OK;
}
// A hack to get animations to reset. See bug 594771.
mNewRequestsWillNeedAnimationReset = true;
if (InResponsiveMode()) {
if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(aValue);
}
QueueImageLoadTask();
} else if (aNotify) {
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// sync image load from BindToTree. Skip the LoadImage call in that case.
// Force image loading here, so that we'll try to load the image from
// network if it's set to be not cacheable... If we change things so that
// the state gets in Element's attr-setting happen around this
// LoadImage call, we could start passing false instead of aNotify
// here.
LoadImage(aValue, true, aNotify);
// Note that this sync behavior is partially removed from the spec, bug 1076583
mNewRequestsWillNeedAnimationReset = false;
// A hack to get animations to reset. See bug 594771.
mNewRequestsWillNeedAnimationReset = true;
// Force image loading here, so that we'll try to load the image from
// network if it's set to be not cacheable... If we change things so that
// the state gets in Element's attr-setting happen around this
// LoadImage call, we could start passing false instead of aNotify
// here.
LoadImage(aValue, true, aNotify, eImageLoadType_Normal);
mNewRequestsWillNeedAnimationReset = false;
}
}
return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
@ -526,27 +577,31 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
bool addedToPicture = aParent && aParent->Tag() == nsGkAtoms::picture &&
HTMLPictureElement::IsPictureEnabled();
bool haveSrcset = IsSrcsetEnabled() &&
HasAttr(kNameSpaceID_None, nsGkAtoms::srcset);
if (addedToPicture || haveSrcset ||
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
if (addedToPicture) {
QueueImageLoadTask();
} else if (!InResponsiveMode() &&
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
// We skip loading when our attributes were set from parser land,
// so trigger a aForce=false load now to check if things changed.
// This isn't necessary for responsive mode, since creating the
// image load task is asynchronous we don't need to take special
// care to avoid doing so when being filled by the parser.
// FIXME: Bug 660963 it would be nice if we could just have
// ClearBrokenState update our state and do it fast...
ClearBrokenState();
RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
// We don't handle responsive changes when not bound to a tree, update them
// now if necessary
if (addedToPicture || haveSrcset) {
MaybeUpdateResponsiveSelector();
}
// We still act synchronously for the non-responsive case (Bug
// 1076583), but still need to delay if it is unsafe to run
// script.
// If loading is temporarily disabled, don't even launch MaybeLoadImage.
// Otherwise MaybeLoadImage may run later when someone has reenabled
// loading.
if (LoadingEnabled()) {
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
}
}
@ -564,7 +619,13 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
}
}
mResponsiveSelector = nullptr;
if (aNullParent &&
nsINode::GetParentNode()->Tag() == nsGkAtoms::picture &&
HTMLPictureElement::IsPictureEnabled()) {
// Being removed from picture re-triggers selection, even if we
// weren't using a <source> peer
QueueImageLoadTask();
}
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
@ -606,9 +667,9 @@ HTMLImageElement::MaybeLoadImage()
// Note, check LoadingEnabled() after LoadImage call.
nsresult rv = LoadSelectedImage(false, true);
LoadSelectedImage(false, true);
if (NS_FAILED(rv) || !LoadingEnabled()) {
if (!LoadingEnabled()) {
CancelImageRequests(true);
}
}
@ -785,32 +846,85 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
mForm = nullptr;
}
void
HTMLImageElement::QueueImageLoadTask()
{
// 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()) {
return;
}
// The task checks this to determine if it was the last queued event, so this
// implicitly cancels earlier tasks
mPendingImageLoadTask = new ImageLoadTask(this);
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
appShell->RunInStableState(mPendingImageLoadTask);
} else {
MOZ_ASSERT(false, "expect appshell for HTMLImageElement");
}
}
bool
HTMLImageElement::HaveSrcsetOrInPicture()
{
if (IsSrcsetEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::srcset)) {
return true;
}
if (!HTMLPictureElement::IsPictureEnabled()) {
return false;
}
nsINode *parent = nsINode::GetParentNode();
return (parent && parent->Tag() == nsGkAtoms::picture);
}
bool
HTMLImageElement::InResponsiveMode()
{
// When we lose srcset or leave a <picture> element, the fallback to img.src
// will happen from the microtask, and we should behave responsively in the
// interim
return mResponsiveSelector ||
mPendingImageLoadTask ||
HaveSrcsetOrInPicture();
}
nsresult
HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
{
nsresult rv = NS_ERROR_FAILURE;
if (aForce) {
// In responsive mode we generally want to re-run the full
// selection algorithm whenever starting a new load, per
// spec. This also causes us to re-resolve the URI as appropriate.
UpdateResponsiveSource();
}
if (mResponsiveSelector) {
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
if (url) {
rv = LoadImage(url, aForce, aNotify);
} else {
CancelImageRequests(aNotify);
rv = NS_OK;
}
rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset);
} else {
nsAutoString src;
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
CancelImageRequests(aNotify);
rv = NS_OK;
} else {
rv = LoadImage(src, aForce, aNotify);
if (NS_FAILED(rv)) {
CancelImageRequests(aNotify);
}
// If we have a srcset attribute or are in a <picture> element,
// we always use the Imageset load type, even if we parsed no
// valid responsive sources from either, per spec.
rv = LoadImage(src, aForce, aNotify,
HaveSrcsetOrInPicture() ? eImageLoadType_Imageset
: eImageLoadType_Normal);
}
}
if (NS_FAILED(rv)) {
CancelImageRequests(aNotify);
}
return rv;
}
@ -819,31 +933,28 @@ HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
const nsAString& aNewValue,
bool aNotify)
{
if (aSourceNode != AsContent() && !HTMLPictureElement::IsPictureEnabled()) {
// Don't consider <source> nodes if picture is pref'd off
bool isSelf = aSourceNode == this;
if (!IsSrcsetEnabled() ||
(!isSelf && !HTMLPictureElement::IsPictureEnabled())) {
return;
}
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
: nullptr;
MOZ_ASSERT(isSelf || IsPreviousSibling(aSourceNode, this),
"Should not be getting notifications for non-previous-siblings");
nsIContent *currentSrc =
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
if (aSourceNode == currentSrc) {
// We're currently using this node as our responsive selector source.
// We're currently using this node as our responsive selector
// source.
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
// Search for a new source if we are no longer valid.
MaybeUpdateResponsiveSelector(currentSrc);
LoadSelectedImage(false, aNotify);
} else if (currentSrc && IsPreviousSibling(currentSrc, aSourceNode)) {
// If we have a source and it is previous to the one being updated, ignore
return;
} else {
// This is previous to our current source or we don't have a current source,
// use it if valid.
if (TryCreateResponsiveSelector(aSourceNode, &aNewValue, nullptr)) {
LoadSelectedImage(false, aNotify);
}
}
// This always triggers the image update steps per the spec, even if
// we are not using this source.
QueueImageLoadTask();
}
void
@ -852,109 +963,118 @@ HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
bool aNotify)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
// Don't consider sizes at all if picture support is disabled
return;
}
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
: nullptr;
MOZ_ASSERT(aSourceNode == this ||
IsPreviousSibling(aSourceNode, this),
"Should not be getting notifications for non-previous-siblings");
nsIContent *currentSrc =
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
if (aSourceNode == currentSrc) {
// We're currently using this node as our responsive selector source.
// We're currently using this node as our responsive selector
// source.
mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
LoadSelectedImage(false, aNotify);
}
// This always triggers the image update steps per the spec, even if
// we are not using this source.
QueueImageLoadTask();
}
void
HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode,
bool aNotify)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
"Should not be getting notifications for non-previous-siblings");
// This always triggers the image update steps per the spec, even if
// we are not switching to/from this source
QueueImageLoadTask();
}
void
HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
{
// If the source node is previous to our current one, or ourselves if we have
// no responsive source, try to use it as a responsive source.
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
: AsContent();
if (HTMLPictureElement::IsPictureEnabled() &&
IsPreviousSibling(aSourceNode, currentSrc) &&
TryCreateResponsiveSelector(aSourceNode, nullptr, nullptr)) {
LoadSelectedImage(false, true);
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
QueueImageLoadTask();
}
void
HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
{
// If this is our current source, we'll need to find another one or leave
// responsive mode.
if (mResponsiveSelector && mResponsiveSelector->Content() == aSourceNode) {
MaybeUpdateResponsiveSelector(aSourceNode, true);
LoadSelectedImage(false, true);
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
QueueImageLoadTask();
}
bool
HTMLImageElement::MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource,
bool aSourceRemoved)
void
HTMLImageElement::UpdateResponsiveSource()
{
nsIContent *thisContent = AsContent();
if (!aCurrentSource && mResponsiveSelector) {
aCurrentSource = mResponsiveSelector->Content();
}
// If we have a source with candidates, no update is needed unless it is being
// removed
if (aCurrentSource && !aSourceRemoved &&
mResponsiveSelector->NumCandidates()) {
return false;
}
// Otherwise, invalidate
bool hadSelector = !!mResponsiveSelector;
mResponsiveSelector = nullptr;
if (!IsSrcsetEnabled()) {
return hadSelector;
mResponsiveSelector = nullptr;
return;
}
// See if there's another source node we could use.
nsIContent *currentSource =
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
nsIContent *nextSource = nullptr;
if (pictureEnabled && aCurrentSource && aCurrentSource != thisContent) {
// If current source is the <img> tag, there is no next candidate. Otherwise,
// it's the next sibling of the current source.
MOZ_ASSERT(IsPreviousSibling(aCurrentSource, thisContent) &&
thisContent->GetParentNode()->Tag() == nsGkAtoms::picture);
nextSource = aCurrentSource->GetNextSibling();
} else if (!aCurrentSource) {
// If no current source at all, start from the first possible source, which
// is the first node of the <picture> element or ourselves if we're not a
// picture
nsINode *parent = pictureEnabled ? thisContent->GetParentNode() : nullptr;
if (parent && parent->Tag() == nsGkAtoms::picture) {
nextSource = parent->GetFirstChild();
} else {
nextSource = thisContent;
}
nsINode *parent = pictureEnabled ? this->nsINode::GetParentNode() : nullptr;
nsINode *candidateSource = nullptr;
if (parent && parent->Tag() == nsGkAtoms::picture) {
// Walk source nodes previous to ourselves
candidateSource = parent->GetFirstChild();
} else {
candidateSource = this;
}
while (nextSource) {
if (nextSource == thisContent) {
// We are the last possible source, so stop searching if we match or
// not
TryCreateResponsiveSelector(nextSource);
while (candidateSource) {
if (candidateSource == currentSource) {
// found no better source before current, re-run selection on
// that and keep it if it's still usable.
mResponsiveSelector->SelectImage(true);
if (mResponsiveSelector->NumCandidates()) {
break;
}
// no longer valid
mResponsiveSelector = nullptr;
if (candidateSource == this) {
// No further possibilities
break;
}
} else if (candidateSource == this) {
// We are the last possible source
if (!TryCreateResponsiveSelector(candidateSource->AsContent())) {
// Failed to find any source
mResponsiveSelector = nullptr;
}
break;
} else if (nextSource->Tag() == nsGkAtoms::source &&
TryCreateResponsiveSelector(nextSource)) {
// If this led to a valid source, stop
} else if (candidateSource->Tag() == nsGkAtoms::source &&
TryCreateResponsiveSelector(candidateSource->AsContent())) {
// This led to a valid source, stop
break;
}
nextSource = nextSource->GetNextSibling();
candidateSource = candidateSource->GetNextSibling();
}
// State changed unless we didn't make a selector and didn't start with one
return mResponsiveSelector || hadSelector;
if (!candidateSource) {
// Ran out of siblings without finding ourself, e.g. XBL magic.
mResponsiveSelector = nullptr;
}
}
bool
@ -972,16 +1092,23 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
if (isSourceTag) {
DebugOnly<nsINode *> parent(nsINode::GetParentNode());
MOZ_ASSERT(parent && parent->Tag() == nsGkAtoms::picture);
MOZ_ASSERT(IsPreviousSibling(aSourceNode, AsContent()));
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
MOZ_ASSERT(pictureEnabled);
// Check media and type
HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
if (!src->MatchesCurrentMedia()) {
return false;
}
nsAutoString type;
if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
!imgLoader::SupportImageWithMimeType(NS_ConvertUTF16toUTF8(type).get())) {
return false;
}
} else if (aSourceNode->Tag() == nsGkAtoms::img) {
// Otherwise this is the <img> tag itself
MOZ_ASSERT(aSourceNode == AsContent());
MOZ_ASSERT(aSourceNode == this);
}
// Skip if has no srcset or an empty srcset
@ -999,7 +1126,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
// Try to parse
nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(this);
nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode);
if (!sel->SetCandidatesFromSourceSet(srcset)) {
// No possible candidates, don't need to bother parsing sizes
return false;
@ -1015,7 +1142,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
// If this is the <img> tag, also pull in src as the default source
if (!isSourceTag) {
MOZ_ASSERT(aSourceNode == AsContent());
MOZ_ASSERT(aSourceNode == this);
nsAutoString src;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
sel->SetDefaultSource(src);

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

@ -12,6 +12,7 @@
#include "nsIDOMHTMLImageElement.h"
#include "imgRequestProxy.h"
#include "Units.h"
#include "nsCycleCollectionParticipant.h"
// Only needed for IsPictureEnabled()
#include "mozilla/dom/HTMLPictureElement.h"
@ -26,6 +27,8 @@ class HTMLImageElement MOZ_FINAL : public nsGenericHTMLElement,
public nsIDOMHTMLImageElement
{
friend class HTMLSourceElement;
friend class HTMLPictureElement;
friend class ImageLoadTask;
public:
explicit HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
@ -35,6 +38,9 @@ public:
const Optional<uint32_t>& aHeight,
ErrorResult& aError);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLImageElement,
nsGenericHTMLElement)
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
@ -43,6 +49,8 @@ public:
// nsIDOMHTMLImageElement
NS_DECL_NSIDOMHTMLIMAGEELEMENT
NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLImageElement, img)
// override from nsImageLoadingContent
CORSMode GetCORSMode();
@ -197,6 +205,22 @@ public:
protected:
virtual ~HTMLImageElement();
// Queues a task to run LoadSelectedImage pending stable state.
//
// Pending Bug 1076583 this is only used by the responsive image
// algorithm (InResponsiveMode()) -- synchronous actions when just
// using img.src will bypass this, and update source and kick off
// image load synchronously.
void QueueImageLoadTask();
// True if we have a srcset attribute or a <picture> parent, regardless of if
// any valid responsive sources were parsed from either.
bool HaveSrcsetOrInPicture();
// True if we are using the newer image loading algorithm. This will be the
// only mode after Bug 1076583
bool InResponsiveMode();
// Resolve and load the current mResponsiveSelector (responsive mode) or src
// attr image.
nsresult LoadSelectedImage(bool aForce, bool aNotify);
@ -206,13 +230,26 @@ protected:
const nsAString& aNewValue, bool aNotify);
void PictureSourceSizesChanged(nsIContent *aSourceNode,
const nsAString& aNewValue, bool aNotify);
// As we re-run the source selection on these mutations regardless,
// we don't actually care which changed or to what
void PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode, bool aNotify);
void PictureSourceAdded(nsIContent *aSourceNode);
// This should be called prior to the unbind, such that nextsibling works
void PictureSourceRemoved(nsIContent *aSourceNode);
bool MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource = nullptr,
bool aSourceRemoved = false);
// Re-evaluates all source nodes (picture <source>,<img>) and finds
// the best source set for mResponsiveSelector. If a better source
// is found, creates a new selector and feeds the source to it. If
// the current ResponsiveSelector is not changed, runs
// SelectImage(true) to re-evaluate its candidates.
//
// Because keeping the existing selector is the common case (and we
// often do no-op reselections), this does not re-parse values for
// the existing mResponsiveSelector, meaning you need to update its
// parameters as appropriate before calling (or null it out to force
// recreation)
void UpdateResponsiveSource();
// Given a <source> node that is a previous sibling *or* ourselves, try to
// create a ResponsiveSelector.
@ -247,6 +284,8 @@ protected:
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData);
nsCOMPtr<nsIRunnable> mPendingImageLoadTask;
};
} // namespace dom

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

@ -1314,7 +1314,7 @@ HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
} else if (aNotify && aName == nsGkAtoms::src &&
mType == NS_FORM_INPUT_IMAGE) {
if (aValue) {
LoadImage(aValue->String(), true, aNotify);
LoadImage(aValue->String(), true, aNotify, eImageLoadType_Normal);
} else {
// Null value means the attr got unset; drop the image
CancelImageRequests(aNotify);
@ -1393,7 +1393,7 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// whether we have an image to load;
nsAutoString src;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
LoadImage(src, false, aNotify);
LoadImage(src, false, aNotify, eImageLoadType_Normal);
}
}
}
@ -4393,7 +4393,7 @@ HTMLInputElement::MaybeLoadImage()
nsAutoString uri;
if (mType == NS_FORM_INPUT_IMAGE &&
GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) &&
(NS_FAILED(LoadImage(uri, false, true)) ||
(NS_FAILED(LoadImage(uri, false, true, eImageLoadType_Normal)) ||
!LoadingEnabled())) {
CancelImageRequests(true);
}

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

@ -40,6 +40,28 @@ NS_IMPL_ISUPPORTS_INHERITED(HTMLPictureElement, nsGenericHTMLElement,
NS_IMPL_ELEMENT_CLONE(HTMLPictureElement)
void
HTMLPictureElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
{
// Find all img siblings after this <source> to notify them of its demise
nsCOMPtr<nsINode> child = GetChildAt(aIndex);
nsCOMPtr<nsIContent> nextSibling;
if (child && child->Tag() == nsGkAtoms::source) {
nextSibling = child->GetNextSibling();
}
nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
if (nextSibling && nextSibling->GetParentNode() == this) {
do {
HTMLImageElement* img = HTMLImageElement::FromContent(nextSibling);
if (img) {
img->PictureSourceRemoved(child->AsContent());
}
} while ( (nextSibling = nextSibling->GetNextSibling()) );
}
}
bool
HTMLPictureElement::IsPictureEnabled()
{

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

@ -29,6 +29,7 @@ public:
NS_DECL_NSIDOMHTMLPICTUREELEMENT
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) MOZ_OVERRIDE;
static bool IsPictureEnabled();

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

@ -63,9 +63,11 @@ HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// responsive parameter changes
nsINode *parent = nsINode::GetParentNode();
if (aNameSpaceID == kNameSpaceID_None &&
(aName == nsGkAtoms::srcset || aName == nsGkAtoms::sizes) &&
parent && parent->Tag() == nsGkAtoms::picture && MatchesCurrentMedia()) {
(aName == nsGkAtoms::srcset ||
aName == nsGkAtoms::sizes ||
aName == nsGkAtoms::media ||
aName == nsGkAtoms::type) &&
parent && parent->Tag() == nsGkAtoms::picture) {
nsString strVal = aValue ? aValue->GetStringValue() : EmptyString();
// Find all img siblings after this <source> and notify them of the change
nsCOMPtr<nsINode> sibling = AsContent();
@ -76,6 +78,9 @@ HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
img->PictureSourceSrcsetChanged(AsContent(), strVal, aNotify);
} else if (aName == nsGkAtoms::sizes) {
img->PictureSourceSizesChanged(AsContent(), strVal, aNotify);
} else if (aName == nsGkAtoms::media ||
aName == nsGkAtoms::type) {
img->PictureSourceMediaOrTypeChanged(AsContent(), aNotify);
}
}
}
@ -137,24 +142,6 @@ HTMLSourceElement::BindToTree(nsIDocument *aDocument,
return NS_OK;
}
void
HTMLSourceElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsINode *parent = nsINode::GetParentNode();
if (parent && parent->Tag() == nsGkAtoms::picture) {
// Find all img siblings after this <source> and notify them of our demise
nsCOMPtr<nsINode> sibling = AsContent();
while ( (sibling = sibling->GetNextSibling()) ) {
if (sibling->Tag() == nsGkAtoms::img) {
HTMLImageElement *img = static_cast<HTMLImageElement*>(sibling.get());
img->PictureSourceRemoved(AsContent());
}
}
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
JSObject*
HTMLSourceElement::WrapNode(JSContext* aCx)
{

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

@ -39,7 +39,6 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers) MOZ_OVERRIDE;
virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
// If this element's media attr matches for its owner document. Returns true
// if no media attr was set.

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

@ -85,7 +85,7 @@ SVGFEImageElement::LoadSVGImage(bool aForce, bool aNotify)
}
}
return LoadImage(href, aForce, aNotify);
return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
}
//----------------------------------------------------------------------

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

@ -127,7 +127,7 @@ SVGImageElement::LoadSVGImage(bool aForce, bool aNotify)
if (baseURI && !href.IsEmpty())
NS_MakeAbsoluteURI(href, href, baseURI);
return LoadImage(href, aForce, aNotify);
return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
}
//----------------------------------------------------------------------

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

@ -3854,6 +3854,32 @@ bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath& mPath, double x
return tempPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
}
// Returns a surface that contains only the part needed to draw aSourceRect.
// On entry, aSourceRect is relative to aSurface, and on return aSourceRect is
// relative to the returned surface.
static TemporaryRef<SourceSurface>
ExtractSubrect(SourceSurface* aSurface, mgfx::Rect* aSourceRect, DrawTarget* aTargetDT)
{
mgfx::Rect roundedOutSourceRect = *aSourceRect;
roundedOutSourceRect.RoundOut();
mgfx::IntRect roundedOutSourceRectInt;
if (!roundedOutSourceRect.ToIntRect(&roundedOutSourceRectInt)) {
return aSurface;
}
RefPtr<DrawTarget> subrectDT =
aTargetDT->CreateSimilarDrawTarget(roundedOutSourceRectInt.Size(), SurfaceFormat::B8G8R8A8);
if (!subrectDT) {
return aSurface;
}
*aSourceRect -= roundedOutSourceRect.TopLeft();
subrectDT->CopySurface(aSurface, roundedOutSourceRectInt, IntPoint());
return subrectDT->Snapshot();
}
//
// image
//
@ -3999,10 +4025,19 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
}
if (srcSurf) {
mgfx::Rect sourceRect(sx, sy, sw, sh);
if (element == mCanvasElement) {
// srcSurf is a snapshot of mTarget. If we draw to mTarget now, we'll
// trigger a COW copy of the whole canvas into srcSurf. That's a huge
// waste if sourceRect doesn't cover the whole canvas.
// We avoid copying the whole canvas by manually copying just the part
// that we need.
srcSurf = ExtractSubrect(srcSurf, &sourceRect, mTarget);
}
AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
DrawSurface(srcSurf,
mgfx::Rect(dx, dy, dw, dh),
mgfx::Rect(sx, sy, sw, sh),
sourceRect,
DrawSurfaceOptions(filter),
DrawOptions(CurrentState().globalAlpha, UsedOperation()));
} else {

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

@ -375,7 +375,7 @@ parent:
* batched and only processed for panning and zooming if content does not
* preventDefault.
*/
ContentReceivedTouch(ScrollableLayerGuid aGuid, bool aPreventDefault);
ContentReceivedTouch(ScrollableLayerGuid aGuid, uint64_t aInputBlockId, bool aPreventDefault);
/**
* Updates the zoom constraints for a scrollable frame in this tab.
@ -436,7 +436,7 @@ child:
AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
@ -462,10 +462,10 @@ child:
RealMouseEvent(WidgetMouseEvent event);
RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
MouseWheelEvent(WidgetWheelEvent event);
RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid);
RealTouchEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
// We use a separate message for touchmove events only to apply
// compression to them.
RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid) compress;
RealTouchMoveEvent(WidgetTouchEvent aEvent, ScrollableLayerGuid aGuid, uint64_t aInputBlockId) compress;
/**
* @see nsIDOMWindowUtils sendKeyEvent.

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

@ -826,6 +826,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mOrientation(eScreenOrientation_PortraitPrimary)
, mUpdateHitRegion(false)
, mPendingTouchPreventedResponse(false)
, mPendingTouchPreventedBlockId(0)
, mTouchEndCancelled(false)
, mEndTouchIsClick(false)
, mIgnoreKeyPressEvent(false)
@ -1511,7 +1512,7 @@ TabChild::SendPendingTouchPreventedResponse(bool aPreventDefault,
{
if (mPendingTouchPreventedResponse) {
MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
SendContentReceivedTouch(mPendingTouchPreventedGuid, aPreventDefault);
SendContentReceivedTouch(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, aPreventDefault);
mPendingTouchPreventedResponse = false;
}
}
@ -1961,7 +1962,7 @@ TabChild::FireSingleTapEvent(LayoutDevicePoint aPoint)
}
bool
TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
{
TABC_LOG("Handling long tap at %s with %p %p\n",
Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
@ -1991,7 +1992,7 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& a
TABC_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
}
SendContentReceivedTouch(aGuid, eventHandled);
SendContentReceivedTouch(aGuid, aInputBlockId, eventHandled);
return true;
}
@ -2260,7 +2261,8 @@ TabChild::CancelTapTracking()
bool
TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid)
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
TABC_LOG("Receiving touch event of type %d\n", aEvent.message);
@ -2289,14 +2291,15 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
if (mPendingTouchPreventedResponse) {
// We can enter here if we get two TOUCH_STARTs in a row and didn't
// respond to the first one. Respond to it now.
SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
SendContentReceivedTouch(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, false);
mPendingTouchPreventedResponse = false;
}
if (isTouchPrevented) {
SendContentReceivedTouch(aGuid, isTouchPrevented);
SendContentReceivedTouch(aGuid, aInputBlockId, isTouchPrevented);
} else {
mPendingTouchPreventedResponse = true;
mPendingTouchPreventedGuid = aGuid;
mPendingTouchPreventedBlockId = aInputBlockId;
}
break;
}
@ -2324,9 +2327,10 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
bool
TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid)
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
return RecvRealTouchEvent(aEvent, aGuid);
return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId);
}
void

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

@ -324,7 +324,8 @@ public:
virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
const mozilla::layers::ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) MOZ_OVERRIDE;
virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
@ -344,9 +345,11 @@ public:
const MaybeNativeKeyBinding& aBindings) MOZ_OVERRIDE;
virtual bool RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& event) MOZ_OVERRIDE;
virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) MOZ_OVERRIDE;
virtual bool RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) MOZ_OVERRIDE;
virtual bool RecvKeyEvent(const nsString& aType,
const int32_t& aKeyCode,
const int32_t& aCharCode,
@ -597,6 +600,7 @@ private:
bool mUpdateHitRegion;
bool mPendingTouchPreventedResponse;
ScrollableLayerGuid mPendingTouchPreventedGuid;
uint64_t mPendingTouchPreventedBlockId;
void FireSingleTapEvent(LayoutDevicePoint aPoint);
bool mTouchEndCancelled;

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

@ -644,10 +644,11 @@ void TabParent::HandleSingleTap(const CSSPoint& aPoint,
void TabParent::HandleLongTap(const CSSPoint& aPoint,
int32_t aModifiers,
const ScrollableLayerGuid &aGuid)
const ScrollableLayerGuid &aGuid,
uint64_t aInputBlockId)
{
if (!mIsDestroyed) {
unused << SendHandleLongTap(aPoint, aGuid);
unused << SendHandleLongTap(aPoint, aGuid, aInputBlockId);
}
}
@ -877,7 +878,7 @@ bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event)
if (mIsDestroyed) {
return false;
}
nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr);
nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
if (status == nsEventStatus_eConsumeNoDefault ||
!MapEventCoordinatesForChildProcess(&event)) {
return false;
@ -913,13 +914,13 @@ bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLaye
return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aGuid);
}
bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
{
if (mIsDestroyed) {
return false;
}
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid);
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid, aInputBlockId);
}
bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
@ -945,7 +946,7 @@ bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
if (mIsDestroyed) {
return false;
}
nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr);
nsEventStatus status = MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
if (status == nsEventStatus_eConsumeNoDefault ||
!MapEventCoordinatesForChildProcess(&event)) {
return false;
@ -999,7 +1000,7 @@ bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event)
if (mIsDestroyed) {
return false;
}
MaybeForwardEventToRenderFrame(event, nullptr);
MaybeForwardEventToRenderFrame(event, nullptr, nullptr);
if (!MapEventCoordinatesForChildProcess(&event)) {
return false;
}
@ -1067,7 +1068,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
}
ScrollableLayerGuid guid;
nsEventStatus status = MaybeForwardEventToRenderFrame(event, &guid);
uint64_t blockId;
nsEventStatus status = MaybeForwardEventToRenderFrame(event, &guid, &blockId);
if (status == nsEventStatus_eConsumeNoDefault || mIsDestroyed) {
return false;
@ -1076,8 +1078,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
MapEventCoordinatesForChildProcess(mChildProcessOffsetAtTouchStart, &event);
return (event.message == NS_TOUCH_MOVE) ?
PBrowserParent::SendRealTouchMoveEvent(event, guid) :
PBrowserParent::SendRealTouchEvent(event, guid);
PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId) :
PBrowserParent::SendRealTouchEvent(event, guid, blockId);
}
/*static*/ TabParent*
@ -2010,10 +2012,11 @@ TabParent::UseAsyncPanZoom()
nsEventStatus
TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid)
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
return rfp->NotifyInputEvent(aEvent, aOutTargetGuid);
return rfp->NotifyInputEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
}
return nsEventStatus_eIgnore;
}
@ -2067,10 +2070,11 @@ TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
bool
TabParent::RecvContentReceivedTouch(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const bool& aPreventDefault)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->ContentReceivedTouch(aGuid, aPreventDefault);
rfp->ContentReceivedTouch(aGuid, aInputBlockId, aPreventDefault);
}
return true;
}

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

@ -210,6 +210,7 @@ public:
const bool& aIsRoot,
const ZoomConstraints& aConstraints) MOZ_OVERRIDE;
virtual bool RecvContentReceivedTouch(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
const bool& aPreventDefault) MOZ_OVERRIDE;
virtual PColorPickerParent*
@ -233,7 +234,8 @@ public:
const ScrollableLayerGuid& aGuid);
void HandleLongTap(const CSSPoint& aPoint,
int32_t aModifiers,
const ScrollableLayerGuid& aGuid);
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId);
void HandleLongTapUp(const CSSPoint& aPoint,
int32_t aModifiers,
const ScrollableLayerGuid& aGuid);
@ -261,7 +263,7 @@ public:
bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
bool SendRealTouchEvent(WidgetTouchEvent& event);
bool SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
bool SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
bool SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
@ -423,8 +425,12 @@ private:
// |aOutTargetGuid| will contain the identifier
// of the APZC instance that handled the event. aOutTargetGuid may be
// null.
// |aOutInputBlockId| will contain the identifier of the input block
// that this event was added to, if there was one. aOutInputBlockId may
// be null.
nsEventStatus MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid);
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
// The offset for the child process which is sampled at touch start. This
// means that the touch events are relative to where the frame was at the
// start of the touch. We need to look for a better solution to this

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

@ -24,6 +24,9 @@
#include "nsILocalFileMac.h"
#include "nsCocoaFeatures.h"
#include "nsExceptionHandler.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
@ -469,6 +472,22 @@ nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
return NS_OK;
}
// Don't load "fbplugin" (a Facebook plugin) if we're running on OS X 10.10
// (Yosemite) or later. It crashes on load, in the call to LoadPlugin()
// below. See bug 1086977.
if (nsCocoaFeatures::OnYosemiteOrLater()) {
if (fileName.EqualsLiteral("fbplugin")) {
NS_WARNING("Preventing load of fbplugin (see bug 1086977)");
return NS_ERROR_FAILURE;
}
// The block above assumes that "fbplugin" is the filename of the plugin
// to be blocked, but be don't yet know for sure if this is true. It might
// also be the name of a file indirectly loaded by the plugin. So for the
// time being we must record extra information in our crash logs.
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"),
fileName);
}
// It's possible that our plugin has 2 entry points that'll give us mime type
// info. Quicktime does this to get around the need of having admin rights to
// change mime info in the resource fork. We need to use this info instead of
@ -476,6 +495,13 @@ nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
// Sadly we have to load the library for this to work.
rv = LoadPlugin(outLibrary);
if (nsCocoaFeatures::OnYosemiteOrLater()) {
// If we didn't crash in LoadPlugin(), change the previous annotation so we
// don't sow confusion. Unfortunately there's not (yet) any way to get rid
// of the annotation completely.
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"),
NS_LITERAL_CSTRING("Didn't crash, please ignore"));
}
if (NS_FAILED(rv))
return rv;

Двоичные данные
dom/tests/mochitest/general/image_100.png Normal file

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

После

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

Двоичные данные
dom/tests/mochitest/general/image_200.png Normal file

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

После

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

Двоичные данные
dom/tests/mochitest/general/image_50.png Normal file

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

После

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

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

@ -11,6 +11,9 @@ support-files =
file_moving_xhr.html
file_showModalDialog.html
historyframes.html
image_50.png
image_100.png
image_200.png
resource_timing_iframe.html
resource_timing_main_test.html
resource_timing_cross_origin.html
@ -61,6 +64,7 @@ skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'androi
[test_framedhistoryframes.html]
[test_idleapi_permissions.html]
skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet'
[test_img_mutations.html]
[test_interfaces.html]
skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
# [test_network_events.html]
@ -72,6 +76,7 @@ skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug
skip-if = buildapp == 'mulet'
[test_paste_selection.html]
skip-if = buildapp == 'mulet'
[test_picture_mutations.html]
[test_picture_pref.html]
[test_resource_timing.html]
skip-if = buildapp == 'b2g' || buildapp == 'mulet'

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

@ -0,0 +1,227 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Image srcset mutations</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
"use strict";
// Tests the relevant mutations part of the spec for img src and srcset
// and that img.src still behaves by the older spec. (Bug 1076583)
// https://html.spec.whatwg.org/#relevant-mutations
SimpleTest.waitForExplicitFinish();
// 50x50 png
var testPNG50 = new URL("image_50.png", location).href;
// 100x100 png
var testPNG100 = new URL("image_100.png", location).href;
// 200x200 png
var testPNG200 = new URL("image_200.png", location).href;
var tests = [];
var img;
var expectingErrors = 0;
var expectingLoads = 0;
var afterExpectCallback;
function onImgLoad() {
ok(expectingLoads > 0, "expected load");
if (expectingLoads > 0) {
expectingLoads--;
}
if (!expectingLoads && !expectingErrors) {
setTimeout(afterExpectCallback, 0);
}
}
function onImgError() {
ok(expectingErrors > 0, "expected error");
if (expectingErrors > 0) {
expectingErrors--;
}
if (!expectingLoads && !expectingErrors) {
setTimeout(afterExpectCallback, 0);
}
}
function expectEvents(loads, errors, callback) {
if (!loads && !errors) {
setTimeout(callback, 0);
} else {
expectingLoads += loads;
expectingErrors += errors;
info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events");
afterExpectCallback = callback;
}
}
//
// Test that img.src still does some work synchronously per the older spec (bug 1076583)
//
tests.push(function test1() {
info("test 1");
img.src = testPNG50;
is(img.currentSrc, testPNG50, "Should have synchronously selected source");
img.src = "non_existent_image.404";
ok(img.currentSrc.endsWith("non_existent_image.404"), "Should have synchronously selected source");
img.removeAttribute("src");
is(img.currentSrc, null, "Should have dropped currentSrc");
// Load another image while previous load is still pending
img.src = testPNG200;
is(img.currentSrc, testPNG200, "Should have synchronously selected source");
// No events should have fired synchronously, now we should get just one load (and no 404 error)
expectEvents(1, 0, nextTest);
});
// Setting srcset should be async
tests.push(function () {
info("test 2");
img.srcset = testPNG100;
is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request");
nextTest();
});
});
// Setting srcset, even to no ultimate effect, should trigger a reload
tests.push(function () {
info("test 3");
img.srcset = testPNG100 + " 1x, " + testPNG200 + " 2x";
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
nextTest();
});
});
// Should switch to src as 1x source
tests.push(function () {
info("test 4");
img.srcset = testPNG50 + " 2x";
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
nextTest();
});
});
// Changing src while we have responsive attributes should not be sync
tests.push(function () {
info("test 5");
img.src = testPNG100;
is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request");
// Switch to using srcset again for next test
img.srcset = testPNG100;
expectEvents(1, 0, nextTest);
});
});
// img.src = img.src should trigger an async event even in responsive mode
tests.push(function () {
info("test 6");
is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request");
img.src = img.src;
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(1, 0, nextTest);
});
// img.srcset = img.srcset should be a no-op
tests.push(function () {
info("test 7");
img.srcset = img.srcset;
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(0, 0, nextTest);
});
// Re-binding image to document should be a no-op
tests.push(function () {
info("test 8");
document.body.appendChild(img);
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(0, 0, nextTest);
});
// We should re-run our selection algorithm when any load event occurs
tests.push(function () {
info("test 9");
img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x";
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] },
function() {
// We don't currently dynamically switch, but doing so is
// up to the UA so we may in the future. In which case
// this test needs to be changed.
is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
img.src = img.src;
is(img.currentSrc, testPNG50, "Should still have testPNG50 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
nextTest();
});
});
});
});
// Removing srcset attr should async switch back to src
tests.push(function () {
info("test 10");
is(img.currentSrc, testPNG200, "Should have testPNG200 as current request");
img.removeAttribute("srcset");
is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request");
expectEvents(0, 0, nextTest);
});
});
function nextTest() {
if (tests.length) {
// Spin event loop to make sure no unexpected image events are
// pending (unexpected events will assert in the handlers)
setTimeout(function() {
(tests.shift())();
}, 0);
} else {
SimpleTest.finish();
}
}
addEventListener("load", function() {
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "1.0"],
[ "dom.image.srcset.enabled", true ]] },
function() {
// Create this after the pref is set, as it is guarding webIDL attributes
img = document.createElement("img");
img.addEventListener("load", onImgLoad);
img.addEventListener("error", onImgError);
document.body.appendChild(img);
setTimeout(nextTest, 0);
});
});
</script>
</body>
</html>

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

@ -0,0 +1,308 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Image srcset mutations</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
"use strict";
// Tests the relevant mutations part of the spec for <img> inside <picture> tags
// https://html.spec.whatwg.org/#relevant-mutations
SimpleTest.waitForExplicitFinish();
// 50x50 png
var testPNG50 = new URL("image_50.png", location).href;
// 100x100 png
var testPNG100 = new URL("image_100.png", location).href;
// 200x200 png
var testPNG200 = new URL("image_200.png", location).href;
var tests = [];
var img;
var picture;
var source1;
var source2;
var source3;
var expectingErrors = 0;
var expectingLoads = 0;
var afterExpectCallback;
function onImgLoad() {
ok(expectingLoads > 0, "expected load");
if (expectingLoads > 0) {
expectingLoads--;
}
if (!expectingLoads && !expectingErrors) {
setTimeout(afterExpectCallback, 0);
}
}
function onImgError() {
ok(expectingErrors > 0, "expected error");
if (expectingErrors > 0) {
expectingErrors--;
}
if (!expectingLoads && !expectingErrors) {
setTimeout(afterExpectCallback, 0);
}
}
function expectEvents(loads, errors, callback) {
if (!loads && !errors) {
setTimeout(callback, 0);
} else {
expectingLoads += loads;
expectingErrors += errors;
info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events");
afterExpectCallback = callback;
}
}
// Setup image outside the tree dom, make sure it loads
tests.push(function() {
info("test 1");
img.srcset = testPNG100;
img.src = testPNG50;
is(img.currentSrc, null, "Should not have synchronously selected source");
// No events should have fired synchronously, now we should get just one load (and no 404 error)
expectEvents(1, 0, nextTest);
});
// Binding to an empty picture should trigger an event, even if source doesn't change
tests.push(function() {
info("test 2");
is(img.currentSrc, testPNG100, "Should have loaded testPNG100");
document.body.appendChild(picture);
picture.appendChild(img);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
expectEvents(1, 0, nextTest);
});
// inserting and removing an empty source before the image should both trigger a no-op reload
tests.push(function() {
info("test 3");
is(img.currentSrc, testPNG100, "Should still have testPNG100");
picture.insertBefore(source1, img);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// should fire one event, not change source
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should still have testPNG100");
picture.removeChild(source1);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// Should also no-op fire
expectEvents(1, 0, nextTest);
});
});
// insert and remove valid source before
tests.push(function() {
info("test 4");
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// Insert source1 before img with valid candidate
source1.srcset = testPNG50;
picture.insertBefore(source1, img);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// should fire one event, change to the source
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
picture.removeChild(source1);
is(img.currentSrc, testPNG50, "Should still have testPNG50");
// Should also no-op fire
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should have returned to testPNG100");
nextTest();
});
});
});
// insert and remove valid source after
tests.push(function() {
info("test 5");
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// Insert source1 before img with valid candidate
source1.srcset = testPNG50;
picture.appendChild(source1);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// should fire nothing, no action
expectEvents(0, 0, function() {
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// Same with removing
picture.removeChild(source1);
expectEvents(0, 0, function() {
is(img.currentSrc, testPNG100, "Should still have testPNG100");
nextTest();
});
});
});
// Should re-consider earlier sources when a load event occurs.
tests.push(function() {
info("test 6");
// Insert two valid sources, with MQ causing us to select the second
source1.srcset = testPNG50 + " 1x";
source1.media = "(min-resolution: 2dppx)"; // Wont match, test starts at 1x
source2.srcset = testPNG200;
picture.insertBefore(source1, img);
picture.insertBefore(source2, img);
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// should get one load, selecting source2
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG200, "Should have selected testPNG200");
// Switch DPI to match the first source, then add a source
// *also* wanting that DPI *just before* the selected
// source. Properly re-running the algorithm should
// re-consider all sources and thus go back to the first
// source, not just the valid source just inserted before us.
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] },
function() {
// When we add dynamic reaction to MQs, this test will need to be updated.
is(img.currentSrc, testPNG200, "Should still have testPNG200");
source3.media = source1.media;
source3.srcset = testPNG100;
picture.insertBefore(source3, source2);
// This should trigger a reload, but we should re-consider
// source1 and select that, not just the newly added source2
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should have selected testPNG50");
expectEvents(0, 0, nextTest);
});
});
});
});
// insert and remove valid source after our current source should
// trigger a reload, but not switch source
tests.push(function() {
info("test 7");
// Should be using source1 from last test
is(img.currentSrc, testPNG50, "Should still have testPNG50");
// Remove source2, should trigger an event even though we would
// not switch
picture.removeChild(source2);
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should still have testPNG50");
// Same with re-adding
picture.insertBefore(source2, img);
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should still have testPNG50");
expectEvents(0, 0, nextTest);
});
});
});
// Changing source attributes should trigger updates
tests.push(function() {
info("test 8");
// Should be using source1 from last test
is(img.currentSrc, testPNG50, "Should still have testPNG50");
// Reconfigure source1 to have empty srcset. Should switch to
// next source due to becoming invalid.
source1.srcset = "";
is(img.currentSrc, testPNG50, "Should still have testPNG50");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should have switched to testPNG100");
// Give source1 valid sizes. Should trigger an event but not switch anywhere.
source1.sizes = "100vw";
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG100, "Should still have testPNG100");
// And a valid MQ
source1.media = "(min-resolution: 3dppx)";
expectEvents(1, 0, function() {
// And a valid type...
source1.type = "image/png";
expectEvents(1, 0, function() {
// Finally restore srcset, should trigger load and re-consider source1 which is valid again
source1.srcset = testPNG50;
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should have selected testPNG50");
expectEvents(0, 0, nextTest);
});
});
});
});
});
});
// Inserting a source after <img> and touching its attributes should all be no-ops
tests.push(function() {
info("test 9");
// Setup: source2
picture.removeChild(source2);
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should still have testPNG50");
source2.srcset = testPNG200;
source2.media = "";
source2.sizes = "100vw";
source2.type = "image/png";
// Append valid source
picture.appendChild(source2);
// Touch all the things (but keep it valid)
source2.srcset = testPNG100;
source2.media = "(min-resolution: 2dppx)";
source2.sizes = "50vw";
source2.type = "image/png";
// No event should fire. Source should not change.
expectEvents(0, 0, function() {
is(img.currentSrc, testPNG50, "Should still have testPNG50");
expectEvents(0, 0, nextTest);
});
});
});
function nextTest() {
if (tests.length) {
// Spin event loop to make sure no unexpected image events are
// pending (unexpected events will assert in the handlers)
setTimeout(function() {
(tests.shift())();
}, 0);
} else {
SimpleTest.finish();
}
}
addEventListener("load", function() {
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "1.0" ],
[ "dom.image.srcset.enabled", true ],
[ "dom.image.picture.enabled", true ]] },
function() {
// Create these after the pref is set, as it is guarding webIDL attributes
img = document.createElement("img");
img.addEventListener("load", onImgLoad);
img.addEventListener("error", onImgError);
picture = document.createElement("picture");
source1 = document.createElement("source");
source2 = document.createElement("source");
source3 = document.createElement("source");
setTimeout(nextTest, 0);
});
});
</script>
</body>
</html>

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

@ -103,7 +103,7 @@ interface MozImageLoadingContent {
[ChromeOnly,Throws]
nsIStreamListener? loadImageWithChannel(MozChannel aChannel);
[ChromeOnly,Throws]
void forceReload();
void forceReload(optional boolean aNotify);
[ChromeOnly]
void forceImageState(boolean aForce, unsigned long long aState);
};

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

@ -275,7 +275,7 @@ nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode *aDOMNode, imgRe
// bail for the parent node of the root element or null argument
if (!domElement)
break;
nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
window->GetComputedStyle(domElement, EmptyString(),
getter_AddRefs(computedStyle));
@ -296,7 +296,8 @@ nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode *aDOMNode, imgRe
return il->LoadImage(bgUri, nullptr, nullptr, principal, nullptr,
nullptr, nullptr, nsIRequest::LOAD_NORMAL,
nullptr, EmptyString(), aRequest);
nullptr, nsIContentPolicy::TYPE_IMAGE,
EmptyString(), aRequest);
}
}

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

@ -42,7 +42,7 @@ nsWebBrowserContentPolicy::ShouldLoad(uint32_t contentType,
if (!shell) {
return NS_OK;
}
nsresult rv;
bool allowed = true;
@ -60,6 +60,7 @@ nsWebBrowserContentPolicy::ShouldLoad(uint32_t contentType,
break;
#endif
case nsIContentPolicy::TYPE_IMAGE:
case nsIContentPolicy::TYPE_IMAGESET:
rv = shell->GetAllowImages(&allowed);
break;
default:

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

@ -1191,6 +1191,8 @@ public:
#ifdef XP_MACOSX
static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
static TemporaryRef<GlyphRenderingOptions>
CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
#endif
#ifdef WIN32

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

@ -144,12 +144,19 @@ InterpolationQualityFromFilter(Filter aFilter)
DrawTargetCG::DrawTargetCG()
: mColorSpace(nullptr)
, mCg(nullptr)
, mMayContainInvalidPremultipliedData(false)
{
}
DrawTargetCG::~DrawTargetCG()
{
MarkChanged();
if (mSnapshot) {
if (mSnapshot->refCount() > 1) {
// We only need to worry about snapshots that someone else knows about
mSnapshot->DrawTargetWillGoAway();
}
mSnapshot = nullptr;
}
// Both of these are OK with nullptr arguments, so we do not
// need to check (these could be nullptr if Init fails)
@ -183,6 +190,7 @@ DrawTargetCG::Snapshot()
if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
return new SourceSurfaceCGIOSurfaceContext(this);
}
Flush();
mSnapshot = new SourceSurfaceCGBitmapContext(this);
}
@ -1165,16 +1173,59 @@ CGRect ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, f
return extents;
}
typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
static CGContextSetFontSmoothingBackgroundColorFunc
GetCGContextSetFontSmoothingBackgroundColorFunc()
{
static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
static bool lookedUpFunc = false;
if (!lookedUpFunc) {
func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
lookedUpFunc = true;
}
return func;
}
void
DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions,
const GlyphRenderingOptions*)
const GlyphRenderingOptions *aGlyphRenderingOptions)
{
MarkChanged();
assert(aBuffer.mNumGlyphs);
CGContextSaveGState(mCg);
if (aGlyphRenderingOptions && aGlyphRenderingOptions->GetType() == FontType::MAC) {
Color fontSmoothingBackgroundColor =
static_cast<const GlyphRenderingOptionsCG*>(aGlyphRenderingOptions)->FontSmoothingBackgroundColor();
if (fontSmoothingBackgroundColor.a > 0) {
CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
GetCGContextSetFontSmoothingBackgroundColorFunc();
if (setFontSmoothingBGColorFunc) {
CGColorRef color = ColorToCGColor(mColorSpace, fontSmoothingBackgroundColor);
setFontSmoothingBGColorFunc(mCg, color);
CGColorRelease(color);
// Font rendering with a non-transparent font smoothing background color
// can leave pixels in our buffer where the rgb components exceed the alpha
// component. When this happens we need to clean up the data afterwards.
// The purpose of this is probably the following: Correct compositing of
// subpixel anti-aliased fonts on transparent backgrounds requires
// different alpha values per RGB component. Usually, premultiplied color
// values are derived by multiplying all components with the same per-pixel
// alpha value. However, if you multiply each component with a *different*
// alpha, and set the alpha component of the pixel to, say, the average
// of the alpha values that you used during the premultiplication of the
// RGB components, you can trick OVER compositing into doing a simplified
// form of component alpha compositing. (You just need to make sure to
// clamp the components of the result pixel to [0,255] afterwards.)
mMayContainInvalidPremultipliedData = true;
}
}
}
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
@ -1433,11 +1484,54 @@ DrawTargetCG::Init(BackendType aType,
return true;
}
static void
EnsureValidPremultipliedData(CGContextRef aContext)
{
if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
return;
}
uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
int w = CGBitmapContextGetWidth(aContext);
int h = CGBitmapContextGetHeight(aContext);
int stride = CGBitmapContextGetBytesPerRow(aContext);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int i = y * stride + x * 4;
uint8_t a = bitmapData[i + 3];
// Clamp rgb components to the alpha component.
if (bitmapData[i + 0] > a) {
bitmapData[i + 0] = a;
}
if (bitmapData[i + 1] > a) {
bitmapData[i + 1] = a;
}
if (bitmapData[i + 2] > a) {
bitmapData[i + 2] = a;
}
}
}
}
void
DrawTargetCG::Flush()
{
if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
CGContextFlush(mCg);
} else if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP &&
mMayContainInvalidPremultipliedData) {
// We can't guarantee that all our users can handle pixel data where an RGB
// component value exceeds the pixel's alpha value. In particular, the
// color conversion that CG does when we draw a CGImage snapshot of this
// context into a context that has a different color space throws up on
// invalid premultiplied data and creates completely wrong colors.
// Sanitizing the data means that we lose some of the fake component alpha
// behavior that font rendering tries to give us, but the result still
// looks good enough to prefer it over grayscale font anti-aliasing.
EnsureValidPremultipliedData(mCg);
mMayContainInvalidPremultipliedData = false;
}
}
@ -1620,6 +1714,7 @@ BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
aDT->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) &&
!aDT->IsTiledDrawTarget() && !aDT->IsDualDrawTarget()) {
DrawTargetCG* cgDT = static_cast<DrawTargetCG*>(aDT);
cgDT->Flush();
cgDT->MarkChanged();
// swap out the context

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

@ -91,12 +91,29 @@ SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
}
}
class GlyphRenderingOptionsCG : public GlyphRenderingOptions
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsCG)
GlyphRenderingOptionsCG(const Color &aFontSmoothingBackgroundColor)
: mFontSmoothingBackgroundColor(aFontSmoothingBackgroundColor)
{}
const Color &FontSmoothingBackgroundColor() const { return mFontSmoothingBackgroundColor; }
virtual FontType GetType() const MOZ_OVERRIDE { return FontType::MAC; }
private:
Color mFontSmoothingBackgroundColor;
};
class DrawTargetCG : public DrawTarget
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCG)
friend class BorrowedCGContext;
friend class SourceSurfaceCGBitmapContext;
DrawTargetCG();
virtual ~DrawTargetCG();
@ -182,6 +199,7 @@ private:
AlignedArray<uint8_t> mData;
RefPtr<SourceSurfaceCGContext> mSnapshot;
bool mMayContainInvalidPremultipliedData;
};
}

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

@ -708,6 +708,12 @@ Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize
}
return retVal.forget();
}
TemporaryRef<GlyphRenderingOptions>
Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor)
{
return new GlyphRenderingOptionsCG(aFontSmoothingBackgroundColor);
}
#endif
TemporaryRef<DataSourceSurface>

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

@ -360,6 +360,24 @@ SourceSurfaceCGBitmapContext::DrawTargetWillChange()
}
}
void
SourceSurfaceCGBitmapContext::DrawTargetWillGoAway()
{
if (mDrawTarget) {
if (mDrawTarget->mData != CGBitmapContextGetData(mCg)) {
DrawTargetWillChange();
return;
}
// Instead of copying the data over, we can just swap it.
mDataHolder.Swap(mDrawTarget->mData);
mData = mDataHolder;
mCg = nullptr;
mDrawTarget = nullptr;
// mImage is still valid because it still points to the same data.
}
}
SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
{
if (mImage)

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

@ -101,6 +101,7 @@ class SourceSurfaceCGContext : public DataSourceSurface
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGContext)
virtual void DrawTargetWillChange() = 0;
virtual void DrawTargetWillGoAway() = 0;
virtual CGImageRef GetImage() = 0;
};
@ -139,6 +140,7 @@ private:
//XXX: do the other backends friend their DrawTarget?
friend class DrawTargetCG;
virtual void DrawTargetWillChange();
virtual void DrawTargetWillGoAway();
void EnsureImage() const;
// We hold a weak reference to these two objects.
@ -181,6 +183,7 @@ private:
//XXX: do the other backends friend their DrawTarget?
friend class DrawTargetCG;
virtual void DrawTargetWillChange();
virtual void DrawTargetWillGoAway() { DrawTargetWillChange(); }
void EnsureImage() const;
SurfaceFormat mFormat;

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

@ -7,6 +7,7 @@
#define MOZILLA_GFX_TOOLS_H_
#include "mozilla/CheckedInt.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h"
#include "Types.h"
#include "Point.h"
@ -178,6 +179,13 @@ struct AlignedArray
mCount = aCount;
}
void Swap(AlignedArray<T, alignment>& aOther)
{
mozilla::Swap(mPtr, aOther.mPtr);
mozilla::Swap(mStorage, aOther.mStorage);
mozilla::Swap(mCount, aOther.mCount);
}
MOZ_ALWAYS_INLINE operator T*()
{
return mPtr;

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

@ -135,6 +135,17 @@ enum SurfaceInitMode
INIT_MODE_CLEAR
};
/**
* A base class for a platform-dependent helper for use by TextureHost.
*/
class CompositorBackendSpecificData
{
NS_INLINE_DECL_REFCOUNTING(CompositorBackendSpecificData)
protected:
virtual ~CompositorBackendSpecificData() {}
};
/**
* Common interface for compositor backends.
*
@ -470,6 +481,10 @@ public:
return fillRatio;
}
virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() {
return nullptr;
}
ScreenRotation GetScreenRotation() const {
return mScreenRotation;
}

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

@ -62,7 +62,8 @@ public:
*/
virtual void HandleLongTap(const CSSPoint& aPoint,
int32_t aModifiers,
const ScrollableLayerGuid& aGuid) = 0;
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId) = 0;
/**
* Requests handling of releasing a long tap. |aPoint| is in CSS pixels,

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

@ -7,6 +7,7 @@
#include "AsyncPanZoomController.h"
#include "Compositor.h" // for Compositor
#include "CompositorParent.h" // for CompositorParent, etc
#include "InputBlockState.h" // for InputBlockState
#include "InputData.h" // for InputData, etc
#include "Layers.h" // for Layer, etc
#include "mozilla/dom/Touch.h" // for Touch
@ -71,7 +72,8 @@ APZCTreeManager::CalculatePendingDisplayPort(
}
APZCTreeManager::APZCTreeManager()
: mTreeLock("APZCTreeLock"),
: mInputQueue(new InputQueue()),
mTreeLock("APZCTreeLock"),
mInOverscrolledApzc(false),
mRetainedTouchIdentifier(-1),
mTouchCount(0),
@ -109,13 +111,10 @@ APZCTreeManager::GetAllowedTouchBehavior(WidgetInputEvent* aEvent,
}
void
APZCTreeManager::SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags> &aValues)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->SetAllowedTouchBehavior(aValues);
}
mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
}
/* Flatten the tree of APZC instances into the given nsTArray */
@ -300,7 +299,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
// a new one.
bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
if (newApzc) {
apzc = new AsyncPanZoomController(aLayersId, this, state->mController,
apzc = new AsyncPanZoomController(aLayersId, this, mInputQueue, state->mController,
AsyncPanZoomController::USE_GESTURE_DETECTOR);
apzc->SetCompositorParent(aState.mCompositor);
if (state->mCrossProcessParent != nullptr) {
@ -517,15 +516,21 @@ TransformScreenToGecko(T* aPoint, AsyncPanZoomController* aApzc, APZCTreeManager
nsEventStatus
APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
ScrollableLayerGuid* aOutTargetGuid)
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
{
// Initialize aOutInputBlockId to a sane value, and then later we overwrite
// it if the input event goes into a block.
if (aOutInputBlockId) {
*aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
}
nsEventStatus result = nsEventStatus_eIgnore;
Matrix4x4 transformToApzc;
bool inOverscrolledApzc = false;
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
result = ProcessTouchInput(touchInput, aOutTargetGuid);
result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
break;
} case PANGESTURE_INPUT: {
PanGestureInput& panInput = aEvent.AsPanGestureInput();
@ -538,7 +543,7 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
PanGestureInput inputForApzc(panInput);
transformToApzc = GetScreenToApzcTransform(apzc);
ApplyTransform(&(inputForApzc.mPanStartPoint), transformToApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
result = mInputQueue->ReceiveInputEvent(apzc, inputForApzc, aOutInputBlockId);
// Update the out-parameters so they are what the caller expects.
apzc->GetGuid(aOutTargetGuid);
@ -556,7 +561,7 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
PinchGestureInput inputForApzc(pinchInput);
transformToApzc = GetScreenToApzcTransform(apzc);
ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
result = mInputQueue->ReceiveInputEvent(apzc, inputForApzc, aOutInputBlockId);
// Update the out-parameters so they are what the caller expects.
apzc->GetGuid(aOutTargetGuid);
@ -574,7 +579,7 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
TapGestureInput inputForApzc(tapInput);
transformToApzc = GetScreenToApzcTransform(apzc);
ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
result = mInputQueue->ReceiveInputEvent(apzc, inputForApzc, aOutInputBlockId);
// Update the out-parameters so they are what the caller expects.
apzc->GetGuid(aOutTargetGuid);
@ -628,7 +633,8 @@ APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
nsEventStatus
APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
ScrollableLayerGuid* aOutTargetGuid)
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
{
if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
// If we are in an overscrolled state and a second finger goes down,
@ -653,7 +659,7 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
// in the middle of a panning touch block (for example) and not clean up properly.
if (mApzcForInputBlock) {
MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
mApzcForInputBlock->ReceiveInputEvent(cancel);
mInputQueue->ReceiveInputEvent(mApzcForInputBlock, cancel, nullptr);
}
mApzcForInputBlock = apzc;
}
@ -702,7 +708,7 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock, inputForApzc, aOutInputBlockId);
// For computing the event to pass back to Gecko, use the up-to-date transforms.
// This ensures that transformToApzc and transformToGecko are in sync
@ -757,7 +763,8 @@ APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
nsEventStatus
APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid)
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
{
MOZ_ASSERT(NS_IsMainThread());
nsEventStatus result = nsEventStatus_eIgnore;
@ -782,7 +789,8 @@ APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
nsEventStatus
APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid)
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId)
{
// This function will be removed as part of bug 930939.
// In general it is preferable to use the version of ReceiveInputEvent
@ -790,11 +798,17 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
MOZ_ASSERT(NS_IsMainThread());
// Initialize aOutInputBlockId to a sane value, and then later we overwrite
// it if the input event goes into a block.
if (aOutInputBlockId) {
*aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
}
switch (aEvent.mClass) {
case eTouchEventClass: {
WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
MultiTouchInput touchInput(touchEvent);
nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid);
nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
// touchInput was modified in-place to possibly remove some
// touch points (if we are overscrolled), and the coordinates were
// modified using the APZ untransform. We need to copy these changes
@ -807,7 +821,7 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
return result;
}
default: {
return ProcessEvent(aEvent, aOutTargetGuid);
return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId);
}
}
}
@ -823,13 +837,10 @@ APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
}
void
APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
APZCTreeManager::ContentReceivedTouch(uint64_t aInputBlockId,
bool aPreventDefault)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {
apzc->ContentReceivedTouch(aPreventDefault);
}
mInputQueue->ContentReceivedTouch(aInputBlockId, aPreventDefault);
}
void

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

@ -46,6 +46,7 @@ class APZPaintLogHelper;
class OverscrollHandoffChain;
struct OverscrollHandoffState;
class LayerMetricsWrapper;
class InputQueue;
/**
* ****************** NOTE ON LOCK ORDERING IN APZ **************************
@ -159,9 +160,12 @@ public:
* @param aEvent input event object; is modified in-place
* @param aOutTargetGuid returns the guid of the apzc this event was
* delivered to. May be null.
* @param aOutInputBlockId returns the id of the input block that this event
* was added to, if that was the case. May be null.
*/
nsEventStatus ReceiveInputEvent(InputData& aEvent,
ScrollableLayerGuid* aOutTargetGuid);
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
/**
* WidgetInputEvent handler. Transforms |aEvent| (which is assumed to be an
@ -175,13 +179,11 @@ public:
* NOTE: On unix, mouse events are treated as touch and are forwarded
* to the appropriate apz as such.
*
* @param aEvent input event object; is modified in-place
* @param aOutTargetGuid returns the guid of the apzc this event was
* delivered to. May be null.
* @return See documentation for other ReceiveInputEvent above.
* See documentation for other ReceiveInputEvent above.
*/
nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid);
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
/**
* A helper for transforming coordinates to gecko coordinate space.
@ -206,7 +208,7 @@ public:
* that have come in. If |aPreventDefault| is true, any touch events in the
* queue will be discarded.
*/
void ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
void ContentReceivedTouch(uint64_t aInputBlockId,
bool aPreventDefault);
/**
@ -266,12 +268,14 @@ public:
nsTArray<TouchBehaviorFlags>& aOutValues);
/**
* Sets allowed touch behavior values for current touch-session for specific apzc (determined by guid).
* Should be invoked by the widget. Each value of the aValues arrays corresponds to the different
* touch point that is currently active.
* Must be called after receiving the TOUCH_START event that starts the touch-session.
* Sets allowed touch behavior values for current touch-session for specific
* input block (determined by aInputBlock).
* Should be invoked by the widget. Each value of the aValues arrays
* corresponds to the different touch point that is currently active.
* Must be called after receiving the TOUCH_START event that starts the
* touch-session.
*/
void SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
void SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aValues);
/**
@ -355,6 +359,7 @@ public:
* Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|.
*/
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget);
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~APZCTreeManager();
@ -383,9 +388,11 @@ private:
already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
bool* aOutInOverscrolledApzc);
nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
ScrollableLayerGuid* aOutTargetGuid);
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
ScrollableLayerGuid* aOutTargetGuid);
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
const ZoomConstraints& aConstraints);
void FlushRepaintsRecursively(AsyncPanZoomController* aApzc);
@ -418,6 +425,13 @@ private:
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
const AsyncPanZoomController* apzc);
protected:
/* The input queue where input events are held until we know enough to
* figure out where they're going. Protected so gtests can access it.
*/
nsRefPtr<InputQueue> mInputQueue;
private:
/* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held.
* This lock does not need to be held while manipulating a single APZC instance in

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

@ -16,6 +16,7 @@
#include "GestureEventListener.h" // for GestureEventListener
#include "InputData.h" // for MultiTouchInput, etc
#include "InputBlockState.h" // for InputBlockState, TouchBlockState
#include "InputQueue.h" // for InputQueue
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
#include "TaskThrottler.h" // for TaskThrottler
#include "Units.h" // for CSSRect, CSSPoint, etc
@ -866,6 +867,7 @@ AsyncPanZoomController::InitializeGlobalState()
AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
APZCTreeManager* aTreeManager,
const nsRefPtr<InputQueue>& aInputQueue,
GeckoContentController* aGeckoContentController,
GestureBehavior aGestures)
: mLayersId(aLayersId),
@ -885,7 +887,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
mAsyncScrollTimeoutTask(nullptr),
mState(NOTHING),
mNotificationBlockers(0),
mTouchBlockBalance(0),
mInputQueue(aInputQueue),
mTreeManager(aTreeManager),
mAPZCId(sAsyncPanZoomControllerCount++),
mSharedLock(nullptr),
@ -926,6 +928,11 @@ AsyncPanZoomController::GetGestureEventListener() const {
return listener.forget();
}
const nsRefPtr<InputQueue>&
AsyncPanZoomController::GetInputQueue() const {
return mInputQueue;
}
void
AsyncPanZoomController::Destroy()
{
@ -933,8 +940,6 @@ AsyncPanZoomController::Destroy()
CancelAnimation();
mTouchBlockQueue.Clear();
{ // scope the lock
MonitorAutoLock lock(mRefPtrMonitor);
mGeckoContentController = nullptr;
@ -1006,79 +1011,6 @@ AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint
return true;
}
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
AssertOnControllerThread();
if (aEvent.mInputType != MULTITOUCH_INPUT) {
HandleInputEvent(aEvent);
// The return value for non-touch input isn't really used, so just return
// ConsumeDoDefault for now. This can be changed later if needed.
return nsEventStatus_eConsumeDoDefault;
}
TouchBlockState* block = nullptr;
if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
block = StartNewTouchBlock(false);
APZC_LOG("%p started new touch block %p\n", this, block);
// We want to cancel animations here as soon as possible (i.e. without waiting for
// content responses) because a finger has gone down and we don't want to keep moving
// the content under the finger. However, to prevent "future" touchstart events from
// interfering with "past" animations (i.e. from a previous touch block that is still
// being processed) we only do this animation-cancellation if there are no older
// touch blocks still in the queue.
if (block == CurrentTouchBlock()) {
if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
// If we're already in a fast fling, then we want the touch event to stop the fling
// and to disallow the touch event from being used as part of a fling.
block->DisallowSingleTap();
}
block->GetOverscrollHandoffChain()->CancelAnimations();
}
if (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret()) {
// Content may intercept the touch events and prevent-default them. So we schedule
// a timeout to give content time to do that.
ScheduleContentResponseTimeout();
} else {
// Content won't prevent-default this, so we can just pretend like we scheduled
// a timeout and it expired. Note that we will still receive a ContentReceivedTouch
// callback for this block, and so we need to make sure we adjust the touch balance.
APZC_LOG("%p not waiting for content response on block %p\n", this, block);
mTouchBlockBalance++;
block->TimeoutContentResponse();
}
} else if (mTouchBlockQueue.IsEmpty()) {
NS_WARNING("Received a non-start touch event while no touch blocks active!");
} else {
// this touch is part of the most-recently created block
block = mTouchBlockQueue.LastElement().get();
APZC_LOG("%p received new event in block %p\n", this, block);
}
if (!block) {
return nsEventStatus_eIgnore;
}
nsEventStatus result = ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
APZC_LOG("%p's current touch block is ready with preventdefault %d\n",
this, block->IsDefaultPrevented());
if (block->IsDefaultPrevented()) {
return result;
}
HandleInputEvent(aEvent);
return result;
}
// Otherwise, add it to the queue for the touch block
block->AddEvent(aEvent.AsMultiTouchInput());
return result;
}
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
AssertOnControllerThread();
@ -1534,7 +1466,7 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
CancelAnimation();
}
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
mPanGestureState = MakeUnique<InputBlockState>(this);
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
@ -1613,7 +1545,7 @@ nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput&
CancelAnimation();
}
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
mPanGestureState = MakeUnique<InputBlockState>(this);
return nsEventStatus_eConsumeNoDefault;
}
@ -1641,9 +1573,8 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
CSSPoint geckoScreenPoint;
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
StartNewTouchBlock(true);
ScheduleContentResponseTimeout();
controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid(), blockId);
return nsEventStatus_eConsumeNoDefault;
}
}
@ -2917,160 +2848,34 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
}
}
void
AsyncPanZoomController::ScheduleContentResponseTimeout() {
APZC_LOG("%p scheduling content response timeout\n", this);
PostDelayedTask(
NewRunnableMethod(this, &AsyncPanZoomController::ContentResponseTimeout),
gfxPrefs::APZContentResponseTimeout());
}
void
AsyncPanZoomController::ContentResponseTimeout() {
AssertOnControllerThread();
mTouchBlockBalance++;
APZC_LOG("%p got a content response timeout; balance %d\n", this, mTouchBlockBalance);
if (mTouchBlockBalance > 0) {
// Find the first touch block in the queue that hasn't already received
// the content response timeout callback, and notify it.
bool found = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->TimeoutContentResponse()) {
found = true;
break;
}
}
if (found) {
ProcessPendingInputBlocks();
} else {
NS_WARNING("APZC received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
}
}
}
void
AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
AssertOnControllerThread();
mTouchBlockBalance--;
APZC_LOG("%p got a content response; balance %d\n", this, mTouchBlockBalance);
if (mTouchBlockBalance < 0) {
// Find the first touch block in the queue that hasn't already received
// its response from content, and notify it.
bool found = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->SetContentResponse(aPreventDefault)) {
found = true;
break;
}
}
if (found) {
ProcessPendingInputBlocks();
} else {
NS_WARNING("APZC received more ContentReceivedTouch calls than it has unprocessed touch blocks\n");
}
}
}
void
AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
AssertOnControllerThread();
bool found = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
found = true;
break;
}
}
if (found) {
ProcessPendingInputBlocks();
} else {
NS_WARNING("APZC received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
}
}
void
AsyncPanZoomController::ProcessPendingInputBlocks() {
AssertOnControllerThread();
while (true) {
TouchBlockState* curBlock = CurrentTouchBlock();
if (!curBlock->IsReadyForHandling()) {
break;
}
APZC_LOG("%p processing input block %p; preventDefault %d\n",
this, curBlock, curBlock->IsDefaultPrevented());
if (curBlock->IsDefaultPrevented()) {
SetState(NOTHING);
curBlock->DropEvents();
// Also clear the state in the gesture event listener
nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
if (listener) {
MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
listener->HandleInputEvent(cancel);
}
} else {
while (curBlock->HasEvents()) {
HandleInputEvent(curBlock->RemoveFirstEvent());
}
}
MOZ_ASSERT(!curBlock->HasEvents());
if (mTouchBlockQueue.Length() == 1) {
// If |curBlock| is the only touch block in the queue, then it is still
// active and we cannot remove it yet. We only know that a touch block is
// over when we start the next one. This block will be removed by the code
// in StartNewTouchBlock, where new touch blocks are added.
break;
}
// If we get here, we know there are more touch blocks in the queue after
// |curBlock|, so we can remove |curBlock| and try to process the next one.
APZC_LOG("%p discarding depleted touch block %p\n", this, curBlock);
mTouchBlockQueue.RemoveElementAt(0);
}
}
TouchBlockState*
AsyncPanZoomController::StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent)
bool
AsyncPanZoomController::NeedToWaitForContent() const
{
TouchBlockState* newBlock = new TouchBlockState(BuildOverscrollHandoffChain());
if (gfxPrefs::TouchActionEnabled() && aCopyAllowedTouchBehaviorFromCurrent) {
newBlock->CopyAllowedTouchBehaviorsFrom(*CurrentTouchBlock());
}
// We're going to start a new block, so clear out any depleted blocks at the head of the queue.
// See corresponding comment in ProcessPendingInputBlocks.
while (!mTouchBlockQueue.IsEmpty()) {
if (mTouchBlockQueue[0]->IsReadyForHandling() && !mTouchBlockQueue[0]->HasEvents()) {
APZC_LOG("%p discarding depleted touch block %p\n", this, mTouchBlockQueue[0].get());
mTouchBlockQueue.RemoveElementAt(0);
} else {
break;
}
}
// Add the new block to the queue.
mTouchBlockQueue.AppendElement(newBlock);
return newBlock;
return (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret());
}
TouchBlockState*
AsyncPanZoomController::CurrentTouchBlock()
{
AssertOnControllerThread();
return GetInputQueue()->CurrentTouchBlock();
}
MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
return mTouchBlockQueue[0].get();
void
AsyncPanZoomController::ResetInputState()
{
SetState(NOTHING);
// Also clear the state in the gesture event listener
nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
if (listener) {
MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
listener->HandleInputEvent(cancel);
}
}
bool
AsyncPanZoomController::HasReadyTouchBlock()
{
return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
return GetInputQueue()->HasReadyTouchBlock();
}
AsyncPanZoomController::TouchBehaviorFlags

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

@ -19,6 +19,7 @@
#include "mozilla/Atomics.h"
#include "InputData.h"
#include "Axis.h"
#include "InputQueue.h"
#include "TaskThrottler.h"
#include "mozilla/gfx/Matrix.h"
#include "nsRegion.h"
@ -95,6 +96,7 @@ public:
AsyncPanZoomController(uint64_t aLayersId,
APZCTreeManager* aTreeManager,
const nsRefPtr<InputQueue>& aInputQueue,
GeckoContentController* aController,
GestureBehavior aGestures = DEFAULT_GESTURES);
@ -113,16 +115,6 @@ public:
// These methods must only be called on the controller/UI thread.
//
/**
* General handler for incoming input events. Manipulates the frame metrics
* based on what type of input it is. For example, a PinchGestureEvent will
* cause scaling. This should only be called externally to this class.
* HandleInputEvent() should be used internally.
* See the documentation on APZCTreeManager::ReceiveInputEvent for info on
* return values from this function.
*/
nsEventStatus ReceiveInputEvent(const InputData& aEvent);
/**
* Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
* in. The actual animation is done on the compositor thread after being set
@ -575,6 +567,11 @@ protected:
*/
APZCTreeManager* GetApzcTreeManager() const;
/**
* Gets a ref to the input queue that is shared across the entire tree manager.
*/
const nsRefPtr<InputQueue>& GetInputQueue() const;
/**
* Timeout function for mozbrowserasyncscroll event. Because we throttle
* mozbrowserasyncscroll events in some conditions, this function ensures
@ -583,15 +580,6 @@ protected:
*/
void FireAsyncScrollOnTimeout();
private:
/**
* Given the number of touch points in an input event and touch block they
* belong to, check if the event can result in a panning/zooming behavior.
* This is primarily used to figure out when to dispatch the pointercancel
* event for the pointer events spec.
*/
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
/**
* Convert ScreenPoint relative to this APZC to CSSPoint relative
* to the parent document. This excludes the transient compositor transform.
@ -780,97 +768,36 @@ private:
* listeners.
*/
public:
/**
* This function is invoked by the APZCTreeManager which in turn is invoked
* by the widget when web content decides whether or not it wants to
* cancel a block of events. This automatically gets applied to the next
* block of events that has not yet been responded to. This function MUST
* be invoked exactly once for each touch block.
*/
void ContentReceivedTouch(bool aPreventDefault);
/**
* Sets allowed touch behavior for current touch session.
* This method is invoked by the APZCTreeManager which in its turn invoked by
* the widget after performing touch-action values retrieving.
* Must be called after receiving the TOUCH_START even that started the
* touch session.
*/
void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
/**
* Flush a repaint request if one is needed, without throttling it with the
* paint throttler.
*/
void FlushRepaintForNewInputBlock();
private:
void ScheduleContentResponseTimeout();
void ContentResponseTimeout();
/**
* Processes any pending input blocks that are ready for processing. There
* must be at least one input block in the queue when this function is called.
* Given the number of touch points in an input event and touch block they
* belong to, check if the event can result in a panning/zooming behavior.
* This is primarily used to figure out when to dispatch the pointercancel
* event for the pointer events spec.
*/
void ProcessPendingInputBlocks();
TouchBlockState* StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent);
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
/**
* Return true if there are are touch listeners registered on content
* scrolled by this APZC.
*/
bool NeedToWaitForContent() const;
/**
* Clear internal state relating to input handling.
*/
void ResetInputState();
private:
nsRefPtr<InputQueue> mInputQueue;
TouchBlockState* CurrentTouchBlock();
bool HasReadyTouchBlock();
private:
// The queue of touch blocks that have not yet been processed by this APZC.
// This member must only be accessed on the controller/UI thread.
nsTArray<UniquePtr<TouchBlockState>> mTouchBlockQueue;
// This variable requires some explanation. Strap yourself in.
//
// For each block of events, we do two things: (1) send the events to gecko and expect
// exactly one call to ContentReceivedTouch in return, and (2) kick off a timeout
// that triggers in case we don't hear from web content in a timely fashion.
// Since events are constantly coming in, we need to be able to handle more than one
// block of input events sitting in the queue.
//
// There are ordering restrictions on events that we can take advantage of, and that
// we need to abide by. Blocks of events in the queue will always be in the order that
// the user generated them. Responses we get from content will be in the same order as
// as the blocks of events in the queue. The timeout callbacks that have been posted
// will also fire in the same order as the blocks of events in the queue.
// HOWEVER, we may get multiple responses from content interleaved with multiple
// timeout expirations, and that interleaving is not predictable.
//
// Therefore, we need to make sure that for each block of events, we process the queued
// events exactly once, either when we get the response from content, or when the
// timeout expires (whichever happens first). There is no way to associate the timeout
// or response from content with a particular block of events other than via ordering.
//
// So, what we do to accomplish this is to track a "touch block balance", which is the
// number of timeout expirations that have fired, minus the number of content responses
// that have been received. (Think "balance" as in teeter-totter balance). This
// value is:
// - zero when we are in a state where the next content response we expect to receive
// and the next timeout expiration we expect to fire both correspond to the next
// unprocessed block of events in the queue.
// - negative when we are in a state where we have received more content responses than
// timeout expirations. This means that the next content repsonse we receive will
// correspond to the first unprocessed block, but the next n timeout expirations need
// to be ignored as they are for blocks we have already processed. (n is the absolute
// value of the balance.)
// - positive when we are in a state where we have received more timeout expirations
// than content responses. This means that the next timeout expiration that we will
// receive will correspond to the first unprocessed block, but the next n content
// responses need to be ignored as they are for blocks we have already processed.
// (n is the absolute value of the balance.)
//
// Note that each touch block internally carries flags that indicate whether or not it
// has received a content response and/or timeout expiration. However, we cannot rely
// on that alone to deliver these notifications to the right input block, because
// once an input block has been processed, it can potentially be removed from the queue.
// Therefore the information in that block is lost. An alternative approach would
// be to keep around those blocks until they have received both the content response
// and timeout expiration, but that involves a higher level of memory usage.
//
// This member must only be accessed on the controller/UI thread.
int32_t mTouchBlockBalance;
/* ===================================================================
* The functions and members in this section are used to manage
@ -1028,6 +955,27 @@ public:
*/
bool SnapBackIfOverscrolled();
/**
* Build the chain of APZCs along which scroll will be handed off when
* this APZC receives input events.
*
* Notes on lifetime and const-correctness:
* - The returned handoff chain is |const|, to indicate that it cannot be
* changed after being built.
* - When passing the chain to a function that uses it without storing it,
* pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
* - When storing the chain, store it by RefPtr-to-const (as in
* |nsRefPtr<const OverscrollHandoffChain>|). This ensures the chain is
* kept alive. Note that queueing a task that uses the chain as an
* argument constitutes storing, as the task may outlive its queuer.
* - When passing the chain to a function that will store it, pass it as
* |const nsRefPtr<const OverscrollHandoffChain>&|. This allows the
* function to copy it into the |nsRefPtr<const OverscrollHandoffChain>|
* that will store it, while avoiding an unnecessary copy (and thus
* AddRef() and Release()) when passing it.
*/
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
private:
/**
* A helper function for calling APZCTreeManager::DispatchScroll().
@ -1054,26 +1002,6 @@ private:
*/
bool OverscrollBy(const ScreenPoint& aOverscroll);
/**
* Build the chain of APZCs along which scroll will be handed off when
* this APZC receives input events.
*
* Notes on lifetime and const-correctness:
* - The returned handoff chain is |const|, to indicate that it cannot be
* changed after being built.
* - When passing the chain to a function that uses it without storing it,
* pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
* - When storing the chain, store it by RefPtr-to-const (as in
* |nsRefPtr<const OverscrollHandoffChain>|). This ensures the chain is
* kept alive. Note that queueing a task that uses the chain as an
* argument constitutes storing, as the task may outlive its queuer.
* - When passing the chain to a function that will store it, pass it as
* |const nsRefPtr<const OverscrollHandoffChain>&|. This allows the
* function to copy it into the |nsRefPtr<const OverscrollHandoffChain>|
* that will store it, while avoiding an unnecessary copy (and thus
* AddRef() and Release()) when passing it.
*/
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
/* ===================================================================
* The functions and members in this section are used to maintain the

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

@ -6,6 +6,7 @@
#include "InputBlockState.h"
#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
#include "AsyncPanZoomController.h" // for AsyncPanZoomController
#include "gfxPrefs.h" // for gfxPrefs
#include "OverscrollHandoffState.h"
@ -15,11 +16,21 @@
namespace mozilla {
namespace layers {
InputBlockState::InputBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain)
: mOverscrollHandoffChain(aOverscrollHandoffChain)
static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
InputBlockState::InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
: mTargetApzc(aTargetApzc)
, mBlockId(sBlockCounter++)
{
// We should never be constructed with a nullptr handoff chain.
MOZ_ASSERT(mOverscrollHandoffChain);
// We should never be constructed with a nullptr target.
MOZ_ASSERT(mTargetApzc);
mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
}
const nsRefPtr<AsyncPanZoomController>&
InputBlockState::GetTargetApzc() const
{
return mTargetApzc;
}
const nsRefPtr<const OverscrollHandoffChain>&
@ -28,8 +39,14 @@ InputBlockState::GetOverscrollHandoffChain() const
return mOverscrollHandoffChain;
}
TouchBlockState::TouchBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain)
: InputBlockState(aOverscrollHandoffChain)
uint64_t
InputBlockState::GetBlockId() const
{
return mBlockId;
}
TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
: InputBlockState(aTargetApzc)
, mAllowedTouchBehaviorSet(false)
, mPreventDefault(false)
, mContentResponded(false)

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

@ -14,6 +14,7 @@
namespace mozilla {
namespace layers {
class AsyncPanZoomController;
class OverscrollHandoffChain;
/**
@ -23,11 +24,17 @@ class OverscrollHandoffChain;
class InputBlockState
{
public:
explicit InputBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain);
static const uint64_t NO_BLOCK_ID = 0;
explicit InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
const nsRefPtr<AsyncPanZoomController>& GetTargetApzc() const;
const nsRefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
uint64_t GetBlockId() const;
private:
nsRefPtr<AsyncPanZoomController> mTargetApzc;
nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
const uint64_t mBlockId;
};
/**
@ -66,7 +73,7 @@ class TouchBlockState : public InputBlockState
public:
typedef uint32_t TouchBehaviorFlags;
explicit TouchBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain);
explicit TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
/**
* Record whether or not content cancelled this block of events.

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

@ -0,0 +1,254 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InputQueue.h"
#include "AsyncPanZoomController.h"
#include "gfxPrefs.h"
#include "InputBlockState.h"
#include "OverscrollHandoffState.h"
#define INPQ_LOG(...)
// #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
namespace mozilla {
namespace layers {
InputQueue::InputQueue()
{
}
InputQueue::~InputQueue() {
mTouchBlockQueue.Clear();
}
nsEventStatus
InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent, uint64_t* aOutInputBlockId) {
AsyncPanZoomController::AssertOnControllerThread();
if (aEvent.mInputType != MULTITOUCH_INPUT) {
aTarget->HandleInputEvent(aEvent);
// The return value for non-touch input isn't really used, so just return
// ConsumeDoDefault for now. This can be changed later if needed.
return nsEventStatus_eConsumeDoDefault;
}
TouchBlockState* block = nullptr;
if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
block = StartNewTouchBlock(aTarget, false);
INPQ_LOG("started new touch block %p for target %p\n", block, aTarget.get());
// We want to cancel animations here as soon as possible (i.e. without waiting for
// content responses) because a finger has gone down and we don't want to keep moving
// the content under the finger. However, to prevent "future" touchstart events from
// interfering with "past" animations (i.e. from a previous touch block that is still
// being processed) we only do this animation-cancellation if there are no older
// touch blocks still in the queue.
if (block == CurrentTouchBlock()) {
if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
// If we're already in a fast fling, then we want the touch event to stop the fling
// and to disallow the touch event from being used as part of a fling.
block->DisallowSingleTap();
}
block->GetOverscrollHandoffChain()->CancelAnimations();
}
if (aTarget->NeedToWaitForContent()) {
// Content may intercept the touch events and prevent-default them. So we schedule
// a timeout to give content time to do that.
ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
} else {
// Content won't prevent-default this, so we can just pretend like we scheduled
// a timeout and it expired. Note that we will still receive a ContentReceivedTouch
// callback for this block, and so we need to make sure we adjust the touch balance.
INPQ_LOG("not waiting for content response on block %p\n", block);
block->TimeoutContentResponse();
}
} else if (mTouchBlockQueue.IsEmpty()) {
NS_WARNING("Received a non-start touch event while no touch blocks active!");
} else {
// this touch is part of the most-recently created block
block = mTouchBlockQueue.LastElement().get();
INPQ_LOG("received new event in block %p\n", block);
}
if (!block) {
return nsEventStatus_eIgnore;
}
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
nsEventStatus result = aTarget->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
INPQ_LOG("current touch block is ready with preventdefault %d\n",
block->IsDefaultPrevented());
if (block->IsDefaultPrevented()) {
return result;
}
aTarget->HandleInputEvent(aEvent);
return result;
}
// Otherwise, add it to the queue for the touch block
block->AddEvent(aEvent.AsMultiTouchInput());
return result;
}
uint64_t
InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
{
TouchBlockState* block = StartNewTouchBlock(aTarget, true);
INPQ_LOG("%p injecting new touch block with id %llu and target %p\n",
this, block->GetBlockId(), aTarget);
ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
return block->GetBlockId();
}
TouchBlockState*
InputQueue::StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent)
{
TouchBlockState* newBlock = new TouchBlockState(aTarget);
if (gfxPrefs::TouchActionEnabled() && aCopyAllowedTouchBehaviorFromCurrent) {
newBlock->CopyAllowedTouchBehaviorsFrom(*CurrentTouchBlock());
}
// We're going to start a new block, so clear out any depleted blocks at the head of the queue.
// See corresponding comment in ProcessPendingInputBlocks.
while (!mTouchBlockQueue.IsEmpty()) {
if (mTouchBlockQueue[0]->IsReadyForHandling() && !mTouchBlockQueue[0]->HasEvents()) {
INPQ_LOG("discarding depleted touch block %p\n", mTouchBlockQueue[0].get());
mTouchBlockQueue.RemoveElementAt(0);
} else {
break;
}
}
// Add the new block to the queue.
mTouchBlockQueue.AppendElement(newBlock);
return newBlock;
}
TouchBlockState*
InputQueue::CurrentTouchBlock() const
{
AsyncPanZoomController::AssertOnControllerThread();
MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
return mTouchBlockQueue[0].get();
}
bool
InputQueue::HasReadyTouchBlock() const
{
return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
}
void
InputQueue::ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId) {
INPQ_LOG("scheduling content response timeout for target %p\n", aTarget.get());
aTarget->PostDelayedTask(
NewRunnableMethod(this, &InputQueue::ContentResponseTimeout, aInputBlockId),
gfxPrefs::APZContentResponseTimeout());
}
void
InputQueue::ContentResponseTimeout(const uint64_t& aInputBlockId) {
AsyncPanZoomController::AssertOnControllerThread();
INPQ_LOG("got a content response timeout; block=%llu\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
success = mTouchBlockQueue[i]->TimeoutContentResponse();
break;
}
}
if (success) {
ProcessPendingInputBlocks();
}
}
void
InputQueue::ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
AsyncPanZoomController::AssertOnControllerThread();
INPQ_LOG("got a content response; block=%llu\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
success = mTouchBlockQueue[i]->SetContentResponse(aPreventDefault);
break;
}
}
if (success) {
ProcessPendingInputBlocks();
}
}
void
InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
AsyncPanZoomController::AssertOnControllerThread();
INPQ_LOG("got allowed touch behaviours; block=%llu\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
success = mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors);
break;
}
}
if (success) {
ProcessPendingInputBlocks();
} else {
NS_WARNING("INPQ received useless SetAllowedTouchBehavior");
}
}
void
InputQueue::ProcessPendingInputBlocks() {
AsyncPanZoomController::AssertOnControllerThread();
while (true) {
TouchBlockState* curBlock = CurrentTouchBlock();
if (!curBlock->IsReadyForHandling()) {
break;
}
INPQ_LOG("processing input block %p; preventDefault %d target %p\n",
curBlock, curBlock->IsDefaultPrevented(),
curBlock->GetTargetApzc().get());
nsRefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
if (curBlock->IsDefaultPrevented()) {
curBlock->DropEvents();
target->ResetInputState();
} else {
while (curBlock->HasEvents()) {
target->HandleInputEvent(curBlock->RemoveFirstEvent());
}
}
MOZ_ASSERT(!curBlock->HasEvents());
if (mTouchBlockQueue.Length() == 1) {
// If |curBlock| is the only touch block in the queue, then it is still
// active and we cannot remove it yet. We only know that a touch block is
// over when we start the next one. This block will be removed by the code
// in StartNewTouchBlock, where new touch blocks are added.
break;
}
// If we get here, we know there are more touch blocks in the queue after
// |curBlock|, so we can remove |curBlock| and try to process the next one.
INPQ_LOG("discarding depleted touch block %p\n", curBlock);
mTouchBlockQueue.RemoveElementAt(0);
}
}
} // namespace layers
} // namespace mozilla

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

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_InputQueue_h
#define mozilla_layers_InputQueue_h
#include "mozilla/EventForwards.h"
#include "mozilla/UniquePtr.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
namespace mozilla {
class InputData;
namespace layers {
class AsyncPanZoomController;
class OverscrollHandoffChain;
class TouchBlockState;
/**
* This class stores incoming input events, separated into "input blocks", until
* they are ready for handling. Currently input blocks are only created from
* touch input.
*/
class InputQueue {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
public:
typedef uint32_t TouchBehaviorFlags;
public:
InputQueue();
/**
* Notifies the InputQueue of a new incoming input event. The APZC that the
* input event was targeted to should be provided in the |aTarget| parameter.
* See the documentation on APZCTreeManager::ReceiveInputEvent for info on
* return values from this function, including |aOutInputBlockId|.
*/
nsEventStatus ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent, uint64_t* aOutInputBlockId);
/**
* This function should be invoked to notify the InputQueue when web content
* decides whether or not it wants to cancel a block of events. The block
* id to which this applies should be provided in |aInputBlockId|.
*/
void ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault);
/**
* This function should be invoked to notify the InputQueue of the touch-
* action properties for the different touch points in an input block. The
* input block this applies to should be specified by the |aInputBlockId|
* parameter. If touch-action is not enabled on the platform, this function
* does nothing and need not be called.
*/
void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors);
/**
* Adds a new touch block at the end of the input queue that has the same
* allowed touch behaviour flags as the the touch block currently being
* processed. This should only be called when processing of a touch block
* triggers the creation of a new touch block. Returns the input block id
* of the the newly-created block.
*/
uint64_t InjectNewTouchBlock(AsyncPanZoomController* aTarget);
/**
* Returns the touch block at the head of the queue.
*/
TouchBlockState* CurrentTouchBlock() const;
/**
* Returns true iff the touch block at the head of the queue is ready for
* handling.
*/
bool HasReadyTouchBlock() const;
private:
~InputQueue();
TouchBlockState* StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent);
void ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId);
void ContentResponseTimeout(const uint64_t& aInputBlockId);
void ProcessPendingInputBlocks();
private:
// The queue of touch blocks that have not yet been processed.
// This member must only be accessed on the controller/UI thread.
nsTArray<UniquePtr<TouchBlockState>> mTouchBlockQueue;
};
}
}
#endif // mozilla_layers_InputQueue_h

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

@ -24,6 +24,14 @@ namespace layers {
class Compositor;
CompositableBackendSpecificData::CompositableBackendSpecificData()
: mAllowSharingTextureHost(false)
{
static uint64_t sNextID = 1;
++sNextID;
mId = sNextID;
}
/**
* IPDL actor used by CompositableHost to match with its corresponding
* CompositableClient on the content side.
@ -79,6 +87,9 @@ CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
CompositableHost::~CompositableHost()
{
MOZ_COUNT_DTOR(CompositableHost);
if (mBackendData) {
mBackendData->ClearData();
}
}
PCompositableParent*
@ -110,6 +121,7 @@ CompositableHost::UseTextureHost(TextureHost* aTexture)
return;
}
aTexture->SetCompositor(GetCompositor());
aTexture->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
}
void
@ -118,12 +130,17 @@ CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
{
MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
aTextureOnBlack->SetCompositor(GetCompositor());
aTextureOnBlack->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
aTextureOnWhite->SetCompositor(GetCompositor());
aTextureOnWhite->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
}
void
CompositableHost::RemoveTextureHost(TextureHost* aTexture)
{}
{
// Clear strong refrence to CompositableBackendSpecificData
aTexture->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
}
void
CompositableHost::SetCompositor(Compositor* aCompositor)
@ -136,7 +153,7 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects,
const gfx::Matrix4x4& aTransform,
bool aIs3D)
{
CompositableTextureSourceRef source;
RefPtr<TextureSource> source;
RefPtr<TextureHost> host = GetAsTextureHost();
if (!host) {
@ -149,12 +166,14 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects,
return false;
}
if (!host->BindTextureSource(source)) {
source = host->GetTextureSources();
MOZ_ASSERT(source);
if (!source) {
NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
host->Unlock();
return false;
}
MOZ_ASSERT(source);
RefPtr<EffectMask> effect = new EffectMask(source,
source->GetSize(),
@ -173,6 +192,9 @@ CompositableHost::RemoveMaskEffect()
}
}
// implemented in TextureHostOGL.cpp
TemporaryRef<CompositableBackendSpecificData> CreateCompositableBackendSpecificDataOGL();
/* static */ TemporaryRef<CompositableHost>
CompositableHost::Create(const TextureInfo& aTextureInfo)
{
@ -205,6 +227,12 @@ CompositableHost::Create(const TextureInfo& aTextureInfo)
default:
NS_ERROR("Unknown CompositableType");
}
// We know that Tiled buffers don't use the compositable backend-specific
// data, so don't bother creating it.
if (result && aTextureInfo.mCompositableType != CompositableType::BUFFER_TILED) {
RefPtr<CompositableBackendSpecificData> data = CreateCompositableBackendSpecificDataOGL();
result->SetCompositableBackendSpecificData(data);
}
return result;
}

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

@ -49,6 +49,42 @@ class CompositableParentManager;
class PCompositableParent;
struct EffectChain;
/**
* A base class for doing CompositableHost and platform dependent task on TextureHost.
*/
class CompositableBackendSpecificData
{
protected:
virtual ~CompositableBackendSpecificData() {}
public:
NS_INLINE_DECL_REFCOUNTING(CompositableBackendSpecificData)
CompositableBackendSpecificData();
virtual void ClearData() {}
virtual void SetCompositor(Compositor* aCompositor) {}
bool IsAllowingSharingTextureHost()
{
return mAllowSharingTextureHost;
}
void SetAllowSharingTextureHost(bool aAllow)
{
mAllowSharingTextureHost = aAllow;
}
uint64_t GetId()
{
return mId;
}
public:
bool mAllowSharingTextureHost;
uint64_t mId;
};
/**
* The compositor-side counterpart to CompositableClient. Responsible for
* updating textures and data about textures from IPC and how textures are
@ -76,6 +112,16 @@ public:
virtual CompositableType GetType() = 0;
virtual CompositableBackendSpecificData* GetCompositableBackendSpecificData()
{
return mBackendData;
}
virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
mBackendData = aBackendData;
}
// If base class overrides, it should still call the parent implementation
virtual void SetCompositor(Compositor* aCompositor);
@ -206,6 +252,9 @@ public:
SetLayer(nullptr);
mAttached = false;
mKeepAttached = false;
if (mBackendData) {
mBackendData->ClearData();
}
}
}
bool IsAttached() { return mAttached; }
@ -265,6 +314,7 @@ protected:
uint64_t mCompositorID;
RefPtr<Compositor> mCompositor;
Layer* mLayer;
RefPtr<CompositableBackendSpecificData> mBackendData;
uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
bool mAttached;
bool mKeepAttached;

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

@ -35,12 +35,12 @@ ContentHostBase::~ContentHostBase()
}
void
ContentHostTexture::Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const Filter& aFilter,
const Rect& aClipRect,
const nsIntRegion* aVisibleRegion)
ContentHostBase::Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const Filter& aFilter,
const Rect& aClipRect,
const nsIntRegion* aVisibleRegion)
{
NS_ASSERTION(aVisibleRegion, "Requires a visible region");
@ -49,18 +49,14 @@ ContentHostTexture::Composite(EffectChain& aEffectChain,
return;
}
if (!mTextureHost->BindTextureSource(mTextureSource)) {
return;
}
MOZ_ASSERT(mTextureSource.get());
RefPtr<TextureSource> source = GetTextureSource();
RefPtr<TextureSource> sourceOnWhite = GetTextureSourceOnWhite();
if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
if (!source) {
return;
}
RefPtr<TexturedEffect> effect = CreateTexturedEffect(mTextureSource.get(),
mTextureSourceOnWhite.get(),
aFilter, true);
RefPtr<TexturedEffect> effect = GenEffect(aFilter);
if (!effect) {
return;
}
@ -85,7 +81,7 @@ ContentHostTexture::Composite(EffectChain& aEffectChain,
region.MoveBy(-origin);
// Figure out the intersecting draw region
gfx::IntSize texSize = mTextureSource->GetSize();
gfx::IntSize texSize = source->GetSize();
nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
textureRect.MoveBy(region.GetBounds().TopLeft());
nsIntRegion subregion;
@ -109,14 +105,14 @@ ContentHostTexture::Composite(EffectChain& aEffectChain,
regionRects.Or(regionRects, regionRect);
}
BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator();
BigImageIterator* bigImgIter = source->AsBigImageIterator();
BigImageIterator* iterOnWhite = nullptr;
if (bigImgIter) {
bigImgIter->BeginBigImageIteration();
}
if (mTextureSourceOnWhite) {
iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator();
if (sourceOnWhite) {
iterOnWhite = sourceOnWhite->AsBigImageIterator();
MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
"Tile count mismatch on component alpha texture");
if (iterOnWhite) {
@ -209,39 +205,32 @@ ContentHostTexture::Composite(EffectChain& aEffectChain,
aTransform, mFlashCounter);
}
TemporaryRef<TexturedEffect>
ContentHostBase::GenEffect(const gfx::Filter& aFilter)
{
RefPtr<TextureSource> source = GetTextureSource();
RefPtr<TextureSource> sourceOnWhite = GetTextureSourceOnWhite();
if (!source) {
return nullptr;
}
return CreateTexturedEffect(source, sourceOnWhite, aFilter, true);
}
void
ContentHostTexture::UseTextureHost(TextureHost* aTexture)
{
if (mTextureHost && mTextureHost != aTexture) {
mTextureHost->UnbindTextureSource();
}
ContentHostBase::UseTextureHost(aTexture);
mTextureHost = aTexture;
mTextureHostOnWhite = nullptr;
if (mTextureHost) {
mTextureHost->PrepareTextureSource(mTextureSource);
}
}
void
ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
TextureHost* aTextureOnWhite)
{
if (mTextureHost && mTextureHost != aTextureOnBlack) {
mTextureHost->UnbindTextureSource();
}
if (mTextureHostOnWhite && mTextureHostOnWhite != aTextureOnWhite) {
mTextureHostOnWhite->UnbindTextureSource();
}
ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite);
mTextureHost = aTextureOnBlack;
mTextureHostOnWhite = aTextureOnWhite;
if (mTextureHost) {
mTextureHost->PrepareTextureSource(mTextureSource);
}
if (mTextureHostOnWhite) {
mTextureHostOnWhite->PrepareTextureSource(mTextureSourceOnWhite);
}
}
void
@ -428,176 +417,6 @@ ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId,
FlushUpdateQueue();
}
void
ContentHostIncremental::Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const Filter& aFilter,
const Rect& aClipRect,
const nsIntRegion* aVisibleRegion)
{
NS_ASSERTION(aVisibleRegion, "Requires a visible region");
AutoLockCompositableHost lock(this);
if (lock.Failed()) {
return;
}
if (!mSource) {
return;
}
RefPtr<TexturedEffect> effect = CreateTexturedEffect(mSource.get(),
mSourceOnWhite.get(),
aFilter, true);
if (!effect) {
return;
}
aEffectChain.mPrimaryEffect = effect;
nsIntRegion tmpRegion;
const nsIntRegion* renderRegion;
if (PaintWillResample()) {
// If we're resampling, then the texture image will contain exactly the
// entire visible region's bounds, and we should draw it all in one quad
// to avoid unexpected aliasing.
tmpRegion = aVisibleRegion->GetBounds();
renderRegion = &tmpRegion;
} else {
renderRegion = aVisibleRegion;
}
nsIntRegion region(*renderRegion);
nsIntPoint origin = GetOriginOffset();
// translate into TexImage space, buffer origin might not be at texture (0,0)
region.MoveBy(-origin);
// Figure out the intersecting draw region
gfx::IntSize texSize = mSource->GetSize();
nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
textureRect.MoveBy(region.GetBounds().TopLeft());
nsIntRegion subregion;
subregion.And(region, textureRect);
if (subregion.IsEmpty()) {
// Region is empty, nothing to draw
return;
}
nsIntRegion screenRects;
nsIntRegion regionRects;
// Collect texture/screen coordinates for drawing
nsIntRegionRectIterator iter(subregion);
while (const nsIntRect* iterRect = iter.Next()) {
nsIntRect regionRect = *iterRect;
nsIntRect screenRect = regionRect;
screenRect.MoveBy(origin);
screenRects.Or(screenRects, screenRect);
regionRects.Or(regionRects, regionRect);
}
BigImageIterator* bigImgIter = mSource->AsBigImageIterator();
BigImageIterator* iterOnWhite = nullptr;
if (bigImgIter) {
bigImgIter->BeginBigImageIteration();
}
if (mSourceOnWhite) {
iterOnWhite = mSourceOnWhite->AsBigImageIterator();
MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
"Tile count mismatch on component alpha texture");
if (iterOnWhite) {
iterOnWhite->BeginBigImageIteration();
}
}
bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
do {
if (iterOnWhite) {
MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
"component alpha textures should be the same size.");
}
nsIntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
: nsIntRect(0, 0,
texSize.width,
texSize.height);
// Draw texture. If we're using tiles, we do repeating manually, as texture
// repeat would cause each individual tile to repeat instead of the
// compound texture as a whole. This involves drawing at most 4 sections,
// 2 for each axis that has texture repeat.
for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
nsIntRect currentTileRect(texRect);
currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
nsIntRegionRectIterator screenIter(screenRects);
nsIntRegionRectIterator regionIter(regionRects);
const nsIntRect* screenRect;
const nsIntRect* regionRect;
while ((screenRect = screenIter.Next()) &&
(regionRect = regionIter.Next())) {
nsIntRect tileScreenRect(*screenRect);
nsIntRect tileRegionRect(*regionRect);
// When we're using tiles, find the intersection between the tile
// rect and this region rect. Tiling is then handled by the
// outer for-loops and modifying the tile rect.
if (usingTiles) {
tileScreenRect.MoveBy(-origin);
tileScreenRect = tileScreenRect.Intersect(currentTileRect);
tileScreenRect.MoveBy(origin);
if (tileScreenRect.IsEmpty())
continue;
tileRegionRect = regionRect->Intersect(currentTileRect);
tileRegionRect.MoveBy(-currentTileRect.TopLeft());
}
gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
tileScreenRect.width, tileScreenRect.height);
effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
Float(tileRegionRect.y) / texRect.height,
Float(tileRegionRect.width) / texRect.width,
Float(tileRegionRect.height) / texRect.height);
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
if (usingTiles) {
DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
if (iterOnWhite) {
diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
}
GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
aTransform, mFlashCounter);
}
}
}
}
if (iterOnWhite) {
iterOnWhite->NextTile();
}
} while (usingTiles && bigImgIter->NextTile());
if (bigImgIter) {
bigImgIter->EndBigImageIteration();
}
if (iterOnWhite) {
iterOnWhite->EndBigImageIteration();
}
DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
if (iterOnWhite) {
diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
}
GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
aTransform, mFlashCounter);
}
void
ContentHostIncremental::FlushUpdateQueue()
{
@ -620,6 +439,20 @@ ContentHostIncremental::ProcessTextureUpdates()
mUpdateList.Clear();
}
TextureSource*
ContentHostIncremental::GetTextureSource()
{
MOZ_ASSERT(mLocked);
return mSource;
}
TextureSource*
ContentHostIncremental::GetTextureSourceOnWhite()
{
MOZ_ASSERT(mLocked);
return mSourceOnWhite;
}
void
ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost)
{
@ -850,32 +683,6 @@ ContentHostTexture::GetRenderState()
return result;
}
TemporaryRef<TexturedEffect>
ContentHostTexture::GenEffect(const gfx::Filter& aFilter)
{
if (!mTextureHost) {
return nullptr;
}
if (!mTextureHost->BindTextureSource(mTextureSource)) {
return nullptr;
}
if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
return nullptr;
}
return CreateTexturedEffect(mTextureSource.get(),
mTextureSourceOnWhite.get(),
aFilter, true);
}
TemporaryRef<TexturedEffect>
ContentHostIncremental::GenEffect(const gfx::Filter& aFilter)
{
if (!mSource) {
return nullptr;
}
return CreateTexturedEffect(mSource, mSourceOnWhite, aFilter, true);
}
#ifdef MOZ_DUMP_PAINTING
TemporaryRef<gfx::DataSourceSurface>
ContentHostTexture::GetAsSurface()

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

@ -96,6 +96,18 @@ public:
explicit ContentHostBase(const TextureInfo& aTextureInfo);
virtual ~ContentHostBase();
virtual void Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Filter& aFilter,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion = nullptr);
virtual TextureSource* GetTextureSource() = 0;
virtual TextureSource* GetTextureSourceOnWhite() = 0;
virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
protected:
virtual nsIntPoint GetOriginOffset()
{
@ -120,13 +132,6 @@ public:
, mLocked(false)
{ }
virtual void Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Filter& aFilter,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion = nullptr);
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
#ifdef MOZ_DUMP_PAINTING
@ -168,15 +173,23 @@ public:
mLocked = false;
}
LayerRenderState GetRenderState();
virtual TextureSource* GetTextureSource() MOZ_OVERRIDE {
MOZ_ASSERT(mLocked);
return mTextureHost->GetTextureSources();
}
virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE {
MOZ_ASSERT(mLocked);
if (mTextureHostOnWhite) {
return mTextureHostOnWhite->GetTextureSources();
}
return nullptr;
}
virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
LayerRenderState GetRenderState();
protected:
RefPtr<TextureHost> mTextureHost;
RefPtr<TextureHost> mTextureHostOnWhite;
CompositableTextureSourceRef mTextureSource;
CompositableTextureSourceRef mTextureSourceOnWhite;
bool mLocked;
};
@ -264,13 +277,6 @@ public:
return false;
}
virtual void Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Filter& aFilter,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion = nullptr);
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) MOZ_OVERRIDE;
virtual bool Lock() MOZ_OVERRIDE {
@ -285,8 +291,8 @@ public:
mLocked = false;
}
virtual TemporaryRef<TexturedEffect>
GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
virtual TextureSource* GetTextureSource() MOZ_OVERRIDE;
virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE;
private:

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

@ -37,21 +37,28 @@ ImageHost::ImageHost(const TextureInfo& aTextureInfo)
ImageHost::~ImageHost()
{
if (mFrontBuffer) {
mFrontBuffer->UnbindTextureSource();
mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
}
}
void
ImageHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
CompositableHost::SetCompositableBackendSpecificData(aBackendData);
// ImageHost allows TextureHost sharing among ImageHosts.
if (aBackendData) {
aBackendData->SetAllowSharingTextureHost(true);
}
}
void
ImageHost::UseTextureHost(TextureHost* aTexture)
{
if (mFrontBuffer && mFrontBuffer != aTexture) {
mFrontBuffer->UnbindTextureSource();
}
CompositableHost::UseTextureHost(aTexture);
mFrontBuffer = aTexture;
if (mFrontBuffer) {
mFrontBuffer->PrepareTextureSource(mTextureSource);
mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
}
mFrontBuffer = aTexture;
}
void
@ -59,8 +66,7 @@ ImageHost::RemoveTextureHost(TextureHost* aTexture)
{
CompositableHost::RemoveTextureHost(aTexture);
if (aTexture && mFrontBuffer == aTexture) {
mFrontBuffer->UnbindTextureSource();
mTextureSource = nullptr;
aTexture->SetCompositableBackendSpecificData(nullptr);
mFrontBuffer = nullptr;
}
}
@ -91,34 +97,25 @@ ImageHost::Composite(EffectChain& aEffectChain,
// Make sure the front buffer has a compositor
mFrontBuffer->SetCompositor(GetCompositor());
mFrontBuffer->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
AutoLockCompositableHost autoLock(this);
if (autoLock.Failed()) {
NS_WARNING("failed to lock front buffer");
return;
}
if (!mFrontBuffer->BindTextureSource(mTextureSource)) {
RefPtr<TextureSource> source = GetTextureSource();
if (!source) {
return;
}
if (!mTextureSource) {
// BindTextureSource above should have returned false!
MOZ_ASSERT(false);
return;
}
bool isAlphaPremultiplied = !(mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
RefPtr<TexturedEffect> effect = CreateTexturedEffect(mFrontBuffer->GetFormat(),
mTextureSource.get(),
aFilter,
isAlphaPremultiplied);
RefPtr<TexturedEffect> effect = GenEffect(aFilter);
if (!effect) {
return;
}
aEffectChain.mPrimaryEffect = effect;
IntSize textureSize = mTextureSource->GetSize();
IntSize textureSize = source->GetSize();
gfx::Rect gfxPictureRect
= mHasPictureRect ? gfx::Rect(0, 0, mPictureRect.width, mPictureRect.height)
: gfx::Rect(0, 0, textureSize.width, textureSize.height);
@ -126,7 +123,7 @@ ImageHost::Composite(EffectChain& aEffectChain,
gfx::Rect pictureRect(0, 0,
mPictureRect.width,
mPictureRect.height);
BigImageIterator* it = mTextureSource->AsBigImageIterator();
BigImageIterator* it = source->AsBigImageIterator();
if (it) {
// This iteration does not work if we have multiple texture sources here
@ -142,7 +139,7 @@ ImageHost::Composite(EffectChain& aEffectChain,
// the corresponding source tiles from all planes, with appropriate
// per-plane per-tile texture coords.
// DrawQuad currently assumes that all planes use the same texture coords.
MOZ_ASSERT(it->GetTileCount() == 1 || !mTextureSource->GetNextSibling(),
MOZ_ASSERT(it->GetTileCount() == 1 || !source->GetNextSibling(),
"Can't handle multi-plane BigImages");
it->BeginBigImageIteration();
@ -173,7 +170,7 @@ ImageHost::Composite(EffectChain& aEffectChain,
gfxPictureRect, aClipRect,
aTransform, mFlashCounter);
} else {
IntSize textureSize = mTextureSource->GetSize();
IntSize textureSize = source->GetSize();
gfx::Rect rect;
if (mHasPictureRect) {
effect->mTextureCoords = Rect(Float(mPictureRect.x) / textureSize.width,
@ -276,10 +273,18 @@ ImageHost::Unlock()
mLocked = false;
}
TemporaryRef<TextureSource>
ImageHost::GetTextureSource()
{
MOZ_ASSERT(mLocked);
return mFrontBuffer->GetTextureSources();
}
TemporaryRef<TexturedEffect>
ImageHost::GenEffect(const gfx::Filter& aFilter)
{
if (!mFrontBuffer->BindTextureSource(mTextureSource)) {
RefPtr<TextureSource> source = GetTextureSource();
if (!source) {
return nullptr;
}
bool isAlphaPremultiplied = true;
@ -287,7 +292,7 @@ ImageHost::GenEffect(const gfx::Filter& aFilter)
isAlphaPremultiplied = false;
return CreateTexturedEffect(mFrontBuffer->GetFormat(),
mTextureSource,
source,
aFilter,
isAlphaPremultiplied);
}

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

@ -45,6 +45,8 @@ public:
virtual CompositableType GetType() { return mTextureInfo.mCompositableType; }
virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
virtual void Composite(EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
@ -82,12 +84,13 @@ public:
virtual void Unlock() MOZ_OVERRIDE;
virtual TemporaryRef<TextureSource> GetTextureSource();
virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
protected:
RefPtr<TextureHost> mFrontBuffer;
CompositableTextureSourceRef mTextureSource;
nsIntRect mPictureRect;
bool mHasPictureRect;
bool mLocked;

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

@ -144,13 +144,6 @@ TextureHost::GetIPDLActor()
return mActor;
}
bool
TextureHost::BindTextureSource(CompositableTextureSourceRef& texture)
{
texture = GetTextureSources();
return !!texture;
}
FenceHandle
TextureHost::GetAndResetReleaseFenceHandle()
{
@ -284,6 +277,18 @@ TextureHost::CompositorRecycle()
static_cast<TextureParent*>(mActor)->CompositorRecycle();
}
void
TextureHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
mCompositableBackendData = aBackendData;
}
void
TextureHost::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
mCompositableBackendData = nullptr;
}
TextureHost::TextureHost(TextureFlags aFlags)
: mActor(nullptr)
, mFlags(aFlags)
@ -317,11 +322,9 @@ TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
}
TextureSource::TextureSource()
: mCompositableCount(0)
{
MOZ_COUNT_CTOR(TextureSource);
}
TextureSource::~TextureSource()
{
MOZ_COUNT_DTOR(TextureSource);
@ -841,9 +844,8 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor
GLenum target = surf->ConsTextureTarget();
GLuint tex = surf->ConsTexture(gl);
texSource = new GLTextureSource(compositorOGL, tex, target,
surf->mSize, format,
true/*externally owned*/);
texSource = new GLTextureSource(compositorOGL, tex, format, target,
surf->mSize);
break;
}
case gl::SharedSurfaceType::EGLImageShare: {
@ -858,9 +860,8 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor
GLuint tex = 0;
surf->AcquireConsumerTexture(gl, &tex, &target);
texSource = new GLTextureSource(compositorOGL, tex, target,
surf->mSize, format,
true/*externally owned*/);
texSource = new GLTextureSource(compositorOGL, tex, format, target,
surf->mSize);
break;
}
#ifdef XP_MACOSX

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

@ -46,6 +46,7 @@ namespace layers {
class Compositor;
class CompositableHost;
class CompositableBackendSpecificData;
class CompositableParentManager;
class SurfaceDescriptor;
class SharedSurfaceDescriptor;
@ -149,69 +150,10 @@ public:
return nullptr;
}
void AddCompositableRef() { ++mCompositableCount; }
void ReleaseCompositableRef() {
--mCompositableCount;
MOZ_ASSERT(mCompositableCount >= 0);
}
int NumCompositableRefs() const { return mCompositableCount; }
protected:
virtual ~TextureSource();
RefPtr<TextureSource> mNextSibling;
int mCompositableCount;
};
/**
* equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
* ReleaseCompositableRef in addition to the usual AddRef and Release.
*/
class CompositableTextureSourceRef {
public:
CompositableTextureSourceRef() {}
~CompositableTextureSourceRef()
{
if (mRef) {
mRef->ReleaseCompositableRef();
}
}
CompositableTextureSourceRef& operator=(const TemporaryRef<TextureSource>& aOther)
{
RefPtr<TextureSource> temp = aOther;
if (temp) {
temp->AddCompositableRef();
}
if (mRef) {
mRef->ReleaseCompositableRef();
}
mRef = temp;
return *this;
}
CompositableTextureSourceRef& operator=(TextureSource* aOther)
{
if (aOther) {
aOther->AddCompositableRef();
}
if (mRef) {
mRef->ReleaseCompositableRef();
}
mRef = aOther;
return *this;
}
TextureSource* get() const { return mRef; }
operator TextureSource*() const { return mRef; }
TextureSource* operator->() const { return mRef; }
TextureSource& operator*() const { return *mRef; }
private:
RefPtr<TextureSource> mRef;
};
/**
@ -360,25 +302,6 @@ public:
*/
virtual TextureSource* GetTextureSources() = 0;
/**
* Called during the transaction. The TextureSource may or may not be composited.
*
* Note that this is called outside of lock/unlock.
*/
virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
/**
* Called at composition time, just before compositing the TextureSource composited.
*
* Note that this is called only withing lock/unlock.
*/
virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture);
/**
* Called when another TextureHost will take over.
*/
virtual void UnbindTextureSource() {}
/**
* Is called before compositing if the shared data has changed since last
* composition.
@ -483,6 +406,10 @@ public:
return LayerRenderState();
}
virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData);
virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData);
// If a texture host holds a reference to shmem, it should override this method
// to forget about the shmem _without_ releasing it.
virtual void OnShutdown() {}
@ -508,6 +435,7 @@ public:
protected:
PTextureParent* mActor;
TextureFlags mFlags;
RefPtr<CompositableBackendSpecificData> mCompositableBackendData;
friend class TextureParent;
};

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

@ -240,6 +240,7 @@ UNIFIED_SOURCES += [
'apz/src/Axis.cpp',
'apz/src/GestureEventListener.cpp',
'apz/src/InputBlockState.cpp',
'apz/src/InputQueue.cpp',
'apz/src/OverscrollHandoffState.cpp',
'apz/src/TaskThrottler.cpp',
'apz/testutil/APZTestData.cpp',

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

@ -136,6 +136,17 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
gl()->fActiveTexture(aTextureUnit);
gl()->fBindTexture(textureTarget, tex);
if (mTextureBackendSpecificData) {
// There are two paths for locking/unlocking - if mTextureBackendSpecificData is
// set, we use the texture on there, otherwise we use
// CompositorBackendSpecificData from the compositor and bind the EGLImage
// only in Lock().
if (!mEGLImage) {
mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
}
BindEGLImage();
}
ApplyFilterToBoundTexture(gl(), aFilter, textureTarget);
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
@ -148,6 +159,10 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
bool GrallocTextureSourceOGL::Lock()
{
if (mTextureBackendSpecificData) {
return true;
}
MOZ_ASSERT(IsValid());
if (!IsValid()) {
return false;
@ -173,7 +188,7 @@ bool GrallocTextureSourceOGL::Lock()
bool
GrallocTextureSourceOGL::IsValid() const
{
return !!gl() && !!mGraphicBuffer.get() && !!mCompositor;
return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mTextureBackendSpecificData);
}
gl::GLContext*
@ -215,6 +230,62 @@ GrallocTextureSourceOGL::GetTextureTarget() const
return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
}
void
GrallocTextureSourceOGL::SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData)
{
if (!aBackendData) {
DeallocateDeviceData();
// Update mTextureBackendSpecificData after calling DeallocateDeviceData().
mTextureBackendSpecificData = nullptr;
return;
}
if (mTextureBackendSpecificData != aBackendData) {
mNeedsReset = true;
}
if (!gl() || !gl()->MakeCurrent()) {
NS_WARNING("Failed to make the context current");
return;
}
if (!mNeedsReset) {
// Update binding to the EGLImage
GLuint tex = GetGLTexture();
GLuint textureTarget = GetTextureTarget();
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(textureTarget, tex);
BindEGLImage();
return;
}
if (!mCompositor) {
mTextureBackendSpecificData = aBackendData;
return;
}
// delete old EGLImage
DeallocateDeviceData();
// Update mTextureBackendSpecificData after calling DeallocateDeviceData().
mTextureBackendSpecificData = aBackendData;
GLuint tex = GetGLTexture();
GLuint textureTarget = GetTextureTarget();
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(textureTarget, tex);
// Setup texure parameters at the first binding.
gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, GetWrapMode());
gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, GetWrapMode());
// create new EGLImage
mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
BindEGLImage();
mNeedsReset = false;
}
gfx::IntSize
GrallocTextureSourceOGL::GetSize() const
{
@ -233,6 +304,9 @@ GrallocTextureSourceOGL::DeallocateDeviceData()
if (!gl() || !gl()->MakeCurrent()) {
return;
}
if (mTextureBackendSpecificData) {
mTextureBackendSpecificData->ClearBoundEGLImage(mEGLImage);
}
EGLImageDestroy(gl(), mEGLImage);
mEGLImage = EGL_NO_IMAGE;
}
@ -241,48 +315,49 @@ GrallocTextureSourceOGL::DeallocateDeviceData()
GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags,
const NewSurfaceDescriptorGralloc& aDescriptor)
: TextureHost(aFlags)
, mGrallocHandle(aDescriptor)
, mSize(0, 0)
, mDescriptorSize(aDescriptor.size())
, mFormat(gfx::SurfaceFormat::UNKNOWN)
, mEGLImage(EGL_NO_IMAGE)
{
gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
mGrallocHandle = aDescriptor;
android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
MOZ_ASSERT(graphicBuffer);
mSize = aDescriptor.size();
if (graphicBuffer) {
mFormat =
format =
SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
aFlags & TextureFlags::RB_SWAPPED);
mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
mTextureSource = new GrallocTextureSourceOGL(nullptr,
this,
graphicBuffer,
format);
} else {
printf_stderr("gralloc buffer is nullptr");
}
}
GrallocTextureHostOGL::~GrallocTextureHostOGL()
{}
{
MOZ_ASSERT(!mTextureSource || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
"Leaking our buffer");
}
void
GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
{
mCompositor = static_cast<CompositorOGL*>(aCompositor);
if (mTilingTextureSource) {
mTilingTextureSource->SetCompositor(mCompositor);
}
if (mGLTextureSource) {
mGLTextureSource->SetCompositor(mCompositor);
}
if (mCompositor && aCompositor != mCompositor) {
DestroyEGLImage();
if (mTextureSource) {
mTextureSource->SetCompositor(static_cast<CompositorOGL*>(aCompositor));
}
}
bool
GrallocTextureHostOGL::Lock()
{
return IsValid();
if (IsValid()) {
mTextureSource->Lock();
return true;
}
return false;
}
void
@ -294,29 +369,28 @@ GrallocTextureHostOGL::Unlock()
bool
GrallocTextureHostOGL::IsValid() const
{
android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
return graphicBuffer != nullptr;
if (!mTextureSource) {
return false;
}
return mTextureSource->IsValid();
}
gfx::SurfaceFormat
GrallocTextureHostOGL::GetFormat() const
{
return mFormat;
if (!mTextureSource) {
return gfx::SurfaceFormat::UNKNOWN;
}
return mTextureSource->GetFormat();
}
void
GrallocTextureHostOGL::DeallocateSharedData()
{
if (mTilingTextureSource) {
mTilingTextureSource->ForgetBuffer();
mTilingTextureSource = nullptr;
if (mTextureSource) {
mTextureSource->ForgetBuffer();
mTextureSource = nullptr;
}
if (mGLTextureSource) {
mGLTextureSource = nullptr;
}
DestroyEGLImage();
if (mGrallocHandle.buffer().type() != SurfaceDescriptor::Tnull_t) {
MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer();
base::ProcessId owner;
@ -334,33 +408,24 @@ GrallocTextureHostOGL::DeallocateSharedData()
void
GrallocTextureHostOGL::ForgetSharedData()
{
if (mTilingTextureSource) {
mTilingTextureSource->ForgetBuffer();
mTilingTextureSource = nullptr;
}
if (mGLTextureSource) {
mGLTextureSource = nullptr;
if (mTextureSource) {
mTextureSource->ForgetBuffer();
mTextureSource = nullptr;
}
}
void
GrallocTextureHostOGL::DeallocateDeviceData()
{
if (mTilingTextureSource) {
mTilingTextureSource->DeallocateDeviceData();
if (mTextureSource) {
mTextureSource->DeallocateDeviceData();
}
if (mGLTextureSource) {
mGLTextureSource = nullptr;
}
DestroyEGLImage();
}
LayerRenderState
GrallocTextureHostOGL::GetRenderState()
{
android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
if (graphicBuffer) {
if (IsValid()) {
LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT;
if (mFlags & TextureFlags::NEEDS_Y_FLIP) {
flags |= LayerRenderStateFlags::Y_FLIPPED;
@ -368,8 +433,8 @@ GrallocTextureHostOGL::GetRenderState()
if (mFlags & TextureFlags::RB_SWAPPED) {
flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
}
return LayerRenderState(graphicBuffer,
gfx::ThebesIntSize(mDescriptorSize),
return LayerRenderState(mTextureSource->mGraphicBuffer.get(),
gfx::ThebesIntSize(mSize),
flags,
this);
}
@ -379,8 +444,8 @@ GrallocTextureHostOGL::GetRenderState()
TemporaryRef<gfx::DataSourceSurface>
GrallocTextureHostOGL::GetAsSurface() {
return mTilingTextureSource ? mTilingTextureSource->GetAsSurface()
: nullptr;
return mTextureSource ? mTextureSource->GetAsSurface()
: nullptr;
}
TemporaryRef<gfx::DataSourceSurface>
@ -408,186 +473,103 @@ GrallocTextureSourceOGL::GetAsSurface() {
GLuint
GrallocTextureSourceOGL::GetGLTexture()
{
if (mTextureBackendSpecificData) {
mTextureBackendSpecificData->SetCompositor(mCompositor);
return mTextureBackendSpecificData->GetTexture();
}
return mTexture;
}
void
GrallocTextureSourceOGL::BindEGLImage()
{
gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage);
if (mTextureBackendSpecificData) {
mTextureBackendSpecificData->BindEGLImage(GetTextureTarget(), mEGLImage);
} else {
gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage);
}
}
TextureSource*
GrallocTextureHostOGL::GetTextureSources()
void
GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
// This is now only used with tiled layers, and will eventually be removed.
// Other layer types use BindTextureSource instead.
MOZ_ASSERT(!mGLTextureSource);
if (!mTilingTextureSource) {
android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
MOZ_ASSERT(graphicBuffer);
if (!graphicBuffer) {
return nullptr;
if(!aBackendData) {
return;
}
// Update mTextureBackendSpecificData if it is not set yet.
if (!mTextureBackendSpecificData) {
MOZ_ASSERT(!mCompositableBackendData);
mCompositableBackendData = aBackendData;
CompositableDataGonkOGL* backend = static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get());
mTextureBackendSpecificData = backend->GetTextureBackendSpecificData();
}
// If TextureHost sharing by multiple CompositableHosts are detected,
// enable mBackendDatas usage.
if (!mBackendDatas &&
mCompositableBackendData &&
mCompositableBackendData != aBackendData &&
mTextureBackendSpecificData->IsAllowingSharingTextureHost())
{
mBackendDatas = MakeUnique<std::map<uint64_t, RefPtr<CompositableBackendSpecificData> > >();
(*mBackendDatas)[mCompositableBackendData->GetId()] = mCompositableBackendData;
mCompositableBackendData = nullptr;
// Get new mTextureBackendSpecificData
mTextureBackendSpecificData =
mTextureBackendSpecificData->GetNewTextureBackendSpecificData(mTextureSource->GetEGLImage());
mTextureBackendSpecificData->SetOwnedByTextureHost();
}
// Update mCompositableBackendData.
if (mBackendDatas)
{
// Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL.
MOZ_ASSERT(aBackendData->IsAllowingSharingTextureHost());
(*mBackendDatas)[aBackendData->GetId()] = aBackendData;
if (mBackendDatas->size() > 200) {
NS_WARNING("Too many CompositableBackends");
}
mTilingTextureSource = new GrallocTextureSourceOGL(mCompositor, this,
graphicBuffer, mFormat);
}
mTilingTextureSource->Lock();
return mTilingTextureSource;
}
void
GrallocTextureHostOGL::UnbindTextureSource()
{
// Clear the reference to the TextureSource (if any), because we know that
// another TextureHost is being bound to the TextureSource. This means that
// we will have to re-do gl->fEGLImageTargetTexture2D next time we go through
// BindTextureSource (otherwise we would have skipped it).
// Note that this doesn't "unlock" the gralloc buffer or force it to be
// detached, Although decreasing the refcount of the TextureSource may lead
// to the gl handle being destroyed, which would unlock the gralloc buffer.
// That said, this method is called before another TextureHost attaches to the
// TextureSource, which has the effect of unlocking the gralloc buffer. So when
// this is called we know we are going to be unlocked soon.
mGLTextureSource = nullptr;
}
GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) {
MOZ_ASSERT(aGL);
if (aGL->Renderer() == gl::GLRenderer::SGX530 ||
aGL->Renderer() == gl::GLRenderer::SGX540) {
// SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will
// result in black pixels when trying to draw from bound textures.
// Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on
// performance.
// See Bug 950050.
return LOCAL_GL_TEXTURE_EXTERNAL;
} else {
return TextureTargetForAndroidPixelFormat(aFormat);
// Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL.
mCompositableBackendData = aBackendData;
CompositableDataGonkOGL* backend = static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get());
mTextureBackendSpecificData = backend->GetTextureBackendSpecificData();
}
if (mTextureSource) {
mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData);
}
}
void
GrallocTextureHostOGL::DestroyEGLImage()
GrallocTextureHostOGL::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
{
// Only called when we want to get rid of the gralloc buffer, usually
// around the end of life of the TextureHost.
if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) {
EGLImageDestroy(GetGLContext(), mEGLImage);
mEGLImage = EGL_NO_IMAGE;
}
}
void
GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource)
{
// This happens during the layers transaction.
// All of the gralloc magic goes here. The only thing that happens externally
// and that is good to keep in mind is that when the TextureSource is deleted,
// it destroys its gl texture handle which is important for genlock.
// If this TextureHost's mGLTextureSource member is non-null, it means we are
// still bound to the TextureSource, in which case we can skip the driver
// overhead of binding the texture again (fEGLImageTargetTexture2D)
// As a result, if the TextureHost is used with several CompositableHosts,
// it will be bound to only one TextureSource, and we'll do the driver work
// only once, which is great. This means that all of the compositables that
// use this TextureHost will keep a reference to this TextureSource at least
// for the duration of this frame.
// If the compositable already has a TextureSource (the aTextureSource parameter),
// that is compatible and is not in use by several compositable, we try to
// attach to it. This has the effect of unlocking the previous TextureHost that
// we attached to the TextureSource (the previous frame)
// If the TextureSource used by the compositable is also used by other
// compositables (see NumCompositableRefs), we have to create a new TextureSource,
// because otherwise we would be modifying the content of every layer that uses
// the TextureSource in question, even thoug they don't use this TextureHost.
MOZ_ASSERT(!mTilingTextureSource);
android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
MOZ_ASSERT(graphicBuffer);
if (!graphicBuffer) {
mGLTextureSource = nullptr;
if(!aBackendData ||
!mTextureBackendSpecificData) {
return;
}
if (mGLTextureSource && !mGLTextureSource->IsValid()) {
mGLTextureSource = nullptr;
}
if (mGLTextureSource) {
// We are already attached to a TextureSource, nothing to do except tell
// the compositable to use it.
aTextureSource = mGLTextureSource.get();
return;
}
gl::GLContext* gl = GetGLContext();
if (!gl || !gl->MakeCurrent()) {
mGLTextureSource = nullptr;
return;
}
if (mEGLImage == EGL_NO_IMAGE) {
// Should only happen the first time.
mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer());
}
GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat());
GLTextureSource* glSource = aTextureSource.get() ?
aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr;
bool shouldCreateTextureSource = !glSource || !glSource->IsValid()
|| glSource->NumCompositableRefs() > 1
|| glSource->GetTextureTarget() != textureTarget;
if (shouldCreateTextureSource) {
GLuint textureHandle;
gl->fGenTextures(1, &textureHandle);
gl->fBindTexture(textureTarget, textureHandle);
gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget,
mSize, mFormat);
aTextureSource = mGLTextureSource.get();
if (mBackendDatas)
{
// Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL.
mBackendDatas->erase(aBackendData->GetId());
if (mBackendDatas->size() == 0) {
mCompositableBackendData = nullptr;
mTextureBackendSpecificData = nullptr;
}
} else {
gl->fBindTexture(textureTarget, glSource->GetTextureHandle());
gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
glSource->SetSize(mSize);
glSource->SetFormat(mFormat);
mGLTextureSource = glSource;
}
}
bool
GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource)
{
// This happens at composition time.
// If mGLTextureSource is null it means PrepareTextureSource failed.
if (!mGLTextureSource) {
return false;
// Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL.
mCompositableBackendData = nullptr;
mTextureBackendSpecificData = nullptr;
}
// If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource,
// otherwise it means something has fiddled with the TextureSource between Prepare and
// now.
MOZ_ASSERT(mGLTextureSource == aTextureSource);
aTextureSource = mGLTextureSource.get();
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
// Wait until it's ready.
WaitAcquireFenceSyncComplete();
#endif
return true;
if (mTextureSource) {
mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData);
}
}
} // namepsace layers

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

@ -17,7 +17,6 @@ namespace layers {
class GrallocTextureHostOGL;
// Progressively getting replaced by GLTextureSource
class GrallocTextureSourceOGL : public TextureSource
, public TextureSourceOGL
{
@ -48,6 +47,8 @@ public:
return LOCAL_GL_CLAMP_TO_EDGE;
}
virtual void SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData);
void DeallocateDeviceData();
gl::GLContext* gl() const;
@ -74,6 +75,7 @@ public:
bool Lock();
protected:
RefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
RefPtr<CompositorOGL> mCompositor;
GrallocTextureHostOGL* mTextureHost;
android::sp<android::GraphicBuffer> mGraphicBuffer;
@ -111,17 +113,14 @@ public:
virtual gfx::SurfaceFormat GetFormat() const;
virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mDescriptorSize; }
virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE;
virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE;
virtual void UnbindTextureSource() MOZ_OVERRIDE;
virtual TextureSource* GetTextureSources() MOZ_OVERRIDE;
virtual TextureSource* GetTextureSources() MOZ_OVERRIDE
{
return mTextureSource;
}
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
virtual TextureHostOGL* AsHostOGL() MOZ_OVERRIDE
@ -132,27 +131,21 @@ public:
virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
bool IsValid() const;
virtual const char* Name() MOZ_OVERRIDE { return "GrallocTextureHostOGL"; }
gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; }
private:
void DestroyEGLImage();
NewSurfaceDescriptorGralloc mGrallocHandle;
RefPtr<GLTextureSource> mGLTextureSource;
RefPtr<CompositorOGL> mCompositor;
// only used for tiling, will be removed.
RefPtr<GrallocTextureSourceOGL> mTilingTextureSource;
// Size reported by the GraphicBuffer
gfx::IntSize mSize;
// Size reported by TextureClient, can be different in some cases (video?),
// used by LayerRenderState.
gfx::IntSize mDescriptorSize;
gfx::SurfaceFormat mFormat;
EGLImage mEGLImage;
RefPtr<GrallocTextureSourceOGL> mTextureSource;
gfx::IntSize mSize; // See comment in textureClientOGL.h
RefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
UniquePtr<std::map<uint64_t, RefPtr<CompositableBackendSpecificData> > > mBackendDatas;
};
} // namespace layers

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

@ -40,6 +40,16 @@ namespace layers {
class Compositor;
TemporaryRef<CompositableBackendSpecificData>
CreateCompositableBackendSpecificDataOGL()
{
#ifdef MOZ_WIDGET_GONK
return new CompositableDataGonkOGL();
#else
return nullptr;
#endif
}
TemporaryRef<TextureHost>
CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
ISurfaceAllocator* aDeallocator,
@ -109,6 +119,174 @@ FlagsToGLFlags(TextureFlags aFlags)
return static_cast<gl::TextureImage::Flags>(result);
}
CompositableDataGonkOGL::CompositableDataGonkOGL()
{
}
CompositableDataGonkOGL::~CompositableDataGonkOGL()
{
ClearData();
}
void
CompositableDataGonkOGL::ClearData()
{
CompositableBackendSpecificData::ClearData();
mTextureBackendSpecificData = nullptr;
mCompositor = nullptr;
}
void
CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor)
{
mCompositor = static_cast<CompositorOGL*>(aCompositor);
if (mTextureBackendSpecificData) {
mTextureBackendSpecificData->SetCompositor(aCompositor);
}
}
TextureSharedDataGonkOGL*
CompositableDataGonkOGL::GetTextureBackendSpecificData()
{
if (!mTextureBackendSpecificData) {
mTextureBackendSpecificData = new TextureSharedDataGonkOGL();
mTextureBackendSpecificData->SetCompositor(mCompositor);
mTextureBackendSpecificData->SetAllowSharingTextureHost(IsAllowingSharingTextureHost());
}
return mTextureBackendSpecificData;
}
TextureSharedDataGonkOGL::TextureSharedDataGonkOGL()
: mOwnedByCompositableHost(true)
, mAllowSharingTextureHost(false)
, mTexture(0)
, mBoundEGLImage(EGL_NO_IMAGE)
{
}
TextureSharedDataGonkOGL::TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor)
: mOwnedByCompositableHost(true)
, mAllowSharingTextureHost(false)
, mCompositor(aCompositor)
, mTexture(aTexture)
, mBoundEGLImage(aImage)
{
}
TextureSharedDataGonkOGL::~TextureSharedDataGonkOGL()
{
DeleteTextureIfPresent();
}
gl::GLContext*
TextureSharedDataGonkOGL::gl() const
{
return mCompositor ? mCompositor->gl() : nullptr;
}
void
TextureSharedDataGonkOGL::SetCompositor(Compositor* aCompositor)
{
if (gl() && mCompositor != aCompositor) {
DeleteTextureIfPresent();
}
mCompositor = static_cast<CompositorOGL*>(aCompositor);
}
void
TextureSharedDataGonkOGL::ClearData()
{
DeleteTextureIfPresent();
}
TemporaryRef<TextureSharedDataGonkOGL>
TextureSharedDataGonkOGL::GetNewTextureBackendSpecificData(EGLImage aImage)
{
MOZ_ASSERT(IsAllowingSharingTextureHost());
if (IsEGLImageBound(aImage))
{
// If EGLImage is already bound to OpenGL Texture,
// handover the OpenGL Texture to caller
GLuint textureId = GetAndResetGLTextureOwnership();
RefPtr<TextureSharedDataGonkOGL> data = new TextureSharedDataGonkOGL(textureId, aImage, mCompositor);
data->SetCompositor(mCompositor);
data->SetAllowSharingTextureHost(true);
return data;
}
// Create brand new TextureSharedDataGonkOGL
RefPtr<TextureSharedDataGonkOGL> data = new TextureSharedDataGonkOGL();
data->SetCompositor(mCompositor);
data->SetAllowSharingTextureHost(true);
return data;
}
GLuint
TextureSharedDataGonkOGL::GetTexture()
{
if (!mTexture) {
if (gl() && gl()->MakeCurrent()) {
gl()->fGenTextures(1, &mTexture);
}
}
return mTexture;
}
GLuint
TextureSharedDataGonkOGL::GetAndResetGLTextureOwnership()
{
GLuint texture = mTexture;
mTexture = 0;
mBoundEGLImage = EGL_NO_IMAGE;
return texture;
}
void
TextureSharedDataGonkOGL::DeleteTextureIfPresent()
{
if (mTexture) {
MOZ_ASSERT(mCompositor);
if (gl() && gl()->MakeCurrent()) {
gl()->fDeleteTextures(1, &mTexture);
}
mTexture = 0;
mBoundEGLImage = EGL_NO_IMAGE;
}
}
void
TextureSharedDataGonkOGL::BindEGLImage(GLuint aTarget, EGLImage aImage)
{
if (mBoundEGLImage != aImage) {
MOZ_ASSERT(gl());
if (gl()) {
gl()->fEGLImageTargetTexture2D(aTarget, aImage);
}
mBoundEGLImage = aImage;
}
}
void
TextureSharedDataGonkOGL::ClearBoundEGLImage(EGLImage aImage)
{
if (mBoundEGLImage == aImage) {
DeleteTextureIfPresent();
mBoundEGLImage = EGL_NO_IMAGE;
}
}
bool
TextureSharedDataGonkOGL::IsEGLImageBound(EGLImage aImage)
{
if (mTexture != 0 &&
aImage != EGL_NO_IMAGE &&
aImage == mBoundEGLImage) {
return true;
}
return false;
}
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
bool
TextureHostOGL::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
@ -351,56 +529,27 @@ TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilt
// GLTextureSource
GLTextureSource::GLTextureSource(CompositorOGL* aCompositor,
GLuint aTextureHandle,
GLenum aTarget,
gfx::IntSize aSize,
GLuint aTex,
gfx::SurfaceFormat aFormat,
bool aExternallyOwned)
: mCompositor(aCompositor)
, mTextureHandle(aTextureHandle)
, mTextureTarget(aTarget)
, mSize(aSize)
GLenum aTarget,
gfx::IntSize aSize)
: mSize(aSize)
, mCompositor(aCompositor)
, mTex(aTex)
, mFormat(aFormat)
, mExternallyOwned(aExternallyOwned)
, mTextureTarget(aTarget)
{
MOZ_COUNT_CTOR(GLTextureSource);
}
GLTextureSource::~GLTextureSource()
{
MOZ_COUNT_DTOR(GLTextureSource);
if (!mExternallyOwned) {
DeleteTextureHandle();
}
}
void
GLTextureSource::DeallocateDeviceData()
{
if (!mExternallyOwned) {
DeleteTextureHandle();
}
}
void
GLTextureSource::DeleteTextureHandle()
{
if (mTextureHandle != 0 && gl() && gl()->MakeCurrent()) {
gl()->fDeleteTextures(1, &mTextureHandle);
}
mTextureHandle = 0;
}
void
GLTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
{
MOZ_ASSERT(gl());
MOZ_ASSERT(mTextureHandle != 0);
if (!gl()) {
NS_WARNING("Trying to bind a texture without a GLContext");
return;
}
gl()->fActiveTexture(aTextureUnit);
gl()->fBindTexture(mTextureTarget, mTextureHandle);
gl()->fBindTexture(mTextureTarget, mTex);
ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget);
}
@ -413,7 +562,7 @@ GLTextureSource::SetCompositor(Compositor* aCompositor)
bool
GLTextureSource::IsValid() const
{
return !!gl() && mTextureHandle != 0;
return !!gl();
}
gl::GLContext*
@ -465,10 +614,6 @@ SurfaceTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
void
SurfaceTextureSource::SetCompositor(Compositor* aCompositor)
{
if (mCompositor != aCompositor) {
DeallocateDeviceData();
}
mCompositor = static_cast<CompositorOGL*>(aCompositor);
}

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

@ -58,7 +58,99 @@ class Compositor;
class CompositorOGL;
class TextureImageTextureSourceOGL;
class TextureSharedDataGonkOGL;
class GLTextureSource;
/**
* CompositableBackendSpecificData implementation for the Gonk OpenGL backend.
* Share a same texture between TextureHosts in the same CompositableHost.
* By shareing the texture among the TextureHosts, number of texture allocations
* can be reduced than texture allocation in every TextureHosts.
* From Bug 912134, use only one texture among all TextureHosts degrade
* the rendering performance.
* CompositableDataGonkOGL chooses in a middile of them.
*/
class CompositableDataGonkOGL : public CompositableBackendSpecificData
{
protected:
virtual ~CompositableDataGonkOGL();
public:
CompositableDataGonkOGL();
virtual void ClearData() MOZ_OVERRIDE;
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
TextureSharedDataGonkOGL* GetTextureBackendSpecificData();
protected:
nsRefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
RefPtr<CompositorOGL> mCompositor;
};
/**
* Manage actual shared resources of CompositableDataGonkOGL.
* The resources are split from CompositableDataGonkOGL to handle two use cases.
* Normally TextureHost is used from one CompositableHost at the same time.
* In this case, performance is good if the resources are owned by CompositableDataGonkOGL.
* But TextureHost could be shared among multiple ImageHosts.
* If it happens, performance is good if the resource is owned by TextureHost.
* The resources ownership is carryed over from CompositableDataGonkOGL to TextureHost.
* See Bug 1017351.
*/
class TextureSharedDataGonkOGL
{
protected:
virtual ~TextureSharedDataGonkOGL();
public:
NS_INLINE_DECL_REFCOUNTING(TextureSharedDataGonkOGL)
TextureSharedDataGonkOGL();
TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor);
void SetCompositor(Compositor* aCompositor);
void ClearData();
// Mark TextureSharedDataGonkOGL as owned by TextureHost.
void SetOwnedByTextureHost()
{
mOwnedByCompositableHost = false;
}
// Check if this is owned by CompositableHost or TextureHost.
bool IsOwnedByCompositableHost()
{
return mOwnedByCompositableHost;
}
bool IsAllowingSharingTextureHost()
{
return mAllowSharingTextureHost;
}
void SetAllowSharingTextureHost(bool aAllow)
{
mAllowSharingTextureHost = aAllow;
}
// Create new TextureSharedDataGonkOGL.
// If aImage is already bound to OpenGL texture, the OpenGL textre is carried over
// to a new object. It could reduce calling fEGLImageTargetTexture2D()
// during resources ownership carry over from CompositableHost to TextureHost.
TemporaryRef<TextureSharedDataGonkOGL> GetNewTextureBackendSpecificData(EGLImage aImage);
GLuint GetTexture();
void DeleteTextureIfPresent();
gl::GLContext* gl() const;
void BindEGLImage(GLuint aTarget, EGLImage aImage);
void ClearBoundEGLImage(EGLImage aImage);
bool IsEGLImageBound(EGLImage aImage);
protected:
GLuint GetAndResetGLTextureOwnership();
bool mOwnedByCompositableHost;
bool mAllowSharingTextureHost;
RefPtr<CompositorOGL> mCompositor;
GLuint mTexture;
EGLImage mBoundEGLImage;
};
inline void ApplyFilterToBoundTexture(gl::GLContext* aGL,
gfx::Filter aFilter,
@ -113,8 +205,6 @@ public:
virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; }
virtual GLTextureSource* AsGLTextureSource() { return nullptr; }
void SetFilter(gl::GLContext* aGL, gfx::Filter aFilter)
{
if (mHasCachedFilter &&
@ -283,15 +373,10 @@ class GLTextureSource : public TextureSource
{
public:
GLTextureSource(CompositorOGL* aCompositor,
GLuint aTextureHandle,
GLenum aTarget,
gfx::IntSize aSize,
GLuint aTex,
gfx::SurfaceFormat aFormat,
bool aExternallyOwned = false);
~GLTextureSource();
virtual GLTextureSource* AsGLTextureSource() MOZ_OVERRIDE { return this; }
GLenum aTarget,
gfx::IntSize aSize);
virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
@ -307,29 +392,18 @@ public:
virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; }
virtual void DeallocateDeviceData() MOZ_OVERRIDE;
virtual void DeallocateDeviceData() MOZ_OVERRIDE {}
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
void SetSize(gfx::IntSize aSize) { mSize = aSize; }
void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; }
GLuint GetTextureHandle() const { return mTextureHandle; }
gl::GLContext* gl() const;
protected:
void DeleteTextureHandle();
const gfx::IntSize mSize;
RefPtr<CompositorOGL> mCompositor;
GLuint mTextureHandle;
GLenum mTextureTarget;
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
// If the texture is externally owned, the gl handle will not be deleted
// in the destructor.
bool mExternallyOwned;
const GLuint mTex;
const gfx::SurfaceFormat mFormat;
const GLenum mTextureTarget;
};
////////////////////////////////////////////////////////////////////////

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

@ -28,10 +28,10 @@ class nsIAtom;
class nsIWidget;
// IID for the nsITheme interface
// {48f64fde-ff0e-4989-bbe3-f746573f9b7c}
// {cc3a6c72-50c2-414d-b9f2-b778a5e0f136}
#define NS_ITHEME_IID \
{ 0x48f64fde, 0xff0e, 0x4989, \
{ 0xbb, 0xe3, 0xf7, 0x46, 0x57, 0x3f, 0x9b, 0x7c } }
{ 0xcc3a6c72, 0x50c2, 0x414d, \
{ 0xb9, 0xf2, 0xb7, 0x78, 0xa5, 0xe0, 0xf1, 0x36 } }
// {0ae05515-cf7a-45a8-9e02-6556de7685b1}
#define NS_THEMERENDERER_CID \
{ 0x0ae05515, 0xcf7a, 0x45a8, \
@ -143,6 +143,10 @@ public:
virtual bool NeedToClearBackgroundBehindWidget(uint8_t aWidgetType)
{ return false; }
virtual bool WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame,
uint8_t aWidgetType, nscolor* aColor)
{ return false; }
/**
* Can the nsITheme implementation handle this widget?
*/

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

@ -62,7 +62,7 @@ public:
MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&, uint64_t));
MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
@ -119,14 +119,33 @@ private:
nsTArray<Task*> mTaskQueue;
};
class TestAPZCTreeManager : public APZCTreeManager {
public:
nsRefPtr<InputQueue> GetInputQueue() const {
return mInputQueue;
}
};
class TestAsyncPanZoomController : public AsyncPanZoomController {
public:
TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc,
APZCTreeManager* aTreeManager = nullptr,
TestAPZCTreeManager* aTreeManager,
GestureBehavior aBehavior = DEFAULT_GESTURES)
: AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior)
: AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(), aMcc, aBehavior)
{}
nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
return GetInputQueue()->ReceiveInputEvent(this, aEvent, aOutInputBlockId);
}
void ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
GetInputQueue()->ContentReceivedTouch(aInputBlockId, aPreventDefault);
}
void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
}
void SetFrameMetrics(const FrameMetrics& metrics) {
ReentrantMonitorAutoEnter lock(mMonitor);
mFrameMetrics = metrics;
@ -169,9 +188,6 @@ public:
}
};
class TestAPZCTreeManager : public APZCTreeManager {
};
static FrameMetrics
TestFrameMetrics()
{
@ -243,23 +259,23 @@ public:
};
static nsEventStatus
ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int aTime)
ApzcDown(TestAsyncPanZoomController* apzc, int aX, int aY, int aTime, uint64_t* aOutInputBlockId = nullptr)
{
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
return apzc->ReceiveInputEvent(mti);
return apzc->ReceiveInputEvent(mti, aOutInputBlockId);
}
static nsEventStatus
ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int aTime)
ApzcUp(TestAsyncPanZoomController* apzc, int aX, int aY, int aTime)
{
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
return apzc->ReceiveInputEvent(mti);
return apzc->ReceiveInputEvent(mti, nullptr);
}
static void
ApzcTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength,
ApzcTap(TestAsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength,
nsEventStatus (*aOutEventStatuses)[2] = nullptr)
{
nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
@ -274,7 +290,7 @@ ApzcTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLengt
}
static void
ApzcTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength)
ApzcTapAndCheckStatus(TestAsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength)
{
nsEventStatus statuses[2];
ApzcTap(aApzc, aX, aY, aTime, aTapLength, &statuses);
@ -283,19 +299,27 @@ ApzcTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
}
static void
ApzcPan(AsyncPanZoomController* aApzc,
ApzcPan(TestAsyncPanZoomController* aApzc,
int& aTime,
int aTouchStartY,
int aTouchEndY,
bool aKeepFingerDown = false,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
nsEventStatus (*aOutEventStatuses)[4] = nullptr)
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
const int TIME_BETWEEN_TOUCH_EVENT = 100;
const int OVERCOME_TOUCH_TOLERANCE = 100;
// Even if the caller doesn't care about the block id, we need it to set the
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
uint64_t blockId;
if (!aOutInputBlockId) {
aOutInputBlockId = &blockId;
}
// Make sure the move is large enough to not be handled as a tap
nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime);
nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime, aOutInputBlockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
@ -304,12 +328,12 @@ ApzcPan(AsyncPanZoomController* aApzc,
// Allowed touch behaviours must be set after sending touch-start.
if (gfxPrefs::TouchActionEnabled() && aAllowedTouchBehaviors) {
aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
aApzc->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
}
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
status = aApzc->ReceiveInputEvent(mti);
status = aApzc->ReceiveInputEvent(mti, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
@ -318,7 +342,7 @@ ApzcPan(AsyncPanZoomController* aApzc,
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
status = aApzc->ReceiveInputEvent(mti);
status = aApzc->ReceiveInputEvent(mti, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
@ -342,15 +366,16 @@ ApzcPan(AsyncPanZoomController* aApzc,
* consumed them and triggered scrolling behavior.
*/
static void
ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
ApzcPanAndCheckStatus(TestAsyncPanZoomController* aApzc,
int& aTime,
int aTouchStartY,
int aTouchEndY,
bool aExpectConsumed,
nsTArray<uint32_t>* aAllowedTouchBehaviors)
nsTArray<uint32_t>* aAllowedTouchBehaviors,
uint64_t* aOutInputBlockId = nullptr)
{
nsEventStatus statuses[4]; // down, move, move, up
ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
@ -365,17 +390,18 @@ ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
}
static void
ApzcPanNoFling(AsyncPanZoomController* aApzc,
ApzcPanNoFling(TestAsyncPanZoomController* aApzc,
int& aTime,
int aTouchStartY,
int aTouchEndY)
int aTouchEndY,
uint64_t* aOutInputBlockId = nullptr)
{
ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY);
ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
aApzc->CancelAnimation();
}
static void
ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
ApzcPinchWithPinchInput(TestAsyncPanZoomController* aApzc,
int aFocusX, int aFocusY, float aScale,
nsEventStatus (*aOutEventStatuses)[3] = nullptr)
{
@ -405,7 +431,7 @@ ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
}
static void
ApzcPinchWithPinchInputAndCheckStatus(AsyncPanZoomController* aApzc,
ApzcPinchWithPinchInputAndCheckStatus(TestAsyncPanZoomController* aApzc,
int aFocusX, int aFocusY, float aScale,
bool aShouldTriggerPinch)
{
@ -420,33 +446,41 @@ ApzcPinchWithPinchInputAndCheckStatus(AsyncPanZoomController* aApzc,
}
static void
ApzcPinchWithTouchInput(AsyncPanZoomController* aApzc,
ApzcPinchWithTouchInput(TestAsyncPanZoomController* aApzc,
int aFocusX, int aFocusY, float aScale,
int& inputId,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
nsEventStatus (*aOutEventStatuses)[4] = nullptr)
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
// Having pinch coordinates in float type may cause problems with high-precision scale values
// since SingleTouchData accepts integer value. But for trivial tests it should be ok.
float pinchLength = 100.0;
float pinchLengthScaled = pinchLength * aScale;
// Even if the caller doesn't care about the block id, we need it to set the
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
uint64_t blockId;
if (!aOutInputBlockId) {
aOutInputBlockId = &blockId;
}
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mtiStart.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
mtiStart.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
nsEventStatus status = aApzc->ReceiveInputEvent(mtiStart);
nsEventStatus status = aApzc->ReceiveInputEvent(mtiStart, aOutInputBlockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
if (gfxPrefs::TouchActionEnabled() && aAllowedTouchBehaviors) {
aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
aApzc->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
}
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove1.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
mtiMove1.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
status = aApzc->ReceiveInputEvent(mtiMove1);
status = aApzc->ReceiveInputEvent(mtiMove1, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
@ -454,7 +488,7 @@ ApzcPinchWithTouchInput(AsyncPanZoomController* aApzc,
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove2.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
mtiMove2.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
status = aApzc->ReceiveInputEvent(mtiMove2);
status = aApzc->ReceiveInputEvent(mtiMove2, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
@ -462,7 +496,7 @@ ApzcPinchWithTouchInput(AsyncPanZoomController* aApzc,
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mtiEnd.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
mtiEnd.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
status = aApzc->ReceiveInputEvent(mtiEnd);
status = aApzc->ReceiveInputEvent(mtiEnd, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[3] = status;
}
@ -471,7 +505,7 @@ ApzcPinchWithTouchInput(AsyncPanZoomController* aApzc,
}
static void
ApzcPinchWithTouchInputAndCheckStatus(AsyncPanZoomController* aApzc,
ApzcPinchWithTouchInputAndCheckStatus(TestAsyncPanZoomController* aApzc,
int aFocusX, int aFocusY, float aScale,
int& inputId, bool aShouldTriggerPinch,
nsTArray<uint32_t>* aAllowedTouchBehaviors)
@ -618,10 +652,11 @@ TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
MakeApzcZoomable();
int touchInputId = 0;
ApzcPinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId);
uint64_t blockId = 0;
ApzcPinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId, nullptr, nullptr, &blockId);
// Send the prevent-default notification for the touch block
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(blockId, true);
// Run all pending tasks (this should include at least the
// prevent-default timer).
@ -822,15 +857,16 @@ protected:
int touchEnd = 10;
ScreenPoint pointOut;
ViewTransform viewTransformOut;
uint64_t blockId = 0;
// Pan down
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, &allowedTouchBehaviors);
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
// Send the signal that content has handled and preventDefaulted the touch
// events. This flushes the event queue.
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(blockId, true);
// Run all pending tasks (this should include at least the
// prevent-default timer).
EXPECT_LE(1, mcc->RunThroughDelayedTasks());
@ -1110,10 +1146,11 @@ protected:
int time = 0;
int touchStart = 50;
int touchEnd = 10;
uint64_t blockId = 0;
// Start the fling down.
ApzcPan(apzc, time, touchStart, touchEnd);
apzc->ContentReceivedTouch(false);
ApzcPan(apzc, time, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
apzc->ContentReceivedTouch(blockId, false);
while (mcc->RunThroughDelayedTasks());
// Sample the fling a couple of times to ensure it's going.
@ -1124,7 +1161,7 @@ protected:
EXPECT_GT(finalPoint.y, point.y);
// Now we put our finger down to stop the fling
ApzcDown(apzc, 10, 10, time);
ApzcDown(apzc, 10, 10, time, &blockId);
// Re-sample to make sure it hasn't moved
apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(30), &viewTransform, point);
@ -1133,7 +1170,7 @@ protected:
// respond to the touchdown that stopped the fling.
// even if we do a prevent-default on it, the animation should remain stopped.
apzc->ContentReceivedTouch(aPreventDefault);
apzc->ContentReceivedTouch(blockId, aPreventDefault);
while (mcc->RunThroughDelayedTasks());
// Verify the page hasn't moved
@ -1211,18 +1248,19 @@ protected:
MakeApzcUnzoomable();
int time = 0;
uint64_t blockId = 0;
nsEventStatus status = ApzcDown(apzc, 10, 10, time);
nsEventStatus status = ApzcDown(apzc, 10, 10, time, &blockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled()) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(aBehavior);
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
}
// Have content "respond" to the touchstart
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockId, false);
MockFunction<void(std::string checkPointName)> check;
@ -1230,7 +1268,8 @@ protected:
InSequence s;
EXPECT_CALL(check, Call("preHandleLongTap"));
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
blockId++;
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
EXPECT_CALL(check, Call("postHandleLongTap"));
EXPECT_CALL(check, Call("preHandleLongTapUp"));
@ -1254,7 +1293,7 @@ protected:
// in the queue. Deal with those here. We do the content response first
// with preventDefault=false, and then we run the timeout task which
// "loses the race" and does nothing.
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockId, false);
mcc->CheckHasDelayedTask();
mcc->RunDelayedTask();
@ -1281,17 +1320,18 @@ protected:
touchEndY = 50;
int time = 0;
nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
uint64_t blockId = 0;
nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time, &blockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled()) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(aBehavior);
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
}
// Have content "respond" to the touchstart
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockId, false);
MockFunction<void(std::string checkPointName)> check;
@ -1299,7 +1339,8 @@ protected:
InSequence s;
EXPECT_CALL(check, Call("preHandleLongTap"));
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
blockId++;
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
EXPECT_CALL(check, Call("postHandleLongTap"));
}
@ -1318,7 +1359,7 @@ protected:
// Send the signal that content has handled the long-tap, and then run
// the timeout task (it will be a no-op because the content "wins" the
// race. This takes the place of the "contextmenu" event.
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(blockId, true);
mcc->CheckHasDelayedTask();
mcc->RunDelayedTask();
@ -1326,7 +1367,7 @@ protected:
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
status = apzc->ReceiveInputEvent(mti);
status = apzc->ReceiveInputEvent(mti, nullptr);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
@ -1367,23 +1408,31 @@ TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
}
static void
ApzcDoubleTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
nsEventStatus (*aOutEventStatuses)[4] = nullptr)
ApzcDoubleTap(TestAsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t (*aOutInputBlockIds)[2] = nullptr)
{
nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
uint64_t blockId;
nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime, &blockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
if (aOutInputBlockIds) {
(*aOutInputBlockIds)[0] = blockId;
}
aTime += 10;
status = ApzcUp(aApzc, aX, aY, aTime);
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
aTime += 10;
status = ApzcDown(aApzc, aX, aY, aTime);
status = ApzcDown(aApzc, aX, aY, aTime, &blockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
if (aOutInputBlockIds) {
(*aOutInputBlockIds)[1] = blockId;
}
aTime += 10;
status = ApzcUp(aApzc, aX, aY, aTime);
if (aOutEventStatuses) {
@ -1392,10 +1441,10 @@ ApzcDoubleTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
}
static void
ApzcDoubleTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime)
ApzcDoubleTapAndCheckStatus(TestAsyncPanZoomController* aApzc, int aX, int aY, int& aTime, uint64_t (*aOutInputBlockIds)[2] = nullptr)
{
nsEventStatus statuses[4];
ApzcDoubleTap(aApzc, aX, aY, aTime, &statuses);
ApzcDoubleTap(aApzc, aX, aY, aTime, &statuses, aOutInputBlockIds);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
@ -1410,11 +1459,12 @@ TEST_F(APZCGestureDetectorTester, DoubleTap) {
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
int time = 0;
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
uint64_t blockIds[2];
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockIds[0], false);
apzc->ContentReceivedTouch(blockIds[1], false);
while (mcc->RunThroughDelayedTasks());
@ -1429,11 +1479,12 @@ TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
int time = 0;
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
uint64_t blockIds[2];
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockIds[0], false);
apzc->ContentReceivedTouch(blockIds[1], false);
while (mcc->RunThroughDelayedTasks());
@ -1448,11 +1499,12 @@ TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
int time = 0;
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
uint64_t blockIds[2];
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(false);
apzc->ContentReceivedTouch(blockIds[0], true);
apzc->ContentReceivedTouch(blockIds[1], false);
while (mcc->RunThroughDelayedTasks());
@ -1467,11 +1519,13 @@ TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
int time = 0;
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
uint64_t blockIds[2];
ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
printf_stderr("blockids %llu %llu\n", blockIds[0], blockIds[1]);
// responses to the two touchstarts
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(true);
apzc->ContentReceivedTouch(blockIds[0], true);
apzc->ContentReceivedTouch(blockIds[1], true);
while (mcc->RunThroughDelayedTasks());
@ -1493,12 +1547,12 @@ TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
apzc->ReceiveInputEvent(mti, nullptr);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
apzc->ReceiveInputEvent(mti, nullptr);
while (mcc->RunThroughDelayedTasks());
@ -1517,17 +1571,17 @@ TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
MultiTouchInput mti;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
apzc->ReceiveInputEvent(mti, nullptr);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
apzc->ReceiveInputEvent(mti, nullptr);
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti);
apzc->ReceiveInputEvent(mti, nullptr);
while (mcc->RunThroughDelayedTasks());
@ -1636,26 +1690,26 @@ ApzctmPan(APZCTreeManager* aTreeManager,
// Make sure the move is large enough to not be handled as a tap
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
aTreeManager->ReceiveInputEvent(mti, nullptr);
aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
aTime += TIME_BETWEEN_TOUCH_EVENT;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
aTreeManager->ReceiveInputEvent(mti, nullptr);
aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
aTime += TIME_BETWEEN_TOUCH_EVENT;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
aTreeManager->ReceiveInputEvent(mti, nullptr);
aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
aTime += TIME_BETWEEN_TOUCH_EVENT;
if (!aKeepFingerDown) {
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
aTreeManager->ReceiveInputEvent(mti, nullptr);
aTreeManager->ReceiveInputEvent(mti, nullptr, nullptr);
}
aTime += TIME_BETWEEN_TOUCH_EVENT;
@ -1744,7 +1798,7 @@ TEST_F(APZHitTestingTester, HitTesting1) {
// No APZC attached so hit testing will return no APZC at (20,20)
nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
AsyncPanZoomController* nullAPZC = nullptr;
TestAsyncPanZoomController* nullAPZC = nullptr;
EXPECT_EQ(nullAPZC, hit.get());
EXPECT_EQ(Matrix4x4(), transformToApzc);
EXPECT_EQ(Matrix4x4(), transformToGecko);
@ -1815,9 +1869,9 @@ TEST_F(APZHitTestingTester, HitTesting2) {
// layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
// layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
AsyncPanZoomController* apzcroot = ApzcOf(root);
AsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
AsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
// Hit an area that's clearly on the root layer but not any of the child layers.
nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
@ -1925,7 +1979,7 @@ TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
AsyncPanZoomController* nullAPZC = nullptr;
TestAsyncPanZoomController* nullAPZC = nullptr;
// so they should have the same APZC
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
@ -1966,7 +2020,7 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
ScopedLayerTreeRegistration registration(0, root, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
AsyncPanZoomController* nullAPZC = nullptr;
TestAsyncPanZoomController* nullAPZC = nullptr;
// Ensure all the scrollable layers have an APZC
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
@ -1991,10 +2045,10 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
// Ensure the shape of the APZC tree is as expected
AsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
AsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
AsyncPanZoomController* layer7 = ApzcOf(layers[7]);
AsyncPanZoomController* layer9 = ApzcOf(layers[9]);
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
EXPECT_EQ(nullptr, layers1_2->GetParent());
EXPECT_EQ(nullptr, layers4_6_8->GetParent());
EXPECT_EQ(layers4_6_8, layer7->GetParent());
@ -2021,7 +2075,7 @@ TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
CreateSimpleScrollingLayer();
ScopedLayerTreeRegistration registration(0, root, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
AsyncPanZoomController* apzcroot = ApzcOf(root);
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
// At this point, the following holds (all coordinates in screen pixels):
// layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
@ -2048,13 +2102,13 @@ TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
check.Call("post-first-touch-start");
// Send a touchend to clear state
mti.mType = MultiTouchInput::MULTITOUCH_END;
manager->ReceiveInputEvent(mti, nullptr);
manager->ReceiveInputEvent(mti, nullptr, nullptr);
AsyncPanZoomController::SetFrameTime(testStartTime + TimeDuration::FromMilliseconds(1000));
@ -2071,12 +2125,12 @@ TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
// Ensure that a touch start again doesn't get untransformed by flushing
// a repaint
mti.mType = MultiTouchInput::MULTITOUCH_START;
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
check.Call("post-second-touch-start");
mti.mType = MultiTouchInput::MULTITOUCH_END;
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr));
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
mcc->RunThroughDelayedTasks();
@ -2180,10 +2234,11 @@ TEST_F(APZOverscrollHandoffTester, DeferredInputEventProcessing) {
// Queue input events for a pan.
int time = 0;
ApzcPanNoFling(childApzc, time, 90, 30);
uint64_t blockId = 0;
ApzcPanNoFling(childApzc, time, 90, 30, &blockId);
// Allow the pan to be processed.
childApzc->ContentReceivedTouch(false);
childApzc->ContentReceivedTouch(blockId, false);
// Make sure overscroll was handed off correctly.
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
@ -2207,7 +2262,8 @@ TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
// Queue input events for a pan.
int time = 0;
ApzcPanNoFling(childApzc, time, 90, 30);
uint64_t blockId = 0;
ApzcPanNoFling(childApzc, time, 90, 30, &blockId);
// Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
// between the child and the root.
@ -2217,10 +2273,11 @@ TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
// Queue input events for another pan.
ApzcPanNoFling(childApzc, time, 30, 90);
uint64_t secondBlockId = 0;
ApzcPanNoFling(childApzc, time, 30, 90, &secondBlockId);
// Allow the first pan to be processed.
childApzc->ContentReceivedTouch(false);
childApzc->ContentReceivedTouch(blockId, false);
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the first pan was queued.
@ -2229,7 +2286,7 @@ TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
// Allow the second pan to be processed.
childApzc->ContentReceivedTouch(false);
childApzc->ContentReceivedTouch(secondBlockId, false);
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the second pan was queued.
@ -2259,12 +2316,12 @@ TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
// Use the same touch identifier for the first touch (0) as ApzctmPan(). (A bit hacky.)
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
manager->ReceiveInputEvent(secondFingerDown, nullptr);
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
// Release the fingers.
MultiTouchInput fingersUp = secondFingerDown;
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
manager->ReceiveInputEvent(fingersUp, nullptr);
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
// Allow any animations to run their course.
child->AdvanceAnimationsUntilEnd(testStartTime);

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

@ -876,6 +876,17 @@ gfxContext::GetPattern()
return pat.forget();
}
void
gfxContext::SetFontSmoothingBackgroundColor(const Color& aColor)
{
CurrentState().fontSmoothingBackgroundColor = aColor;
}
Color
gfxContext::GetFontSmoothingBackgroundColor()
{
return CurrentState().fontSmoothingBackgroundColor;
}
// masking
void

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

@ -346,6 +346,13 @@ public:
*/
void SetPattern(gfxPattern *pattern);
/**
* Set the color that text drawn on top of transparent pixels should be
* anti-aliased into.
*/
void SetFontSmoothingBackgroundColor(const mozilla::gfx::Color& aColor);
mozilla::gfx::Color GetFontSmoothingBackgroundColor();
/**
* Get the source pattern (solid color, normal pattern, surface, etc)
*/
@ -645,6 +652,7 @@ private:
mozilla::gfx::AntialiasMode aaMode;
bool patternTransformChanged;
Matrix patternTransform;
Color fontSmoothingBackgroundColor;
// This is used solely for using minimal intermediate surface size.
mozilla::gfx::Point deviceOffset;
};

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

@ -601,7 +601,7 @@ gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
}
TemporaryRef<GlyphRenderingOptions>
gfxDWriteFont::GetGlyphRenderingOptions()
gfxDWriteFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
if (UsingClearType()) {
return Factory::CreateDWriteGlyphRenderingOptions(

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

@ -56,7 +56,8 @@ public:
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> GetGlyphRenderingOptions();
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;

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

@ -276,7 +276,7 @@ gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
#ifdef USE_SKIA
mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
gfxFT2Font::GetGlyphRenderingOptions()
gfxFT2Font::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
mozilla::gfx::FontHinting hinting;

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

@ -68,7 +68,8 @@ public: // new functions
FontCacheSizes* aSizes) const;
#ifdef USE_SKIA
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> GetGlyphRenderingOptions();
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
#endif
protected:

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

@ -1949,7 +1949,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
// to transform the Brush inside flush.
fontParams.passedInvMatrix = nullptr;
fontParams.renderingOptions = GetGlyphRenderingOptions();
fontParams.renderingOptions = GetGlyphRenderingOptions(&aRunParams);
fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
// The cairo DrawTarget backend uses the cairo_scaled_font directly

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

@ -1413,7 +1413,8 @@ public:
// Return Azure GlyphRenderingOptions for drawing this font.
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions() { return nullptr; }
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr)
{ return nullptr; }
gfxFloat SynthesizeSpaceWidth(uint32_t aCh);
@ -2058,6 +2059,7 @@ struct TextRunDrawParams {
gfxFont::Spacing *spacing;
gfxTextRunDrawCallbacks *callbacks;
gfxTextContextPaint *runContextPaint;
mozilla::gfx::Color fontSmoothingBGColor;
gfxFloat direction;
double devPerApp;
DrawMode drawMode;

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

@ -419,6 +419,15 @@ gfxMacFont::GetScaledFont(DrawTarget *aTarget)
return mAzureScaledFont;
}
TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
gfxMacFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
if (aRunParams) {
return mozilla::gfx::Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
}
return nullptr;
}
void
gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const

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

@ -39,6 +39,9 @@ public:
virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(mozilla::gfx::DrawTarget *aTarget);
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,

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

@ -665,7 +665,8 @@ public:
const gfxFontStyle *aFontStyle);
#ifdef USE_SKIA
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> GetGlyphRenderingOptions();
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
#endif
// return a cloned font resized and offset to simulate sub/superscript glyphs
@ -2142,7 +2143,7 @@ ApplyGdkScreenFontOptions(FcPattern *aPattern)
#ifdef USE_SKIA
mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
gfxFcFont::GetGlyphRenderingOptions()
gfxFcFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
cairo_scaled_font_t *scaled_font = CairoScaledFont();
cairo_font_options_t *options = cairo_font_options_create();

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

@ -615,6 +615,7 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
params.runContextPaint = aContextPaint;
params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
params.dt = aContext->GetDrawTarget();
params.fontSmoothingBGColor = aContext->GetFontSmoothingBackgroundColor();
// synthetic bolding draws glyphs twice ==> colors with opacity won't draw
// correctly unless first drawn without alpha

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

@ -18,6 +18,7 @@ interface nsIURI;
interface nsISimpleEnumerator;
#include "nsIRequest.idl" // for nsLoadFlags
#include "nsIContentPolicy.idl" // for nsContentPolicyType
/**
* imgILoader interface
@ -26,7 +27,7 @@ interface nsISimpleEnumerator;
* @version 0.3
* @see imagelib2
*/
[scriptable, builtinclass, uuid(046d5fa6-ac59-489d-b51e-0ffe57e8df59)]
[scriptable, builtinclass, uuid(42ef3b0a-cd82-454b-b4c4-f3470014a68b)]
interface imgILoader : nsISupports
{
// Extra flags to pass to loadImage if you want a load to use CORS
@ -46,6 +47,9 @@ interface imgILoader : nsISupports
* @param aLoadFlags Load flags for the request
* @param aCacheKey cache key to use for a load if the original
* image came from a request that had post data
* @param aContentPolicyType [optional] the nsContentPolicyType to
* use for this load. Defaults to
* nsIContentPolicy::TYPE_IMAGE
* libpr0n does NOT keep a strong ref to the observer; this prevents
@ -61,13 +65,15 @@ interface imgILoader : nsISupports
in imgINotificationObserver aObserver,
in nsISupports aCX,
in nsLoadFlags aLoadFlags,
in nsISupports cacheKey);
in nsISupports cacheKey,
[optional]
in nsContentPolicyType aContentPolicyType);
/**
* Start the load and decode of an image.
* @param aChannel the channel to load the image from. This must
* already be opened before ths method is called, and there
* must have been no OnDataAvailable calls for it yet.
* must have been no OnDataAvailable calls for it yet.
* @param aObserver the observer (may be null)
* @param cx some random data
* @param aListener [out]

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

@ -635,6 +635,7 @@ static nsresult NewImageChannel(nsIChannel **aResult,
nsILoadGroup *aLoadGroup,
const nsCString& aAcceptHeader,
nsLoadFlags aLoadFlags,
nsContentPolicyType aPolicyType,
nsIPrincipal *aLoadingPrincipal,
nsISupports *aRequestingContext)
{
@ -680,6 +681,7 @@ static nsresult NewImageChannel(nsIChannel **aResult,
if (inherit) {
securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
}
// Note we are calling NS_NewChannelInternal() here with a node and a principal.
// This is for things like background images that are specified by user
// stylesheets, where the document is being styled, but the principal is that
@ -689,7 +691,7 @@ static nsresult NewImageChannel(nsIChannel **aResult,
requestingNode,
requestingPrincipal,
securityFlags,
nsIContentPolicy::TYPE_IMAGE,
aPolicyType,
nullptr, // loadGroup
callbacks,
aLoadFlags);
@ -1442,6 +1444,7 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsContentPolicyType aLoadPolicyType,
imgRequestProxy **aProxyRequest,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode)
@ -1490,6 +1493,7 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
aLoadGroup,
mAcceptHeader,
aLoadFlags,
aLoadPolicyType,
aLoadingPrincipal,
aCX);
if (NS_FAILED(rv)) {
@ -1567,6 +1571,7 @@ bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
imgINotificationObserver *aObserver,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsContentPolicyType aLoadPolicyType,
bool aCanMakeNewChannel,
imgRequestProxy **aProxyRequest,
nsIPrincipal* aLoadingPrincipal,
@ -1678,8 +1683,9 @@ bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
aReferrerURI, aLoadGroup, aObserver,
aCX, aLoadFlags, aProxyRequest,
aLoadingPrincipal, aCORSMode);
aCX, aLoadFlags, aLoadPolicyType,
aProxyRequest, aLoadingPrincipal,
aCORSMode);
}
return !validateRequest;
@ -1853,8 +1859,13 @@ NS_IMETHODIMP imgLoader::LoadImageXPCOM(nsIURI *aURI,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
nsContentPolicyType aContentPolicyType,
imgIRequest **_retval)
{
// Optional parameter, so defaults to 0 (== TYPE_INVALID)
if (!aContentPolicyType) {
aContentPolicyType = nsIContentPolicy::TYPE_IMAGE;
}
imgRequestProxy *proxy;
nsresult result = LoadImage(aURI,
aInitialDocumentURI,
@ -1865,6 +1876,7 @@ NS_IMETHODIMP imgLoader::LoadImageXPCOM(nsIURI *aURI,
aCX,
aLoadFlags,
aCacheKey,
aContentPolicyType,
EmptyString(),
&proxy);
*_retval = proxy;
@ -1889,10 +1901,11 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
nsContentPolicyType aContentPolicyType,
const nsAString& initiatorType,
imgRequestProxy **_retval)
{
VerifyCacheSizes();
VerifyCacheSizes();
NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
@ -1967,8 +1980,9 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
aLoadGroup, aObserver, aCX, requestFlags, true,
_retval, aLoadingPrincipal, corsmode)) {
aLoadGroup, aObserver, aCX, requestFlags,
aContentPolicyType, true, _retval,
aLoadingPrincipal, corsmode)) {
request = entry->GetRequest();
// If this entry has no proxies, its request has no reference to the entry.
@ -2010,6 +2024,7 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
aLoadGroup,
mAcceptHeader,
requestFlags,
aContentPolicyType,
aLoadingPrincipal,
aCX);
if (NS_FAILED(rv))
@ -2192,9 +2207,13 @@ nsresult imgLoader::LoadImageWithChannel(nsIChannel *channel, imgINotificationOb
//
// XXX -- should this be changed? it's pretty much verbatim from the old
// code, but seems nonsensical.
if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr, aObserver, aCX,
requestFlags, false, nullptr, nullptr,
imgIRequest::CORS_NONE)) {
//
// Since aCanMakeNewChannel == false, we don't need to pass content policy
// type/principal/etc
if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr,
aObserver, aCX, requestFlags,
nsIContentPolicy::TYPE_INVALID, false, nullptr,
nullptr, imgIRequest::CORS_NONE)) {
request = entry->GetRequest();
} else {
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));

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

@ -257,8 +257,10 @@ public:
nsISupports *aCX,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
nsContentPolicyType aContentPolicyType,
const nsAString& initiatorType,
imgRequestProxy **_retval);
nsresult LoadImageWithChannel(nsIChannel *channel,
imgINotificationObserver *aObserver,
nsISupports *aCX,
@ -336,7 +338,9 @@ private: // methods
nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI,
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver, nsISupports *aCX,
nsLoadFlags aLoadFlags, bool aCanMakeNewChannel,
nsLoadFlags aLoadFlags,
nsContentPolicyType aContentPolicyType,
bool aCanMakeNewChannel,
imgRequestProxy **aProxyRequest,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode);
@ -347,6 +351,7 @@ private: // methods
nsILoadGroup *aLoadGroup,
imgINotificationObserver *aObserver,
nsISupports *aCX, nsLoadFlags aLoadFlags,
nsContentPolicyType aContentPolicyType,
imgRequestProxy **aProxyRequest,
nsIPrincipal* aLoadingPrincipal,
int32_t aCORSMode);

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

@ -48,6 +48,7 @@ namespace JS {
D(OUT_OF_NURSERY) \
D(EVICT_NURSERY) \
D(FULL_STORE_BUFFER) \
D(SHARED_MEMORY_LIMIT) \
\
/* These are reserved for future use. */ \
D(RESERVED0) \
@ -69,7 +70,6 @@ namespace JS {
D(RESERVED16) \
D(RESERVED17) \
D(RESERVED18) \
D(RESERVED19) \
\
/* Reasons from Firefox */ \
D(DOM_WINDOW_UTILS) \

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

@ -328,6 +328,21 @@ IsUseOfName(ParseNode *pn, PropertyName *name)
return pn->isKind(PNK_NAME) && pn->name() == name;
}
static inline bool
IsIgnoredDirectiveName(ExclusiveContext *cx, JSAtom *atom)
{
return atom != cx->names().useStrict;
}
static inline bool
IsIgnoredDirective(ExclusiveContext *cx, ParseNode *pn)
{
return pn->isKind(PNK_SEMI) &&
UnaryKid(pn) &&
UnaryKid(pn)->isKind(PNK_STRING) &&
IsIgnoredDirectiveName(cx, UnaryKid(pn)->pn_atom);
}
static inline bool
IsEmptyStatement(ParseNode *pn)
{
@ -3512,8 +3527,11 @@ CheckPrecedingStatements(ModuleCompiler &m, ParseNode *stmtList)
{
MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
if (ListLength(stmtList) != 0)
return m.fail(ListHead(stmtList), "invalid asm.js statement");
ParseNode *stmt = ListHead(stmtList);
for (unsigned i = 0, n = ListLength(stmtList); i < n; i++) {
if (!IsIgnoredDirective(m.cx(), stmt))
return m.fail(stmt, "invalid asm.js statement");
}
return true;
}
@ -3840,6 +3858,22 @@ CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst)
return m.fail(initNode, "unsupported import expression");
}
static bool
CheckModuleProcessingDirectives(ModuleCompiler &m)
{
TokenStream &ts = m.parser().tokenStream;
while (true) {
if (!ts.matchToken(TOK_STRING))
return true;
if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
return m.fail(nullptr, "unsupported processing directive");
if (!ts.matchToken(TOK_SEMI))
return m.fail(nullptr, "expected semicolon after string literal");
}
}
static bool
CheckModuleGlobals(ModuleCompiler &m)
{
@ -3893,6 +3927,18 @@ CheckArgumentType(FunctionCompiler &f, ParseNode *stmt, PropertyName *name, VarT
return true;
}
static bool
CheckProcessingDirectives(ModuleCompiler &m, ParseNode **stmtIter)
{
ParseNode *stmt = *stmtIter;
while (stmt && IsIgnoredDirective(m.cx(), stmt))
stmt = NextNode(stmt);
*stmtIter = stmt;
return true;
}
static bool
CheckArguments(FunctionCompiler &f, ParseNode **stmtIter, VarTypeVector *argTypes)
{
@ -6853,11 +6899,15 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
if (!funpc.init(tokenStream))
return false;
if (!m.parser().functionArgsAndBodyGeneric(fn, fun, Normal, Statement))
return false;
if (!m.parser().functionArgsAndBodyGeneric(fn, fun, Normal, Statement)) {
if (tokenStream.hadError() || directives == newDirectives)
return false;
if (tokenStream.hadError() || directives != newDirectives)
return false;
return m.fail(nullptr, "encountered new directive");
}
MOZ_ASSERT(!tokenStream.hadError());
MOZ_ASSERT(directives == newDirectives);
outerpc->blockidGen = funpc.blockidGen;
fn->pn_blockid = outerpc->blockid();
@ -6898,6 +6948,9 @@ CheckFunction(ModuleCompiler &m, LifoAlloc &lifo, MIRGenerator **mir, ModuleComp
ParseNode *stmtIter = ListHead(FunctionStatementList(fn));
if (!CheckProcessingDirectives(m, &stmtIter))
return false;
VarTypeVector argTypes(m.lifo());
if (!CheckArguments(f, &stmtIter, &argTypes))
return false;
@ -8501,6 +8554,9 @@ CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
if (!CheckPrecedingStatements(m, stmtList))
return false;
if (!CheckModuleProcessingDirectives(m))
return false;
if (!CheckModuleGlobals(m))
return false;

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

@ -53,7 +53,7 @@ ValidateAsmJS(ExclusiveContext *cx, AsmJSParser &parser, frontend::ParseNode *st
// The assumed page size; dynamically checked in ValidateAsmJS.
const size_t AsmJSPageSize = 4096;
#ifdef JS_CODEGEN_X64
#ifdef JS_CPU_X64
// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
// byteLength portion of which is accessible) so that out-of-bounds accesses
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.

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

@ -19,6 +19,18 @@ assertAsmTypeFail(USE_ASM + 'function f(){} var f=[f,f]; return f');
assertAsmTypeFail(USE_ASM + 'function f() 0; return f');
assertAsmTypeFail('"use strict";' + USE_ASM + 'function f() {} return f');
assertAsmTypeFail(USE_ASM + '"use strict"; function f() {} return f');
assertAsmTypeFail(USE_ASM + '"use foopy" + 1; function f() {} return f');
assertAsmTypeFail(USE_ASM + 'function f() { "use strict"; } return f');
assertEq(asmLink(asmCompile(USE_ASM + '"use asm"; function f() {} return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + '"use foopy"; function f() {} return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + '"use foopy"; "use blarg"; function f() {} return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f() { "use asm"; } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f() { "use foopy"; } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f() { "use foopy"; "use blarg"; } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { "use foopy"; i=i|0; } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { "use foopy"; "use asm"; i=i|0; } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + '"use warm"; function f(i) { "use cold"; i=i|0 } function g() { "use hot"; return 0 } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + '"use warm"; function f(i) { "use cold"; i=i|0 } function g() { "use asm"; return 0 } return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f(){} return f'))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + 'function f(){;} return f'))(), undefined);
assertAsmTypeFail(USE_ASM + 'function f(i,j){;} return f');

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

@ -302,16 +302,16 @@ class MacroAssemblerNone : public Assembler
template <typename T> void compareExchange16SignExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
template <typename T> void compareExchange16ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
template <typename T> void compareExchange32(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAdd32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchSub32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAnd8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAnd8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
template <typename T, typename S> void atomicFetchAnd16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }

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

@ -991,6 +991,23 @@ class AutoNewContext
}
};
static void
my_LargeAllocFailCallback(void *data)
{
JSContext *cx = (JSContext*)data;
JSRuntime *rt = cx->runtime();
if (InParallelSection() || !cx->allowGC())
return;
MOZ_ASSERT(!rt->isHeapBusy());
MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess());
JS::PrepareForFullGC(rt);
AutoKeepAtoms keepAtoms(cx->perThreadData);
rt->gc.gc(GC_NORMAL, JS::gcreason::SHARED_MEMORY_LIMIT);
}
static const uint32_t CacheEntry_SOURCE = 0;
static const uint32_t CacheEntry_BYTECODE = 1;
@ -2716,6 +2733,8 @@ WorkerMain(void *arg)
return;
}
JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx);
do {
JSAutoRequest ar(cx);
@ -2738,6 +2757,8 @@ WorkerMain(void *arg)
JS_ExecuteScript(cx, global, script, &result);
} while (0);
JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
DestroyContext(cx, false);
JS_DestroyRuntime(rt);
@ -6133,6 +6154,8 @@ main(int argc, char **argv, char **envp)
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx);
// Set some parameters to allow incremental GC in low memory conditions,
// as is done for the browser, except in more-deterministic builds or when
// disabled by command line options.
@ -6153,6 +6176,8 @@ main(int argc, char **argv, char **envp)
printf("OOM max count: %u\n", OOM_counter);
#endif
JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
DestroyContext(cx, true);
KillWatchdog();

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