Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-01-20 22:12:46 -05:00
commit 06f5f25641
250 changed files with 5430 additions and 4646 deletions

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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().

View File

@ -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;
}

View File

@ -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.

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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", :)

View File

@ -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

View File

@ -1803,6 +1803,12 @@ Element::IsLabelable() const
return false;
}
bool
Element::IsInteractiveHTMLContent() const
{
return false;
}
css::StyleRule*
Element::GetInlineStyleRule()
{

View File

@ -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?
*

View File

@ -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,

View File

@ -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;

View File

@ -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();
}
}
//

View File

@ -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;

View File

@ -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();

View File

@ -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' }

View File

@ -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:

View File

@ -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.

View File

@ -151,24 +151,36 @@ 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);
// 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();

View File

@ -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

View File

@ -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,

View File

@ -23,6 +23,9 @@ public:
explicit HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo);
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
// nsIDOMHTMLMediaElement
using HTMLMediaElement::GetPaused;

View File

@ -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

View File

@ -26,6 +26,12 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
{
return true;
}
// nsIDOMHTMLIFrameElement
NS_DECL_NSIDOMHTMLIFRAMEELEMENT

View File

@ -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()
{

View File

@ -46,6 +46,9 @@ public:
virtual bool Draggable() const MOZ_OVERRIDE;
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
// nsIDOMHTMLImageElement
NS_DECL_NSIDOMHTMLIMAGEELEMENT

View File

@ -3211,6 +3211,12 @@ HTMLInputElement::Focus(ErrorResult& aError)
return;
}
bool
HTMLInputElement::IsInteractiveHTMLContent() const
{
return mType != NS_FORM_INPUT_HIDDEN;
}
NS_IMETHODIMP
HTMLInputElement::Select()
{

View File

@ -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

View File

@ -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:

View File

@ -32,6 +32,12 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
{
return true;
}
// nsIDOMHTMLLabelElement
NS_DECL_NSIDOMHTMLLABELELEMENT

View File

@ -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()

View File

@ -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 {

View File

@ -45,6 +45,12 @@ HTMLObjectElement::~HTMLObjectElement()
DestroyImageLoadingContent();
}
bool
HTMLObjectElement::IsInteractiveHTMLContent() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
}
bool
HTMLObjectElement::IsDoneAddingChildren()
{

View File

@ -30,6 +30,9 @@ public:
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
// nsIDOMHTMLObjectElement
NS_DECL_NSIDOMHTMLOBJECTELEMENT

View File

@ -147,6 +147,12 @@ public:
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
{
return true;
}
// nsIDOMHTMLSelectElement
NS_DECL_NSIDOMHTMLSELECTELEMENT

View File

@ -53,6 +53,12 @@ public:
virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
{
return true;
}
// nsIDOMHTMLTextAreaElement
NS_DECL_NSIDOMHTMLTEXTAREAELEMENT

View File

@ -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.");

View File

@ -49,6 +49,9 @@ public:
virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
// Element
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
// WebIDL
uint32_t Width() const

View File

@ -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()
{

View File

@ -904,6 +904,7 @@ public:
}
virtual bool IsLabelable() const MOZ_OVERRIDE;
virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);

View File

@ -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

View 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>

View 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>

View File

@ -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]

View 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>

View 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>

View File

@ -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();
}
/*******************************************************************************

View File

@ -282,7 +282,7 @@ parent:
int32_t IMEOpen,
intptr_t NativeIMEContext);
prio(urgent) sync SetInputContext(int32_t IMEEnabled,
prio(urgent) async SetInputContext(int32_t IMEEnabled,
int32_t IMEOpen,
nsString type,
nsString inputmode,

View File

@ -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));

View File

@ -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;

View File

@ -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]

View 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>

View File

@ -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;

View File

@ -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.

View File

@ -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);
};

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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>

View File

@ -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);

View File

@ -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,

View File

@ -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();
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,

View File

@ -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) :

View File

@ -78,17 +78,33 @@ function testBug1109574() {
}
function testMethod() {
// 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] });

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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_

View File

@ -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,
void ComputeFilters(ImageOperations::ResizeMethod method,
int src_size, int dst_size,
int dest_subset_lo, int dest_subset_size,
float scale, ConvolutionFilter1D* output) {
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()));

View File

@ -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_

View File

@ -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())) {

View File

@ -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; }

View File

@ -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);

View File

@ -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:

View File

@ -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));

View File

@ -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() {}
};

View File

@ -22,20 +22,25 @@ 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());
if (IsVsyncEnabled()) {
return;
}
{ // scope lock
mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
mVsyncEnabled = true;
MOZ_ASSERT(!mVsyncThread->IsRunning());
@ -45,13 +50,18 @@ SoftwareDisplay::EnableVsync()
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) {

View File

@ -41,6 +41,7 @@ VsyncSource::GetRefreshTimerVsyncDispatcher()
VsyncSource::Display::Display()
: mDispatcherLock("display dispatcher lock")
, mRefreshTimerNeedsVsync(false)
{
MOZ_ASSERT(NS_IsMainThread());
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
@ -72,20 +73,58 @@ VsyncSource::Display::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aC
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
{ // scope lock
MutexAutoLock lock(mDispatcherLock);
if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
}
}
UpdateVsyncStatus();
}
void
VsyncSource::Display::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(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>
VsyncSource::Display::GetRefreshTimerVsyncDispatcher()

View File

@ -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;
};

View File

@ -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

View File

@ -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");

View File

@ -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()
{

View File

@ -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.

View File

@ -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,
virtual nsresult GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts);
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,
virtual gfxFontGroup* CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
const gfxFontStyle *aStyle,
gfxUserFontSet *aUserFontSet);
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;

View File

@ -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) {

View File

@ -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();

View File

@ -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) +
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");
}

View File

@ -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
View 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
View 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_

View File

@ -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

View File

@ -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