Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2013-07-18 15:01:47 +01:00
Родитель a65b6c2ed4 5ab829f932
Коммит 14d2681e74
507 изменённых файлов: 17354 добавлений и 9271 удалений

Просмотреть файл

@ -11,7 +11,7 @@ typedef long AccessibleTextBoundary;
interface nsIAccessible; interface nsIAccessible;
interface nsIPersistentProperties; interface nsIPersistentProperties;
[scriptable, uuid(0f4633b1-550c-4b50-8c04-0eb1005eef2f)] [scriptable, uuid(43d81eb0-1215-4dc4-9226-a4355bd2d20d)]
interface nsIAccessibleText : nsISupports interface nsIAccessibleText : nsISupports
{ {
// In parameters for character offsets: // 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_SENTENCE_END = 4; // don't use, deprecated
const AccessibleTextBoundary BOUNDARY_LINE_START = 5; const AccessibleTextBoundary BOUNDARY_LINE_START = 5;
const AccessibleTextBoundary BOUNDARY_LINE_END = 6; const AccessibleTextBoundary BOUNDARY_LINE_END = 6;
const AccessibleTextBoundary BOUNDARY_ATTRIBUTE_RANGE = 7;
/** /**
* The current current caret offset. * The current current caret offset.

Просмотреть файл

@ -300,7 +300,7 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
// In case of bound element check its anonymous subtree. // In case of bound element check its anonymous subtree.
if (!mContent->IsInAnonymousSubtree()) { if (!mContent->IsInAnonymousSubtree()) {
dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID); dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID);
if (refElm || !mContent->OwnerDoc()->BindingManager()->GetBinding(mContent)) if (refElm || !mContent->GetXBLBinding())
return refElm; return refElm;
} }
@ -318,7 +318,7 @@ IDRefsIterator::GetElem(const nsDependentSubstring& aID)
} }
// Check inside the binding of the element. // Check inside the binding of the element.
if (mContent->OwnerDoc()->BindingManager()->GetBinding(mContent)) { if (mContent->GetXBLBinding()) {
return mContent->OwnerDoc()-> return mContent->OwnerDoc()->
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID); GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID);
} }

Просмотреть файл

@ -878,186 +878,6 @@ HyperTextAccessible::FindLineBoundary(int32_t aOffset,
return -1; 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. * nsIAccessibleText impl.
*/ */
@ -1120,10 +940,6 @@ HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
*aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd); *aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText); return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default: default:
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
@ -1177,10 +993,6 @@ HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
*aEndOffset = FindLineBoundary(offset, eThisLineEnd); *aEndOffset = FindLineBoundary(offset, eThisLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText); return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAt, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default: default:
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
@ -1243,10 +1055,6 @@ HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
*aEndOffset = FindLineBoundary(offset, eNextLineEnd); *aEndOffset = FindLineBoundary(offset, eNextLineEnd);
return GetText(*aStartOffset, *aEndOffset, aText); return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAfter, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
default: default:
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }

Просмотреть файл

@ -334,22 +334,8 @@ protected:
nsSelectionAmount aAmount, nsSelectionAmount aAmount,
EWordMovementType aWordMovementType = eDefaultBehavior); 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. * by word/line/etc.
* *
* @param aPresShell the current presshell we're moving in * @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' // Keep track of message managers tha already have a 'content-script.js'
// injected. // 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 = { var Output = {
@ -472,7 +521,7 @@ var Output = {
} }
let padding = aDetails.padding; 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. // First hide it to avoid flickering when changing the style.
highlightBox.style.display = 'none'; highlightBox.style.display = 'none';
@ -536,7 +585,7 @@ var Output = {
for each (let androidEvent in aDetails) { for each (let androidEvent in aDetails) {
androidEvent.type = 'Accessibility:Event'; androidEvent.type = 'Accessibility:Event';
if (androidEvent.bounds) if (androidEvent.bounds)
androidEvent.bounds = this._adjustBounds(androidEvent.bounds, aBrowser, true); androidEvent.bounds = AccessFu.adjustContentBounds(androidEvent.bounds, aBrowser);
switch(androidEvent.eventType) { switch(androidEvent.eventType) {
case ANDROID_VIEW_TEXT_CHANGED: case ANDROID_VIEW_TEXT_CHANGED:
@ -559,33 +608,6 @@ var Output = {
Braille: function Braille(aDetails, aBrowser) { Braille: function Braille(aDetails, aBrowser) {
Logger.debug('Braille output: ' + aDetails.text); 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); this._handleKeypress(aEvent);
break; break;
case 'mozAccessFuGesture': 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); this._handleGesture(aEvent.detail);
break; break;
} }
@ -796,10 +815,10 @@ var Input = {
activateContextMenu: function activateContextMenu(aMessage) { activateContextMenu: function activateContextMenu(aMessage) {
if (Utils.MozBuildApp === 'mobile/android') { 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', Services.obs.notifyObservers(null, 'Gesture:LongPress',
JSON.stringify({x: aMessage.x / vp.zoom, JSON.stringify({x: p.x, y: p.y}));
y: aMessage.y / vp.zoom}));
} }
}, },

Просмотреть файл

@ -326,8 +326,8 @@ this.TouchAdapter = {
* of one single touch. * of one single touch.
*/ */
function TouchPoint(aTouch, aTime, aDPI) { function TouchPoint(aTouch, aTime, aDPI) {
this.startX = this.x = aTouch.screenX; this.startX = this.x = aTouch.screenX * this.scaleFactor;
this.startY = this.y = aTouch.screenY; this.startY = this.y = aTouch.screenY * this.scaleFactor;
this.startTime = aTime; this.startTime = aTime;
this.distanceTraveled = 0; this.distanceTraveled = 0;
this.dpi = aDPI; this.dpi = aDPI;
@ -338,8 +338,8 @@ TouchPoint.prototype = {
update: function TouchPoint_update(aTouch, aTime) { update: function TouchPoint_update(aTouch, aTime) {
let lastX = this.x; let lastX = this.x;
let lastY = this.y; let lastY = this.y;
this.x = aTouch.screenX; this.x = aTouch.screenX * this.scaleFactor;
this.y = aTouch.screenY; this.y = aTouch.screenY * this.scaleFactor;
this.time = aTime; this.time = aTime;
this.distanceTraveled += this.getDistanceToCoord(lastX, lastY); 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)); 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() { finish: function TouchPoint_finish() {
this.done = true; this.done = true;
}, },

Просмотреть файл

@ -221,11 +221,6 @@ this.Utils = {
return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor; return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
}, },
getPixelsPerCSSPixel: function getPixelsPerCSSPixel(aWindow) {
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
},
getBounds: function getBounds(aAccessible) { getBounds: function getBounds(aAccessible) {
let objX = {}, objY = {}, objW = {}, objH = {}; let objX = {}, objY = {}, objW = {}, objH = {};
aAccessible.getBounds(objX, objY, objW, objH); aAccessible.getBounds(objX, objY, objW, objH);

Просмотреть файл

@ -97,10 +97,8 @@ function moveToPoint(aMessage) {
let rule = TraversalRules[details.rule]; let rule = TraversalRules[details.rule];
try { try {
if (!this._ppcp) { let dpr = content.devicePixelRatio;
this._ppcp = Utils.getPixelsPerCSSPixel(content); vc.moveToPoint(rule, details.x * dpr, details.y * dpr, true);
}
vc.moveToPoint(rule, details.x * this._ppcp, details.y * this._ppcp, true);
forwardToChild(aMessage, moveToPoint, vc.position); forwardToChild(aMessage, moveToPoint, vc.position);
} catch (x) { } catch (x) {
Logger.logException(x, 'Failed move to point'); Logger.logException(x, 'Failed move to point');
@ -219,9 +217,7 @@ function activateCurrent(aMessage) {
function activateContextMenu(aMessage) { function activateContextMenu(aMessage) {
function sendContextMenuCoordinates(aAccessible) { function sendContextMenuCoordinates(aAccessible) {
let bounds = Utils.getBounds(aAccessible); let bounds = Utils.getBounds(aAccessible);
sendAsyncMessage('AccessFu:ActivateContextMenu', sendAsyncMessage('AccessFu:ActivateContextMenu', {bounds: bounds});
{ x: bounds.left + bounds.width / 2,
y: bounds.top + bounds.height / 2 });
} }
let position = Utils.getVirtualCursor(content.document).position; 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_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END;
const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START; const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START;
const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END; 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 kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT;
const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET; const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET;
@ -585,7 +584,5 @@ function boundaryToString(aBoundaryType)
return "line start"; return "line start";
case BOUNDARY_LINE_END: case BOUNDARY_LINE_END:
return "line end"; return "line end";
case BOUNDARY_ATTRIBUTE_RANGE:
return "attr range";
} }
} }

Просмотреть файл

@ -1,4 +1,5 @@
. "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux32"
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging ac_add_options --enable-update-packaging

Просмотреть файл

@ -1,4 +1,5 @@
. "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux"
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging ac_add_options --enable-update-packaging

Просмотреть файл

@ -175,7 +175,6 @@
#ifdef MOZ_B2G_FM #ifdef MOZ_B2G_FM
@BINPATH@/components/dom_fm.xpt @BINPATH@/components/dom_fm.xpt
#endif #endif
@BINPATH@/components/dom_battery.xpt
#ifdef MOZ_B2G_BT #ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt @BINPATH@/components/dom_bluetooth.xpt
#endif #endif

Просмотреть файл

@ -571,7 +571,7 @@ HistoryMenu.prototype = {
m.setAttribute("label", strings.getString("menuRestoreAllTabs.label")); m.setAttribute("label", strings.getString("menuRestoreAllTabs.label"));
m.addEventListener("command", function() { m.addEventListener("command", function() {
for (var i = 0; i < undoItems.length; i++) for (var i = 0; i < undoItems.length; i++)
undoCloseTab(); undoCloseTab(0);
}, false); }, false);
}, },

Просмотреть файл

@ -4432,6 +4432,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function (aWindow) { isTabContentWindow: function (aWindow) {
return gBrowser.browsers.some(function (browser) browser.contentWindow == 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 tab = null;
var ss = Cc["@mozilla.org/browser/sessionstore;1"]. var ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore); getService(Ci.nsISessionStore);
if (ss.getClosedTabCount(window) > (aIndex || 0)) { let numberOfTabsToUndoClose = 0;
TabView.prepareUndoCloseTab(blankTabToRemove); if (Number.isInteger(aIndex)) {
tab = ss.undoCloseTab(window, aIndex || 0); if (ss.getClosedTabCount(window) > aIndex) {
TabView.afterUndoCloseTab(); numberOfTabsToUndoClose = 1;
} else {
if (blankTabToRemove) return tab;
gBrowser.removeTab(blankTabToRemove); }
} 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; return tab;
} }
@ -7061,10 +7081,15 @@ var TabContextMenu = {
menuItem.disabled = disabled; menuItem.disabled = disabled;
// Session store // Session store
document.getElementById("context_undoCloseTab").disabled = let ss = Cc["@mozilla.org/browser/sessionstore;1"].
Cc["@mozilla.org/browser/sessionstore;1"]. getService(Ci.nsISessionStore);
getService(Ci.nsISessionStore). let undoCloseTabElement = document.getElementById("context_undoCloseTab");
getClosedTabCount(window) == 0; 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 // Only one of pin/unpin should be visible
document.getElementById("context_pinTab").hidden = this.contextTab.pinned; document.getElementById("context_pinTab").hidden = this.contextTab.pinned;

Просмотреть файл

@ -103,7 +103,8 @@
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/> oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
<menuseparator/> <menuseparator/>
<menuitem id="context_undoCloseTab" <menuitem id="context_undoCloseTab"
label="&undoCloseTab.label;" singletablabel="&undoCloseTab.label;"
multipletablabel="&undoCloseTabs.label;"
accesskey="&undoCloseTab.accesskey;" accesskey="&undoCloseTab.accesskey;"
observes="History:UndoCloseTab"/> observes="History:UndoCloseTab"/>
<menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;" <menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"

Просмотреть файл

@ -1565,13 +1565,9 @@
throw new Error("Invalid argument: " + aCloseTabs); throw new Error("Invalid argument: " + aCloseTabs);
} }
if (tabsToClose <= 1) if (tabsToClose <= 1 ||
return true; aCloseTabs != this.closingTabsEnum.ALL ||
!Services.prefs.getBoolPref("browser.tabs.warnOnClose"))
const pref = aCloseTabs == this.closingTabsEnum.ALL ?
"browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
var shouldPrompt = Services.prefs.getBoolPref(pref);
if (!shouldPrompt)
return true; return true;
var ps = Services.prompt; var ps = Services.prompt;
@ -1595,13 +1591,12 @@
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1), + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
bundle.getString("tabs.closeButtonMultiple"), bundle.getString("tabs.closeButtonMultiple"),
null, null, null, null,
aCloseTabs == this.closingTabsEnum.ALL ? bundle.getString("tabs.closeWarningPromptMe"),
bundle.getString("tabs.closeWarningPromptMe") : null,
warnOnClose); warnOnClose);
var reallyClose = (buttonPressed == 0); var reallyClose = (buttonPressed == 0);
// don't set the pref unless they press OK and it's false // 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); Services.prefs.setBoolPref(pref, false);
return reallyClose; return reallyClose;
@ -1629,9 +1624,13 @@
<![CDATA[ <![CDATA[
if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) { if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
let tabs = this.getTabsToTheEndFrom(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}); this.removeTab(tabs[i], {animate: true});
} }
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
ss.setNumberOfTabsClosedLast(window, numberOfTabsToClose);
} }
]]> ]]>
</body> </body>
@ -1648,11 +1647,17 @@
let tabs = this.visibleTabs; let tabs = this.visibleTabs;
this.selectedTab = aTab; this.selectedTab = aTab;
let closedTabs = 0;
for (let i = tabs.length - 1; i >= 0; --i) { 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]); this.removeTab(tabs[i]);
closedTabs++;
} }
} }
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
ss.setNumberOfTabsClosedLast(window, closedTabs);
}
]]> ]]>
</body> </body>
</method> </method>
@ -1680,6 +1685,10 @@
var byMouse = aParams.byMouse; 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 // Handle requests for synchronously removing an already
// asynchronously closing tab. // asynchronously closing tab.
if (!animate && if (!animate &&

Просмотреть файл

@ -184,6 +184,7 @@ MOCHITEST_BROWSER_FILES = \
browser_bug832435.js \ browser_bug832435.js \
browser_bug839103.js \ browser_bug839103.js \
browser_bug882977.js \ browser_bug882977.js \
browser_bug887515.js \
browser_canonizeURL.js \ browser_canonizeURL.js \
browser_clearplugindata_noage.html \ browser_clearplugindata_noage.html \
browser_clearplugindata.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|. * |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 interface nsISessionStore : nsISupports
{ {
/** /**
@ -105,6 +105,20 @@ interface nsISessionStore : nsISupports
nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab, nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab,
[optional] in long aDelta); [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 * Get the number of restore-able tabs for a browser window
*/ */

Просмотреть файл

@ -170,6 +170,14 @@ this.SessionStore = {
return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta); 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) { getClosedTabCount: function ss_getClosedTabCount(aWindow) {
return SessionStoreInternal.getClosedTabCount(aWindow); return SessionStoreInternal.getClosedTabCount(aWindow);
}, },
@ -1529,6 +1537,28 @@ let SessionStoreInternal = {
return newTab; 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) { getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
if ("__SSi" in aWindow) { if ("__SSi" in aWindow) {
return this._windows[aWindow.__SSi]._closedTabs.length; 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 // 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 // attributes when collecting tab data and will re-set those attributes when
// the given tab data is restored to a new tab. // the given tab data is restored to a new tab.

Просмотреть файл

@ -40,4 +40,8 @@ function test() {
"Invalid window for getWindowValue throws"); "Invalid window for getWindowValue throws");
ok(test(function() ss.setWindowValue({}, "", "")), ok(test(function() ss.setWindowValue({}, "", "")),
"Invalid window for setWindowValue throws"); "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); gBrowser.removeTab(tabTwo);
finish(); finish();
}); });
}); }, 0);
} }

Просмотреть файл

@ -64,7 +64,7 @@ function test() {
createBlankTab(); createBlankTab();
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab); afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab);
}); }, 0);
}); });
} }
@ -94,7 +94,7 @@ function test() {
gBrowser.removeTab(gBrowser.tabs[0]); gBrowser.removeTab(gBrowser.tabs[0]);
afterAllTabsLoaded(finishTest); afterAllTabsLoaded(finishTest);
}); }, 0);
}); });
} }

Просмотреть файл

@ -81,7 +81,7 @@ function test() {
gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]);
gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(finishTest); hideTabView(finishTest);
}); }, 0);
} }
waitForExplicitFinish(); waitForExplicitFinish();

Просмотреть файл

@ -20,7 +20,7 @@ function test() {
whenTabViewIsHidden(function() { whenTabViewIsHidden(function() {
win.gBrowser.removeTab(win.gBrowser.selectedTab); win.gBrowser.removeTab(win.gBrowser.selectedTab);
executeSoon(function() { executeSoon(function() {
win.undoCloseTab(); win.undoCloseTab(0);
groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) { groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) {
groupItemTwo.removeSubscriber("childAdded", onChildAdded); groupItemTwo.removeSubscriber("childAdded", onChildAdded);

Просмотреть файл

@ -359,7 +359,7 @@ function newWindowWithState(state, callback) {
function restoreTab(callback, index, win) { function restoreTab(callback, index, win) {
win = win || window; win = win || window;
let tab = win.undoCloseTab(index || 0); let tab = win.undoCloseTab(index);
let tabItem = tab._tabViewTabItem; let tabItem = tab._tabViewTabItem;
let finalize = function () { let finalize = function () {

Просмотреть файл

@ -2167,3 +2167,49 @@ gcli.addCommand({
} }
}); });
}(this)); }(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_cookie.js \
browser_cmd_jsb.js \ browser_cmd_jsb.js \
browser_cmd_jsb_script.jsi \ browser_cmd_jsb_script.jsi \
browser_cmd_media.html \
browser_cmd_media.js \
browser_cmd_pagemod_export.html \ browser_cmd_pagemod_export.html \
browser_cmd_pagemod_export.js \ browser_cmd_pagemod_export.js \
browser_cmd_pref.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_icc.xpt
@BINPATH@/components/dom_wappush.xpt @BINPATH@/components/dom_wappush.xpt
#endif #endif
@BINPATH@/components/dom_battery.xpt
#ifdef MOZ_B2G_BT #ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt @BINPATH@/components/dom_bluetooth.xpt
#endif #endif

Просмотреть файл

@ -46,6 +46,11 @@ can reach it easily. -->
<!ENTITY bookmarkAllTabs.accesskey "T"> <!ENTITY bookmarkAllTabs.accesskey "T">
<!ENTITY undoCloseTab.label "Undo Close Tab"> <!ENTITY undoCloseTab.label "Undo Close Tab">
<!ENTITY undoCloseTab.accesskey "U"> <!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.label "Close Tab">
<!ENTITY closeTab.accesskey "c"> <!ENTITY closeTab.accesskey "c">

Просмотреть файл

@ -1282,3 +1282,13 @@ listenInitOutput=Listening on port %1$S
# LOCALIZATION NOTE (listenNoInitOutput) Text of a message output during the # LOCALIZATION NOTE (listenNoInitOutput) Text of a message output during the
# execution of the 'listen' command. # execution of the 'listen' command.
listenNoInitOutput=DebuggerServer not initialized 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 * URIs and schemes
*/ */

Просмотреть файл

@ -71,7 +71,8 @@ var Appbar = {
}, },
onDownloadButton: function() { onDownloadButton: function() {
PanelUI.show("downloads-container"); // TODO: Bug 883962: Toggle the downloads infobar when the
// download button is clicked
ContextUI.dismiss(); 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"], ["Downloads", "chrome://browser/content/downloads.js"],
["BookmarksPanelView", "chrome://browser/content/bookmarks.js"], ["BookmarksPanelView", "chrome://browser/content/bookmarks.js"],
["ConsolePanelView", "chrome://browser/content/console.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"], ["BookmarksStartView", "chrome://browser/content/bookmarks.js"],
["HistoryView", "chrome://browser/content/history.js"], ["HistoryView", "chrome://browser/content/history.js"],
["HistoryStartView", "chrome://browser/content/history.js"], ["HistoryStartView", "chrome://browser/content/history.js"],

Просмотреть файл

@ -1219,7 +1219,6 @@ var PanelUI = {
views: { views: {
"bookmarks-container": "BookmarksPanelView", "bookmarks-container": "BookmarksPanelView",
"downloads-container": "DownloadsPanelView",
"console-container": "ConsolePanelView", "console-container": "ConsolePanelView",
"remotetabs-container": "RemoteTabsPanelView", "remotetabs-container": "RemoteTabsPanelView",
"history-container" : "HistoryPanelView" "history-container" : "HistoryPanelView"

Просмотреть файл

@ -156,27 +156,6 @@ richlistitem {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#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="error"],
richlistitem[type="warning"] { richlistitem[type="warning"] {
-moz-binding: url("chrome://browser/content/bindings/console.xml#error"); -moz-binding: url("chrome://browser/content/bindings/console.xml#error");

Просмотреть файл

@ -1205,6 +1205,10 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function(aWindow) { isTabContentWindow: function(aWindow) {
return Browser.browsers.some(function (browser) browser.contentWindow == 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) { function showDownloadManager(aWindowContext, aID, aReason) {
PanelUI.show("downloads-container"); // TODO: Bug 883962: Toggle the downloads infobar as our current "download manager".
// TODO: select the download with aID
} }
function Tab(aURI, aParams, aOwner) { function Tab(aURI, aParams, aOwner) {

Просмотреть файл

@ -320,9 +320,6 @@
<scrollbox id="history-container" flex="1"> <scrollbox id="history-container" flex="1">
<richgrid id="history-list" noun="history" class="canSnapTiles" seltype="multiple" flex="1"/> <richgrid id="history-list" noun="history" class="canSnapTiles" seltype="multiple" flex="1"/>
</scrollbox> </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"> <scrollbox id="remotetabs-container" flex="1">
<richgrid id="remotetabs-list" noun="tab" class="canSnapTiles" seltype="single" flex="1"/> <richgrid id="remotetabs-list" noun="tab" class="canSnapTiles" seltype="single" flex="1"/>
</scrollbox> </scrollbox>

Просмотреть файл

@ -6,8 +6,19 @@
const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png"; const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
var Downloads = { 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, _inited: false,
_progressAlert: null, _progressAlert: null,
_lastSec: Infinity,
_notificationBox: null,
_progressNotification: null,
_progressNotificationInfo: new Map(),
_runDownloadBooleanMap: new Map(),
get manager() { get manager() {
return Cc["@mozilla.org/download-manager;1"] 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-start", true);
Services.obs.addObserver(this, "dl-done", 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() { uninit: function dh_uninit() {
if (this._inited) { if (this._inited) {
Services.obs.removeObserver(this, "dl-start"); Services.obs.removeObserver(this, "dl-start");
Services.obs.removeObserver(this, "dl-done"); Services.obs.removeObserver(this, "dl-done");
Services.obs.removeObserver(this, "dl-run");
Services.obs.removeObserver(this, "dl-failed");
} }
}, },
openDownload: function dh_openDownload(aDownload) { openDownload: function dh_openDownload(aDownload) {
// expects xul item let fileURI = aDownload.target
let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id);
let fileURI = download.target;
if (!(fileURI && fileURI.spec)) { if (!(fileURI && fileURI.spec)) {
Util.dumpLn("Cant open download "+id+", fileURI is invalid"); Util.dumpLn("Cant open download "+id+", fileURI is invalid");
@ -61,7 +78,7 @@ var Downloads = {
let file = this._getLocalFile(fileURI); let file = this._getLocalFile(fileURI);
try { try {
file && file.launch(); file && MetroUtils.launchInDesktop(aDownload.target.spec, "");
} catch (ex) { } catch (ex) {
Util.dumpLn("Failed to open download, with id: "+id+", download target URI spec: " + fileURI.spec); 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)); Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
@ -75,33 +92,46 @@ var Downloads = {
let download = this.manager.getDownload(id); let download = this.manager.getDownload(id);
if (download) { if (download) {
// TODO <sfoster> add class/mark element for removal transition?
this.manager.removeDownload(id); this.manager.removeDownload(id);
} }
}, },
cancelDownload: function dh_cancelDownload(aDownload) { cancelDownload: function dh_cancelDownload(aDownload) {
let id = aDownload.getAttribute("downloadId"); this._progressNotificationInfo.delete(aDownload.guid);
let download = this.manager.getDownload(id); this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
this.manager.cancelDownload(id); this._downloadCount--;
if (this._progressNotificationInfo.size == 0) {
let fileURI = download.target; this._notificationBox.removeNotification(this._progressNotification);
this._progressNotification = null;
}
let fileURI = aDownload.target;
if (!(fileURI && fileURI.spec)) { 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; return;
} }
let file = this._getLocalFile(fileURI); let file = this._getLocalFile(fileURI);
try { try {
this.manager.cancelDownload(aDownload.id);
if (file && file.exists()) if (file && file.exists())
file.remove(false); file.remove(false);
} catch (ex) { } 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)); 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) { pauseDownload: function dh_pauseDownload(aDownload) {
let id = aDownload.getAttribute("downloadId"); let id = aDownload.getAttribute("downloadId");
this.manager.pauseDownload(id); this.manager.pauseDownload(id);
@ -112,11 +142,6 @@ var Downloads = {
this.manager.resumeDownload(id); this.manager.resumeDownload(id);
}, },
retryDownload: function dh_retryDownload(aDownload) {
let id = aDownload.getAttribute("downloadId");
this.manager.retryDownload(id);
},
showPage: function dh_showPage(aDownload) { showPage: function dh_showPage(aDownload) {
let id = aDownload.getAttribute("downloadId"); let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id); let download = this.manager.getDownload(id);
@ -132,8 +157,9 @@ var Downloads = {
// Callback for tapping on the alert popup // Callback for tapping on the alert popup
let observer = { let observer = {
observe: function (aSubject, aTopic, aData) { observe: function (aSubject, aTopic, aData) {
if (aTopic == "alertclickcallback") if (aTopic == "alertclickcallback") {
PanelUI.show("downloads-container"); // TODO: Bug 783232 turns this alert into a native toast.
}
} }
}; };
@ -146,27 +172,207 @@ var Downloads = {
notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer, aName); 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) { observe: function (aSubject, aTopic, aData) {
let download = aSubject.QueryInterface(Ci.nsIDownload); let message = "";
let msgKey = ""; let msgTitle = "";
switch (aTopic) { switch (aTopic) {
case "dl-run":
let file = aSubject.QueryInterface(Ci.nsIFile);
this._runDownloadBooleanMap.set(file.path, (aData == 'true'));
break;
case "dl-start": 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) { if (!this._progressAlert) {
this._progressAlert = new AlertDownloadProgressListener(); this._progressAlert = new AlertDownloadProgressListener();
this.manager.addListener(this._progressAlert); this.manager.addListener(this._progressAlert);
} }
this.updateInfobar(download);
break; break;
case "dl-done": case "dl-done":
msgKey = "alertDownloadsDone"; download = aSubject.QueryInterface(Ci.nsIDownload);
break; let runAfterDownload = this._runDownloadBooleanMap.get(download.targetFile.path);
if (runAfterDownload) {
this.openDownload(download);
} }
if (msgKey) { this._progressNotificationInfo.delete(download.guid);
let message = Strings.browser.formatStringFromName(msgKey, [download.displayName], 1); this._runDownloadBooleanMap.delete(download.targetFile.path);
let url = download.target.spec.replace("file:", "download:"); if (this._progressNotificationInfo.size == 0) {
this.showAlert(url, message); 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) { function DownloadProgressListener(aDownloads) {
this._view = aView; this._downloads = aDownloads;
} }
DownloadProgressListener.prototype = { DownloadProgressListener.prototype = {
_view: null, _downloads: null,
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
//// nsIDownloadProgressListener //// nsIDownloadProgressListener
onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) { onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) {
let state = aDownload.state; // TODO: Use DownloadProgressListener instead of observers in the Downloads object.
switch (state) { this._downloads.updateDownload(aDownload);
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);
}, },
onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) { onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
// TODO <jwilde>: Add more detailed progress information. // TODO <jwilde>: Add more detailed progress information.
this._view.updateDownload(aDownload); this._downloads.updateDownload(aDownload);
}, },
onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { }, onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
@ -483,7 +442,7 @@ AlertDownloadProgressListener.prototype = {
Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"), Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"),
strings.GetStringFromName("alertDownloadsNoSpace"), strings.GetStringFromName("alertDownloadsNoSpace"),
strings.GetStringFromName("alertDownloadsSize")); 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/toggleswitch.xml (content/bindings/toggleswitch.xml)
content/bindings/browser.xml (content/bindings/browser.xml) content/bindings/browser.xml (content/bindings/browser.xml)
content/bindings/browser.js (content/bindings/browser.js) 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/console.xml (content/bindings/console.xml)
content/bindings/dialog.xml (content/bindings/dialog.xml) content/bindings/dialog.xml (content/bindings/dialog.xml)
content/bindings/arrowbox.xml (content/bindings/arrowbox.xml) content/bindings/arrowbox.xml (content/bindings/arrowbox.xml)

Просмотреть файл

@ -112,8 +112,6 @@ let gDownloadRowTemplate = {
// Test Infrastructure // Test Infrastructure
function test() { function test() {
DownloadsPanelView._view.clearDownloads();
PanelUI.show("downloads-container");
runTests(); runTests();
} }
@ -212,43 +210,11 @@ function gen_addDownloadRows(aDataRows){
///////////////////////////////////// /////////////////////////////////////
// Test implementations // 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({ gTests.push({
desc: "zero downloads", desc: "zero downloads",
run: function () { run: function () {
yield resetDownloads(); yield resetDownloads();
todo(false, "Test there are no visible notifications with an empty db.");
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");
} }
}); });
@ -276,74 +242,30 @@ gTests.push({
yield resetDownloads(); 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 { try {
// Populate the downloads database with the data required by this test. // Populate the downloads database with the data required by this test.
// we're going to add stuff to the downloads db. // we're going to add stuff to the downloads db.
yield spawn( gen_addDownloadRows( DownloadData ) ); yield spawn( gen_addDownloadRows( DownloadData ) );
// tickle the view to cause it to refresh itself todo( false, "Check that Downloads._progressNotificationInfo and Downloads._downloadCount \
DownloadsPanelView._view.getDownloads(); have the correct length (DownloadData.length) \
May also test that the correct notifications show up for various states.");
yield isReady; todo(false, "Iterate through download objects in Downloads._progressNotificationInfo \
and confirm that the downloads they refer to are the same as those in \
if (!isReady || isReady instanceof Error){ DownloadData.");
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");
}
}
} catch(e) { } catch(e) {
info("Show downloads, some error: " + e); info("Show downloads, some error: " + e);
} }
finally { finally {
// Clean up when the test finishes. // Clean up when the test finishes.
DownloadsPanelView._view.clearDownloads();
yield resetDownloads(); 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({ gTests.push({
desc: "Remove downloads", desc: "Remove downloads",
@ -356,26 +278,11 @@ gTests.push({
]; ];
yield resetDownloads(); yield resetDownloads();
DownloadsPanelView._view.getDownloads();
try { try {
// Populate the downloads database with the data required by this test. // Populate the downloads database with the data required by this test.
yield spawn( gen_addDownloadRows( DownloadData ) ); 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, let downloadRows = null,
promisedDownloads; promisedDownloads;
// get all the downloads from the db // 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"); is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
// remove the first one todo(false, "Get some download from Downloads._progressNotificationInfo, \
let itemNode = downloadsList.children[0]; confirm that its file exists, then remove it.");
let id = itemNode.getAttribute("downloadId");
// check the file exists
let download = Downloads.manager.getDownload( id );
let file = download.targetFile;
ok(file && file.exists());
Downloads.manager.removeDownload( id );
// remove is async(?), wait a bit // remove is async(?), wait a bit
yield waitForMs(0); yield waitForMs(0);
@ -417,18 +318,14 @@ gTests.push({
}); });
yield promisedDownloads; yield promisedDownloads;
is(downloadRows.length, 2, "Correct number of downloads in the db after removal"); todo(false, "confirm that the removed download is no longer in the database \
and its file no longer exists.");
is(2, downloadsList.children.length,
"Removing a download updates the items list");
ok(file && file.exists(), "File still exists after download removal");
} catch(e) { } catch(e) {
info("Remove downloads, some error: " + e); info("Remove downloads, some error: " + e);
} }
finally { finally {
// Clean up when the test finishes. // Clean up when the test finishes.
DownloadsPanelView._view.clearDownloads();
yield resetDownloads(); yield resetDownloads();
} }
} }

Просмотреть файл

@ -5,17 +5,8 @@
<body> <body>
<form action="texarea.html"> <form action="texarea.html">
<div style="margin-left: 250px;"> <div style="margin-left: 250px;">
<textarea wrap="on" style="width:800px; height:100px; overflow:scroll;" id="inputtext"> <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 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>
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>
</div> </div>
</body> </body>
</html> </html>

Просмотреть файл

@ -48,41 +48,7 @@ gTests.push({
}); });
gTests.push({ gTests.push({
desc: "textarea basic selection", desc: "textarea selection and drag",
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",
setUp: setUpAndTearDown, setUp: setUpAndTearDown,
tearDown: setUpAndTearDown, tearDown: setUpAndTearDown,
run: function test() { run: function test() {
@ -90,9 +56,10 @@ gTests.push({
yield waitForMs(100); yield waitForMs(100);
let textarea = gWindow.document.getElementById("inputtext"); let textarea = gWindow.document.getElementById("inputtext");
textarea.focus();
let promise = waitForEvent(document, "popupshown"); let promise = waitForEvent(document, "popupshown");
sendContextMenuClick(355, 50); sendContextMenuClickToElement(gWindow, textarea, 20, 10);
yield promise; yield promise;
checkContextUIMenuItemVisibility(["context-select", checkContextUIMenuItemVisibility(["context-select",
@ -111,7 +78,7 @@ gTests.push({
}, kCommonWaitMs, kCommonPollMs); }, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active"); 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 xpos = SelectionHelperUI.endMark.xPos;
let ypos = SelectionHelperUI.endMark.yPos + 10; let ypos = SelectionHelperUI.endMark.yPos + 10;
@ -120,15 +87,15 @@ gTests.push({
// end marker and off the text area to the right // end marker and off the text area to the right
yield touchdrag.start(gWindow, xpos, ypos, 1200, 400); yield touchdrag.start(gWindow, xpos, ypos, 1200, 400);
let textLength = getTrimmedSelection(textarea).toString().length; let token = "(end)";
yield waitForCondition(function () { yield waitForCondition(function () {
let newTextLength = getTrimmedSelection(textarea).toString().length; let selection = getTrimmedSelection(textarea).toString();
if (textLength != newTextLength) { if (selection.length < token.length ||
textLength = newTextLength; selection.substring(selection.length - token.length) != token) {
return false; return false;
} }
return true; return true;
}, 45000, 1000); }, 5000, 100);
touchdrag.end(); touchdrag.end();
touchdrag = null; touchdrag = null;
@ -137,10 +104,6 @@ gTests.push({
return !SelectionHelperUI.hasActiveDrag; return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs); }, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler(); 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 updateService = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
var updateTimerCallback = updateService.QueryInterface(Ci.nsITimerCallback); var updateTimerCallback = updateService.QueryInterface(Ci.nsITimerCallback);
updateTimerCallback.notify(null); 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() { get visible() {
let browser = Services.wm.getMostRecentWindow("navigator:browser"); // For now there is no Download Manager UI.
if (browser) {
return browser.DownloadsView.visible;
}
return false; 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/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DownloadUtils.jsm");
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// HelperApp Launcher Dialog // HelperApp Launcher Dialog
@ -32,23 +33,79 @@ HelperAppLauncherDialog.prototype = {
aLauncher.launchWithApplication(null, false); aLauncher.launchWithApplication(null, false);
} else { } else {
let wasClicked = false; let wasClicked = false;
let listener = { this._showDownloadInfobar(aLauncher);
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");
_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); aLauncher.saveToDisk(null, false);
} else { Services.obs.notifyObservers(aLauncher.targetFile, "dl-run", "true");
if (!wasClicked)
aLauncher.cancel(Cr.NS_BINDING_ABORTED);
} }
},
{
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) { promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
@ -203,16 +260,6 @@ HelperAppLauncherDialog.prototype = {
isUsableDirectory: function hald_isUsableDirectory(aDirectory) { isUsableDirectory: function hald_isUsableDirectory(aDirectory) {
return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable(); 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]); this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]);

Просмотреть файл

@ -55,15 +55,35 @@ browserForOpenLocation=Open Location
# Download Manager # Download Manager
downloadsUnknownSize=Unknown size 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 # Alerts
alertLinkBookmarked=Bookmark added alertLinkBookmarked=Bookmark added
alertDownloads=Downloads alertDownloads=Downloads
alertDownloadsStart=Downloading: %S alertDownloadsStart=Downloading: %S
alertDownloadsDone=%S has finished downloading 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. alertTapToSave=Tap to save this file.
alertDownloadsSize=Download too big alertDownloadsSize=Download too big
alertDownloadsNoSpace=Not enough storage space 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 # Popup Blocker
# LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms. # LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.

Просмотреть файл

@ -2465,6 +2465,13 @@ ia64*-hpux*)
esac esac
case "$target" in
*-*linux*)
# Includes linux-android
AC_DEFINE(XP_LINUX)
;;
esac
AC_SUBST(MOZ_LINKER) AC_SUBST(MOZ_LINKER)
if test -n "$MOZ_LINKER"; then if test -n "$MOZ_LINKER"; then
AC_DEFINE(MOZ_LINKER) AC_DEFINE(MOZ_LINKER)
@ -2735,11 +2742,10 @@ else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
dnl Check whether we can use gcc's c++0x mode dnl Check whether gcc's c++0x mode works
AC_LANG_CPLUSPLUS AC_LANG_CPLUSPLUS
if test "$GNU_CXX"; then if test "$GNU_CXX"; then
_SAVE_CXXFLAGS=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -std=gnu++0x" CXXFLAGS="$CXXFLAGS -std=gnu++0x"
AC_CACHE_CHECK(for gcc c++0x headers bug without rtti, 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")]) ac_cv_cxx0x_clang_workaround="no")])
if test "ac_cv_cxx0x_clang_workaround" = "no"; then 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 fi
elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then 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
fi fi
@ -4293,7 +4299,6 @@ NS_PRINTING=1
MOZ_PDF_PRINTING= MOZ_PDF_PRINTING=
MOZ_DISABLE_CRYPTOLEGACY= MOZ_DISABLE_CRYPTOLEGACY=
NSS_DISABLE_DBM= NSS_DISABLE_DBM=
NECKO_WIFI=1
NECKO_COOKIES=1 NECKO_COOKIES=1
NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device" NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
USE_ARM_KUSER= USE_ARM_KUSER=
@ -4323,11 +4328,6 @@ case "${target}" in
fi fi
NSS_DISABLE_DBM=1 NSS_DISABLE_DBM=1
if test -z "$gonkdir"; then
NECKO_WIFI=
else
NECKO_WIFI=1
fi
MOZ_THEME_FASTSTRIPE=1 MOZ_THEME_FASTSTRIPE=1
MOZ_TREE_FREETYPE=1 MOZ_TREE_FREETYPE=1
MOZ_MEMORY=1 MOZ_MEMORY=1
@ -4507,6 +4507,14 @@ if test -z "$gonkdir" ; then
case "$MOZ_BUILD_APP" in case "$MOZ_BUILD_APP" in
mobile/android) mobile/android)
android_min_api_level=16 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 esac
@ -8124,6 +8132,13 @@ dnl ========================================================
dnl Graphics checks. dnl Graphics checks.
dnl ======================================================== 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 if test "${OS_TARGET}" = "WINNT" -o "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
case "${target_cpu}" in case "${target_cpu}" in
i*86*|x86_64|arm) i*86*|x86_64|arm)
@ -8471,28 +8486,36 @@ done
dnl dnl
dnl option to disable necko's wifi scanner dnl option to disable necko's wifi scanner
dnl 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, MOZ_ARG_DISABLE_BOOL(necko-wifi,
[ --disable-necko-wifi Disable necko wifi scanner], [ --disable-necko-wifi Disable necko wifi scanner],
NECKO_WIFI=, NECKO_WIFI=,
NECKO_WIFI=1) 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 if test "$NECKO_WIFI"; then
AC_DEFINE(NECKO_WIFI) AC_DEFINE(NECKO_WIFI)
_NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_WIFI" _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_WIFI"
fi fi
AC_SUBST(NECKO_WIFI) AC_SUBST(NECKO_WIFI)
AC_SUBST(NECKO_WIFI_DBUS)
dnl dnl
dnl option to disable cookies dnl option to disable cookies

Просмотреть файл

@ -215,6 +215,11 @@ public:
virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE; virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE;
virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE; virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
virtual nsIContent *GetBindingParent() const 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 bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
virtual void DestroyContent() MOZ_OVERRIDE; virtual void DestroyContent() MOZ_OVERRIDE;
@ -359,6 +364,16 @@ public:
* An object implementing the .classList property for this element. * An object implementing the .classList property for this element.
*/ */
nsRefPtr<nsDOMTokenList> mClassList; nsRefPtr<nsDOMTokenList> mClassList;
/**
* XBL binding installed on the element.
*/
nsRefPtr<nsXBLBinding> mXBLBinding;
/**
* XBL binding installed on the lement.
*/
nsCOMPtr<nsIContent> mXBLInsertionParent;
}; };
protected: protected:

Просмотреть файл

@ -18,6 +18,7 @@ class nsAttrValue;
class nsAttrName; class nsAttrName;
class nsTextFragment; class nsTextFragment;
class nsIFrame; class nsIFrame;
class nsXBLBinding;
namespace mozilla { namespace mozilla {
namespace widget { namespace widget {
@ -615,6 +616,44 @@ public:
*/ */
virtual nsIContent *GetBindingParent() const = 0; 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 * 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 * tree. For nodes that are not filtered into an insertion point, this

Просмотреть файл

@ -46,6 +46,7 @@ public:
// nsIDOMCharacterData // nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMComment // nsIDOMComment
// Empty interface // 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 // We must ensure that the XBL Binding is installed before we hand
// back this object. // back this object.
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) {
doc->BindingManager()->GetBinding(this)) {
// There's already a binding for this element so nothing left to // There's already a binding for this element so nothing left to
// be done here. // be done here.
@ -1719,8 +1718,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
nsRefPtr<nsXBLBinding> binding = nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
OwnerDoc()->BindingManager()->GetBinding(this);
if (binding) { if (binding) {
binding->AttributeChanged(aName, aNamespaceID, false, aNotify); binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
} }
@ -1903,8 +1901,7 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
nsRefPtr<nsXBLBinding> binding = nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
OwnerDoc()->BindingManager()->GetBinding(this);
if (binding) { if (binding) {
binding->AttributeChanged(aName, aNameSpaceID, true, aNotify); binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
} }

Просмотреть файл

@ -150,8 +150,7 @@ nsIContent*
nsIContent::GetFlattenedTreeParent() const nsIContent::GetFlattenedTreeParent() const
{ {
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
nsIContent* parent = OwnerDoc()->BindingManager()-> nsIContent* parent = GetXBLInsertionParent();
GetInsertionParent(const_cast<nsIContent*>(this));
if (parent) { if (parent) {
return parent; return parent;
} }
@ -285,8 +284,7 @@ nsIContent::GetBaseURI() const
if (elem->IsSVG()) { if (elem->IsSVG()) {
nsIContent* bindingParent = elem->GetBindingParent(); nsIContent* bindingParent = elem->GetBindingParent();
if (bindingParent) { if (bindingParent) {
nsXBLBinding* binding = nsXBLBinding* binding = bindingParent->GetXBLBinding();
bindingParent->OwnerDoc()->BindingManager()->GetBinding(bindingParent);
if (binding) { if (binding) {
// XXX sXBL/XBL2 issue // XXX sXBL/XBL2 issue
// If this is an anonymous XBL element use the binding // If this is an anonymous XBL element use the binding
@ -566,6 +564,12 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb,
cb.NoteXPCOMChild(mControllers); 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"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList)); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
@ -584,6 +588,8 @@ FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
} }
if (aIsXUL) if (aIsXUL)
NS_IF_RELEASE(mControllers); NS_IF_RELEASE(mControllers);
mXBLBinding = nullptr;
mXBLInsertionParent = nullptr;
mChildrenList = nullptr; mChildrenList = nullptr;
mUndoManager = nullptr; mUndoManager = nullptr;
if (mClassList) { if (mClassList) {
@ -803,8 +809,7 @@ nsIContent::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// check for an anonymous parent // check for an anonymous parent
// XXX XBL2/sXBL issue // XXX XBL2/sXBL issue
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
nsIContent* insertionParent = OwnerDoc()->BindingManager()-> nsIContent* insertionParent = GetXBLInsertionParent();
GetInsertionParent(this);
NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent && NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
aVisitor.mEventTargetAtParent != insertionParent), aVisitor.mEventTargetAtParent != insertionParent),
"Retargeting and having insertion parent!"); "Retargeting and having insertion parent!");
@ -889,6 +894,80 @@ FragmentOrElement::GetBindingParent() const
return nullptr; 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 nsresult
FragmentOrElement::InsertChildAt(nsIContent* aKid, FragmentOrElement::InsertChildAt(nsIContent* aKid,
uint32_t aIndex, uint32_t aIndex,
@ -1387,8 +1466,7 @@ NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
bool bool
OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode) OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
{ {
return aNode->IsElement() && return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
aCurrentDoc->BindingManager()->GetBinding(aNode->AsElement());
} }
// CanSkip checks if aNode is black, and if it is, returns // CanSkip checks if aNode is black, and if it is, returns

Просмотреть файл

@ -1748,8 +1748,7 @@ bool
nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent, nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent,
JS::HandleId aId, JSPropertyDescriptor* aDesc) JS::HandleId aId, JSPropertyDescriptor* aDesc)
{ {
nsXBLBinding* binding = aContent->OwnerDoc()->BindingManager() nsXBLBinding* binding = aContent->GetXBLBinding();
->GetBinding(aContent);
if (!binding) if (!binding)
return true; return true;
return binding->LookupMember(aCx, aId, aDesc); return binding->LookupMember(aCx, aId, aDesc);
@ -3568,8 +3567,7 @@ nsContentUtils::HasMutationListeners(nsINode* aNode,
if (aNode->IsNodeOfType(nsINode::eCONTENT)) { if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
nsIContent* content = static_cast<nsIContent*>(aNode); nsIContent* content = static_cast<nsIContent*>(aNode);
nsIContent* insertionParent = nsIContent* insertionParent = content->GetXBLInsertionParent();
doc->BindingManager()->GetInsertionParent(content);
if (insertionParent) { if (insertionParent) {
aNode = insertionParent; aNode = insertionParent;
continue; continue;

Просмотреть файл

@ -5418,10 +5418,7 @@ nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
if (aSheetSet.IsEmpty()) { if (aSheetSet.IsEmpty()) {
aSheetSet = title; aSheetSet = title;
return; } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
}
if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
// Sheets from multiple sets enabled; return null string, per spec. // Sheets from multiple sets enabled; return null string, per spec.
SetDOMStringToNull(aSheetSet); SetDOMStringToNull(aSheetSet);
return; return;

Просмотреть файл

@ -86,11 +86,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode)
return NS_SUCCESS_INTERRUPTED_TRAVERSE; return NS_SUCCESS_INTERRUPTED_TRAVERSE;
} }
nsDataSlots *slots = tmp->GetExistingDataSlots();
if (slots) {
slots->Traverse(cb);
}
tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb); tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
nsINode::Unlink(tmp); nsINode::Unlink(tmp);
nsDataSlots *slots = tmp->GetExistingDataSlots();
if (slots) {
slots->Unlink();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode) NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
@ -331,6 +341,8 @@ nsGenericDOMDataNode::SetTextInternal(uint32_t aOffset, uint32_t aCount,
delete [] to; delete [] to;
} }
UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);
if (document && mText.IsBidi()) { if (document && mText.IsBidi()) {
// If we found bidi characters in mText.SetTo() above, indicate that the // If we found bidi characters in mText.SetTo() above, indicate that the
// document contains bidi characters. // document contains bidi characters.
@ -637,6 +649,41 @@ nsGenericDOMDataNode::GetBindingParent() const
return slots ? slots->mBindingParent : nullptr; 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 bool
nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
{ {
@ -682,6 +729,19 @@ nsGenericDOMDataNode::CreateSlots()
return new nsDataSlots(); 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 // Implementation of the nsIDOMText interface
@ -849,6 +909,10 @@ nsGenericDOMDataNode::TextIsOnlyWhitespace()
return false; return false;
} }
if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) {
return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE);
}
const char* cp = mText.Get1b(); const char* cp = mText.Get1b();
const char* end = cp + mText.GetLength(); const char* end = cp + mText.GetLength();
@ -856,12 +920,15 @@ nsGenericDOMDataNode::TextIsOnlyWhitespace()
char ch = *cp; char ch = *cp;
if (!dom::IsSpaceCharacter(ch)) { if (!dom::IsSpaceCharacter(ch)) {
UnsetFlags(NS_TEXT_IS_ONLY_WHITESPACE);
SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);
return false; return false;
} }
++cp; ++cp;
} }
SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE | NS_TEXT_IS_ONLY_WHITESPACE);
return true; return true;
} }

Просмотреть файл

@ -41,11 +41,20 @@ enum {
// This bit is set to indicate that if the text node changes to // This bit is set to indicate that if the text node changes to
// whitespace, we may need to reframe it (or its ancestors). // 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 // 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 #undef DATA_NODE_FLAG_BIT
@ -141,6 +150,11 @@ public:
#endif #endif
virtual nsIContent *GetBindingParent() const 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 IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE; virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE; virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
@ -169,7 +183,7 @@ public:
// WebIDL API // WebIDL API
// Our XPCOM GetData is just fine for WebIDL // 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); 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. * The nearest enclosing content node with a binding that created us.
* @see nsIContent::GetBindingParent * @see nsIContent::GetBindingParent
*/ */
nsIContent* mBindingParent; // [Weak] nsIContent* mBindingParent; // [Weak]
/**
* @see nsIContent::GetXBLInsertionParent
*/
nsCOMPtr<nsIContent> mXBLInsertionParent;
}; };
// Override from nsINode // Override from nsINode

Просмотреть файл

@ -249,8 +249,7 @@ nsNodeUtils::LastRelease(nsINode* aNode)
ownerDoc->ClearBoxObjectFor(elem); ownerDoc->ClearBoxObjectFor(elem);
NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) || NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
!ownerDoc->BindingManager() || !elem->GetXBLBinding(),
!ownerDoc->BindingManager()->GetBinding(elem),
"Non-forced node has binding on destruction"); "Non-forced node has binding on destruction");
// if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding // 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(); nsIContent* bindingParent = aFromContent->GetBindingParent();
if (bindingParent) { if (bindingParent) {
nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent); nsXBLBinding* binding = bindingParent->GetXBLBinding();
if (binding) { if (binding) {
bool isEqualExceptRef; bool isEqualExceptRef;
rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(), rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),

Просмотреть файл

@ -53,6 +53,7 @@ public:
// nsIDOMCharacterData // nsIDOMCharacterData
NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::) NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
using nsGenericDOMDataNode::SetData; // Prevent hiding overloaded virtual function.
// nsIDOMText // nsIDOMText
NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::) NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)

Просмотреть файл

@ -248,6 +248,7 @@ MOCHITEST_FILES_A = \
test_bug811701.xhtml \ test_bug811701.xhtml \
test_bug820909.html \ test_bug820909.html \
test_bug704063.html \ test_bug704063.html \
test_bug894874.html \
$(NULL) $(NULL)
MOCHITEST_FILES_B = \ MOCHITEST_FILES_B = \
@ -638,6 +639,7 @@ MOCHITEST_FILES_C= \
test_bug890580.html \ test_bug890580.html \
test_declare_stylesheet_obsolete.html \ test_declare_stylesheet_obsolete.html \
variable_style_sheet.sjs \ variable_style_sheet.sjs \
test_processing_instruction_update_stylesheet.xhtml \
$(NULL) $(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec # OOP tests don't work on Windows (bug 763081) or native-fennec

Просмотреть файл

@ -27,8 +27,15 @@ function make_object()
o.b = true; o.b = true;
o.s = "hello"; o.s = "hello";
o.x = { i: 10 }; 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, return { "data": o,
"throwing": throwing,
"document": content.document "document": content.document
}; };
} }

Просмотреть файл

@ -37,6 +37,39 @@
ok(data.b === false, "boolean property"); ok(data.b === false, "boolean property");
ok(data.s === "bye", "string property"); ok(data.s === "bye", "string property");
ok(data.x === null, "nested 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) { 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 * 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/. */ * 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 nsIDocumentEncoder = Components.interfaces.nsIDocumentEncoder;
const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;

Просмотреть файл

@ -4,9 +4,10 @@
Components.utils.import("resource://testing-common/httpd.js"); 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 HTTP_BASE = "http://localhost:" + SERVER_PORT;
const redirectPath = "/redirect"; const redirectPath = "/redirect";
const headerCheckPath = "/headerCheck"; const headerCheckPath = "/headerCheck";
@ -36,10 +37,8 @@ function headerCheckHandler(metadata, response) {
} }
function run_test() { function run_test() {
var server = new HttpServer();
server.registerPathHandler(redirectPath, redirectHandler); server.registerPathHandler(redirectPath, redirectHandler);
server.registerPathHandler(headerCheckPath, headerCheckHandler); server.registerPathHandler(headerCheckPath, headerCheckHandler);
server.start(SERVER_PORT);
do_test_pending(); do_test_pending();
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]

Просмотреть файл

@ -5,7 +5,6 @@ const Cu = Components.utils;
const Cr = Components.results; const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm'); Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import("resource://testing-common/httpd.js");
var httpserv = null; var httpserv = null;

Просмотреть файл

@ -10,16 +10,14 @@ const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm'); Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm');
// load the HTTP server var httpServer = new HttpServer();
Cu.import("resource://testing-common/httpd.js"); 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_URI = "http://localhost";
const REPORT_SERVER_PATH = "/report"; const REPORT_SERVER_PATH = "/report";
var httpServer = null;
var testsToFinish = 0;
/** /**
* Construct a callback that listens to a report submission and either passes * Construct a callback that listens to a report submission and either passes
* or fails a test based on what it gets. * or fails a test based on what it gets.
@ -99,9 +97,6 @@ function run_test() {
":" + REPORT_SERVER_PORT + ":" + REPORT_SERVER_PORT +
"/foo/self"); "/foo/self");
httpServer = new HttpServer();
httpServer.start(REPORT_SERVER_PORT);
// test that inline script violations cause a report. // test that inline script violations cause a report.
makeTest(0, {"blocked-uri": "self"}, false, makeTest(0, {"blocked-uri": "self"}, false,
function(csp) { function(csp) {

Просмотреть файл

@ -11,13 +11,11 @@ const Cr = Components.results;
Cu.import('resource://gre/modules/CSPUtils.jsm'); Cu.import('resource://gre/modules/CSPUtils.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm');
// load the HTTP server
Cu.import("resource://testing-common/httpd.js");
var httpServer = new HttpServer(); var httpServer = new HttpServer();
httpServer.start(-1);
const POLICY_FROM_URI = "default-src 'self'; img-src *"; 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 = "http://localhost:" + POLICY_PORT + "/policy";
const POLICY_URI_RELATIVE = "/policy"; const POLICY_URI_RELATIVE = "/policy";
@ -1048,7 +1046,6 @@ function run_test() {
} }
//server.registerDirectory("/", nsILocalFileForBasePath); //server.registerDirectory("/", nsILocalFileForBasePath);
httpServer.registerPathHandler("/policy", policyresponder); httpServer.registerPathHandler("/policy", policyresponder);
httpServer.start(POLICY_PORT);
for(let i in tests) { for(let i in tests) {
tests[i](); tests[i]();

Просмотреть файл

@ -8,6 +8,7 @@ tail =
[test_csputils.js] [test_csputils.js]
[test_cspreports.js] [test_cspreports.js]
[test_error_codes.js] [test_error_codes.js]
run-sequentially = Hardcoded 4444 port.
[test_thirdpartyutil.js] [test_thirdpartyutil.js]
[test_xhr_standalone.js] [test_xhr_standalone.js]
[test_xmlserializer.js] [test_xmlserializer.js]

Просмотреть файл

@ -24,6 +24,15 @@ public:
virtual ~WebGL1Context(); virtual ~WebGL1Context();
// -------------------------------------------------------------------------
// IMPLEMENT WebGLContext
virtual bool IsWebGL2() const MOZ_OVERRIDE
{
return false;
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// IMPLEMENT nsWrapperCache // 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) { if (mDisableExtensions) {
return false; return false;
} }
@ -1123,7 +1128,20 @@ 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 // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
if (!IsExtensionEnabled(ext)) { if (!IsExtensionEnabled(ext)) {
WebGLExtensionBase *obj = nullptr; 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) { switch (ext) {
case OES_element_index_uint: case OES_element_index_uint:
obj = new WebGLExtensionElementIndexUint(this); obj = new WebGLExtensionElementIndexUint(this);
@ -1167,11 +1185,8 @@ WebGLContext::GetExtension(JSContext *cx, const nsAString& aName, ErrorResult& r
default: default:
MOZ_ASSERT(false, "should not get there."); 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 void

Просмотреть файл

@ -153,6 +153,8 @@ public:
virtual JSObject* WrapObject(JSContext *cx, virtual JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> scope) = 0; JS::Handle<JSObject*> scope) = 0;
virtual bool IsWebGL2() const = 0;
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
// nsICanvasRenderingContextInternal // nsICanvasRenderingContextInternal
@ -388,6 +390,7 @@ public:
void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count); void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count);
void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
WebGLintptr byteOffset); WebGLintptr byteOffset);
void DrawBuffers(const dom::Sequence<GLenum>& buffers);
void Enable(WebGLenum cap); void Enable(WebGLenum cap);
void EnableVertexAttribArray(WebGLuint index); void EnableVertexAttribArray(WebGLuint index);
void Flush() { void Flush() {
@ -892,11 +895,15 @@ protected:
}; };
nsTArray<nsRefPtr<WebGLExtensionBase> > mExtensions; 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. // returns true if the extension has been enabled by calling getExtension.
bool IsExtensionEnabled(WebGLExtensionID ext) const; bool IsExtensionEnabled(WebGLExtensionID ext) const;
// returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes) // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const; bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const;
bool IsExtensionSupported(WebGLExtensionID ext) const;
nsTArray<WebGLenum> mCompressedTextureFormats; 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; 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 void
WebGLContext::BindTexture(WebGLenum target, WebGLTexture *tex) WebGLContext::BindTexture(WebGLenum target, WebGLTexture *tex)
{ {
@ -645,129 +612,6 @@ WebGLContext::CheckFramebufferStatus(WebGLenum target)
return gl->fCheckFramebufferStatus(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 void
WebGLContext::CopyTexSubImage2D_base(WebGLenum target, WebGLContext::CopyTexSubImage2D_base(WebGLenum target,
WebGLint level, WebGLint level,
@ -1165,24 +1009,6 @@ WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer *rbuf)
rbuf->RequestDelete(); 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 void
WebGLContext::DeleteTexture(WebGLTexture *tex) WebGLContext::DeleteTexture(WebGLTexture *tex)
{ {
@ -1271,17 +1097,6 @@ WebGLContext::DepthFunc(WebGLenum func)
gl->fDepthFunc(func); gl->fDepthFunc(func);
} }
void
WebGLContext::DepthMask(WebGLboolean b)
{
if (!IsContextStable())
return;
MakeContextCurrent();
mDepthWriteMask = b;
gl->fDepthMask(b);
}
void void
WebGLContext::DepthRange(WebGLfloat zNear, WebGLfloat zFar) WebGLContext::DepthRange(WebGLfloat zNear, WebGLfloat zFar)
{ {
@ -2113,7 +1928,18 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv)
case LOCAL_GL_RENDERER: case LOCAL_GL_RENDERER:
return StringValue(cx, "Mozilla", rv); return StringValue(cx, "Mozilla", rv);
case LOCAL_GL_VERSION: 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: case LOCAL_GL_SHADING_LANGUAGE_VERSION:
return StringValue(cx, "WebGL GLSL ES 1.0", rv); return StringValue(cx, "WebGL GLSL ES 1.0", rv);
@ -3181,20 +3007,6 @@ WebGLContext::IsRenderbuffer(WebGLRenderbuffer *rb)
rb->HasEverBeenBound(); rb->HasEverBeenBound();
} }
bool
WebGLContext::IsVertexArray(WebGLVertexArray *array)
{
if (!IsContextStable())
return false;
if (!array)
return false;
return ValidateObjectAllowDeleted("isVertexArray", array) &&
!array->IsDeleted() &&
array->HasEverBeenBound();
}
bool bool
WebGLContext::IsShader(WebGLShader *shader) WebGLContext::IsShader(WebGLShader *shader)
{ {
@ -3748,45 +3560,6 @@ WebGLContext::StencilFuncSeparate(WebGLenum face, WebGLenum func, WebGLint ref,
gl->fStencilFuncSeparate(face, func, ref, mask); 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 void
WebGLContext::StencilOp(WebGLenum sfail, WebGLenum dpfail, WebGLenum dppass) WebGLContext::StencilOp(WebGLenum sfail, WebGLenum dpfail, WebGLenum dppass)
{ {
@ -4416,22 +4189,6 @@ WebGLContext::CreateRenderbuffer()
return globj.forget(); 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 void
WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height) 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMWebGLRenderingContext.h" #include "nsIDOMWebGLRenderingContext.h"
#include "WebGL2Context.h"
#define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; } #define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; }
DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext) DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext)
WebGL2Context * WebGL2Context::Create()
{
return nullptr;
}

Просмотреть файл

@ -1049,6 +1049,14 @@ WebGLContext::InitAndValidateGL()
return false; return false;
} }
if (IsWebGL2() &&
(!IsExtensionSupported(OES_vertex_array_object) ||
!IsExtensionSupported(WEBGL_draw_buffers)
))
{
return false;
}
mMemoryPressureObserver mMemoryPressureObserver
= new WebGLMemoryPressureObserver(this); = new WebGLMemoryPressureObserver(this);
nsCOMPtr<nsIObserverService> observerService nsCOMPtr<nsIObserverService> observerService
@ -1063,5 +1071,13 @@ WebGLContext::InitAndValidateGL()
mDefaultVertexArray->mAttribBuffers.SetLength(mGLMaxVertexAttribs); mDefaultVertexArray->mAttribBuffers.SetLength(mGLMaxVertexAttribs);
mBoundVertexArray = mDefaultVertexArray; 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; 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) void WebGLExtensionDrawBuffers::DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers)
{ {
const size_t buffersLength = buffers.Length(); mContext->DrawBuffers(buffers);
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());
} }
bool WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* context) bool WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* context)

Просмотреть файл

@ -44,6 +44,12 @@ void WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array)
bool WebGLExtensionVertexArray::IsSupported(const WebGLContext* context) 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(); gl::GLContext* gl = context->GL();
if (gl->IsGLES2()) { if (gl->IsGLES2()) {
@ -52,6 +58,7 @@ bool WebGLExtensionVertexArray::IsSupported(const WebGLContext* context)
return gl->IsExtensionSupported(gl::GLContext::ARB_vertex_array_object) || return gl->IsExtensionSupported(gl::GLContext::ARB_vertex_array_object) ||
gl->IsExtensionSupported(gl::GLContext::APPLE_vertex_array_object); gl->IsExtensionSupported(gl::GLContext::APPLE_vertex_array_object);
*/
} }
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionVertexArray) IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionVertexArray)

Просмотреть файл

@ -7,6 +7,7 @@
#define WEBGLVERTEXARRAY_H_ #define WEBGLVERTEXARRAY_H_
#include "WebGLObjectModel.h" #include "WebGLObjectModel.h"
#include "WebGLBuffer.h"
#include "WebGLVertexAttribData.h" #include "WebGLVertexAttribData.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"

Просмотреть файл

@ -29,11 +29,14 @@ if CONFIG['MOZ_WEBGL']:
'WebGLActiveInfo.cpp', 'WebGLActiveInfo.cpp',
'WebGLBuffer.cpp', 'WebGLBuffer.cpp',
'WebGL1Context.cpp', 'WebGL1Context.cpp',
'WebGL2Context.cpp',
'WebGLContext.cpp', 'WebGLContext.cpp',
'WebGLContextGL.cpp', 'WebGLContextGL.cpp',
'WebGLContextUtils.cpp', 'WebGLContextUtils.cpp',
'WebGLContextReporter.cpp', 'WebGLContextReporter.cpp',
'WebGLContextValidate.cpp', 'WebGLContextValidate.cpp',
'WebGLContextFramebufferOperations.cpp',
'WebGLContextVertexArray.cpp',
'WebGLElementArrayCache.cpp', 'WebGLElementArrayCache.cpp',
'WebGLExtensionBase.cpp', 'WebGLExtensionBase.cpp',
'WebGLExtensionCompressedTextureATC.cpp', 'WebGLExtensionCompressedTextureATC.cpp',

Просмотреть файл

@ -1,7 +1,6 @@
oes-standard-derivatives.html oes-standard-derivatives.html
ext-texture-filter-anisotropic.html ext-texture-filter-anisotropic.html
oes-texture-float.html oes-texture-float.html
oes-vertex-array-object.html
webgl-debug-renderer-info.html webgl-debug-renderer-info.html
webgl-debug-shaders.html webgl-debug-shaders.html
--min-version 1.0.2 webgl-compressed-texture-s3tc.html --min-version 1.0.2 webgl-compressed-texture-s3tc.html

Просмотреть файл

@ -31,6 +31,8 @@
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsStreamUtils.h" #include "nsStreamUtils.h"
#include "../canvas/src/WebGL2Context.h"
using namespace mozilla::layers; using namespace mozilla::layers;
NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas) NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
@ -682,6 +684,20 @@ HTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
ctx.forget(aContext); ctx.forget(aContext);
return NS_OK; 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); NS_ConvertUTF16toUTF8 ctxId(aContextId);

Просмотреть файл

@ -213,8 +213,13 @@ bool IsValidBufferSize(uint32_t aBufferSize) {
} }
already_AddRefed<MediaStreamAudioDestinationNode> already_AddRefed<MediaStreamAudioDestinationNode>
AudioContext::CreateMediaStreamDestination() AudioContext::CreateMediaStreamDestination(ErrorResult& aRv)
{ {
if (mIsOffline) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
nsRefPtr<MediaStreamAudioDestinationNode> node = nsRefPtr<MediaStreamAudioDestinationNode> node =
new MediaStreamAudioDestinationNode(this); new MediaStreamAudioDestinationNode(this);
return node.forget(); return node.forget();
@ -401,6 +406,9 @@ void
AudioContext::UnregisterPannerNode(PannerNode* aNode) AudioContext::UnregisterPannerNode(PannerNode* aNode)
{ {
mPannerNodes.RemoveEntry(aNode); mPannerNodes.RemoveEntry(aNode);
if (mListener) {
mListener->UnregisterPannerNode(aNode);
}
} }
void void

Просмотреть файл

@ -127,7 +127,7 @@ public:
bool aMixToMono, ErrorResult& aRv); bool aMixToMono, ErrorResult& aRv);
already_AddRefed<MediaStreamAudioDestinationNode> already_AddRefed<MediaStreamAudioDestinationNode>
CreateMediaStreamDestination(); CreateMediaStreamDestination(ErrorResult& aRv);
already_AddRefed<ScriptProcessorNode> already_AddRefed<ScriptProcessorNode>
CreateScriptProcessor(uint32_t aBufferSize, CreateScriptProcessor(uint32_t aBufferSize,

Просмотреть файл

@ -52,6 +52,11 @@ AudioListener::RegisterPannerNode(PannerNode* aPannerNode)
UpdatePannersVelocity(); UpdatePannersVelocity();
} }
void AudioListener::UnregisterPannerNode(PannerNode* aPannerNode)
{
mPanners.RemoveElement(aPannerNode);
}
void void
AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue) AudioListener::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
{ {

Просмотреть файл

@ -127,6 +127,7 @@ public:
} }
void RegisterPannerNode(PannerNode* aPannerNode); void RegisterPannerNode(PannerNode* aPannerNode);
void UnregisterPannerNode(PannerNode* aPannerNode);
private: private:
void SendDoubleParameterToStream(uint32_t aIndex, double aValue); 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); return PannerNodeBinding::Wrap(aCx, aScope, this);
} }
void PannerNode::DestroyMediaStream()
{
if (Context()) {
Context()->UnregisterPannerNode(this);
}
AudioNode::DestroyMediaStream();
}
// Those three functions are described in the spec. // Those three functions are described in the spec.
float float
PannerNodeEngine::LinearGainFunction(float aDistance) PannerNodeEngine::LinearGainFunction(float aDistance)

Просмотреть файл

@ -31,9 +31,12 @@ public:
explicit PannerNode(AudioContext* aContext); explicit PannerNode(AudioContext* aContext);
virtual ~PannerNode(); virtual ~PannerNode();
virtual JSObject* WrapObject(JSContext* aCx, virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE; JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
virtual void DestroyMediaStream() MOZ_OVERRIDE;
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode) NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PannerNode, AudioNode)

Просмотреть файл

@ -27,6 +27,7 @@ MOCHITEST_FILES := \
test_bug867203.html \ test_bug867203.html \
test_bug875221.html \ test_bug875221.html \
test_bug875402.html \ test_bug875402.html \
test_bug894150.html \
test_analyserNode.html \ test_analyserNode.html \
test_AudioBuffer.html \ test_AudioBuffer.html \
test_AudioContext.html \ test_AudioContext.html \

Просмотреть файл

@ -22,6 +22,10 @@ addLoadEvent(function() {
} }
} }
expectException(function() {
ctx.createMediaStreamDestination();
}, DOMException.NOT_SUPPORTED_ERR);
expectException(function() { expectException(function() {
new OfflineAudioContext(2, 100, 0); new OfflineAudioContext(2, 100, 0);
}, DOMException.NOT_SUPPORTED_ERR); }, 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 "nsDOMEventTargetHelper.h"
#include "nsString.h" #include "nsString.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "nsIDOMNavigatorUserMedia.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "MediaManager.h" #include "MediaManager.h"

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше