зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound a=merge
This commit is contained in:
Коммит
a3086563dc
|
@ -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;
|
||||
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 91 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 100 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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();
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче