mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
06f5f25641
@ -331,6 +331,64 @@ IDRefsIterator::Next()
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ARIAOwnedByIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ARIAOwnedByIterator::ARIAOwnedByIterator(const Accessible* aDependent) :
|
||||
RelatedAccIterator(aDependent->Document(), aDependent->GetContent(),
|
||||
nsGkAtoms::aria_owns), mDependent(aDependent)
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
ARIAOwnedByIterator::Next()
|
||||
{
|
||||
Accessible* owner = RelatedAccIterator::Next();
|
||||
Accessible* cur = owner;
|
||||
while (cur) {
|
||||
if (cur == mDependent)
|
||||
return Next(); // owner cannot be a child of dependent.
|
||||
|
||||
if (cur->IsDoc())
|
||||
break; // don't cross document boundaries
|
||||
|
||||
cur = cur->Parent();
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ARIAOwnsIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ARIAOwnsIterator::ARIAOwnsIterator(const Accessible* aOwner) :
|
||||
mIter(aOwner->Document(), aOwner->GetContent(), nsGkAtoms::aria_owns),
|
||||
mOwner(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
ARIAOwnsIterator::Next()
|
||||
{
|
||||
Accessible* child = mIter.Next();
|
||||
const Accessible* cur = mOwner;
|
||||
while (cur) {
|
||||
if (cur == child)
|
||||
return Next(); // cannot own its own parent
|
||||
|
||||
if (cur->IsDoc())
|
||||
break; // don't cross document boundaries
|
||||
|
||||
cur = cur->Parent();
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SingleAccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -247,6 +247,48 @@ private:
|
||||
nsAString::index_type mCurrIdx;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Iterates over related accessible referred by aria-owns.
|
||||
*/
|
||||
class ARIAOwnedByIterator MOZ_FINAL : public RelatedAccIterator
|
||||
{
|
||||
public:
|
||||
explicit ARIAOwnedByIterator(const Accessible* aDependent);
|
||||
virtual ~ARIAOwnedByIterator() { }
|
||||
|
||||
virtual Accessible* Next() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
ARIAOwnedByIterator() = delete;
|
||||
ARIAOwnedByIterator(const ARIAOwnedByIterator&) = delete;
|
||||
ARIAOwnedByIterator& operator = (const ARIAOwnedByIterator&) = delete;
|
||||
|
||||
const Accessible* mDependent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Iterates over related accessible referred by aria-owns.
|
||||
*/
|
||||
class ARIAOwnsIterator MOZ_FINAL : public AccIterable
|
||||
{
|
||||
public:
|
||||
explicit ARIAOwnsIterator(const Accessible* aOwner);
|
||||
virtual ~ARIAOwnsIterator() { }
|
||||
|
||||
virtual Accessible* Next() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
ARIAOwnsIterator() = delete;
|
||||
ARIAOwnsIterator(const ARIAOwnsIterator&) = delete;
|
||||
ARIAOwnsIterator& operator = (const ARIAOwnsIterator&) = delete;
|
||||
|
||||
IDRefsIterator mIter;
|
||||
const Accessible* mOwner;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Iterator that points to a single accessible returning it on the first call
|
||||
* to Next().
|
||||
|
@ -324,9 +324,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
|
||||
if (!tryOwnsParent)
|
||||
break;
|
||||
|
||||
RelatedAccIterator iter(child->Document(), child->GetContent(),
|
||||
nsGkAtoms::aria_owns);
|
||||
parent = iter.Next();
|
||||
parent = ARIAOwnedByIterator(child).Next();
|
||||
tryOwnsParent = false;
|
||||
}
|
||||
|
||||
|
@ -1281,7 +1281,7 @@ Accessible::Value(nsString& aValue)
|
||||
Accessible* option = CurrentItem();
|
||||
if (!option) {
|
||||
Accessible* listbox = nullptr;
|
||||
IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
|
||||
ARIAOwnsIterator iter(this);
|
||||
while ((listbox = iter.Next()) && !listbox->IsListControl());
|
||||
|
||||
if (!listbox) {
|
||||
@ -1559,8 +1559,7 @@ Accessible::RelationByType(RelationType aType)
|
||||
}
|
||||
|
||||
case RelationType::NODE_CHILD_OF: {
|
||||
Relation rel(new RelatedAccIterator(Document(), mContent,
|
||||
nsGkAtoms::aria_owns));
|
||||
Relation rel(new ARIAOwnedByIterator(this));
|
||||
|
||||
// This is an ARIA tree or treegrid that doesn't use owns, so we need to
|
||||
// get the parent the hard way.
|
||||
@ -1590,7 +1589,7 @@ Accessible::RelationByType(RelationType aType)
|
||||
}
|
||||
|
||||
case RelationType::NODE_PARENT_OF: {
|
||||
Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
|
||||
Relation rel(new ARIAOwnsIterator(this));
|
||||
|
||||
// ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
|
||||
// also can be organized by groups.
|
||||
|
@ -74,6 +74,15 @@
|
||||
testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
|
||||
testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
|
||||
|
||||
// aria-owns, bad relations
|
||||
testRelation("ariaowns_container", RELATION_NODE_CHILD_OF, null);
|
||||
testRelation("ariaowns_self", RELATION_NODE_CHILD_OF, null);
|
||||
testRelation("ariaowns_uncle", RELATION_NODE_CHILD_OF, "ariaowns_self");
|
||||
|
||||
testRelation("ariaowns_container", RELATION_NODE_PARENT_OF, null);
|
||||
testRelation("ariaowns_self", RELATION_NODE_PARENT_OF, "ariaowns_uncle");
|
||||
testRelation("ariaowns_uncle", RELATION_NODE_PARENT_OF, null);
|
||||
|
||||
// 'node child of' relation for outlineitem role
|
||||
testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
|
||||
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
|
||||
@ -310,6 +319,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="ariaowns_container">
|
||||
<div id="ariaowns_self"
|
||||
aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
|
||||
</div>
|
||||
<div id="ariaowns_uncle"></div>
|
||||
|
||||
<div aria-owns="simplegrid-ownrow" role="grid" id="simplegrid">
|
||||
<div role="row" id="simplegrid-row1" aria-level="1">
|
||||
<div role="gridcell">cell 1,1</div>
|
||||
|
@ -59,8 +59,11 @@ MOZ_B2G=1
|
||||
if test "$OS_TARGET" = "Android"; then
|
||||
MOZ_NUWA_PROCESS=1
|
||||
MOZ_B2G_LOADER=1
|
||||
# Warnings-as-errors cannot be enabled on Lollipop until bug 1119980 is fixed.
|
||||
if test "$PLATFORM_SDK_VERSION" -lt 21; then
|
||||
MOZ_ENABLE_WARNINGS_AS_ERRORS=1
|
||||
fi
|
||||
fi
|
||||
|
||||
MOZ_JSDOWNLOADS=1
|
||||
|
||||
|
@ -144,19 +144,11 @@ pref("app.update.cert.maxErrors", 5);
|
||||
|
||||
// Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
|
||||
// This condition protects us against accidentally using it for release builds.
|
||||
#ifndef RELEASE_BUILD
|
||||
pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
|
||||
pref("app.update.certs.1.commonName", "aus4.mozilla.org");
|
||||
|
||||
pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
|
||||
pref("app.update.certs.2.commonName", "aus4.mozilla.org");
|
||||
#else
|
||||
pref("app.update.certs.1.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
|
||||
pref("app.update.certs.1.commonName", "aus3.mozilla.org");
|
||||
|
||||
pref("app.update.certs.2.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
|
||||
pref("app.update.certs.2.commonName", "aus3.mozilla.org");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Whether or not app updates are enabled
|
||||
@ -194,11 +186,7 @@ pref("app.update.badge", false);
|
||||
pref("app.update.staging.enabled", true);
|
||||
|
||||
// Update service URL:
|
||||
#ifndef RELEASE_BUILD
|
||||
pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
|
||||
#else
|
||||
pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
|
||||
#endif
|
||||
// app.update.url.manual is in branding section
|
||||
// app.update.url.details is in branding section
|
||||
|
||||
|
@ -57,6 +57,8 @@ function SearchSuggestionUIController(inputElement, tableParent, onClick=null,
|
||||
|
||||
this._stickyInputValue = "";
|
||||
this._hideSuggestions();
|
||||
|
||||
this._ignoreInputEvent = false;
|
||||
}
|
||||
|
||||
SearchSuggestionUIController.prototype = {
|
||||
@ -143,6 +145,10 @@ SearchSuggestionUIController.prototype = {
|
||||
},
|
||||
|
||||
_onInput: function () {
|
||||
if (this._ignoreInputEvent) {
|
||||
this._ignoreInputEvent = false;
|
||||
return;
|
||||
}
|
||||
if (this.input.value) {
|
||||
this._getSuggestions();
|
||||
}
|
||||
@ -231,6 +237,20 @@ SearchSuggestionUIController.prototype = {
|
||||
let idx = this._indexOfTableRowOrDescendent(event.target);
|
||||
let suggestion = this.suggestionAtIndex(idx);
|
||||
this._stickyInputValue = suggestion;
|
||||
|
||||
// Commit composition string forcibly, because setting input value does not
|
||||
// work if input has composition string (see bug 1115616 and bug 632744).
|
||||
try {
|
||||
let imeEditor = this.input.editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
|
||||
if (imeEditor.composing) {
|
||||
// Ignore input event for compisition end to avoid getting suggestion
|
||||
// again.
|
||||
this._ignoreInputEvent = true;
|
||||
imeEditor.forceCompositionEnd();
|
||||
this._ignoreInputEvent = false;
|
||||
}
|
||||
} catch(e) { }
|
||||
|
||||
this.input.value = suggestion;
|
||||
this.input.setAttribute("selection-index", idx);
|
||||
this.input.setAttribute("selection-kind", "mouse");
|
||||
|
@ -187,6 +187,46 @@ add_task(function* formHistory() {
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* composition() {
|
||||
yield setUp();
|
||||
|
||||
let state = yield msg("startComposition", { data: "" });
|
||||
checkState(state, "", [], -1);
|
||||
state = yield msg("updateComposition", { data: "x" });
|
||||
checkState(state, "", [], -1);
|
||||
state = yield msg("changeComposition", { data: "x", waitForSuggestions: true });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1);
|
||||
|
||||
// Mouse over the first suggestion.
|
||||
state = yield msg("mousemove", 0);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 0);
|
||||
|
||||
// Mouse over the second suggestion.
|
||||
state = yield msg("mousemove", 1);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 1);
|
||||
|
||||
// Click the second suggestion. This should make it sticky. To make sure it
|
||||
// sticks, trigger suggestions again and cycle through them by pressing Down
|
||||
// until nothing is selected again.
|
||||
state = yield msg("mousedown", 1);
|
||||
|
||||
checkState(state, "xbar", [], -1);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
|
||||
checkState(state, "xbar", ["xbarfoo", "xbarbar"], -1);
|
||||
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xbarfoo", ["xbarfoo", "xbarbar"], 0);
|
||||
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xbarbar", ["xbarfoo", "xbarbar"], 1);
|
||||
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xbar", ["xbarfoo", "xbarbar"], -1);
|
||||
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
|
||||
let gDidInitialSetUp = false;
|
||||
|
||||
|
@ -27,6 +27,33 @@ let messageHandlers = {
|
||||
wait(ack);
|
||||
},
|
||||
|
||||
startComposition: function (arg) {
|
||||
let data = typeof(arg) == "string" ? arg : arg.data;
|
||||
content.synthesizeComposition({ type: "compositionstart", data: data });
|
||||
ack();
|
||||
},
|
||||
|
||||
updateComposition: function (arg) {
|
||||
let data = typeof(arg) == "string" ? arg : arg.data;
|
||||
content.synthesizeComposition({ type: "compositionupdate", data: data });
|
||||
ack();
|
||||
},
|
||||
|
||||
changeComposition: function (arg) {
|
||||
let data = typeof(arg) == "string" ? arg : arg.data;
|
||||
content.synthesizeCompositionChange({
|
||||
composition: {
|
||||
string: data,
|
||||
clauses: [
|
||||
{ length: data.length, attr: content.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
caret: { start: data.length, length: 0 }
|
||||
});
|
||||
let wait = arg.waitForSuggestions ? waitForSuggestions : cb => cb();
|
||||
wait(ack);
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
gController.input.focus();
|
||||
ack();
|
||||
|
@ -479,11 +479,7 @@ pref("app.update.silent", true);
|
||||
pref("app.update.staging.enabled", true);
|
||||
|
||||
// Update service URL:
|
||||
#ifndef RELEASE_BUILD
|
||||
pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
|
||||
#else
|
||||
pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
|
||||
#endif
|
||||
|
||||
// Show the Update Checking/Ready UI when the user was idle for x seconds
|
||||
pref("app.update.idletime", 60);
|
||||
|
@ -172,7 +172,7 @@ AC_PROG_CXX
|
||||
|
||||
AC_CHECK_PROGS(RANLIB, "${target_alias}-ranlib" "${target}-ranlib", :)
|
||||
AC_CHECK_PROGS(AR, "${target_alias}-ar" "${target}-ar", :)
|
||||
MOZ_PATH_PROGS(AS, "${target_alias}-as" "${target}-as", :)
|
||||
AC_CHECK_PROGS(AS, "${target_alias}-as" "${target}-as", :)
|
||||
AC_CHECK_PROGS(LD, "${target_alias}-ld" "${target}-ld", :)
|
||||
AC_CHECK_PROGS(STRIP, "${target_alias}-strip" "${target}-strip", :)
|
||||
AC_CHECK_PROGS(WINDRES, "${target_alias}-windres" "${target}-windres", :)
|
||||
|
@ -533,7 +533,7 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
|
||||
|
||||
dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
|
||||
dnl not something else like "magnetic tape manipulation utility".
|
||||
MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'`
|
||||
MSMT_TOOL=`${MT-mt} 2>&1|grep 'Microsoft (R) Manifest Tool'`
|
||||
if test -z "$MSMT_TOOL"; then
|
||||
AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.])
|
||||
fi
|
||||
|
@ -1803,6 +1803,12 @@ Element::IsLabelable() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Element::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
css::StyleRule*
|
||||
Element::GetInlineStyleRule()
|
||||
{
|
||||
|
@ -284,6 +284,11 @@ public:
|
||||
*/
|
||||
virtual bool IsLabelable() const;
|
||||
|
||||
/**
|
||||
* Returns if the element is interactive content as per HTML specification.
|
||||
*/
|
||||
virtual bool IsInteractiveHTMLContent() const;
|
||||
|
||||
/**
|
||||
* Is the attribute named stored in the mapped attributes?
|
||||
*
|
||||
|
@ -235,6 +235,36 @@ MultipartFileImpl::GetMozFullPathInternal(nsAString& aFilename,
|
||||
blobImpl->GetMozFullPathInternal(aFilename, aRv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MultipartFileImpl::SetMutable(bool aMutable)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// This looks a little sketchy since FileImpl objects are supposed to be
|
||||
// threadsafe. However, we try to enforce that all FileImpl objects must be
|
||||
// set to immutable *before* being passed to another thread, so this should
|
||||
// be safe.
|
||||
if (!aMutable && !mImmutable && !mBlobImpls.IsEmpty()) {
|
||||
for (uint32_t index = 0, count = mBlobImpls.Length();
|
||||
index < count;
|
||||
index++) {
|
||||
rv = mBlobImpls[index]->SetMutable(aMutable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = FileImplBase::SetMutable(aMutable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(!aMutable, mImmutable);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MultipartFileImpl::InitializeChromeFile(File& aBlob,
|
||||
const ChromeFilePropertyBag& aBag,
|
||||
|
@ -101,6 +101,9 @@ public:
|
||||
virtual void GetMozFullPathInternal(nsAString& aFullPath,
|
||||
ErrorResult& aRv) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
SetMutable(bool aMutable) MOZ_OVERRIDE;
|
||||
|
||||
void SetName(const nsAString& aName)
|
||||
{
|
||||
mName = aName;
|
||||
|
@ -5252,6 +5252,7 @@ nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
nsCOMPtr<Element> container = shell->GetCanvasFrame()
|
||||
->GetCustomContentContainer();
|
||||
if (!container) {
|
||||
@ -5276,6 +5277,8 @@ nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
|
||||
new AnonymousContent(clonedElement->AsElement());
|
||||
mAnonymousContents.AppendElement(anonymousContent);
|
||||
|
||||
shell->GetCanvasFrame()->ShowCustomContentContainer();
|
||||
|
||||
return anonymousContent.forget();
|
||||
}
|
||||
|
||||
@ -5289,6 +5292,7 @@ nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
nsCOMPtr<Element> container = shell->GetCanvasFrame()
|
||||
->GetCustomContentContainer();
|
||||
if (!container) {
|
||||
@ -5314,6 +5318,9 @@ nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mAnonymousContents.IsEmpty()) {
|
||||
shell->GetCanvasFrame()->HideCustomContentContainer();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -1477,7 +1477,7 @@ void
|
||||
nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
|
||||
const nsAString& aType,
|
||||
bool aLengthComputable,
|
||||
uint64_t aLoaded, uint64_t aTotal)
|
||||
int64_t aLoaded, int64_t aTotal)
|
||||
{
|
||||
NS_ASSERTION(aTarget, "null target");
|
||||
NS_ASSERTION(!aType.IsEmpty(), "missing event type");
|
||||
@ -1497,7 +1497,7 @@ nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
|
||||
init.mCancelable = false;
|
||||
init.mLengthComputable = aLengthComputable;
|
||||
init.mLoaded = aLoaded;
|
||||
init.mTotal = (aTotal == UINT64_MAX) ? 0 : aTotal;
|
||||
init.mTotal = (aTotal == -1) ? 0 : aTotal;
|
||||
|
||||
nsRefPtr<ProgressEvent> event =
|
||||
ProgressEvent::Constructor(aTarget, aType, init);
|
||||
@ -2781,10 +2781,15 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
nsAutoCString defaultContentType;
|
||||
nsCOMPtr<nsIInputStream> postDataStream;
|
||||
|
||||
uint64_t size_u64;
|
||||
rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
|
||||
&mUploadTotal, defaultContentType, charset);
|
||||
&size_u64, defaultContentType, charset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// make sure it fits within js MAX_SAFE_INTEGER
|
||||
mUploadTotal =
|
||||
net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
|
||||
|
||||
if (postDataStream) {
|
||||
// If no content type header was set by the client, we set it to
|
||||
// application/xml.
|
||||
@ -3588,18 +3593,18 @@ nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64_t aProgress, uint64_t aProgressMax)
|
||||
nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
|
||||
{
|
||||
// We're uploading if our state is XML_HTTP_REQUEST_OPENED or
|
||||
// XML_HTTP_REQUEST_SENT
|
||||
bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
|
||||
// When uploading, OnProgress reports also headers in aProgress and aProgressMax.
|
||||
// So, try to remove the headers, if possible.
|
||||
bool lengthComputable = (aProgressMax != UINT64_MAX);
|
||||
bool lengthComputable = (aProgressMax != -1);
|
||||
if (upload) {
|
||||
uint64_t loaded = aProgress;
|
||||
int64_t loaded = aProgress;
|
||||
if (lengthComputable) {
|
||||
uint64_t headerSize = aProgressMax - mUploadTotal;
|
||||
int64_t headerSize = aProgressMax - mUploadTotal;
|
||||
loaded -= headerSize;
|
||||
}
|
||||
mUploadLengthComputable = lengthComputable;
|
||||
|
@ -549,7 +549,7 @@ public:
|
||||
void DispatchProgressEvent(mozilla::DOMEventTargetHelper* aTarget,
|
||||
const nsAString& aType,
|
||||
bool aLengthComputable,
|
||||
uint64_t aLoaded, uint64_t aTotal);
|
||||
int64_t aLoaded, int64_t aTotal);
|
||||
|
||||
// Dispatch the "progress" event on the XHR or XHR.upload object if we've
|
||||
// received data since the last "progress" event. Also dispatches
|
||||
@ -724,8 +724,8 @@ protected:
|
||||
uint32_t mState;
|
||||
|
||||
nsRefPtr<nsXMLHttpRequestUpload> mUpload;
|
||||
uint64_t mUploadTransferred;
|
||||
uint64_t mUploadTotal;
|
||||
int64_t mUploadTransferred;
|
||||
int64_t mUploadTotal;
|
||||
bool mUploadLengthComputable;
|
||||
bool mUploadComplete;
|
||||
bool mProgressSinceLastProgressEvent;
|
||||
@ -744,7 +744,7 @@ protected:
|
||||
bool mWarnAboutMultipartHtml;
|
||||
bool mWarnAboutSyncHtml;
|
||||
bool mLoadLengthComputable;
|
||||
uint64_t mLoadTotal; // 0 if not known.
|
||||
int64_t mLoadTotal; // 0 if not known.
|
||||
// Amount of script-exposed (i.e. after undoing gzip compresion) data
|
||||
// received.
|
||||
uint64_t mDataAvailable;
|
||||
@ -755,7 +755,7 @@ protected:
|
||||
// mDataReceived except between the OnProgress that changes mLoadTransferred
|
||||
// and the corresponding OnDataAvailable (which changes mDataReceived).
|
||||
// Ordering of OnProgress and OnDataAvailable is undefined.
|
||||
uint64_t mLoadTransferred;
|
||||
int64_t mLoadTransferred;
|
||||
nsCOMPtr<nsITimer> mProgressNotifier;
|
||||
void HandleProgressTimerCallback();
|
||||
|
||||
|
@ -322,21 +322,13 @@ DOMInterfaces = {
|
||||
'headerFile': 'DeviceStorage.h',
|
||||
},
|
||||
|
||||
'Document': [
|
||||
{
|
||||
'Document': {
|
||||
'nativeType': 'nsIDocument',
|
||||
'binaryNames': {
|
||||
'documentURI': 'documentURIFromJS',
|
||||
'URL': 'documentURIFromJS'
|
||||
}
|
||||
},
|
||||
# Note: we still need the worker descriptor here because
|
||||
# XMLHttpRequest.send() uses it.
|
||||
{
|
||||
'nativeType': 'JSObject',
|
||||
'workers': True,
|
||||
'skipGen': True
|
||||
}],
|
||||
|
||||
'DOMException': {
|
||||
'binaryNames': {
|
||||
@ -431,15 +423,9 @@ DOMInterfaces = {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'FormData': [
|
||||
{
|
||||
'FormData': {
|
||||
'nativeType': 'nsFormData'
|
||||
},
|
||||
{
|
||||
'workers': True,
|
||||
'skipGen': True,
|
||||
'nativeType': 'JSObject'
|
||||
}],
|
||||
|
||||
'Geolocation': {
|
||||
'headerFile': 'nsGeolocation.h'
|
||||
@ -622,13 +608,11 @@ DOMInterfaces = {
|
||||
'InstallPhaseEvent': {
|
||||
'headerFile': 'ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::InstallPhaseEvent',
|
||||
'workers': True
|
||||
},
|
||||
|
||||
'InstallEvent': {
|
||||
'headerFile': 'ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::InstallEvent',
|
||||
'workers': True
|
||||
},
|
||||
|
||||
'KeyEvent': {
|
||||
@ -649,23 +633,10 @@ DOMInterfaces = {
|
||||
'headerFile': 'nsIMediaList.h',
|
||||
},
|
||||
|
||||
'MediaSource': [{
|
||||
},
|
||||
{
|
||||
'nativeType': 'JSObject',
|
||||
'workers': True,
|
||||
'skipGen': True
|
||||
}],
|
||||
|
||||
'MediaStream': [{
|
||||
'MediaStream': {
|
||||
'headerFile': 'DOMMediaStream.h',
|
||||
'nativeType': 'mozilla::DOMMediaStream'
|
||||
},
|
||||
{
|
||||
'nativeType': 'JSObject',
|
||||
'workers': True,
|
||||
'skipGen': True
|
||||
}],
|
||||
|
||||
'MediaStreamAudioDestinationNode': {
|
||||
'binaryNames': { 'stream': 'DOMStream' }
|
||||
|
@ -6832,7 +6832,29 @@ class CGMethodCall(CGThing):
|
||||
argConversionStartsAt=argConversionStartsAt,
|
||||
isConstructor=isConstructor)
|
||||
|
||||
signatures = method.signatures()
|
||||
def filteredSignatures(signatures, descriptor):
|
||||
def typeExposedInWorkers(type):
|
||||
return (not type.isGeckoInterface() or
|
||||
type.inner.isExternal() or
|
||||
type.inner.isExposedInAnyWorker())
|
||||
if descriptor.workers:
|
||||
# Filter out the signatures that should not be exposed in a
|
||||
# worker. The IDL parser enforces the return value being
|
||||
# exposed correctly, but we have to check the argument types.
|
||||
assert all(typeExposedInWorkers(sig[0]) for sig in signatures)
|
||||
signatures = filter(
|
||||
lambda sig: all(typeExposedInWorkers(arg.type)
|
||||
for arg in sig[1]),
|
||||
signatures)
|
||||
if len(signatures) == 0:
|
||||
raise TypeError("%s.%s has a worker binding with no "
|
||||
"signatures that take arguments exposed in "
|
||||
"workers." %
|
||||
(descriptor.interface.identifier.name,
|
||||
method.identifier.name))
|
||||
return signatures
|
||||
|
||||
signatures = filteredSignatures(method.signatures(), descriptor)
|
||||
if len(signatures) == 1:
|
||||
# Special case: we can just do a per-signature method call
|
||||
# here for our one signature and not worry about switching
|
||||
@ -6859,14 +6881,17 @@ class CGMethodCall(CGThing):
|
||||
|
||||
argCountCases = []
|
||||
for argCountIdx, argCount in enumerate(allowedArgCounts):
|
||||
possibleSignatures = method.signaturesForArgCount(argCount)
|
||||
possibleSignatures = filteredSignatures(
|
||||
method.signaturesForArgCount(argCount),
|
||||
descriptor)
|
||||
|
||||
# Try to optimize away cases when the next argCount in the list
|
||||
# will have the same code as us; if it does, we can fall through to
|
||||
# that case.
|
||||
if argCountIdx+1 < len(allowedArgCounts):
|
||||
nextPossibleSignatures = \
|
||||
method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
|
||||
nextPossibleSignatures = filteredSignatures(
|
||||
method.signaturesForArgCount(allowedArgCounts[argCountIdx+1]),
|
||||
descriptor)
|
||||
else:
|
||||
nextPossibleSignatures = None
|
||||
if possibleSignatures == nextPossibleSignatures:
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "mozilla/TextComposition.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/HTMLFormElement.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
|
||||
#include "HTMLInputElement.h"
|
||||
#include "IMEContentObserver.h"
|
||||
@ -389,27 +388,6 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
IMEState newState = GetNewIMEState(aPresContext, aContent);
|
||||
|
||||
// In e10s, remote content may have IME focus. The main process (i.e. this process)
|
||||
// would attempt to set state to DISABLED if, for example, the user clicks
|
||||
// some other remote content. The content process would later re-ENABLE IME, meaning
|
||||
// that all state-changes were unnecessary.
|
||||
// Here we filter the common case where the main process knows that the remote
|
||||
// process controls IME focus. The DISABLED->re-ENABLED progression can
|
||||
// still happen since remote content may be concurrently communicating its claim
|
||||
// on focus to the main process... but this cannot cause bugs like missed keypresses.
|
||||
// (It just means a lot of needless IPC.)
|
||||
if ((newState.mEnabled == IMEState::DISABLED) && TabParent::GetIMETabParent()) {
|
||||
PR_LOG(sISMLog, PR_LOG_DEBUG,
|
||||
("ISM: IMEStateManager::OnChangeFocusInternal(), "
|
||||
"Parent process cancels to set DISABLED state because the content process "
|
||||
"has IME focus and has already sets IME state"));
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"TabParent::GetIMETabParent() should never return non-null value "
|
||||
"in the content process");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!focusActuallyChanging) {
|
||||
// actual focus isn't changing, but if IME enabled state is changing,
|
||||
// we should do it.
|
||||
|
@ -151,23 +151,35 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
request->SetCredentialsMode(credentials);
|
||||
}
|
||||
|
||||
// Request constructor step 14.
|
||||
if (aInit.mMethod.WasPassed()) {
|
||||
nsCString method = aInit.mMethod.Value();
|
||||
ToLowerCase(method);
|
||||
nsAutoCString method(aInit.mMethod.Value());
|
||||
nsAutoCString upperCaseMethod = method;
|
||||
ToUpperCase(upperCaseMethod);
|
||||
|
||||
if (!method.EqualsASCII("options") &&
|
||||
!method.EqualsASCII("get") &&
|
||||
!method.EqualsASCII("head") &&
|
||||
!method.EqualsASCII("post") &&
|
||||
!method.EqualsASCII("put") &&
|
||||
!method.EqualsASCII("delete")) {
|
||||
// Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
|
||||
// token, since HTTP states that Method may be any of the defined values or
|
||||
// a token (extension method).
|
||||
if (upperCaseMethod.EqualsLiteral("CONNECT") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACE") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACK") ||
|
||||
!NS_IsValidHTTPToken(method)) {
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ToUpperCase(method);
|
||||
request->SetMethod(method);
|
||||
// Step 14.2
|
||||
if (upperCaseMethod.EqualsLiteral("DELETE") ||
|
||||
upperCaseMethod.EqualsLiteral("GET") ||
|
||||
upperCaseMethod.EqualsLiteral("HEAD") ||
|
||||
upperCaseMethod.EqualsLiteral("POST") ||
|
||||
upperCaseMethod.EqualsLiteral("PUT") ||
|
||||
upperCaseMethod.EqualsLiteral("OPTIONS")) {
|
||||
request->SetMethod(upperCaseMethod);
|
||||
} else {
|
||||
request->SetMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
|
||||
|
@ -42,6 +42,12 @@ public:
|
||||
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
|
||||
virtual bool Draggable() const MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLAnchorElement
|
||||
NS_DECL_NSIDOMHTMLANCHORELEMENT
|
||||
|
||||
|
@ -40,6 +40,12 @@ HTMLAudioElement::~HTMLAudioElement()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLAudioElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
|
||||
}
|
||||
|
||||
already_AddRefed<HTMLAudioElement>
|
||||
HTMLAudioElement::Audio(const GlobalObject& aGlobal,
|
||||
const Optional<nsAString>& aSrc,
|
||||
|
@ -23,6 +23,9 @@ public:
|
||||
|
||||
explicit HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo);
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
// nsIDOMHTMLMediaElement
|
||||
using HTMLMediaElement::GetPaused;
|
||||
|
||||
|
@ -36,6 +36,12 @@ public:
|
||||
|
||||
NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLButtonElement, button)
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLButtonElement
|
||||
NS_DECL_NSIDOMHTMLBUTTONELEMENT
|
||||
|
||||
|
@ -26,6 +26,12 @@ public:
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLIFrameElement
|
||||
NS_DECL_NSIDOMHTMLIFRAMEELEMENT
|
||||
|
||||
|
@ -148,6 +148,12 @@ NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
|
||||
NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
|
||||
NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
|
||||
|
||||
bool
|
||||
HTMLImageElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::IsSrcsetEnabled()
|
||||
{
|
||||
|
@ -46,6 +46,9 @@ public:
|
||||
|
||||
virtual bool Draggable() const MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
// nsIDOMHTMLImageElement
|
||||
NS_DECL_NSIDOMHTMLIMAGEELEMENT
|
||||
|
||||
|
@ -3211,6 +3211,12 @@ HTMLInputElement::Focus(ErrorResult& aError)
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLInputElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return mType != NS_FORM_INPUT_HIDDEN;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLInputElement::Select()
|
||||
{
|
||||
|
@ -119,6 +119,9 @@ public:
|
||||
virtual void Blur(ErrorResult& aError) MOZ_OVERRIDE;
|
||||
virtual void Focus(ErrorResult& aError) MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
// nsIDOMHTMLInputElement
|
||||
NS_DECL_NSIDOMHTMLINPUTELEMENT
|
||||
|
||||
|
@ -83,19 +83,14 @@ HTMLLabelElement::Focus(ErrorResult& aError)
|
||||
}
|
||||
|
||||
static bool
|
||||
EventTargetIn(WidgetEvent* aEvent, nsIContent* aChild, nsIContent* aStop)
|
||||
InInteractiveHTMLContent(nsIContent* aContent, nsIContent* aStop)
|
||||
{
|
||||
nsCOMPtr<nsIContent> c = do_QueryInterface(aEvent->target);
|
||||
nsIContent *content = c;
|
||||
while (content) {
|
||||
if (content == aChild) {
|
||||
nsIContent* content = aContent;
|
||||
while (content && content != aStop) {
|
||||
if (content->IsElement() &&
|
||||
content->AsElement()->IsInteractiveHTMLContent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (content == aStop) {
|
||||
break;
|
||||
}
|
||||
|
||||
content = content->GetParent();
|
||||
}
|
||||
return false;
|
||||
@ -115,10 +110,15 @@ HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> target = do_QueryInterface(aVisitor.mEvent->target);
|
||||
if (InInteractiveHTMLContent(target, this)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Strong ref because event dispatch is going to happen.
|
||||
nsRefPtr<Element> content = GetLabeledElement();
|
||||
|
||||
if (content && !EventTargetIn(aVisitor.mEvent, content, this)) {
|
||||
if (content) {
|
||||
mHandlingEvent = true;
|
||||
switch (aVisitor.mEvent->message) {
|
||||
case NS_MOUSE_BUTTON_DOWN:
|
||||
|
@ -32,6 +32,12 @@ public:
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLLabelElement
|
||||
NS_DECL_NSIDOMHTMLLABELELEMENT
|
||||
|
||||
|
@ -434,6 +434,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
|
||||
@ -2887,6 +2888,25 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||
|
||||
mSrcStream = aStream;
|
||||
|
||||
nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we have access to |mSrcStream| we can pipe it to our shadow
|
||||
// version |mPlaybackStream|. If two media elements are playing the
|
||||
// same realtime DOMMediaStream, this allows them to pause playback
|
||||
// independently of each other.
|
||||
mPlaybackStream = DOMMediaStream::CreateTrackUnionStream(window);
|
||||
mPlaybackStreamInputPort = mPlaybackStream->GetStream()->AsProcessedStream()->
|
||||
AllocateInputPort(mSrcStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||
|
||||
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
|
||||
mPlaybackStream->CombineWithPrincipal(principal);
|
||||
|
||||
// Let |mSrcStream| decide when the stream has finished.
|
||||
GetSrcMediaStream()->AsProcessedStream()->SetAutofinish(true);
|
||||
|
||||
nsRefPtr<MediaStream> stream = mSrcStream->GetStream();
|
||||
if (stream) {
|
||||
stream->SetAudioChannelType(mAudioChannel);
|
||||
@ -2933,6 +2953,8 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||
}
|
||||
mSrcStream->DisconnectTrackListListeners(AudioTracks(), VideoTracks());
|
||||
|
||||
mPlaybackStreamInputPort->Destroy();
|
||||
|
||||
// Kill its reference to this element
|
||||
mSrcStreamListener->Forget();
|
||||
mSrcStreamListener = nullptr;
|
||||
@ -2953,6 +2975,8 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||
stream->ChangeExplicitBlockerCount(-1);
|
||||
}
|
||||
mSrcStream = nullptr;
|
||||
mPlaybackStreamInputPort = nullptr;
|
||||
mPlaybackStream = nullptr;
|
||||
}
|
||||
|
||||
void HTMLMediaElement::ProcessMediaFragmentURI()
|
||||
|
@ -338,8 +338,8 @@ public:
|
||||
|
||||
MediaStream* GetSrcMediaStream() const
|
||||
{
|
||||
NS_ASSERTION(mSrcStream, "Don't call this when not playing a stream");
|
||||
return mSrcStream->GetStream();
|
||||
NS_ASSERTION(mPlaybackStream, "Don't call this when not playing a stream");
|
||||
return mPlaybackStream->GetStream();
|
||||
}
|
||||
|
||||
// WebIDL
|
||||
@ -997,6 +997,14 @@ protected:
|
||||
// At most one of mDecoder and mSrcStream can be non-null.
|
||||
nsRefPtr<DOMMediaStream> mSrcStream;
|
||||
|
||||
// Holds a reference to a MediaInputPort connecting mSrcStream to mPlaybackStream.
|
||||
nsRefPtr<MediaInputPort> mPlaybackStreamInputPort;
|
||||
|
||||
// Holds a reference to a stream with mSrcStream as input but intended for
|
||||
// playback. Used so we don't block playback of other video elements
|
||||
// playing the same mSrcStream.
|
||||
nsRefPtr<DOMMediaStream> mPlaybackStream;
|
||||
|
||||
// Holds references to the DOM wrappers for the MediaStreams that we're
|
||||
// writing to.
|
||||
struct OutputMediaStream {
|
||||
|
@ -45,6 +45,12 @@ HTMLObjectElement::~HTMLObjectElement()
|
||||
DestroyImageLoadingContent();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLObjectElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLObjectElement::IsDoneAddingChildren()
|
||||
{
|
||||
|
@ -30,6 +30,9 @@ public:
|
||||
|
||||
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
// nsIDOMHTMLObjectElement
|
||||
NS_DECL_NSIDOMHTMLOBJECTELEMENT
|
||||
|
||||
|
@ -147,6 +147,12 @@ public:
|
||||
|
||||
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLSelectElement
|
||||
NS_DECL_NSIDOMHTMLSELECTELEMENT
|
||||
|
||||
|
@ -53,6 +53,12 @@ public:
|
||||
|
||||
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIDOMHTMLTextAreaElement
|
||||
NS_DECL_NSIDOMHTMLTEXTAREAELEMENT
|
||||
|
||||
|
@ -126,6 +126,12 @@ nsresult HTMLVideoElement::SetAcceptHeader(nsIHttpChannel* aChannel)
|
||||
false);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLVideoElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
|
||||
}
|
||||
|
||||
uint32_t HTMLVideoElement::MozParsedFrames() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
@ -49,6 +49,9 @@ public:
|
||||
|
||||
virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
|
||||
|
||||
// Element
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
// WebIDL
|
||||
|
||||
uint32_t Width() const
|
||||
|
@ -1791,6 +1791,15 @@ nsGenericHTMLElement::IsLabelable() const
|
||||
Tag() == nsGkAtoms::meter;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGenericHTMLElement::IsInteractiveHTMLContent() const
|
||||
{
|
||||
return Tag() == nsGkAtoms::details ||
|
||||
Tag() == nsGkAtoms::embed ||
|
||||
Tag() == nsGkAtoms::keygen ||
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex);
|
||||
}
|
||||
|
||||
already_AddRefed<UndoManager>
|
||||
nsGenericHTMLElement::GetUndoManager()
|
||||
{
|
||||
|
@ -904,6 +904,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool IsLabelable() const MOZ_OVERRIDE;
|
||||
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
|
||||
|
||||
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
|
||||
|
||||
|
@ -64,6 +64,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_input_untrusted_key_events.html]
|
||||
[test_input_url.html]
|
||||
[test_interactive_content_in_label.html]
|
||||
[test_label_control_attribute.html]
|
||||
[test_label_input_controls.html]
|
||||
[test_max_attribute.html]
|
||||
@ -81,6 +82,7 @@ skip-if = e10s
|
||||
[test_output_element.html]
|
||||
[test_pattern_attribute.html]
|
||||
[test_progress_element.html]
|
||||
[test_radio_in_label.html]
|
||||
[test_radio_radionodelist.html]
|
||||
[test_required_attribute.html]
|
||||
skip-if = e10s
|
||||
|
99
dom/html/test/forms/test_interactive_content_in_label.html
Normal file
99
dom/html/test/forms/test_interactive_content_in_label.html
Normal file
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=229925
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 229925</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229925">Mozilla Bug 229925</a>
|
||||
<p id="display"></p>
|
||||
<form action="#">
|
||||
<label>
|
||||
<span id="text">label</span>
|
||||
<input type="button" id="target" value="target">
|
||||
|
||||
<a id="yes1" href="#">a</a>
|
||||
<audio id="yes2" controls></audio>
|
||||
<button id="yes3">button</button>
|
||||
<details id="yes4">details</details>
|
||||
<embed id="yes5">embed</embed>
|
||||
<iframe id="yes6" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
|
||||
<img id="yes7" src="data:image/png," usemap="#map">
|
||||
<input id="yes8" type="text" size="4">
|
||||
<keygen id="yes9">
|
||||
<label id="yes10">label</label>
|
||||
<object id="yes11" usemap="#map">object</object>
|
||||
<select id="yes12"><option>select</option></select>
|
||||
<textarea id="yes13" cols="1" rows="1"></textarea>
|
||||
<video id="yes14" controls></video>
|
||||
<span id="yes15" tabindex="1">tabindex</span>
|
||||
|
||||
<audio id="no1"></audio>
|
||||
<img id="no2" src="data:image/png,">
|
||||
<input id="no3" type="hidden">
|
||||
<object id="no4">object</object>
|
||||
<video id="no5"></video>
|
||||
</label>
|
||||
</form>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 229925 **/
|
||||
|
||||
var target = document.getElementById("target");
|
||||
|
||||
var yes_nodes = [
|
||||
document.getElementById("yes1"),
|
||||
document.getElementById("yes2"),
|
||||
document.getElementById("yes3"),
|
||||
document.getElementById("yes4"),
|
||||
document.getElementById("yes5"),
|
||||
document.getElementById("yes6"),
|
||||
document.getElementById("yes7"),
|
||||
document.getElementById("yes8"),
|
||||
document.getElementById("yes9"),
|
||||
document.getElementById("yes10"),
|
||||
document.getElementById("yes11"),
|
||||
document.getElementById("yes12"),
|
||||
document.getElementById("yes13"),
|
||||
document.getElementById("yes14"),
|
||||
document.getElementById("yes15"),
|
||||
];
|
||||
|
||||
var no_nodes = [
|
||||
document.getElementById("text"),
|
||||
document.getElementById("no1"),
|
||||
document.getElementById("no2"),
|
||||
document.getElementById("no3"),
|
||||
document.getElementById("no4"),
|
||||
document.getElementById("no5"),
|
||||
];
|
||||
|
||||
var target_clicked = false;
|
||||
target.addEventListener("click", function() {
|
||||
target_clicked = true;
|
||||
});
|
||||
|
||||
var node;
|
||||
for (node of yes_nodes) {
|
||||
target_clicked = false;
|
||||
node.click();
|
||||
is(target_clicked, false, "mouse click on interactive content " + node.nodeName + " shouldn't dispatch event to label target");
|
||||
}
|
||||
|
||||
for (node of no_nodes) {
|
||||
target_clicked = false;
|
||||
node.click();
|
||||
is(target_clicked, true, "mouse click on non interactive content " + node.nodeName + " should dispatch event to label target");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
51
dom/html/test/forms/test_radio_in_label.html
Normal file
51
dom/html/test/forms/test_radio_in_label.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=229925
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 229925</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229925">Mozilla Bug 229925</a>
|
||||
<p id="display"></p>
|
||||
<form>
|
||||
<label>
|
||||
<span id="s1">LABEL</span>
|
||||
<input type="radio" name="rdo" value="1" id="r1" onmousedown="document.body.appendChild(document.createTextNode('down'));">
|
||||
<input type="radio" name="rdo" value="2" id="r2" checked="checked">
|
||||
</label>
|
||||
</form>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 229925 **/
|
||||
var r1 = document.getElementById("r1");
|
||||
var r2 = document.getElementById("r2");
|
||||
var s1 = document.getElementById("s1");
|
||||
|
||||
r1.click();
|
||||
ok(r1.checked,
|
||||
"The first radio input element should be checked by clicking the element");
|
||||
r2.click();
|
||||
ok(r2.checked,
|
||||
"The second radio input element should be checked by clicking the element");
|
||||
s1.click();
|
||||
ok(r1.checked,
|
||||
"The first radio input element should be checked by clicking other element");
|
||||
|
||||
r1.focus();
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
ok(r2.checked,
|
||||
"The second radio input element should be checked by key");
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
ok(r1.checked,
|
||||
"The first radio input element should be checked by key");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -124,8 +124,12 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_blob_worker_xhr_post.html]
|
||||
skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
|
||||
[test_blob_worker_xhr_post_multifile.html]
|
||||
skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
|
||||
[test_blob_worker_xhr_read.html]
|
||||
skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
|
||||
[test_blob_worker_xhr_read_slice.html]
|
||||
skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
|
||||
[test_blocked_order.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_bug937006.html]
|
||||
|
113
dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
Normal file
113
dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
Normal file
@ -0,0 +1,113 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
/**
|
||||
* Create a composite/multi-file Blob on the worker, then post it as an XHR
|
||||
* payload and ensure that we don't hang/generate an assertion/etc. but
|
||||
* instead generate the expected 404. This test is basically the same as
|
||||
* test_blob_worker_xhr_post.html except for the composite Blob.
|
||||
*/
|
||||
function testSteps()
|
||||
{
|
||||
const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
|
||||
const BLOB_TYPE = "text/plain";
|
||||
const BLOB_SIZE = BLOB_DATA.join("").length;
|
||||
|
||||
info("Setting up");
|
||||
|
||||
let request = indexedDB.open(window.location.pathname, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
let event = yield undefined;
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
ok(db, "Created database");
|
||||
|
||||
info("Creating objectStore");
|
||||
|
||||
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
|
||||
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
ok(true, "Opened database");
|
||||
|
||||
let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE });
|
||||
|
||||
info("Adding blob to database");
|
||||
|
||||
objectStore = db.transaction("foo", "readwrite").objectStore("foo");
|
||||
objectStore.add(blob).onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
let blobKey = event.target.result;
|
||||
ok(blobKey, "Got a key for the blob");
|
||||
|
||||
info("Getting blob from the database");
|
||||
|
||||
objectStore = db.transaction("foo").objectStore("foo");
|
||||
objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
blob = event.target.result;
|
||||
|
||||
ok(blob instanceof Blob, "Got a blob");
|
||||
is(blob.size, BLOB_SIZE, "Correct size");
|
||||
is(blob.type, BLOB_TYPE, "Correct type");
|
||||
|
||||
function workerScript() {
|
||||
onmessage = function(event) {
|
||||
var blob = event.data;
|
||||
var compositeBlob = new Blob(["preceding string. ", blob],
|
||||
{ type: "text/plain" });
|
||||
var xhr = new XMLHttpRequest();
|
||||
// We just want to make sure the error case doesn't fire; it's fine for
|
||||
// us to just want a 404.
|
||||
xhr.open('POST', 'http://mochi.test:8888/does-not-exist', true);
|
||||
xhr.onload = function() {
|
||||
postMessage({ status: xhr.status });
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
postMessage({ status: 'error' });
|
||||
}
|
||||
xhr.send(compositeBlob);
|
||||
}
|
||||
}
|
||||
|
||||
let workerScriptUrl =
|
||||
URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
|
||||
|
||||
let xhrWorker = new Worker(workerScriptUrl);
|
||||
xhrWorker.postMessage(blob);
|
||||
xhrWorker.onmessage = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.data.status, 404, "XHR generated the expected 404");
|
||||
xhrWorker.terminate();
|
||||
|
||||
URL.revokeObjectURL(workerScriptUrl);
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
116
dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
Normal file
116
dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
Normal file
@ -0,0 +1,116 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Blob Read From Worker</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
/**
|
||||
* Create an IndexedDB-backed Blob, send it to the worker, try and read the
|
||||
* *SLICED* contents of the Blob from the worker using an XHR. This is
|
||||
* (as of the time of writing this) basically the same as
|
||||
* test_blob_worker_xhr_read.html but with slicing added.
|
||||
*/
|
||||
function testSteps()
|
||||
{
|
||||
const BLOB_DATA = ["Green"];
|
||||
const BLOB_TYPE = "text/plain";
|
||||
const BLOB_SIZE = BLOB_DATA.join("").length;
|
||||
|
||||
info("Setting up");
|
||||
|
||||
let request = indexedDB.open(window.location.pathname, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
let event = yield undefined;
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
ok(db, "Created database");
|
||||
|
||||
info("Creating objectStore");
|
||||
|
||||
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
|
||||
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
ok(true, "Opened database");
|
||||
|
||||
let blob = new Blob(BLOB_DATA, { type: BLOB_TYPE });
|
||||
|
||||
info("Adding blob to database");
|
||||
|
||||
objectStore = db.transaction("foo", "readwrite").objectStore("foo");
|
||||
objectStore.add(blob).onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
let blobKey = event.target.result;
|
||||
ok(blobKey, "Got a key for the blob");
|
||||
|
||||
info("Getting blob from the database");
|
||||
|
||||
objectStore = db.transaction("foo").objectStore("foo");
|
||||
objectStore.get(blobKey).onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
blob = event.target.result;
|
||||
|
||||
ok(blob instanceof Blob, "Got a blob");
|
||||
is(blob.size, BLOB_SIZE, "Correct size");
|
||||
is(blob.type, BLOB_TYPE, "Correct type");
|
||||
|
||||
info("Sending blob to a worker");
|
||||
|
||||
function workerScript() {
|
||||
onmessage = function(event) {
|
||||
var blob = event.data;
|
||||
var slicedBlob = blob.slice(0, 3, "text/plain");
|
||||
var blobUrl = URL.createObjectURL(slicedBlob);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', blobUrl, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onload = function() {
|
||||
postMessage({ data: xhr.response });
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
postMessage({ data: null });
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
let workerScriptUrl =
|
||||
URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
|
||||
|
||||
let xhrWorker = new Worker(workerScriptUrl);
|
||||
xhrWorker.postMessage(blob);
|
||||
xhrWorker.onmessage = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.data.data, "Gre", "XHR returned expected sliced payload.");
|
||||
xhrWorker.terminate();
|
||||
|
||||
URL.revokeObjectURL(workerScriptUrl);
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
@ -1923,7 +1923,11 @@ public:
|
||||
virtual int64_t
|
||||
GetFileId() MOZ_OVERRIDE;
|
||||
|
||||
virtual int64_t GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
|
||||
virtual int64_t
|
||||
GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
SetMutable(bool aMutable) MOZ_OVERRIDE;
|
||||
|
||||
virtual BlobChild*
|
||||
GetBlobChild() MOZ_OVERRIDE;
|
||||
@ -2005,6 +2009,17 @@ public:
|
||||
return mStart;
|
||||
}
|
||||
|
||||
void
|
||||
EnsureActorWasCreated()
|
||||
{
|
||||
MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
|
||||
mActorWasCreated);
|
||||
|
||||
if (!mActorWasCreated) {
|
||||
EnsureActorWasCreatedInternal();
|
||||
}
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
virtual BlobChild*
|
||||
@ -2013,6 +2028,9 @@ public:
|
||||
private:
|
||||
~RemoteBlobSliceImpl()
|
||||
{ }
|
||||
|
||||
void
|
||||
EnsureActorWasCreatedInternal();
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
@ -2390,6 +2408,26 @@ RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
|
||||
return mLastModificationDate;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BlobChild::
|
||||
RemoteBlobImpl::SetMutable(bool aMutable)
|
||||
{
|
||||
if (!aMutable && IsSlice()) {
|
||||
// Make sure that slices are backed by a real actor now while we are still
|
||||
// on the correct thread.
|
||||
AsSlice()->EnsureActorWasCreated();
|
||||
}
|
||||
|
||||
nsresult rv = FileImplBase::SetMutable(aMutable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(!aMutable, mImmutable);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
BlobChild*
|
||||
BlobChild::
|
||||
RemoteBlobImpl::GetBlobChild()
|
||||
@ -2572,21 +2610,12 @@ RemoteBlobSliceImpl::RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
|
||||
mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
|
||||
BlobChild::RemoteBlobImpl)
|
||||
|
||||
BlobChild*
|
||||
void
|
||||
BlobChild::
|
||||
RemoteBlobSliceImpl::GetBlobChild()
|
||||
RemoteBlobSliceImpl::EnsureActorWasCreatedInternal()
|
||||
{
|
||||
MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
|
||||
mActorWasCreated);
|
||||
|
||||
if (mActorWasCreated) {
|
||||
return RemoteBlobImpl::GetBlobChild();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
|
||||
MOZ_ASSERT(!mActorWasCreated);
|
||||
|
||||
mActorWasCreated = true;
|
||||
|
||||
@ -2611,8 +2640,18 @@ RemoteBlobSliceImpl::GetBlobChild()
|
||||
mActor =
|
||||
SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
|
||||
}
|
||||
}
|
||||
|
||||
return mActor;
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
|
||||
BlobChild::RemoteBlobImpl)
|
||||
|
||||
BlobChild*
|
||||
BlobChild::
|
||||
RemoteBlobSliceImpl::GetBlobChild()
|
||||
{
|
||||
EnsureActorWasCreated();
|
||||
|
||||
return RemoteBlobImpl::GetBlobChild();
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -282,13 +282,13 @@ parent:
|
||||
int32_t IMEOpen,
|
||||
intptr_t NativeIMEContext);
|
||||
|
||||
prio(urgent) sync SetInputContext(int32_t IMEEnabled,
|
||||
int32_t IMEOpen,
|
||||
nsString type,
|
||||
nsString inputmode,
|
||||
nsString actionHint,
|
||||
int32_t cause,
|
||||
int32_t focusChange);
|
||||
prio(urgent) async SetInputContext(int32_t IMEEnabled,
|
||||
int32_t IMEOpen,
|
||||
nsString type,
|
||||
nsString inputmode,
|
||||
nsString actionHint,
|
||||
int32_t cause,
|
||||
int32_t focusChange);
|
||||
|
||||
sync IsParentWindowMainWidgetVisible() returns (bool visible);
|
||||
|
||||
|
@ -2028,25 +2028,14 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
|
||||
const int32_t& aCause,
|
||||
const int32_t& aFocusChange)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget || !AllowContentIME()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
InputContext oldContext = widget->GetInputContext();
|
||||
|
||||
// Ignore if current widget IME setting is not DISABLED and didn't come
|
||||
// from remote content. Chrome content may have taken over.
|
||||
if (oldContext.mIMEState.mEnabled != IMEState::DISABLED &&
|
||||
oldContext.IsOriginMainProcess()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus
|
||||
// When the input mode is set to anything but IMEState::DISABLED,
|
||||
// mIMETabParent should be set to this
|
||||
mIMETabParent =
|
||||
aIMEEnabled != static_cast<int32_t>(IMEState::DISABLED) ? this : nullptr;
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget || !AllowContentIME())
|
||||
return true;
|
||||
|
||||
InputContext context;
|
||||
context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
|
||||
@ -2054,8 +2043,6 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
|
||||
context.mHTMLInputType.Assign(aType);
|
||||
context.mHTMLInputInputmode.Assign(aInputmode);
|
||||
context.mActionHint.Assign(aActionHint);
|
||||
context.mOrigin = InputContext::ORIGIN_CONTENT;
|
||||
|
||||
InputContextAction action(
|
||||
static_cast<InputContextAction::Cause>(aCause),
|
||||
static_cast<InputContextAction::FocusChange>(aFocusChange));
|
||||
|
@ -163,12 +163,19 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
|
||||
return NS_OK;
|
||||
}
|
||||
case android::INFO_FORMAT_CHANGED:
|
||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
||||
{
|
||||
// If the format changed, update our cached info.
|
||||
GADM_LOG("Decoder format changed");
|
||||
return Output(aStreamOffset, aOutData);
|
||||
}
|
||||
case android::INFO_OUTPUT_BUFFERS_CHANGED:
|
||||
{
|
||||
GADM_LOG("Info Output Buffers Changed");
|
||||
if (mDecoder->UpdateOutputBuffers()) {
|
||||
return Output(aStreamOffset, aOutData);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
case -EAGAIN:
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -509,6 +509,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
[test_streams_element_capture_playback.html]
|
||||
[test_streams_element_capture_reset.html]
|
||||
[test_streams_gc.html]
|
||||
[test_streams_individual_pause.html]
|
||||
[test_streams_srcObject.html]
|
||||
[test_streams_tracks.html]
|
||||
[test_texttrack.html]
|
||||
|
74
dom/media/test/test_streams_individual_pause.html
Normal file
74
dom/media/test/test_streams_individual_pause.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1073406. Pausing a video element should not pause another playing the same stream.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video id="video1" autoplay></video>
|
||||
<video id="video2" autoplay></video>
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var getVideoImagePixelData = function(v) {
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(v, 0, 0);
|
||||
var imgData = ctx.getImageData(canvas.width/2, canvas.height/2, 1, 1).data;
|
||||
return "r" + imgData[0] +
|
||||
"g" + imgData[1] +
|
||||
"b" + imgData[2] +
|
||||
"a" + imgData[3];
|
||||
}
|
||||
|
||||
navigator.mozGetUserMedia({video: true, fake: true}, function(stream) {
|
||||
var stream = stream;
|
||||
var video1 = document.getElementById('video1');
|
||||
var video2 = document.getElementById('video2');
|
||||
|
||||
var src = URL.createObjectURL(stream);
|
||||
video1.src = src;
|
||||
video2.src = src;
|
||||
|
||||
video1.onplaying = () => video1.pause();
|
||||
|
||||
var v1PausedImageData;
|
||||
var v2PausedImageData;
|
||||
|
||||
video1.onpause = function() {
|
||||
v1PausedImageData = getVideoImagePixelData(video1);
|
||||
v2PausedImageData = getVideoImagePixelData(video2);
|
||||
v2TimesToTest = 3;
|
||||
video2.ontimeupdate = function() {
|
||||
if (getVideoImagePixelData(video2) === v2PausedImageData) {
|
||||
// Wait until video2 has progressed it's video.
|
||||
// If it doesn't, we'll time out and fail.
|
||||
info("video2 has not progressed. Waiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (--v2TimesToTest > 0) {
|
||||
// Wait for a while to be sure video1 would have gotten a frame
|
||||
// if it is playing.
|
||||
info("video2 progressed OK");
|
||||
return;
|
||||
}
|
||||
|
||||
video2.ontimeupdate = null;
|
||||
ok(true, "video2 is playing");
|
||||
isnot(video1.currentTime, video2.currentTime,
|
||||
"v1 and v2 should not be at the same currentTime");
|
||||
is(v1PausedImageData, getVideoImagePixelData(video1),
|
||||
"video1 video frame should not have updated since video1 paused");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
};
|
||||
}, function(error) {
|
||||
ok(false, "getUserMedia should not fail, got " + error.name);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -572,8 +572,8 @@ nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
|
||||
|
||||
NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
|
||||
nsISupports* aContext,
|
||||
uint64_t aProgress,
|
||||
uint64_t aProgressMax)
|
||||
int64_t aProgress,
|
||||
int64_t aProgressMax)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
return rv;
|
||||
|
@ -17,6 +17,7 @@
|
||||
function runTests() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
var scale = utils.screenPixelsPerCSSPixel;
|
||||
|
||||
var plugin1 = document.getElementById("plugin1"); // What we're testing.
|
||||
var plugin2 = document.getElementById("plugin2"); // Dummy.
|
||||
@ -55,8 +56,8 @@
|
||||
is(initialStateUnknown, true, "Initial state should be unknown, assumed false.");
|
||||
|
||||
// Give the plugin focus (the window is already focused).
|
||||
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1);
|
||||
expectedEventCount++;
|
||||
|
||||
is(plugin1.getFocusState(), true, "(1) Plugin should have focus.");
|
||||
@ -78,8 +79,8 @@
|
||||
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
|
||||
|
||||
// Take focus from the plugin.
|
||||
utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseDown, 0, plugin2);
|
||||
utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseUp, 0, plugin2);
|
||||
utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseDown, 0, plugin2);
|
||||
utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseUp, 0, plugin2);
|
||||
expectedEventCount++;
|
||||
|
||||
is(plugin1.getFocusState(), false, "(4) Plugin should not have focus.");
|
||||
@ -89,8 +90,8 @@
|
||||
// changes that took place while the window was inactive.
|
||||
|
||||
// Give the plugin focus (the window is already focused).
|
||||
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1);
|
||||
utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1);
|
||||
expectedEventCount++;
|
||||
|
||||
// Blur the window.
|
||||
|
@ -57,7 +57,7 @@ interface DataStore : EventTarget {
|
||||
};
|
||||
|
||||
partial interface DataStore {
|
||||
[ChromeOnly, Throws]
|
||||
[ChromeOnly, Throws, Exposed=Window]
|
||||
void setDataStoreImpl(DataStoreImpl store);
|
||||
};
|
||||
|
||||
@ -82,7 +82,7 @@ interface DataStoreCursor {
|
||||
};
|
||||
|
||||
partial interface DataStoreCursor {
|
||||
[ChromeOnly]
|
||||
[ChromeOnly, Exposed=Window]
|
||||
void setDataStoreCursorImpl(DataStoreCursorImpl cursor);
|
||||
};
|
||||
|
||||
|
@ -715,12 +715,6 @@ WorkerDataStore::Sync(JSContext* aCx,
|
||||
return workerCursor.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDataStore::SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv)
|
||||
{
|
||||
NS_NOTREACHED("We don't use this for the WorkerDataStore!");
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDataStore::SetBackingDataStore(
|
||||
const nsMainThreadPtrHandle<DataStore>& aBackingStore)
|
||||
|
@ -85,9 +85,6 @@ public:
|
||||
|
||||
IMPL_EVENT_HANDLER(change)
|
||||
|
||||
// We don't use this for the WorkerDataStore.
|
||||
void SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv);
|
||||
|
||||
void SetBackingDataStore(
|
||||
const nsMainThreadPtrHandle<DataStore>& aBackingStore);
|
||||
|
||||
|
@ -187,12 +187,6 @@ WorkerDataStoreCursor::Close(JSContext* aCx, ErrorResult& aRv)
|
||||
runnable->Dispatch(aCx);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDataStoreCursor::SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor)
|
||||
{
|
||||
NS_NOTREACHED("We don't use this for the WorkerDataStoreCursor!");
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDataStoreCursor::SetBackingDataStoreCursor(
|
||||
const nsMainThreadPtrHandle<DataStoreCursor>& aBackingCursor)
|
||||
|
@ -45,9 +45,6 @@ public:
|
||||
|
||||
void Close(JSContext *aCx, ErrorResult& aRv);
|
||||
|
||||
// We don't use this for the WorkerDataStore.
|
||||
void SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor);
|
||||
|
||||
void SetBackingDataStoreCursor(
|
||||
const nsMainThreadPtrHandle<DataStoreCursor>& aBackingCursor);
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
|
||||
{
|
||||
return mozilla::dom::InstallPhaseEventBinding_workers::Wrap(aCx, this);
|
||||
return mozilla::dom::InstallPhaseEventBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
static already_AddRefed<InstallPhaseEvent>
|
||||
@ -90,7 +90,7 @@ public:
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
|
||||
{
|
||||
return mozilla::dom::InstallEventBinding_workers::Wrap(aCx, this);
|
||||
return mozilla::dom::InstallEventBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
static already_AddRefed<InstallEvent>
|
||||
|
@ -81,6 +81,10 @@ public:
|
||||
mURL(aURL)
|
||||
{
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -114,6 +118,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsIDocument* doc = nullptr;
|
||||
|
||||
@ -870,19 +878,6 @@ URL::SetHash(const nsAString& aHash, ErrorResult& aRv)
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
URL::CreateObjectURL(const GlobalObject& aGlobal, JSObject* aBlob,
|
||||
const mozilla::dom::objectURLOptions& aOptions,
|
||||
nsString& aResult, mozilla::ErrorResult& aRv)
|
||||
{
|
||||
SetDOMStringToNull(aResult);
|
||||
|
||||
NS_NAMED_LITERAL_STRING(argStr, "Argument 1 of URL.createObjectURL");
|
||||
NS_NAMED_LITERAL_STRING(blobStr, "MediaStream");
|
||||
aRv.ThrowTypeError(MSG_DOES_NOT_IMPLEMENT_INTERFACE, &argStr, &blobStr);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
URL::CreateObjectURL(const GlobalObject& aGlobal, File& aBlob,
|
||||
@ -892,8 +887,16 @@ URL::CreateObjectURL(const GlobalObject& aGlobal, File& aBlob,
|
||||
JSContext* cx = aGlobal.Context();
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl = aBlob.Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
aRv = blobImpl->SetMutable(false);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<CreateURLRunnable> runnable =
|
||||
new CreateURLRunnable(workerPrivate, aBlob.Impl(), aOptions, aResult);
|
||||
new CreateURLRunnable(workerPrivate, blobImpl, aOptions, aResult);
|
||||
|
||||
if (!runnable->Dispatch(cx)) {
|
||||
JS_ReportPendingException(cx);
|
||||
|
@ -57,11 +57,6 @@ public:
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const nsAString& aBase, ErrorResult& aRv);
|
||||
|
||||
static void
|
||||
CreateObjectURL(const GlobalObject& aGlobal,
|
||||
JSObject* aArg, const objectURLOptions& aOptions,
|
||||
nsString& aResult, ErrorResult& aRv);
|
||||
|
||||
static void
|
||||
CreateObjectURL(const GlobalObject& aGlobal,
|
||||
File& aArg, const objectURLOptions& aOptions,
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "MultipartFileImpl.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsError.h"
|
||||
@ -307,6 +308,84 @@ LogErrorToConsole(const nsAString& aMessage,
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// Recursive!
|
||||
already_AddRefed<FileImpl>
|
||||
EnsureBlobForBackgroundManager(FileImpl* aBlobImpl,
|
||||
PBackgroundChild* aManager = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
if (!aManager) {
|
||||
aManager = BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(aManager);
|
||||
}
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl = aBlobImpl;
|
||||
|
||||
const nsTArray<nsRefPtr<FileImpl>>* subBlobImpls =
|
||||
aBlobImpl->GetSubBlobImpls();
|
||||
|
||||
if (!subBlobImpls) {
|
||||
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
|
||||
// Always make sure we have a blob from an actor we can use on this
|
||||
// thread.
|
||||
BlobChild* blobChild = BlobChild::GetOrCreate(aManager, blobImpl);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
blobImpl = blobChild->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
const uint32_t subBlobCount = subBlobImpls->Length();
|
||||
MOZ_ASSERT(subBlobCount);
|
||||
|
||||
nsTArray<nsRefPtr<FileImpl>> newSubBlobImpls;
|
||||
newSubBlobImpls.SetLength(subBlobCount);
|
||||
|
||||
bool newBlobImplNeeded = false;
|
||||
|
||||
for (uint32_t index = 0; index < subBlobCount; index++) {
|
||||
const nsRefPtr<FileImpl>& subBlobImpl = subBlobImpls->ElementAt(index);
|
||||
MOZ_ASSERT(subBlobImpl);
|
||||
|
||||
nsRefPtr<FileImpl>& newSubBlobImpl = newSubBlobImpls[index];
|
||||
|
||||
newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager);
|
||||
MOZ_ASSERT(newSubBlobImpl);
|
||||
|
||||
if (subBlobImpl != newSubBlobImpl) {
|
||||
newBlobImplNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBlobImplNeeded) {
|
||||
nsString contentType;
|
||||
blobImpl->GetType(contentType);
|
||||
|
||||
if (blobImpl->IsFile()) {
|
||||
nsString name;
|
||||
blobImpl->GetName(name);
|
||||
|
||||
blobImpl = new MultipartFileImpl(newSubBlobImpls, name, contentType);
|
||||
} else {
|
||||
blobImpl = new MultipartFileImpl(newSubBlobImpls, contentType);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ReadBlobOrFile(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
@ -327,22 +406,8 @@ ReadBlobOrFile(JSContext* aCx,
|
||||
blobImpl = rawBlobImpl;
|
||||
}
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
|
||||
PBackgroundChild* backgroundManager =
|
||||
BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
|
||||
// Always make sure we have a blob from an actor we can use on this thread.
|
||||
BlobChild* blobChild = BlobChild::GetOrCreate(backgroundManager, blobImpl);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
blobImpl = blobChild->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
}
|
||||
blobImpl = EnsureBlobForBackgroundManager(blobImpl);
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
nsCOMPtr<nsISupports> parent;
|
||||
if (aIsMainThread) {
|
||||
@ -376,24 +441,10 @@ WriteBlobOrFile(JSContext* aCx,
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aBlobOrFileImpl);
|
||||
|
||||
nsRefPtr<FileImpl> newBlobOrFileImpl;
|
||||
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(aBlobOrFileImpl)) {
|
||||
PBackgroundChild* backgroundManager =
|
||||
BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
nsRefPtr<FileImpl> blobImpl = EnsureBlobForBackgroundManager(aBlobOrFileImpl);
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// Always make sure we have a blob from an actor we can use on this thread.
|
||||
BlobChild* blobChild =
|
||||
BlobChild::GetOrCreate(backgroundManager, aBlobOrFileImpl);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
newBlobOrFileImpl = blobChild->GetBlobImpl();
|
||||
MOZ_ASSERT(newBlobOrFileImpl);
|
||||
|
||||
aBlobOrFileImpl = newBlobOrFileImpl;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aBlobOrFileImpl->SetMutable(false)));
|
||||
aBlobOrFileImpl = blobImpl;
|
||||
|
||||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
|
||||
NS_WARN_IF(!JS_WriteBytes(aWriter,
|
||||
|
@ -2178,6 +2178,14 @@ XMLHttpRequest::Send(File& aBody, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<FileImpl> blobImpl = aBody.Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
aRv = blobImpl->SetMutable(false);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
mWorkerPrivate->IsChromeWorker() ?
|
||||
ChromeWorkerStructuredCloneCallbacks(false) :
|
||||
|
@ -78,17 +78,33 @@ function testBug1109574() {
|
||||
}
|
||||
|
||||
function testMethod() {
|
||||
var allowed = ["delete", "get", "head", "options", "post", "put"];
|
||||
// These get normalized.
|
||||
var allowed = ["delete", "get", "head", "options", "post", "put" ];
|
||||
for (var i = 0; i < allowed.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: allowed[i] });
|
||||
ok(true, "Method " + allowed[i] + " should be allowed");
|
||||
is(r.method, allowed[i].toUpperCase(),
|
||||
"Standard HTTP method " + allowed[i] + " should be normalized");
|
||||
} catch(e) {
|
||||
ok(false, "Method " + allowed[i] + " should be allowed");
|
||||
}
|
||||
}
|
||||
|
||||
var forbidden = ["aardvark", "connect", "trace", "track"];
|
||||
var allowed = [ "pAtCh", "foo" ];
|
||||
for (var i = 0; i < allowed.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: allowed[i] });
|
||||
ok(true, "Method " + allowed[i] + " should be allowed");
|
||||
is(r.method, allowed[i],
|
||||
"Non-standard but valid HTTP method " + allowed[i] +
|
||||
" should not be normalized");
|
||||
} catch(e) {
|
||||
ok(false, "Method " + allowed[i] + " should be allowed");
|
||||
}
|
||||
}
|
||||
|
||||
var forbidden = ["connect", "trace", "track", "<invalid token??"];
|
||||
for (var i = 0; i < forbidden.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: forbidden[i] });
|
||||
|
@ -910,10 +910,10 @@ nsWebBrowserPersist::OnDataAvailable(
|
||||
//*****************************************************************************
|
||||
|
||||
/* void onProgress (in nsIRequest request, in nsISupports ctxt,
|
||||
in unsigned long long aProgress, in unsigned long long aProgressMax); */
|
||||
in long long aProgress, in long long aProgressMax); */
|
||||
NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
|
||||
nsIRequest *request, nsISupports *ctxt, uint64_t aProgress,
|
||||
uint64_t aProgressMax)
|
||||
nsIRequest *request, nsISupports *ctxt, int64_t aProgress,
|
||||
int64_t aProgressMax)
|
||||
{
|
||||
if (!mProgressListener)
|
||||
{
|
||||
@ -925,16 +925,16 @@ NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
|
||||
OutputData *data = mOutputMap.Get(keyPtr);
|
||||
if (data)
|
||||
{
|
||||
data->mSelfProgress = int64_t(aProgress);
|
||||
data->mSelfProgressMax = int64_t(aProgressMax);
|
||||
data->mSelfProgress = aProgress;
|
||||
data->mSelfProgressMax = aProgressMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
UploadData *upData = mUploadList.Get(keyPtr);
|
||||
if (upData)
|
||||
{
|
||||
upData->mSelfProgress = int64_t(aProgress);
|
||||
upData->mSelfProgressMax = int64_t(aProgressMax);
|
||||
upData->mSelfProgress = aProgress;
|
||||
upData->mSelfProgressMax = aProgressMax;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,19 +45,19 @@ SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
|
||||
}
|
||||
|
||||
void
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride)
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride)
|
||||
{
|
||||
uint32_t* pixel = reinterpret_cast<uint32_t*>(aData);
|
||||
int height = aSize.height, width = aSize.width * 4;
|
||||
|
||||
for (int row = 0; row < aSize.height; ++row) {
|
||||
for (int column = 0; column < aSize.width; ++column) {
|
||||
for (int row = 0; row < height; ++row) {
|
||||
for (int column = 0; column < width; column += 4) {
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
pixel[column] |= 0x000000FF;
|
||||
aData[column] = 0xFF;
|
||||
#else
|
||||
pixel[column] |= 0xFF000000;
|
||||
aData[column + 3] = 0xFF;
|
||||
#endif
|
||||
}
|
||||
pixel += (aStride/4);
|
||||
aData += aStride;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride);
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
|
||||
|
||||
/**
|
||||
* Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
|
||||
|
@ -1108,7 +1108,19 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
|
||||
DrawGradient(mColorSpace, cg, aPattern, extents);
|
||||
} else {
|
||||
SetStrokeFromPattern(cg, mColorSpace, aPattern);
|
||||
CGContextStrokeRect(cg, RectToCGRect(aRect));
|
||||
// We'd like to use CGContextStrokeRect(cg, RectToCGRect(aRect));
|
||||
// Unfortunately, newer versions of OS X no longer start at the top-left
|
||||
// corner and stroke clockwise as older OS X versions and all the other
|
||||
// Moz2D backends do. (Newer versions start at the top right-hand corner
|
||||
// and stroke counter-clockwise.) For consistency we draw the rect by hand.
|
||||
CGRect rect = RectToCGRect(aRect);
|
||||
CGContextBeginPath(cg);
|
||||
CGContextMoveToPoint(cg, CGRectGetMinX(rect), CGRectGetMinY(rect));
|
||||
CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMinY(rect));
|
||||
CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
|
||||
CGContextAddLineToPoint(cg, CGRectGetMinX(rect), CGRectGetMaxY(rect));
|
||||
CGContextClosePath(cg);
|
||||
CGContextStrokePath(cg);
|
||||
}
|
||||
|
||||
fixer.Fix(mCg);
|
||||
|
@ -153,6 +153,8 @@ class CircularRowBuffer {
|
||||
std::vector<unsigned char*> row_addresses_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Convolves horizontally along a single row. The row data is given in
|
||||
// |src_data| and continues for the [begin, end) of the filter.
|
||||
template<bool has_alpha>
|
||||
@ -267,7 +269,60 @@ void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
|
||||
int filter_length,
|
||||
unsigned char* const* source_data_rows,
|
||||
int width, unsigned char* out_row,
|
||||
bool has_alpha, bool use_sse2) {
|
||||
int processed = 0;
|
||||
|
||||
#if defined(USE_SSE2)
|
||||
// If the binary was not built with SSE2 support, we had to fallback to C version.
|
||||
int simd_width = width & ~3;
|
||||
if (use_sse2 && simd_width) {
|
||||
ConvolveVertically_SSE2(filter_values, filter_length,
|
||||
source_data_rows, 0, simd_width,
|
||||
out_row, has_alpha);
|
||||
processed = simd_width;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (width > processed) {
|
||||
if (has_alpha) {
|
||||
ConvolveVertically<true>(filter_values, filter_length, source_data_rows,
|
||||
processed, width, out_row);
|
||||
} else {
|
||||
ConvolveVertically<false>(filter_values, filter_length, source_data_rows,
|
||||
processed, width, out_row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolveHorizontally(const unsigned char* src_data,
|
||||
const ConvolutionFilter1D& filter,
|
||||
unsigned char* out_row,
|
||||
bool has_alpha, bool use_sse2) {
|
||||
int width = filter.num_values();
|
||||
int processed = 0;
|
||||
#if defined(USE_SSE2)
|
||||
int simd_width = width & ~3;
|
||||
if (use_sse2 && simd_width) {
|
||||
// SIMD implementation works with 4 pixels at a time.
|
||||
// Therefore we process as much as we can using SSE and then use
|
||||
// C implementation for leftovers
|
||||
ConvolveHorizontally_SSE2(src_data, 0, simd_width, filter, out_row);
|
||||
processed = simd_width;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (width > processed) {
|
||||
if (has_alpha) {
|
||||
ConvolveHorizontally<true>(src_data, processed, width, filter, out_row);
|
||||
} else {
|
||||
ConvolveHorizontally<false>(src_data, processed, width, filter, out_row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConvolutionFilter1D ---------------------------------------------------------
|
||||
|
||||
@ -462,24 +517,9 @@ void BGRAConvolve2D(const unsigned char* source_data,
|
||||
unsigned char* const* first_row_for_filter =
|
||||
&rows_to_convolve[filter_offset - first_row_in_circular_buffer];
|
||||
|
||||
int processed = 0;
|
||||
#if defined(USE_SSE2)
|
||||
int simd_width = pixel_width & ~3;
|
||||
if (use_sse2 && simd_width) {
|
||||
ConvolveVertically_SSE2(filter_values, filter_length, first_row_for_filter,
|
||||
0, simd_width, cur_output_row, source_has_alpha);
|
||||
processed = simd_width;
|
||||
}
|
||||
#endif
|
||||
if (source_has_alpha) {
|
||||
ConvolveVertically<true>(filter_values, filter_length,
|
||||
first_row_for_filter,
|
||||
processed, pixel_width, cur_output_row);
|
||||
} else {
|
||||
ConvolveVertically<false>(filter_values, filter_length,
|
||||
first_row_for_filter,
|
||||
processed, pixel_width, cur_output_row);
|
||||
}
|
||||
ConvolveVertically(filter_values, filter_length,
|
||||
first_row_for_filter, pixel_width,
|
||||
cur_output_row, source_has_alpha, use_sse2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,6 +186,17 @@ void BGRAConvolve2D(const unsigned char* source_data,
|
||||
int output_byte_row_stride,
|
||||
unsigned char* output);
|
||||
|
||||
void ConvolveHorizontally(const unsigned char* src_data,
|
||||
const ConvolutionFilter1D& filter,
|
||||
unsigned char* out_row,
|
||||
bool has_alpha, bool use_sse2);
|
||||
|
||||
void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
|
||||
int filter_length,
|
||||
unsigned char* const* source_data_rows,
|
||||
int pixel_width, unsigned char* out_row,
|
||||
bool has_alpha, bool use_sse2);
|
||||
|
||||
} // namespace skia
|
||||
|
||||
#endif // SKIA_EXT_CONVOLVER_H_
|
||||
|
@ -44,171 +44,7 @@
|
||||
|
||||
namespace skia {
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns the ceiling/floor as an integer.
|
||||
inline int CeilInt(float val) {
|
||||
return static_cast<int>(ceil(val));
|
||||
}
|
||||
inline int FloorInt(float val) {
|
||||
return static_cast<int>(floor(val));
|
||||
}
|
||||
|
||||
// Filter function computation -------------------------------------------------
|
||||
|
||||
// Evaluates the box filter, which goes from -0.5 to +0.5.
|
||||
float EvalBox(float x) {
|
||||
return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
// Evaluates the Lanczos filter of the given filter size window for the given
|
||||
// position.
|
||||
//
|
||||
// |filter_size| is the width of the filter (the "window"), outside of which
|
||||
// the value of the function is 0. Inside of the window, the value is the
|
||||
// normalized sinc function:
|
||||
// lanczos(x) = sinc(x) * sinc(x / filter_size);
|
||||
// where
|
||||
// sinc(x) = sin(pi*x) / (pi*x);
|
||||
float EvalLanczos(int filter_size, float x) {
|
||||
if (x <= -filter_size || x >= filter_size)
|
||||
return 0.0f; // Outside of the window.
|
||||
if (x > -std::numeric_limits<float>::epsilon() &&
|
||||
x < std::numeric_limits<float>::epsilon())
|
||||
return 1.0f; // Special case the discontinuity at the origin.
|
||||
float xpi = x * static_cast<float>(M_PI);
|
||||
return (sin(xpi) / xpi) * // sinc(x)
|
||||
sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
|
||||
}
|
||||
|
||||
// Evaluates the Hamming filter of the given filter size window for the given
|
||||
// position.
|
||||
//
|
||||
// The filter covers [-filter_size, +filter_size]. Outside of this window
|
||||
// the value of the function is 0. Inside of the window, the value is sinus
|
||||
// cardinal multiplied by a recentered Hamming function. The traditional
|
||||
// Hamming formula for a window of size N and n ranging in [0, N-1] is:
|
||||
// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
|
||||
// In our case we want the function centered for x == 0 and at its minimum
|
||||
// on both ends of the window (x == +/- filter_size), hence the adjusted
|
||||
// formula:
|
||||
// hamming(x) = (0.54 -
|
||||
// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
|
||||
// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
|
||||
// = 0.54 + 0.46 * cos(pi * x / filter_size)
|
||||
float EvalHamming(int filter_size, float x) {
|
||||
if (x <= -filter_size || x >= filter_size)
|
||||
return 0.0f; // Outside of the window.
|
||||
if (x > -std::numeric_limits<float>::epsilon() &&
|
||||
x < std::numeric_limits<float>::epsilon())
|
||||
return 1.0f; // Special case the sinc discontinuity at the origin.
|
||||
const float xpi = x * static_cast<float>(M_PI);
|
||||
|
||||
return ((sin(xpi) / xpi) * // sinc(x)
|
||||
(0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x)
|
||||
}
|
||||
|
||||
// ResizeFilter ----------------------------------------------------------------
|
||||
|
||||
// Encapsulates computation and storage of the filters required for one complete
|
||||
// resize operation.
|
||||
class ResizeFilter {
|
||||
public:
|
||||
ResizeFilter(ImageOperations::ResizeMethod method,
|
||||
int src_full_width, int src_full_height,
|
||||
int dest_width, int dest_height,
|
||||
const SkIRect& dest_subset);
|
||||
|
||||
// Returns the filled filter values.
|
||||
const ConvolutionFilter1D& x_filter() { return x_filter_; }
|
||||
const ConvolutionFilter1D& y_filter() { return y_filter_; }
|
||||
|
||||
private:
|
||||
// Returns the number of pixels that the filer spans, in filter space (the
|
||||
// destination image).
|
||||
float GetFilterSupport(float scale) {
|
||||
switch (method_) {
|
||||
case ImageOperations::RESIZE_BOX:
|
||||
// The box filter just scales with the image scaling.
|
||||
return 0.5f; // Only want one side of the filter = /2.
|
||||
case ImageOperations::RESIZE_HAMMING1:
|
||||
// The Hamming filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 1 for Hamming1.
|
||||
return 1.0f;
|
||||
case ImageOperations::RESIZE_LANCZOS2:
|
||||
// The Lanczos filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 2 for Lanczos2.
|
||||
return 2.0f;
|
||||
case ImageOperations::RESIZE_LANCZOS3:
|
||||
// The Lanczos filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 3 for Lanczos3.
|
||||
return 3.0f;
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Computes one set of filters either horizontally or vertically. The caller
|
||||
// will specify the "min" and "max" rather than the bottom/top and
|
||||
// right/bottom so that the same code can be re-used in each dimension.
|
||||
//
|
||||
// |src_depend_lo| and |src_depend_size| gives the range for the source
|
||||
// depend rectangle (horizontally or vertically at the caller's discretion
|
||||
// -- see above for what this means).
|
||||
//
|
||||
// Likewise, the range of destination values to compute and the scale factor
|
||||
// for the transform is also specified.
|
||||
void ComputeFilters(int src_size,
|
||||
int dest_subset_lo, int dest_subset_size,
|
||||
float scale, ConvolutionFilter1D* output);
|
||||
|
||||
// Computes the filter value given the coordinate in filter space.
|
||||
inline float ComputeFilter(float pos) {
|
||||
switch (method_) {
|
||||
case ImageOperations::RESIZE_BOX:
|
||||
return EvalBox(pos);
|
||||
case ImageOperations::RESIZE_HAMMING1:
|
||||
return EvalHamming(1, pos);
|
||||
case ImageOperations::RESIZE_LANCZOS2:
|
||||
return EvalLanczos(2, pos);
|
||||
case ImageOperations::RESIZE_LANCZOS3:
|
||||
return EvalLanczos(3, pos);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ImageOperations::ResizeMethod method_;
|
||||
|
||||
// Subset of scaled destination bitmap to compute.
|
||||
SkIRect out_bounds_;
|
||||
|
||||
ConvolutionFilter1D x_filter_;
|
||||
ConvolutionFilter1D y_filter_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResizeFilter);
|
||||
};
|
||||
|
||||
ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
|
||||
int src_full_width, int src_full_height,
|
||||
int dest_width, int dest_height,
|
||||
const SkIRect& dest_subset)
|
||||
: method_(method),
|
||||
out_bounds_(dest_subset) {
|
||||
// method_ will only ever refer to an "algorithm method".
|
||||
SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
|
||||
(method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
|
||||
|
||||
float scale_x = static_cast<float>(dest_width) /
|
||||
static_cast<float>(src_full_width);
|
||||
float scale_y = static_cast<float>(dest_height) /
|
||||
static_cast<float>(src_full_height);
|
||||
|
||||
ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
|
||||
scale_x, &x_filter_);
|
||||
ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
|
||||
scale_y, &y_filter_);
|
||||
}
|
||||
namespace resize {
|
||||
|
||||
// TODO(egouriou): Take advantage of periods in the convolution.
|
||||
// Practical resizing filters are periodic outside of the border area.
|
||||
@ -221,9 +57,16 @@ ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
|
||||
// Small periods reduce computational load and improve cache usage if
|
||||
// the coefficients can be shared. For periods of 1 we can consider
|
||||
// loading the factors only once outside the borders.
|
||||
void ResizeFilter::ComputeFilters(int src_size,
|
||||
int dest_subset_lo, int dest_subset_size,
|
||||
float scale, ConvolutionFilter1D* output) {
|
||||
void ComputeFilters(ImageOperations::ResizeMethod method,
|
||||
int src_size, int dst_size,
|
||||
int dest_subset_lo, int dest_subset_size,
|
||||
ConvolutionFilter1D* output) {
|
||||
// method_ will only ever refer to an "algorithm method".
|
||||
SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
|
||||
(method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
|
||||
|
||||
float scale = static_cast<float>(dst_size) / static_cast<float>(src_size);
|
||||
|
||||
int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi)
|
||||
|
||||
// When we're doing a magnification, the scale will be larger than one. This
|
||||
@ -233,7 +76,7 @@ void ResizeFilter::ComputeFilters(int src_size,
|
||||
// some computations.
|
||||
float clamped_scale = std::min(1.0f, scale);
|
||||
|
||||
float src_support = GetFilterSupport(clamped_scale) / clamped_scale;
|
||||
float src_support = GetFilterSupport(method, clamped_scale) / clamped_scale;
|
||||
|
||||
// Speed up the divisions below by turning them into multiplies.
|
||||
float inv_scale = 1.0f / scale;
|
||||
@ -281,7 +124,7 @@ void ResizeFilter::ComputeFilters(int src_size,
|
||||
float dest_filter_dist = src_filter_dist * clamped_scale;
|
||||
|
||||
// Compute the filter value at that location.
|
||||
float filter_value = ComputeFilter(dest_filter_dist);
|
||||
float filter_value = ComputeFilter(method, dest_filter_dist);
|
||||
filter_values->push_back(filter_value);
|
||||
|
||||
filter_sum += filter_value;
|
||||
@ -312,6 +155,8 @@ void ResizeFilter::ComputeFilters(int src_size,
|
||||
output->PaddingForSIMD(8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
|
||||
ImageOperations::ResizeMethod method) {
|
||||
// Convert any "Quality Method" into an "Algorithm Method"
|
||||
@ -341,8 +186,6 @@ ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Resize ----------------------------------------------------------------------
|
||||
|
||||
// static
|
||||
@ -496,8 +339,11 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
|
||||
if (!source.readyToDraw())
|
||||
return SkBitmap();
|
||||
|
||||
ResizeFilter filter(method, source.width(), source.height(),
|
||||
dest_width, dest_height, dest_subset);
|
||||
ConvolutionFilter1D x_filter;
|
||||
ConvolutionFilter1D y_filter;
|
||||
|
||||
resize::ComputeFilters(method, source.width(), dest_width, dest_subset.fLeft, dest_subset.width(), &x_filter);
|
||||
resize::ComputeFilters(method, source.height(), dest_height, dest_subset.fTop, dest_subset.height(), &y_filter);
|
||||
|
||||
// Get a source bitmap encompassing this touched area. We construct the
|
||||
// offsets and row strides such that it looks like a new bitmap, while
|
||||
@ -522,7 +368,7 @@ SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
|
||||
return SkBitmap();
|
||||
|
||||
BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
|
||||
!source.isOpaque(), filter.x_filter(), filter.y_filter(),
|
||||
!source.isOpaque(), x_filter, y_filter,
|
||||
static_cast<int>(result.rowBytes()),
|
||||
static_cast<unsigned char*>(result.getPixels()));
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#include "skia/SkTypes.h"
|
||||
#include "Types.h"
|
||||
#include "convolver.h"
|
||||
#include "skia/SkRect.h"
|
||||
|
||||
class SkBitmap;
|
||||
struct SkIRect;
|
||||
@ -152,6 +154,132 @@ class ImageOperations {
|
||||
const SkIRect& dest_subset);
|
||||
};
|
||||
|
||||
// Returns the ceiling/floor as an integer.
|
||||
inline int CeilInt(float val) {
|
||||
return static_cast<int>(ceil(val));
|
||||
}
|
||||
inline int FloorInt(float val) {
|
||||
return static_cast<int>(floor(val));
|
||||
}
|
||||
|
||||
// Filter function computation -------------------------------------------------
|
||||
|
||||
// Evaluates the box filter, which goes from -0.5 to +0.5.
|
||||
inline float EvalBox(float x) {
|
||||
return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
// Evaluates the Lanczos filter of the given filter size window for the given
|
||||
// position.
|
||||
//
|
||||
// |filter_size| is the width of the filter (the "window"), outside of which
|
||||
// the value of the function is 0. Inside of the window, the value is the
|
||||
// normalized sinc function:
|
||||
// lanczos(x) = sinc(x) * sinc(x / filter_size);
|
||||
// where
|
||||
// sinc(x) = sin(pi*x) / (pi*x);
|
||||
inline float EvalLanczos(int filter_size, float x) {
|
||||
if (x <= -filter_size || x >= filter_size)
|
||||
return 0.0f; // Outside of the window.
|
||||
if (x > -std::numeric_limits<float>::epsilon() &&
|
||||
x < std::numeric_limits<float>::epsilon())
|
||||
return 1.0f; // Special case the discontinuity at the origin.
|
||||
float xpi = x * static_cast<float>(M_PI);
|
||||
return (sin(xpi) / xpi) * // sinc(x)
|
||||
sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
|
||||
}
|
||||
|
||||
// Evaluates the Hamming filter of the given filter size window for the given
|
||||
// position.
|
||||
//
|
||||
// The filter covers [-filter_size, +filter_size]. Outside of this window
|
||||
// the value of the function is 0. Inside of the window, the value is sinus
|
||||
// cardinal multiplied by a recentered Hamming function. The traditional
|
||||
// Hamming formula for a window of size N and n ranging in [0, N-1] is:
|
||||
// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
|
||||
// In our case we want the function centered for x == 0 and at its minimum
|
||||
// on both ends of the window (x == +/- filter_size), hence the adjusted
|
||||
// formula:
|
||||
// hamming(x) = (0.54 -
|
||||
// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
|
||||
// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
|
||||
// = 0.54 + 0.46 * cos(pi * x / filter_size)
|
||||
inline float EvalHamming(int filter_size, float x) {
|
||||
if (x <= -filter_size || x >= filter_size)
|
||||
return 0.0f; // Outside of the window.
|
||||
if (x > -std::numeric_limits<float>::epsilon() &&
|
||||
x < std::numeric_limits<float>::epsilon())
|
||||
return 1.0f; // Special case the sinc discontinuity at the origin.
|
||||
const float xpi = x * static_cast<float>(M_PI);
|
||||
|
||||
return ((sin(xpi) / xpi) * // sinc(x)
|
||||
(0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x)
|
||||
}
|
||||
|
||||
// ResizeFilter ----------------------------------------------------------------
|
||||
|
||||
// Encapsulates computation and storage of the filters required for one complete
|
||||
// resize operation.
|
||||
|
||||
namespace resize {
|
||||
|
||||
// Returns the number of pixels that the filer spans, in filter space (the
|
||||
// destination image).
|
||||
inline float GetFilterSupport(ImageOperations::ResizeMethod method,
|
||||
float scale) {
|
||||
switch (method) {
|
||||
case ImageOperations::RESIZE_BOX:
|
||||
// The box filter just scales with the image scaling.
|
||||
return 0.5f; // Only want one side of the filter = /2.
|
||||
case ImageOperations::RESIZE_HAMMING1:
|
||||
// The Hamming filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 1 for Hamming1.
|
||||
return 1.0f;
|
||||
case ImageOperations::RESIZE_LANCZOS2:
|
||||
// The Lanczos filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 2 for Lanczos2.
|
||||
return 2.0f;
|
||||
case ImageOperations::RESIZE_LANCZOS3:
|
||||
// The Lanczos filter takes as much space in the source image in
|
||||
// each direction as the size of the window = 3 for Lanczos3.
|
||||
return 3.0f;
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Computes one set of filters either horizontally or vertically. The caller
|
||||
// will specify the "min" and "max" rather than the bottom/top and
|
||||
// right/bottom so that the same code can be re-used in each dimension.
|
||||
//
|
||||
// |src_depend_lo| and |src_depend_size| gives the range for the source
|
||||
// depend rectangle (horizontally or vertically at the caller's discretion
|
||||
// -- see above for what this means).
|
||||
//
|
||||
// Likewise, the range of destination values to compute and the scale factor
|
||||
// for the transform is also specified.
|
||||
void ComputeFilters(ImageOperations::ResizeMethod method,
|
||||
int src_size, int dst_size,
|
||||
int dest_subset_lo, int dest_subset_size,
|
||||
ConvolutionFilter1D* output);
|
||||
|
||||
// Computes the filter value given the coordinate in filter space.
|
||||
inline float ComputeFilter(ImageOperations::ResizeMethod method, float pos) {
|
||||
switch (method) {
|
||||
case ImageOperations::RESIZE_BOX:
|
||||
return EvalBox(pos);
|
||||
case ImageOperations::RESIZE_HAMMING1:
|
||||
return EvalHamming(1, pos);
|
||||
case ImageOperations::RESIZE_LANCZOS2:
|
||||
return EvalLanczos(2, pos);
|
||||
case ImageOperations::RESIZE_LANCZOS3:
|
||||
return EvalLanczos(3, pos);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace skia
|
||||
|
||||
#endif // SKIA_EXT_IMAGE_OPERATIONS_H_
|
||||
|
@ -292,6 +292,10 @@ static EventRegions
|
||||
GetEventRegions(const LayerMetricsWrapper& aLayer)
|
||||
{
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
if (aLayer.IsScrollInfoLayer()) {
|
||||
return EventRegions(nsIntRegion(ParentLayerIntRect::ToUntyped(
|
||||
RoundedToInt(aLayer.Metrics().mCompositionBounds))));
|
||||
}
|
||||
return aLayer.GetEventRegions();
|
||||
}
|
||||
return EventRegions(aLayer.GetVisibleRegion());
|
||||
@ -329,9 +333,6 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
if (!aMetrics.IsScrollable()) {
|
||||
needsApzc = false;
|
||||
}
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled() && aLayer.IsScrollInfoLayer()) {
|
||||
needsApzc = false;
|
||||
}
|
||||
|
||||
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
|
||||
if (!(state && state->mController.get())) {
|
||||
|
@ -43,7 +43,7 @@ class TextureClientX11 : public TextureClient
|
||||
|
||||
virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
|
||||
|
||||
virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
|
||||
virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
|
||||
|
||||
virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
|
||||
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
|
||||
virtual void DeallocateDeviceData() MOZ_OVERRIDE { }
|
||||
|
||||
virtual void SetCompositor(Compositor* aCompositor);
|
||||
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
|
||||
|
||||
static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
|
||||
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
}
|
||||
|
||||
#ifdef MOZ_LAYERS_HAVE_LOG
|
||||
virtual const char* Name() { return "X11TextureHost"; }
|
||||
virtual const char* Name() MOZ_OVERRIDE { return "X11TextureHost"; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
@ -96,6 +96,7 @@ CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
|
||||
sCompositor->SendGetTileSize(&width, &height);
|
||||
gfxPlatform::GetPlatform()->SetTileSize(width, height);
|
||||
|
||||
// We release this ref in ActorDestroy().
|
||||
return sCompositor;
|
||||
}
|
||||
|
||||
@ -180,13 +181,14 @@ CompositorChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at CompositorChild");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sCompositor) {
|
||||
sCompositor->Release();
|
||||
sCompositor = nullptr;
|
||||
}
|
||||
// We don't want to release the ref to sCompositor here, during
|
||||
// cleanup, because that will cause it to be deleted while it's
|
||||
// still being used. So defer the deletion to after it's not in
|
||||
// use.
|
||||
sCompositor = nullptr;
|
||||
|
||||
MessageLoop::current()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &CompositorChild::Release));
|
||||
|
@ -33,11 +33,11 @@ public:
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHOD GetClientBounds(nsIntRect &aRect) {
|
||||
NS_IMETHOD GetClientBounds(nsIntRect &aRect) MOZ_OVERRIDE {
|
||||
aRect = nsIntRect(0, 0, gCompWidth, gCompHeight);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD GetBounds(nsIntRect &aRect) { return GetClientBounds(aRect); }
|
||||
NS_IMETHOD GetBounds(nsIntRect &aRect) MOZ_OVERRIDE { return GetClientBounds(aRect); }
|
||||
|
||||
void* GetNativeData(uint32_t aDataType) MOZ_OVERRIDE {
|
||||
if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
|
||||
@ -55,30 +55,30 @@ public:
|
||||
nsNativeWidget aNativeParent,
|
||||
const nsIntRect &aRect,
|
||||
nsDeviceContext *aContext,
|
||||
nsWidgetInitData *aInitData = nullptr) { return NS_OK; }
|
||||
NS_IMETHOD Show(bool aState) { return NS_OK; }
|
||||
virtual bool IsVisible() const { return true; }
|
||||
nsWidgetInitData *aInitData = nullptr) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD Show(bool aState) MOZ_OVERRIDE { return NS_OK; }
|
||||
virtual bool IsVisible() const MOZ_OVERRIDE { return true; }
|
||||
NS_IMETHOD ConstrainPosition(bool aAllowSlop,
|
||||
int32_t *aX, int32_t *aY) { return NS_OK; }
|
||||
NS_IMETHOD Move(double aX, double aY) { return NS_OK; }
|
||||
NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
|
||||
int32_t *aX, int32_t *aY) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD Move(double aX, double aY) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD Resize(double aX, double aY,
|
||||
double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
|
||||
double aWidth, double aHeight, bool aRepaint) MOZ_OVERRIDE { return NS_OK; }
|
||||
|
||||
NS_IMETHOD Enable(bool aState) { return NS_OK; }
|
||||
virtual bool IsEnabled() const { return true; }
|
||||
NS_IMETHOD SetFocus(bool aRaise) { return NS_OK; }
|
||||
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { return NS_OK; }
|
||||
NS_IMETHOD Invalidate(const nsIntRect &aRect) { return NS_OK; }
|
||||
NS_IMETHOD SetTitle(const nsAString& title) { return NS_OK; }
|
||||
virtual nsIntPoint WidgetToScreenOffset() { return nsIntPoint(0, 0); }
|
||||
NS_IMETHOD Enable(bool aState) MOZ_OVERRIDE { return NS_OK; }
|
||||
virtual bool IsEnabled() const MOZ_OVERRIDE { return true; }
|
||||
NS_IMETHOD SetFocus(bool aRaise) MOZ_OVERRIDE { return NS_OK; }
|
||||
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD Invalidate(const nsIntRect &aRect) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD SetTitle(const nsAString& title) MOZ_OVERRIDE { return NS_OK; }
|
||||
virtual nsIntPoint WidgetToScreenOffset() MOZ_OVERRIDE { return nsIntPoint(0, 0); }
|
||||
NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus& aStatus) { return NS_OK; }
|
||||
NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) { return NS_OK; }
|
||||
nsEventStatus& aStatus) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) MOZ_OVERRIDE { return NS_OK; }
|
||||
NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
|
||||
const InputContextAction& aAction) {}
|
||||
NS_IMETHOD_(InputContext) GetInputContext() { abort(); }
|
||||
NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; }
|
||||
const InputContextAction& aAction) MOZ_OVERRIDE {}
|
||||
NS_IMETHOD_(InputContext) GetInputContext() MOZ_OVERRIDE { abort(); }
|
||||
NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent) MOZ_OVERRIDE { return NS_OK; }
|
||||
private:
|
||||
~MockWidget() {}
|
||||
};
|
||||
|
@ -22,36 +22,46 @@ SoftwareVsyncSource::~SoftwareVsyncSource()
|
||||
}
|
||||
|
||||
SoftwareDisplay::SoftwareDisplay()
|
||||
: mCurrentTaskMonitor("SoftwareVsyncCurrentTaskMonitor")
|
||||
: mVsyncEnabled(false)
|
||||
, mCurrentTaskMonitor("SoftwareVsyncCurrentTaskMonitor")
|
||||
{
|
||||
// Mimic 60 fps
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
const double rate = 1000 / 60.0;
|
||||
mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
|
||||
mVsyncThread = new base::Thread("SoftwareVsyncThread");
|
||||
EnableVsync();
|
||||
}
|
||||
|
||||
void
|
||||
SoftwareDisplay::EnableVsync()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
|
||||
mVsyncEnabled = true;
|
||||
MOZ_ASSERT(!mVsyncThread->IsRunning());
|
||||
MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread");
|
||||
mCurrentVsyncTask = NewRunnableMethod(this,
|
||||
&SoftwareDisplay::NotifyVsync,
|
||||
mozilla::TimeStamp::Now());
|
||||
mVsyncThread->message_loop()->PostTask(FROM_HERE, mCurrentVsyncTask);
|
||||
if (IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{ // scope lock
|
||||
mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
|
||||
mVsyncEnabled = true;
|
||||
MOZ_ASSERT(!mVsyncThread->IsRunning());
|
||||
MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread");
|
||||
mCurrentVsyncTask = NewRunnableMethod(this,
|
||||
&SoftwareDisplay::NotifyVsync,
|
||||
mozilla::TimeStamp::Now());
|
||||
mVsyncThread->message_loop()->PostTask(FROM_HERE, mCurrentVsyncTask);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SoftwareDisplay::DisableVsync()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mVsyncThread->IsRunning());
|
||||
{ // Scope lock
|
||||
{ // scope lock
|
||||
mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
|
||||
mVsyncEnabled = false;
|
||||
if (mCurrentVsyncTask) {
|
||||
|
@ -41,6 +41,7 @@ VsyncSource::GetRefreshTimerVsyncDispatcher()
|
||||
|
||||
VsyncSource::Display::Display()
|
||||
: mDispatcherLock("display dispatcher lock")
|
||||
, mRefreshTimerNeedsVsync(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
|
||||
@ -72,10 +73,13 @@ VsyncSource::Display::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aC
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCompositorVsyncDispatcher);
|
||||
MutexAutoLock lock(mDispatcherLock);
|
||||
if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
|
||||
mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
|
||||
{ // scope lock
|
||||
MutexAutoLock lock(mDispatcherLock);
|
||||
if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
|
||||
mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
|
||||
}
|
||||
}
|
||||
UpdateVsyncStatus();
|
||||
}
|
||||
|
||||
void
|
||||
@ -83,8 +87,43 @@ VsyncSource::Display::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher*
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCompositorVsyncDispatcher);
|
||||
MutexAutoLock lock(mDispatcherLock);
|
||||
mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
|
||||
{ // Scope lock
|
||||
MutexAutoLock lock(mDispatcherLock);
|
||||
if (mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
|
||||
mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
|
||||
}
|
||||
}
|
||||
UpdateVsyncStatus();
|
||||
}
|
||||
|
||||
void
|
||||
VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mRefreshTimerNeedsVsync = aEnable;
|
||||
UpdateVsyncStatus();
|
||||
}
|
||||
|
||||
void
|
||||
VsyncSource::Display::UpdateVsyncStatus()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
|
||||
// NotifyVsync grabs a lock to dispatch vsync events
|
||||
// When disabling vsync, we wait for the underlying thread to stop on some platforms
|
||||
// We can deadlock if we wait for the underlying vsync thread to stop
|
||||
// while the vsync thread is in NotifyVsync.
|
||||
bool enableVsync = false;
|
||||
{ // scope lock
|
||||
MutexAutoLock lock(mDispatcherLock);
|
||||
enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync;
|
||||
}
|
||||
|
||||
if (enableVsync) {
|
||||
EnableVsync();
|
||||
} else {
|
||||
DisableVsync();
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<RefreshTimerVsyncDispatcher>
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
|
||||
void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
|
||||
void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
|
||||
void NotifyRefreshTimerVsyncStatus(bool aEnable);
|
||||
|
||||
// These should all only be called on the main thread
|
||||
virtual void EnableVsync() = 0;
|
||||
@ -56,7 +57,10 @@ public:
|
||||
virtual bool IsVsyncEnabled() = 0;
|
||||
|
||||
private:
|
||||
void UpdateVsyncStatus();
|
||||
|
||||
Mutex mDispatcherLock;
|
||||
bool mRefreshTimerNeedsVsync;
|
||||
nsTArray<nsRefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
|
||||
nsRefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
@ -573,9 +573,19 @@ gfx3DMatrix::Inverse() const
|
||||
gfxPoint
|
||||
gfx3DMatrix::Transform(const gfxPoint& point) const
|
||||
{
|
||||
Point3D vec3d(point.x, point.y, 0);
|
||||
vec3d = Transform3D(vec3d);
|
||||
return gfxPoint(vec3d.x, vec3d.y);
|
||||
// Note: we don't use Transform3D here because passing point.x/y via
|
||||
// a Point3D would lose precision and cause bugs, e.g. bug 1091709.
|
||||
gfxFloat px = point.x;
|
||||
gfxFloat py = point.y;
|
||||
|
||||
gfxFloat x = px * _11 + py * _21 + _41;
|
||||
gfxFloat y = px * _12 + py * _22 + _42;
|
||||
gfxFloat w = px * _14 + py * _24 + _44;
|
||||
|
||||
x /= w;
|
||||
y /= w;
|
||||
|
||||
return gfxPoint(x, y);
|
||||
}
|
||||
|
||||
Point3D
|
||||
|
@ -442,7 +442,6 @@ public:
|
||||
public:
|
||||
GonkDisplay() : mVsyncEnabled(false)
|
||||
{
|
||||
EnableVsync();
|
||||
}
|
||||
|
||||
~GonkDisplay()
|
||||
@ -453,12 +452,18 @@ public:
|
||||
virtual void EnableVsync() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(true);
|
||||
}
|
||||
|
||||
virtual void DisableVsync() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(false);
|
||||
}
|
||||
|
||||
@ -484,11 +489,14 @@ already_AddRefed<mozilla::gfx::VsyncSource>
|
||||
gfxAndroidPlatform::CreateHardwareVsyncSource()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsRefPtr<VsyncSource> vsyncSource = new GonkVsyncSource();
|
||||
if (!vsyncSource->GetGlobalDisplay().IsVsyncEnabled()) {
|
||||
nsRefPtr<GonkVsyncSource> vsyncSource = new GonkVsyncSource();
|
||||
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
|
||||
display.EnableVsync();
|
||||
if (!display.IsVsyncEnabled()) {
|
||||
NS_WARNING("Error enabling gonk vsync. Falling back to software vsync\n");
|
||||
return gfxPlatform::CreateHardwareVsyncSource();
|
||||
}
|
||||
display.DisableVsync();
|
||||
return vsyncSource.forget();
|
||||
#else
|
||||
NS_WARNING("Hardware vsync not supported on android yet");
|
||||
|
@ -297,24 +297,6 @@ gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
|
||||
mPathBuilder->Close();
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
|
||||
{
|
||||
// Lifetime needs to be limited here since we may wrap surface's data.
|
||||
RefPtr<SourceSurface> surf =
|
||||
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
|
||||
|
||||
if (!surf) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect rect(0, 0, Float(size.width), Float(size.height));
|
||||
rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
|
||||
|
||||
// XXX - Should fix pixel snapping.
|
||||
mDT->DrawSurface(surf, rect, rect);
|
||||
}
|
||||
|
||||
// transform stuff
|
||||
void
|
||||
gfxContext::Multiply(const gfxMatrix& matrix)
|
||||
@ -611,27 +593,6 @@ gfxContext::Clip()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::ResetClip()
|
||||
{
|
||||
for (int i = mStateStack.Length() - 1; i >= 0; i--) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
mDT->PopClip();
|
||||
}
|
||||
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CurrentState().pushedClips.Clear();
|
||||
CurrentState().clipWasReset = true;
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::UpdateSurfaceClip()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gfxContext::PopClip()
|
||||
{
|
||||
|
@ -321,16 +321,6 @@ public:
|
||||
|
||||
void Mask(mozilla::gfx::SourceSurface *surface, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
|
||||
|
||||
/**
|
||||
** Shortcuts
|
||||
**/
|
||||
|
||||
/**
|
||||
* Creates a new path with a rectangle from 0,0 to size.w,size.h
|
||||
* and calls cairo_fill.
|
||||
*/
|
||||
void DrawSurface(gfxASurface *surface, const gfxSize& size);
|
||||
|
||||
/**
|
||||
** Line Properties
|
||||
**/
|
||||
@ -439,12 +429,6 @@ public:
|
||||
*/
|
||||
void Clip();
|
||||
|
||||
/**
|
||||
* Undoes any clipping. Further drawings will only be restricted by the
|
||||
* surface dimensions.
|
||||
*/
|
||||
void ResetClip();
|
||||
|
||||
/**
|
||||
* Helper functions that will create a rect path and call Clip().
|
||||
* Any current path will be destroyed by these functions!
|
||||
@ -455,12 +439,6 @@ public:
|
||||
|
||||
void PopClip();
|
||||
|
||||
/**
|
||||
* This will ensure that the surface actually has its clip set.
|
||||
* Useful if you are doing native drawing.
|
||||
*/
|
||||
void UpdateSurfaceClip();
|
||||
|
||||
/**
|
||||
* This will return the current bounds of the clip region in user
|
||||
* space.
|
||||
|
@ -32,20 +32,21 @@ public:
|
||||
CreateOffscreenSurface(const IntSize& size,
|
||||
gfxContentType contentType) MOZ_OVERRIDE;
|
||||
|
||||
mozilla::TemporaryRef<mozilla::gfx::ScaledFont>
|
||||
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont);
|
||||
virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont>
|
||||
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) MOZ_OVERRIDE;
|
||||
|
||||
nsresult GetFontList(nsIAtom *aLangGroup,
|
||||
const nsACString& aGenericFamily,
|
||||
nsTArray<nsString>& aListOfFonts);
|
||||
virtual nsresult GetFontList(nsIAtom *aLangGroup,
|
||||
const nsACString& aGenericFamily,
|
||||
nsTArray<nsString>& aListOfFonts) MOZ_OVERRIDE;
|
||||
|
||||
nsresult UpdateFontList();
|
||||
virtual nsresult UpdateFontList() MOZ_OVERRIDE;
|
||||
|
||||
nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
|
||||
virtual nsresult GetStandardFamilyName(const nsAString& aFontName,
|
||||
nsAString& aFamilyName) MOZ_OVERRIDE;
|
||||
|
||||
gfxFontGroup *CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
|
||||
const gfxFontStyle *aStyle,
|
||||
gfxUserFontSet *aUserFontSet);
|
||||
virtual gfxFontGroup* CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
|
||||
const gfxFontStyle *aStyle,
|
||||
gfxUserFontSet *aUserFontSet) MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Look up a local platform font using the full font face name (needed to
|
||||
@ -54,7 +55,7 @@ public:
|
||||
virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
bool aItalic);
|
||||
bool aItalic) MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Activate a platform font (needed to support @font-face src url() )
|
||||
@ -65,14 +66,14 @@ public:
|
||||
int16_t aStretch,
|
||||
bool aItalic,
|
||||
const uint8_t* aFontData,
|
||||
uint32_t aLength);
|
||||
uint32_t aLength) MOZ_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Check whether format is supported on a platform or not (if unclear,
|
||||
* returns true).
|
||||
*/
|
||||
virtual bool IsFontFormatSupported(nsIURI *aFontURI,
|
||||
uint32_t aFormatFlags);
|
||||
uint32_t aFormatFlags) MOZ_OVERRIDE;
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
static void SetGdkDrawable(cairo_surface_t *target,
|
||||
@ -104,15 +105,16 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual gfxImageFormat GetOffscreenFormat();
|
||||
virtual gfxImageFormat GetOffscreenFormat() MOZ_OVERRIDE;
|
||||
|
||||
virtual int GetScreenDepth() const;
|
||||
virtual int GetScreenDepth() const MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
static gfxFontconfigUtils *sFontconfigUtils;
|
||||
|
||||
private:
|
||||
virtual void GetPlatformCMSOutputProfile(void *&mem, size_t &size);
|
||||
virtual void GetPlatformCMSOutputProfile(void *&mem,
|
||||
size_t &size) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
static bool sUseXRender;
|
||||
|
@ -436,8 +436,8 @@ public:
|
||||
{
|
||||
public:
|
||||
OSXDisplay()
|
||||
: mDisplayLink(nullptr)
|
||||
{
|
||||
EnableVsync();
|
||||
}
|
||||
|
||||
~OSXDisplay()
|
||||
@ -448,6 +448,9 @@ public:
|
||||
virtual void EnableVsync() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a display link capable of being used with all active displays
|
||||
// TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
|
||||
@ -473,6 +476,9 @@ public:
|
||||
virtual void DisableVsync() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsVsyncEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release the display link
|
||||
if (mDisplayLink) {
|
||||
|
@ -51,10 +51,11 @@ public:
|
||||
virtual ~gfxXlibSurface();
|
||||
|
||||
virtual already_AddRefed<gfxASurface>
|
||||
CreateSimilarSurface(gfxContentType aType, const gfxIntSize& aSize);
|
||||
CreateSimilarSurface(gfxContentType aType,
|
||||
const gfxIntSize& aSize) MOZ_OVERRIDE;
|
||||
virtual void Finish() MOZ_OVERRIDE;
|
||||
|
||||
virtual const gfxIntSize GetSize() const;
|
||||
virtual const gfxIntSize GetSize() const MOZ_OVERRIDE;
|
||||
|
||||
Display* XDisplay() { return mDisplay; }
|
||||
Screen* XScreen();
|
||||
@ -80,7 +81,7 @@ public:
|
||||
|
||||
// This surface is a wrapper around X pixmaps, which are stored in the X
|
||||
// server, not the main application.
|
||||
virtual gfxMemoryLocation GetMemoryLocation() const;
|
||||
virtual gfxMemoryLocation GetMemoryLocation() const MOZ_OVERRIDE;
|
||||
|
||||
#if defined(GL_PROVIDER_GLX)
|
||||
GLXPixmap GetGLXPixmap();
|
||||
|
@ -139,6 +139,20 @@ nsJPEGDecoder::SpeedHistogram()
|
||||
return Telemetry::IMAGE_DECODE_SPEED_JPEG;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJPEGDecoder::SetTargetSize(const nsIntSize& aSize)
|
||||
{
|
||||
// Make sure the size is reasonable.
|
||||
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Create a downscaler that we'll filter our output through.
|
||||
mDownscaler.emplace(aSize);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsJPEGDecoder::InitInternal()
|
||||
{
|
||||
@ -394,6 +408,17 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDownscaler) {
|
||||
nsresult rv = mDownscaler->BeginFrame(GetSize(),
|
||||
mImageData,
|
||||
/* aHasAlpha = */ false);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = JPEG_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
|
||||
(" JPEGDecoderAccounting: nsJPEGDecoder::"
|
||||
"Write -- created image frame with %ux%u pixels",
|
||||
@ -512,6 +537,7 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
break;
|
||||
|
||||
mInfo.output_scanline = 0;
|
||||
mDownscaler->ResetForNextProgressivePass();
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,9 +617,15 @@ nsJPEGDecoder::OutputScanlines(bool* suspend)
|
||||
const uint32_t top = mInfo.output_scanline;
|
||||
|
||||
while ((mInfo.output_scanline < mInfo.output_height)) {
|
||||
// Use the Cairo image buffer as scanline buffer
|
||||
uint32_t* imageRow = ((uint32_t*)mImageData) +
|
||||
(mInfo.output_scanline * mInfo.output_width);
|
||||
uint32_t* imageRow = nullptr;
|
||||
if (mDownscaler) {
|
||||
imageRow = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
|
||||
} else {
|
||||
imageRow = reinterpret_cast<uint32_t*>(mImageData) +
|
||||
(mInfo.output_scanline * mInfo.output_width);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(imageRow, "Should have a row buffer here");
|
||||
|
||||
if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
|
||||
// Special case: scanline will be directly converted into packed ARGB
|
||||
@ -601,6 +633,9 @@ nsJPEGDecoder::OutputScanlines(bool* suspend)
|
||||
*suspend = true; // suspend
|
||||
break;
|
||||
}
|
||||
if (mDownscaler) {
|
||||
mDownscaler->CommitRow();
|
||||
}
|
||||
continue; // all done for this row!
|
||||
}
|
||||
|
||||
@ -676,13 +711,22 @@ nsJPEGDecoder::OutputScanlines(bool* suspend)
|
||||
sampleRow[2]);
|
||||
sampleRow += 3;
|
||||
}
|
||||
|
||||
if (mDownscaler) {
|
||||
mDownscaler->CommitRow();
|
||||
}
|
||||
}
|
||||
|
||||
if (top != mInfo.output_scanline) {
|
||||
nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
|
||||
PostInvalidation(r);
|
||||
PostInvalidation(nsIntRect(0, top,
|
||||
mInfo.output_width,
|
||||
mInfo.output_scanline - top),
|
||||
mDownscaler ? Some(mDownscaler->TakeInvalidRect())
|
||||
: Nothing());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mDownscaler || !mDownscaler->HasInvalidation(),
|
||||
"Didn't send downscaler's invalidation");
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "Downscaler.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
@ -55,6 +56,8 @@ public:
|
||||
nsJPEGDecoder(RasterImage* aImage, Decoder::DecodeStyle aDecodeStyle);
|
||||
virtual ~nsJPEGDecoder();
|
||||
|
||||
virtual nsresult SetTargetSize(const nsIntSize& aSize) MOZ_OVERRIDE;
|
||||
|
||||
virtual void InitInternal() MOZ_OVERRIDE;
|
||||
virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;
|
||||
virtual void FinishInternal() MOZ_OVERRIDE;
|
||||
@ -66,6 +69,8 @@ protected:
|
||||
Orientation ReadOrientationFromEXIF();
|
||||
void OutputScanlines(bool* suspend);
|
||||
|
||||
Maybe<Downscaler> mDownscaler;
|
||||
|
||||
public:
|
||||
struct jpeg_decompress_struct mInfo;
|
||||
struct jpeg_source_mgr mSourceMgr;
|
||||
|
219
image/src/Downscaler.cpp
Normal file
219
image/src/Downscaler.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Downscaler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include "gfxPrefs.h"
|
||||
#include "image_operations.h"
|
||||
#include "convolver.h"
|
||||
#include "skia/SkTypes.h"
|
||||
|
||||
using std::max;
|
||||
using std::swap;
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
Downscaler::Downscaler(const nsIntSize& aTargetSize)
|
||||
: mTargetSize(aTargetSize)
|
||||
, mOutputBuffer(nullptr)
|
||||
, mXFilter(MakeUnique<skia::ConvolutionFilter1D>())
|
||||
, mYFilter(MakeUnique<skia::ConvolutionFilter1D>())
|
||||
, mWindowCapacity(0)
|
||||
, mHasAlpha(true)
|
||||
{
|
||||
MOZ_ASSERT(gfxPrefs::ImageDownscaleDuringDecodeEnabled(),
|
||||
"Downscaling even though downscale-during-decode is disabled?");
|
||||
MOZ_ASSERT(mTargetSize.width > 0 && mTargetSize.height > 0,
|
||||
"Invalid target size");
|
||||
}
|
||||
|
||||
Downscaler::~Downscaler()
|
||||
{
|
||||
ReleaseWindow();
|
||||
}
|
||||
|
||||
void
|
||||
Downscaler::ReleaseWindow()
|
||||
{
|
||||
if (!mWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < mWindowCapacity; ++i) {
|
||||
delete[] mWindow[i];
|
||||
}
|
||||
|
||||
mWindow = nullptr;
|
||||
mWindowCapacity = 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
|
||||
uint8_t* aOutputBuffer,
|
||||
bool aHasAlpha)
|
||||
{
|
||||
MOZ_ASSERT(aOutputBuffer);
|
||||
MOZ_ASSERT(mTargetSize != aOriginalSize,
|
||||
"Created a downscaler, but not downscaling?");
|
||||
MOZ_ASSERT(mTargetSize.width <= aOriginalSize.width,
|
||||
"Created a downscaler, but width is larger");
|
||||
MOZ_ASSERT(mTargetSize.height <= aOriginalSize.height,
|
||||
"Created a downscaler, but height is larger");
|
||||
MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0,
|
||||
"Invalid original size");
|
||||
|
||||
mOriginalSize = aOriginalSize;
|
||||
mOutputBuffer = aOutputBuffer;
|
||||
mHasAlpha = aHasAlpha;
|
||||
|
||||
ResetForNextProgressivePass();
|
||||
ReleaseWindow();
|
||||
|
||||
auto resizeMethod = skia::ImageOperations::RESIZE_LANCZOS3;
|
||||
|
||||
skia::resize::ComputeFilters(resizeMethod, mOriginalSize.width,
|
||||
mTargetSize.width, 0,
|
||||
mTargetSize.width, mXFilter.get());
|
||||
|
||||
skia::resize::ComputeFilters(resizeMethod, mOriginalSize.height,
|
||||
mTargetSize.height, 0,
|
||||
mTargetSize.height, mYFilter.get());
|
||||
|
||||
// Allocate the buffer, which contains scanlines of the original image.
|
||||
mRowBuffer = MakeUnique<uint8_t[]>(mOriginalSize.width * sizeof(uint32_t));
|
||||
if (MOZ_UNLIKELY(!mRowBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Allocate the window, which contains horizontally downscaled scanlines. (We
|
||||
// can store scanlines which are already downscale because our downscaling
|
||||
// filter is separable.)
|
||||
mWindowCapacity = mYFilter->max_filter();
|
||||
mWindow = MakeUnique<uint8_t*[]>(mWindowCapacity);
|
||||
if (MOZ_UNLIKELY(!mWindow)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
bool anyAllocationFailed = false;
|
||||
const int rowSize = mTargetSize.width * sizeof(uint32_t);
|
||||
for (int32_t i = 0; i < mWindowCapacity; ++i) {
|
||||
mWindow[i] = new uint8_t[rowSize];
|
||||
anyAllocationFailed = anyAllocationFailed || mWindow[i] == nullptr;
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(anyAllocationFailed)) {
|
||||
// We intentionally iterate through the entire array even if an allocation
|
||||
// fails, to ensure that all the pointers in it are either valid or nullptr.
|
||||
// That in turn ensures that ReleaseWindow() can clean up correctly.
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Downscaler::ResetForNextProgressivePass()
|
||||
{
|
||||
mPrevInvalidatedLine = 0;
|
||||
mCurrentOutLine = 0;
|
||||
mCurrentInLine = 0;
|
||||
mLinesInBuffer = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Downscaler::CommitRow()
|
||||
{
|
||||
MOZ_ASSERT(mOutputBuffer, "Should have a current frame");
|
||||
MOZ_ASSERT(mCurrentInLine < mOriginalSize.height, "Past end of input");
|
||||
MOZ_ASSERT(mCurrentOutLine < mTargetSize.height, "Past end of output");
|
||||
|
||||
int32_t filterOffset = 0;
|
||||
int32_t filterLength = 0;
|
||||
mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
|
||||
|
||||
int32_t inLineToRead = filterOffset + mLinesInBuffer;
|
||||
MOZ_ASSERT(mCurrentInLine <= inLineToRead, "Reading past end of input");
|
||||
if (mCurrentInLine == inLineToRead) {
|
||||
skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
|
||||
mWindow[mLinesInBuffer++], mHasAlpha,
|
||||
/* use_sse2 = */ true);
|
||||
}
|
||||
|
||||
while (mLinesInBuffer == filterLength &&
|
||||
mCurrentOutLine < mTargetSize.height) {
|
||||
DownscaleInputLine();
|
||||
mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
|
||||
}
|
||||
|
||||
mCurrentInLine += 1;
|
||||
}
|
||||
|
||||
bool
|
||||
Downscaler::HasInvalidation() const
|
||||
{
|
||||
return mCurrentOutLine > mPrevInvalidatedLine;
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
Downscaler::TakeInvalidRect()
|
||||
{
|
||||
if (MOZ_UNLIKELY(!HasInvalidation())) {
|
||||
return nsIntRect();
|
||||
}
|
||||
|
||||
nsIntRect invalidRect(0, mPrevInvalidatedLine,
|
||||
mTargetSize.width,
|
||||
mCurrentOutLine - mPrevInvalidatedLine);
|
||||
mPrevInvalidatedLine = mCurrentOutLine;
|
||||
return invalidRect;
|
||||
}
|
||||
|
||||
void
|
||||
Downscaler::DownscaleInputLine()
|
||||
{
|
||||
typedef skia::ConvolutionFilter1D::Fixed FilterValue;
|
||||
|
||||
MOZ_ASSERT(mOutputBuffer);
|
||||
MOZ_ASSERT(mCurrentOutLine < mTargetSize.height, "Writing past end of output");
|
||||
|
||||
int32_t filterOffset = 0;
|
||||
int32_t filterLength = 0;
|
||||
auto filterValues =
|
||||
mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
|
||||
|
||||
uint8_t* outputLine =
|
||||
&mOutputBuffer[mCurrentOutLine * mTargetSize.width * sizeof(uint32_t)];
|
||||
skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
|
||||
filterLength, mWindow.get(), mXFilter->num_values(),
|
||||
outputLine, mHasAlpha, /* use_sse2 = */ true);
|
||||
|
||||
mCurrentOutLine += 1;
|
||||
|
||||
if (mCurrentOutLine == mTargetSize.height) {
|
||||
// We're done.
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t newFilterOffset = 0;
|
||||
int32_t newFilterLength = 0;
|
||||
mYFilter->FilterForValue(mCurrentOutLine, &newFilterOffset, &newFilterLength);
|
||||
|
||||
int diff = newFilterOffset - filterOffset;
|
||||
MOZ_ASSERT(diff >= 0, "Moving backwards in the filter?");
|
||||
|
||||
// Shift the buffer. We're just moving pointers here, so this is cheap.
|
||||
mLinesInBuffer -= diff;
|
||||
mLinesInBuffer = max(mLinesInBuffer, 0);
|
||||
for (int32_t i = 0; i < mLinesInBuffer; ++i) {
|
||||
swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
154
image/src/Downscaler.h
Normal file
154
image/src/Downscaler.h
Normal file
@ -0,0 +1,154 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Downscaler is a high-quality, streaming image downscaler based upon Skia's
|
||||
* scaling implementation.
|
||||
*/
|
||||
|
||||
#ifndef MOZILLA_IMAGELIB_DOWNSCALER_H_
|
||||
#define MOZILLA_IMAGELIB_DOWNSCALER_H_
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
#ifdef MOZ_ENABLE_SKIA
|
||||
|
||||
namespace skia {
|
||||
class ConvolutionFilter1D;
|
||||
} // namespace skia
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
/**
|
||||
* Downscaler is a high-quality, streaming image downscaler based upon Skia's
|
||||
* scaling implementation.
|
||||
*
|
||||
* Decoders can construct a Downscaler once they know their target size, then
|
||||
* call BeginFrame() for each frame they decode. They should write a decoded row
|
||||
* into the buffer returned by RowBuffer(), and then call CommitRow() to signal
|
||||
* that they have finished.
|
||||
*
|
||||
|
||||
* Because invalidations need to be computed in terms of the scaled version of
|
||||
* the image, Downscaler also tracks them. Decoders can call HasInvalidation()
|
||||
* and TakeInvalidRect() instead of tracking invalidations themselves.
|
||||
*/
|
||||
class Downscaler
|
||||
{
|
||||
public:
|
||||
/// Constructs a new Downscaler which to scale to size @aTargetSize.
|
||||
explicit Downscaler(const nsIntSize& aTargetSize);
|
||||
~Downscaler();
|
||||
|
||||
const nsIntSize& OriginalSize() const { return mOriginalSize; }
|
||||
const nsIntSize& TargetSize() const { return mTargetSize; }
|
||||
|
||||
/**
|
||||
* Begins a new frame and reinitializes the Downscaler.
|
||||
*
|
||||
* @param aOriginalSize The original size of this frame, before scaling.
|
||||
* @param aOutputBuffer The buffer to which the Downscaler should write its
|
||||
* output; this is the same buffer where the Decoder
|
||||
* would write its output when not downscaling during
|
||||
* decode.
|
||||
* @param aHasAlpha Whether or not this frame has an alpha channel.
|
||||
* Performance is a little better if it doesn't have one.
|
||||
*/
|
||||
nsresult BeginFrame(const nsIntSize& aOriginalSize,
|
||||
uint8_t* aOutputBuffer,
|
||||
bool aHasAlpha);
|
||||
|
||||
/// Retrieves the buffer into which the Decoder should write each row.
|
||||
uint8_t* RowBuffer() { return mRowBuffer.get(); }
|
||||
|
||||
/// Signals that the decoder has finished writing a row into the row buffer.
|
||||
void CommitRow();
|
||||
|
||||
/// Returns true if there is a non-empty invalid rect available.
|
||||
bool HasInvalidation() const;
|
||||
|
||||
/// Takes the Downscaler's current invalid rect and resets it.
|
||||
nsIntRect TakeInvalidRect();
|
||||
|
||||
/**
|
||||
* Resets the Downscaler's position in the image, for a new progressive pass
|
||||
* over the same frame. Because the same data structures can be reused, this
|
||||
* is more efficient than calling BeginFrame.
|
||||
*/
|
||||
void ResetForNextProgressivePass();
|
||||
|
||||
private:
|
||||
void DownscaleInputLine();
|
||||
void ReleaseWindow();
|
||||
|
||||
nsIntSize mOriginalSize;
|
||||
nsIntSize mTargetSize;
|
||||
|
||||
uint8_t* mOutputBuffer;
|
||||
|
||||
UniquePtr<uint8_t[]> mRowBuffer;
|
||||
UniquePtr<uint8_t*[]> mWindow;
|
||||
|
||||
UniquePtr<skia::ConvolutionFilter1D> mXFilter;
|
||||
UniquePtr<skia::ConvolutionFilter1D> mYFilter;
|
||||
|
||||
int32_t mWindowCapacity;
|
||||
|
||||
int32_t mLinesInBuffer;
|
||||
int32_t mPrevInvalidatedLine;
|
||||
int32_t mCurrentOutLine;
|
||||
int32_t mCurrentInLine;
|
||||
|
||||
bool mHasAlpha;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/**
|
||||
* Downscaler requires Skia to work, so we provide a dummy implementation if
|
||||
* Skia is disabled that asserts if constructed.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
class Downscaler
|
||||
{
|
||||
public:
|
||||
explicit Downscaler(const nsIntSize&)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT("Skia is not enabled");
|
||||
}
|
||||
|
||||
const nsIntSize& OriginalSize() const { return nsIntSize(); }
|
||||
const nsIntSize& TargetSize() const { return nsIntSize(); }
|
||||
uint8_t* Buffer() { return nullptr; }
|
||||
|
||||
nsresult BeginFrame(const nsIntSize&, uint8_t*, bool)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void CommitRow() { }
|
||||
bool HasInvalidation() const { return false; }
|
||||
nsIntRect TakeInvalidRect() { return nsIntRect(); }
|
||||
void ResetForNextProgressivePass() { }
|
||||
};
|
||||
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
#endif // MOZILLA_IMAGELIB_DOWNSCALER_H_
|
@ -34,8 +34,9 @@ ImageFactory::Initialize()
|
||||
static bool
|
||||
ShouldDownscaleDuringDecode(const nsCString& aMimeType)
|
||||
{
|
||||
// Not enabled for anything yet.
|
||||
return false;
|
||||
return aMimeType.EqualsLiteral(IMAGE_JPEG) ||
|
||||
aMimeType.EqualsLiteral(IMAGE_JPG) ||
|
||||
aMimeType.EqualsLiteral(IMAGE_PJPEG);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
|
@ -1415,6 +1415,17 @@ void
|
||||
RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
|
||||
bool aShouldSyncNotify)
|
||||
{
|
||||
if (mDownscaleDuringDecode) {
|
||||
// We're about to decode again, which may mean that some of the previous
|
||||
// sizes we've decoded at aren't useful anymore. We can allow them to
|
||||
// expire from the cache by unlocking them here. When the decode finishes,
|
||||
// it will send an invalidation that will cause all instances of this image
|
||||
// to redraw. If this image is locked, any surfaces that are still useful
|
||||
// will become locked again when LookupFrame touches them, and the remainder
|
||||
// will eventually expire.
|
||||
SurfaceCache::UnlockSurfaces(ImageKey(this));
|
||||
}
|
||||
|
||||
if (aShouldSyncNotify) {
|
||||
// We can sync notify, which means we can also sync decode.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user