mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Merge mozilla-central to build-system.
This commit is contained in:
commit
08bf89ab6c
@ -65,19 +65,19 @@ MozKeyboard.prototype = {
|
||||
},
|
||||
|
||||
setSelectedOption: function mozKeyboardSetSelectedOption(index) {
|
||||
this._messageManager.broadcastAsyncMessage("Forms:Select:Choice", {
|
||||
this._messageManager.sendAsyncMessage("Forms:Select:Choice", {
|
||||
"index": index
|
||||
});
|
||||
},
|
||||
|
||||
setValue: function mozKeyboardSetValue(value) {
|
||||
this._messageManager.broadcastAsyncMessage("Forms:Input:Value", {
|
||||
this._messageManager.sendAsyncMessage("Forms:Input:Value", {
|
||||
"value": value
|
||||
});
|
||||
},
|
||||
|
||||
setSelectedOptions: function mozKeyboardSetSelectedOptions(indexes) {
|
||||
this._messageManager.broadcastAsyncMessage("Forms:Select:Choice", {
|
||||
this._messageManager.sendAsyncMessage("Forms:Select:Choice", {
|
||||
"indexes": indexes || []
|
||||
});
|
||||
},
|
||||
|
@ -3228,25 +3228,16 @@
|
||||
</method>
|
||||
|
||||
<method name="_finishAnimateTabMove">
|
||||
<parameter name="event"/>
|
||||
<body><![CDATA[
|
||||
if (this.getAttribute("movingtab") != "true")
|
||||
return;
|
||||
|
||||
if (event) {
|
||||
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if ("animDropIndex" in draggedTab._dragData) {
|
||||
let newIndex = draggedTab._dragData.animDropIndex;
|
||||
if (newIndex > draggedTab._tPos)
|
||||
newIndex--;
|
||||
this.tabbrowser.moveTabTo(draggedTab, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
for (let tab of this.tabbrowser.visibleTabs)
|
||||
tab.style.transform = "";
|
||||
|
||||
this.removeAttribute("movingtab");
|
||||
|
||||
this._handleTabSelect();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -3623,7 +3614,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this._finishAnimateTabMove(event);
|
||||
this._finishAnimateTabMove();
|
||||
|
||||
if (effects == "link") {
|
||||
let tab = this._getDragTargetTab(event);
|
||||
@ -3639,7 +3630,7 @@
|
||||
|
||||
var newIndex = this._getDropIndex(event);
|
||||
var scrollRect = tabStrip.scrollClientRect;
|
||||
var rect = this.getBoundingClientRect();
|
||||
var rect = tabStrip.getBoundingClientRect();
|
||||
var minMargin = scrollRect.left - rect.left;
|
||||
var maxMargin = Math.min(minMargin + scrollRect.width,
|
||||
scrollRect.right);
|
||||
@ -3700,7 +3691,14 @@
|
||||
if (draggedTab.parentNode != this || event.shiftKey)
|
||||
this.selectedItem = newTab;
|
||||
} else if (draggedTab && draggedTab.parentNode == this) {
|
||||
this._finishAnimateTabMove(event);
|
||||
// actually move the dragged tab
|
||||
if ("animDropIndex" in draggedTab._dragData) {
|
||||
let newIndex = draggedTab._dragData.animDropIndex;
|
||||
if (newIndex > draggedTab._tPos)
|
||||
newIndex--;
|
||||
this.tabbrowser.moveTabTo(draggedTab, newIndex);
|
||||
}
|
||||
this._finishAnimateTabMove();
|
||||
} else if (draggedTab) {
|
||||
// swap the dropped tab with a new one we create and then close
|
||||
// it in the other window (making it seem to have moved between
|
||||
@ -3769,7 +3767,7 @@
|
||||
// isn't dispatched when the tab is moved within the tabstrip,
|
||||
// see bug 460801.
|
||||
|
||||
this._finishAnimateTabMove(event);
|
||||
this._finishAnimateTabMove();
|
||||
|
||||
var dt = event.dataTransfer;
|
||||
var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
|
@ -274,7 +274,6 @@ _BROWSER_FILES = \
|
||||
social_flyout.html \
|
||||
social_window.html \
|
||||
social_worker.js \
|
||||
browser_bug784142.js \
|
||||
$(NULL)
|
||||
|
||||
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
|
@ -1,61 +0,0 @@
|
||||
const windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Need to enable modal dialogs for this test.
|
||||
Services.prefs.setBoolPref("prompts.tab_modal.enabled", false);
|
||||
|
||||
windowMediator.addListener(promptListener);
|
||||
|
||||
// Open a new tab and have that tab open a new window. This is done to
|
||||
// ensure that the new window is a normal browser window.
|
||||
var script = "window.open('data:text/html,<button id=\"button\" onclick=\"window.close(); alert(5);\">Close</button>', null, 'width=200,height=200');";
|
||||
gBrowser.selectedTab =
|
||||
gBrowser.addTab("data:text/html,<body onload='setTimeout(dotest, 0)'><script>function dotest() { " + script + "}</script></body>");
|
||||
}
|
||||
|
||||
function windowOpened(win)
|
||||
{
|
||||
// Wait for the page in the window to load.
|
||||
waitForFocus(clickButton, win.content);
|
||||
}
|
||||
|
||||
function clickButton(win)
|
||||
{
|
||||
// Set the window in the prompt listener to indicate that the alert window
|
||||
// is now expected to open.
|
||||
promptListener.window = win;
|
||||
|
||||
// Click the Close button in the window.
|
||||
EventUtils.synthesizeMouseAtCenter(win.content.document.getElementById("button"), { }, win);
|
||||
|
||||
windowMediator.removeListener(promptListener);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
|
||||
is(promptListener.message, "window appeared", "modal prompt closer didn't crash");
|
||||
Services.prefs.clearUserPref("prompts.tab_modal.enabled", false);
|
||||
finish();
|
||||
}
|
||||
|
||||
var promptListener = {
|
||||
onWindowTitleChange: function () {},
|
||||
onOpenWindow: function (win) {
|
||||
let domWin = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
|
||||
if (!promptListener.window) {
|
||||
// The first window that is open is the one opened by the new tab.
|
||||
waitForFocus(windowOpened, domWin);
|
||||
}
|
||||
else {
|
||||
// The second window is the alert opened when clicking the Close button in the window
|
||||
ok(promptListener.window.closed, "window has closed");
|
||||
|
||||
// Assign a message so that it can be checked just before the test
|
||||
// finishes to ensure that the alert opened properly.
|
||||
promptListener.message = "window appeared";
|
||||
executeSoon(function () { domWin.close() });
|
||||
}
|
||||
},
|
||||
onCloseWindow: function () {}
|
||||
};
|
@ -3818,7 +3818,7 @@ MOZ_ARG_WITH_BOOL(system-nspr,
|
||||
_USE_SYSTEM_NSPR=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSPR"; then
|
||||
AM_PATH_NSPR(4.9.0, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
AM_PATH_NSPR(4.9.2, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSPR"; then
|
||||
|
@ -5136,7 +5136,7 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
nsresult errorCode;
|
||||
float scaleMinFloat = minScaleStr.ToFloat(&errorCode);
|
||||
|
||||
if (errorCode) {
|
||||
if (NS_FAILED(errorCode)) {
|
||||
scaleMinFloat = kViewportMinScale;
|
||||
}
|
||||
|
||||
@ -5151,7 +5151,7 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
nsresult scaleMaxErrorCode;
|
||||
float scaleMaxFloat = maxScaleStr.ToFloat(&scaleMaxErrorCode);
|
||||
|
||||
if (scaleMaxErrorCode) {
|
||||
if (NS_FAILED(scaleMaxErrorCode)) {
|
||||
scaleMaxFloat = kViewportMaxScale;
|
||||
}
|
||||
|
||||
@ -5210,7 +5210,7 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
|
||||
|
||||
uint32_t width = widthStr.ToInteger(&errorCode);
|
||||
if (errorCode) {
|
||||
if (NS_FAILED(errorCode)) {
|
||||
if (autoSize) {
|
||||
width = screenWidth;
|
||||
} else {
|
||||
@ -5229,7 +5229,7 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
|
||||
uint32_t height = heightStr.ToInteger(&errorCode);
|
||||
|
||||
if (errorCode) {
|
||||
if (NS_FAILED(errorCode)) {
|
||||
height = width * ((float)screenHeight / screenWidth);
|
||||
}
|
||||
|
||||
@ -5244,10 +5244,10 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
|
||||
// We need to perform a conversion, but only if the initial or maximum
|
||||
// scale were set explicitly by the user.
|
||||
if (!scaleStr.IsEmpty() && !scaleErrorCode) {
|
||||
if (!scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode)) {
|
||||
width = NS_MAX(width, (uint32_t)(screenWidth / scaleFloat));
|
||||
height = NS_MAX(height, (uint32_t)(screenHeight / scaleFloat));
|
||||
} else if (!maxScaleStr.IsEmpty() && !scaleMaxErrorCode) {
|
||||
} else if (!maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode)) {
|
||||
width = NS_MAX(width, (uint32_t)(screenWidth / scaleMaxFloat));
|
||||
height = NS_MAX(height, (uint32_t)(screenHeight / scaleMaxFloat));
|
||||
}
|
||||
|
@ -614,6 +614,7 @@ GK_ATOM(OFF, "OFF")
|
||||
GK_ATOM(ol, "ol")
|
||||
GK_ATOM(omitXmlDeclaration, "omit-xml-declaration")
|
||||
GK_ATOM(onabort, "onabort")
|
||||
GK_ATOM(onadapteradded, "onadapteradded")
|
||||
GK_ATOM(onafterprint, "onafterprint")
|
||||
GK_ATOM(onafterscriptexecute, "onafterscriptexecute")
|
||||
GK_ATOM(onalerting, "onalerting")
|
||||
@ -692,6 +693,7 @@ GK_ATOM(onget, "onget")
|
||||
GK_ATOM(onhashchange, "onhashchange")
|
||||
GK_ATOM(onheld, "onheld")
|
||||
GK_ATOM(onholding, "onholding")
|
||||
GK_ATOM(oniccinfochange, "oniccinfochange")
|
||||
GK_ATOM(onincoming, "onincoming")
|
||||
GK_ATOM(oninput, "oninput")
|
||||
GK_ATOM(oninvalid, "oninvalid")
|
||||
|
@ -287,7 +287,8 @@ public:
|
||||
|
||||
mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
|
||||
|
||||
Float blurRadius = mSigma * 3;
|
||||
static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||
int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
|
||||
|
||||
// We need to enlarge and possibly offset our temporary surface
|
||||
// so that things outside of the canvas may cast shadows.
|
||||
@ -311,8 +312,8 @@ public:
|
||||
transform._32 -= mTempRect.y;
|
||||
|
||||
mTarget =
|
||||
mCtx->mTarget->CreateSimilarDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
|
||||
FORMAT_B8G8R8A8);
|
||||
mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
|
||||
FORMAT_B8G8R8A8, mSigma);
|
||||
|
||||
if (!mTarget) {
|
||||
// XXX - Deal with the situation where our temp size is too big to
|
||||
@ -3102,8 +3103,9 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
|
||||
buffer.mGlyphs = &glyphBuf.front();
|
||||
buffer.mNumGlyphs = glyphBuf.size();
|
||||
|
||||
Rect bounds(mBoundingBox.x, mBoundingBox.y, mBoundingBox.width, mBoundingBox.height);
|
||||
if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL) {
|
||||
AdjustedTarget(mCtx)->
|
||||
AdjustedTarget(mCtx, &bounds)->
|
||||
FillGlyphs(scaledFont, buffer,
|
||||
CanvasGeneralPattern().
|
||||
ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
|
||||
@ -3112,7 +3114,7 @@ struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiP
|
||||
RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
|
||||
|
||||
const ContextState& state = *mState;
|
||||
AdjustedTarget(mCtx)->
|
||||
AdjustedTarget(mCtx, &bounds)->
|
||||
Stroke(path, CanvasGeneralPattern().
|
||||
ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_STROKE, mCtx->mTarget),
|
||||
StrokeOptions(state.lineWidth, state.lineJoin,
|
||||
|
@ -14740,14 +14740,8 @@ isPixel(ctx, 0,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 50,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 99,25, 0,255,0,255, 0);
|
||||
isPixel(ctx, 0,49, 0,255,0,255, 0);
|
||||
if (IsAzureEnabled() && IsAzureCairo()) {
|
||||
// Bug 764108
|
||||
todo_isPixel(ctx, 50,49, 0,255,0,255, 0);
|
||||
todo_isPixel(ctx, 99,49, 0,255,0,255, 0);
|
||||
} else {
|
||||
isPixel(ctx, 50,49, 0,255,0,255, 0);
|
||||
isPixel(ctx, 99,49, 0,255,0,255, 0);
|
||||
}
|
||||
isPixel(ctx, 50,49, 0,255,0,255, 0);
|
||||
isPixel(ctx, 99,49, 0,255,0,255, 0);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -1005,11 +1005,22 @@ bool
|
||||
nsEventListenerManager::HasListenersFor(const nsAString& aEventName)
|
||||
{
|
||||
nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
|
||||
return HasListenersFor(atom);
|
||||
}
|
||||
|
||||
bool
|
||||
nsEventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
nsAutoString name;
|
||||
aEventNameWithOn->ToString(name);
|
||||
#endif
|
||||
NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")),
|
||||
"Event name does not start with 'on'");
|
||||
uint32_t count = mListeners.Length();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
nsListenerStruct* ls = &mListeners.ElementAt(i);
|
||||
if (ls->mTypeAtom == atom) {
|
||||
if (ls->mTypeAtom == aEventNameWithOn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ struct nsListenerStruct
|
||||
nsCOMPtr<nsIAtom> mTypeAtom;
|
||||
uint16_t mFlags;
|
||||
uint8_t mListenerType;
|
||||
bool mListenerIsHandler;
|
||||
bool mHandlerIsString;
|
||||
bool mListenerIsHandler : 1;
|
||||
bool mHandlerIsString : 1;
|
||||
|
||||
nsIJSEventListener* GetJSListener() const {
|
||||
return (mListenerType == eJSEventListener) ?
|
||||
@ -183,6 +183,12 @@ public:
|
||||
*/
|
||||
bool HasListenersFor(const nsAString& aEventName);
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one event listener for aEventNameWithOn.
|
||||
* Note that aEventNameWithOn must start with "on"!
|
||||
*/
|
||||
bool HasListenersFor(nsIAtom* aEventNameWithOn);
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one event listener.
|
||||
*/
|
||||
|
@ -4874,7 +4874,8 @@ nsEventStateManager::UnregisterAccessKey(nsIContent* aContent, uint32_t aKey)
|
||||
uint32_t
|
||||
nsEventStateManager::GetRegisteredAccessKey(nsIContent* aContent)
|
||||
{
|
||||
NS_ENSURE_ARG(aContent);
|
||||
NS_ASSERTION(aContent, "Null pointer passed to GetRegisteredAccessKey");
|
||||
NS_ENSURE_TRUE(aContent, 0);
|
||||
|
||||
if (mAccessKeys.IndexOf(aContent) == -1)
|
||||
return 0;
|
||||
|
@ -1688,7 +1688,8 @@ nsHTMLFormElement::CheckValidFormSubmission()
|
||||
nsCOMPtr<nsISimpleEnumerator> theEnum;
|
||||
nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
|
||||
getter_AddRefs(theEnum));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Return true on error here because that's what we always did
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
bool hasObserver = false;
|
||||
rv = theEnum->HasMoreElements(&hasObserver);
|
||||
@ -1698,7 +1699,8 @@ nsHTMLFormElement::CheckValidFormSubmission()
|
||||
if (NS_SUCCEEDED(rv) && hasObserver) {
|
||||
nsCOMPtr<nsIMutableArray> invalidElements =
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Return true on error here because that's what we always did
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
if (!CheckFormValidity(invalidElements.get())) {
|
||||
// For the first invalid submission, we should update element states.
|
||||
|
@ -277,7 +277,7 @@ nsHTMLFrameSetElement::ParseRowCol(const nsAString & aValue,
|
||||
// Otherwise just convert to integer.
|
||||
nsresult err;
|
||||
specs[i].mValue = token.ToInteger(&err);
|
||||
if (err) {
|
||||
if (NS_FAILED(err)) {
|
||||
specs[i].mValue = 0;
|
||||
}
|
||||
}
|
||||
|
@ -917,7 +917,8 @@ class FileMediaResource : public MediaResource
|
||||
{
|
||||
public:
|
||||
FileMediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
|
||||
MediaResource(aDecoder, aChannel, aURI), mSize(-1),
|
||||
MediaResource(aDecoder, aChannel, aURI),
|
||||
mSize(-1),
|
||||
mLock("FileMediaResource.mLock"),
|
||||
mSizeInitialized(false)
|
||||
{
|
||||
@ -958,14 +959,28 @@ public:
|
||||
}
|
||||
virtual int64_t GetLength() {
|
||||
MutexAutoLock lock(mLock);
|
||||
EnsureLengthInitialized();
|
||||
return mSize;
|
||||
if (mInput) {
|
||||
EnsureSizeInitialized();
|
||||
}
|
||||
return mSizeInitialized ? mSize : 0;
|
||||
}
|
||||
virtual int64_t GetNextCachedData(int64_t aOffset)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mInput) {
|
||||
return -1;
|
||||
}
|
||||
EnsureSizeInitialized();
|
||||
return (aOffset < mSize) ? aOffset : -1;
|
||||
}
|
||||
virtual int64_t GetCachedDataEnd(int64_t aOffset) { return NS_MAX(aOffset, mSize); }
|
||||
virtual int64_t GetCachedDataEnd(int64_t aOffset) {
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mInput) {
|
||||
return aOffset;
|
||||
}
|
||||
EnsureSizeInitialized();
|
||||
return NS_MAX(aOffset, mSize);
|
||||
}
|
||||
virtual bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
|
||||
virtual bool IsSuspendedByCache(MediaResource** aActiveResource)
|
||||
{
|
||||
@ -980,16 +995,18 @@ public:
|
||||
|
||||
private:
|
||||
// Ensures mSize is initialized, if it can be.
|
||||
void EnsureLengthInitialized();
|
||||
// mLock must be held when this is called, and mInput must be non-null.
|
||||
void EnsureSizeInitialized();
|
||||
|
||||
// The file size, or -1 if not known. Immutable after Open().
|
||||
// Can be used from any thread.
|
||||
int64_t mSize;
|
||||
|
||||
// This lock handles synchronisation between calls to Close() and
|
||||
// the Read, Seek, etc calls. Close must not be called while a
|
||||
// Read or Seek is in progress since it resets various internal
|
||||
// values to null.
|
||||
// This lock protects mSeekable and mInput.
|
||||
// This lock protects mSeekable, mInput, mSize, and mSizeInitialized.
|
||||
Mutex mLock;
|
||||
|
||||
// Seekable stream interface to file. This can be used from any
|
||||
@ -997,7 +1014,9 @@ private:
|
||||
nsCOMPtr<nsISeekableStream> mSeekable;
|
||||
|
||||
// Input stream for the media data. This can be used from any
|
||||
// thread.
|
||||
// thread. This is annulled when the decoder is being shutdown.
|
||||
// The decoder can be shut down while we're calculating buffered
|
||||
// ranges or seeking, so this must be null-checked before it's used.
|
||||
nsCOMPtr<nsIInputStream> mInput;
|
||||
|
||||
// Whether we've attempted to initialize mSize. Note that mSize can be -1
|
||||
@ -1028,9 +1047,10 @@ private:
|
||||
nsRefPtr<nsMediaDecoder> mDecoder;
|
||||
};
|
||||
|
||||
void FileMediaResource::EnsureLengthInitialized()
|
||||
void FileMediaResource::EnsureSizeInitialized()
|
||||
{
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
NS_ASSERTION(mInput, "Must have file input stream");
|
||||
if (mSizeInitialized) {
|
||||
return;
|
||||
}
|
||||
@ -1048,7 +1068,10 @@ void FileMediaResource::EnsureLengthInitialized()
|
||||
nsresult FileMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
EnsureLengthInitialized();
|
||||
if (!mInput) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
EnsureSizeInitialized();
|
||||
if (mSize == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -1162,9 +1185,9 @@ MediaResource* FileMediaResource::CloneData(nsMediaDecoder* aDecoder)
|
||||
nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
EnsureLengthInitialized();
|
||||
if (!mInput || !mSeekable)
|
||||
return NS_ERROR_FAILURE;
|
||||
EnsureSizeInitialized();
|
||||
int64_t offset = 0;
|
||||
nsresult res = mSeekable->Tell(&offset);
|
||||
NS_ENSURE_SUCCESS(res,res);
|
||||
@ -1192,9 +1215,9 @@ nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32
|
||||
nsresult FileMediaResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
EnsureLengthInitialized();
|
||||
if (!mInput)
|
||||
return NS_ERROR_FAILURE;
|
||||
EnsureSizeInitialized();
|
||||
return mInput->Read(aBuffer, aCount, aBytes);
|
||||
}
|
||||
|
||||
@ -1205,7 +1228,7 @@ nsresult FileMediaResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mSeekable)
|
||||
return NS_ERROR_FAILURE;
|
||||
EnsureLengthInitialized();
|
||||
EnsureSizeInitialized();
|
||||
return mSeekable->Seek(aWhence, aOffset);
|
||||
}
|
||||
|
||||
@ -1216,7 +1239,7 @@ int64_t FileMediaResource::Tell()
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mSeekable)
|
||||
return 0;
|
||||
EnsureLengthInitialized();
|
||||
EnsureSizeInitialized();
|
||||
|
||||
int64_t offset = 0;
|
||||
mSeekable->Tell(&offset);
|
||||
|
@ -3,6 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaResource.h"
|
||||
#include "nsTimeRanges.h"
|
||||
#include "nsMathUtils.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
@ -38,3 +40,52 @@ void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio)
|
||||
aDisplay.height = ConditionDimension(aDisplay.height / aAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) {
|
||||
NS_ASSERTION(length > 0, "Must have positive length");
|
||||
double r = double(offset) / double(length);
|
||||
if (r > 1.0)
|
||||
r = 1.0;
|
||||
return int64_t(double(durationUs) * r);
|
||||
}
|
||||
|
||||
void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
|
||||
int64_t aDurationUsecs,
|
||||
nsTimeRanges* aOutBuffered)
|
||||
{
|
||||
// Nothing to cache if the media takes 0us to play.
|
||||
if (aDurationUsecs <= 0 || !aStream || !aOutBuffered)
|
||||
return;
|
||||
|
||||
// Special case completely cached files. This also handles local files.
|
||||
if (aStream->IsDataCachedToEndOfResource(0)) {
|
||||
aOutBuffered->Add(0, double(aDurationUsecs) / USECS_PER_S);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t totalBytes = aStream->GetLength();
|
||||
|
||||
// If we can't determine the total size, pretend that we have nothing
|
||||
// buffered. This will put us in a state of eternally-low-on-undecoded-data
|
||||
// which is not great, but about the best we can do.
|
||||
if (totalBytes <= 0)
|
||||
return;
|
||||
|
||||
int64_t startOffset = aStream->GetNextCachedData(0);
|
||||
while (startOffset >= 0) {
|
||||
int64_t endOffset = aStream->GetCachedDataEnd(startOffset);
|
||||
// Bytes [startOffset..endOffset] are cached.
|
||||
NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered");
|
||||
NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered");
|
||||
|
||||
int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs);
|
||||
int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs);
|
||||
if (startUs != endUs) {
|
||||
aOutBuffered->Add(double(startUs) / USECS_PER_S,
|
||||
double(endUs) / USECS_PER_S);
|
||||
}
|
||||
startOffset = aStream->GetNextCachedData(endOffset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -85,8 +85,21 @@ private:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
class MediaResource;
|
||||
} // namespace mozilla
|
||||
|
||||
class nsTimeRanges;
|
||||
|
||||
// Estimates the buffered ranges of a MediaResource using a simple
|
||||
// (byteOffset/length)*duration method. Probably inaccurate, but won't
|
||||
// do file I/O, and can be used when we don't have detailed knowledge
|
||||
// of the byte->time mapping of a resource. aDurationUsecs is the duration
|
||||
// of the media in microseconds. Estimated buffered ranges are stored in
|
||||
// aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
|
||||
void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
|
||||
int64_t aDurationUsecs,
|
||||
nsTimeRanges* aOutBuffered);
|
||||
|
||||
// Converts from number of audio frames (aFrames) to microseconds, given
|
||||
// the specified audio rate (aRate). Stores result in aOutUsecs. Returns true
|
||||
// if the operation succeeded, or false if there was an integer overflow
|
||||
|
@ -233,6 +233,7 @@ VideoData* VideoData::Create(nsVideoInfo& aInfo,
|
||||
data.mPicSize = gfxIntSize(aPicture.width, aPicture.height);
|
||||
data.mStereoMode = aInfo.mStereoMode;
|
||||
|
||||
videoImage->SetDelayedConversion(true);
|
||||
videoImage->SetData(data);
|
||||
return v.forget();
|
||||
}
|
||||
|
@ -20,6 +20,12 @@ extern "C" {
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// On B2G estimate the buffered ranges rather than calculating them explicitly.
|
||||
// This prevents us doing I/O on the main thread, which is prohibited in B2G.
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#define OGG_ESTIMATE_BUFFERED 1
|
||||
#endif
|
||||
|
||||
// Un-comment to enable logging of seek bisections.
|
||||
//#define SEEK_LOGGING
|
||||
|
||||
@ -532,7 +538,7 @@ nsresult nsOggReader::DecodeOpus(ogg_packet* aPacket) {
|
||||
/*4*/{ {0.4226f,0}, {0,0.4226f}, {0.366f,0.2114f}, {0.2114f,0.366f}},
|
||||
/*5*/{ {0.651f,0}, {0.46f,0.46f}, {0,0.651f}, {0.5636f,0.3254f}, {0.3254f,0.5636f}},
|
||||
/*6*/{ {0.529f,0}, {0.3741f,0.3741f}, {0,0.529f}, {0.4582f,0.2645f}, {0.2645f,0.4582f}, {0.3741f,0.3741f}},
|
||||
/*7*/{ {0.4553f,0}, {0.322f,0.322f}, {0,4553}, {0.3943f,0.2277f}, {0.2277f,0.3943f}, {0.2788f,0.2788f}, {0.322f,0.322f}},
|
||||
/*7*/{ {0.4553f,0}, {0.322f,0.322f}, {0,0.4553f}, {0.3943f,0.2277f}, {0.2277f,0.3943f}, {0.2788f,0.2788f}, {0.322f,0.322f}},
|
||||
/*8*/{ {0.3886f,0}, {0.2748f,0.2748f}, {0,0.3886f}, {0.3366f,0.1943f}, {0.1943f,0.3366f}, {0.3366f,0.1943f}, {0.1943f,0.3366f}, {0.2748f,0.2748f}},
|
||||
};
|
||||
for (int32_t i = 0; i < frames; i++) {
|
||||
@ -1620,6 +1626,17 @@ nsresult nsOggReader::SeekBisection(int64_t aTarget,
|
||||
|
||||
nsresult nsOggReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime)
|
||||
{
|
||||
#ifdef OGG_ESTIMATE_BUFFERED
|
||||
MediaResource* stream = mDecoder->GetResource();
|
||||
int64_t durationUs = 0;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
durationUs = mDecoder->GetStateMachine()->GetDuration();
|
||||
}
|
||||
GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
|
||||
|
||||
return NS_OK;
|
||||
#else
|
||||
// HasAudio and HasVideo are not used here as they take a lock and cause
|
||||
// a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
|
||||
// after metadata is read.
|
||||
@ -1720,6 +1737,7 @@ nsresult nsOggReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime)
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool nsOggReader::IsKnownStream(uint32_t aSerial)
|
||||
|
@ -290,13 +290,6 @@ nsresult nsMediaPluginReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t
|
||||
return DecodeToTarget(aTarget);
|
||||
}
|
||||
|
||||
static uint64_t BytesToTime(int64_t offset, uint64_t length, uint64_t durationUs) {
|
||||
double perc = double(offset) / double(length);
|
||||
if (perc > 1.0)
|
||||
perc = 1.0;
|
||||
return uint64_t(double(durationUs) * perc);
|
||||
}
|
||||
|
||||
nsresult nsMediaPluginReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime)
|
||||
{
|
||||
if (!mPlugin)
|
||||
@ -307,37 +300,7 @@ nsresult nsMediaPluginReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStar
|
||||
int64_t durationUs = 0;
|
||||
mPlugin->GetDuration(mPlugin, &durationUs);
|
||||
|
||||
// Nothing to cache if the media takes 0us to play.
|
||||
if (!durationUs)
|
||||
return NS_OK;
|
||||
|
||||
// Special case completely cached files. This also handles local files.
|
||||
if (stream->IsDataCachedToEndOfResource(0)) {
|
||||
aBuffered->Add(0, durationUs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int64_t totalBytes = stream->GetLength();
|
||||
|
||||
// If we can't determine the total size, pretend that we have nothing
|
||||
// buffered. This will put us in a state of eternally-low-on-undecoded-data
|
||||
// which is not get, but about the best we can do.
|
||||
if (totalBytes == -1)
|
||||
return NS_OK;
|
||||
|
||||
int64_t startOffset = stream->GetNextCachedData(0);
|
||||
while (startOffset >= 0) {
|
||||
int64_t endOffset = stream->GetCachedDataEnd(startOffset);
|
||||
// Bytes [startOffset..endOffset] are cached.
|
||||
NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered");
|
||||
NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered");
|
||||
|
||||
uint64_t startUs = BytesToTime(startOffset, totalBytes, durationUs);
|
||||
uint64_t endUs = BytesToTime(endOffset, totalBytes, durationUs);
|
||||
if (startUs != endUs) {
|
||||
aBuffered->Add(startUs, endUs);
|
||||
}
|
||||
startOffset = stream->GetNextCachedData(endOffset);
|
||||
}
|
||||
GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1891,7 +1891,7 @@ nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer,
|
||||
// found "static" XUL element count hint
|
||||
nsresult strErr = NS_OK;
|
||||
staticCount = staticValue.ToInteger(&strErr);
|
||||
if (strErr)
|
||||
if (NS_FAILED(strErr))
|
||||
staticCount = 0;
|
||||
} else {
|
||||
// compute the "static" XUL element count
|
||||
|
@ -1850,7 +1850,7 @@ nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResu
|
||||
nsCOMPtr<nsISupports> ref;
|
||||
nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
return 0;
|
||||
|
||||
nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
|
||||
if (container) {
|
||||
|
@ -78,9 +78,15 @@ let DOMApplicationRegistry = {
|
||||
#endif
|
||||
let currentId = 1;
|
||||
dirList.forEach((function(dir) {
|
||||
let curFile = FileUtils.getFile(dir, ["webapps", "webapps.json"], true);
|
||||
if (curFile.exists()) {
|
||||
let appDir = FileUtils.getDir(dir, ["webapps"]);
|
||||
let curFile;
|
||||
try {
|
||||
// getFile calls getDir with |shouldCreate = true|, so we have
|
||||
// to wrap in a try..catch in case the file does not exist on a
|
||||
// read-only partition.
|
||||
curFile = FileUtils.getFile(dir, ["webapps", "webapps.json"], false);
|
||||
} catch(e) { }
|
||||
if (curFile && curFile.exists()) {
|
||||
let appDir = FileUtils.getDir(dir, ["webapps"], false);
|
||||
this._loadJSONAsync(curFile, (function(aData) {
|
||||
if (!aData) {
|
||||
return;
|
||||
|
@ -2518,7 +2518,7 @@ nsGlobalWindow::DialogsAreBlocked(bool *aBeingAbused)
|
||||
|
||||
nsGlobalWindow *topWindow = GetScriptableTop();
|
||||
if (!topWindow) {
|
||||
NS_ASSERTION(!mDocShell, "DialogsAreBlocked() called without a top window?");
|
||||
NS_ERROR("DialogsAreBlocked() called without a top window?");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7652,11 +7652,9 @@ nsGlobalWindow::ActivateOrDeactivate(bool aActivate)
|
||||
|
||||
// widgetListener should be a nsXULWindow
|
||||
nsIWidgetListener* listener = topLevelWidget->GetWidgetListener();
|
||||
if (listener) {
|
||||
nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow();
|
||||
nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window));
|
||||
topLevelWindow = do_GetInterface(req);
|
||||
}
|
||||
nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow();
|
||||
nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window));
|
||||
topLevelWindow = do_GetInterface(req);
|
||||
}
|
||||
if (topLevelWindow) {
|
||||
nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow));
|
||||
|
@ -83,7 +83,7 @@ public:
|
||||
sc->GetNativeGlobal(),
|
||||
adapter,
|
||||
aValue);
|
||||
bool result = NS_SUCCEEDED(rv) ? true : false;
|
||||
bool result = NS_SUCCEEDED(rv);
|
||||
if (!result) {
|
||||
NS_WARNING("Cannot create native object!");
|
||||
SetError(NS_LITERAL_STRING("BluetoothNativeObjectError"));
|
||||
@ -247,6 +247,20 @@ BluetoothManager::HandleMozsettingChanged(const PRUnichar* aData)
|
||||
}
|
||||
|
||||
bool enabled = value.toBoolean();
|
||||
bool isEnabled = (bs->IsEnabledInternal() > 0);
|
||||
if (!isEnabled && enabled) {
|
||||
if (NS_FAILED(bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), this))) {
|
||||
NS_ERROR("Failed to register object with observer!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else if (isEnabled && !enabled){
|
||||
if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), this))) {
|
||||
NS_WARNING("Failed to unregister object with observer!");
|
||||
}
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> resultTask = new ToggleBtResultTask(this, enabled);
|
||||
|
||||
if (enabled) {
|
||||
@ -330,17 +344,19 @@ BluetoothManager::GetDefaultAdapter(nsIDOMDOMRequest** aAdapter)
|
||||
// static
|
||||
already_AddRefed<BluetoothManager>
|
||||
BluetoothManager::Create(nsPIDOMWindow* aWindow) {
|
||||
|
||||
nsRefPtr<BluetoothManager> manager = new BluetoothManager(aWindow);
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
NS_WARNING("BluetoothService not available!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_FAILED(bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), manager))) {
|
||||
NS_ERROR("Failed to register object with observer!");
|
||||
return nullptr;
|
||||
|
||||
bool isEnabled = (bs->IsEnabledInternal() > 0);
|
||||
if (isEnabled) {
|
||||
if (NS_FAILED(bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), manager))) {
|
||||
NS_ERROR("Failed to register object with observer!");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return manager.forget();
|
||||
@ -390,13 +406,28 @@ NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
|
||||
void
|
||||
BluetoothManager::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
if (aData.name().EqualsLiteral("AdapterAdded")) {
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(NS_LITERAL_STRING("adapteradded"), false, false);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to init the adapteradded event!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling manager signal: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
|
||||
NS_WARNING(warningMsg.get());
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling manager signal: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
|
||||
NS_WARNING(warningMsg.get());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_EVENT_HANDLER(BluetoothManager, enabled)
|
||||
NS_IMPL_EVENT_HANDLER(BluetoothManager, disabled)
|
||||
NS_IMPL_EVENT_HANDLER(BluetoothManager, adapteradded)
|
||||
|
@ -225,6 +225,7 @@ public:
|
||||
virtual bool SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey) = 0;
|
||||
virtual bool SetPairingConfirmationInternal(const nsAString& aDeviceAddress, bool aConfirm) = 0;
|
||||
virtual bool SetAuthorizationInternal(const nsAString& aDeviceAddress, bool aAllow) = 0;
|
||||
virtual int IsEnabledInternal() = 0;
|
||||
|
||||
/**
|
||||
* Due to the fact that some operations require multiple calls, a
|
||||
|
@ -128,6 +128,16 @@ StartStopGonkBluetooth(bool aShouldEnable)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
BluetoothGonkService::IsEnabledInternal()
|
||||
{
|
||||
if (!EnsureBluetoothInit()) {
|
||||
NS_ERROR("Failed to load bluedroid library.\n");
|
||||
return false;
|
||||
}
|
||||
return IsBluetoothEnabled();
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothGonkService::StartInternal()
|
||||
{
|
||||
|
@ -52,6 +52,11 @@ public:
|
||||
* otherwise
|
||||
*/
|
||||
virtual nsresult StopInternal();
|
||||
|
||||
/**
|
||||
* @return true if bluetooth daemon is enabled, false otherwise
|
||||
*/
|
||||
virtual int IsEnabledInternal();
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
@ -831,7 +831,6 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
|
||||
BluetoothValue v;
|
||||
|
||||
if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) {
|
||||
|
||||
DBusMessageIter iter;
|
||||
|
||||
if (!dbus_message_iter_init(aMsg, &iter)) {
|
||||
@ -892,6 +891,16 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
|
||||
errorStr,
|
||||
sDeviceProperties,
|
||||
ArrayLength(sDeviceProperties));
|
||||
} else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) {
|
||||
const char* str;
|
||||
if (!dbus_message_get_args(aMsg, &err,
|
||||
DBUS_TYPE_OBJECT_PATH, &str,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
|
||||
errorStr.AssignLiteral("Cannot parse manager path!");
|
||||
} else {
|
||||
v = NS_ConvertUTF8toUTF16(str);
|
||||
}
|
||||
} else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "PropertyChanged")) {
|
||||
ParsePropertyChange(aMsg,
|
||||
v,
|
||||
@ -1028,6 +1037,14 @@ BluetoothDBusService::StopInternal()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
BluetoothDBusService::IsEnabledInternal()
|
||||
{
|
||||
// assume bluetooth is always enabled on desktop
|
||||
return true;
|
||||
}
|
||||
|
||||
class DefaultAdapterPropertiesRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -82,6 +82,8 @@ public:
|
||||
virtual bool
|
||||
SetAuthorizationInternal(const nsAString& aDeviceAddress, bool aAllow);
|
||||
|
||||
virtual int IsEnabledInternal();
|
||||
|
||||
private:
|
||||
nsresult SendGetPropertyMessage(const nsAString& aPath,
|
||||
const char* aInterface,
|
||||
|
@ -9,7 +9,7 @@
|
||||
interface nsIDOMDOMRequest;
|
||||
interface nsIDOMBluetoothAdapter;
|
||||
|
||||
[scriptable, builtinclass, uuid(b9e0a4a2-fa84-402d-8830-d0f3625f120a)]
|
||||
[scriptable, builtinclass, uuid(d27ec867-949f-4585-b718-d2352e420ec6)]
|
||||
interface nsIDOMBluetoothManager : nsIDOMEventTarget
|
||||
{
|
||||
readonly attribute bool enabled;
|
||||
@ -18,4 +18,5 @@ interface nsIDOMBluetoothManager : nsIDOMEventTarget
|
||||
|
||||
[implicit_jscontext] attribute jsval onenabled;
|
||||
[implicit_jscontext] attribute jsval ondisabled;
|
||||
[implicit_jscontext] attribute jsval onadapteradded;
|
||||
};
|
||||
|
@ -6,12 +6,13 @@
|
||||
|
||||
interface nsIDOMEventListener;
|
||||
interface nsIDOMDOMRequest;
|
||||
interface nsIDOMMozMobileICCInfo;
|
||||
interface nsIDOMMozMobileConnectionInfo;
|
||||
interface nsIDOMMozMobileNetworkInfo;
|
||||
interface nsIDOMMozMobileCellInfo;
|
||||
interface nsIDOMMozIccManager;
|
||||
|
||||
[scriptable, builtinclass, uuid(fda3bb30-3259-4ba7-8cff-c486c30821a4)]
|
||||
[scriptable, builtinclass, uuid(d9009d90-a4b3-44fd-a592-42b09f330fe5)]
|
||||
interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
||||
{
|
||||
/**
|
||||
@ -22,6 +23,11 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
||||
*/
|
||||
readonly attribute DOMString cardState;
|
||||
|
||||
/**
|
||||
* Information stored in the device's ICC card.
|
||||
*/
|
||||
readonly attribute nsIDOMMozMobileICCInfo iccInfo;
|
||||
|
||||
/**
|
||||
* Information about the voice connection.
|
||||
*/
|
||||
@ -210,6 +216,12 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
||||
*/
|
||||
[implicit_jscontext] attribute jsval oncardstatechange;
|
||||
|
||||
/**
|
||||
* The 'iccinfochange' event is notified whenever the icc info object
|
||||
* changes.
|
||||
*/
|
||||
[implicit_jscontext] attribute jsval oniccinfochange;
|
||||
|
||||
/**
|
||||
* The 'voicechange' event is notified whenever the voice connection object
|
||||
* changes.
|
||||
@ -334,3 +346,17 @@ interface nsIDOMMozMobileCellInfo: nsISupports
|
||||
*/
|
||||
readonly attribute unsigned long gsmCellId;
|
||||
};
|
||||
|
||||
[scriptable, uuid(109c1117-1199-47aa-aad2-ea9f456220fa)]
|
||||
interface nsIDOMMozMobileICCInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* Mobile Country Code (MCC) of the subscriber's home network.
|
||||
*/
|
||||
readonly attribute unsigned short mcc;
|
||||
|
||||
/**
|
||||
* Mobile Network Code (MNC) of the subscriber's home network.
|
||||
*/
|
||||
readonly attribute unsigned short mnc;
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMMozMobileICCInfo;
|
||||
interface nsIDOMMozMobileConnectionInfo;
|
||||
interface nsIDOMMozMobileNetworkInfo;
|
||||
interface nsIDOMDOMRequest;
|
||||
@ -13,10 +14,11 @@ interface nsIDOMWindow;
|
||||
* XPCOM component (in the content process) that provides the mobile
|
||||
* network information.
|
||||
*/
|
||||
[scriptable, uuid(fb3fac34-c1c2-45a9-ad18-a7af0f7997c9)]
|
||||
[scriptable, uuid(63787ba1-5091-450b-8810-d321a8b4f77a)]
|
||||
interface nsIMobileConnectionProvider : nsISupports
|
||||
{
|
||||
readonly attribute DOMString cardState;
|
||||
readonly attribute nsIDOMMozMobileICCInfo iccInfo;
|
||||
readonly attribute nsIDOMMozMobileConnectionInfo voiceConnectionInfo;
|
||||
readonly attribute nsIDOMMozMobileConnectionInfo dataConnectionInfo;
|
||||
readonly attribute DOMString networkSelectionMode;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define VOICECHANGE_EVENTNAME NS_LITERAL_STRING("voicechange")
|
||||
#define DATACHANGE_EVENTNAME NS_LITERAL_STRING("datachange")
|
||||
#define CARDSTATECHANGE_EVENTNAME NS_LITERAL_STRING("cardstatechange")
|
||||
#define ICCINFOCHANGE_EVENTNAME NS_LITERAL_STRING("iccinfochange")
|
||||
#define USSDRECEIVED_EVENTNAME NS_LITERAL_STRING("ussdreceived")
|
||||
|
||||
DOMCI_DATA(MozMobileConnection, mozilla::dom::network::MobileConnection)
|
||||
@ -27,6 +28,7 @@ namespace network {
|
||||
const char* kVoiceChangedTopic = "mobile-connection-voice-changed";
|
||||
const char* kDataChangedTopic = "mobile-connection-data-changed";
|
||||
const char* kCardStateChangedTopic = "mobile-connection-cardstate-changed";
|
||||
const char* kIccInfoChangedTopic = "mobile-connection-iccinfo-changed";
|
||||
const char* kUssdReceivedTopic = "mobile-connection-ussd-received";
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MobileConnection)
|
||||
@ -51,6 +53,7 @@ NS_IMPL_ADDREF_INHERITED(MobileConnection, nsDOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(MobileConnection, nsDOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_EVENT_HANDLER(MobileConnection, cardstatechange)
|
||||
NS_IMPL_EVENT_HANDLER(MobileConnection, iccinfochange)
|
||||
NS_IMPL_EVENT_HANDLER(MobileConnection, voicechange)
|
||||
NS_IMPL_EVENT_HANDLER(MobileConnection, datachange)
|
||||
NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived)
|
||||
@ -80,6 +83,7 @@ MobileConnection::Init(nsPIDOMWindow* aWindow)
|
||||
obs->AddObserver(this, kVoiceChangedTopic, false);
|
||||
obs->AddObserver(this, kDataChangedTopic, false);
|
||||
obs->AddObserver(this, kCardStateChangedTopic, false);
|
||||
obs->AddObserver(this, kIccInfoChangedTopic, false);
|
||||
obs->AddObserver(this, kUssdReceivedTopic, false);
|
||||
|
||||
mIccManager = new icc::IccManager();
|
||||
@ -98,6 +102,7 @@ MobileConnection::Shutdown()
|
||||
obs->RemoveObserver(this, kVoiceChangedTopic);
|
||||
obs->RemoveObserver(this, kDataChangedTopic);
|
||||
obs->RemoveObserver(this, kCardStateChangedTopic);
|
||||
obs->RemoveObserver(this, kIccInfoChangedTopic);
|
||||
obs->RemoveObserver(this, kUssdReceivedTopic);
|
||||
|
||||
if (mIccManager) {
|
||||
@ -128,6 +133,11 @@ MobileConnection::Observe(nsISupports* aSubject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, kIccInfoChangedTopic)) {
|
||||
InternalDispatchEvent(ICCINFOCHANGE_EVENTNAME);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, kUssdReceivedTopic)) {
|
||||
nsString ussd;
|
||||
ussd.Assign(aData);
|
||||
@ -157,6 +167,16 @@ MobileConnection::GetCardState(nsAString& cardState)
|
||||
return mProvider->GetCardState(cardState);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::GetIccInfo(nsIDOMMozMobileICCInfo** aIccInfo)
|
||||
{
|
||||
if (!mProvider) {
|
||||
*aIccInfo = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetIccInfo(aIccInfo);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
|
||||
{
|
||||
|
@ -8,3 +8,8 @@ qemu = true
|
||||
b2g = true
|
||||
browser = false
|
||||
qemu = true
|
||||
|
||||
[test_mobile_iccinfo.js]
|
||||
b2g = true
|
||||
browser = false
|
||||
qemu = true
|
||||
|
18
dom/network/tests/marionette/test_mobile_iccinfo.js
Normal file
18
dom/network/tests/marionette/test_mobile_iccinfo.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let connection = navigator.mozMobileConnection;
|
||||
ok(connection instanceof MozMobileConnection,
|
||||
"connection is instanceof " + connection.constructor);
|
||||
|
||||
// The emulator's hard coded mcc and mnc codes.
|
||||
// See it here {B2G_HOME}/external/qemu/telephony/android_modem.c#L2465.
|
||||
is(connection.iccInfo.mcc, 310);
|
||||
is(connection.iccInfo.mnc, 260);
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
@ -18,8 +18,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,9 @@ SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.ignoreAllUncaughtExceptions();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,9 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
|
||||
if (isOSXLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7, see bug 705047");
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
if (isOSXLion || isOSXMtnLion) {
|
||||
todo(false, "Can't test plugin crash notification on OS X 10.7 or 10.8, see bug 705047");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,8 @@ const DEBUG = RIL.DEBUG_CONTENT_HELPER;
|
||||
|
||||
const RILCONTENTHELPER_CID =
|
||||
Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
|
||||
const MOBILEICCINFO_CID =
|
||||
Components.ID("{8649c12f-f8f4-4664-bbdd-7d115c23e2a7}");
|
||||
const MOBILECONNECTIONINFO_CID =
|
||||
Components.ID("{a35cfd39-2d93-4489-ac7d-396475dacb27}");
|
||||
const MOBILENETWORKINFO_CID =
|
||||
@ -40,6 +42,7 @@ const VOICEMAILSTATUS_CID=
|
||||
|
||||
const RIL_IPC_MSG_NAMES = [
|
||||
"RIL:CardStateChanged",
|
||||
"RIL:IccInfoChanged",
|
||||
"RIL:VoiceInfoChanged",
|
||||
"RIL:DataInfoChanged",
|
||||
"RIL:EnumerateCalls",
|
||||
@ -64,6 +67,7 @@ const RIL_IPC_MSG_NAMES = [
|
||||
const kVoiceChangedTopic = "mobile-connection-voice-changed";
|
||||
const kDataChangedTopic = "mobile-connection-data-changed";
|
||||
const kCardStateChangedTopic = "mobile-connection-cardstate-changed";
|
||||
const kIccInfoChangedTopic = "mobile-connection-iccinfo-changed";
|
||||
const kUssdReceivedTopic = "mobile-connection-ussd-received";
|
||||
const kStkCommandTopic = "icc-manager-stk-command";
|
||||
const kStkSessionEndTopic = "icc-manager-stk-session-end";
|
||||
@ -76,6 +80,23 @@ XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
function MobileICCInfo() {}
|
||||
MobileICCInfo.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozMobileICCInfo]),
|
||||
classID: MOBILEICCINFO_CID,
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
classID: MOBILEICCINFO_CID,
|
||||
classDescription: "MobileICCInfo",
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||
interfaces: [Ci.nsIDOMMozMobileICCInfo]
|
||||
}),
|
||||
|
||||
// nsIDOMMozMobileICCInfo
|
||||
|
||||
mcc: 0,
|
||||
mnc: 0
|
||||
};
|
||||
|
||||
function MobileConnectionInfo() {}
|
||||
MobileConnectionInfo.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozMobileConnectionInfo]),
|
||||
@ -157,6 +178,7 @@ VoicemailStatus.prototype = {
|
||||
};
|
||||
|
||||
function RILContentHelper() {
|
||||
this.iccInfo = new MobileICCInfo();
|
||||
this.voiceConnectionInfo = new MobileConnectionInfo();
|
||||
this.dataConnectionInfo = new MobileConnectionInfo();
|
||||
|
||||
@ -172,6 +194,7 @@ function RILContentHelper() {
|
||||
return;
|
||||
}
|
||||
this.cardState = rilContext.cardState;
|
||||
this.updateICCInfo(rilContext.icc, this.iccInfo);
|
||||
this.updateConnectionInfo(rilContext.voice, this.voiceConnectionInfo);
|
||||
this.updateConnectionInfo(rilContext.data, this.dataConnectionInfo);
|
||||
}
|
||||
@ -188,6 +211,11 @@ RILContentHelper.prototype = {
|
||||
interfaces: [Ci.nsIMobileConnectionProvider,
|
||||
Ci.nsIRILContentHelper]}),
|
||||
|
||||
updateICCInfo: function updateICCInfo(srcInfo, destInfo) {
|
||||
destInfo.mcc = srcInfo.mcc;
|
||||
destInfo.mnc = srcInfo.mnc;
|
||||
},
|
||||
|
||||
updateConnectionInfo: function updateConnectionInfo(srcInfo, destInfo) {
|
||||
for (let key in srcInfo) {
|
||||
if ((key != "network") && (key != "cell")) {
|
||||
@ -227,9 +255,10 @@ RILContentHelper.prototype = {
|
||||
|
||||
// nsIRILContentHelper
|
||||
|
||||
cardState: RIL.GECKO_CARDSTATE_UNAVAILABLE,
|
||||
voiceConnectionInfo: null,
|
||||
dataConnectionInfo: null,
|
||||
cardState: RIL.GECKO_CARDSTATE_UNAVAILABLE,
|
||||
iccInfo: null,
|
||||
voiceConnectionInfo: null,
|
||||
dataConnectionInfo: null,
|
||||
networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
|
||||
|
||||
/**
|
||||
@ -584,6 +613,10 @@ RILContentHelper.prototype = {
|
||||
Services.obs.notifyObservers(null, kCardStateChangedTopic, null);
|
||||
}
|
||||
break;
|
||||
case "RIL:IccInfoChanged":
|
||||
this.updateICCInfo(msg.json, this.iccInfo);
|
||||
Services.obs.notifyObservers(null, kIccInfoChangedTopic, null);
|
||||
break;
|
||||
case "RIL:VoiceInfoChanged":
|
||||
this.updateConnectionInfo(msg.json, this.voiceConnectionInfo);
|
||||
Services.obs.notifyObservers(null, kVoiceChangedTopic, null);
|
||||
|
@ -422,7 +422,7 @@ RadioInterfaceLayer.prototype = {
|
||||
" timestamp=" + message.localTimeStampInMS);
|
||||
break;
|
||||
case "iccinfochange":
|
||||
this.rilContext.icc = message;
|
||||
this.handleICCInfoChange(message);
|
||||
break;
|
||||
case "iccGetCardLock":
|
||||
case "iccSetCardLock":
|
||||
@ -1017,6 +1017,17 @@ RadioInterfaceLayer.prototype = {
|
||||
[message.datacalls, message.datacalls.length]);
|
||||
},
|
||||
|
||||
handleICCInfoChange: function handleICCInfoChange(message) {
|
||||
let oldIcc = this.rilContext.icc;
|
||||
this.rilContext.icc = message;
|
||||
if (oldIcc && (oldIcc.mcc == message.mcc || oldIcc.mnc == message.mnc)) {
|
||||
return;
|
||||
}
|
||||
// RIL:IccInfoChanged corresponds to a DOM event that gets fired only
|
||||
// when the MCC or MNC codes have changed.
|
||||
ppmm.broadcastAsyncMessage("RIL:IccInfoChanged", message);
|
||||
},
|
||||
|
||||
handleICCCardLockResult: function handleICCCardLockResult(message) {
|
||||
this._sendRequestResults("RIL:CardLockResult", message);
|
||||
},
|
||||
|
@ -1364,7 +1364,7 @@ let RIL = {
|
||||
pin2: null,
|
||||
type: EF_TYPE_LINEAR_FIXED,
|
||||
callback: callback,
|
||||
onerror: error
|
||||
onerror: error,
|
||||
loadAll: true,
|
||||
requestId: options.requestId,
|
||||
});
|
||||
|
@ -71,11 +71,10 @@ DOMWifiManager.prototype = {
|
||||
// Maintain this state for synchronous APIs.
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "disconnected";
|
||||
this._enabled = true;
|
||||
this._enabled = false;
|
||||
this._lastConnectionInfo = null;
|
||||
|
||||
const messages = ["WifiManager:setEnabled:Return:OK", "WifiManager:setEnabled:Return:NO",
|
||||
"WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
|
||||
const messages = ["WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
|
||||
"WifiManager:associate:Return:OK", "WifiManager:associate:Return:NO",
|
||||
"WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
|
||||
"WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
|
||||
@ -125,19 +124,6 @@ DOMWifiManager.prototype = {
|
||||
|
||||
let request;
|
||||
switch (aMessage.name) {
|
||||
case "WifiManager:setEnabled:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
this._enabled = msg.data;
|
||||
if (!this._enabled)
|
||||
this._currentNetwork = null;
|
||||
Services.DOMRequest.fireSuccess(request, true);
|
||||
break;
|
||||
|
||||
case "WifiManager:setEnabled:Return:NO":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireError(request, "Unable to initialize wifi");
|
||||
break;
|
||||
|
||||
case "WifiManager:getNetworks:Return:OK":
|
||||
request = this.takeRequest(msg.rid);
|
||||
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
|
||||
@ -284,14 +270,6 @@ DOMWifiManager.prototype = {
|
||||
},
|
||||
|
||||
// nsIDOMWifiManager
|
||||
setEnabled: function nsIDOMWifiManager_setEnabled(enabled) {
|
||||
if (!this._hasPrivileges)
|
||||
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
|
||||
var request = this.createRequest();
|
||||
this._sendMessageForRequest("WifiManager:setEnabled", enabled, request);
|
||||
return request;
|
||||
},
|
||||
|
||||
getNetworks: function nsIDOMWifiManager_getNetworks() {
|
||||
if (!this._hasPrivileges)
|
||||
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
|
||||
|
@ -19,6 +19,7 @@ const WIFIWORKER_CID = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e
|
||||
const WIFIWORKER_WORKER = "resource://gre/modules/wifi_worker.js";
|
||||
|
||||
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
|
||||
const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
|
||||
const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
|
||||
@ -27,6 +28,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
|
||||
"@mozilla.org/network/manager;1",
|
||||
"nsINetworkManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
||||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
// A note about errors and error handling in this file:
|
||||
// The libraries that we use in this file are intended for C code. For
|
||||
// C code, it is natural to return -1 for errors and 0 for success.
|
||||
@ -998,8 +1003,8 @@ var WifiManager = (function() {
|
||||
}
|
||||
|
||||
// Get wifi interface and load wifi driver when enable Ap mode.
|
||||
manager.setWifiApEnabled = function(enable, callback) {
|
||||
if (enable) {
|
||||
manager.setWifiApEnabled = function(enabled, callback) {
|
||||
if (enabled) {
|
||||
getProperty("wifi.interface", "tiwlan0", function (ifname) {
|
||||
if (!ifname) {
|
||||
callback(-1, null);
|
||||
@ -1362,7 +1367,7 @@ function WifiWorker() {
|
||||
|
||||
this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageListenerManager);
|
||||
const messages = ["WifiManager:setEnabled", "WifiManager:getNetworks",
|
||||
const messages = ["WifiManager:getNetworks",
|
||||
"WifiManager:associate", "WifiManager:forget",
|
||||
"WifiManager:wps", "WifiManager:getState",
|
||||
"WifiManager:managerFinished"];
|
||||
@ -1371,6 +1376,8 @@ function WifiWorker() {
|
||||
this._mm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
|
||||
this.wantScanResults = [];
|
||||
|
||||
this._needToEnableNetworks = false;
|
||||
@ -1464,6 +1471,7 @@ function WifiWorker() {
|
||||
WifiManager.onsupplicantconnection = function() {
|
||||
debug("Connected to supplicant");
|
||||
WifiManager.enabled = true;
|
||||
self._updateWifiSetting(true);
|
||||
WifiManager.getMacAddress(function (mac) {
|
||||
debug("Got mac: " + mac);
|
||||
});
|
||||
@ -1475,44 +1483,47 @@ function WifiWorker() {
|
||||
self.waitForScan(function firstScan() {});
|
||||
});
|
||||
|
||||
// Check if we need to fire request replies first:
|
||||
if (self._stateRequests.length > 0)
|
||||
self._notifyAfterStateChange(true, true);
|
||||
// Check if we need to dequeue requests first.
|
||||
self._notifyAfterStateChange(true, true);
|
||||
|
||||
// Notify everybody, even if they didn't ask us to come up.
|
||||
self._fireEvent("wifiUp", {});
|
||||
}
|
||||
};
|
||||
|
||||
WifiManager.onsupplicantlost = function() {
|
||||
WifiManager.enabled = WifiManager.supplicantStarted = false;
|
||||
self._updateWifiSetting(false);
|
||||
WifiManager.state = "UNINITIALIZED";
|
||||
debug("Supplicant died!");
|
||||
|
||||
// Check if we need to fire request replies first:
|
||||
if (self._stateRequests.length > 0)
|
||||
self._notifyAfterStateChange(true, false);
|
||||
// Check if we need to dequeue requests first.
|
||||
self._notifyAfterStateChange(true, false);
|
||||
|
||||
// Notify everybody, even if they didn't ask us to come up.
|
||||
self._fireEvent("wifiDown", {});
|
||||
}
|
||||
};
|
||||
|
||||
WifiManager.onsupplicantfailed = function() {
|
||||
WifiManager.enabled = WifiManager.supplicantStarted = false;
|
||||
self._updateWifiSetting(false);
|
||||
WifiManager.state = "UNINITIALIZED";
|
||||
debug("Couldn't connect to supplicant");
|
||||
|
||||
if (self._stateRequests.length > 0)
|
||||
self._notifyAfterStateChange(false, false);
|
||||
}
|
||||
// Check if we need to dequeue requests first.
|
||||
self._notifyAfterStateChange(false, false);
|
||||
};
|
||||
|
||||
WifiManager.onpasswordmaybeincorrect = function() {
|
||||
WifiManager.authenticationFailuresCount++;
|
||||
}
|
||||
};
|
||||
|
||||
WifiManager.ondisconnected = function() {
|
||||
var currentNetwork = self.currentNetwork;
|
||||
if (currentNetwork) {
|
||||
WifiManager.disableNetwork(currentNetwork.netId, function() {});
|
||||
self._fireEvent("onconnectingfailed", {network: currentNetwork});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WifiManager.onstatechange = function() {
|
||||
debug("State change: " + this.prevState + " -> " + this.state);
|
||||
@ -1705,16 +1716,26 @@ function WifiWorker() {
|
||||
self.wantScanResults.forEach(function(callback) { callback(self.networks) });
|
||||
self.wantScanResults = [];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
WifiManager.setWifiEnabled(true, function (ok) {
|
||||
if (ok === 0)
|
||||
WifiManager.start();
|
||||
else
|
||||
debug("Couldn't start Wifi");
|
||||
});
|
||||
|
||||
debug("Wifi starting");
|
||||
// Read the 'wifi.enabled' setting in order to start with a known
|
||||
// value at boot time. The handle() will be called after reading.
|
||||
//
|
||||
// nsISettingsServiceCallback implementation
|
||||
var initWifiEnabledCb = {
|
||||
handle: function handle(aName, aResult) {
|
||||
if (aName !== "wifi.enabled")
|
||||
return;
|
||||
if (aResult === null)
|
||||
aResult = true;
|
||||
self.setWifiEnabled({enabled: aResult});
|
||||
},
|
||||
handleError: function handleError(aErrorMessage) {
|
||||
debug("Error reading the 'wifi.enabled' setting. Default to wifi on.");
|
||||
self.setWifiEnabled({enabled: true});
|
||||
},
|
||||
};
|
||||
gSettingsService.getLock().get("wifi.enabled", initWifiEnabledCb);
|
||||
}
|
||||
|
||||
function translateState(state) {
|
||||
@ -1744,7 +1765,8 @@ WifiWorker.prototype = {
|
||||
contractID: WIFIWORKER_CONTRACTID,
|
||||
classDescription: "WifiWorker",
|
||||
interfaces: [Ci.nsIWorkerHolder,
|
||||
Ci.nsIWifi]}),
|
||||
Ci.nsIWifi,
|
||||
Ci.nsIObserver]}),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder,
|
||||
Ci.nsIWifi]),
|
||||
@ -1941,9 +1963,6 @@ WifiWorker.prototype = {
|
||||
msg.manager = aMessage.target;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "WifiManager:setEnabled":
|
||||
this.setWifiEnabled(msg);
|
||||
break;
|
||||
case "WifiManager:getNetworks":
|
||||
this.getNetworks(msg);
|
||||
break;
|
||||
@ -2019,6 +2038,9 @@ WifiWorker.prototype = {
|
||||
},
|
||||
|
||||
_notifyAfterStateChange: function(success, newState) {
|
||||
if (!this._stateRequests.length)
|
||||
return;
|
||||
|
||||
// First, notify all of the requests that were trying to make this change.
|
||||
let state = this._stateRequests[0].enabled;
|
||||
|
||||
@ -2034,9 +2056,7 @@ WifiWorker.prototype = {
|
||||
if (!success || state === newState) {
|
||||
do {
|
||||
if (!("callback" in this._stateRequests[0])) {
|
||||
let req = this._stateRequests.shift();
|
||||
this._sendMessage("WifiManager:setEnabled:Return",
|
||||
success, state, req);
|
||||
this._stateRequests.shift();
|
||||
}
|
||||
// Don't remove more than one request if the previous one failed.
|
||||
} while (success &&
|
||||
@ -2087,7 +2107,6 @@ WifiWorker.prototype = {
|
||||
// and handle each on/off request in turn.
|
||||
// - Because we can't pass a callback to WifiManager.start, we need to
|
||||
// have a way to communicate with our onsupplicantconnection callback.
|
||||
msg.enabled = msg.data;
|
||||
this._stateRequests.push(msg);
|
||||
if (this._stateRequests.length === 1) {
|
||||
if ("callback" in this._stateRequests[0]) {
|
||||
@ -2098,12 +2117,12 @@ WifiWorker.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setWifiEnabledInternal: function(enable, callback) {
|
||||
this.setWifiEnabled({enabled: enable, callback: callback});
|
||||
setWifiEnabledInternal: function(enabled, callback) {
|
||||
this.setWifiEnabled({enabled: enabled, callback: callback});
|
||||
},
|
||||
|
||||
setWifiApEnabled: function(enable, callback) {
|
||||
WifiManager.setWifiApEnabled(enable, callback);
|
||||
setWifiApEnabled: function(enabled, callback) {
|
||||
WifiManager.setWifiApEnabled(enabled, callback);
|
||||
},
|
||||
|
||||
associate: function(msg) {
|
||||
@ -2247,7 +2266,7 @@ WifiWorker.prototype = {
|
||||
|
||||
shutdown: function() {
|
||||
debug("shutting down ...");
|
||||
this.setWifiEnabled(false);
|
||||
this.setWifiEnabled({enabled: false});
|
||||
},
|
||||
|
||||
setWifiTethering: function(enabled, callback) {
|
||||
@ -2289,6 +2308,36 @@ WifiWorker.prototype = {
|
||||
// This should not be happened. Return error to NetworkManager.
|
||||
callback.wifiTetheringEnabledChange(1, null);
|
||||
}
|
||||
},
|
||||
|
||||
_updateWifiSetting: function(enabled) {
|
||||
// This is used to update the setting value, whenever the
|
||||
// WifiManager.enabled is re-assigned based on supplicant
|
||||
// connection/lost/failed.
|
||||
//
|
||||
// To avoid WifiWorker setting the wifi again, we mark the
|
||||
// "fromInternalSetting" so WifiWorker won't deal with such
|
||||
// an internal "mozsettings-changed" event when receiving it.
|
||||
gSettingsService.getLock().set(
|
||||
"wifi.enabled", enabled, null, "fromInternalSetting");
|
||||
},
|
||||
|
||||
// nsIObserver implementation
|
||||
observe: function observe(subject, topic, data) {
|
||||
// Note that this function gets called for any and all settings changes,
|
||||
// so we need to carefully check if we have the one we're interested in.
|
||||
// The string we're interested in will be a JSON string that looks like:
|
||||
// {"key":"wifi.enabled","value":"true"}.
|
||||
if (topic !== kMozSettingsChangedObserverTopic)
|
||||
return;
|
||||
let setting = JSON.parse(data);
|
||||
if (setting.key !== "wifi.enabled")
|
||||
return;
|
||||
// To avoid WifiWorker setting the wifi again, don't need to deal with
|
||||
// the "mozsettings-changed" event fired from internal setting.
|
||||
if (setting.message && setting.message === "fromInternalSetting")
|
||||
return;
|
||||
this.setWifiEnabled({enabled: setting.value});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,18 +36,9 @@ interface nsIWifi : nsISupports
|
||||
void setWifiTethering(in boolean enabled, in nsIWifiTetheringCallback callback);
|
||||
};
|
||||
|
||||
[scriptable, uuid(b1f2e67f-75a8-4781-bf7f-eb21662ae9f3)]
|
||||
[scriptable, uuid(540674a6-edbe-11e1-87c9-dbcd31c74284)]
|
||||
interface nsIDOMWifiManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* TODO Remove in favor of a settings API.
|
||||
* Activates or disactivates wifi.
|
||||
* onsuccess: Wifi has been successfully activated and can start
|
||||
* attempting to connect to networks. request.value will be true.
|
||||
* onerror: Wifi was not successfully activated. (TODO provide details!)
|
||||
*/
|
||||
nsIDOMDOMRequest setEnabled(in boolean enabled);
|
||||
|
||||
/**
|
||||
* Returns the list of currently available networks as well as the list of
|
||||
* currently configured networks.
|
||||
@ -97,7 +88,6 @@ interface nsIDOMWifiManager : nsISupports
|
||||
nsIDOMDOMRequest wps(in jsval detail);
|
||||
|
||||
/**
|
||||
* TODO Remove in favor of a settings API.
|
||||
* Returns whether or not wifi is currently enabled.
|
||||
*/
|
||||
readonly attribute boolean enabled;
|
||||
|
@ -1470,12 +1470,14 @@ uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent,
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager>
|
||||
securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
||||
NS_ENSURE_TRUE(securityManager, NS_ERROR_FAILURE);
|
||||
|
||||
bool isChrome = false;
|
||||
nsresult rv = securityManager->SubjectPrincipalIsSystem(&isChrome);
|
||||
if (NS_FAILED(rv)) {
|
||||
isChrome = false;
|
||||
nsresult rv;
|
||||
if (securityManager) {
|
||||
rv = securityManager->SubjectPrincipalIsSystem(&isChrome);
|
||||
if (NS_FAILED(rv)) {
|
||||
isChrome = false;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefBranch;
|
||||
@ -1578,11 +1580,13 @@ uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent,
|
||||
*/
|
||||
|
||||
// Check security state for use in determing window dimensions
|
||||
bool enabled;
|
||||
nsresult res =
|
||||
securityManager->IsCapabilityEnabled("UniversalXPConnect", &enabled);
|
||||
bool enabled = false;
|
||||
if (securityManager) {
|
||||
rv = securityManager->IsCapabilityEnabled("UniversalXPConnect",
|
||||
&enabled);
|
||||
}
|
||||
|
||||
if (NS_FAILED(res) || !enabled || (isChrome && !aHasChromeParent)) {
|
||||
if (NS_FAILED(rv) || !enabled || (isChrome && !aHasChromeParent)) {
|
||||
// If priv check fails (or if we're called from chrome, but the
|
||||
// parent is not a chrome window), set all elements to minimum
|
||||
// reqs., else leave them alone.
|
||||
|
15
gfx/2d/2D.h
15
gfx/2d/2D.h
@ -727,6 +727,21 @@ public:
|
||||
virtual TemporaryRef<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
|
||||
|
||||
/*
|
||||
* Create a draw target optimized for drawing a shadow.
|
||||
*
|
||||
* Note that aSigma is the blur radius that must be used when we draw the
|
||||
* shadow. Also note that this doesn't affect the size of the allocated
|
||||
* surface, the caller is still responsible for including the shadow area in
|
||||
* its size.
|
||||
*/
|
||||
virtual TemporaryRef<DrawTarget>
|
||||
CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
|
||||
float aSigma) const
|
||||
{
|
||||
return CreateSimilarDrawTarget(aSize, aFormat);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a path builder with the specified fillmode.
|
||||
*
|
||||
|
@ -330,7 +330,8 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
const Rect* aSkipRect)
|
||||
: mSpreadRadius(aSpreadRadius),
|
||||
mBlurRadius(aBlurRadius),
|
||||
mData(nullptr)
|
||||
mData(nullptr),
|
||||
mFreeData(true)
|
||||
{
|
||||
Rect rect(aRect);
|
||||
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
@ -384,9 +385,25 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
}
|
||||
}
|
||||
|
||||
AlphaBoxBlur::AlphaBoxBlur(uint8_t* aData,
|
||||
const Rect& aRect,
|
||||
int32_t aStride,
|
||||
float aSigma)
|
||||
: mSpreadRadius(),
|
||||
mBlurRadius(CalculateBlurRadius(Point(aSigma, aSigma))),
|
||||
mData(aData),
|
||||
mFreeData(false),
|
||||
mStride(aStride),
|
||||
mRect(aRect.x, aRect.y, aRect.width, aRect.height)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AlphaBoxBlur::~AlphaBoxBlur()
|
||||
{
|
||||
free(mData);
|
||||
if (mFreeData) {
|
||||
delete mData;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char*
|
||||
|
@ -57,6 +57,11 @@ public:
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect);
|
||||
|
||||
AlphaBoxBlur(uint8_t* aData,
|
||||
const Rect& aRect,
|
||||
int32_t aStride,
|
||||
float aSigma);
|
||||
|
||||
~AlphaBoxBlur();
|
||||
|
||||
/**
|
||||
@ -135,7 +140,12 @@ private:
|
||||
/**
|
||||
* A pointer to the backing 8-bit alpha surface.
|
||||
*/
|
||||
unsigned char* mData;
|
||||
uint8_t* mData;
|
||||
|
||||
/**
|
||||
* True if we need to dispose the data.
|
||||
*/
|
||||
bool mFreeData;
|
||||
|
||||
/**
|
||||
* The stride of the data contained in mData.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "ScaledFontBase.h"
|
||||
|
||||
#include "cairo.h"
|
||||
#include "cairo-tee.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "Blur.h"
|
||||
@ -280,6 +281,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
|
||||
|
||||
DrawTargetCairo::DrawTargetCairo()
|
||||
: mContext(nullptr)
|
||||
, mPathObserver(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -387,49 +389,35 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
return;
|
||||
}
|
||||
|
||||
WillChange();
|
||||
|
||||
Float width = aSurface->GetSize().width;
|
||||
Float height = aSurface->GetSize().height;
|
||||
Rect extents(0, 0, width, height);
|
||||
|
||||
SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
|
||||
cairo_surface_t* sourcesurf = source->GetSurface();
|
||||
cairo_surface_t* blursurf;
|
||||
cairo_surface_t* surf;
|
||||
|
||||
AlphaBoxBlur blur(extents, IntSize(0, 0),
|
||||
AlphaBoxBlur::CalculateBlurRadius(Point(aSigma, aSigma)),
|
||||
nullptr, nullptr);
|
||||
if (!blur.GetData()) {
|
||||
return;
|
||||
// We only use the A8 surface for blurred shadows. Unblurred shadows can just
|
||||
// use the RGBA surface directly.
|
||||
if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
|
||||
blursurf = cairo_tee_surface_index(sourcesurf, 0);
|
||||
surf = cairo_tee_surface_index(sourcesurf, 1);
|
||||
|
||||
MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
|
||||
Rect extents(0, 0, width, height);
|
||||
AlphaBoxBlur blur(cairo_image_surface_get_data(blursurf),
|
||||
extents,
|
||||
cairo_image_surface_get_stride(blursurf),
|
||||
aSigma);
|
||||
blur.Blur();
|
||||
} else {
|
||||
blursurf = sourcesurf;
|
||||
surf = sourcesurf;
|
||||
}
|
||||
|
||||
IntSize blursize = blur.GetSize();
|
||||
cairo_surface_t* blursurf = cairo_image_surface_create_for_data(blur.GetData(),
|
||||
CAIRO_FORMAT_A8,
|
||||
blursize.width,
|
||||
blursize.height,
|
||||
blur.GetStride());
|
||||
|
||||
WillChange();
|
||||
ClearSurfaceForUnboundedSource(aOperator);
|
||||
|
||||
// Draw the source surface into the surface we're going to blur.
|
||||
SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
|
||||
cairo_surface_t* surf = source->GetSurface();
|
||||
cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
|
||||
cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
|
||||
|
||||
cairo_t* ctx = cairo_create(blursurf);
|
||||
|
||||
cairo_set_source(ctx, pat);
|
||||
|
||||
IntRect blurrect = blur.GetRect();
|
||||
cairo_new_path(ctx);
|
||||
cairo_rectangle(ctx, blurrect.x, blurrect.y, blurrect.width, blurrect.height);
|
||||
cairo_clip(ctx);
|
||||
cairo_paint(ctx);
|
||||
|
||||
cairo_destroy(ctx);
|
||||
|
||||
// Blur the result, then use that blurred result as a mask to draw the shadow
|
||||
// colour to the surface.
|
||||
blur.Blur();
|
||||
cairo_save(mContext);
|
||||
cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
|
||||
cairo_identity_matrix(mContext);
|
||||
@ -440,33 +428,26 @@ DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
cairo_push_group(mContext);
|
||||
cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
|
||||
cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
|
||||
cairo_pop_group_to_source(mContext);
|
||||
cairo_paint(mContext);
|
||||
|
||||
// Now that the shadow has been drawn, we can draw the surface on top.
|
||||
cairo_push_group(mContext);
|
||||
// Now that the shadow has been drawn, we can draw the surface on top.
|
||||
cairo_set_source_surface(mContext, surf, 0, 0);
|
||||
cairo_new_path(mContext);
|
||||
cairo_rectangle(mContext, 0, 0, width, height);
|
||||
cairo_set_source(mContext, pat);
|
||||
cairo_fill(mContext);
|
||||
cairo_pop_group_to_source(mContext);
|
||||
cairo_paint(mContext);
|
||||
} else {
|
||||
cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
|
||||
cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
|
||||
|
||||
// Now that the shadow has been drawn, we can draw the surface on top.
|
||||
cairo_set_source(mContext, pat);
|
||||
cairo_set_source_surface(mContext, surf, 0, 0);
|
||||
cairo_new_path(mContext);
|
||||
cairo_rectangle(mContext, 0, 0, width, height);
|
||||
cairo_clip(mContext);
|
||||
cairo_fill(mContext);
|
||||
}
|
||||
|
||||
cairo_paint(mContext);
|
||||
|
||||
cairo_restore(mContext);
|
||||
|
||||
cairo_pattern_destroy(pat);
|
||||
cairo_surface_destroy(blursurf);
|
||||
}
|
||||
|
||||
void
|
||||
@ -707,11 +688,6 @@ DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) cons
|
||||
const_cast<DrawTargetCairo*>(this),
|
||||
aFillRule);
|
||||
|
||||
// Creating a PathBuilder implicitly resets our mPathObserver, as it calls
|
||||
// SetPathObserver() on us. Since this guarantees our old path is saved off,
|
||||
// it's safe to reset the path here.
|
||||
cairo_new_path(mContext);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -821,6 +797,49 @@ DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize&
|
||||
return true;
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
|
||||
float aSigma) const
|
||||
{
|
||||
cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
|
||||
GfxFormatToCairoContent(aFormat),
|
||||
aSize.width, aSize.height);
|
||||
|
||||
if (cairo_surface_status(similar)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we don't have a blur then we can use the RGBA mask and keep all the
|
||||
// operations in graphics memory.
|
||||
if (aSigma == 0.0F) {
|
||||
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
|
||||
target->InitAlreadyReferenced(similar, aSize);
|
||||
return target;
|
||||
}
|
||||
|
||||
cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
|
||||
aSize.width,
|
||||
aSize.height);
|
||||
|
||||
if (cairo_surface_status(blursurf)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
|
||||
cairo_surface_destroy(blursurf);
|
||||
if (cairo_surface_status(tee)) {
|
||||
cairo_surface_destroy(similar);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cairo_tee_surface_add(tee, similar);
|
||||
cairo_surface_destroy(similar);
|
||||
|
||||
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
|
||||
target->InitAlreadyReferenced(tee, aSize);
|
||||
return target;
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize)
|
||||
{
|
||||
@ -898,12 +917,6 @@ DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
|
||||
void
|
||||
DrawTargetCairo::SetTransform(const Matrix& aTransform)
|
||||
{
|
||||
// We're about to logically change our transformation. Our current path will
|
||||
// need to change, because Cairo stores paths in device space.
|
||||
if (mPathObserver) {
|
||||
mPathObserver->MatrixWillChange(aTransform);
|
||||
}
|
||||
|
||||
mTransform = aTransform;
|
||||
|
||||
cairo_matrix_t mat;
|
||||
|
@ -115,6 +115,9 @@ public:
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
|
||||
virtual TemporaryRef<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
|
||||
virtual TemporaryRef<DrawTarget>
|
||||
CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
|
||||
float aSigma) const;
|
||||
|
||||
virtual TemporaryRef<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
@ -166,7 +169,12 @@ private: // data
|
||||
cairo_surface_t* mSurface;
|
||||
IntSize mSize;
|
||||
std::vector<SourceSurfaceCairo*> mSnapshots;
|
||||
mutable RefPtr<CairoPathContext> mPathObserver;
|
||||
|
||||
// It is safe to use a regular pointer here because the CairoPathContext will
|
||||
// deregister itself on destruction. Using a RefPtr would extend the life-
|
||||
// span of the CairoPathContext. This causes a problem when
|
||||
// PathBuilderCairo.Finish()
|
||||
mutable CairoPathContext* mPathObserver;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -207,6 +207,32 @@ GfxFillRuleToCairoFillRule(FillRule rule)
|
||||
return CAIRO_FILL_RULE_WINDING;
|
||||
}
|
||||
|
||||
// RAII class for temporarily changing the cairo matrix transform. It will use
|
||||
// the given matrix transform while it is in scope. When it goes out of scope
|
||||
// it will put the cairo context back the way it was.
|
||||
|
||||
class CairoTempMatrix
|
||||
{
|
||||
public:
|
||||
CairoTempMatrix(cairo_t* aCtx, const Matrix& aMatrix)
|
||||
: mCtx(aCtx)
|
||||
{
|
||||
cairo_get_matrix(aCtx, &mSaveMatrix);
|
||||
cairo_matrix_t matrix;
|
||||
GfxMatrixToCairoMatrix(aMatrix, matrix);
|
||||
cairo_set_matrix(aCtx, &matrix);
|
||||
}
|
||||
|
||||
~CairoTempMatrix()
|
||||
{
|
||||
cairo_get_matrix(mCtx, &mSaveMatrix);
|
||||
}
|
||||
|
||||
private:
|
||||
cairo_t* mCtx;
|
||||
cairo_matrix_t mSaveMatrix;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,61 +13,40 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget,
|
||||
FillRule aFillRule,
|
||||
const Matrix& aTransform /* = Matrix() */)
|
||||
: mTransform(aTransform)
|
||||
, mContext(aCtx)
|
||||
CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget)
|
||||
: mContext(aCtx)
|
||||
, mDrawTarget(aDrawTarget)
|
||||
, mFillRule(aFillRule)
|
||||
{
|
||||
cairo_reference(mContext);
|
||||
cairo_set_fill_rule(mContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
|
||||
// If we don't have an identity transformation, we need to have a separate
|
||||
// context from the draw target, because we can't set a transformation on its
|
||||
// context.
|
||||
if (mDrawTarget && !mTransform.IsIdentity()) {
|
||||
DuplicateContextAndPath(mTransform);
|
||||
// A new path in the DrawTarget's context.
|
||||
aDrawTarget->SetPathObserver(this);
|
||||
cairo_new_path(mContext);
|
||||
}
|
||||
|
||||
ForgetDrawTarget();
|
||||
} else if (mDrawTarget) {
|
||||
mDrawTarget->SetPathObserver(this);
|
||||
}
|
||||
CairoPathContext::CairoPathContext(CairoPathContext& aPathContext)
|
||||
: mContext(aPathContext.mContext)
|
||||
, mDrawTarget(nullptr)
|
||||
{
|
||||
cairo_reference(mContext);
|
||||
DuplicateContextAndPath();
|
||||
}
|
||||
|
||||
CairoPathContext::~CairoPathContext()
|
||||
{
|
||||
if (mDrawTarget) {
|
||||
mDrawTarget->SetPathObserver(nullptr);
|
||||
DrawTargetCairo* drawTarget = mDrawTarget;
|
||||
ForgetDrawTarget();
|
||||
|
||||
// We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise
|
||||
// we will think we need to make a defensive copy of the path.
|
||||
drawTarget->SetPathObserver(nullptr);
|
||||
}
|
||||
cairo_destroy(mContext);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::ObserveTarget(DrawTargetCairo* aDrawTarget)
|
||||
{
|
||||
if (!aDrawTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDrawTarget) {
|
||||
mDrawTarget->SetPathObserver(nullptr);
|
||||
}
|
||||
mDrawTarget = aDrawTarget;
|
||||
|
||||
// If there is a transform on the path, then we must have a separate context
|
||||
// from the draw target, so we cannot be its observer
|
||||
if (!mTransform.IsIdentity()) {
|
||||
ForgetDrawTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
mDrawTarget->SetPathObserver(this);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::DuplicateContextAndPath(const Matrix& aMatrix /* = Matrix() */)
|
||||
CairoPathContext::DuplicateContextAndPath()
|
||||
{
|
||||
// Duplicate the path.
|
||||
cairo_path_t* path = cairo_copy_path(mContext);
|
||||
@ -75,20 +54,31 @@ CairoPathContext::DuplicateContextAndPath(const Matrix& aMatrix /* = Matrix() */
|
||||
|
||||
// Duplicate the context.
|
||||
cairo_surface_t* surf = cairo_get_target(mContext);
|
||||
cairo_matrix_t matrix;
|
||||
cairo_get_matrix(mContext, &matrix);
|
||||
cairo_destroy(mContext);
|
||||
|
||||
mContext = cairo_create(surf);
|
||||
|
||||
// Transform the context.
|
||||
cairo_matrix_t matrix;
|
||||
GfxMatrixToCairoMatrix(aMatrix, matrix);
|
||||
cairo_transform(mContext, &matrix);
|
||||
// Set the matrix to match the source context so that the path is copied in
|
||||
// device space. After this point it doesn't matter what the transform is
|
||||
// set to because it's always swapped out before use.
|
||||
cairo_set_matrix(mContext, &matrix);
|
||||
|
||||
// Add the path, and throw away our duplicate.
|
||||
cairo_append_path(mContext, path);
|
||||
cairo_set_fill_rule(mContext, rule);
|
||||
cairo_path_destroy(path);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::ForgetDrawTarget()
|
||||
{
|
||||
// We don't need to set the path observer back to nullptr in this case
|
||||
// because ForgetDrawTarget() is trigged when the target has been
|
||||
// grabbed by another path observer.
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::PathWillChange()
|
||||
{
|
||||
@ -100,68 +90,22 @@ CairoPathContext::PathWillChange()
|
||||
// The context we point to is going to change from under us. To continue
|
||||
// using this path, we need to copy it to a new context.
|
||||
DuplicateContextAndPath();
|
||||
|
||||
ForgetDrawTarget();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::MatrixWillChange(const Matrix& aNewMatrix)
|
||||
{
|
||||
// Cairo paths are stored in device space. Since we logically operate in user
|
||||
// space, we want to make it so our path will be in the same location if and
|
||||
// when our path is copied out.
|
||||
// To effect this, we copy out our path (which, in Cairo, implicitly converts
|
||||
// to user space), then temporarily set the context to have the new
|
||||
// transform. We then set the path, which ensures that the points are all
|
||||
// transformed correctly. Finally, we set the matrix back to its original
|
||||
// value.
|
||||
cairo_path_t* path = cairo_copy_path(mContext);
|
||||
|
||||
cairo_matrix_t origMatrix;
|
||||
cairo_get_matrix(mContext, &origMatrix);
|
||||
|
||||
cairo_matrix_t newMatrix;
|
||||
GfxMatrixToCairoMatrix(aNewMatrix, newMatrix);
|
||||
cairo_set_matrix(mContext, &newMatrix);
|
||||
|
||||
cairo_new_path(mContext);
|
||||
cairo_append_path(mContext, path);
|
||||
cairo_path_destroy(path);
|
||||
|
||||
cairo_set_matrix(mContext, &origMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::CopyPathTo(cairo_t* aToContext)
|
||||
CairoPathContext::CopyPathTo(cairo_t* aToContext, Matrix& aTransform)
|
||||
{
|
||||
if (aToContext != mContext) {
|
||||
cairo_set_fill_rule(aToContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
|
||||
cairo_matrix_t origMat;
|
||||
cairo_get_matrix(aToContext, &origMat);
|
||||
|
||||
cairo_matrix_t mat;
|
||||
GfxMatrixToCairoMatrix(mTransform, mat);
|
||||
cairo_transform(aToContext, &mat);
|
||||
|
||||
// cairo_copy_path gives us a user-space copy of the path, so we don't have
|
||||
// to worry about transformations here.
|
||||
CairoTempMatrix tempMatrix(mContext, aTransform);
|
||||
cairo_path_t* path = cairo_copy_path(mContext);
|
||||
cairo_new_path(aToContext);
|
||||
cairo_append_path(aToContext, path);
|
||||
cairo_path_destroy(path);
|
||||
|
||||
cairo_set_matrix(aToContext, &origMat);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CairoPathContext::ForgetDrawTarget()
|
||||
{
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
CairoPathContext::ContainsPath(const Path* aPath)
|
||||
{
|
||||
@ -175,38 +119,32 @@ CairoPathContext::ContainsPath(const Path* aPath)
|
||||
}
|
||||
|
||||
PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
|
||||
FillRule aFillRule,
|
||||
const Matrix& aTransform /* = Matrix() */)
|
||||
: mFillRule(aPathContext->GetFillRule())
|
||||
{
|
||||
RefPtr<DrawTargetCairo> drawTarget = aPathContext->GetDrawTarget();
|
||||
mPathContext = new CairoPathContext(*aPathContext, drawTarget, mFillRule,
|
||||
aPathContext->GetTransform() * aTransform);
|
||||
|
||||
// We need to ensure that we are allowed to modify the path currently set on
|
||||
// aPathContext. If we don't have a draw target, CairoPathContext's
|
||||
// constructor has no way to make aPathContext duplicate its path (normally,
|
||||
// calling drawTarget->SetPathObserver() would do so). In this case, we
|
||||
// explicitly make aPathContext copy out its context and path, leaving our
|
||||
// path alone.
|
||||
if (!drawTarget) {
|
||||
aPathContext->DuplicateContextAndPath();
|
||||
}
|
||||
}
|
||||
: mPathContext(aPathContext)
|
||||
, mTransform(aTransform)
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
|
||||
: mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule))
|
||||
: mPathContext(new CairoPathContext(aCtx, aDrawTarget))
|
||||
, mTransform(aDrawTarget->GetTransform())
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
void
|
||||
PathBuilderCairo::MoveTo(const Point &aPoint)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::LineTo(const Point &aPoint)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
@ -215,6 +153,8 @@ PathBuilderCairo::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
|
||||
}
|
||||
|
||||
@ -222,6 +162,9 @@ void
|
||||
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
PrepareForWrite();
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
|
||||
// We need to elevate the degree of this quadratic Bézier to cubic, so we're
|
||||
// going to add an intermediate control point, and recompute control point 1.
|
||||
// The first and last control points remain the same.
|
||||
@ -237,6 +180,7 @@ PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
|
||||
void
|
||||
PathBuilderCairo::Close()
|
||||
{
|
||||
PrepareForWrite();
|
||||
cairo_close_path(*mPathContext);
|
||||
}
|
||||
|
||||
@ -250,6 +194,7 @@ PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
Point
|
||||
PathBuilderCairo::CurrentPoint() const
|
||||
{
|
||||
CairoTempMatrix tempMatrix(*mPathContext, mTransform);
|
||||
double x, y;
|
||||
cairo_get_current_point(*mPathContext, &x, &y);
|
||||
return Point(x, y);
|
||||
@ -258,11 +203,7 @@ PathBuilderCairo::CurrentPoint() const
|
||||
TemporaryRef<Path>
|
||||
PathBuilderCairo::Finish()
|
||||
{
|
||||
RefPtr<PathCairo> path = new PathCairo(*mPathContext,
|
||||
mPathContext->GetDrawTarget(),
|
||||
mFillRule,
|
||||
mPathContext->GetTransform());
|
||||
return path;
|
||||
return new PathCairo(mPathContext, mTransform, mFillRule);
|
||||
}
|
||||
|
||||
TemporaryRef<CairoPathContext>
|
||||
@ -271,43 +212,63 @@ PathBuilderCairo::GetPathContext()
|
||||
return mPathContext;
|
||||
}
|
||||
|
||||
PathCairo::PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform)
|
||||
: mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule, aTransform))
|
||||
void
|
||||
PathBuilderCairo::PrepareForWrite()
|
||||
{
|
||||
// Only PathBuilder and PathCairo maintain references to CairoPathContext.
|
||||
// DrawTarget does not. If we're sharing a reference to the context then we
|
||||
// need to create a copy that we can modify. This provides copy on write
|
||||
// behaviour.
|
||||
if (mPathContext->refCount() != 1) {
|
||||
mPathContext = new CairoPathContext(*mPathContext);
|
||||
}
|
||||
}
|
||||
|
||||
PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform,
|
||||
FillRule aFillRule)
|
||||
: mPathContext(aPathContext)
|
||||
, mTransform(aTransform)
|
||||
, mFillRule(aFillRule)
|
||||
{}
|
||||
|
||||
TemporaryRef<PathBuilder>
|
||||
PathCairo::CopyToBuilder(FillRule aFillRule) const
|
||||
{
|
||||
// Note: This PathBuilderCairo constructor causes our mPathContext to copy
|
||||
// out the path, since the path builder is going to change the path on us.
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext);
|
||||
return builder;
|
||||
return new PathBuilderCairo(mPathContext, aFillRule, mTransform);
|
||||
}
|
||||
|
||||
TemporaryRef<PathBuilder>
|
||||
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
|
||||
{
|
||||
// Note: This PathBuilderCairo constructor causes our mPathContext to copy
|
||||
// out the path, since the path builder is going to change the path on us.
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext,
|
||||
aTransform);
|
||||
return builder;
|
||||
// We are given the transform we would apply from device space to user space.
|
||||
// However in cairo our path is in device space so we view the transform as
|
||||
// being the other way round. We therefore need to apply the inverse transform
|
||||
// to our current cairo transform.
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
|
||||
return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse);
|
||||
}
|
||||
|
||||
bool
|
||||
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix(*mPathContext, mTransform);
|
||||
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformed = inverse * aPoint;
|
||||
|
||||
// Needs the correct fill rule set.
|
||||
cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
Rect
|
||||
PathCairo::GetBounds(const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix(*mPathContext, mTransform);
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
|
||||
@ -319,6 +280,8 @@ Rect
|
||||
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
CairoTempMatrix(*mPathContext, mTransform);
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
|
||||
@ -337,13 +300,8 @@ PathCairo::GetPathContext()
|
||||
void
|
||||
PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
|
||||
{
|
||||
if (mPathContext->GetContext() != aContext) {
|
||||
mPathContext->CopyPathTo(aContext);
|
||||
|
||||
// Since aDrawTarget wants us to be the current path on its context, we
|
||||
// should also listen to it for updates to that path (as an optimization).
|
||||
mPathContext->ObserveTarget(aDrawTarget);
|
||||
}
|
||||
mPathContext->CopyPathTo(aContext, mTransform);
|
||||
cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,74 +37,57 @@ class DrawTargetCairo;
|
||||
class CairoPathContext : public RefCounted<CairoPathContext>
|
||||
{
|
||||
public:
|
||||
// Construct a CairoPathContext and set it to be the path observer of
|
||||
// aDrawTarget. Optionally, this path can be transformed by aMatrix.
|
||||
CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget,
|
||||
FillRule aFillRule,
|
||||
const Matrix& aMatrix = Matrix());
|
||||
// Construct a new empty CairoPathContext that uses the given draw target and
|
||||
// its cairo context. Using the existing context may save having to copy the
|
||||
// path later.
|
||||
CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget);
|
||||
|
||||
// Copy the path.
|
||||
CairoPathContext(CairoPathContext& aPathContext);
|
||||
|
||||
~CairoPathContext();
|
||||
|
||||
// Copy the path on mContext to be the path on aToContext, if they aren't the
|
||||
// same.
|
||||
void CopyPathTo(cairo_t* aToContext);
|
||||
// same. At this point we set the fill rule for the destination context as
|
||||
// there is little point in doing this earlier.
|
||||
void CopyPathTo(cairo_t* aToContext, Matrix& aTransform);
|
||||
|
||||
// This method must be called by the draw target before it changes the path
|
||||
// currently on the cairo context.
|
||||
void PathWillChange();
|
||||
|
||||
// This method must be called by the draw target whenever it is going to
|
||||
// change the current transformation on mContext.
|
||||
void MatrixWillChange(const Matrix& aMatrix);
|
||||
|
||||
// This method must be called as the draw target is dying. In this case, we
|
||||
// forget our reference to the draw target, and become the only reference to
|
||||
// our context.
|
||||
void ForgetDrawTarget();
|
||||
|
||||
// Create a duplicate context, and copy this path to that context. Optionally,
|
||||
// the new context can be transformed.
|
||||
void DuplicateContextAndPath(const Matrix& aMatrix = Matrix());
|
||||
// Create a duplicate context, and copy this path to that context.
|
||||
void DuplicateContextAndPath();
|
||||
|
||||
// Returns true if this CairoPathContext represents path.
|
||||
bool ContainsPath(const Path* path);
|
||||
|
||||
// add ourselves as an observer of aDrawTarget, if possible
|
||||
// if we succeed, then mDrawTarget is set to aDrawTarget
|
||||
void ObserveTarget(DrawTargetCairo* aDrawTarget);
|
||||
|
||||
cairo_t* GetContext() const { return mContext; }
|
||||
DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
|
||||
Matrix GetTransform() const { return mTransform; }
|
||||
FillRule GetFillRule() const { return mFillRule; }
|
||||
|
||||
operator cairo_t* () const { return mContext; }
|
||||
|
||||
private: // methods
|
||||
CairoPathContext(const CairoPathContext&) MOZ_DELETE;
|
||||
|
||||
private: // data
|
||||
Matrix mTransform;
|
||||
cairo_t* mContext;
|
||||
// Not a RefPtr to avoid cycles.
|
||||
DrawTargetCairo* mDrawTarget;
|
||||
FillRule mFillRule;
|
||||
};
|
||||
|
||||
class PathBuilderCairo : public PathBuilder
|
||||
{
|
||||
public:
|
||||
// This constructor implicitly takes ownership of aCtx by calling
|
||||
// aDrawTarget->SetPathObserver(). Therefore, if the draw target has a path
|
||||
// observer, this constructor will cause it to copy out its path.
|
||||
// The path currently set on aCtx is not changed.
|
||||
// Creates a new empty path. It also implicitly takes ownership of aCtx by
|
||||
// calling aDrawTarget->SetPathObserver(). Therefore, if the draw target has a
|
||||
// path observer, this constructor will cause it to copy out its path.
|
||||
PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
|
||||
|
||||
// This constructor, called with a CairoPathContext*, implicitly takes
|
||||
// ownership of the path, and therefore makes aPathContext copy out its path
|
||||
// regardless of whether it has a pointer to a DrawTargetCairo.
|
||||
// The path currently set on aPathContext is not changed.
|
||||
explicit PathBuilderCairo(CairoPathContext* aPathContext,
|
||||
const Matrix& aTransform = Matrix());
|
||||
// Creates a path builder out of an existing CairoPathContext with a new fill
|
||||
// rule and transform.
|
||||
PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix());
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
@ -121,18 +104,18 @@ public:
|
||||
|
||||
TemporaryRef<CairoPathContext> GetPathContext();
|
||||
|
||||
private: // methods
|
||||
void SetFillRule(FillRule aFillRule);
|
||||
|
||||
private: // data
|
||||
void PrepareForWrite();
|
||||
|
||||
RefPtr<CairoPathContext> mPathContext;
|
||||
Matrix mTransform;
|
||||
FillRule mFillRule;
|
||||
};
|
||||
|
||||
class PathCairo : public Path
|
||||
{
|
||||
public:
|
||||
PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform);
|
||||
PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule);
|
||||
|
||||
virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
|
||||
|
||||
@ -158,6 +141,7 @@ public:
|
||||
|
||||
private:
|
||||
RefPtr<CairoPathContext> mPathContext;
|
||||
Matrix mTransform;
|
||||
FillRule mFillRule;
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
|
||||
: PlanarYCbCrImage(aRecycleBin)
|
||||
, mScaleHint(aScaleHint)
|
||||
, mDelayedConversion(false)
|
||||
{
|
||||
SetOffscreenFormat(aOffscreenFormat);
|
||||
}
|
||||
@ -44,12 +45,15 @@ public:
|
||||
}
|
||||
|
||||
virtual void SetData(const Data& aData);
|
||||
virtual void SetDelayedConversion(bool aDelayed) { mDelayedConversion = aDelayed; }
|
||||
|
||||
already_AddRefed<gfxASurface> GetAsSurface();
|
||||
|
||||
private:
|
||||
nsAutoArrayPtr<uint8_t> mDecodedBuffer;
|
||||
gfxIntSize mScaleHint;
|
||||
int mStride;
|
||||
nsAutoArrayPtr<uint8_t> mDecodedBuffer;
|
||||
bool mDelayedConversion;
|
||||
};
|
||||
|
||||
class BasicImageFactory : public ImageFactory
|
||||
@ -81,6 +85,10 @@ BasicPlanarYCbCrImage::SetData(const Data& aData)
|
||||
{
|
||||
PlanarYCbCrImage::SetData(aData);
|
||||
|
||||
if (mDelayedConversion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do some sanity checks to prevent integer overflow
|
||||
if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
|
||||
aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
|
||||
|
@ -535,8 +535,23 @@ gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurfa
|
||||
dt->Flush();
|
||||
}
|
||||
srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (aSurface->CairoSurface() && aTarget->GetType() == BACKEND_CAIRO) {
|
||||
// If this is an xlib cairo surface we don't want to fetch it into memory
|
||||
// because this is a major slow down.
|
||||
NativeSurface surf;
|
||||
surf.mFormat = format;
|
||||
surf.mType = NATIVE_SURFACE_CAIRO_SURFACE;
|
||||
surf.mSurface = aSurface->CairoSurface();
|
||||
srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
|
||||
|
||||
if (srcBuffer) {
|
||||
// It's cheap enough to make a new one so we won't keep it around and
|
||||
// keeping it creates a cycle.
|
||||
return srcBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!srcBuffer) {
|
||||
nsRefPtr<gfxImageSurface> imgSurface = aSurface->GetAsImageSurface();
|
||||
|
@ -3219,7 +3219,7 @@ fi
|
||||
dnl Top-level Mozilla switched to requiring NSPR 4.8.6 (bug 560582), but we don't need it in JS.
|
||||
if test -n "$_USE_SYSTEM_NSPR"; then
|
||||
MOZ_NATIVE_NSPR=
|
||||
AM_PATH_NSPR(4.7.0, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
AM_PATH_NSPR(4.9.2, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSPR"; then
|
||||
|
@ -79,7 +79,7 @@ using namespace js::frontend;
|
||||
JS_END_MACRO
|
||||
#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
|
||||
|
||||
StrictMode::StrictModeState
|
||||
StrictMode
|
||||
StrictModeGetter::get() const
|
||||
{
|
||||
return parser->pc->sc->strictModeState;
|
||||
@ -388,7 +388,7 @@ Parser::newObjectBox(JSObject *obj)
|
||||
}
|
||||
|
||||
FunctionBox::FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseContext *outerpc,
|
||||
StrictMode::StrictModeState sms)
|
||||
StrictMode sms)
|
||||
: ObjectBox(traceListHead, obj),
|
||||
siblings(outerpc->functionList),
|
||||
kids(NULL),
|
||||
@ -444,7 +444,7 @@ FunctionBox::FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseContext *
|
||||
}
|
||||
|
||||
FunctionBox *
|
||||
Parser::newFunctionBox(JSObject *obj, ParseContext *outerpc, StrictMode::StrictModeState sms)
|
||||
Parser::newFunctionBox(JSObject *obj, ParseContext *outerpc, StrictMode sms)
|
||||
{
|
||||
JS_ASSERT(obj && !IsPoisonedPtr(obj));
|
||||
JS_ASSERT(obj->isFunction());
|
||||
@ -1612,7 +1612,7 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
return NULL;
|
||||
|
||||
// Inherit strictness if neeeded.
|
||||
StrictMode::StrictModeState sms = (outerpc->sc->strictModeState == StrictMode::STRICT) ?
|
||||
StrictMode sms = (outerpc->sc->strictModeState == StrictMode::STRICT) ?
|
||||
StrictMode::STRICT : StrictMode::UNKNOWN;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
@ -1779,7 +1779,7 @@ Parser::functionExpr()
|
||||
}
|
||||
|
||||
void
|
||||
FunctionBox::recursivelySetStrictMode(StrictMode::StrictModeState strictness)
|
||||
FunctionBox::recursivelySetStrictMode(StrictMode strictness)
|
||||
{
|
||||
if (strictModeState == StrictMode::UNKNOWN) {
|
||||
strictModeState = strictness;
|
||||
|
@ -303,7 +303,7 @@ struct Parser : private AutoGCRooter
|
||||
*/
|
||||
ObjectBox *newObjectBox(JSObject *obj);
|
||||
|
||||
FunctionBox *newFunctionBox(JSObject *obj, ParseContext *pc, StrictMode::StrictModeState sms);
|
||||
FunctionBox *newFunctionBox(JSObject *obj, ParseContext *pc, StrictMode sms);
|
||||
|
||||
/*
|
||||
* Create a new function object given parse context (pc) and a name (which
|
||||
|
@ -16,7 +16,7 @@ namespace frontend {
|
||||
|
||||
inline
|
||||
SharedContext::SharedContext(JSContext *cx, JSObject *scopeChain, JSFunction *fun,
|
||||
FunctionBox *funbox, StrictMode::StrictModeState sms)
|
||||
FunctionBox *funbox, StrictMode sms)
|
||||
: context(cx),
|
||||
fun_(cx, fun),
|
||||
funbox_(funbox),
|
||||
|
@ -156,12 +156,12 @@ struct SharedContext {
|
||||
// can have any kind of children.
|
||||
//
|
||||
// When parsing is done, no context may be in the UNKNOWN strictness state.
|
||||
StrictMode::StrictModeState strictModeState;
|
||||
StrictMode strictModeState;
|
||||
|
||||
// If it's function code, fun must be non-NULL and scopeChain must be NULL.
|
||||
// If it's global code, fun and funbox must be NULL.
|
||||
inline SharedContext(JSContext *cx, JSObject *scopeChain, JSFunction *fun, FunctionBox *funbox,
|
||||
StrictMode::StrictModeState sms);
|
||||
StrictMode sms);
|
||||
|
||||
// In theory, |fun*| flags are only relevant if |inFunction()| is true.
|
||||
// However, we get and set in some cases where |inFunction()| is false,
|
||||
@ -297,7 +297,7 @@ struct FunctionBox : public ObjectBox
|
||||
size_t bufStart;
|
||||
size_t bufEnd;
|
||||
uint16_t ndefaults;
|
||||
StrictMode::StrictModeState strictModeState;
|
||||
StrictMode strictModeState;
|
||||
bool inWith:1; /* some enclosing scope is a with-statement
|
||||
or E4X filter-expression */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
@ -305,13 +305,13 @@ struct FunctionBox : public ObjectBox
|
||||
ContextFlags cxFlags;
|
||||
|
||||
FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseContext *pc,
|
||||
StrictMode::StrictModeState sms);
|
||||
StrictMode sms);
|
||||
|
||||
bool funIsGenerator() const { return cxFlags.funIsGenerator; }
|
||||
|
||||
JSFunction *function() const { return (JSFunction *) object; }
|
||||
|
||||
void recursivelySetStrictMode(StrictMode::StrictModeState strictness);
|
||||
void recursivelySetStrictMode(StrictMode strictness);
|
||||
};
|
||||
|
||||
// Push the C-stack-allocated struct at stmt onto the StmtInfoPC stack.
|
||||
|
@ -433,16 +433,14 @@ struct CompileError {
|
||||
void throwError();
|
||||
};
|
||||
|
||||
namespace StrictMode {
|
||||
/* For an explanation of how these are used, see the comment in the FunctionBox definition. */
|
||||
enum StrictModeState {
|
||||
MOZ_BEGIN_ENUM_CLASS(StrictMode, uint8_t)
|
||||
NOTSTRICT,
|
||||
UNKNOWN,
|
||||
STRICT
|
||||
};
|
||||
}
|
||||
MOZ_END_ENUM_CLASS(StrictMode)
|
||||
|
||||
inline StrictMode::StrictModeState
|
||||
inline StrictMode
|
||||
StrictModeFromContext(JSContext *cx)
|
||||
{
|
||||
return cx->hasRunOption(JSOPTION_STRICT_MODE) ? StrictMode::STRICT : StrictMode::UNKNOWN;
|
||||
@ -463,7 +461,7 @@ class StrictModeGetter {
|
||||
public:
|
||||
StrictModeGetter(Parser *p) : parser(p) { }
|
||||
|
||||
StrictMode::StrictModeState get() const;
|
||||
StrictMode get() const;
|
||||
CompileError *queuedStrictModeError() const;
|
||||
void setQueuedStrictModeError(CompileError *e);
|
||||
};
|
||||
@ -536,9 +534,9 @@ class TokenStream
|
||||
void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); }
|
||||
void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); }
|
||||
|
||||
StrictMode::StrictModeState strictModeState() const
|
||||
StrictMode strictModeState() const
|
||||
{
|
||||
return strictModeGetter ? strictModeGetter->get() : StrictMode::NOTSTRICT;
|
||||
return strictModeGetter ? strictModeGetter->get() : StrictMode(StrictMode::NOTSTRICT);
|
||||
}
|
||||
bool isXMLTagMode() const { return !!(flags & TSF_XMLTAGMODE); }
|
||||
bool isXMLOnlyMode() const { return !!(flags & TSF_XMLONLYMODE); }
|
||||
|
@ -551,8 +551,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx,
|
||||
JSObject *cached = cache->GetWrapper();
|
||||
if (cached) {
|
||||
if (IS_SLIM_WRAPPER_OBJECT(cached)) {
|
||||
if (!XPCWrappedNative::Morph(ccx, cached, Interface, cache,
|
||||
getter_AddRefs(wrapper)))
|
||||
if (NS_FAILED(XPCWrappedNative::Morph(ccx, cached,
|
||||
Interface, cache, getter_AddRefs(wrapper))))
|
||||
return NS_ERROR_FAILURE;
|
||||
} else {
|
||||
wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(cached));
|
||||
|
@ -497,11 +497,19 @@ public:
|
||||
mXScale(1.f), mYScale(1.f),
|
||||
mActiveScrolledRootPosition(0, 0) {}
|
||||
|
||||
/**
|
||||
* Record the number of clips in the Thebes layer's mask layer.
|
||||
* Should not be reset when the layer is recycled since it is used to track
|
||||
* changes in the use of mask layers.
|
||||
*/
|
||||
uint32_t mMaskClipCount;
|
||||
|
||||
/**
|
||||
* A color that should be painted over the bounds of the layer's visible
|
||||
* region before any other content is painted.
|
||||
*/
|
||||
nscolor mForcedBackgroundColor;
|
||||
|
||||
/**
|
||||
* The resolution scale used.
|
||||
*/
|
||||
@ -1675,6 +1683,10 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
|
||||
layer = thebesLayerData->mLayer;
|
||||
}
|
||||
|
||||
// check to see if the new item has rounded rect clips in common with
|
||||
// other items in the layer
|
||||
thebesLayerData->UpdateCommonClipCount(aClip);
|
||||
|
||||
thebesLayerData->Accumulate(this, aItem, aVisibleRect, aDrawRect, aClip);
|
||||
|
||||
return thebesLayerData;
|
||||
@ -3202,7 +3214,7 @@ FrameLayerBuilder::Clip::RemoveRoundedCorners()
|
||||
}
|
||||
|
||||
gfxRect
|
||||
CalculateBounds(nsTArray<FrameLayerBuilder::Clip::RoundedRect> aRects, int32_t A2D)
|
||||
CalculateBounds(const nsTArray<FrameLayerBuilder::Clip::RoundedRect>& aRects, int32_t A2D)
|
||||
{
|
||||
nsRect bounds = aRects[0].mRect;
|
||||
for (uint32_t i = 1; i < aRects.Length(); ++i) {
|
||||
@ -3211,16 +3223,36 @@ CalculateBounds(nsTArray<FrameLayerBuilder::Clip::RoundedRect> aRects, int32_t A
|
||||
|
||||
return nsLayoutUtils::RectToGfxRect(bounds, A2D);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
SetClipCount(ThebesDisplayItemLayerUserData* aThebesData,
|
||||
uint32_t aClipCount)
|
||||
{
|
||||
if (aThebesData) {
|
||||
aThebesData->mMaskClipCount = aClipCount;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
|
||||
uint32_t aRoundedRectClipCount)
|
||||
{
|
||||
// if the number of clips we are going to mask has decreased, then aLayer might have
|
||||
// cached graphics which assume the existence of a soon-to-be non-existent mask layer
|
||||
// in that case, invalidate the whole layer.
|
||||
ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer);
|
||||
if (thebesData &&
|
||||
aRoundedRectClipCount < thebesData->mMaskClipCount) {
|
||||
ThebesLayer* thebes = aLayer->AsThebesLayer();
|
||||
thebes->InvalidateRegion(thebes->GetValidRegion().GetBounds());
|
||||
}
|
||||
|
||||
// don't build an unnecessary mask
|
||||
nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds();
|
||||
if (aClip.mRoundedClipRects.IsEmpty() ||
|
||||
aRoundedRectClipCount <= 0 ||
|
||||
aRoundedRectClipCount == 0 ||
|
||||
layerBounds.IsEmpty()) {
|
||||
SetClipCount(thebesData, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3238,6 +3270,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
|
||||
if (*userData == newData) {
|
||||
aLayer->SetMaskLayer(maskLayer);
|
||||
SetClipCount(thebesData, aRoundedRectClipCount);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3263,28 +3296,24 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
gfxMatrix imageTransform = maskTransform;
|
||||
imageTransform.Scale(mParameters.mXScale, mParameters.mYScale);
|
||||
|
||||
nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey(
|
||||
new MaskLayerImageCache::MaskLayerImageKey(aLayer->Manager()->GetBackendType()));
|
||||
|
||||
// copy and transform the rounded rects
|
||||
nsTArray<MaskLayerImageCache::PixelRoundedRect> roundedRects;
|
||||
for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) {
|
||||
roundedRects.AppendElement(
|
||||
newKey->mRoundedClipRects.AppendElement(
|
||||
MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i],
|
||||
mContainerFrame->PresContext()));
|
||||
roundedRects[i].ScaleAndTranslate(imageTransform);
|
||||
newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform);
|
||||
}
|
||||
|
||||
// check to see if we can reuse a mask image
|
||||
const MaskLayerImageCache::MaskLayerImageKey* key =
|
||||
new MaskLayerImageCache::MaskLayerImageKey(roundedRects, aLayer->Manager()->GetBackendType());
|
||||
const MaskLayerImageCache::MaskLayerImageKey* lookupKey = key;
|
||||
const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey;
|
||||
|
||||
// check to see if we can reuse a mask image
|
||||
nsRefPtr<ImageContainer> container =
|
||||
GetMaskLayerImageCache()->FindImageFor(&lookupKey);
|
||||
|
||||
if (container) {
|
||||
// track the returned key for the mask image
|
||||
delete key;
|
||||
key = lookupKey;
|
||||
} else {
|
||||
if (!container) {
|
||||
// no existing mask image, so build a new one
|
||||
nsRefPtr<gfxASurface> surface =
|
||||
aLayer->Manager()->CreateOptimalMaskSurface(surfaceSize);
|
||||
@ -3292,6 +3321,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
// fail if we can't get the right surface
|
||||
if (!surface || surface->CairoStatus()) {
|
||||
NS_WARNING("Could not create surface for mask layer.");
|
||||
SetClipCount(thebesData, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3314,7 +3344,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
static_cast<CairoImage*>(image.get())->SetData(data);
|
||||
container->SetCurrentImageInTransaction(image);
|
||||
|
||||
GetMaskLayerImageCache()->PutImage(key, container);
|
||||
GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
|
||||
}
|
||||
|
||||
maskLayer->SetContainer(container);
|
||||
@ -3324,9 +3354,10 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
userData->mScaleX = newData.mScaleX;
|
||||
userData->mScaleY = newData.mScaleY;
|
||||
userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects);
|
||||
userData->mImageKey = key;
|
||||
userData->mImageKey = lookupKey;
|
||||
|
||||
aLayer->SetMaskLayer(maskLayer);
|
||||
SetClipCount(thebesData, aRoundedRectClipCount);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ CPPSRCS = \
|
||||
nsStyleChangeList.cpp \
|
||||
nsStyleSheetService.cpp \
|
||||
PaintTracker.cpp \
|
||||
PositionedEventTargeting.cpp \
|
||||
StackArena.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
@ -12,10 +12,13 @@ namespace mozilla {
|
||||
|
||||
MaskLayerImageCache::MaskLayerImageCache()
|
||||
{
|
||||
MOZ_COUNT_CTOR(MaskLayerImageCache);
|
||||
mMaskImageContainers.Init();
|
||||
}
|
||||
MaskLayerImageCache::~MaskLayerImageCache()
|
||||
{}
|
||||
{
|
||||
MOZ_COUNT_DTOR(MaskLayerImageCache);
|
||||
}
|
||||
|
||||
|
||||
/* static */ PLDHashOperator
|
||||
|
@ -50,10 +50,24 @@ public:
|
||||
aPresContext->AppUnitsToGfxUnits(aRRect.mRect.width),
|
||||
aPresContext->AppUnitsToGfxUnits(aRRect.mRect.height))
|
||||
{
|
||||
MOZ_COUNT_CTOR(PixelRoundedRect);
|
||||
NS_FOR_CSS_HALF_CORNERS(corner) {
|
||||
mRadii[corner] = aPresContext->AppUnitsToGfxUnits(aRRect.mRadii[corner]);
|
||||
}
|
||||
}
|
||||
PixelRoundedRect(const PixelRoundedRect& aPRR)
|
||||
: mRect(aPRR.mRect)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PixelRoundedRect);
|
||||
NS_FOR_CSS_HALF_CORNERS(corner) {
|
||||
mRadii[corner] = aPRR.mRadii[corner];
|
||||
}
|
||||
}
|
||||
|
||||
~PixelRoundedRect()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PixelRoundedRect);
|
||||
}
|
||||
|
||||
// Applies the scale and translate components of aTransform.
|
||||
// It is an error to pass a matrix which does more than just scale
|
||||
@ -99,6 +113,9 @@ public:
|
||||
gfxRect mRect;
|
||||
// Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
|
||||
gfxFloat mRadii[8];
|
||||
|
||||
private:
|
||||
PixelRoundedRect() MOZ_DELETE;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -111,14 +128,27 @@ public:
|
||||
* pointers to a key object (the +1 being from the hashtable entry), but this
|
||||
* invariant may be temporarily broken.
|
||||
*/
|
||||
class MaskLayerImageKey
|
||||
struct MaskLayerImageKey
|
||||
{
|
||||
public:
|
||||
MaskLayerImageKey(const nsTArray<PixelRoundedRect>& aRoundedClipRects, layers::LayersBackend aBackend)
|
||||
MaskLayerImageKey(layers::LayersBackend aBackend)
|
||||
: mBackend(aBackend)
|
||||
, mLayerCount(0)
|
||||
, mRoundedClipRects(aRoundedClipRects)
|
||||
{}
|
||||
, mRoundedClipRects()
|
||||
{
|
||||
MOZ_COUNT_CTOR(MaskLayerImageKey);
|
||||
}
|
||||
MaskLayerImageKey(const MaskLayerImageKey& aKey)
|
||||
: mBackend(aKey.mBackend)
|
||||
, mLayerCount(aKey.mLayerCount)
|
||||
, mRoundedClipRects(aKey.mRoundedClipRects)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MaskLayerImageKey);
|
||||
}
|
||||
|
||||
~MaskLayerImageKey()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MaskLayerImageKey);
|
||||
}
|
||||
|
||||
void AddRef() const { ++mLayerCount; }
|
||||
void Release() const
|
||||
@ -172,12 +202,19 @@ protected:
|
||||
typedef const MaskLayerImageKey& KeyType;
|
||||
typedef const MaskLayerImageKey* KeyTypePointer;
|
||||
|
||||
MaskLayerImageEntry(KeyTypePointer aKey) : mKey(aKey) {}
|
||||
MaskLayerImageEntry(KeyTypePointer aKey) : mKey(aKey)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MaskLayerImageEntry);
|
||||
}
|
||||
MaskLayerImageEntry(const MaskLayerImageEntry& aOther)
|
||||
: mKey(aOther.mKey.get())
|
||||
{
|
||||
NS_ERROR("ALLOW_MEMMOVE == true, should never be called");
|
||||
}
|
||||
~MaskLayerImageEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MaskLayerImageEntry);
|
||||
}
|
||||
|
||||
// KeyEquals(): does this entry match this key?
|
||||
bool KeyEquals(KeyTypePointer aKey) const
|
||||
|
274
layout/base/PositionedEventTargeting.cpp
Normal file
274
layout/base/PositionedEventTargeting.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/* 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 "PositionedEventTargeting.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsEventListenerManager.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* The basic goal of FindFrameTargetedByInputEvent() is to find a good
|
||||
* target element that can respond to mouse events. Both mouse events and touch
|
||||
* events are targeted at this element. Note that even for touch events, we
|
||||
* check responsiveness to mouse events. We assume Web authors
|
||||
* designing for touch events will take their own steps to account for
|
||||
* inaccurate touch events.
|
||||
*
|
||||
* IsElementClickable() encapsulates the heuristic that determines whether an
|
||||
* element is expected to respond to mouse events. An element is deemed
|
||||
* "clickable" if it has registered listeners for "click", "mousedown" or
|
||||
* "mouseup", or is on a whitelist of element tags (<a>, <button>, <input>,
|
||||
* <select>, <textarea>, <label>), or has role="button", or is a link, or
|
||||
* is a suitable XUL element.
|
||||
* Any descendant (in the same document) of a clickable element is also
|
||||
* deemed clickable since events will propagate to the clickable element from its
|
||||
* descendant.
|
||||
*
|
||||
* If the element directly under the event position is clickable (or
|
||||
* event radii are disabled), we always use that element. Otherwise we collect
|
||||
* all frames intersecting a rectangle around the event position (taking CSS
|
||||
* transforms into account) and choose the best candidate in GetClosest().
|
||||
* Only IsElementClickable() candidates are considered; if none are found,
|
||||
* then we revert to targeting the element under the event position.
|
||||
* We ignore candidates outside the document subtree rooted by the
|
||||
* document of the element directly under the event position. This ensures that
|
||||
* event listeners in ancestor documents don't make it completely impossible
|
||||
* to target a non-clickable element in a child document.
|
||||
*
|
||||
* When both a frame and its ancestor are in the candidate list, we ignore
|
||||
* the ancestor. Otherwise a large ancestor element with a mouse event listener
|
||||
* and some descendant elements that need to be individually targetable would
|
||||
* disable intelligent targeting of those descendants within its bounds.
|
||||
*
|
||||
* GetClosest() computes the transformed axis-aligned bounds of each
|
||||
* candidate frame, then computes the Manhattan distance from the event point
|
||||
* to the bounds rect (which can be zero). The frame with the
|
||||
* shortest distance is chosen. For visited links we multiply the distance
|
||||
* by a specified constant weight; this can be used to make visited links
|
||||
* more or less likely to be targeted than non-visited links.
|
||||
*/
|
||||
|
||||
struct EventRadiusPrefs
|
||||
{
|
||||
uint32_t mVisitedWeight; // in percent, i.e. default is 100
|
||||
uint32_t mSideRadii[4]; // TRBL order, in millimetres
|
||||
bool mEnabled;
|
||||
bool mRegistered;
|
||||
};
|
||||
|
||||
static EventRadiusPrefs sMouseEventRadiusPrefs;
|
||||
static EventRadiusPrefs sTouchEventRadiusPrefs;
|
||||
|
||||
static const EventRadiusPrefs*
|
||||
GetPrefsFor(uint8_t aEventStructType)
|
||||
{
|
||||
EventRadiusPrefs* prefs = nullptr;
|
||||
const char* prefBranch = nullptr;
|
||||
if (aEventStructType == NS_MOZTOUCH_EVENT ||
|
||||
aEventStructType == NS_TOUCH_EVENT) {
|
||||
prefBranch = "touch";
|
||||
prefs = &sTouchEventRadiusPrefs;
|
||||
} else if (aEventStructType == NS_MOUSE_EVENT) {
|
||||
// Mostly for testing purposes
|
||||
prefBranch = "mouse";
|
||||
prefs = &sMouseEventRadiusPrefs;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prefs->mRegistered) {
|
||||
prefs->mRegistered = true;
|
||||
|
||||
nsPrintfCString enabledPref("ui.%s.radius.enabled", prefBranch);
|
||||
Preferences::AddBoolVarCache(&prefs->mEnabled, enabledPref.get(), false);
|
||||
|
||||
nsPrintfCString visitedWeightPref("ui.%s.radius.visitedWeight", prefBranch);
|
||||
Preferences::AddUintVarCache(&prefs->mVisitedWeight, visitedWeightPref.get(), 100);
|
||||
|
||||
static const char prefNames[4][9] =
|
||||
{ "topmm", "rightmm", "bottommm", "leftmm" };
|
||||
for (int32_t i = 0; i < 4; ++i) {
|
||||
nsPrintfCString radiusPref("ui.%s.radius.%s", prefBranch, prefNames[i]);
|
||||
Preferences::AddUintVarCache(&prefs->mSideRadii[i], radiusPref.get(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
static bool
|
||||
HasMouseListener(nsIContent* aContent)
|
||||
{
|
||||
nsEventListenerManager* elm = aContent->GetListenerManager(false);
|
||||
if (!elm) {
|
||||
return false;
|
||||
}
|
||||
return elm->HasListenersFor(nsGkAtoms::onclick) ||
|
||||
elm->HasListenersFor(nsGkAtoms::onmousedown) ||
|
||||
elm->HasListenersFor(nsGkAtoms::onmouseup);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsElementClickable(nsIFrame* aFrame)
|
||||
{
|
||||
// Input events propagate up the content tree so we'll follow the content
|
||||
// ancestors to look for elements accepting the click.
|
||||
for (nsIContent* content = aFrame->GetContent(); content;
|
||||
content = content->GetFlattenedTreeParent()) {
|
||||
if (HasMouseListener(content)) {
|
||||
return true;
|
||||
}
|
||||
if (content->IsHTML()) {
|
||||
nsIAtom* tag = content->Tag();
|
||||
if (tag == nsGkAtoms::button ||
|
||||
tag == nsGkAtoms::input ||
|
||||
tag == nsGkAtoms::select ||
|
||||
tag == nsGkAtoms::textarea ||
|
||||
tag == nsGkAtoms::label) {
|
||||
return true;
|
||||
}
|
||||
} else if (content->IsXUL()) {
|
||||
nsIAtom* tag = content->Tag();
|
||||
// See nsCSSFrameConstructor::FindXULTagData. This code is not
|
||||
// really intended to be used with XUL, though.
|
||||
if (tag == nsGkAtoms::button ||
|
||||
tag == nsGkAtoms::checkbox ||
|
||||
tag == nsGkAtoms::radio ||
|
||||
tag == nsGkAtoms::autorepeatbutton ||
|
||||
tag == nsGkAtoms::menu ||
|
||||
tag == nsGkAtoms::menubutton ||
|
||||
tag == nsGkAtoms::menuitem ||
|
||||
tag == nsGkAtoms::menulist ||
|
||||
tag == nsGkAtoms::scrollbarbutton ||
|
||||
tag == nsGkAtoms::resizer) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::role,
|
||||
nsGkAtoms::button, eIgnoreCase)) {
|
||||
return true;
|
||||
}
|
||||
nsCOMPtr<nsIURI> linkURI;
|
||||
if (content->IsLink(getter_AddRefs(linkURI))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static nscoord
|
||||
AppUnitsFromMM(nsIFrame* aFrame, uint32_t aMM, bool aVertical)
|
||||
{
|
||||
nsPresContext* pc = aFrame->PresContext();
|
||||
nsIPresShell* presShell = pc->PresShell();
|
||||
float result = float(aMM) *
|
||||
(pc->DeviceContext()->AppUnitsPerPhysicalInch() / MM_PER_INCH_FLOAT) *
|
||||
(aVertical ? presShell->GetYResolution() : presShell->GetXResolution());
|
||||
return NSToCoordRound(result);
|
||||
}
|
||||
|
||||
static nsRect
|
||||
GetTargetRect(nsIFrame* aRootFrame, const nsPoint& aPointRelativeToRootFrame,
|
||||
const EventRadiusPrefs* aPrefs)
|
||||
{
|
||||
nsMargin m(AppUnitsFromMM(aRootFrame, aPrefs->mSideRadii[3], false),
|
||||
AppUnitsFromMM(aRootFrame, aPrefs->mSideRadii[0], true),
|
||||
AppUnitsFromMM(aRootFrame, aPrefs->mSideRadii[1], false),
|
||||
AppUnitsFromMM(aRootFrame, aPrefs->mSideRadii[2], true));
|
||||
nsRect r(aPointRelativeToRootFrame, nsSize(0,0));
|
||||
r.Inflate(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static float
|
||||
ComputeDistanceFromRect(const nsPoint& aPoint, const nsRect& aRect)
|
||||
{
|
||||
nscoord dx = NS_MAX(0, NS_MAX(aRect.x - aPoint.x, aPoint.x - aRect.XMost()));
|
||||
nscoord dy = NS_MAX(0, NS_MAX(aRect.y - aPoint.y, aPoint.y - aRect.YMost()));
|
||||
return float(NS_hypot(dx, dy));
|
||||
}
|
||||
|
||||
static nsIFrame*
|
||||
GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
|
||||
const EventRadiusPrefs* aPrefs, nsIFrame* aRestrictToDescendants,
|
||||
nsTArray<nsIFrame*>& aCandidates)
|
||||
{
|
||||
nsIFrame* bestTarget = nullptr;
|
||||
// Lower is better; distance is in appunits
|
||||
float bestDistance = 1e6f;
|
||||
for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
|
||||
nsIFrame* f = aCandidates[i];
|
||||
if (!IsElementClickable(f)) {
|
||||
continue;
|
||||
}
|
||||
// If our current closest frame is a descendant of 'f', skip 'f' (prefer
|
||||
// the nested frame).
|
||||
if (bestTarget && nsLayoutUtils::IsProperAncestorFrameCrossDoc(f, bestTarget, aRoot)) {
|
||||
continue;
|
||||
}
|
||||
if (!nsLayoutUtils::IsAncestorFrameCrossDoc(aRestrictToDescendants, f, aRoot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor(f,
|
||||
nsRect(nsPoint(0, 0), f->GetSize()), aRoot);
|
||||
// distance is in appunits
|
||||
float distance = ComputeDistanceFromRect(aPointRelativeToRootFrame, borderBox);
|
||||
nsIContent* content = f->GetContent();
|
||||
if (content && content->IsElement() &&
|
||||
content->AsElement()->State().HasState(nsEventStates(NS_EVENT_STATE_VISITED))) {
|
||||
distance *= aPrefs->mVisitedWeight / 100.0f;
|
||||
}
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestTarget = f;
|
||||
}
|
||||
}
|
||||
return bestTarget;
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
FindFrameTargetedByInputEvent(uint8_t aEventStructType,
|
||||
nsIFrame* aRootFrame,
|
||||
const nsPoint& aPointRelativeToRootFrame,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
bool ignoreRootScrollFrame = (aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME) != 0;
|
||||
nsIFrame* target =
|
||||
nsLayoutUtils::GetFrameForPoint(aRootFrame, aPointRelativeToRootFrame,
|
||||
false, ignoreRootScrollFrame);
|
||||
const EventRadiusPrefs* prefs = GetPrefsFor(aEventStructType);
|
||||
if (!prefs || !prefs->mEnabled || (target && IsElementClickable(target))) {
|
||||
return target;
|
||||
}
|
||||
|
||||
nsRect targetRect = GetTargetRect(aRootFrame, aPointRelativeToRootFrame, prefs);
|
||||
nsAutoTArray<nsIFrame*,8> candidates;
|
||||
nsresult rv = nsLayoutUtils::GetFramesForArea(aRootFrame, targetRect, candidates,
|
||||
false, ignoreRootScrollFrame);
|
||||
if (NS_FAILED(rv)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
// If the exact target is non-null, only consider candidate targets in the same
|
||||
// document as the exact target. Otherwise, if an ancestor document has
|
||||
// a mouse event handler for example, targets that are !IsElementClickable can
|
||||
// never be targeted --- something nsSubDocumentFrame in an ancestor document
|
||||
// would be targeted instead.
|
||||
nsIFrame* restrictToDescendants = target ?
|
||||
target->PresContext()->PresShell()->GetRootFrame() : aRootFrame;
|
||||
nsIFrame* closestClickable =
|
||||
GetClosest(aRootFrame, aPointRelativeToRootFrame, prefs,
|
||||
restrictToDescendants, candidates);
|
||||
return closestClickable ? closestClickable : target;
|
||||
}
|
||||
|
||||
}
|
31
layout/base/PositionedEventTargeting.h
Normal file
31
layout/base/PositionedEventTargeting.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_PositionedEventTargeting_h
|
||||
#define mozilla_PositionedEventTargeting_h
|
||||
|
||||
#include "nsPoint.h"
|
||||
|
||||
class nsGUIEvent;
|
||||
class nsIFrame;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum {
|
||||
INPUT_IGNORE_ROOT_SCROLL_FRAME = 0x01
|
||||
};
|
||||
/**
|
||||
* Finds the target frame for a pointer event given the event type and location.
|
||||
* This can look for frames within a rectangle surrounding the actual location
|
||||
* that are suitable targets, to account for inaccurate pointing devices.
|
||||
*/
|
||||
nsIFrame*
|
||||
FindFrameTargetedByInputEvent(uint8_t aEventStructType,
|
||||
nsIFrame* aRootFrame,
|
||||
const nsPoint& aPointRelativeToRootFrame,
|
||||
uint32_t aFlags = 0);
|
||||
|
||||
}
|
||||
|
||||
#endif /* mozilla_PositionedEventTargeting_h */
|
@ -615,19 +615,9 @@ bool
|
||||
nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
|
||||
nsIFrame* aCommonAncestor)
|
||||
{
|
||||
if (aFrame == aCommonAncestor)
|
||||
if (aFrame == aAncestorFrame)
|
||||
return false;
|
||||
|
||||
nsIFrame* parentFrame = GetCrossDocParentFrame(aFrame);
|
||||
|
||||
while (parentFrame != aCommonAncestor) {
|
||||
if (parentFrame == aAncestorFrame)
|
||||
return true;
|
||||
|
||||
parentFrame = GetCrossDocParentFrame(parentFrame);
|
||||
}
|
||||
|
||||
return false;
|
||||
return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -635,9 +625,12 @@ bool
|
||||
nsLayoutUtils::IsAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
|
||||
nsIFrame* aCommonAncestor)
|
||||
{
|
||||
if (aFrame == aAncestorFrame)
|
||||
return true;
|
||||
return IsProperAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
|
||||
for (nsIFrame* f = aFrame; f != aCommonAncestor;
|
||||
f = GetCrossDocParentFrame(f)) {
|
||||
if (f == aAncestorFrame)
|
||||
return true;
|
||||
}
|
||||
return aCommonAncestor == aAncestorFrame;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -645,21 +638,13 @@ bool
|
||||
nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
|
||||
nsIFrame* aCommonAncestor)
|
||||
{
|
||||
if (aFrame == aCommonAncestor) {
|
||||
if (aFrame == aAncestorFrame)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIFrame* parentFrame = aFrame->GetParent();
|
||||
|
||||
while (parentFrame != aCommonAncestor) {
|
||||
if (parentFrame == aAncestorFrame) {
|
||||
for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
|
||||
if (f == aAncestorFrame)
|
||||
return true;
|
||||
}
|
||||
|
||||
parentFrame = parentFrame->GetParent();
|
||||
}
|
||||
|
||||
return false;
|
||||
return aCommonAncestor == aAncestorFrame;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -99,6 +99,7 @@
|
||||
#ifdef MOZ_REFLOW_PERF
|
||||
#include "nsFontMetrics.h"
|
||||
#endif
|
||||
#include "PositionedEventTargeting.h"
|
||||
|
||||
#include "nsIReflowCallback.h"
|
||||
|
||||
@ -5891,16 +5892,15 @@ PresShell::HandleEvent(nsIFrame *aFrame,
|
||||
} else {
|
||||
eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
|
||||
}
|
||||
{
|
||||
bool ignoreRootScrollFrame = false;
|
||||
if (aEvent->eventStructType == NS_MOUSE_EVENT) {
|
||||
ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame;
|
||||
}
|
||||
nsIFrame* target = nsLayoutUtils::GetFrameForPoint(frame, eventPoint,
|
||||
false, ignoreRootScrollFrame);
|
||||
if (target) {
|
||||
frame = target;
|
||||
}
|
||||
uint32_t flags = 0;
|
||||
if (aEvent->eventStructType == NS_MOUSE_EVENT &&
|
||||
static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame) {
|
||||
flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
|
||||
}
|
||||
nsIFrame* target =
|
||||
FindFrameTargetedByInputEvent(aEvent->eventStructType, frame, eventPoint, flags);
|
||||
if (target) {
|
||||
frame = target;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,7 @@ MOCHITEST_FILES = \
|
||||
test_bug667512.html \
|
||||
test_bug677878.html \
|
||||
test_bug696020.html \
|
||||
test_event_target_radius.html \
|
||||
test_mozPaintCount.html \
|
||||
test_scroll_selection_into_view.html \
|
||||
test_bug583889.html \
|
||||
|
177
layout/base/tests/test_event_target_radius.html
Normal file
177
layout/base/tests/test_event_target_radius.html
Normal file
@ -0,0 +1,177 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html id="html" style="height:100%">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=780847
|
||||
-->
|
||||
<head>
|
||||
<title>Test radii for mouse events</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
.target { position:absolute; left:100px; top:100px; width:100px; height:100px; background:blue; }
|
||||
</style>
|
||||
</head>
|
||||
<body id="body" onload="setTimeout(runTest, 0)" style="margin:0; width:100%; height:100%">
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="ruler" style="position:absolute; left:0; top:0; width:1mozmm; height:0;"></div>
|
||||
|
||||
<div class="target" id="t" onmousedown="x=1"></div>
|
||||
|
||||
<div class="target" id="t2" hidden></div>
|
||||
|
||||
<input class="target" id="t3_1" hidden></input>
|
||||
<a href="#" class="target" id="t3_2" hidden></a>
|
||||
<label class="target" id="t3_3" hidden></label>
|
||||
<button class="target" id="t3_4" hidden></button>
|
||||
<select class="target" id="t3_5" hidden></select>
|
||||
<textarea class="target" id="t3_6" hidden></textarea>
|
||||
<div role="button" class="target" id="t3_7" hidden></div>
|
||||
<img class="target" id="t3_8" hidden></img>
|
||||
|
||||
<div class="target" style="transform:translate(-80px,0);" id="t4" onmousedown="x=1" hidden></div>
|
||||
|
||||
<div class="target" style="left:0; z-index:1" id="t5_left" onmousedown="x=1" hidden></div>
|
||||
<div class="target" style="left:106px;" id="t5_right" onmousedown="x=1" hidden></div>
|
||||
<div class="target" style="left:0; top:210px;" id="t5_below" onmousedown="x=1" hidden></div>
|
||||
|
||||
<div class="target" id="t6" onmousedown="x=1" hidden>
|
||||
<div id="t6_inner" style="position:absolute; left:-20px; top:20px; width:60px; height:60px; background:yellow;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
var prefBase = "ui.mouse.radius.";
|
||||
SpecialPowers.setBoolPref(prefBase + "enabled", true);
|
||||
SpecialPowers.setIntPref(prefBase + "leftmm", 12);
|
||||
SpecialPowers.setIntPref(prefBase + "topmm", 8);
|
||||
SpecialPowers.setIntPref(prefBase + "rightmm", 4);
|
||||
SpecialPowers.setIntPref(prefBase + "bottommm", 4);
|
||||
SpecialPowers.setIntPref(prefBase + "visitedWeight", 50);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var mm = document.getElementById("ruler").getBoundingClientRect().width;
|
||||
ok(4*mm >= 10, "WARNING: mm " + mm + " too small in this configuration. Test results will be bogus");
|
||||
|
||||
function endTest() {
|
||||
SpecialPowers.clearUserPref(prefBase + "enabled");
|
||||
SpecialPowers.clearUserPref(prefBase + "leftmmm");
|
||||
SpecialPowers.clearUserPref(prefBase + "topmm");
|
||||
SpecialPowers.clearUserPref(prefBase + "rightmm");
|
||||
SpecialPowers.clearUserPref(prefBase + "bottommm");
|
||||
SpecialPowers.clearUserPref(prefBase + "visitedWeight");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var eventTarget;
|
||||
window.onmousedown = function(event) { eventTarget = event.target; };
|
||||
|
||||
function testMouseClick(idPosition, dx, dy, idTarget, msg) {
|
||||
eventTarget = null;
|
||||
synthesizeMouse(document.getElementById(idPosition), dx, dy, {});
|
||||
try {
|
||||
is(eventTarget.id, idTarget,
|
||||
"checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]");
|
||||
} catch (ex) {
|
||||
ok(false, "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]; got " + eventTarget);
|
||||
}
|
||||
}
|
||||
|
||||
function setShowing(id, show) {
|
||||
var e = document.getElementById(id);
|
||||
e.hidden = !show;
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Test basic functionality: clicks sufficiently close to the element
|
||||
// should be allowed to hit the element. We test points just inside and
|
||||
// just outside the edges we set up in the prefs.
|
||||
testMouseClick("t", 100 + 13*mm, 10, "body", "basic functionality");
|
||||
testMouseClick("t", 100 + 11*mm, 10, "t", "basic functionality");
|
||||
testMouseClick("t", 10, 100 + 9*mm, "body", "basic functionality");
|
||||
testMouseClick("t", 10, 100 + 7*mm, "t", "basic functionality");
|
||||
testMouseClick("t", -5*mm, 10, "body", "basic functionality");
|
||||
testMouseClick("t", -3*mm, 10, "t", "basic functionality");
|
||||
testMouseClick("t", 10, -5*mm, "body", "basic functionality");
|
||||
testMouseClick("t", 10, -3*mm, "t", "basic functionality");
|
||||
setShowing("t", false);
|
||||
|
||||
// Now test the criteria we use to determine which elements are hittable
|
||||
// this way.
|
||||
|
||||
setShowing("t2", true);
|
||||
var t2 = document.getElementById("t2");
|
||||
// Unadorned DIVs are not click radius targets
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "body", "unadorned DIV");
|
||||
// DIVs with the right event handlers are click radius targets
|
||||
t2.onmousedown = function() {};
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onmousedown");
|
||||
t2.onmousedown = null;
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onmousedown removed");
|
||||
t2.onmouseup = function() {};
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onmouseup");
|
||||
t2.onmouseup = null;
|
||||
t2.onclick = function() {};
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onclick");
|
||||
t2.onclick = null;
|
||||
// Keypresses don't make click radius targets
|
||||
t2.onkeypress = function() {};
|
||||
testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onkeypress");
|
||||
t2.onkeypress = null;
|
||||
setShowing("t2", false);
|
||||
|
||||
// Now check that certain elements are click radius targets and others are not
|
||||
for (var i = 1; i <= 8; ++i) {
|
||||
var id = "t3_" + i;
|
||||
var shouldHit = i <= 7;
|
||||
setShowing(id, true);
|
||||
testMouseClick(id, 100 + 11*mm, 10, shouldHit ? id : "body",
|
||||
"<" + document.getElementById(id).tagName + "> element");
|
||||
setShowing(id, false);
|
||||
}
|
||||
|
||||
// Check that our targeting computations take into account the effects of
|
||||
// CSS transforms
|
||||
setShowing("t4", true);
|
||||
testMouseClick("t4", -1, 10, "t4", "translated DIV");
|
||||
setShowing("t4", false);
|
||||
|
||||
// Test the prioritization of multiple targets based on distance to
|
||||
// the target.
|
||||
setShowing("t5_left", true);
|
||||
setShowing("t5_right", true);
|
||||
setShowing("t5_below", true);
|
||||
testMouseClick("t5_left", 102, 10, "t5_left", "closest DIV is left");
|
||||
testMouseClick("t5_left", 102.5, 10, "t5_left",
|
||||
"closest DIV to midpoint is left because of its higher z-index");
|
||||
testMouseClick("t5_left", 104, 10, "t5_right", "closest DIV is right");
|
||||
testMouseClick("t5_left", 10, 104, "t5_left", "closest DIV is left");
|
||||
testMouseClick("t5_left", 10, 105, "t5_left",
|
||||
"closest DIV to midpoint is left because of its higher z-index");
|
||||
testMouseClick("t5_left", 10, 106, "t5_below", "closest DIV is below");
|
||||
setShowing("t5_left", false);
|
||||
setShowing("t5_right", false);
|
||||
setShowing("t5_below", false);
|
||||
|
||||
// Test behavior of nested elements.
|
||||
// The following behaviors are questionable and may need to be changed.
|
||||
setShowing("t6", true);
|
||||
testMouseClick("t6_inner", -1, 10, "t6_inner",
|
||||
"inner element is clickable because its parent is, even when it sticks outside parent");
|
||||
testMouseClick("t6_inner", 19, -1, "t6_inner",
|
||||
"when outside both inner and parent, but in range of both, the inner is selected");
|
||||
testMouseClick("t6_inner", 25, -1, "t6",
|
||||
"clicking in clickable parent close to inner activates parent, not inner");
|
||||
setShowing("t6", false);
|
||||
|
||||
// Not yet tested:
|
||||
// -- visited link weight
|
||||
// -- "Closest" using Euclidean distance
|
||||
endTest();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1575,7 +1575,7 @@ nsListControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
|
||||
nsresult error = NS_OK;
|
||||
bool selected = false;
|
||||
int32_t indx = val.ToInteger(&error, 10); // Get index from aValue
|
||||
if (error == 0)
|
||||
if (NS_SUCCEEDED(error))
|
||||
selected = IsContentSelectedByIndex(indx);
|
||||
|
||||
aValue.Assign(selected ? NS_LITERAL_STRING("1") : NS_LITERAL_STRING("0"));
|
||||
|
@ -935,7 +935,7 @@ nsFrameSelection::MoveCaret(uint32_t aKeycode,
|
||||
aVisualMovement);
|
||||
|
||||
if (NS_FAILED(result) || !frame)
|
||||
return result?result:NS_ERROR_FAILURE;
|
||||
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
|
||||
|
||||
//set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking
|
||||
//when we hit scrollable views. If no limiter then just let it go ahead
|
||||
@ -5567,7 +5567,7 @@ Selection::SelectionLanguageChange(bool aLangRTL)
|
||||
uint8_t levelBefore, levelAfter;
|
||||
result = GetPresContext(getter_AddRefs(context));
|
||||
if (NS_FAILED(result) || !context)
|
||||
return result?result:NS_ERROR_FAILURE;
|
||||
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
|
||||
|
||||
uint8_t level = NS_GET_EMBEDDING_LEVEL(focusFrame);
|
||||
int32_t focusOffset = GetFocusOffset();
|
||||
|
@ -7622,8 +7622,15 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
int32_t skipLength = newLineOffset >= 0 ? length - 1 : length;
|
||||
int32_t whitespaceCount =
|
||||
GetTrimmableWhitespaceCount(frag, offset, skipLength, 1);
|
||||
offset += whitespaceCount;
|
||||
length -= whitespaceCount;
|
||||
if (whitespaceCount) {
|
||||
offset += whitespaceCount;
|
||||
length -= whitespaceCount;
|
||||
// Make sure this frame maps the trimmable whitespace.
|
||||
if (NS_UNLIKELY(offset > GetContentEnd())) {
|
||||
SetLength(offset - GetContentOffset(), &aLineLayout,
|
||||
ALLOW_FRAME_CREATION_AND_DESTRUCTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool completedFirstLetter = false;
|
||||
|
@ -182,11 +182,14 @@ math[display="inline"] {
|
||||
ms {
|
||||
display: inline;
|
||||
}
|
||||
ms:before {
|
||||
content: open-quote;
|
||||
ms:before, ms:after {
|
||||
content: "\0022";
|
||||
}
|
||||
ms:after {
|
||||
content: close-quote;
|
||||
ms[lquote]:before {
|
||||
content: attr(lquote);
|
||||
}
|
||||
ms[rquote]:after {
|
||||
content: attr(rquote);
|
||||
}
|
||||
|
||||
merror {
|
||||
|
@ -111,7 +111,6 @@ nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
|
||||
|
||||
ForceTrimChildTextFrames();
|
||||
|
||||
SetQuotes(false);
|
||||
ProcessTextData();
|
||||
return rv;
|
||||
}
|
||||
@ -248,20 +247,6 @@ nsMathMLTokenFrame::MarkIntrinsicWidthsDirty()
|
||||
nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMathMLTokenFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType)
|
||||
{
|
||||
if (nsGkAtoms::lquote_ == aAttribute ||
|
||||
nsGkAtoms::rquote_ == aAttribute) {
|
||||
SetQuotes(true);
|
||||
}
|
||||
|
||||
return nsMathMLContainerFrame::
|
||||
AttributeChanged(aNameSpaceID, aAttribute, aModType);
|
||||
}
|
||||
|
||||
void
|
||||
nsMathMLTokenFrame::ProcessTextData()
|
||||
{
|
||||
@ -369,55 +354,3 @@ nsMathMLTokenFrame::SetTextStyle()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// For <ms>, it is assumed that the mathml.css file contains two rules:
|
||||
// ms:before { content: open-quote; }
|
||||
// ms:after { content: close-quote; }
|
||||
// With these two rules, the frame construction code will
|
||||
// create inline frames that contain text frames which themselves
|
||||
// contain the text content of the quotes.
|
||||
// So the main idea in this code is to see if there are lquote and
|
||||
// rquote attributes. If these are there, we ovewrite the default
|
||||
// quotes in the text frames.
|
||||
// XXX this is somewhat bogus, we probably should map lquote and rquote
|
||||
// to 'content' style rules
|
||||
//
|
||||
// But what if the mathml.css file wasn't loaded?
|
||||
// We also check that we are not relying on null pointers...
|
||||
|
||||
static void
|
||||
SetQuote(nsIFrame* aFrame, nsString& aValue, bool aNotify)
|
||||
{
|
||||
if (!aFrame)
|
||||
return;
|
||||
|
||||
nsIFrame* textFrame = aFrame->GetFirstPrincipalChild();
|
||||
if (!textFrame)
|
||||
return;
|
||||
|
||||
nsIContent* quoteContent = textFrame->GetContent();
|
||||
if (!quoteContent->IsNodeOfType(nsINode::eTEXT))
|
||||
return;
|
||||
|
||||
quoteContent->SetText(aValue, aNotify);
|
||||
}
|
||||
|
||||
void
|
||||
nsMathMLTokenFrame::SetQuotes(bool aNotify)
|
||||
{
|
||||
if (mContent->Tag() != nsGkAtoms::ms_)
|
||||
return;
|
||||
|
||||
nsAutoString value;
|
||||
// lquote
|
||||
if (GetAttribute(mContent, mPresentationData.mstyle,
|
||||
nsGkAtoms::lquote_, value)) {
|
||||
SetQuote(nsLayoutUtils::GetBeforeFrame(this), value, aNotify);
|
||||
}
|
||||
// rquote
|
||||
if (GetAttribute(mContent, mPresentationData.mstyle,
|
||||
nsGkAtoms::rquote_, value)) {
|
||||
SetQuote(nsLayoutUtils::GetAfterFrame(this), value, aNotify);
|
||||
}
|
||||
}
|
||||
|
@ -60,11 +60,6 @@ public:
|
||||
|
||||
virtual void MarkIntrinsicWidthsDirty();
|
||||
|
||||
NS_IMETHOD
|
||||
AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType);
|
||||
|
||||
virtual nsresult
|
||||
ChildListChanged(int32_t aModType)
|
||||
{
|
||||
@ -85,9 +80,6 @@ protected:
|
||||
// depending on its textual content
|
||||
bool SetTextStyle();
|
||||
|
||||
// helper to set the quotes of <ms>
|
||||
void SetQuotes(bool aNotify);
|
||||
|
||||
void ForceTrimChildTextFrames();
|
||||
};
|
||||
|
||||
|
28
layout/reftests/canvas/784573-1-ref.html
Normal file
28
layout/reftests/canvas/784573-1-ref.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<p><canvas width="100" height="100" id="c"></canvas></p>
|
||||
<script type="text/javascript">
|
||||
var c = document.getElementById('c').getContext('2d');
|
||||
|
||||
c.shadowColor = '#f00';
|
||||
c.shadowBlur = 4;
|
||||
c.lineWidth = 2;
|
||||
|
||||
c.moveTo(80, 40);
|
||||
c.lineTo(50, 70);
|
||||
c.lineTo(20, 40);
|
||||
c.lineTo(50, 10);
|
||||
c.closePath();
|
||||
c.stroke();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
36
layout/reftests/canvas/784573-1.html
Normal file
36
layout/reftests/canvas/784573-1.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
This tests that paths are stored in device space. This means that changing
|
||||
the transform does not move the points already drawn.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<p><canvas width="100" height="100" id="c"></canvas></p>
|
||||
<script type="text/javascript">
|
||||
var c = document.getElementById('c').getContext('2d');
|
||||
|
||||
c.shadowColor = '#f00';
|
||||
c.shadowBlur = 4;
|
||||
c.lineWidth = 2;
|
||||
|
||||
c.translate(50, 40);
|
||||
|
||||
var d = 30;
|
||||
c.beginPath();
|
||||
c.moveTo(d, 0);
|
||||
for (var n = 0; n < 3; n++) {
|
||||
c.rotate(3.14159 / 2);
|
||||
c.lineTo(d, 0);
|
||||
}
|
||||
c.closePath();
|
||||
c.stroke();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -82,3 +82,6 @@ fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == 672646-alpha-radial-g
|
||||
== transformed-path.html transformed-path.html
|
||||
|
||||
== 749467-1.html 749467-1-ref.html
|
||||
|
||||
# You get a little bit of rounding fuzz on OSX from transforming the paths between user space and device space
|
||||
fuzzy-if(azureQuartz,2,113) fuzzy-if(d2d,12,17) == 784573-1.html 784573-1-ref.html
|
||||
|
11
layout/reftests/mathml/dir-9-ref.html
Normal file
11
layout/reftests/mathml/dir-9-ref.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Test dir=rtl</title></head>
|
||||
<body>
|
||||
<p>ms lquote="A" rquote="B":
|
||||
<math>
|
||||
<ms lquote="B" rquote="A">___</ms>
|
||||
</math>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
11
layout/reftests/mathml/dir-9.html
Normal file
11
layout/reftests/mathml/dir-9.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Test dir=rtl</title></head>
|
||||
<body>
|
||||
<p>ms lquote="A" rquote="B":
|
||||
<math dir="rtl">
|
||||
<ms lquote="A" rquote="B">___</ms>
|
||||
</math>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -7,9 +7,12 @@ p { height:2em; }
|
||||
</head>
|
||||
<body>
|
||||
<p>:L_R</p>
|
||||
<p>:L_”</p>
|
||||
<p>:“_R</p>
|
||||
<p>:L_”</p>
|
||||
<p>:“_R</p>
|
||||
<p>:L_"</p>
|
||||
<p>:"_R</p>
|
||||
<p>:L_"</p>
|
||||
<p>:"_R</p>
|
||||
<p>:"_"</p>
|
||||
<p>:"_"</p>
|
||||
<p>:"_"</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -11,6 +11,9 @@ p { height:2em; }
|
||||
<p>:<ms id="m2" xmlns="http://www.w3.org/1998/Math/MathML">_</ms></p>
|
||||
<p>:<ms id="m3" xmlns="http://www.w3.org/1998/Math/MathML" lquote="_">_</ms></p>
|
||||
<p>:<ms id="m4" xmlns="http://www.w3.org/1998/Math/MathML" rquote="_">_</ms></p>
|
||||
<p>:<ms id="m5" xmlns="http://www.w3.org/1998/Math/MathML" lquote="_" rquote="_">_</ms></p>
|
||||
<p>:<ms id="m6" xmlns="http://www.w3.org/1998/Math/MathML" lquote="_">_</ms></p>
|
||||
<p>:<ms id="m7" xmlns="http://www.w3.org/1998/Math/MathML" rquote="_">_</ms></p>
|
||||
<script>
|
||||
function doTest() {
|
||||
var m1 = document.getElementById("m1");
|
||||
@ -21,6 +24,13 @@ function doTest() {
|
||||
m3.setAttribute("lquote", "L");
|
||||
var m4 = document.getElementById("m4");
|
||||
m4.setAttribute("rquote", "R");
|
||||
var m5 = document.getElementById("m5");
|
||||
m5.removeAttribute("lquote");
|
||||
m5.removeAttribute("rquote");
|
||||
var m6 = document.getElementById("m6");
|
||||
m6.removeAttribute("lquote");
|
||||
var m7 = document.getElementById("m7");
|
||||
m7.removeAttribute("rquote");
|
||||
document.documentElement.removeAttribute('class');
|
||||
}
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
|
@ -6,6 +6,7 @@
|
||||
fails == dir-6.html dir-6-ref.html
|
||||
== dir-7.html dir-7-ref.html
|
||||
fails == dir-8.html dir-8-ref.html
|
||||
fails == dir-9.html dir-9-ref.html # Bug 787215
|
||||
== mirror-op-1.html mirror-op-1-ref.html
|
||||
!= mirror-op-2.html mirror-op-2-ref.html
|
||||
!= mirror-op-3.html mirror-op-3-ref.html
|
||||
@ -71,7 +72,7 @@ fails-if(winWidget) == mfenced-10.html mfenced-10-ref.html
|
||||
== mstyle-2.xhtml mstyle-2-ref.xhtml
|
||||
== mstyle-3.xhtml mstyle-3-ref.xhtml
|
||||
== mstyle-4.xhtml mstyle-4-ref.xhtml
|
||||
fails == mstyle-5.xhtml mstyle-5-ref.xhtml # See bug 569125#c29
|
||||
fails == mstyle-5.xhtml mstyle-5-ref.xhtml # Bug 787215
|
||||
== scale-stretchy-1.xhtml scale-stretchy-1-ref.xhtml
|
||||
!= scale-stretchy-2.xhtml scale-stretchy-2-ref.xhtml
|
||||
== scale-stretchy-3.xhtml scale-stretchy-3-ref.xhtml
|
||||
@ -107,5 +108,5 @@ fails == mstyle-5.xhtml mstyle-5-ref.xhtml # See bug 569125#c29
|
||||
== whitespace-trim-1.html whitespace-trim-1-ref.html
|
||||
== whitespace-trim-2.html whitespace-trim-2-ref.html
|
||||
== whitespace-trim-3.html whitespace-trim-3-ref.html
|
||||
fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 560100
|
||||
fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215
|
||||
== whitespace-trim-5.html whitespace-trim-5-ref.html
|
||||
|
@ -734,7 +734,7 @@ nsUserFontSet::LogMessage(gfxProxyFontEntry *aProxy,
|
||||
nsCSSProps::kFontStretchKTable).get(),
|
||||
aProxy->mSrcIndex);
|
||||
|
||||
if (aStatus != 0) {
|
||||
if (NS_FAILED(aStatus)) {
|
||||
msg.Append(": ");
|
||||
switch (aStatus) {
|
||||
case NS_ERROR_DOM_BAD_URI:
|
||||
|
@ -191,10 +191,16 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
input.inputProcRefCon = stm;
|
||||
r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Global, 0, &input, sizeof(input));
|
||||
if (r != 0) {
|
||||
fprintf(stderr, "cubeb_audiounit: FATAL: AudioUnitSetProperty(SetRenderCallback) returned %ld\n", (long) r);
|
||||
}
|
||||
assert(r == 0);
|
||||
|
||||
r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
|
||||
0, &ss, sizeof(ss));
|
||||
if (r != 0) {
|
||||
fprintf(stderr, "cubeb_audiounit: FATAL: AudioUnitSetProperty(StreamFormat) returned %ld\n", (long) r);
|
||||
}
|
||||
assert(r == 0);
|
||||
|
||||
buffer_size = ss.mSampleRate / 1000.0 * latency * ss.mBytesPerFrame / NBUFS;
|
||||
@ -204,6 +210,9 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
assert(buffer_size % ss.mBytesPerFrame == 0);
|
||||
|
||||
r = AudioUnitInitialize(stm->unit);
|
||||
if (r != 0) {
|
||||
fprintf(stderr, "cubeb_audiounit: FATAL: AudioUnitInitialize returned %ld\n", (long) r);
|
||||
}
|
||||
assert(r == 0);
|
||||
|
||||
*stream = stm;
|
||||
|
@ -1551,11 +1551,21 @@ void (*_malloc_message)(const char *p1, const char *p2, const char *p3,
|
||||
#endif
|
||||
|
||||
#include <mozilla/Assertions.h>
|
||||
#include <mozilla/Attributes.h>
|
||||
|
||||
/* RELEASE_ASSERT calls jemalloc_crash() instead of calling MOZ_CRASH()
|
||||
* directly because we want crashing to add a frame to the stack. This makes
|
||||
* it easier to find the failing assertion in crash stacks. */
|
||||
MOZ_NEVER_INLINE static void
|
||||
jemalloc_crash()
|
||||
{
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
#if defined(MOZ_JEMALLOC_HARD_ASSERTS)
|
||||
# define RELEASE_ASSERT(assertion) do { \
|
||||
if (!(assertion)) { \
|
||||
MOZ_CRASH(); \
|
||||
jemalloc_crash(); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
@ -10,6 +10,7 @@ import android.view.MenuItem;
|
||||
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
public class App extends BrowserApp {
|
||||
|
@ -20,6 +20,7 @@ import java.net.URL;
|
||||
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.WebAppAllocator;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
|
@ -1788,6 +1788,29 @@ pref("font.size.inflation.lineThreshold", 400);
|
||||
*/
|
||||
pref("font.size.inflation.mappingIntercept", 1);
|
||||
|
||||
/*
|
||||
* When enabled, the touch.radius and mouse.radius prefs allow events to be dispatched
|
||||
* to nearby elements that are sensitive to the event. See PositionedEventTargeting.cpp.
|
||||
* The 'mm' prefs define a rectangle around the nominal event target point within which
|
||||
* we will search for suitable elements. 'visitedWeight' is a percentage weight;
|
||||
* a value > 100 makes a visited link be treated as further away from the event target
|
||||
* than it really is, while a value < 100 makes a visited link be treated as closer
|
||||
* to the event target than it really is.
|
||||
*/
|
||||
pref("ui.touch.radius.enabled", false);
|
||||
pref("ui.touch.radius.leftmm", 8);
|
||||
pref("ui.touch.radius.topmm", 12);
|
||||
pref("ui.touch.radius.rightmm", 8);
|
||||
pref("ui.touch.radius.bottommm", 4);
|
||||
pref("ui.touch.radius.visitedWeight", 120);
|
||||
|
||||
pref("ui.mouse.radius.enabled", false);
|
||||
pref("ui.mouse.radius.leftmm", 8);
|
||||
pref("ui.mouse.radius.topmm", 12);
|
||||
pref("ui.mouse.radius.rightmm", 8);
|
||||
pref("ui.mouse.radius.bottommm", 4);
|
||||
pref("ui.mouse.radius.visitedWeight", 120);
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
pref("font.name.serif.ar", "Times New Roman");
|
||||
|
@ -1678,9 +1678,12 @@ NS_SecurityHashURI(nsIURI* aURI)
|
||||
scheme.EqualsLiteral("news"))
|
||||
{
|
||||
nsAutoCString spec;
|
||||
uint32_t specHash = baseURI->GetSpec(spec);
|
||||
if (NS_SUCCEEDED(specHash))
|
||||
uint32_t specHash;
|
||||
nsresult res = baseURI->GetSpec(spec);
|
||||
if (NS_SUCCEEDED(res))
|
||||
specHash = mozilla::HashString(spec);
|
||||
else
|
||||
specHash = static_cast<uint32_t>(res);
|
||||
return specHash;
|
||||
}
|
||||
|
||||
|
@ -178,12 +178,12 @@ main(int argc, char* argv[])
|
||||
|
||||
nsCOMPtr<nsICategoryManager> catman =
|
||||
do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
nsCString previous;
|
||||
|
||||
nsCOMPtr<nsIStreamConverterService> StreamConvService =
|
||||
do_GetService(kStreamConverterServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
// Define the *from* content type and *to* content-type for conversion.
|
||||
static const char fromStr[] = "a/foo";
|
||||
@ -201,14 +201,14 @@ main(int argc, char* argv[])
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsCOMPtr<nsIURI> dummyURI;
|
||||
rv = NS_NewURI(getter_AddRefs(dummyURI), "http://meaningless");
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
|
||||
dummyURI,
|
||||
nullptr, // inStr
|
||||
"text/plain", // content-type
|
||||
-1); // XXX fix contentLength
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
nsCOMPtr<nsIRequest> request(do_QueryInterface(channel));
|
||||
#endif
|
||||
@ -227,7 +227,7 @@ main(int argc, char* argv[])
|
||||
nsIStreamListener *converterListener = nullptr;
|
||||
rv = StreamConvService->AsyncConvertData(fromStr, toStr,
|
||||
dataReceiver, nullptr, &converterListener);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
NS_RELEASE(dataReceiver);
|
||||
|
||||
// at this point we have a stream listener to push data to, and the one
|
||||
@ -235,17 +235,17 @@ main(int argc, char* argv[])
|
||||
// going. Typically these On*() calls would be made inside their respective wrappers On*()
|
||||
// methods.
|
||||
rv = converterListener->OnStartRequest(request, nullptr);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = SEND_DATA("aaa");
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = SEND_DATA("aaa");
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
// Finish the request.
|
||||
rv = converterListener->OnStopRequest(request, nullptr, rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
NS_RELEASE(converterListener);
|
||||
#else
|
||||
@ -253,7 +253,7 @@ main(int argc, char* argv[])
|
||||
nsCOMPtr<nsIInputStream> convertedData;
|
||||
rv = StreamConvService->Convert(inputData, fromStr, toStr,
|
||||
nullptr, getter_AddRefs(convertedData));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
#endif
|
||||
|
||||
// Enter the message pump to allow the URL load to proceed.
|
||||
@ -264,5 +264,5 @@ main(int argc, char* argv[])
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
NS_ShutdownXPCOM(nullptr);
|
||||
return rv;
|
||||
return 0;
|
||||
}
|
||||
|
@ -40,14 +40,14 @@ main(int argc, char* argv[])
|
||||
nsIInputStream* in = nullptr;
|
||||
|
||||
nsCOMPtr<nsIIOService> service(do_GetService(kIOServiceCID, &ret));
|
||||
if (NS_FAILED(ret)) return ret;
|
||||
if (NS_FAILED(ret)) return 1;
|
||||
|
||||
nsIChannel *channel = nullptr;
|
||||
ret = service->NewChannel(NS_LITERAL_CSTRING(TEST_URL), nullptr, nullptr, &channel);
|
||||
if (NS_FAILED(ret)) return ret;
|
||||
if (NS_FAILED(ret)) return 1;
|
||||
|
||||
ret = channel->Open(&in);
|
||||
if (NS_FAILED(ret)) return ret;
|
||||
if (NS_FAILED(ret)) return 1;
|
||||
|
||||
nsIPersistentProperties* props;
|
||||
ret = CallCreateInstance(kPersistentPropertiesCID, &props);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user