зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central and inbound
This commit is contained in:
Коммит
14d2681e74
|
@ -11,7 +11,7 @@ typedef long AccessibleTextBoundary;
|
|||
interface nsIAccessible;
|
||||
interface nsIPersistentProperties;
|
||||
|
||||
[scriptable, uuid(0f4633b1-550c-4b50-8c04-0eb1005eef2f)]
|
||||
[scriptable, uuid(43d81eb0-1215-4dc4-9226-a4355bd2d20d)]
|
||||
interface nsIAccessibleText : nsISupports
|
||||
{
|
||||
// In parameters for character offsets:
|
||||
|
@ -27,7 +27,6 @@ interface nsIAccessibleText : nsISupports
|
|||
const AccessibleTextBoundary BOUNDARY_SENTENCE_END = 4; // don't use, deprecated
|
||||
const AccessibleTextBoundary BOUNDARY_LINE_START = 5;
|
||||
const AccessibleTextBoundary BOUNDARY_LINE_END = 6;
|
||||
const AccessibleTextBoundary BOUNDARY_ATTRIBUTE_RANGE = 7;
|
||||
|
||||
/**
|
||||
* The current current caret offset.
|
||||
|
|
|
@ -300,7 +300,7 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
|||
// In case of bound element check its anonymous subtree.
|
||||
if (!mContent->IsInAnonymousSubtree()) {
|
||||
dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID);
|
||||
if (refElm || !mContent->OwnerDoc()->BindingManager()->GetBinding(mContent))
|
||||
if (refElm || !mContent->GetXBLBinding())
|
||||
return refElm;
|
||||
}
|
||||
|
||||
|
@ -318,7 +318,7 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
|||
}
|
||||
|
||||
// Check inside the binding of the element.
|
||||
if (mContent->OwnerDoc()->BindingManager()->GetBinding(mContent)) {
|
||||
if (mContent->GetXBLBinding()) {
|
||||
return mContent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID);
|
||||
}
|
||||
|
|
|
@ -878,186 +878,6 @@ HyperTextAccessible::FindLineBoundary(int32_t aOffset,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the specified text relative to aBoundaryType, which means:
|
||||
BOUNDARY_CHAR The character before/at/after the offset is returned.
|
||||
BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
|
||||
BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
|
||||
BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
|
||||
BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
HyperTextAccessible::GetTextHelper(EGetTextType aType,
|
||||
AccessibleTextBoundary aBoundaryType,
|
||||
int32_t aOffset,
|
||||
int32_t* aStartOffset, int32_t* aEndOffset,
|
||||
nsAString& aText)
|
||||
{
|
||||
aText.Truncate();
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aStartOffset);
|
||||
NS_ENSURE_ARG_POINTER(aEndOffset);
|
||||
*aStartOffset = *aEndOffset = 0;
|
||||
|
||||
int32_t offset = ConvertMagicOffset(aOffset);
|
||||
if (offset < 0)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && offset > 0 &&
|
||||
(aBoundaryType == BOUNDARY_LINE_START ||
|
||||
aBoundaryType == BOUNDARY_LINE_END)) {
|
||||
// It is the same character offset when the caret is visually at
|
||||
// the very end of a line or the start of a new line. Getting text at
|
||||
// the line should provide the line with the visual caret,
|
||||
// otherwise screen readers will announce the wrong line as the user
|
||||
// presses up or down arrow and land at the end of a line.
|
||||
nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
|
||||
if (frameSelection &&
|
||||
frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
|
||||
-- offset; // We are at the start of a line
|
||||
}
|
||||
}
|
||||
|
||||
nsSelectionAmount amount;
|
||||
bool needsStart = false;
|
||||
switch (aBoundaryType) {
|
||||
case BOUNDARY_WORD_START:
|
||||
needsStart = true;
|
||||
amount = eSelectWord;
|
||||
break;
|
||||
|
||||
case BOUNDARY_WORD_END:
|
||||
amount = eSelectWord;
|
||||
break;
|
||||
|
||||
case BOUNDARY_LINE_START:
|
||||
// Newlines are considered at the end of a line. Since getting
|
||||
// the BOUNDARY_LINE_START gets the text from the line-start to the next
|
||||
// line-start, the newline is included at the end of the string.
|
||||
needsStart = true;
|
||||
amount = eSelectLine;
|
||||
break;
|
||||
|
||||
case BOUNDARY_LINE_END:
|
||||
// Newlines are considered at the end of a line. Since getting
|
||||
// the BOUNDARY_END_START gets the text from the line-end to the next
|
||||
//line-end, the newline is included at the beginning of the string.
|
||||
amount = eSelectLine;
|
||||
break;
|
||||
|
||||
case BOUNDARY_ATTRIBUTE_RANGE:
|
||||
{
|
||||
nsresult rv = GetTextAttributes(false, offset,
|
||||
aStartOffset, aEndOffset, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
}
|
||||
|
||||
default: // Note, sentence support is deprecated and falls through to here
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int32_t startOffset = offset + (aBoundaryType == BOUNDARY_LINE_END); // Avoid getting the previous line
|
||||
int32_t endOffset = startOffset;
|
||||
|
||||
// Convert offsets to frame-relative
|
||||
nsRefPtr<Accessible> startAcc;
|
||||
nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
|
||||
nullptr, getter_AddRefs(startAcc));
|
||||
|
||||
if (!startFrame) {
|
||||
int32_t textLength = CharacterCount();
|
||||
if (aBoundaryType == BOUNDARY_LINE_START && offset > 0 && offset == textLength) {
|
||||
// Asking for start of line, while on last character
|
||||
if (startAcc)
|
||||
startFrame = startAcc->GetFrame();
|
||||
}
|
||||
if (!startFrame) {
|
||||
return offset > textLength ? NS_ERROR_FAILURE : NS_OK;
|
||||
}
|
||||
else {
|
||||
// We're on the last continuation since we're on the last character
|
||||
startFrame = startFrame->GetLastContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t finalStartOffset = 0, finalEndOffset = 0;
|
||||
EWordMovementType wordMovementType = needsStart ? eStartWord : eEndWord;
|
||||
|
||||
nsIPresShell* presShell = mDoc->PresShell();
|
||||
// If aType == eGetAt we'll change both the start and end offset from
|
||||
// the original offset
|
||||
if (aType == eGetAfter) {
|
||||
finalStartOffset = offset;
|
||||
}
|
||||
else {
|
||||
finalStartOffset = GetRelativeOffset(presShell, startFrame, startOffset,
|
||||
startAcc,
|
||||
(amount == eSelectLine ? eSelectBeginLine : amount),
|
||||
eDirPrevious, needsStart,
|
||||
wordMovementType);
|
||||
NS_ENSURE_TRUE(finalStartOffset >= 0, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (aType == eGetBefore) {
|
||||
finalEndOffset = offset;
|
||||
}
|
||||
else {
|
||||
// Start moving forward from the start so that we don't get
|
||||
// 2 words/lines if the offset occurred on whitespace boundary
|
||||
// Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
|
||||
// For BOUNDARY_LINE_END, make sure we start of this line
|
||||
startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END);
|
||||
nsRefPtr<Accessible> endAcc;
|
||||
nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
|
||||
nullptr, getter_AddRefs(endAcc));
|
||||
if (endAcc && endAcc->Role() == roles::STATICTEXT) {
|
||||
// Static text like list bullets will ruin our forward calculation,
|
||||
// since the caret cannot be in the static text. Start just after the static text.
|
||||
startOffset = endOffset = finalStartOffset +
|
||||
(aBoundaryType == BOUNDARY_LINE_END) +
|
||||
nsAccUtils::TextLength(endAcc);
|
||||
|
||||
endFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
|
||||
nullptr, getter_AddRefs(endAcc));
|
||||
}
|
||||
if (!endFrame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc,
|
||||
(amount == eSelectLine ? eSelectEndLine : amount),
|
||||
eDirNext, needsStart, wordMovementType);
|
||||
NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE);
|
||||
if (finalEndOffset == offset) {
|
||||
if (aType == eGetAt && amount == eSelectWord) {
|
||||
// Fix word error for the first character in word: PeekOffset() will return the previous word when
|
||||
// offset points to the first character of the word, but accessibility APIs want the current word
|
||||
// that the first character is in
|
||||
return GetTextHelper(eGetAfter, aBoundaryType, offset,
|
||||
aStartOffset, aEndOffset, aText);
|
||||
}
|
||||
int32_t textLength = CharacterCount();
|
||||
if (finalEndOffset < textLength) {
|
||||
// This happens sometimes when current character at finalStartOffset
|
||||
// is an embedded object character representing another hypertext, that
|
||||
// the AT really needs to dig into separately
|
||||
++ finalEndOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*aStartOffset = finalStartOffset;
|
||||
*aEndOffset = finalEndOffset;
|
||||
|
||||
NS_ASSERTION((finalStartOffset < offset && finalEndOffset >= offset) || aType != eGetBefore, "Incorrect results for GetTextHelper");
|
||||
NS_ASSERTION((finalStartOffset <= offset && finalEndOffset > offset) || aType == eGetBefore, "Incorrect results for GetTextHelper");
|
||||
|
||||
GetPosAndText(finalStartOffset, finalEndOffset, &aText);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIAccessibleText impl.
|
||||
*/
|
||||
|
@ -1120,10 +940,6 @@ HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
|
|||
*aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd);
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
|
||||
case BOUNDARY_ATTRIBUTE_RANGE:
|
||||
return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
|
||||
aStartOffset, aEndOffset, aText);
|
||||
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -1177,10 +993,6 @@ HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
|
|||
*aEndOffset = FindLineBoundary(offset, eThisLineEnd);
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
|
||||
case BOUNDARY_ATTRIBUTE_RANGE:
|
||||
return GetTextHelper(eGetAt, aBoundaryType, aOffset,
|
||||
aStartOffset, aEndOffset, aText);
|
||||
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -1243,10 +1055,6 @@ HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
|
|||
*aEndOffset = FindLineBoundary(offset, eNextLineEnd);
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
|
||||
case BOUNDARY_ATTRIBUTE_RANGE:
|
||||
return GetTextHelper(eGetAfter, aBoundaryType, aOffset,
|
||||
aStartOffset, aEndOffset, aText);
|
||||
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
|
|
@ -334,22 +334,8 @@ protected:
|
|||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType = eDefaultBehavior);
|
||||
|
||||
/*
|
||||
* This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
|
||||
* @param aType, eGetBefore, eGetAt, eGetAfter
|
||||
* @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
|
||||
* @param aOffset, offset into the hypertext to start from
|
||||
* @param *aStartOffset, the resulting start offset for the returned substring
|
||||
* @param *aEndOffset, the resulting end offset for the returned substring
|
||||
* @param aText, the resulting substring
|
||||
* @return success/failure code
|
||||
*/
|
||||
nsresult GetTextHelper(EGetTextType aType, AccessibleTextBoundary aBoundaryType,
|
||||
int32_t aOffset, int32_t *aStartOffset, int32_t *aEndOffset,
|
||||
nsAString & aText);
|
||||
|
||||
/**
|
||||
* Used by GetTextHelper() to move backward/forward from a given point
|
||||
* Used by FindOffset() to move backward/forward from a given point
|
||||
* by word/line/etc.
|
||||
*
|
||||
* @param aPresShell the current presshell we're moving in
|
||||
|
|
|
@ -356,7 +356,56 @@ this.AccessFu = {
|
|||
|
||||
// Keep track of message managers tha already have a 'content-script.js'
|
||||
// injected.
|
||||
_processedMessageManagers: []
|
||||
_processedMessageManagers: [],
|
||||
|
||||
/**
|
||||
* Adjusts the given bounds relative to the given browser. Converts from screen
|
||||
* or device pixels to either device or CSS pixels.
|
||||
* @param {Rect} aJsonBounds the bounds to adjust
|
||||
* @param {browser} aBrowser the browser we want the bounds relative to
|
||||
* @param {bool} aToCSSPixels whether to convert to CSS pixels (as opposed to
|
||||
* device pixels)
|
||||
* @param {bool} aFromDevicePixels whether to convert from device pixels (as
|
||||
* opposed to screen pixels)
|
||||
*/
|
||||
adjustContentBounds: function(aJsonBounds, aBrowser, aToCSSPixels, aFromDevicePixels) {
|
||||
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
|
||||
aJsonBounds.right - aJsonBounds.left,
|
||||
aJsonBounds.bottom - aJsonBounds.top);
|
||||
let win = Utils.win;
|
||||
let dpr = win.devicePixelRatio;
|
||||
let vp = Utils.getViewport(win);
|
||||
let offset = { left: -win.mozInnerScreenX, top: -win.mozInnerScreenY };
|
||||
|
||||
if (!aBrowser.contentWindow) {
|
||||
// OOP browser, add offset of browser.
|
||||
// The offset of the browser element in relation to its parent window.
|
||||
let clientRect = aBrowser.getBoundingClientRect();
|
||||
let win = aBrowser.ownerDocument.defaultView;
|
||||
offset.left += clientRect.left + win.mozInnerScreenX;
|
||||
offset.top += clientRect.top + win.mozInnerScreenY;
|
||||
}
|
||||
|
||||
// Here we scale from screen pixels to layout device pixels by dividing by
|
||||
// the resolution (caused by pinch-zooming). The resolution is the viewport
|
||||
// zoom divided by the devicePixelRatio. If there's no viewport, then we're
|
||||
// on a platform without pinch-zooming and we can just ignore this.
|
||||
if (!aFromDevicePixels && vp) {
|
||||
bounds = bounds.scale(vp.zoom / dpr, vp.zoom / dpr);
|
||||
}
|
||||
|
||||
// Add the offset; the offset is in CSS pixels, so multiply the
|
||||
// devicePixelRatio back in before adding to preserve unit consistency.
|
||||
bounds = bounds.translate(offset.left * dpr, offset.top * dpr);
|
||||
|
||||
// If we want to get to CSS pixels from device pixels, this needs to be
|
||||
// further divided by the devicePixelRatio due to widget scaling.
|
||||
if (aToCSSPixels) {
|
||||
bounds = bounds.scale(1 / dpr, 1 / dpr);
|
||||
}
|
||||
|
||||
return bounds.expandToIntegers();
|
||||
}
|
||||
};
|
||||
|
||||
var Output = {
|
||||
|
@ -472,7 +521,7 @@ var Output = {
|
|||
}
|
||||
|
||||
let padding = aDetails.padding;
|
||||
let r = this._adjustBounds(aDetails.bounds, aBrowser);
|
||||
let r = AccessFu.adjustContentBounds(aDetails.bounds, aBrowser, true);
|
||||
|
||||
// First hide it to avoid flickering when changing the style.
|
||||
highlightBox.style.display = 'none';
|
||||
|
@ -536,7 +585,7 @@ var Output = {
|
|||
for each (let androidEvent in aDetails) {
|
||||
androidEvent.type = 'Accessibility:Event';
|
||||
if (androidEvent.bounds)
|
||||
androidEvent.bounds = this._adjustBounds(androidEvent.bounds, aBrowser, true);
|
||||
androidEvent.bounds = AccessFu.adjustContentBounds(androidEvent.bounds, aBrowser);
|
||||
|
||||
switch(androidEvent.eventType) {
|
||||
case ANDROID_VIEW_TEXT_CHANGED:
|
||||
|
@ -559,33 +608,6 @@ var Output = {
|
|||
|
||||
Braille: function Braille(aDetails, aBrowser) {
|
||||
Logger.debug('Braille output: ' + aDetails.text);
|
||||
},
|
||||
|
||||
_adjustBounds: function(aJsonBounds, aBrowser, aIncludeZoom) {
|
||||
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
|
||||
aJsonBounds.right - aJsonBounds.left,
|
||||
aJsonBounds.bottom - aJsonBounds.top);
|
||||
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0, offsetY: 0 };
|
||||
let root = Utils.win;
|
||||
let offset = { left: -root.mozInnerScreenX, top: -root.mozInnerScreenY };
|
||||
let scale = 1 / Utils.getPixelsPerCSSPixel(Utils.win);
|
||||
|
||||
if (!aBrowser.contentWindow) {
|
||||
// OOP browser, add offset of browser.
|
||||
// The offset of the browser element in relation to its parent window.
|
||||
let clientRect = aBrowser.getBoundingClientRect();
|
||||
let win = aBrowser.ownerDocument.defaultView;
|
||||
offset.left += clientRect.left + win.mozInnerScreenX;
|
||||
offset.top += clientRect.top + win.mozInnerScreenY;
|
||||
}
|
||||
|
||||
let newBounds = bounds.scale(scale, scale).translate(offset.left, offset.top);
|
||||
|
||||
if (aIncludeZoom) {
|
||||
newBounds = newBounds.scale(vp.zoom, vp.zoom);
|
||||
}
|
||||
|
||||
return newBounds.expandToIntegers();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -615,9 +637,6 @@ var Input = {
|
|||
this._handleKeypress(aEvent);
|
||||
break;
|
||||
case 'mozAccessFuGesture':
|
||||
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0 };
|
||||
aEvent.detail.x *= vp.zoom;
|
||||
aEvent.detail.y *= vp.zoom;
|
||||
this._handleGesture(aEvent.detail);
|
||||
break;
|
||||
}
|
||||
|
@ -796,10 +815,10 @@ var Input = {
|
|||
|
||||
activateContextMenu: function activateContextMenu(aMessage) {
|
||||
if (Utils.MozBuildApp === 'mobile/android') {
|
||||
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0 };
|
||||
let p = AccessFu.adjustContentBounds(aMessage.bounds, Utils.CurrentBrowser,
|
||||
true, true).center();
|
||||
Services.obs.notifyObservers(null, 'Gesture:LongPress',
|
||||
JSON.stringify({x: aMessage.x / vp.zoom,
|
||||
y: aMessage.y / vp.zoom}));
|
||||
JSON.stringify({x: p.x, y: p.y}));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -326,8 +326,8 @@ this.TouchAdapter = {
|
|||
* of one single touch.
|
||||
*/
|
||||
function TouchPoint(aTouch, aTime, aDPI) {
|
||||
this.startX = this.x = aTouch.screenX;
|
||||
this.startY = this.y = aTouch.screenY;
|
||||
this.startX = this.x = aTouch.screenX * this.scaleFactor;
|
||||
this.startY = this.y = aTouch.screenY * this.scaleFactor;
|
||||
this.startTime = aTime;
|
||||
this.distanceTraveled = 0;
|
||||
this.dpi = aDPI;
|
||||
|
@ -338,8 +338,8 @@ TouchPoint.prototype = {
|
|||
update: function TouchPoint_update(aTouch, aTime) {
|
||||
let lastX = this.x;
|
||||
let lastY = this.y;
|
||||
this.x = aTouch.screenX;
|
||||
this.y = aTouch.screenY;
|
||||
this.x = aTouch.screenX * this.scaleFactor;
|
||||
this.y = aTouch.screenY * this.scaleFactor;
|
||||
this.time = aTime;
|
||||
|
||||
this.distanceTraveled += this.getDistanceToCoord(lastX, lastY);
|
||||
|
@ -349,6 +349,20 @@ TouchPoint.prototype = {
|
|||
return Math.sqrt(Math.pow(this.x - aX, 2) + Math.pow(this.y - aY, 2));
|
||||
},
|
||||
|
||||
get scaleFactor() {
|
||||
if (!this._scaleFactor) {
|
||||
// Android events come with the x, y coordinates affected by the widget
|
||||
// scaling; we restore it to normal here.
|
||||
if (Utils.MozBuildApp == 'mobile/android') {
|
||||
this._scaleFactor = Utils.win.devicePixelRatio;
|
||||
} else {
|
||||
this._scaleFactor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return this._scaleFactor;
|
||||
},
|
||||
|
||||
finish: function TouchPoint_finish() {
|
||||
this.done = true;
|
||||
},
|
||||
|
|
|
@ -221,11 +221,6 @@ this.Utils = {
|
|||
return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
|
||||
},
|
||||
|
||||
getPixelsPerCSSPixel: function getPixelsPerCSSPixel(aWindow) {
|
||||
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
|
||||
},
|
||||
|
||||
getBounds: function getBounds(aAccessible) {
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
aAccessible.getBounds(objX, objY, objW, objH);
|
||||
|
|
|
@ -97,10 +97,8 @@ function moveToPoint(aMessage) {
|
|||
let rule = TraversalRules[details.rule];
|
||||
|
||||
try {
|
||||
if (!this._ppcp) {
|
||||
this._ppcp = Utils.getPixelsPerCSSPixel(content);
|
||||
}
|
||||
vc.moveToPoint(rule, details.x * this._ppcp, details.y * this._ppcp, true);
|
||||
let dpr = content.devicePixelRatio;
|
||||
vc.moveToPoint(rule, details.x * dpr, details.y * dpr, true);
|
||||
forwardToChild(aMessage, moveToPoint, vc.position);
|
||||
} catch (x) {
|
||||
Logger.logException(x, 'Failed move to point');
|
||||
|
@ -219,9 +217,7 @@ function activateCurrent(aMessage) {
|
|||
function activateContextMenu(aMessage) {
|
||||
function sendContextMenuCoordinates(aAccessible) {
|
||||
let bounds = Utils.getBounds(aAccessible);
|
||||
sendAsyncMessage('AccessFu:ActivateContextMenu',
|
||||
{ x: bounds.left + bounds.width / 2,
|
||||
y: bounds.top + bounds.height / 2 });
|
||||
sendAsyncMessage('AccessFu:ActivateContextMenu', {bounds: bounds});
|
||||
}
|
||||
|
||||
let position = Utils.getVirtualCursor(content.document).position;
|
||||
|
|
|
@ -6,7 +6,6 @@ const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START;
|
|||
const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END;
|
||||
const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START;
|
||||
const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END;
|
||||
const BOUNDARY_ATTRIBUTE_RANGE = nsIAccessibleText.BOUNDARY_ATTRIBUTE_RANGE;
|
||||
|
||||
const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT;
|
||||
const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET;
|
||||
|
@ -585,7 +584,5 @@ function boundaryToString(aBoundaryType)
|
|||
return "line start";
|
||||
case BOUNDARY_LINE_END:
|
||||
return "line end";
|
||||
case BOUNDARY_ATTRIBUTE_RANGE:
|
||||
return "attr range";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
. "$topsrcdir/build/unix/mozconfig.linux32"
|
||||
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
ac_add_options --enable-update-packaging
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
. "$topsrcdir/build/unix/mozconfig.linux"
|
||||
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
ac_add_options --enable-update-packaging
|
||||
|
|
|
@ -175,7 +175,6 @@
|
|||
#ifdef MOZ_B2G_FM
|
||||
@BINPATH@/components/dom_fm.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_battery.xpt
|
||||
#ifdef MOZ_B2G_BT
|
||||
@BINPATH@/components/dom_bluetooth.xpt
|
||||
#endif
|
||||
|
|
|
@ -571,7 +571,7 @@ HistoryMenu.prototype = {
|
|||
m.setAttribute("label", strings.getString("menuRestoreAllTabs.label"));
|
||||
m.addEventListener("command", function() {
|
||||
for (var i = 0; i < undoItems.length; i++)
|
||||
undoCloseTab();
|
||||
undoCloseTab(0);
|
||||
}, false);
|
||||
},
|
||||
|
||||
|
|
|
@ -4432,6 +4432,10 @@ nsBrowserAccess.prototype = {
|
|||
|
||||
isTabContentWindow: function (aWindow) {
|
||||
return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
get contentWindow() {
|
||||
return gBrowser.contentWindow;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6259,15 +6263,31 @@ function undoCloseTab(aIndex) {
|
|||
var tab = null;
|
||||
var ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
if (ss.getClosedTabCount(window) > (aIndex || 0)) {
|
||||
TabView.prepareUndoCloseTab(blankTabToRemove);
|
||||
tab = ss.undoCloseTab(window, aIndex || 0);
|
||||
TabView.afterUndoCloseTab();
|
||||
|
||||
if (blankTabToRemove)
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
let numberOfTabsToUndoClose = 0;
|
||||
if (Number.isInteger(aIndex)) {
|
||||
if (ss.getClosedTabCount(window) > aIndex) {
|
||||
numberOfTabsToUndoClose = 1;
|
||||
} else {
|
||||
return tab;
|
||||
}
|
||||
} else {
|
||||
numberOfTabsToUndoClose = ss.getNumberOfTabsClosedLast(window);
|
||||
aIndex = 0;
|
||||
}
|
||||
|
||||
while (numberOfTabsToUndoClose > 0 &&
|
||||
numberOfTabsToUndoClose--) {
|
||||
TabView.prepareUndoCloseTab(blankTabToRemove);
|
||||
tab = ss.undoCloseTab(window, aIndex);
|
||||
TabView.afterUndoCloseTab();
|
||||
if (blankTabToRemove) {
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
blankTabToRemove = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the number of tabs closed last time to the default.
|
||||
ss.setNumberOfTabsClosedLast(window, 1);
|
||||
return tab;
|
||||
}
|
||||
|
||||
|
@ -7061,10 +7081,15 @@ var TabContextMenu = {
|
|||
menuItem.disabled = disabled;
|
||||
|
||||
// Session store
|
||||
document.getElementById("context_undoCloseTab").disabled =
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore).
|
||||
getClosedTabCount(window) == 0;
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
let closedTabCount = ss.getNumberOfTabsClosedLast(window);
|
||||
undoCloseTabElement.disabled = closedTabCount == 0;
|
||||
// Change the label of "Undo Close Tab" to specify if it will undo a batch-close
|
||||
// or a single close.
|
||||
let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel";
|
||||
undoCloseTabElement.setAttribute("label", undoCloseTabElement.getAttribute(visibleLabel));
|
||||
|
||||
// Only one of pin/unpin should be visible
|
||||
document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
|
||||
|
|
|
@ -103,7 +103,8 @@
|
|||
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_undoCloseTab"
|
||||
label="&undoCloseTab.label;"
|
||||
singletablabel="&undoCloseTab.label;"
|
||||
multipletablabel="&undoCloseTabs.label;"
|
||||
accesskey="&undoCloseTab.accesskey;"
|
||||
observes="History:UndoCloseTab"/>
|
||||
<menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
|
||||
|
|
|
@ -1565,13 +1565,9 @@
|
|||
throw new Error("Invalid argument: " + aCloseTabs);
|
||||
}
|
||||
|
||||
if (tabsToClose <= 1)
|
||||
return true;
|
||||
|
||||
const pref = aCloseTabs == this.closingTabsEnum.ALL ?
|
||||
"browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
|
||||
var shouldPrompt = Services.prefs.getBoolPref(pref);
|
||||
if (!shouldPrompt)
|
||||
if (tabsToClose <= 1 ||
|
||||
aCloseTabs != this.closingTabsEnum.ALL ||
|
||||
!Services.prefs.getBoolPref("browser.tabs.warnOnClose"))
|
||||
return true;
|
||||
|
||||
var ps = Services.prompt;
|
||||
|
@ -1595,13 +1591,12 @@
|
|||
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
|
||||
bundle.getString("tabs.closeButtonMultiple"),
|
||||
null, null,
|
||||
aCloseTabs == this.closingTabsEnum.ALL ?
|
||||
bundle.getString("tabs.closeWarningPromptMe") : null,
|
||||
bundle.getString("tabs.closeWarningPromptMe"),
|
||||
warnOnClose);
|
||||
var reallyClose = (buttonPressed == 0);
|
||||
|
||||
// don't set the pref unless they press OK and it's false
|
||||
if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
|
||||
if (reallyClose && !warnOnClose.value)
|
||||
Services.prefs.setBoolPref(pref, false);
|
||||
|
||||
return reallyClose;
|
||||
|
@ -1629,9 +1624,13 @@
|
|||
<![CDATA[
|
||||
if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
|
||||
let tabs = this.getTabsToTheEndFrom(aTab);
|
||||
for (let i = tabs.length - 1; i >= 0; --i) {
|
||||
let numberOfTabsToClose = tabs.length;
|
||||
for (let i = numberOfTabsToClose - 1; i >= 0; --i) {
|
||||
this.removeTab(tabs[i], {animate: true});
|
||||
}
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, numberOfTabsToClose);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -1648,11 +1647,17 @@
|
|||
let tabs = this.visibleTabs;
|
||||
this.selectedTab = aTab;
|
||||
|
||||
let closedTabs = 0;
|
||||
for (let i = tabs.length - 1; i >= 0; --i) {
|
||||
if (tabs[i] != aTab && !tabs[i].pinned)
|
||||
if (tabs[i] != aTab && !tabs[i].pinned) {
|
||||
this.removeTab(tabs[i]);
|
||||
closedTabs++;
|
||||
}
|
||||
}
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, closedTabs);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -1680,6 +1685,10 @@
|
|||
var byMouse = aParams.byMouse;
|
||||
}
|
||||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
ss.setNumberOfTabsClosedLast(window, 1);
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
// asynchronously closing tab.
|
||||
if (!animate &&
|
||||
|
|
|
@ -184,6 +184,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_bug832435.js \
|
||||
browser_bug839103.js \
|
||||
browser_bug882977.js \
|
||||
browser_bug887515.js \
|
||||
browser_canonizeURL.js \
|
||||
browser_clearplugindata_noage.html \
|
||||
browser_clearplugindata.html \
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
function numClosedTabs()
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore).
|
||||
getNumberOfTabsClosedLast(window);
|
||||
|
||||
var originalTab;
|
||||
var tab1Loaded = false;
|
||||
var tab2Loaded = false;
|
||||
|
||||
function verifyUndoMultipleClose() {
|
||||
if (!tab1Loaded || !tab2Loaded)
|
||||
return;
|
||||
|
||||
gBrowser.removeAllTabsBut(originalTab);
|
||||
updateTabContextMenu();
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled.");
|
||||
is(numClosedTabs(), 2, "There should be 2 closed tabs.");
|
||||
is(gBrowser.tabs.length, 1, "There should only be 1 open tab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"),
|
||||
"The label should be showing that the command will restore multiple tabs");
|
||||
undoCloseTab();
|
||||
|
||||
is(gBrowser.tabs.length, 3, "There should be 3 open tabs");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"),
|
||||
"The label should be showing that the command will restore a single tab");
|
||||
|
||||
gBrowser.removeTabsToTheEndFrom(originalTab);
|
||||
updateTabContextMenu();
|
||||
ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled.");
|
||||
is(numClosedTabs(), 2, "There should be 2 closed tabs.");
|
||||
is(gBrowser.tabs.length, 1, "There should only be 1 open tab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"),
|
||||
"The label should be showing that the command will restore multiple tabs");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
originalTab.linkedBrowser.loadURI("about:blank");
|
||||
originalTab = null;
|
||||
});
|
||||
|
||||
let undoCloseTabElement = document.getElementById("context_undoCloseTab");
|
||||
updateTabContextMenu();
|
||||
is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"),
|
||||
"The label should be showing that the command will restore a single tab");
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/");
|
||||
var tab1 = gBrowser.addTab("http://mochi.test:8888/");
|
||||
var tab2 = gBrowser.addTab("http://mochi.test:8888/");
|
||||
var browser1 = gBrowser.getBrowserForTab(tab1);
|
||||
browser1.addEventListener("load", function onLoad1() {
|
||||
browser1.removeEventListener("load", onLoad1, true);
|
||||
tab1Loaded = true;
|
||||
tab1 = null;
|
||||
|
||||
verifyUndoMultipleClose();
|
||||
}, true);
|
||||
var browser2 = gBrowser.getBrowserForTab(tab2);
|
||||
browser2.addEventListener("load", function onLoad2() {
|
||||
browser2.removeEventListener("load", onLoad2, true);
|
||||
tab2Loaded = true;
|
||||
tab2 = null;
|
||||
|
||||
verifyUndoMultipleClose();
|
||||
}, true);
|
||||
}
|
|
@ -25,7 +25,7 @@ interface nsIDOMNode;
|
|||
* |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(59bfaf00-e3d8-4728-b4f0-cc0b9dfb4806)]
|
||||
[scriptable, uuid(0aa5492c-15ad-4376-8eac-28895796826e)]
|
||||
interface nsISessionStore : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -105,6 +105,20 @@ interface nsISessionStore : nsISupports
|
|||
nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab,
|
||||
[optional] in long aDelta);
|
||||
|
||||
/**
|
||||
* Set the number of tabs that was closed during the last close-tabs
|
||||
* operation. This helps us keep track of batch-close operations so
|
||||
* we can restore multiple tabs at once.
|
||||
*/
|
||||
void setNumberOfTabsClosedLast(in nsIDOMWindow aWindow, in unsigned long aNumber);
|
||||
|
||||
/**
|
||||
* Get the number of tabs that was closed during the last close-tabs
|
||||
* operation. This helps us keep track of batch-close operations so
|
||||
* we can restore multiple tabs at once.
|
||||
*/
|
||||
unsigned long getNumberOfTabsClosedLast(in nsIDOMWindow aWindow);
|
||||
|
||||
/**
|
||||
* Get the number of restore-able tabs for a browser window
|
||||
*/
|
||||
|
|
|
@ -170,6 +170,14 @@ this.SessionStore = {
|
|||
return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta);
|
||||
},
|
||||
|
||||
getNumberOfTabsClosedLast: function ss_getNumberOfTabsClosedLast(aWindow) {
|
||||
return SessionStoreInternal.getNumberOfTabsClosedLast(aWindow);
|
||||
},
|
||||
|
||||
setNumberOfTabsClosedLast: function ss_setNumberOfTabsClosedLast(aWindow, aNumber) {
|
||||
return SessionStoreInternal.setNumberOfTabsClosedLast(aWindow, aNumber);
|
||||
},
|
||||
|
||||
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
|
||||
return SessionStoreInternal.getClosedTabCount(aWindow);
|
||||
},
|
||||
|
@ -1529,6 +1537,28 @@ let SessionStoreInternal = {
|
|||
return newTab;
|
||||
},
|
||||
|
||||
setNumberOfTabsClosedLast: function ssi_setNumberOfTabsClosedLast(aWindow, aNumber) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return NumberOfTabsClosedLastPerWindow.set(aWindow, aNumber);
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
/* Used to undo batch tab-close operations. Defaults to 1. */
|
||||
getNumberOfTabsClosedLast: function ssi_getNumberOfTabsClosedLast(aWindow) {
|
||||
if ("__SSi" in aWindow) {
|
||||
// Blank tabs cannot be undo-closed, so the number returned by
|
||||
// the NumberOfTabsClosedLastPerWindow can be greater than the
|
||||
// return value of getClosedTabCount. We won't restore blank
|
||||
// tabs, so we return the minimum of these two values.
|
||||
return Math.min(NumberOfTabsClosedLastPerWindow.get(aWindow) || 1,
|
||||
this.getClosedTabCount(aWindow));
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
|
@ -4694,6 +4724,11 @@ let DyingWindowCache = {
|
|||
}
|
||||
};
|
||||
|
||||
// A map storing the number of tabs last closed per windoow. This only
|
||||
// stores the most recent tab-close operation, and is used to undo
|
||||
// batch tab-closing operations.
|
||||
let NumberOfTabsClosedLastPerWindow = new WeakMap();
|
||||
|
||||
// A set of tab attributes to persist. We will read a given list of tab
|
||||
// attributes when collecting tab data and will re-set those attributes when
|
||||
// the given tab data is restored to a new tab.
|
||||
|
|
|
@ -40,4 +40,8 @@ function test() {
|
|||
"Invalid window for getWindowValue throws");
|
||||
ok(test(function() ss.setWindowValue({}, "", "")),
|
||||
"Invalid window for setWindowValue throws");
|
||||
ok(test(function() ss.getNumberOfTabsClosedLast({})),
|
||||
"Invalid window for getNumberOfTabsClosedLast throws");
|
||||
ok(test(function() ss.setNumberOfTabsClosedLast({}, 1)),
|
||||
"Invalid window for setNumberOfTabsClosedLast throws");
|
||||
}
|
||||
|
|
|
@ -38,5 +38,5 @@ function onTabViewWindowLoaded() {
|
|||
gBrowser.removeTab(tabTwo);
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ function test() {
|
|||
|
||||
createBlankTab();
|
||||
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab);
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ function test() {
|
|||
gBrowser.removeTab(gBrowser.tabs[0]);
|
||||
|
||||
afterAllTabsLoaded(finishTest);
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ function test() {
|
|||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView(finishTest);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
|
|
@ -20,7 +20,7 @@ function test() {
|
|||
whenTabViewIsHidden(function() {
|
||||
win.gBrowser.removeTab(win.gBrowser.selectedTab);
|
||||
executeSoon(function() {
|
||||
win.undoCloseTab();
|
||||
win.undoCloseTab(0);
|
||||
|
||||
groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) {
|
||||
groupItemTwo.removeSubscriber("childAdded", onChildAdded);
|
||||
|
|
|
@ -359,7 +359,7 @@ function newWindowWithState(state, callback) {
|
|||
function restoreTab(callback, index, win) {
|
||||
win = win || window;
|
||||
|
||||
let tab = win.undoCloseTab(index || 0);
|
||||
let tab = win.undoCloseTab(index);
|
||||
let tabItem = tab._tabViewTabItem;
|
||||
|
||||
let finalize = function () {
|
||||
|
|
|
@ -2167,3 +2167,49 @@ gcli.addCommand({
|
|||
}
|
||||
});
|
||||
}(this));
|
||||
|
||||
/* CmdMedia ------------------------------------------------------- */
|
||||
|
||||
(function(module) {
|
||||
/**
|
||||
* 'media' command
|
||||
*/
|
||||
|
||||
gcli.addCommand({
|
||||
name: "media",
|
||||
description: gcli.lookup("mediaDesc")
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: "media emulate",
|
||||
description: gcli.lookup("mediaEmulateDesc"),
|
||||
manual: gcli.lookup("mediaEmulateManual"),
|
||||
params: [
|
||||
{
|
||||
name: "type",
|
||||
description: gcli.lookup("mediaEmulateType"),
|
||||
type: {
|
||||
name: "selection",
|
||||
data: ["braille", "embossed", "handheld", "print", "projection",
|
||||
"screen", "speech", "tty", "tv"]
|
||||
}
|
||||
}
|
||||
],
|
||||
exec: function(args, context) {
|
||||
let markupDocumentViewer = context.environment.chromeWindow
|
||||
.gBrowser.markupDocumentViewer;
|
||||
markupDocumentViewer.emulateMedium(args.type);
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: "media reset",
|
||||
description: gcli.lookup("mediaResetDesc"),
|
||||
manual: gcli.lookup("mediaEmulateManual"),
|
||||
exec: function(args, context) {
|
||||
let markupDocumentViewer = context.environment.chromeWindow
|
||||
.gBrowser.markupDocumentViewer;
|
||||
markupDocumentViewer.stopEmulatingMedium();
|
||||
}
|
||||
});
|
||||
}(this));
|
||||
|
|
|
@ -35,6 +35,8 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_cmd_cookie.js \
|
||||
browser_cmd_jsb.js \
|
||||
browser_cmd_jsb_script.jsi \
|
||||
browser_cmd_media.html \
|
||||
browser_cmd_media.js \
|
||||
browser_cmd_pagemod_export.html \
|
||||
browser_cmd_pagemod_export.js \
|
||||
browser_cmd_pref.js \
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GCLI Test for Bug 819930</title>
|
||||
<style>
|
||||
@media braille {
|
||||
body {
|
||||
background-color: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
@media embossed {
|
||||
body {
|
||||
background-color: indigo;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen {
|
||||
body {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,80 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that screenshot command works properly
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
|
||||
"test/browser_cmd_media.html";
|
||||
let tests = {
|
||||
testInput: function(options) {
|
||||
return helpers.audit(options, [
|
||||
{
|
||||
setup: "media emulate braille",
|
||||
check: {
|
||||
input: "media emulate braille",
|
||||
markup: "VVVVVVVVVVVVVVVVVVVVV",
|
||||
status: "VALID",
|
||||
args: {
|
||||
type: { value: "braille"},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
setup: "media reset",
|
||||
check: {
|
||||
input: "media reset",
|
||||
markup: "VVVVVVVVVVV",
|
||||
status: "VALID",
|
||||
args: {
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
testEmulateMedia: function(options) {
|
||||
return helpers.audit(options, [
|
||||
{
|
||||
setup: "media emulate braille",
|
||||
check: {
|
||||
args: {
|
||||
type: { value: "braille"}
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
output: ""
|
||||
},
|
||||
post: function() {
|
||||
let body = options.window.document.body;
|
||||
let style = options.window.getComputedStyle(body);
|
||||
is(style.backgroundColor, "rgb(255, 255, 0)", "media correctly emulated");
|
||||
}
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
testEndMediaEmulation: function(options) {
|
||||
return helpers.audit(options, [
|
||||
{
|
||||
setup: function() {
|
||||
let mDV = options.browser.markupDocumentViewer;
|
||||
mDV.emulateMedium("embossed");
|
||||
return helpers.setInput(options, "media reset");
|
||||
},
|
||||
exec: {
|
||||
output: ""
|
||||
},
|
||||
post: function() {
|
||||
let body = options.window.document.body;
|
||||
let style = options.window.getComputedStyle(body);
|
||||
is(style.backgroundColor, "rgb(255, 255, 255)", "media reset");
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
function test() {
|
||||
helpers.addTabWithToolbar(TEST_URI, function(options) {
|
||||
return helpers.runTests(options, tests);
|
||||
}).then(finish);
|
||||
}
|
|
@ -183,7 +183,6 @@
|
|||
@BINPATH@/components/dom_icc.xpt
|
||||
@BINPATH@/components/dom_wappush.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_battery.xpt
|
||||
#ifdef MOZ_B2G_BT
|
||||
@BINPATH@/components/dom_bluetooth.xpt
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,11 @@ can reach it easily. -->
|
|||
<!ENTITY bookmarkAllTabs.accesskey "T">
|
||||
<!ENTITY undoCloseTab.label "Undo Close Tab">
|
||||
<!ENTITY undoCloseTab.accesskey "U">
|
||||
<!-- LOCALIZATION NOTE (undoCloseTabs.label) : This label is used
|
||||
when the previous tab-closing operation closed more than one tab. It
|
||||
replaces the undoCloseTab.label and will use the same accesskey as the
|
||||
undoCloseTab.label so users will not need to learn new keyboard controls. -->
|
||||
<!ENTITY undoCloseTabs.label "Undo Close Tabs">
|
||||
<!ENTITY closeTab.label "Close Tab">
|
||||
<!ENTITY closeTab.accesskey "c">
|
||||
|
||||
|
|
|
@ -1282,3 +1282,13 @@ listenInitOutput=Listening on port %1$S
|
|||
# LOCALIZATION NOTE (listenNoInitOutput) Text of a message output during the
|
||||
# execution of the 'listen' command.
|
||||
listenNoInitOutput=DebuggerServer not initialized
|
||||
|
||||
# LOCALIZATION NOTE (mediaDesc, mediaEmulateDesc, mediaEmulateManual,
|
||||
# mediaEmulateType, mediaResetDesc, mediaResetManual) These strings describe
|
||||
# the 'media' commands and all available parameters.
|
||||
mediaDesc=CSS media type emulation
|
||||
mediaEmulateDesc=Emulate a specified CSS media type
|
||||
mediaEmulateManual=View the document as if rendered on a device supporting the given media type, with the relevant CSS rules applied.
|
||||
mediaEmulateType=The media type to emulate
|
||||
mediaResetDesc=Stop emulating a CSS media type
|
||||
mediaResetManual=Stop emulating a CSS media type
|
||||
|
|
|
@ -275,6 +275,18 @@ let Util = {
|
|||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* DownloadUtils.convertByteUnits returns [size, localized-unit-string]
|
||||
* so they are joined for a single download size string.
|
||||
*/
|
||||
getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
let [size, units] = DownloadUtils.convertByteUnits(aSize);
|
||||
if (size > 0)
|
||||
return size + units;
|
||||
else
|
||||
return Strings.browser.GetStringFromName("downloadsUnknownSize");
|
||||
},
|
||||
|
||||
/*
|
||||
* URIs and schemes
|
||||
*/
|
||||
|
|
|
@ -71,7 +71,8 @@ var Appbar = {
|
|||
},
|
||||
|
||||
onDownloadButton: function() {
|
||||
PanelUI.show("downloads-container");
|
||||
// TODO: Bug 883962: Toggle the downloads infobar when the
|
||||
// download button is clicked
|
||||
ContextUI.dismiss();
|
||||
},
|
||||
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<bindings
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="download-base" extends="chrome://browser/content/bindings/grid.xml#richgrid-item">
|
||||
<content>
|
||||
<xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox class="richgrid-icon-container">
|
||||
<xul:box class="richgrid-icon-box"><xul:image xbl:inherits="src=iconURI"/></xul:box>
|
||||
<xul:box flex="1" />
|
||||
</xul:hbox>
|
||||
<xul:description xbl:inherits="value=label" crop="end"/>
|
||||
</xul:vbox>
|
||||
<children/>
|
||||
</content>
|
||||
<implementation>
|
||||
<field name="nsIDLMgr">Components.interfaces.nsIDownloadManager</field>
|
||||
|
||||
<property name="paused">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return parseInt(this.getAttribute("state")) == this.nsIDLMgr.DOWNLOAD_PAUSED;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
<property name="openable">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return parseInt(this.getAttribute("state")) == this.nsIDLMgr.DOWNLOAD_FINISHED;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
<property name="inProgress">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
var state = parseInt(this.getAttribute("state"));
|
||||
return state == this.nsIDLMgr.DOWNLOAD_NOTSTARTED ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_QUEUED ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_DOWNLOADING ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_PAUSED ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_SCANNING;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
<property name="removable">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
var state = parseInt(this.getAttribute("state"));
|
||||
return state == this.nsIDLMgr.DOWNLOAD_FINISHED ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_CANCELED ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_BLOCKED_PARENTAL ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_BLOCKED_POLICY ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_DIRTY ||
|
||||
state == this.nsIDLMgr.DOWNLOAD_FAILED;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="download-not-started" extends="#download-base">
|
||||
<content orient="horizontal" align="start">
|
||||
<xul:image validate="always" xbl:inherits="src=iconURL"/>
|
||||
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox align="center">
|
||||
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
|
||||
<xul:label class="normal" xbl:inherits="value=datetime"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox>
|
||||
<xul:label class="normal" xbl:inherits="value=status"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox class="show-on-select" align="center">
|
||||
<xul:button anonid="showpage-button" label="&downloadShowPage.label;"
|
||||
oncommand="Downloads.showPage(document.getBindingParent(this));"/>
|
||||
<xul:spacer flex="1"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
let referrer = this.hasAttribute("referrer");
|
||||
if (!referrer)
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "showpage-button").setAttribute("disabled", "true");
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
|
||||
<binding id="download-downloading" extends="#download-base">
|
||||
<content orient="horizontal" align="start">
|
||||
<xul:image validate="always" xbl:inherits="src=iconURL"/>
|
||||
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox align="center">
|
||||
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
|
||||
<xul:label class="normal" xbl:inherits="value=datetime"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox align="center">
|
||||
<xul:progressmeter anonid="progressmeter" mode="normal" value="0" flex="1" xbl:inherits="value=progress,mode=progressmode"/>
|
||||
<xul:button class="download-pause" label="&downloadPause.label;"
|
||||
oncommand="Downloads.pauseDownload(document.getBindingParent(this));"/>
|
||||
<xul:button class="download-cancel" label="&downloadCancel.label;"
|
||||
oncommand="Downloads.cancelDownload(document.getBindingParent(this));"/>
|
||||
</xul:hbox>
|
||||
<xul:label class="normal" xbl:inherits="value=status" crop="end"/>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="download-paused" extends="#download-base">
|
||||
<content orient="horizontal" align="start">
|
||||
<xul:image validate="always" xbl:inherits="src=iconURL"/>
|
||||
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox align="center">
|
||||
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
|
||||
<xul:label class="normal" xbl:inherits="value=datetime"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox align="center">
|
||||
<xul:progressmeter anonid="progressmeter" mode="normal" value="0" flex="1" xbl:inherits="value=progress,mode=progressmode"/>
|
||||
<xul:button class="download-resume" label="&downloadResume.label;"
|
||||
oncommand="Downloads.resumeDownload(document.getBindingParent(this));"/>
|
||||
<xul:button class="download-cancel" label="&downloadCancel.label;"
|
||||
oncommand="Downloads.cancelDownload(document.getBindingParent(this));"/>
|
||||
</xul:hbox>
|
||||
<xul:label class="normal" xbl:inherits="value=status" crop="end"/>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="download-retry" extends="#download-base">
|
||||
<content orient="horizontal" align="start">
|
||||
<xul:image validate="always" xbl:inherits="src=iconURL"/>
|
||||
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox align="center">
|
||||
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
|
||||
<xul:label class="normal" xbl:inherits="value=datetime"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox>
|
||||
<xul:label class="normal" xbl:inherits="value=status" crop="end" flex="1"/>
|
||||
<xul:label class="hide-on-select download-retry-failed normal" value="&downloadFailed.label;" xbl:inherits="state" />
|
||||
<xul:button class="show-on-select download-retry" label="&downloadRetry.label;"
|
||||
oncommand="Downloads.retryDownload(document.getBindingParent(this));"/>
|
||||
<xul:button class="show-on-select download-remove" label="&downloadRemove.label;"
|
||||
oncommand="Downloads.removeDownload(document.getBindingParent(this));"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="download-done" extends="#download-base">
|
||||
<content orient="horizontal" align="start">
|
||||
<xul:image validate="always" xbl:inherits="src=iconURL"/>
|
||||
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
|
||||
<xul:hbox align="center">
|
||||
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
|
||||
<xul:label class="normal" xbl:inherits="value=datetime"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox>
|
||||
<xul:label class="normal" xbl:inherits="value=status"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox class="show-on-select" align="center">
|
||||
<xul:button anonid="showpage-button" label="&downloadShowPage.label;"
|
||||
oncommand="Downloads.showPage(document.getBindingParent(this));"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:button anonid="open-button" label="&downloadOpen2.label;"
|
||||
oncommand="Downloads.openDownload(document.getBindingParent(this));"/>
|
||||
<xul:button anonid="remove-button" label="&downloadRemove.label;"
|
||||
oncommand="Downloads.removeDownload(document.getBindingParent(this));"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
let referrer = this.hasAttribute("referrer");
|
||||
if (!referrer)
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "showpage-button").setAttribute("disabled", "true");
|
||||
let file = Downloads._getLocalFile(this.getAttribute("target"));
|
||||
if (!file) {
|
||||
throw new Error("download-done: Couldn't bind item with target: "+this.getAttribute("target"));
|
||||
}
|
||||
let mimeService = Components.classes["@mozilla.org/mime;1"].getService(Components.interfaces.nsIMIMEService);
|
||||
let mimeType;
|
||||
try {
|
||||
mimeType = mimeService.getTypeFromFile(file);
|
||||
}
|
||||
catch(e) {}
|
||||
if (!file.exists() || !mimeType)
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "open-button").setAttribute("disabled", "true");
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
|
@ -118,9 +118,6 @@ let ScriptContexts = {};
|
|||
["Downloads", "chrome://browser/content/downloads.js"],
|
||||
["BookmarksPanelView", "chrome://browser/content/bookmarks.js"],
|
||||
["ConsolePanelView", "chrome://browser/content/console.js"],
|
||||
["DownloadsPanelView", "chrome://browser/content/downloads.js"],
|
||||
["DownloadsView", "chrome://browser/content/downloads.js"],
|
||||
["Downloads", "chrome://browser/content/downloads.js"],
|
||||
["BookmarksStartView", "chrome://browser/content/bookmarks.js"],
|
||||
["HistoryView", "chrome://browser/content/history.js"],
|
||||
["HistoryStartView", "chrome://browser/content/history.js"],
|
||||
|
|
|
@ -1219,7 +1219,6 @@ var PanelUI = {
|
|||
|
||||
views: {
|
||||
"bookmarks-container": "BookmarksPanelView",
|
||||
"downloads-container": "DownloadsPanelView",
|
||||
"console-container": "ConsolePanelView",
|
||||
"remotetabs-container": "RemoteTabsPanelView",
|
||||
"history-container" : "HistoryPanelView"
|
||||
|
|
|
@ -156,27 +156,6 @@ richlistitem {
|
|||
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#richlistitem");
|
||||
}
|
||||
|
||||
richgriditem[typeName="download"][state="-1"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-not-started");
|
||||
}
|
||||
|
||||
richgriditem[typeName="download"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-downloading");
|
||||
}
|
||||
|
||||
richgriditem[typeName="download"][state="1"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-done");
|
||||
}
|
||||
|
||||
richgriditem[typeName="download"][state="2"],
|
||||
richgriditem[typeName="download"][state="3"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-retry");
|
||||
}
|
||||
|
||||
richgriditem[typeName="download"][state="4"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-paused");
|
||||
}
|
||||
|
||||
richlistitem[type="error"],
|
||||
richlistitem[type="warning"] {
|
||||
-moz-binding: url("chrome://browser/content/bindings/console.xml#error");
|
||||
|
|
|
@ -1205,6 +1205,10 @@ nsBrowserAccess.prototype = {
|
|||
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
get contentWindow() {
|
||||
return Browser.selectedBrowser.contentWindow;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1376,8 +1380,7 @@ function getNotificationBox(aBrowser) {
|
|||
}
|
||||
|
||||
function showDownloadManager(aWindowContext, aID, aReason) {
|
||||
PanelUI.show("downloads-container");
|
||||
// TODO: select the download with aID
|
||||
// TODO: Bug 883962: Toggle the downloads infobar as our current "download manager".
|
||||
}
|
||||
|
||||
function Tab(aURI, aParams, aOwner) {
|
||||
|
|
|
@ -320,9 +320,6 @@
|
|||
<scrollbox id="history-container" flex="1">
|
||||
<richgrid id="history-list" noun="history" class="canSnapTiles" seltype="multiple" flex="1"/>
|
||||
</scrollbox>
|
||||
<scrollbox id="downloads-container" flex="1">
|
||||
<richgrid id="downloads-list" noun="download" class="canSnapTiles" seltype="single" flex="1"/>
|
||||
</scrollbox>
|
||||
<scrollbox id="remotetabs-container" flex="1">
|
||||
<richgrid id="remotetabs-list" noun="tab" class="canSnapTiles" seltype="single" flex="1"/>
|
||||
</scrollbox>
|
||||
|
|
|
@ -6,8 +6,19 @@
|
|||
const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
|
||||
|
||||
var Downloads = {
|
||||
/**
|
||||
* _downloadCount keeps track of the number of downloads that a single
|
||||
* notification bar groups together. A download is grouped with other
|
||||
* downloads if it starts before other downloads have completed.
|
||||
*/
|
||||
_downloadCount: 0,
|
||||
_inited: false,
|
||||
_progressAlert: null,
|
||||
_lastSec: Infinity,
|
||||
_notificationBox: null,
|
||||
_progressNotification: null,
|
||||
_progressNotificationInfo: new Map(),
|
||||
_runDownloadBooleanMap: new Map(),
|
||||
|
||||
get manager() {
|
||||
return Cc["@mozilla.org/download-manager;1"]
|
||||
|
@ -39,20 +50,26 @@ var Downloads = {
|
|||
|
||||
Services.obs.addObserver(this, "dl-start", true);
|
||||
Services.obs.addObserver(this, "dl-done", true);
|
||||
Services.obs.addObserver(this, "dl-run", true);
|
||||
Services.obs.addObserver(this, "dl-failed", true);
|
||||
|
||||
this._notificationBox = Browser.getNotificationBox();
|
||||
|
||||
this._progress = new DownloadProgressListener(this);
|
||||
this.manager.addListener(this._progress);
|
||||
},
|
||||
|
||||
uninit: function dh_uninit() {
|
||||
if (this._inited) {
|
||||
Services.obs.removeObserver(this, "dl-start");
|
||||
Services.obs.removeObserver(this, "dl-done");
|
||||
Services.obs.removeObserver(this, "dl-run");
|
||||
Services.obs.removeObserver(this, "dl-failed");
|
||||
}
|
||||
},
|
||||
|
||||
openDownload: function dh_openDownload(aDownload) {
|
||||
// expects xul item
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id);
|
||||
let fileURI = download.target;
|
||||
let fileURI = aDownload.target
|
||||
|
||||
if (!(fileURI && fileURI.spec)) {
|
||||
Util.dumpLn("Cant open download "+id+", fileURI is invalid");
|
||||
|
@ -61,7 +78,7 @@ var Downloads = {
|
|||
|
||||
let file = this._getLocalFile(fileURI);
|
||||
try {
|
||||
file && file.launch();
|
||||
file && MetroUtils.launchInDesktop(aDownload.target.spec, "");
|
||||
} catch (ex) {
|
||||
Util.dumpLn("Failed to open download, with id: "+id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
|
||||
|
@ -75,33 +92,46 @@ var Downloads = {
|
|||
let download = this.manager.getDownload(id);
|
||||
|
||||
if (download) {
|
||||
// TODO <sfoster> add class/mark element for removal transition?
|
||||
this.manager.removeDownload(id);
|
||||
}
|
||||
},
|
||||
|
||||
cancelDownload: function dh_cancelDownload(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id);
|
||||
this.manager.cancelDownload(id);
|
||||
|
||||
let fileURI = download.target;
|
||||
this._progressNotificationInfo.delete(aDownload.guid);
|
||||
this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
|
||||
this._downloadCount--;
|
||||
if (this._progressNotificationInfo.size == 0) {
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
this._progressNotification = null;
|
||||
}
|
||||
|
||||
let fileURI = aDownload.target;
|
||||
if (!(fileURI && fileURI.spec)) {
|
||||
Util.dumpLn("Cant remove download file for: "+id+", fileURI is invalid");
|
||||
Util.dumpLn("Cant remove download file for: "+aDownload.id+", fileURI is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
let file = this._getLocalFile(fileURI);
|
||||
try {
|
||||
this.manager.cancelDownload(aDownload.id);
|
||||
if (file && file.exists())
|
||||
file.remove(false);
|
||||
} catch (ex) {
|
||||
Util.dumpLn("Failed to cancel download, with id: "+id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed to cancel download, with id: "+aDownload.id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
|
||||
}
|
||||
},
|
||||
|
||||
// Cancels all downloads.
|
||||
cancelDownloads: function dh_cancelDownloads() {
|
||||
for (let info of this._progressNotificationInfo) {
|
||||
this.cancelDownload(info[1].download);
|
||||
}
|
||||
this._downloadCount = 0;
|
||||
this._progressNotificationInfo.clear();
|
||||
this._runDownloadBooleanMap.clear();
|
||||
},
|
||||
|
||||
pauseDownload: function dh_pauseDownload(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
this.manager.pauseDownload(id);
|
||||
|
@ -112,11 +142,6 @@ var Downloads = {
|
|||
this.manager.resumeDownload(id);
|
||||
},
|
||||
|
||||
retryDownload: function dh_retryDownload(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
this.manager.retryDownload(id);
|
||||
},
|
||||
|
||||
showPage: function dh_showPage(aDownload) {
|
||||
let id = aDownload.getAttribute("downloadId");
|
||||
let download = this.manager.getDownload(id);
|
||||
|
@ -132,8 +157,9 @@ var Downloads = {
|
|||
// Callback for tapping on the alert popup
|
||||
let observer = {
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "alertclickcallback")
|
||||
PanelUI.show("downloads-container");
|
||||
if (aTopic == "alertclickcallback") {
|
||||
// TODO: Bug 783232 turns this alert into a native toast.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -146,27 +172,207 @@ var Downloads = {
|
|||
notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer, aName);
|
||||
},
|
||||
|
||||
showNotification: function dh_showNotification(title, msg, buttons, priority) {
|
||||
return this._notificationBox.appendNotification(msg,
|
||||
title,
|
||||
URI_GENERIC_ICON_DOWNLOAD,
|
||||
priority,
|
||||
buttons);
|
||||
},
|
||||
|
||||
_showDownloadFailedNotification: function (aDownload) {
|
||||
let tryAgainButtonText =
|
||||
Strings.browser.GetStringFromName("downloadTryAgain");
|
||||
let cancelButtonText =
|
||||
Strings.browser.GetStringFromName("downloadCancel");
|
||||
|
||||
let message = Strings.browser.formatStringFromName("alertDownloadFailed",
|
||||
[aDownload.displayName], 1);
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
isDefault: true,
|
||||
label: tryAgainButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.manager.retryDownload(aDownload.id);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: cancelButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownload(aDownload);
|
||||
}
|
||||
}
|
||||
];
|
||||
this.showNotification("download-failed", message, buttons,
|
||||
this._notificationBox.PRIORITY_WARNING_HIGH);
|
||||
},
|
||||
|
||||
_showDownloadCompleteNotification: function (aDownload) {
|
||||
let message = "";
|
||||
let showInFilesButtonText = PluralForm.get(this._downloadCount,
|
||||
Strings.browser.GetStringFromName("downloadsShowInFiles"));
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: showInFilesButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
let fileURI = aDownload.target;
|
||||
let file = Downloads._getLocalFile(fileURI);
|
||||
file.reveal();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (this._downloadCount > 1) {
|
||||
message = PluralForm.get(this._downloadCount,
|
||||
Strings.browser.GetStringFromName("alertMultipleDownloadsComplete"))
|
||||
.replace("#1", this._downloadCount)
|
||||
} else {
|
||||
let runButtonText =
|
||||
Strings.browser.GetStringFromName("downloadRun");
|
||||
message = Strings.browser.formatStringFromName("alertDownloadsDone2",
|
||||
[aDownload.displayName], 1);
|
||||
|
||||
buttons.unshift({
|
||||
isDefault: true,
|
||||
label: runButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.openDownload(aDownload);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.showNotification("download-complete", message, buttons,
|
||||
this._notificationBox.PRIORITY_WARNING_MEDIUM);
|
||||
},
|
||||
|
||||
_computeDownloadProgressString: function dv_computeDownloadProgressString(aDownload) {
|
||||
let totTransferred = 0, totSize = 0, totSecondsLeft = 0;
|
||||
for (let info of this._progressNotificationInfo) {
|
||||
let size = info[1].download.size;
|
||||
let amountTransferred = info[1].download.amountTransferred;
|
||||
let speed = info[1].download.speed;
|
||||
|
||||
totTransferred += amountTransferred;
|
||||
totSize += size;
|
||||
totSecondsLeft += ((size - amountTransferred) / speed);
|
||||
}
|
||||
// Compute progress in bytes.
|
||||
let amountTransferred = Util.getDownloadSize(totTransferred);
|
||||
let size = Util.getDownloadSize(totSize);
|
||||
let progress = amountTransferred + "/" + size;
|
||||
|
||||
// Compute progress in time.;
|
||||
let [timeLeft, newLast] = DownloadUtils.getTimeLeft(totSecondsLeft, this._lastSec);
|
||||
this._lastSec = newLast;
|
||||
|
||||
if (this._downloadCount == 1) {
|
||||
return Strings.browser.GetStringFromName("alertDownloadsStart2")
|
||||
.replace("#1", aDownload.displayName)
|
||||
.replace("#2", progress)
|
||||
.replace("#3", timeLeft)
|
||||
}
|
||||
|
||||
let numDownloads = this._downloadCount;
|
||||
return PluralForm.get(numDownloads,
|
||||
Strings.browser.GetStringFromName("alertDownloadMultiple"))
|
||||
.replace("#1", numDownloads)
|
||||
.replace("#2", progress)
|
||||
.replace("#3", timeLeft);
|
||||
},
|
||||
|
||||
_saveDownloadData: function dv_saveDownloadData(aDownload) {
|
||||
if (!this._progressNotificationInfo.get(aDownload.guid)) {
|
||||
this._progressNotificationInfo.set(aDownload.guid, {});
|
||||
}
|
||||
let infoObj = this._progressNotificationInfo.get(aDownload.guid);
|
||||
infoObj.download = aDownload;
|
||||
this._progressNotificationInfo.set(aDownload.guid, infoObj);
|
||||
},
|
||||
|
||||
updateInfobar: function dv_updateInfobar(aDownload) {
|
||||
this._saveDownloadData(aDownload);
|
||||
let message = this._computeDownloadProgressString(aDownload);
|
||||
|
||||
if (this._progressNotification == null ||
|
||||
!this._notificationBox.getNotificationWithValue("download-progress")) {
|
||||
|
||||
let cancelButtonText =
|
||||
Strings.browser.GetStringFromName("downloadCancel");
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
isDefault: false,
|
||||
label: cancelButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownloads();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._progressNotification =
|
||||
this.showNotification("download-progress", message, buttons,
|
||||
this._notificationBox.PRIORITY_WARNING_LOW);
|
||||
} else {
|
||||
this._progressNotification.label = message;
|
||||
}
|
||||
},
|
||||
|
||||
updateDownload: function dv_updateDownload(aDownload) {
|
||||
if (this._progressNotification != null) {
|
||||
this._saveDownloadData(aDownload);
|
||||
this._progressNotification.label =
|
||||
this._computeDownloadProgressString(aDownload);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
let download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
let msgKey = "";
|
||||
let message = "";
|
||||
let msgTitle = "";
|
||||
|
||||
switch (aTopic) {
|
||||
case "dl-run":
|
||||
let file = aSubject.QueryInterface(Ci.nsIFile);
|
||||
this._runDownloadBooleanMap.set(file.path, (aData == 'true'));
|
||||
break;
|
||||
case "dl-start":
|
||||
msgKey = "alertDownloadsStart";
|
||||
this._downloadCount++;
|
||||
let download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
if (!this._progressNotificationInfo.get(download.guid)) {
|
||||
this._progressNotificationInfo.set(download.guid, {});
|
||||
}
|
||||
if (!this._progressAlert) {
|
||||
this._progressAlert = new AlertDownloadProgressListener();
|
||||
this.manager.addListener(this._progressAlert);
|
||||
}
|
||||
this.updateInfobar(download);
|
||||
break;
|
||||
case "dl-done":
|
||||
msgKey = "alertDownloadsDone";
|
||||
break;
|
||||
download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
let runAfterDownload = this._runDownloadBooleanMap.get(download.targetFile.path);
|
||||
if (runAfterDownload) {
|
||||
this.openDownload(download);
|
||||
}
|
||||
|
||||
if (msgKey) {
|
||||
let message = Strings.browser.formatStringFromName(msgKey, [download.displayName], 1);
|
||||
let url = download.target.spec.replace("file:", "download:");
|
||||
this.showAlert(url, message);
|
||||
this._progressNotificationInfo.delete(download.guid);
|
||||
this._runDownloadBooleanMap.delete(download.targetFile.path);
|
||||
if (this._progressNotificationInfo.size == 0) {
|
||||
if (this._downloadCount > 1 || !runAfterDownload) {
|
||||
this._showDownloadCompleteNotification(download);
|
||||
}
|
||||
this._downloadCount = 0;
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
this._progressNotification = null;
|
||||
}
|
||||
break;
|
||||
case "dl-failed":
|
||||
download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -179,276 +385,29 @@ var Downloads = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
|
||||
* fills it with the user's downloads. It observes download notifications,
|
||||
* so it'll automatically update to reflect current download progress.
|
||||
*
|
||||
* @param aSet Control implementing nsIDOMXULSelectControlElement.
|
||||
* @param {Number} aLimit Maximum number of items to show in the view.
|
||||
*/
|
||||
function DownloadsView(aSet, aLimit) {
|
||||
this._set = aSet;
|
||||
this._limit = aLimit;
|
||||
|
||||
this._progress = new DownloadProgressListener(this);
|
||||
Downloads.manager.addListener(this._progress);
|
||||
|
||||
// Look for the removed download notification
|
||||
let obs = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
obs.addObserver(this, "download-manager-remove-download-guid", false);
|
||||
|
||||
this.getDownloads();
|
||||
}
|
||||
|
||||
DownloadsView.prototype = {
|
||||
_progress: null,
|
||||
_stmt: null,
|
||||
_timeoutID: null,
|
||||
_set: null,
|
||||
_limit: null,
|
||||
|
||||
_getItemForDownloadGuid: function dv__getItemForDownload(aGuid) {
|
||||
return this._set.querySelector("richgriditem[downloadGuid='" + aGuid + "']");
|
||||
},
|
||||
|
||||
_getItemForDownload: function dv__getItemForDownload(aDownload) {
|
||||
return this._set.querySelector("richgriditem[downloadId='" + aDownload.id + "']");
|
||||
},
|
||||
|
||||
_getDownloadForItem: function dv__getDownloadForItem(anItem) {
|
||||
let id = anItem.getAttribute("downloadId");
|
||||
return Downloads.manager.getDownload(id);
|
||||
},
|
||||
|
||||
_getAttrsForDownload: function dv__getAttrsForDownload(aDownload) {
|
||||
// params: nsiDownload
|
||||
return {
|
||||
typeName: 'download',
|
||||
downloadId: aDownload.id,
|
||||
downloadGuid: aDownload.guid,
|
||||
name: aDownload.displayName,
|
||||
// use the stringified version of the target nsIURI for the item attribute
|
||||
target: aDownload.target.spec,
|
||||
iconURI: "moz-icon://" + aDownload.displayName + "?size=64",
|
||||
date: DownloadUtils.getReadableDates(new Date())[0],
|
||||
domain: DownloadUtils.getURIHost(aDownload.source.spec)[0],
|
||||
size: this._getDownloadSize(aDownload.size),
|
||||
state: aDownload.state
|
||||
};
|
||||
|
||||
},
|
||||
_updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
|
||||
for (let name in aAttrs)
|
||||
anItem.setAttribute(name, aAttrs[name]);
|
||||
if (anItem.refresh)
|
||||
anItem.refresh();
|
||||
},
|
||||
|
||||
_getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
let displaySize = DownloadUtils.convertByteUnits(aSize);
|
||||
if (displaySize[0] > 0) // [0] is size, [1] is units
|
||||
return displaySize.join("");
|
||||
else
|
||||
return Strings.browser.GetStringFromName("downloadsUnknownSize");
|
||||
},
|
||||
|
||||
_initStatement: function dv__initStatement() {
|
||||
if (this._stmt)
|
||||
this._stmt.finalize();
|
||||
|
||||
let limitClause = this._limit ? ("LIMIT " + this._limit) : "";
|
||||
|
||||
this._stmt = Downloads.manager.DBConnection.createStatement(
|
||||
"SELECT id, guid, name, target, source, state, startTime, endTime, referrer, " +
|
||||
"currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " +
|
||||
"FROM moz_downloads " +
|
||||
"ORDER BY isActive DESC, endTime DESC, startTime DESC " +
|
||||
limitClause);
|
||||
},
|
||||
|
||||
_stepDownloads: function dv__stepDownloads(aNumItems) {
|
||||
try {
|
||||
if (!this._stmt.executeStep()) {
|
||||
// final record;
|
||||
this._stmt.finalize();
|
||||
this._stmt = null;
|
||||
this._fire("DownloadsReady", this._set);
|
||||
return;
|
||||
}
|
||||
let attrs = {
|
||||
typeName: 'download',
|
||||
// TODO: <sfoster> the remove event gives us guid not id; should we use guid as the download (record) id?
|
||||
downloadGuid: this._stmt.row.guid,
|
||||
downloadId: this._stmt.row.id,
|
||||
name: this._stmt.row.name,
|
||||
target: this._stmt.row.target,
|
||||
iconURI: "moz-icon://" + this._stmt.row.name + "?size=25",
|
||||
date: DownloadUtils.getReadableDates(new Date(this._stmt.row.endTime / 1000))[0],
|
||||
domain: DownloadUtils.getURIHost(this._stmt.row.source)[0],
|
||||
size: this._getDownloadSize(this._stmt.row.maxBytes),
|
||||
state: this._stmt.row.state
|
||||
};
|
||||
|
||||
let item = this._set.appendItem(attrs.target, attrs.downloadId);
|
||||
this._updateItemWithAttrs(item, attrs);
|
||||
} catch (e) {
|
||||
// Something went wrong when stepping or getting values, so quit
|
||||
this._stmt.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add another item to the list if we should;
|
||||
// otherwise, let the UI update and continue later
|
||||
if (aNumItems > 1) {
|
||||
this._stepDownloads(aNumItems - 1);
|
||||
} else {
|
||||
// Use a shorter delay for earlier downloads to display them faster
|
||||
let delay = Math.min(this._set.itemCount * 10, 300);
|
||||
let self = this;
|
||||
this._timeoutID = setTimeout(function() { self._stepDownloads(5); }, delay);
|
||||
}
|
||||
},
|
||||
|
||||
_fire: function _fire(aName, anElement) {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent(aName, true, true);
|
||||
anElement.dispatchEvent(event);
|
||||
},
|
||||
|
||||
observe: function dv_managerObserver(aSubject, aTopic, aData) {
|
||||
// observer-service message handler
|
||||
switch (aTopic) {
|
||||
case "download-manager-remove-download-guid":
|
||||
let guid = aSubject.QueryInterface(Ci.nsISupportsCString);
|
||||
this.removeDownload({
|
||||
guid: guid
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
getDownloads: function dv_getDownloads() {
|
||||
this._initStatement();
|
||||
clearTimeout(this._timeoutID);
|
||||
|
||||
// Since we're pulling in all downloads, clear the list to avoid duplication
|
||||
this.clearDownloads();
|
||||
|
||||
this._stmt.reset();
|
||||
this._stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED);
|
||||
this._stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
|
||||
this._stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
|
||||
this._stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
|
||||
this._stmt.bindInt32Parameter(4, Ci.nsIDownloadManager.DOWNLOAD_SCANNING);
|
||||
|
||||
// Take a quick break before we actually start building the list
|
||||
let self = this;
|
||||
this._timeoutID = setTimeout(function() {
|
||||
self._stepDownloads(1);
|
||||
}, 0);
|
||||
},
|
||||
|
||||
clearDownloads: function dv_clearDownloads() {
|
||||
this._set.clearAll();
|
||||
},
|
||||
|
||||
addDownload: function dv_addDownload(aDownload) {
|
||||
// expects a download manager download object
|
||||
let attrs = this._getAttrsForDownload(aDownload);
|
||||
let item = this._set.insertItemAt(0, attrs.target, attrs.downloadId);
|
||||
this._updateItemWithAttrs(item, attrs);
|
||||
},
|
||||
|
||||
updateDownload: function dv_updateDownload(aDownload) {
|
||||
// expects a download manager download object
|
||||
let item = this._getItemForDownload(aDownload);
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
let attrs = this._getAttrsForDownload(aDownload);
|
||||
this._updateItemWithAttrs(item, attrs);
|
||||
},
|
||||
|
||||
removeDownload: function dv_removeDownload(aDownload) {
|
||||
// expects an object with id or guid property
|
||||
let item;
|
||||
if (aDownload.id) {
|
||||
item = this._getItemForDownload(aDownload.id);
|
||||
} else if (aDownload.guid) {
|
||||
item = this._getItemForDownloadGuid(aDownload.guid);
|
||||
}
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
let idx = this._set.getIndexOfItem(item);
|
||||
if (idx < 0)
|
||||
return;
|
||||
// any transition needed here?
|
||||
this._set.removeItemAt(idx);
|
||||
},
|
||||
|
||||
destruct: function dv_destruct() {
|
||||
Downloads.manager.removeListener(this._progress);
|
||||
}
|
||||
};
|
||||
|
||||
var DownloadsPanelView = {
|
||||
_view: null,
|
||||
|
||||
get _grid() { return document.getElementById("downloads-list"); },
|
||||
get visible() { return PanelUI.isPaneVisible("downloads-container"); },
|
||||
|
||||
init: function init() {
|
||||
this._view = new DownloadsView(this._grid);
|
||||
},
|
||||
|
||||
show: function show() {
|
||||
this._grid.arrangeItems();
|
||||
},
|
||||
|
||||
uninit: function uninit() {
|
||||
this._view.destruct();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies a DownloadsView about updates in the state of various downloads.
|
||||
* Notifies Downloads object about updates in the state of various downloads.
|
||||
*
|
||||
* @param aView An instance of DownloadsView.
|
||||
* @param aDownloads An instance of Downloads.
|
||||
*/
|
||||
function DownloadProgressListener(aView) {
|
||||
this._view = aView;
|
||||
function DownloadProgressListener(aDownloads) {
|
||||
this._downloads = aDownloads;
|
||||
}
|
||||
|
||||
DownloadProgressListener.prototype = {
|
||||
_view: null,
|
||||
_downloads: null,
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIDownloadProgressListener
|
||||
onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) {
|
||||
let state = aDownload.state;
|
||||
switch (state) {
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY:
|
||||
this._view.addDownload(aDownload);
|
||||
break;
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
|
||||
break;
|
||||
}
|
||||
|
||||
this._view.updateDownload(aDownload);
|
||||
// TODO: Use DownloadProgressListener instead of observers in the Downloads object.
|
||||
this._downloads.updateDownload(aDownload);
|
||||
},
|
||||
|
||||
onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
|
||||
// TODO <jwilde>: Add more detailed progress information.
|
||||
this._view.updateDownload(aDownload);
|
||||
this._downloads.updateDownload(aDownload);
|
||||
},
|
||||
|
||||
onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
|
||||
|
@ -483,7 +442,7 @@ AlertDownloadProgressListener.prototype = {
|
|||
Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"),
|
||||
strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||
strings.GetStringFromName("alertDownloadsSize"));
|
||||
Downloads.cancelDownload(aDownload.id);
|
||||
Downloads.cancelDownload(aDownload);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ chrome.jar:
|
|||
content/bindings/toggleswitch.xml (content/bindings/toggleswitch.xml)
|
||||
content/bindings/browser.xml (content/bindings/browser.xml)
|
||||
content/bindings/browser.js (content/bindings/browser.js)
|
||||
content/bindings/downloads.xml (content/bindings/downloads.xml)
|
||||
content/bindings/console.xml (content/bindings/console.xml)
|
||||
content/bindings/dialog.xml (content/bindings/dialog.xml)
|
||||
content/bindings/arrowbox.xml (content/bindings/arrowbox.xml)
|
||||
|
|
|
@ -112,8 +112,6 @@ let gDownloadRowTemplate = {
|
|||
// Test Infrastructure
|
||||
|
||||
function test() {
|
||||
DownloadsPanelView._view.clearDownloads();
|
||||
PanelUI.show("downloads-container");
|
||||
runTests();
|
||||
}
|
||||
|
||||
|
@ -212,43 +210,11 @@ function gen_addDownloadRows(aDataRows){
|
|||
/////////////////////////////////////
|
||||
// Test implementations
|
||||
|
||||
/**
|
||||
* Test that:
|
||||
* view can represent all possible download states
|
||||
* clearDownloads does in fact empty the view
|
||||
* addDownload adds an item, to the right place
|
||||
* removeDownload removes an item, leaving the view in the correct state
|
||||
*/
|
||||
gTests.push({
|
||||
desc: "UI sanity check",
|
||||
run: function(){
|
||||
|
||||
ok(document.getElementById('downloads-list'), "Downloads panel grid is present");
|
||||
ok(DownloadsPanelView, "Downloads panel object is present");
|
||||
|
||||
PanelUI.show('downloads-container');
|
||||
ok(DownloadsPanelView.visible, "Downloads panel is visible after being shown");
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "zero downloads",
|
||||
run: function () {
|
||||
|
||||
yield resetDownloads();
|
||||
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
yield isReady;
|
||||
|
||||
let count = downloadsList.children.length;
|
||||
is(count, 0, "Zero items in grid view with empty downloads db");
|
||||
todo(false, "Test there are no visible notifications with an empty db.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -276,74 +242,30 @@ gTests.push({
|
|||
|
||||
yield resetDownloads();
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
|
||||
// NB: beware display limits which might cause mismatch btw. rendered item and db rows
|
||||
|
||||
try {
|
||||
// Populate the downloads database with the data required by this test.
|
||||
// we're going to add stuff to the downloads db.
|
||||
yield spawn( gen_addDownloadRows( DownloadData ) );
|
||||
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
todo( false, "Check that Downloads._progressNotificationInfo and Downloads._downloadCount \
|
||||
have the correct length (DownloadData.length) \
|
||||
May also test that the correct notifications show up for various states.");
|
||||
|
||||
yield isReady;
|
||||
|
||||
if (!isReady || isReady instanceof Error){
|
||||
ok(false, "DownloadsReady event never fired");
|
||||
}
|
||||
|
||||
is(downloadsList.children.length, DownloadData.length,
|
||||
"There is the correct number of richlistitems");
|
||||
|
||||
for (let i = 0; i < downloadsList.children.length; i++) {
|
||||
let element = downloadsList.children[i];
|
||||
let id = element.getAttribute("downloadId");
|
||||
let dataItem = Downloads.manager.getDownload(id); // nsIDownload object
|
||||
|
||||
ok( equalStrings(
|
||||
element.getAttribute("name"),
|
||||
dataItem.displayName,
|
||||
DownloadData[i].name
|
||||
), "Download names match up");
|
||||
|
||||
ok( equalNumbers(
|
||||
element.getAttribute("state"),
|
||||
dataItem.state,
|
||||
DownloadData[i].state
|
||||
), "Download states match up");
|
||||
|
||||
ok( equalStrings(
|
||||
element.getAttribute("target"),
|
||||
dataItem.target.spec,
|
||||
DownloadData[i].target
|
||||
), "Download targets match up");
|
||||
|
||||
if (DownloadData[i].source && dataItem.referrer){
|
||||
ok( equalStrings(
|
||||
dataItem.referrer.spec,
|
||||
DownloadData[i].source
|
||||
), "Download sources match up");
|
||||
}
|
||||
}
|
||||
todo(false, "Iterate through download objects in Downloads._progressNotificationInfo \
|
||||
and confirm that the downloads they refer to are the same as those in \
|
||||
DownloadData.");
|
||||
} catch(e) {
|
||||
info("Show downloads, some error: " + e);
|
||||
}
|
||||
finally {
|
||||
// Clean up when the test finishes.
|
||||
DownloadsPanelView._view.clearDownloads();
|
||||
yield resetDownloads();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Make sure the downloads can be removed with the expected result on the panel and its listing
|
||||
* Make sure the downloads can be removed with the expected result on the notifications
|
||||
*/
|
||||
gTests.push({
|
||||
desc: "Remove downloads",
|
||||
|
@ -356,26 +278,11 @@ gTests.push({
|
|||
];
|
||||
|
||||
yield resetDownloads();
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
try {
|
||||
// Populate the downloads database with the data required by this test.
|
||||
yield spawn( gen_addDownloadRows( DownloadData ) );
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let downloadsList = document.getElementById("downloads-list");
|
||||
// wait for the richgrid to announce its readiness
|
||||
// .. fail a test if the timeout is exceeded
|
||||
let isReady = waitForEvent(downloadsList, "DownloadsReady", 2000);
|
||||
// tickle the view to cause it to refresh itself
|
||||
DownloadsPanelView._view.getDownloads();
|
||||
|
||||
yield isReady;
|
||||
|
||||
if (!isReady || isReady instanceof Error){
|
||||
is(false, "DownloadsReady event never fired");
|
||||
}
|
||||
|
||||
let downloadRows = null,
|
||||
promisedDownloads;
|
||||
// get all the downloads from the db
|
||||
|
@ -392,15 +299,9 @@ gTests.push({
|
|||
|
||||
is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
|
||||
|
||||
// remove the first one
|
||||
let itemNode = downloadsList.children[0];
|
||||
let id = itemNode.getAttribute("downloadId");
|
||||
// check the file exists
|
||||
let download = Downloads.manager.getDownload( id );
|
||||
let file = download.targetFile;
|
||||
ok(file && file.exists());
|
||||
todo(false, "Get some download from Downloads._progressNotificationInfo, \
|
||||
confirm that its file exists, then remove it.");
|
||||
|
||||
Downloads.manager.removeDownload( id );
|
||||
// remove is async(?), wait a bit
|
||||
yield waitForMs(0);
|
||||
|
||||
|
@ -417,18 +318,14 @@ gTests.push({
|
|||
});
|
||||
yield promisedDownloads;
|
||||
|
||||
is(downloadRows.length, 2, "Correct number of downloads in the db after removal");
|
||||
|
||||
is(2, downloadsList.children.length,
|
||||
"Removing a download updates the items list");
|
||||
ok(file && file.exists(), "File still exists after download removal");
|
||||
todo(false, "confirm that the removed download is no longer in the database \
|
||||
and its file no longer exists.");
|
||||
|
||||
} catch(e) {
|
||||
info("Remove downloads, some error: " + e);
|
||||
}
|
||||
finally {
|
||||
// Clean up when the test finishes.
|
||||
DownloadsPanelView._view.clearDownloads();
|
||||
yield resetDownloads();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,8 @@
|
|||
<body>
|
||||
<form action="texarea.html">
|
||||
<div style="margin-left: 250px;">
|
||||
<textarea wrap="on" style="width:800px; height:100px; overflow:scroll;" id="inputtext">
|
||||
Alice was beginning to get very tired of sitting by her sister on the bank, and of having
|
||||
nothing to do: once or twice she had peeped into the book her sister was reading, but it
|
||||
had no pictures or conversations in it, `and what is the use of a book,' thought Alice
|
||||
`without pictures or conversation?'
|
||||
|
||||
Either the well was very deep, or she fell very slowly, for she had plenty of time as she
|
||||
went down to look about her and to wonder what was going to happen next. First, she tried
|
||||
to look down and make out what she was coming to, but it was too dark to see anything;
|
||||
then she looked at the sides of the well, and noticed that they were filled with cupboards
|
||||
and book-shelves;(end)</textarea>
|
||||
<textarea wrap="on" style="width:400px; height:75px; overflow:scroll;" id="inputtext">
|
||||
Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'(end)</textarea>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -48,41 +48,7 @@ gTests.push({
|
|||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "textarea basic selection",
|
||||
setUp: setUpAndTearDown,
|
||||
tearDown: setUpAndTearDown,
|
||||
run: function test() {
|
||||
let textarea = gWindow.document.getElementById("inputtext");
|
||||
textarea.focus();
|
||||
|
||||
let promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClick(355, 50);
|
||||
yield promise;
|
||||
|
||||
checkContextUIMenuItemVisibility(["context-select",
|
||||
"context-select-all"]);
|
||||
|
||||
let menuItem = document.getElementById("context-select");
|
||||
ok(menuItem, "menu item exists");
|
||||
ok(!menuItem.hidden, "menu item visible");
|
||||
let popupPromise = waitForEvent(document, "popuphidden");
|
||||
sendElementTap(gWindow, menuItem);
|
||||
yield popupPromise;
|
||||
ok(popupPromise && !(popupPromise instanceof Error), "promise error");
|
||||
|
||||
yield waitForCondition(function () {
|
||||
return SelectionHelperUI.isSelectionUIVisible;
|
||||
}, kCommonWaitMs, kCommonPollMs);
|
||||
|
||||
// check text selection
|
||||
is(getTrimmedSelection(textarea).toString(), "pictures", "selection test");
|
||||
|
||||
clearSelection(textarea);
|
||||
},
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "textarea complex drag selection",
|
||||
desc: "textarea selection and drag",
|
||||
setUp: setUpAndTearDown,
|
||||
tearDown: setUpAndTearDown,
|
||||
run: function test() {
|
||||
|
@ -90,9 +56,10 @@ gTests.push({
|
|||
yield waitForMs(100);
|
||||
|
||||
let textarea = gWindow.document.getElementById("inputtext");
|
||||
textarea.focus();
|
||||
|
||||
let promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClick(355, 50);
|
||||
sendContextMenuClickToElement(gWindow, textarea, 20, 10);
|
||||
yield promise;
|
||||
|
||||
checkContextUIMenuItemVisibility(["context-select",
|
||||
|
@ -111,7 +78,7 @@ gTests.push({
|
|||
}, kCommonWaitMs, kCommonPollMs);
|
||||
|
||||
is(SelectionHelperUI.isActive, true, "selection active");
|
||||
is(getTrimmedSelection(textarea).toString(), "pictures", "selection test");
|
||||
is(getTrimmedSelection(textarea).toString(), "Alice", "selection test");
|
||||
|
||||
let xpos = SelectionHelperUI.endMark.xPos;
|
||||
let ypos = SelectionHelperUI.endMark.yPos + 10;
|
||||
|
@ -120,15 +87,15 @@ gTests.push({
|
|||
|
||||
// end marker and off the text area to the right
|
||||
yield touchdrag.start(gWindow, xpos, ypos, 1200, 400);
|
||||
let textLength = getTrimmedSelection(textarea).toString().length;
|
||||
let token = "(end)";
|
||||
yield waitForCondition(function () {
|
||||
let newTextLength = getTrimmedSelection(textarea).toString().length;
|
||||
if (textLength != newTextLength) {
|
||||
textLength = newTextLength;
|
||||
let selection = getTrimmedSelection(textarea).toString();
|
||||
if (selection.length < token.length ||
|
||||
selection.substring(selection.length - token.length) != token) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, 45000, 1000);
|
||||
}, 5000, 100);
|
||||
|
||||
touchdrag.end();
|
||||
touchdrag = null;
|
||||
|
@ -137,10 +104,6 @@ gTests.push({
|
|||
return !SelectionHelperUI.hasActiveDrag;
|
||||
}, kCommonWaitMs, kCommonPollMs);
|
||||
yield SelectionHelperUI.pingSelectionHandler();
|
||||
|
||||
let text = getTrimmedSelection(textarea).toString();
|
||||
let end = text.substring(text.length - "(end)".length);
|
||||
is(end, "(end)", "selection test");
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -270,8 +270,6 @@ BrowserCLH.prototype = {
|
|||
var updateService = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
|
||||
var updateTimerCallback = updateService.QueryInterface(Ci.nsITimerCallback);
|
||||
updateTimerCallback.notify(null);
|
||||
} else if (alertFlag.length >= 9 && alertFlag.substr(0, 9) == "download:") {
|
||||
showPanelWhenReady(browserWin, "downloads-container");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -26,10 +26,7 @@ DownloadManagerUI.prototype = {
|
|||
},
|
||||
|
||||
get visible() {
|
||||
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browser) {
|
||||
return browser.DownloadsView.visible;
|
||||
}
|
||||
// For now there is no Download Manager UI.
|
||||
return false;
|
||||
},
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-
|
|||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DownloadUtils.jsm");
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// HelperApp Launcher Dialog
|
||||
|
@ -32,23 +33,79 @@ HelperAppLauncherDialog.prototype = {
|
|||
aLauncher.launchWithApplication(null, false);
|
||||
} else {
|
||||
let wasClicked = false;
|
||||
let listener = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "alertclickcallback") {
|
||||
wasClicked = true;
|
||||
let win = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator).getMostRecentWindow("navigator:browser");
|
||||
if (win)
|
||||
win.PanelUI.show("downloads-container");
|
||||
this._showDownloadInfobar(aLauncher);
|
||||
}
|
||||
},
|
||||
|
||||
_getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
let displaySize = DownloadUtils.convertByteUnits(aSize);
|
||||
if (displaySize[0] > 0) // [0] is size, [1] is units
|
||||
return displaySize.join("");
|
||||
else
|
||||
return Strings.browser.GetStringFromName("downloadsUnknownSize");
|
||||
},
|
||||
|
||||
_getChromeWindow: function (aWindow) {
|
||||
let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
return chromeWin;
|
||||
},
|
||||
|
||||
_showDownloadInfobar: function do_showDownloadInfobar(aLauncher) {
|
||||
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let runButtonText =
|
||||
browserBundle.GetStringFromName("downloadRun");
|
||||
let saveButtonText =
|
||||
browserBundle.GetStringFromName("downloadSave");
|
||||
let cancelButtonText =
|
||||
browserBundle.GetStringFromName("downloadCancel");
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
isDefault: true,
|
||||
label: runButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
aLauncher.saveToDisk(null, false);
|
||||
} else {
|
||||
if (!wasClicked)
|
||||
aLauncher.cancel(Cr.NS_BINDING_ABORTED);
|
||||
Services.obs.notifyObservers(aLauncher.targetFile, "dl-run", "true");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: saveButtonText,
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
aLauncher.saveToDisk(null, false);
|
||||
Services.obs.notifyObservers(aLauncher.targetFile, "dl-run", "false");
|
||||
}
|
||||
};
|
||||
this._notify(aLauncher, listener);
|
||||
},
|
||||
{
|
||||
label: cancelButtonText,
|
||||
accessKey: "",
|
||||
callback: function() { aLauncher.cancel(Cr.NS_BINDING_ABORTED); }
|
||||
}
|
||||
];
|
||||
|
||||
let window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let chromeWin = this._getChromeWindow(window).wrappedJSObject;
|
||||
let notificationBox = chromeWin.Browser.getNotificationBox();
|
||||
downloadSize = this._getDownloadSize(aLauncher.contentLength);
|
||||
|
||||
let msg = browserBundle.GetStringFromName("alertDownloadSave")
|
||||
.replace("#1", aLauncher.suggestedFileName)
|
||||
.replace("#2", downloadSize)
|
||||
.replace("#3", aLauncher.source.host);
|
||||
|
||||
let newBar = notificationBox.appendNotification(msg,
|
||||
"save-download",
|
||||
URI_GENERIC_ICON_DOWNLOAD,
|
||||
notificationBox.PRIORITY_WARNING_HIGH,
|
||||
buttons);
|
||||
},
|
||||
|
||||
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
|
||||
|
@ -203,16 +260,6 @@ HelperAppLauncherDialog.prototype = {
|
|||
isUsableDirectory: function hald_isUsableDirectory(aDirectory) {
|
||||
return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable();
|
||||
},
|
||||
|
||||
_notify: function hald_notify(aLauncher, aCallback) {
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let notifier = Cc[aCallback ? "@mozilla.org/alerts-service;1" : "@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
notifier.showAlertNotification(URI_GENERIC_ICON_DOWNLOAD,
|
||||
bundle.GetStringFromName("alertDownloads"),
|
||||
bundle.GetStringFromName("alertTapToSave"),
|
||||
true, "", aCallback, "downloadopen-fail");
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]);
|
||||
|
|
|
@ -55,15 +55,35 @@ browserForOpenLocation=Open Location
|
|||
|
||||
# Download Manager
|
||||
downloadsUnknownSize=Unknown size
|
||||
downloadRun=Run
|
||||
downloadSave=Save
|
||||
downloadCancel=Cancel
|
||||
downloadTryAgain=Try Again
|
||||
# LOCALIZATION NOTE (downloadsShowInFiles): Semicolon-separated list of plural forms.
|
||||
# 'Files' refers to the Windows 8 file explorer
|
||||
downloadsShowInFiles=Show download in Files;Show downloads in Files
|
||||
|
||||
# Alerts
|
||||
alertLinkBookmarked=Bookmark added
|
||||
alertDownloads=Downloads
|
||||
alertDownloadsStart=Downloading: %S
|
||||
alertDownloadsDone=%S has finished downloading
|
||||
# LOCALIZATION NOTE (alertDownloadsStart2): Used in a notification bar for download progress
|
||||
# #1 is the file name, #2 is the amount downloaded so far / total amount to download, and #3 is seconds remaining.
|
||||
alertDownloadsStart2=Downloading #1, #2, #3
|
||||
alertDownloadsDone2=%S has been downloaded
|
||||
alertTapToSave=Tap to save this file.
|
||||
alertDownloadsSize=Download too big
|
||||
alertDownloadsNoSpace=Not enough storage space
|
||||
# LOCALIZATION NOTE (alertDownloadSave): #1 is the file name, #2 is the file size, #3 is the file host
|
||||
alertDownloadSave=Do you want to run or save #1 (#2) from #3?
|
||||
# LOCALIZATION NOTE (alertDownloadMultiple): Semicolon-separated list of plural forms.
|
||||
# #1 is the number of files, #2 is (amount downloaded so far / total amount to download) and #3 is seconds remaining
|
||||
alertDownloadMultiple=Downloading one file, #2, #3; Downloading #1 files, #2, #3
|
||||
# LOCALIZATION NOTE (alertMultipleDownloadsComplete): Semicolon-separated list of plural forms.
|
||||
# #1 is the number of downloads completed
|
||||
alertMultipleDownloadsComplete=One download has been completed;#1 downloads have been completed
|
||||
alertDownloadFailed=Sorry, downloading %S failed.
|
||||
|
||||
# Popup Blocker
|
||||
# LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.
|
||||
|
|
67
configure.in
67
configure.in
|
@ -2465,6 +2465,13 @@ ia64*-hpux*)
|
|||
|
||||
esac
|
||||
|
||||
case "$target" in
|
||||
*-*linux*)
|
||||
# Includes linux-android
|
||||
AC_DEFINE(XP_LINUX)
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(MOZ_LINKER)
|
||||
if test -n "$MOZ_LINKER"; then
|
||||
AC_DEFINE(MOZ_LINKER)
|
||||
|
@ -2735,11 +2742,10 @@ else
|
|||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
dnl Check whether we can use gcc's c++0x mode
|
||||
dnl Check whether gcc's c++0x mode works
|
||||
AC_LANG_CPLUSPLUS
|
||||
|
||||
if test "$GNU_CXX"; then
|
||||
_SAVE_CXXFLAGS=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS -std=gnu++0x"
|
||||
|
||||
AC_CACHE_CHECK(for gcc c++0x headers bug without rtti,
|
||||
|
@ -2757,10 +2763,10 @@ if test "$GNU_CXX"; then
|
|||
ac_cv_cxx0x_clang_workaround="no")])
|
||||
|
||||
if test "ac_cv_cxx0x_clang_workaround" = "no"; then
|
||||
CXXFLAGS="$_SAVE_CXXFLAGS"
|
||||
AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain])
|
||||
fi
|
||||
elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then
|
||||
CXXFLAGS="$_SAVE_CXXFLAGS"
|
||||
AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -4293,7 +4299,6 @@ NS_PRINTING=1
|
|||
MOZ_PDF_PRINTING=
|
||||
MOZ_DISABLE_CRYPTOLEGACY=
|
||||
NSS_DISABLE_DBM=
|
||||
NECKO_WIFI=1
|
||||
NECKO_COOKIES=1
|
||||
NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
|
||||
USE_ARM_KUSER=
|
||||
|
@ -4323,11 +4328,6 @@ case "${target}" in
|
|||
fi
|
||||
|
||||
NSS_DISABLE_DBM=1
|
||||
if test -z "$gonkdir"; then
|
||||
NECKO_WIFI=
|
||||
else
|
||||
NECKO_WIFI=1
|
||||
fi
|
||||
MOZ_THEME_FASTSTRIPE=1
|
||||
MOZ_TREE_FREETYPE=1
|
||||
MOZ_MEMORY=1
|
||||
|
@ -4507,6 +4507,14 @@ if test -z "$gonkdir" ; then
|
|||
case "$MOZ_BUILD_APP" in
|
||||
mobile/android)
|
||||
android_min_api_level=16
|
||||
case "$target" in
|
||||
*-android*|*-linuxandroid*)
|
||||
:
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([You must specify --target=arm-linux-androideabi (or some other valid android target) when building with --enable-application=mobile/android. See https://wiki.mozilla.org/Mobile/Fennec/Android#Setup_Fennec_mozconfig for more information about the necessary options])
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -8124,6 +8132,13 @@ dnl ========================================================
|
|||
dnl Graphics checks.
|
||||
dnl ========================================================
|
||||
|
||||
if test "${OS_TARGET}" = "WINNT"; then
|
||||
if $PERL -e "exit($MOZ_WINSDK_MAXVER < 0x06020000)"; then
|
||||
MOZ_ENABLE_DIRECT2D1_1=1
|
||||
AC_SUBST(MOZ_ENABLE_DIRECT2D1_1)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "${OS_TARGET}" = "WINNT" -o "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
|
||||
case "${target_cpu}" in
|
||||
i*86*|x86_64|arm)
|
||||
|
@ -8471,28 +8486,36 @@ done
|
|||
dnl
|
||||
dnl option to disable necko's wifi scanner
|
||||
dnl
|
||||
|
||||
case "$OS_TARGET" in
|
||||
Android)
|
||||
if test -n "$gonkdir"; then
|
||||
NECKO_WIFI=1
|
||||
fi
|
||||
;;
|
||||
Darwin|SunOS|WINNT)
|
||||
NECKO_WIFI=1
|
||||
;;
|
||||
Linux)
|
||||
if test -z "$MOZ_ENABLE_DBUS"; then
|
||||
AC_MSG_ERROR([Necko WiFi scanning needs DBus on your platform, remove --disable-dbus or use --disable-necko-wifi])
|
||||
fi
|
||||
NECKO_WIFI=1
|
||||
NECKO_WIFI_DBUS=1
|
||||
;;
|
||||
esac
|
||||
|
||||
MOZ_ARG_DISABLE_BOOL(necko-wifi,
|
||||
[ --disable-necko-wifi Disable necko wifi scanner],
|
||||
NECKO_WIFI=,
|
||||
NECKO_WIFI=1)
|
||||
|
||||
if test "$OS_ARCH" = "OS2"; then
|
||||
dnl OS/2 implementation of Necko-WiFi support will be added in bug 506566
|
||||
NECKO_WIFI=
|
||||
fi
|
||||
if test "$NECKO_WIFI" -a \
|
||||
"$OS_ARCH" != "Linux" -a \
|
||||
"$OS_ARCH" != "Darwin" -a \
|
||||
"$OS_ARCH" != "SunOS" -a \
|
||||
"$OS_ARCH" != "WINNT"; then
|
||||
AC_MSG_ERROR([Necko WiFi scanning not supported on your platform, use --disable-necko-wifi])
|
||||
fi
|
||||
|
||||
if test "$NECKO_WIFI"; then
|
||||
AC_DEFINE(NECKO_WIFI)
|
||||
_NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_WIFI"
|
||||
fi
|
||||
AC_SUBST(NECKO_WIFI)
|
||||
AC_SUBST(NECKO_WIFI_DBUS)
|
||||
|
||||
dnl
|
||||
dnl option to disable cookies
|
||||
|
|
|
@ -215,6 +215,11 @@ public:
|
|||
virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE;
|
||||
virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
|
||||
virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
|
||||
virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
|
||||
virtual void SetXBLBinding(nsXBLBinding* aBinding,
|
||||
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIContent *GetXBLInsertionParent() const;
|
||||
virtual void SetXBLInsertionParent(nsIContent* aContent);
|
||||
virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
|
||||
|
||||
virtual void DestroyContent() MOZ_OVERRIDE;
|
||||
|
@ -359,6 +364,16 @@ public:
|
|||
* An object implementing the .classList property for this element.
|
||||
*/
|
||||
nsRefPtr<nsDOMTokenList> mClassList;
|
||||
|
||||
/**
|
||||
* XBL binding installed on the element.
|
||||
*/
|
||||
nsRefPtr<nsXBLBinding> mXBLBinding;
|
||||
|
||||
/**
|
||||
* XBL binding installed on the lement.
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mXBLInsertionParent;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
|
@ -18,6 +18,7 @@ class nsAttrValue;
|
|||
class nsAttrName;
|
||||
class nsTextFragment;
|
||||
class nsIFrame;
|
||||
class nsXBLBinding;
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
@ -615,6 +616,44 @@ public:
|
|||
*/
|
||||
virtual nsIContent *GetBindingParent() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the current XBL binding that is bound to this element.
|
||||
*
|
||||
* @return the current binding.
|
||||
*/
|
||||
virtual nsXBLBinding *GetXBLBinding() const = 0;
|
||||
|
||||
/**
|
||||
* Sets or unsets an XBL binding for this element. Setting a
|
||||
* binding on an element that already has a binding will remove the
|
||||
* old binding.
|
||||
*
|
||||
* @param aBinding The binding to bind to this content. If nullptr is
|
||||
* provided as the argument, then existing binding will be
|
||||
* removed.
|
||||
*
|
||||
* @param aOldBindingManager The old binding manager that contains
|
||||
* this content if this content was adopted
|
||||
* to another document.
|
||||
*/
|
||||
virtual void SetXBLBinding(nsXBLBinding* aBinding,
|
||||
nsBindingManager* aOldBindingManager = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Gets the insertion parent element of the XBL binding.
|
||||
* The insertion parent is our one true parent in the transformed DOM.
|
||||
*
|
||||
* @return the insertion parent element.
|
||||
*/
|
||||
virtual nsIContent *GetXBLInsertionParent() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the insertion parent element of the XBL binding.
|
||||
*
|
||||
* @param aContent The insertion parent element.
|
||||
*/
|
||||
virtual void SetXBLInsertionParent(nsIContent* aContent) = 0;
|
||||
|
||||
/**
|
||||
* Returns the content node that is the parent of this node in the flattened
|
||||
* tree. For nodes that are not filtered into an insertion point, this
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMComment
|
||||
// Empty interface
|
||||
|
|
|
@ -363,8 +363,7 @@ Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
|
|||
// We must ensure that the XBL Binding is installed before we hand
|
||||
// back this object.
|
||||
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
|
||||
doc->BindingManager()->GetBinding(this)) {
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) {
|
||||
// There's already a binding for this element so nothing left to
|
||||
// be done here.
|
||||
|
||||
|
@ -1719,8 +1718,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
|
||||
nsRefPtr<nsXBLBinding> binding =
|
||||
OwnerDoc()->BindingManager()->GetBinding(this);
|
||||
nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
|
||||
if (binding) {
|
||||
binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
|
||||
}
|
||||
|
@ -1903,8 +1901,7 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
|
||||
nsRefPtr<nsXBLBinding> binding =
|
||||
OwnerDoc()->BindingManager()->GetBinding(this);
|
||||
nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
|
||||
if (binding) {
|
||||
binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
|
||||
}
|
||||
|
|
|
@ -150,8 +150,7 @@ nsIContent*
|
|||
nsIContent::GetFlattenedTreeParent() const
|
||||
{
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsIContent* parent = OwnerDoc()->BindingManager()->
|
||||
GetInsertionParent(const_cast<nsIContent*>(this));
|
||||
nsIContent* parent = GetXBLInsertionParent();
|
||||
if (parent) {
|
||||
return parent;
|
||||
}
|
||||
|
@ -285,8 +284,7 @@ nsIContent::GetBaseURI() const
|
|||
if (elem->IsSVG()) {
|
||||
nsIContent* bindingParent = elem->GetBindingParent();
|
||||
if (bindingParent) {
|
||||
nsXBLBinding* binding =
|
||||
bindingParent->OwnerDoc()->BindingManager()->GetBinding(bindingParent);
|
||||
nsXBLBinding* binding = bindingParent->GetXBLBinding();
|
||||
if (binding) {
|
||||
// XXX sXBL/XBL2 issue
|
||||
// If this is an anonymous XBL element use the binding
|
||||
|
@ -566,6 +564,12 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb,
|
|||
cb.NoteXPCOMChild(mControllers);
|
||||
}
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
|
||||
cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
|
||||
cb.NoteXPCOMChild(mXBLInsertionParent.get());
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
|
||||
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
|
||||
|
||||
|
@ -584,6 +588,8 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
|
|||
}
|
||||
if (aIsXUL)
|
||||
NS_IF_RELEASE(mControllers);
|
||||
mXBLBinding = nullptr;
|
||||
mXBLInsertionParent = nullptr;
|
||||
mChildrenList = nullptr;
|
||||
mUndoManager = nullptr;
|
||||
if (mClassList) {
|
||||
|
@ -803,8 +809,7 @@ nsIContent::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
|||
// check for an anonymous parent
|
||||
// XXX XBL2/sXBL issue
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsIContent* insertionParent = OwnerDoc()->BindingManager()->
|
||||
GetInsertionParent(this);
|
||||
nsIContent* insertionParent = GetXBLInsertionParent();
|
||||
NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
|
||||
aVisitor.mEventTargetAtParent != insertionParent),
|
||||
"Retargeting and having insertion parent!");
|
||||
|
@ -889,6 +894,80 @@ FragmentOrElement::GetBindingParent() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsXBLBinding*
|
||||
FragmentOrElement::GetXBLBinding() const
|
||||
{
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsDOMSlots *slots = GetExistingDOMSlots();
|
||||
if (slots) {
|
||||
return slots->mXBLBinding;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
|
||||
nsBindingManager* aOldBindingManager)
|
||||
{
|
||||
nsBindingManager* bindingManager;
|
||||
if (aOldBindingManager) {
|
||||
MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
|
||||
"when removing a binding.");
|
||||
bindingManager = aOldBindingManager;
|
||||
} else {
|
||||
bindingManager = OwnerDoc()->BindingManager();
|
||||
}
|
||||
|
||||
// After this point, aBinding will be the most-derived binding for aContent.
|
||||
// If we already have a binding for aContent, make sure to
|
||||
// remove it from the attached stack. Otherwise we might end up firing its
|
||||
// constructor twice (if aBinding inherits from it) or firing its constructor
|
||||
// after aContent has been deleted (if aBinding is null and the content node
|
||||
// dies before we process mAttachedStack).
|
||||
nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
|
||||
if (oldBinding) {
|
||||
bindingManager->RemoveFromAttachedQueue(oldBinding);
|
||||
}
|
||||
|
||||
nsDOMSlots *slots = DOMSlots();
|
||||
if (aBinding) {
|
||||
SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
|
||||
slots->mXBLBinding = aBinding;
|
||||
bindingManager->AddBoundContent(this);
|
||||
} else {
|
||||
slots->mXBLBinding = nullptr;
|
||||
bindingManager->RemoveBoundContent(this);
|
||||
if (oldBinding) {
|
||||
oldBinding->SetBoundElement(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
FragmentOrElement::GetXBLInsertionParent() const
|
||||
{
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsDOMSlots *slots = GetExistingDOMSlots();
|
||||
if (slots) {
|
||||
return slots->mXBLInsertionParent;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
|
||||
{
|
||||
nsDOMSlots *slots = DOMSlots();
|
||||
if (aContent) {
|
||||
SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
|
||||
}
|
||||
slots->mXBLInsertionParent = aContent;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FragmentOrElement::InsertChildAt(nsIContent* aKid,
|
||||
uint32_t aIndex,
|
||||
|
@ -1387,8 +1466,7 @@ NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
|
|||
bool
|
||||
OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
|
||||
{
|
||||
return aNode->IsElement() &&
|
||||
aCurrentDoc->BindingManager()->GetBinding(aNode->AsElement());
|
||||
return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
|
||||
}
|
||||
|
||||
// CanSkip checks if aNode is black, and if it is, returns
|
||||
|
|
|
@ -1748,8 +1748,7 @@ bool
|
|||
nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent,
|
||||
JS::HandleId aId, JSPropertyDescriptor* aDesc)
|
||||
{
|
||||
nsXBLBinding* binding = aContent->OwnerDoc()->BindingManager()
|
||||
->GetBinding(aContent);
|
||||
nsXBLBinding* binding = aContent->GetXBLBinding();
|
||||
if (!binding)
|
||||
return true;
|
||||
return binding->LookupMember(aCx, aId, aDesc);
|
||||
|
@ -3568,8 +3567,7 @@ nsContentUtils::HasMutationListeners(nsINode* aNode,
|
|||
|
||||
if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
|
||||
nsIContent* content = static_cast<nsIContent*>(aNode);
|
||||
nsIContent* insertionParent =
|
||||
doc->BindingManager()->GetInsertionParent(content);
|
||||
nsIContent* insertionParent = content->GetXBLInsertionParent();
|
||||
if (insertionParent) {
|
||||
aNode = insertionParent;
|
||||
continue;
|
||||
|
|
|
@ -5418,10 +5418,7 @@ nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
|
|||
|
||||
if (aSheetSet.IsEmpty()) {
|
||||
aSheetSet = title;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
|
||||
} else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
|
||||
// Sheets from multiple sets enabled; return null string, per spec.
|
||||
SetDOMStringToNull(aSheetSet);
|
||||
return;
|
||||
|
|
|
@ -86,11 +86,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode)
|
|||
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
|
||||
}
|
||||
|
||||
nsDataSlots *slots = tmp->GetExistingDataSlots();
|
||||
if (slots) {
|
||||
slots->Traverse(cb);
|
||||
}
|
||||
|
||||
tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
|
||||
nsINode::Unlink(tmp);
|
||||
|
||||
nsDataSlots *slots = tmp->GetExistingDataSlots();
|
||||
if (slots) {
|
||||
slots->Unlink();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
|
||||
|
@ -331,6 +341,8 @@ nsGenericDOMDataNode::SetTextInternal(uint32_t aOffset, uint32_t aCount,
|
|||
delete [] to;
|
||||
}
|
||||
|
||||
UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);
|
||||
|
||||
if (document && mText.IsBidi()) {
|
||||
// If we found bidi characters in mText.SetTo() above, indicate that the
|
||||
// document contains bidi characters.
|
||||
|
@ -637,6 +649,41 @@ nsGenericDOMDataNode::GetBindingParent() const
|
|||
return slots ? slots->mBindingParent : nullptr;
|
||||
}
|
||||
|
||||
nsXBLBinding *
|
||||
nsGenericDOMDataNode::GetXBLBinding() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding,
|
||||
nsBindingManager* aOldBindingManager)
|
||||
{
|
||||
}
|
||||
|
||||
nsIContent *
|
||||
nsGenericDOMDataNode::GetXBLInsertionParent() const
|
||||
{
|
||||
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsDataSlots *slots = GetExistingDataSlots();
|
||||
if (slots) {
|
||||
return slots->mXBLInsertionParent;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::SetXBLInsertionParent(nsIContent* aContent)
|
||||
{
|
||||
nsDataSlots *slots = DataSlots();
|
||||
if (aContent) {
|
||||
SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
|
||||
}
|
||||
slots->mXBLInsertionParent = aContent;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
|
||||
{
|
||||
|
@ -682,6 +729,19 @@ nsGenericDOMDataNode::CreateSlots()
|
|||
return new nsDataSlots();
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
|
||||
cb.NoteXPCOMChild(mXBLInsertionParent.get());
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::nsDataSlots::Unlink()
|
||||
{
|
||||
mXBLInsertionParent = nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Implementation of the nsIDOMText interface
|
||||
|
@ -849,6 +909,10 @@ nsGenericDOMDataNode::TextIsOnlyWhitespace()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) {
|
||||
return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE);
|
||||
}
|
||||
|
||||
const char* cp = mText.Get1b();
|
||||
const char* end = cp + mText.GetLength();
|
||||
|
||||
|
@ -856,12 +920,15 @@ nsGenericDOMDataNode::TextIsOnlyWhitespace()
|
|||
char ch = *cp;
|
||||
|
||||
if (!dom::IsSpaceCharacter(ch)) {
|
||||
UnsetFlags(NS_TEXT_IS_ONLY_WHITESPACE);
|
||||
SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);
|
||||
return false;
|
||||
}
|
||||
|
||||
++cp;
|
||||
}
|
||||
|
||||
SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE | NS_TEXT_IS_ONLY_WHITESPACE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,11 +41,20 @@ enum {
|
|||
|
||||
// This bit is set to indicate that if the text node changes to
|
||||
// whitespace, we may need to reframe it (or its ancestors).
|
||||
NS_REFRAME_IF_WHITESPACE = DATA_NODE_FLAG_BIT(1)
|
||||
NS_REFRAME_IF_WHITESPACE = DATA_NODE_FLAG_BIT(1),
|
||||
|
||||
// This bit is set to indicate that we have a cached
|
||||
// TextIsOnlyWhitespace value
|
||||
NS_CACHED_TEXT_IS_ONLY_WHITESPACE = DATA_NODE_FLAG_BIT(2),
|
||||
|
||||
// This bit is only meaningful if the NS_CACHED_TEXT_IS_ONLY_WHITESPACE
|
||||
// bit is set, and if so it indicates whether we're only whitespace or
|
||||
// not.
|
||||
NS_TEXT_IS_ONLY_WHITESPACE = DATA_NODE_FLAG_BIT(3)
|
||||
};
|
||||
|
||||
// Make sure we have enough space for those bits
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 2);
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 4);
|
||||
|
||||
#undef DATA_NODE_FLAG_BIT
|
||||
|
||||
|
@ -141,6 +150,11 @@ public:
|
|||
#endif
|
||||
|
||||
virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
|
||||
virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
|
||||
virtual void SetXBLBinding(nsXBLBinding* aBinding,
|
||||
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIContent *GetXBLInsertionParent() const;
|
||||
virtual void SetXBLInsertionParent(nsIContent* aContent);
|
||||
virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
|
||||
virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
|
||||
|
||||
|
@ -169,7 +183,7 @@ public:
|
|||
|
||||
// WebIDL API
|
||||
// Our XPCOM GetData is just fine for WebIDL
|
||||
void SetData(const nsAString& aData, mozilla::ErrorResult& rv)
|
||||
virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv)
|
||||
{
|
||||
rv = SetData(aData);
|
||||
}
|
||||
|
@ -228,11 +242,19 @@ protected:
|
|||
{
|
||||
}
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback &cb);
|
||||
void Unlink();
|
||||
|
||||
/**
|
||||
* The nearest enclosing content node with a binding that created us.
|
||||
* @see nsIContent::GetBindingParent
|
||||
*/
|
||||
nsIContent* mBindingParent; // [Weak]
|
||||
|
||||
/**
|
||||
* @see nsIContent::GetXBLInsertionParent
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mXBLInsertionParent;
|
||||
};
|
||||
|
||||
// Override from nsINode
|
||||
|
|
|
@ -249,8 +249,7 @@ nsNodeUtils::LastRelease(nsINode* aNode)
|
|||
ownerDoc->ClearBoxObjectFor(elem);
|
||||
|
||||
NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
|
||||
!ownerDoc->BindingManager() ||
|
||||
!ownerDoc->BindingManager()->GetBinding(elem),
|
||||
!elem->GetXBLBinding(),
|
||||
"Non-forced node has binding on destruction");
|
||||
|
||||
// if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
|
||||
|
|
|
@ -48,7 +48,7 @@ nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
|
|||
|
||||
nsIContent* bindingParent = aFromContent->GetBindingParent();
|
||||
if (bindingParent) {
|
||||
nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent);
|
||||
nsXBLBinding* binding = bindingParent->GetXBLBinding();
|
||||
if (binding) {
|
||||
bool isEqualExceptRef;
|
||||
rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
|
||||
// nsIDOMCharacterData
|
||||
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
|
||||
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
|
||||
|
||||
// nsIDOMText
|
||||
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)
|
||||
|
|
|
@ -248,6 +248,7 @@ MOCHITEST_FILES_A = \
|
|||
test_bug811701.xhtml \
|
||||
test_bug820909.html \
|
||||
test_bug704063.html \
|
||||
test_bug894874.html \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_FILES_B = \
|
||||
|
@ -638,6 +639,7 @@ MOCHITEST_FILES_C= \
|
|||
test_bug890580.html \
|
||||
test_declare_stylesheet_obsolete.html \
|
||||
variable_style_sheet.sjs \
|
||||
test_processing_instruction_update_stylesheet.xhtml \
|
||||
$(NULL)
|
||||
|
||||
# OOP tests don't work on Windows (bug 763081) or native-fennec
|
||||
|
|
|
@ -27,8 +27,15 @@ function make_object()
|
|||
o.b = true;
|
||||
o.s = "hello";
|
||||
o.x = { i: 10 };
|
||||
o.f = function () { return 99; }
|
||||
o.f = function () { return 99; };
|
||||
|
||||
// Doing anything with this Proxy will throw.
|
||||
var throwing = new Proxy({}, new Proxy({}, {
|
||||
get: function (trap) { throw trap; }
|
||||
}));
|
||||
|
||||
return { "data": o,
|
||||
"throwing": throwing,
|
||||
"document": content.document
|
||||
};
|
||||
}
|
||||
|
|
|
@ -37,6 +37,39 @@
|
|||
ok(data.b === false, "boolean property");
|
||||
ok(data.s === "bye", "string property");
|
||||
ok(data.x === null, "nested property");
|
||||
|
||||
let throwing = message.objects.throwing;
|
||||
// Based on the table on:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
|
||||
let tests = [
|
||||
() => Object.getOwnPropertyDescriptor(throwing, 'test'),
|
||||
() => Object.getOwnPropertyNames(throwing),
|
||||
() => Object.defineProperty(throwing, 'test', {value: 1}),
|
||||
() => delete throwing.test,
|
||||
() => "test" in throwing,
|
||||
() => Object.prototype.hasOwnProperty.call(throwing, 'test'),
|
||||
() => throwing.test,
|
||||
() => { throwing.test = 1 },
|
||||
// () => { for (let prop in throwing) {} }, Bug 783829
|
||||
() => { for (let prop of throwing) {} },
|
||||
() => Object.keys(throwing),
|
||||
() => Function.prototype.call.call(throwing),
|
||||
() => new throwing,
|
||||
() => Object.preventExtensions(throwing),
|
||||
() => Object.freeze(throwing),
|
||||
() => Object.seal(throwing),
|
||||
]
|
||||
|
||||
for (let test of tests) {
|
||||
let threw = false;
|
||||
try {
|
||||
test()
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
ok(threw, "proxy operation threw exception");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function recvAsyncMessage(message) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=894874
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 894874</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<!-- IMPORTANT: Keep the sheets in this order! -->
|
||||
<link rel="stylesheet" type="text/css" href="data:text/css,">
|
||||
<link rel="stylesheet" type="text/css" title="one" href="data:text/css,">
|
||||
<link rel="stylesheet" type="text/css" title="two" href="data:text/css,">
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 894874 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
is(document.styleSheets[0].disabled, false,
|
||||
"Sheet with no title should be enabled");
|
||||
is(document.styleSheets[1].disabled, false,
|
||||
"First preferred sheet should be enabled");
|
||||
is(document.styleSheets[2].disabled, true,
|
||||
"Second preferred sheet should be disabled");
|
||||
is(document.selectedStyleSheetSet, "one", "Sheet one is enabled");
|
||||
document.styleSheets[0].disabled = true;
|
||||
document.styleSheets[2].disabled = false;
|
||||
ok(document.selectedStyleSheetSet === null,
|
||||
"Sheet one and sheet two are both enabled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=894874">Mozilla Bug 894874</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet href="data:text/css;charset=UTF-8,p{color:red}" type="text/css"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=888864
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 888864</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 888864 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function changeColorAndRun(callback) {
|
||||
var piNode = document.firstChild;
|
||||
piNode.data = 'href="data:text/css;charset=UTF-8,p{color:green}" type="text/css"';
|
||||
piNode.addEventListener("load", callback);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var previousColor = window.getComputedStyle(document.getElementById("display")).
|
||||
getPropertyValue("color");
|
||||
changeColorAndRun(function() {
|
||||
var afterChange = window.getComputedStyle(document.getElementById("display")).
|
||||
getPropertyValue("color");
|
||||
isnot(previousColor, afterChange,
|
||||
"Color of the p element should change.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
</head>
|
||||
<body onload="runTest();">
|
||||
<p id="display">This changes color</p>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=888864">Mozilla Bug 888864</a>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -3,6 +3,8 @@
|
|||
* 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/. */
|
||||
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
|
||||
const nsIDocumentEncoder = Components.interfaces.nsIDocumentEncoder;
|
||||
const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
|
||||
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
|
||||
var server = null;
|
||||
var server = new HttpServer();
|
||||
server.start(-1);
|
||||
|
||||
const SERVER_PORT = 4444;
|
||||
const SERVER_PORT = server.identity.primaryPort;
|
||||
const HTTP_BASE = "http://localhost:" + SERVER_PORT;
|
||||
const redirectPath = "/redirect";
|
||||
const headerCheckPath = "/headerCheck";
|
||||
|
@ -36,10 +37,8 @@ function headerCheckHandler(metadata, response) {
|
|||
}
|
||||
|
||||
function run_test() {
|
||||
var server = new HttpServer();
|
||||
server.registerPathHandler(redirectPath, redirectHandler);
|
||||
server.registerPathHandler(headerCheckPath, headerCheckHandler);
|
||||
server.start(SERVER_PORT);
|
||||
|
||||
do_test_pending();
|
||||
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
|
|
|
@ -5,7 +5,6 @@ const Cu = Components.utils;
|
|||
const Cr = Components.results;
|
||||
|
||||
Cu.import('resource://gre/modules/CSPUtils.jsm');
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
var httpserv = null;
|
||||
|
||||
|
|
|
@ -10,16 +10,14 @@ const Cr = Components.results;
|
|||
Cu.import('resource://gre/modules/CSPUtils.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
|
||||
// load the HTTP server
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
var httpServer = new HttpServer();
|
||||
httpServer.start(-1);
|
||||
var testsToFinish = 0;
|
||||
|
||||
const REPORT_SERVER_PORT = 9000;
|
||||
const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
|
||||
const REPORT_SERVER_URI = "http://localhost";
|
||||
const REPORT_SERVER_PATH = "/report";
|
||||
|
||||
var httpServer = null;
|
||||
var testsToFinish = 0;
|
||||
|
||||
/**
|
||||
* Construct a callback that listens to a report submission and either passes
|
||||
* or fails a test based on what it gets.
|
||||
|
@ -99,9 +97,6 @@ function run_test() {
|
|||
":" + REPORT_SERVER_PORT +
|
||||
"/foo/self");
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.start(REPORT_SERVER_PORT);
|
||||
|
||||
// test that inline script violations cause a report.
|
||||
makeTest(0, {"blocked-uri": "self"}, false,
|
||||
function(csp) {
|
||||
|
|
|
@ -11,13 +11,11 @@ const Cr = Components.results;
|
|||
Cu.import('resource://gre/modules/CSPUtils.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
|
||||
// load the HTTP server
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
var httpServer = new HttpServer();
|
||||
httpServer.start(-1);
|
||||
|
||||
const POLICY_FROM_URI = "default-src 'self'; img-src *";
|
||||
const POLICY_PORT = 9000;
|
||||
const POLICY_PORT = httpServer.identity.primaryPort;
|
||||
const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy";
|
||||
const POLICY_URI_RELATIVE = "/policy";
|
||||
|
||||
|
@ -1048,7 +1046,6 @@ function run_test() {
|
|||
}
|
||||
//server.registerDirectory("/", nsILocalFileForBasePath);
|
||||
httpServer.registerPathHandler("/policy", policyresponder);
|
||||
httpServer.start(POLICY_PORT);
|
||||
|
||||
for(let i in tests) {
|
||||
tests[i]();
|
||||
|
|
|
@ -8,6 +8,7 @@ tail =
|
|||
[test_csputils.js]
|
||||
[test_cspreports.js]
|
||||
[test_error_codes.js]
|
||||
run-sequentially = Hardcoded 4444 port.
|
||||
[test_thirdpartyutil.js]
|
||||
[test_xhr_standalone.js]
|
||||
[test_xmlserializer.js]
|
||||
|
|
|
@ -24,6 +24,15 @@ public:
|
|||
virtual ~WebGL1Context();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT WebGLContext
|
||||
|
||||
virtual bool IsWebGL2() const MOZ_OVERRIDE
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT nsWrapperCache
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "WebGL2Context.h"
|
||||
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CONSTRUCTOR & DESTRUCTOR
|
||||
|
||||
WebGL2Context::WebGL2Context()
|
||||
: WebGLContext()
|
||||
{
|
||||
MOZ_ASSERT(IsSupported(), "not supposed to create a WebGL2Context"
|
||||
"context when not supported");
|
||||
}
|
||||
|
||||
WebGL2Context::~WebGL2Context()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// STATIC FUNCTIONS
|
||||
|
||||
bool
|
||||
WebGL2Context::IsSupported()
|
||||
{
|
||||
#ifdef RELEASE_BUILD
|
||||
return false;
|
||||
#else
|
||||
return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
WebGL2Context*
|
||||
WebGL2Context::Create()
|
||||
{
|
||||
#ifdef RELEASE_BUILD
|
||||
return nullptr;
|
||||
#else
|
||||
return new WebGL2Context();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// IMPLEMENT nsWrapperCache
|
||||
|
||||
JSObject*
|
||||
WebGL2Context::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
|
||||
{
|
||||
return dom::WebGL2RenderingContextBinding::Wrap(cx, scope, this);
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 WEBGL2CONTEXT_H_
|
||||
#define WEBGL2CONTEXT_H_
|
||||
|
||||
#include "WebGLContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WebGL2Context
|
||||
: public WebGLContext
|
||||
{
|
||||
// -----------------------------------------------------------------------------
|
||||
// PUBLIC
|
||||
public:
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// DESTRUCTOR
|
||||
|
||||
virtual ~WebGL2Context();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// STATIC FUNCTIONS
|
||||
|
||||
static bool IsSupported();
|
||||
|
||||
static WebGL2Context* Create();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT WebGLContext
|
||||
|
||||
virtual bool IsWebGL2() const MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IMPLEMENT nsWrapperCache
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PRIVATE
|
||||
private:
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// CONSTRUCTOR
|
||||
|
||||
WebGL2Context();
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -958,6 +958,11 @@ bool WebGLContext::IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) con
|
|||
}
|
||||
}
|
||||
|
||||
return IsExtensionSupported(ext);
|
||||
}
|
||||
|
||||
bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
|
||||
{
|
||||
if (mDisableExtensions) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1123,6 +1128,19 @@ WebGLContext::GetExtension(JSContext *cx, const nsAString& aName, ErrorResult& r
|
|||
|
||||
// step 3: if the extension hadn't been previously been created, create it now, thus enabling it
|
||||
if (!IsExtensionEnabled(ext)) {
|
||||
EnableExtension(ext);
|
||||
}
|
||||
|
||||
return WebGLObjectAsJSObject(cx, mExtensions[ext].get(), rv);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::EnableExtension(WebGLExtensionID ext)
|
||||
{
|
||||
mExtensions.EnsureLengthAtLeast(ext + 1);
|
||||
|
||||
MOZ_ASSERT(IsExtensionEnabled(ext) == false);
|
||||
|
||||
WebGLExtensionBase* obj = nullptr;
|
||||
switch (ext) {
|
||||
case OES_element_index_uint:
|
||||
|
@ -1167,11 +1185,8 @@ WebGLContext::GetExtension(JSContext *cx, const nsAString& aName, ErrorResult& r
|
|||
default:
|
||||
MOZ_ASSERT(false, "should not get there.");
|
||||
}
|
||||
mExtensions.EnsureLengthAtLeast(ext + 1);
|
||||
mExtensions[ext] = obj;
|
||||
}
|
||||
|
||||
return WebGLObjectAsJSObject(cx, mExtensions[ext].get(), rv);
|
||||
mExtensions[ext] = obj;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -153,6 +153,8 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) = 0;
|
||||
|
||||
virtual bool IsWebGL2() const = 0;
|
||||
|
||||
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
|
||||
|
||||
// nsICanvasRenderingContextInternal
|
||||
|
@ -388,6 +390,7 @@ public:
|
|||
void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count);
|
||||
void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
|
||||
WebGLintptr byteOffset);
|
||||
void DrawBuffers(const dom::Sequence<GLenum>& buffers);
|
||||
void Enable(WebGLenum cap);
|
||||
void EnableVertexAttribArray(WebGLuint index);
|
||||
void Flush() {
|
||||
|
@ -892,11 +895,15 @@ protected:
|
|||
};
|
||||
nsTArray<nsRefPtr<WebGLExtensionBase> > mExtensions;
|
||||
|
||||
// enable an extension. the extension should not be enabled before.
|
||||
void EnableExtension(WebGLExtensionID ext);
|
||||
|
||||
// returns true if the extension has been enabled by calling getExtension.
|
||||
bool IsExtensionEnabled(WebGLExtensionID ext) const;
|
||||
|
||||
// returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
|
||||
bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const;
|
||||
bool IsExtensionSupported(WebGLExtensionID ext) const;
|
||||
|
||||
nsTArray<WebGLenum> mCompressedTextureFormats;
|
||||
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "WebGLContext.h"
|
||||
#include "WebGLTexture.h"
|
||||
#include "WebGLRenderbuffer.h"
|
||||
#include "WebGLFramebuffer.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
void
|
||||
WebGLContext::Clear(WebGLbitfield mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
|
||||
if (mask != m)
|
||||
return ErrorInvalidValue("clear: invalid mask bits");
|
||||
|
||||
if (mBoundFramebuffer) {
|
||||
if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers())
|
||||
return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
|
||||
|
||||
gl->fClear(mask);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, we're clearing the default framebuffer/screen.
|
||||
|
||||
bool needsClear = true;
|
||||
if (mIsScreenCleared) {
|
||||
bool isClearRedundant = true;
|
||||
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
|
||||
if (mColorClearValue[0] != 0.0f ||
|
||||
mColorClearValue[1] != 0.0f ||
|
||||
mColorClearValue[2] != 0.0f ||
|
||||
mColorClearValue[3] != 0.0f)
|
||||
{
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
|
||||
if (mDepthClearValue != 1.0f) {
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
|
||||
if (mStencilClearValue != 0) {
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isClearRedundant)
|
||||
needsClear = false;
|
||||
}
|
||||
|
||||
if (needsClear) {
|
||||
gl->fClear(mask);
|
||||
mIsScreenCleared = false;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
mShouldPresent = true;
|
||||
}
|
||||
|
||||
static WebGLclampf
|
||||
GLClampFloat(WebGLclampf val)
|
||||
{
|
||||
if (val < 0.0)
|
||||
return 0.0;
|
||||
|
||||
if (val > 1.0)
|
||||
return 1.0;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearColor(WebGLclampf r, WebGLclampf g,
|
||||
WebGLclampf b, WebGLclampf a)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mColorClearValue[0] = GLClampFloat(r);
|
||||
mColorClearValue[1] = GLClampFloat(g);
|
||||
mColorClearValue[2] = GLClampFloat(b);
|
||||
mColorClearValue[3] = GLClampFloat(a);
|
||||
gl->fClearColor(r, g, b, a);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearDepth(WebGLclampf v)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mDepthClearValue = GLClampFloat(v);
|
||||
gl->fClearDepth(v);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearStencil(WebGLint v)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mStencilClearValue = v;
|
||||
gl->fClearStencil(v);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mColorWriteMask[0] = r;
|
||||
mColorWriteMask[1] = g;
|
||||
mColorWriteMask[2] = b;
|
||||
mColorWriteMask[3] = a;
|
||||
gl->fColorMask(r, g, b, a);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DepthMask(WebGLboolean b)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mDepthWriteMask = b;
|
||||
gl->fDepthMask(b);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
|
||||
{
|
||||
const size_t buffersLength = buffers.Length();
|
||||
|
||||
if (buffersLength == 0) {
|
||||
return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers must not be empty)");
|
||||
}
|
||||
|
||||
if (mBoundFramebuffer == 0)
|
||||
{
|
||||
// OK: we are rendering in the default framebuffer
|
||||
|
||||
/* EXT_draw_buffers :
|
||||
If the GL is bound to the default framebuffer, then <buffersLength> must be 1
|
||||
and the constant must be BACK or NONE. When draw buffer zero is
|
||||
BACK, color values are written into the sole buffer for single-
|
||||
buffered contexts, or into the back buffer for double-buffered
|
||||
contexts. If DrawBuffersEXT is supplied with a constant other than
|
||||
BACK and NONE, the error INVALID_OPERATION is generated.
|
||||
*/
|
||||
if (buffersLength != 1) {
|
||||
return ErrorInvalidValue("drawBuffers: invalid <buffers> (main framebuffer: buffers.length must be 1)");
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
if (buffers[0] == LOCAL_GL_NONE) {
|
||||
const GLenum drawBuffersCommand = LOCAL_GL_NONE;
|
||||
gl->fDrawBuffers(1, &drawBuffersCommand);
|
||||
return;
|
||||
}
|
||||
else if (buffers[0] == LOCAL_GL_BACK) {
|
||||
const GLenum drawBuffersCommand = LOCAL_GL_COLOR_ATTACHMENT0;
|
||||
gl->fDrawBuffers(1, &drawBuffersCommand);
|
||||
return;
|
||||
}
|
||||
return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)");
|
||||
}
|
||||
|
||||
// OK: we are rendering in a framebuffer object
|
||||
|
||||
if (buffersLength > size_t(mGLMaxDrawBuffers)) {
|
||||
/* EXT_draw_buffers :
|
||||
The maximum number of draw buffers is implementation-dependent. The
|
||||
number of draw buffers supported can be queried by calling
|
||||
GetIntegerv with the symbolic constant MAX_DRAW_BUFFERS_EXT. An
|
||||
INVALID_VALUE error is generated if <buffersLength> is greater than
|
||||
MAX_DRAW_BUFFERS_EXT.
|
||||
*/
|
||||
return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers.length > GL_MAX_DRAW_BUFFERS)");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < buffersLength; i++)
|
||||
{
|
||||
/* EXT_draw_buffers :
|
||||
If the GL is bound to a draw framebuffer object, the <i>th buffer listed
|
||||
in <bufs> must be COLOR_ATTACHMENT<i>_EXT or NONE. Specifying a
|
||||
buffer out of order, BACK, or COLOR_ATTACHMENT<m>_EXT where <m> is
|
||||
greater than or equal to the value of MAX_COLOR_ATTACHMENTS_EXT,
|
||||
will generate the error INVALID_OPERATION.
|
||||
*/
|
||||
/* WEBGL_draw_buffers :
|
||||
The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter.
|
||||
*/
|
||||
if (buffers[i] != LOCAL_GL_NONE &&
|
||||
buffers[i] != GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + i)) {
|
||||
return ErrorInvalidOperation("drawBuffers: invalid operation (buffers[i] must be GL_NONE or GL_COLOR_ATTACHMENTi)");
|
||||
}
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fDrawBuffers(buffersLength, buffers.Elements());
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::StencilMask(WebGLuint mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
mStencilWriteMaskFront = mask;
|
||||
mStencilWriteMaskBack = mask;
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fStencilMask(mask);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::StencilMaskSeparate(WebGLenum face, WebGLuint mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (!ValidateFaceEnum(face, "stencilMaskSeparate: face"))
|
||||
return;
|
||||
|
||||
switch (face) {
|
||||
case LOCAL_GL_FRONT_AND_BACK:
|
||||
mStencilWriteMaskFront = mask;
|
||||
mStencilWriteMaskBack = mask;
|
||||
break;
|
||||
case LOCAL_GL_FRONT:
|
||||
mStencilWriteMaskFront = mask;
|
||||
break;
|
||||
case LOCAL_GL_BACK:
|
||||
mStencilWriteMaskBack = mask;
|
||||
break;
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fStencilMaskSeparate(face, mask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -227,39 +227,6 @@ WebGLContext::BindRenderbuffer(WebGLenum target, WebGLRenderbuffer *wrb)
|
|||
mBoundRenderbuffer = wrb;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::BindVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindVertexArrayObject", array))
|
||||
return;
|
||||
|
||||
if (array && array->IsDeleted()) {
|
||||
/* http://www.khronos.org/registry/gles/extensions/OES/OES_vertex_array_object.txt
|
||||
* BindVertexArrayOES fails and an INVALID_OPERATION error is
|
||||
* generated if array is not a name returned from a previous call to
|
||||
* GenVertexArraysOES, or if such a name has since been deleted with
|
||||
* DeleteVertexArraysOES
|
||||
*/
|
||||
ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
|
||||
return;
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
if (array) {
|
||||
gl->fBindVertexArray(array->GLName());
|
||||
array->SetHasEverBeenBound(true);
|
||||
mBoundVertexArray = array;
|
||||
}
|
||||
else {
|
||||
gl->fBindVertexArray(0);
|
||||
mBoundVertexArray = mDefaultVertexArray;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::BindTexture(WebGLenum target, WebGLTexture *tex)
|
||||
{
|
||||
|
@ -645,129 +612,6 @@ WebGLContext::CheckFramebufferStatus(WebGLenum target)
|
|||
return gl->fCheckFramebufferStatus(target);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::Clear(WebGLbitfield mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
|
||||
if (mask != m)
|
||||
return ErrorInvalidValue("clear: invalid mask bits");
|
||||
|
||||
if (mBoundFramebuffer) {
|
||||
if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers())
|
||||
return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
|
||||
|
||||
gl->fClear(mask);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, we're clearing the default framebuffer/screen.
|
||||
|
||||
bool needsClear = true;
|
||||
if (mIsScreenCleared) {
|
||||
bool isClearRedundant = true;
|
||||
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
|
||||
if (mColorClearValue[0] != 0.0f ||
|
||||
mColorClearValue[1] != 0.0f ||
|
||||
mColorClearValue[2] != 0.0f ||
|
||||
mColorClearValue[3] != 0.0f)
|
||||
{
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
|
||||
if (mDepthClearValue != 1.0f) {
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
|
||||
if (mStencilClearValue != 0) {
|
||||
isClearRedundant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isClearRedundant)
|
||||
needsClear = false;
|
||||
}
|
||||
|
||||
if (needsClear) {
|
||||
gl->fClear(mask);
|
||||
mIsScreenCleared = false;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
mShouldPresent = true;
|
||||
}
|
||||
|
||||
static WebGLclampf
|
||||
GLClampFloat(WebGLclampf val)
|
||||
{
|
||||
if (val < 0.0)
|
||||
return 0.0;
|
||||
|
||||
if (val > 1.0)
|
||||
return 1.0;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearColor(WebGLclampf r, WebGLclampf g,
|
||||
WebGLclampf b, WebGLclampf a)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mColorClearValue[0] = GLClampFloat(r);
|
||||
mColorClearValue[1] = GLClampFloat(g);
|
||||
mColorClearValue[2] = GLClampFloat(b);
|
||||
mColorClearValue[3] = GLClampFloat(a);
|
||||
gl->fClearColor(r, g, b, a);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearDepth(WebGLclampf v)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mDepthClearValue = GLClampFloat(v);
|
||||
gl->fClearDepth(v);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ClearStencil(WebGLint v)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mStencilClearValue = v;
|
||||
gl->fClearStencil(v);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mColorWriteMask[0] = r;
|
||||
mColorWriteMask[1] = g;
|
||||
mColorWriteMask[2] = b;
|
||||
mColorWriteMask[3] = a;
|
||||
gl->fColorMask(r, g, b, a);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::CopyTexSubImage2D_base(WebGLenum target,
|
||||
WebGLint level,
|
||||
|
@ -1165,24 +1009,6 @@ WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer *rbuf)
|
|||
rbuf->RequestDelete();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DeleteVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (array == nullptr)
|
||||
return;
|
||||
|
||||
if (array->IsDeleted())
|
||||
return;
|
||||
|
||||
if (mBoundVertexArray == array)
|
||||
BindVertexArray(static_cast<WebGLVertexArray*>(nullptr));
|
||||
|
||||
array->RequestDelete();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DeleteTexture(WebGLTexture *tex)
|
||||
{
|
||||
|
@ -1271,17 +1097,6 @@ WebGLContext::DepthFunc(WebGLenum func)
|
|||
gl->fDepthFunc(func);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DepthMask(WebGLboolean b)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
mDepthWriteMask = b;
|
||||
gl->fDepthMask(b);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DepthRange(WebGLfloat zNear, WebGLfloat zFar)
|
||||
{
|
||||
|
@ -2113,7 +1928,18 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv)
|
|||
case LOCAL_GL_RENDERER:
|
||||
return StringValue(cx, "Mozilla", rv);
|
||||
case LOCAL_GL_VERSION:
|
||||
return StringValue(cx, "WebGL 1.0", rv);
|
||||
{
|
||||
const char* version = 0;
|
||||
|
||||
if (IsWebGL2()) {
|
||||
version = "WebGL 2.0";
|
||||
} else {
|
||||
version = "WebGL 1.0";
|
||||
}
|
||||
|
||||
MOZ_ASSERT(version != 0);
|
||||
return StringValue(cx, version, rv);
|
||||
}
|
||||
case LOCAL_GL_SHADING_LANGUAGE_VERSION:
|
||||
return StringValue(cx, "WebGL GLSL ES 1.0", rv);
|
||||
|
||||
|
@ -3181,20 +3007,6 @@ WebGLContext::IsRenderbuffer(WebGLRenderbuffer *rb)
|
|||
rb->HasEverBeenBound();
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::IsVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return false;
|
||||
|
||||
if (!array)
|
||||
return false;
|
||||
|
||||
return ValidateObjectAllowDeleted("isVertexArray", array) &&
|
||||
!array->IsDeleted() &&
|
||||
array->HasEverBeenBound();
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::IsShader(WebGLShader *shader)
|
||||
{
|
||||
|
@ -3748,45 +3560,6 @@ WebGLContext::StencilFuncSeparate(WebGLenum face, WebGLenum func, WebGLint ref,
|
|||
gl->fStencilFuncSeparate(face, func, ref, mask);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::StencilMask(WebGLuint mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
mStencilWriteMaskFront = mask;
|
||||
mStencilWriteMaskBack = mask;
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fStencilMask(mask);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::StencilMaskSeparate(WebGLenum face, WebGLuint mask)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (!ValidateFaceEnum(face, "stencilMaskSeparate: face"))
|
||||
return;
|
||||
|
||||
switch (face) {
|
||||
case LOCAL_GL_FRONT_AND_BACK:
|
||||
mStencilWriteMaskFront = mask;
|
||||
mStencilWriteMaskBack = mask;
|
||||
break;
|
||||
case LOCAL_GL_FRONT:
|
||||
mStencilWriteMaskFront = mask;
|
||||
break;
|
||||
case LOCAL_GL_BACK:
|
||||
mStencilWriteMaskBack = mask;
|
||||
break;
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fStencilMaskSeparate(face, mask);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::StencilOp(WebGLenum sfail, WebGLenum dpfail, WebGLenum dppass)
|
||||
{
|
||||
|
@ -4416,22 +4189,6 @@ WebGLContext::CreateRenderbuffer()
|
|||
return globj.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLVertexArray>
|
||||
WebGLContext::CreateVertexArray()
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return nullptr;
|
||||
|
||||
nsRefPtr<WebGLVertexArray> globj = new WebGLVertexArray(this);
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fGenVertexArrays(1, &globj->mGLName);
|
||||
|
||||
mVertexArrays.insertBack(globj);
|
||||
|
||||
return globj.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,13 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIDOMWebGLRenderingContext.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
#define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; }
|
||||
|
||||
DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext)
|
||||
|
||||
WebGL2Context * WebGL2Context::Create()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -1049,6 +1049,14 @@ WebGLContext::InitAndValidateGL()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (IsWebGL2() &&
|
||||
(!IsExtensionSupported(OES_vertex_array_object) ||
|
||||
!IsExtensionSupported(WEBGL_draw_buffers)
|
||||
))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mMemoryPressureObserver
|
||||
= new WebGLMemoryPressureObserver(this);
|
||||
nsCOMPtr<nsIObserverService> observerService
|
||||
|
@ -1063,5 +1071,13 @@ WebGLContext::InitAndValidateGL()
|
|||
mDefaultVertexArray->mAttribBuffers.SetLength(mGLMaxVertexAttribs);
|
||||
mBoundVertexArray = mDefaultVertexArray;
|
||||
|
||||
if (IsWebGL2()) {
|
||||
EnableExtension(OES_vertex_array_object);
|
||||
EnableExtension(WEBGL_draw_buffers);
|
||||
|
||||
MOZ_ASSERT(IsExtensionEnabled(OES_vertex_array_object));
|
||||
MOZ_ASSERT(IsExtensionEnabled(WEBGL_draw_buffers));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "WebGLContext.h"
|
||||
#include "WebGLBuffer.h"
|
||||
#include "WebGLVertexAttribData.h"
|
||||
#include "WebGLVertexArray.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
void
|
||||
WebGLContext::BindVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindVertexArrayObject", array))
|
||||
return;
|
||||
|
||||
if (array && array->IsDeleted()) {
|
||||
/* http://www.khronos.org/registry/gles/extensions/OES/OES_vertex_array_object.txt
|
||||
* BindVertexArrayOES fails and an INVALID_OPERATION error is
|
||||
* generated if array is not a name returned from a previous call to
|
||||
* GenVertexArraysOES, or if such a name has since been deleted with
|
||||
* DeleteVertexArraysOES
|
||||
*/
|
||||
ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
|
||||
return;
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
if (array) {
|
||||
gl->fBindVertexArray(array->GLName());
|
||||
array->SetHasEverBeenBound(true);
|
||||
mBoundVertexArray = array;
|
||||
}
|
||||
else {
|
||||
gl->fBindVertexArray(0);
|
||||
mBoundVertexArray = mDefaultVertexArray;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLVertexArray>
|
||||
WebGLContext::CreateVertexArray()
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return nullptr;
|
||||
|
||||
nsRefPtr<WebGLVertexArray> globj = new WebGLVertexArray(this);
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fGenVertexArrays(1, &globj->mGLName);
|
||||
|
||||
mVertexArrays.insertBack(globj);
|
||||
|
||||
return globj.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DeleteVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return;
|
||||
|
||||
if (array == nullptr)
|
||||
return;
|
||||
|
||||
if (array->IsDeleted())
|
||||
return;
|
||||
|
||||
if (mBoundVertexArray == array)
|
||||
BindVertexArray(static_cast<WebGLVertexArray*>(nullptr));
|
||||
|
||||
array->RequestDelete();
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::IsVertexArray(WebGLVertexArray *array)
|
||||
{
|
||||
if (!IsContextStable())
|
||||
return false;
|
||||
|
||||
if (!array)
|
||||
return false;
|
||||
|
||||
return ValidateObjectAllowDeleted("isVertexArray", array) &&
|
||||
!array->IsDeleted() &&
|
||||
array->HasEverBeenBound();
|
||||
}
|
||||
|
||||
|
|
@ -48,77 +48,7 @@ WebGLExtensionDrawBuffers::~WebGLExtensionDrawBuffers()
|
|||
|
||||
void WebGLExtensionDrawBuffers::DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers)
|
||||
{
|
||||
const size_t buffersLength = buffers.Length();
|
||||
|
||||
if (buffersLength == 0) {
|
||||
return mContext->ErrorInvalidValue("drawBuffersWEBGL: invalid <buffers> (buffers must not be empty)");
|
||||
}
|
||||
|
||||
if (mContext->mBoundFramebuffer == 0)
|
||||
{
|
||||
// OK: we are rendering in the default framebuffer
|
||||
|
||||
/* EXT_draw_buffers :
|
||||
If the GL is bound to the default framebuffer, then <buffersLength> must be 1
|
||||
and the constant must be BACK or NONE. When draw buffer zero is
|
||||
BACK, color values are written into the sole buffer for single-
|
||||
buffered contexts, or into the back buffer for double-buffered
|
||||
contexts. If DrawBuffersEXT is supplied with a constant other than
|
||||
BACK and NONE, the error INVALID_OPERATION is generated.
|
||||
*/
|
||||
if (buffersLength != 1) {
|
||||
return mContext->ErrorInvalidValue("drawBuffersWEBGL: invalid <buffers> (main framebuffer: buffers.length must be 1)");
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
|
||||
if (buffers[0] == LOCAL_GL_NONE) {
|
||||
const GLenum drawBufffersCommand = LOCAL_GL_NONE;
|
||||
mContext->GL()->fDrawBuffers(1, &drawBufffersCommand);
|
||||
return;
|
||||
}
|
||||
else if (buffers[0] == LOCAL_GL_BACK) {
|
||||
const GLenum drawBufffersCommand = LOCAL_GL_COLOR_ATTACHMENT0;
|
||||
mContext->GL()->fDrawBuffers(1, &drawBufffersCommand);
|
||||
return;
|
||||
}
|
||||
return mContext->ErrorInvalidOperation("drawBuffersWEBGL: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)");
|
||||
}
|
||||
|
||||
// OK: we are rendering in a framebuffer object
|
||||
|
||||
if (buffersLength > size_t(mContext->mGLMaxDrawBuffers)) {
|
||||
/* EXT_draw_buffers :
|
||||
The maximum number of draw buffers is implementation-dependent. The
|
||||
number of draw buffers supported can be queried by calling
|
||||
GetIntegerv with the symbolic constant MAX_DRAW_BUFFERS_EXT. An
|
||||
INVALID_VALUE error is generated if <buffersLength> is greater than
|
||||
MAX_DRAW_BUFFERS_EXT.
|
||||
*/
|
||||
return mContext->ErrorInvalidValue("drawBuffersWEBGL: invalid <buffers> (buffers.length > GL_MAX_DRAW_BUFFERS)");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < buffersLength; i++)
|
||||
{
|
||||
/* EXT_draw_buffers :
|
||||
If the GL is bound to a draw framebuffer object, the <i>th buffer listed
|
||||
in <bufs> must be COLOR_ATTACHMENT<i>_EXT or NONE. Specifying a
|
||||
buffer out of order, BACK, or COLOR_ATTACHMENT<m>_EXT where <m> is
|
||||
greater than or equal to the value of MAX_COLOR_ATTACHMENTS_EXT,
|
||||
will generate the error INVALID_OPERATION.
|
||||
*/
|
||||
/* WEBGL_draw_buffers :
|
||||
The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter.
|
||||
*/
|
||||
if (buffers[i] != LOCAL_GL_NONE &&
|
||||
buffers[i] != GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + i)) {
|
||||
return mContext->ErrorInvalidOperation("drawBuffersWEBGL: invalid operation (buffers[i] must be GL_NONE or GL_COLOR_ATTACHMENTi)");
|
||||
}
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
|
||||
mContext->GL()->fDrawBuffers(buffersLength, buffers.Elements());
|
||||
mContext->DrawBuffers(buffers);
|
||||
}
|
||||
|
||||
bool WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* context)
|
||||
|
|
|
@ -44,6 +44,12 @@ void WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array)
|
|||
|
||||
bool WebGLExtensionVertexArray::IsSupported(const WebGLContext* context)
|
||||
{
|
||||
/*
|
||||
* Security leak with using Vertex Array Objects found while implementing
|
||||
* WebGL 2.0 => Temporally disabled for fixing.
|
||||
*/
|
||||
return false;
|
||||
/*
|
||||
gl::GLContext* gl = context->GL();
|
||||
|
||||
if (gl->IsGLES2()) {
|
||||
|
@ -52,6 +58,7 @@ bool WebGLExtensionVertexArray::IsSupported(const WebGLContext* context)
|
|||
|
||||
return gl->IsExtensionSupported(gl::GLContext::ARB_vertex_array_object) ||
|
||||
gl->IsExtensionSupported(gl::GLContext::APPLE_vertex_array_object);
|
||||
*/
|
||||
}
|
||||
|
||||
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionVertexArray)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define WEBGLVERTEXARRAY_H_
|
||||
|
||||
#include "WebGLObjectModel.h"
|
||||
#include "WebGLBuffer.h"
|
||||
#include "WebGLVertexAttribData.h"
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
|
|
|
@ -29,11 +29,14 @@ if CONFIG['MOZ_WEBGL']:
|
|||
'WebGLActiveInfo.cpp',
|
||||
'WebGLBuffer.cpp',
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGLContext.cpp',
|
||||
'WebGLContextGL.cpp',
|
||||
'WebGLContextUtils.cpp',
|
||||
'WebGLContextReporter.cpp',
|
||||
'WebGLContextValidate.cpp',
|
||||
'WebGLContextFramebufferOperations.cpp',
|
||||
'WebGLContextVertexArray.cpp',
|
||||
'WebGLElementArrayCache.cpp',
|
||||
'WebGLExtensionBase.cpp',
|
||||
'WebGLExtensionCompressedTextureATC.cpp',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
oes-standard-derivatives.html
|
||||
ext-texture-filter-anisotropic.html
|
||||
oes-texture-float.html
|
||||
oes-vertex-array-object.html
|
||||
webgl-debug-renderer-info.html
|
||||
webgl-debug-shaders.html
|
||||
--min-version 1.0.2 webgl-compressed-texture-s3tc.html
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "nsNetUtil.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
#include "../canvas/src/WebGL2Context.h"
|
||||
|
||||
using namespace mozilla::layers;
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
|
||||
|
@ -682,6 +684,20 @@ HTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
|
|||
ctx.forget(aContext);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (WebGL2Context::IsSupported() &&
|
||||
aContextId.EqualsLiteral("experimental-webgl2"))
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
nsRefPtr<WebGL2Context> ctx = WebGL2Context::Create();
|
||||
|
||||
if (ctx == nullptr) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ctx->SetCanvasElement(this);
|
||||
ctx.forget(aContext);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 ctxId(aContextId);
|
||||
|
||||
|
|
|
@ -213,8 +213,13 @@ bool IsValidBufferSize(uint32_t aBufferSize) {
|
|||
}
|
||||
|
||||
already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
AudioContext::CreateMediaStreamDestination()
|
||||
AudioContext::CreateMediaStreamDestination(ErrorResult& aRv)
|
||||
{
|
||||
if (mIsOffline) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaStreamAudioDestinationNode> node =
|
||||
new MediaStreamAudioDestinationNode(this);
|
||||
return node.forget();
|
||||
|
@ -401,6 +406,9 @@ void
|
|||
AudioContext::UnregisterPannerNode(PannerNode* aNode)
|
||||
{
|
||||
mPannerNodes.RemoveEntry(aNode);
|
||||
if (mListener) {
|
||||
mListener->UnregisterPannerNode(aNode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
bool aMixToMono, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
CreateMediaStreamDestination();
|
||||
CreateMediaStreamDestination(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<ScriptProcessorNode>
|
||||
CreateScriptProcessor(uint32_t aBufferSize,
|
||||
|
|
|
@ -52,6 +52,11 @@ AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
|
|||
UpdatePannersVelocity();
|
||||
}
|
||||
|
||||
void AudioListener::UnregisterPannerNode(PannerNode* aPannerNode)
|
||||
{
|
||||
mPanners.RemoveElement(aPannerNode);
|
||||
}
|
||||
|
||||
void
|
||||
AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
|
||||
{
|
||||
|
|
|
@ -127,6 +127,7 @@ public:
|
|||
}
|
||||
|
||||
void RegisterPannerNode(PannerNode* aPannerNode);
|
||||
void UnregisterPannerNode(PannerNode* aPannerNode);
|
||||
|
||||
private:
|
||||
void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
|
||||
|
|
|
@ -212,6 +212,14 @@ PannerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
|||
return PannerNodeBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void PannerNode::DestroyMediaStream()
|
||||
{
|
||||
if (Context()) {
|
||||
Context()->UnregisterPannerNode(this);
|
||||
}
|
||||
AudioNode::DestroyMediaStream();
|
||||
}
|
||||
|
||||
// Those three functions are described in the spec.
|
||||
float
|
||||
PannerNodeEngine::LinearGainFunction(float aDistance)
|
||||
|
|
|
@ -31,9 +31,12 @@ public:
|
|||
explicit PannerNode(AudioContext* aContext);
|
||||
virtual ~PannerNode();
|
||||
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
virtual void DestroyMediaStream() MOZ_OVERRIDE;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode)
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ MOCHITEST_FILES := \
|
|||
test_bug867203.html \
|
||||
test_bug875221.html \
|
||||
test_bug875402.html \
|
||||
test_bug894150.html \
|
||||
test_analyserNode.html \
|
||||
test_AudioBuffer.html \
|
||||
test_AudioContext.html \
|
||||
|
|
|
@ -22,6 +22,10 @@ addLoadEvent(function() {
|
|||
}
|
||||
}
|
||||
|
||||
expectException(function() {
|
||||
ctx.createMediaStreamDestination();
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
||||
expectException(function() {
|
||||
new OfflineAudioContext(2, 100, 0);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var ac = AudioContext();
|
||||
ac.createPanner();
|
||||
var listener = ac.listener;
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.forceCC();
|
||||
listener.setOrientation(0, 0, -1, 0, 0, 0);
|
||||
|
||||
ok(true, "No crashes should happen!");
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -11,7 +11,6 @@
|
|||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsIDOMNavigatorUserMedia.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "MediaManager.h"
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче