--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1435,18 +1435,25 @@ HyperTextAccessible::SelectionBoundsAt(i
nsINode* tempNode = startNode;
startNode = endNode;
endNode = tempNode;
int32_t tempOffset = startOffset;
startOffset = endOffset;
endOffset = tempOffset;
}
- *aStartOffset = DOMPointToOffset(startNode, startOffset);
- *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
+ if (!nsContentUtils::ContentIsDescendantOf(startNode, mContent))
+ *aStartOffset = 0;
+ else
+ *aStartOffset = DOMPointToOffset(startNode, startOffset);
+
+ if (!nsContentUtils::ContentIsDescendantOf(endNode, mContent))
+ *aEndOffset = CharacterCount();
+ else
+ *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
return true;
}
bool
HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
int32_t aStartOffset,
int32_t aEndOffset)
{
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -115,32 +115,34 @@ this.ContentControl.prototype = {
if (action === 'moveNext') {
childAction = 'moveFirst';
} else if (action === 'movePrevious') {
childAction = 'moveLast';
}
// Attempt to forward move to a potential child cursor in our
// new position.
- this.sendToChild(vc, aMessage, { action: childAction});
+ this.sendToChild(vc, aMessage, { action: childAction }, true);
}
} else if (!this._childMessageSenders.has(aMessage.target)) {
// We failed to move, and the message is not from a child, so forward
// to parent.
this.sendToParent(aMessage);
}
},
handleEvent: function cc_handleEvent(aEvent) {
if (aEvent.type === 'mousemove') {
this.handleMoveToPoint(
{ json: { x: aEvent.screenX, y: aEvent.screenY, rule: 'Simple' } });
}
if (!Utils.getMessageManager(aEvent.target)) {
aEvent.preventDefault();
+ } else {
+ aEvent.target.focus();
}
},
handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
let [x, y] = [aMessage.json.x, aMessage.json.y];
let rule = TraversalRules[aMessage.json.rule];
let dpr = this.window.devicePixelRatio;
@@ -148,16 +150,17 @@ this.ContentControl.prototype = {
},
handleClearCursor: function cc_handleClearCursor(aMessage) {
let forwarded = this.sendToChild(this.vc, aMessage);
this.vc.position = null;
if (!forwarded) {
this._contentScope.get().sendAsyncMessage('AccessFu:CursorCleared');
}
+ this.document.activeElement.blur();
},
handleAutoMove: function cc_handleAutoMove(aMessage) {
this.autoMove(null, aMessage.json);
},
handleActivate: function cc_handleActivate(aMessage) {
let activateAccessible = (aAccessible) => {
@@ -243,17 +246,17 @@ this.ContentControl.prototype = {
return activatable;
}
}
return null;
};
let vc = this.vc;
- if (!this.sendToChild(vc, aMessage)) {
+ if (!this.sendToChild(vc, aMessage, null, true)) {
let position = vc.position;
activateAccessible(getActivatableDescendant(position) || position);
}
},
handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
// XXX: Add sendToChild. Right now this is only used in Android, so no need.
let direction = aMessage.json.direction;
@@ -342,22 +345,28 @@ this.ContentControl.prototype = {
}
return mm;
}
return null;
},
- sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer) {
- let mm = this.getChildCursor(aVirtualCursor.position);
+ sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer,
+ aFocus) {
+ let position = aVirtualCursor.position;
+ let mm = this.getChildCursor(position);
if (!mm) {
return false;
}
+ if (aFocus) {
+ position.takeFocus();
+ }
+
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'parent';
for (let attr in aReplacer) {
newJSON[attr] = aReplacer[attr];
}
mm.sendAsyncMessage(aMessage.name, newJSON);
@@ -427,17 +436,17 @@ this.ContentControl.prototype = {
let sentToChild = this.sendToChild(vc, {
name: 'AccessFu:AutoMove',
json: {
moveMethod: aOptions.moveMethod,
moveToFocused: aOptions.moveToFocused,
noOpIfOnScreen: true,
forcePresent: true
}
- });
+ }, null, true);
if (!moved && !sentToChild) {
forcePresentFunc();
}
};
if (aOptions.delay) {
this._autoMove = this.window.setTimeout(moveFunc, aOptions.delay);
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -151,17 +151,18 @@ this.EventManager.prototype = {
let position = pivot.position;
if (position && position.role == Roles.INTERNAL_FRAME)
break;
let event = aEvent.
QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
let reason = event.reason;
let oldAccessible = event.oldAccessible;
- if (this.editState.editing) {
+ if (this.editState.editing &&
+ !Utils.getState(position).contains(States.FOCUSED)) {
aEvent.accessibleDocument.takeFocus();
}
this.present(
Presentation.pivotChanged(position, oldAccessible, reason,
pivot.startOffset, pivot.endOffset,
aEvent.isFromUserInput));
break;
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -298,16 +298,22 @@ AccessFuContentTest.prototype = {
}
if (expected.android) {
var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
checkFunc.apply(SimpleTest,
this.lazyCompare(android, expected.android));
}
+ if (expected.focused) {
+ var doc = currentTabDocument();
+ is(doc.activeElement, doc.querySelector(expected.focused),
+ 'Correct element is focused');
+ }
+
this.pump();
}
},
lazyCompare: function lazyCompare(aReceived, aExpected) {
var matches = true;
var delta = [];
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -25,20 +25,22 @@
var doc = currentTabDocument();
var iframe = doc.createElement('iframe');
iframe.mozbrowser = true;
iframe.addEventListener('mozbrowserloadend', function () {
var contentTest = new AccessFuContentTest(
[
// Simple traversal forward
[ContentMessages.simpleMoveNext, {
- speak: ['Phone status bar', 'Traversal Rule test document']
+ speak: ['Phone status bar', 'Traversal Rule test document'],
+ focused: 'body'
}],
[ContentMessages.simpleMoveNext, {
- speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app']
+ speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app'],
+ focused: 'iframe'
}],
[ContentMessages.simpleMoveNext, {
speak: ['many option', {'string': 'stateNotChecked'},
{'string': 'checkbutton'}, {'string': 'listStart'},
{'string': 'list'}, {'string': 'listItemsCount', 'count': 1}]
}],
// check checkbox
[ContentMessages.activateCurrent(), {
--- a/accessible/tests/mochitest/textselection/test_general.html
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -106,17 +106,17 @@
this.getID = function removeSelection_getID()
{
return "nsIAccessibleText::removeSelection test for " + aID;
}
}
function changeDOMSelection(aID, aNodeID1, aNodeOffset1,
aNodeID2, aNodeOffset2,
- aStartOffset, aEndOffset)
+ aTests)
{
this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
this.eventSeq = [
new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
];
this.invoke = function changeDOMSelection_invoke()
@@ -125,25 +125,28 @@
var range = document.createRange();
range.setStart(getNode(aNodeID1), aNodeOffset1);
range.setEnd(getNode(aNodeID2), aNodeOffset2);
sel.addRange(range);
}
this.finalCheck = function changeDOMSelection_finalCheck()
{
- is(this.hyperText.selectionCount, 1,
- "setSelectionBounds: Wrong selection count for " + aID);
- var startOffset = {}, endOffset = {};
- this.hyperText.getSelectionBounds(0, startOffset, endOffset);
+ for (var i = 0; i < aTests.length; i++) {
+ var text = getAccessible(aTests[i][0], nsIAccessibleText);
+ is(text.selectionCount, 1,
+ "setSelectionBounds: Wrong selection count for " + aID);
+ var startOffset = {}, endOffset = {};
+ text.getSelectionBounds(0, startOffset, endOffset);
- is(startOffset.value, aStartOffset,
- "setSelectionBounds: Wrong start offset for " + aID);
- is(endOffset.value, aEndOffset,
- "setSelectionBounds: Wrong end offset for " + aID);
+ is(startOffset.value, aTests[i][1],
+ "setSelectionBounds: Wrong start offset for " + aID);
+ is(endOffset.value, aTests[i][2],
+ "setSelectionBounds: Wrong end offset for " + aID);
+ }
}
this.getID = function changeDOMSelection_getID()
{
return "DOM selection change for " + aID;
}
}
@@ -174,17 +177,20 @@
gQueue.push(new removeSelection("paragraph"));
gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox")));
gQueue.push(new changeSelection("textbox", 1, 3));
gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
gQueue.push(new changeSelection("textarea", 1, 3));
- gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0, 2, 2));
+ gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0,
+ [["c1", 2, 2]]));
+ gQueue.push(new changeDOMSelection("c2", "c2", 0, "c2_div2", 1,
+ [["c2", 0, 3], ["c2_div2", 0, 2]]));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
</head>
@@ -204,11 +210,12 @@
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<p id="paragraph">hello</p>
<input id="textbox" value="hello"/>
<textarea id="textarea">hello</textarea>
<div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div>
+ <div id="c2">hi<div id="c2_div2">hi</div></div>
</body>
</html>
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -20,16 +20,17 @@
#include <string.h>
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
#ifdef XP_WIN
// we want a wmain entry point
+#define XRE_DONT_SUPPORT_XPSP2 // See https://bugzil.la/1023941#c32
#include "nsWindowsWMain.cpp"
#define snprintf _snprintf
#define strcasecmp _stricmp
#endif
#ifdef MOZ_WIDGET_GONK
#include "GonkDisplay.h"
#endif
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -25,16 +25,21 @@ GENERATED_INCLUDES += [
]
LOCAL_INCLUDES += [
'/toolkit/xre',
'/xpcom/base',
'/xpcom/build',
]
+DELAYLOAD_DLLS += [
+ 'mozglue.dll',
+]
+USE_STATIC_LIBS = True
+
if CONFIG['_MSC_VER']:
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
if CONFIG['OS_ARCH'] == 'WINNT':
RCINCLUDE = 'splash.rc'
DEFINES['MOZ_PHOENIX'] = True
@@ -45,19 +50,25 @@ if CONFIG['OS_ARCH'] == 'WINNT':
#
# The default heap size is 1MB on Win32.
# The heap will grow if need be.
#
# Set it to 256k. See bug 127069.
if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
LDFLAGS += ['/HEAP:0x40000']
-USE_LIBS += [
- 'xpcomglue',
-]
+if CONFIG['OS_ARCH'] == 'WINNT':
+ USE_LIBS += [
+ 'mozglue',
+ 'xpcomglue_staticruntime',
+ ]
+else:
+ USE_LIBS += [
+ 'xpcomglue',
+ ]
DISABLE_STL_WRAPPING = True
if CONFIG['MOZ_LINKER']:
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
if CONFIG['HAVE_CLOCK_MONOTONIC']:
OS_LIBS += CONFIG['REALTIME_LIBS']
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -244,25 +244,47 @@ var tests = {
Social.installProvider(doc, manifest2, function(addonManifest) {
SocialService.enableProvider(addonManifest.origin, function(provider) {
is(provider.manifest.version, 1, "manifest version is 1");
// watch for the provider-update and test the new version
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
if (topic != "provider-update")
return;
- is(origin, addonManifest.origin, "provider updated")
+ // The worker will have reloaded and the current provider instance
+ // disabled, removed from the provider list. We have a reference
+ // here, check it is is disabled.
+ is(provider.enabled, false, "old provider instance is disabled")
+ is(origin, addonManifest.origin, "provider manifest updated")
SocialService.unregisterProviderListener(providerListener);
- Services.prefs.clearUserPref("social.whitelist");
- let provider = Social._getProviderFromOrigin(origin);
- is(provider.manifest.version, 2, "manifest version is 2");
- Social.uninstallProvider(origin, function() {
- gBrowser.removeTab(tab);
- next();
- });
+
+ // Get the new provider instance, fetch the manifest via workerapi
+ // and validate that data as well.
+ let p = Social._getProviderFromOrigin(origin);
+ is(p.manifest.version, 2, "manifest version is 2");
+ let port = p.getWorkerPort();
+ ok(port, "got a new port");
+ port.onmessage = function (e) {
+ let topic = e.data.topic;
+ switch (topic) {
+ case "social.manifest":
+ let manifest = e.data.data;
+ is(manifest.version, 2, "manifest version is 2");
+ port.close();
+ Social.uninstallProvider(origin, function() {
+ Services.prefs.clearUserPref("social.whitelist");
+ gBrowser.removeTab(tab);
+ next();
+ });
+ break;
+ }
+ }
+ port.postMessage({topic: "test-init"});
+ port.postMessage({topic: "manifest-get"});
+
});
let port = provider.getWorkerPort();
port.postMessage({topic: "worker.update", data: true});
});
});
});
--- a/browser/base/content/test/social/social_worker.js
+++ b/browser/base/content/test/social/social_worker.js
@@ -1,13 +1,13 @@
/* 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/. */
-let testPort, sidebarPort, apiPort;
+let testPort, sidebarPort, apiPort, updatingManifest=false;
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function onMessage(event) {
let topic = event.data.topic;
switch (topic) {
case "test-init":
testPort = port;
@@ -111,18 +111,27 @@ onconnect = function(e) {
break;
case "test-isVisible-response":
testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
break;
case "share-data-message":
if (testPort)
testPort.postMessage({topic:"got-share-data-message", result: event.data.result});
break;
+ case "manifest-get":
+ apiPort.postMessage({topic: 'social.manifest-get'});
+ break;
case "worker.update":
+ updatingManifest = true;
apiPort.postMessage({topic: 'social.manifest-get'});
break;
case "social.manifest":
- event.data.data.version = 2;
- apiPort.postMessage({topic: 'social.manifest-set', data: event.data.data});
+ if (updatingManifest) {
+ updatingManifest = false;
+ event.data.data.version = 2;
+ apiPort.postMessage({topic: 'social.manifest-set', data: event.data.data});
+ } else if (testPort) {
+ testPort.postMessage({topic:"social.manifest", data: event.data.data});
+ }
break;
}
}
}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1663,16 +1663,22 @@
<xul:hbox anonid="trackingContentProtectionDisabled" hidden="true"
class="popup-notification-footer" xbl:inherits="popupid">
<xul:description class="popup-notification-item-message popup-notification-item-message-critical" xbl:inherits="popupid">
&trackingContentBlocked.disabled.message;
</xul:description>
</xul:hbox>
</xul:vbox>
</xul:vbox>
+ <xul:vbox pack="start">
+ <xul:toolbarbutton anonid="closebutton"
+ class="messageCloseButton popup-notification-closebutton tabbable close-icon"
+ xbl:inherits="oncommand=closebuttoncommand"
+ tooltiptext="&closeNotification.tooltip;"/>
+ </xul:vbox>
</xul:hbox>
</content>
<resources>
<stylesheet src="chrome://global/skin/notification.css"/>
</resources>
<implementation>
<field name="_brandShortName">
document.getElementById("bundle_brand").getString("brandShortName")
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -1166,16 +1166,17 @@ SourceScripts.prototype = {
}
});
}
// If there are any stored breakpoints for this source, display them again,
// both in the editor and the breakpoints pane.
DebuggerController.Breakpoints.updatePaneBreakpoints();
DebuggerController.Breakpoints.updateEditorBreakpoints();
+ DebuggerController.HitCounts.updateEditorHitCounts();
// Make sure the events listeners are up to date.
if (DebuggerView.instrumentsPaneTab == "events-tab") {
DebuggerController.Breakpoints.DOM.scheduleEventListenersFetch();
}
// Signal that a new source has been added.
window.emit(EVENTS.NEW_SOURCE);
@@ -1218,16 +1219,17 @@ SourceScripts.prototype = {
else if (!DebuggerView.Sources.selectedValue) {
DebuggerView.Sources.selectedIndex = 0;
}
// If there are any stored breakpoints for the sources, display them again,
// both in the editor and the breakpoints pane.
DebuggerController.Breakpoints.updatePaneBreakpoints();
DebuggerController.Breakpoints.updateEditorBreakpoints();
+ DebuggerController.HitCounts.updateEditorHitCounts();
// Signal that sources have been added.
window.emit(EVENTS.SOURCES_ADDED);
},
/**
* Handler for the debugger client's 'blackboxchange' notification.
*/
@@ -1483,16 +1485,17 @@ Tracer.prototype = {
}
DebuggerView.Tracer.selectTab();
let id = this._trace = "dbg.trace" + Math.random();
let fields = [
"name",
"location",
+ "hitCount",
"parameterNames",
"depth",
"arguments",
"return",
"throw",
"yield"
];
@@ -1516,40 +1519,50 @@ Tracer.prototype = {
}
this.traceClient.stopTrace(this._trace, aResponse => {
const { error } = aResponse;
if (error) {
DevToolsUtils.reportException("Tracer.prototype.stopTracing", error);
}
this._trace = null;
+ DebuggerController.HitCounts.clear();
aCallback(aResponse);
});
},
onTraces: function (aEvent, { traces }) {
const tracesLength = traces.length;
let tracesToShow;
+ // Update hit counts.
+ for (let t of traces) {
+ if (t.type == "enteredFrame") {
+ DebuggerController.HitCounts.set(t.location, t.hitCount);
+ }
+ }
+ DebuggerController.HitCounts.updateEditorHitCounts();
+
+ // Limit number of traces to be shown in the log.
if (tracesLength > TracerView.MAX_TRACES) {
tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES, tracesLength);
this._stack.splice(0, this._stack.length);
DebuggerView.Tracer.empty();
} else {
tracesToShow = traces;
}
+ // Show traces in the log.
for (let t of tracesToShow) {
if (t.type == "enteredFrame") {
this._onCall(t);
} else {
this._onReturn(t);
}
}
-
DebuggerView.Tracer.commit();
},
/**
* Callback for handling a new call frame.
*/
_onCall: function({ name, location, blackBoxed, parameterNames, depth, arguments: args }) {
const item = {
@@ -2220,16 +2233,94 @@ Breakpoints.prototype = {
Object.defineProperty(Breakpoints.prototype, "_addedOrDisabled", {
get: function* () {
yield* this._added.values();
yield* this._disabled.values();
}
});
/**
+ * Handles Tracer's hit counts.
+ */
+function HitCounts() {
+ /**
+ * Storage of hit counts for every location
+ * hitCount = _locations[url][line][column]
+ */
+ this._hitCounts = Object.create(null);
+}
+
+HitCounts.prototype = {
+ set: function({url, line, column}, aHitCount) {
+ if (!this._hitCounts[url]) {
+ this._hitCounts[url] = Object.create(null);
+ }
+ if (!this._hitCounts[url][line]) {
+ this._hitCounts[url][line] = Object.create(null);
+ }
+ this._hitCounts[url][line][column] = aHitCount;
+ },
+
+ /**
+ * Update all the hit counts in the editor view. This is invoked when the
+ * selected script is changed, or when new sources are received via the
+ * _onNewSource and _onSourcesAdded event listeners.
+ */
+ updateEditorHitCounts: function() {
+ // First, remove all hit counters.
+ DebuggerView.editor.removeAllMarkers("hit-counts");
+
+ // Then, add new hit counts, just for the current source.
+ for (let url in this._hitCounts) {
+ for (let line in this._hitCounts[url]) {
+ for (let column in this._hitCounts[url][line]) {
+ this._updateEditorHitCount({url, line, column});
+ }
+ }
+ }
+ },
+
+ /**
+ * Update a hit counter on a certain line.
+ */
+ _updateEditorHitCount: function({url, line, column}) {
+ // Editor must be initialized.
+ if (!DebuggerView.editor) {
+ return;
+ }
+
+ // No need to do anything if the counter's source is not being shown in the
+ // editor.
+ if (DebuggerView.Sources.selectedValue != url) {
+ return;
+ }
+
+ // There might be more counters on the same line. We need to combine them
+ // into one.
+ let content = Object.keys(this._hitCounts[url][line])
+ .sort() // Sort by key (column).
+ .map(a => this._hitCounts[url][line][a]) // Extract values.
+ .map(a => a + "\u00D7") // Format hit count (e.g. 146×).
+ .join("|");
+
+ // CodeMirror's lines are indexed from 0, while traces start from 1
+ DebuggerView.editor.addContentMarker(line - 1, "hit-counts", "hit-count",
+ content);
+ },
+
+ /**
+ * Remove all hit couters and clear the storage
+ */
+ clear: function() {
+ DebuggerView.editor.removeAllMarkers("hit-counts");
+ this._hitCounts = Object.create(null);
+ }
+}
+
+/**
* Localization convenience methods.
*/
let L10N = new ViewHelpers.L10N(DBG_STRINGS_URI);
/**
* Shortcuts for accessing various debugger preferences.
*/
let Prefs = new ViewHelpers.Prefs("devtools", {
@@ -2260,16 +2351,17 @@ EventEmitter.decorate(this);
DebuggerController.initialize();
DebuggerController.Parser = new Parser();
DebuggerController.ThreadState = new ThreadState();
DebuggerController.StackFrames = new StackFrames();
DebuggerController.SourceScripts = new SourceScripts();
DebuggerController.Breakpoints = new Breakpoints();
DebuggerController.Breakpoints.DOM = new EventListeners();
DebuggerController.Tracer = new Tracer();
+DebuggerController.HitCounts = new HitCounts();
/**
* Export some properties to the global scope for easier access.
*/
Object.defineProperties(window, {
"gTarget": {
get: function() DebuggerController._target
},
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -216,22 +216,27 @@ let DebuggerView = {
extraKeys["Esc"] = false;
function bindKey(func, key, modifiers = {}) {
let key = document.getElementById(key).getAttribute("key");
let shortcut = Editor.accel(key, modifiers);
extraKeys[shortcut] = () => DebuggerView.Filtering[func]();
}
+ let gutters = ["breakpoints"];
+ if (Services.prefs.getBoolPref("devtools.debugger.tracer")) {
+ gutters.unshift("hit-counts");
+ }
+
this.editor = new Editor({
mode: Editor.modes.text,
readOnly: true,
lineNumbers: true,
showAnnotationRuler: true,
- gutters: [ "breakpoints" ],
+ gutters: gutters,
extraKeys: extraKeys,
contextMenu: "sourceEditorContextMenu"
});
this.editor.appendTo(document.getElementById("editor")).then(() => {
this.editor.extend(DebuggerEditor);
this._loadingText = L10N.getStr("loadingText");
this._onEditorLoad(aCallback);
@@ -405,16 +410,17 @@ let DebuggerView = {
}
this._setEditorText(aText);
this._setEditorMode(aSource.url, aContentType, aText);
// Synchronize any other components with the currently displayed source.
DebuggerView.Sources.selectedValue = aSource.url;
DebuggerController.Breakpoints.updateEditorBreakpoints();
+ DebuggerController.HitCounts.updateEditorHitCounts();
histogram.add(Date.now() - startTime);
// Resolve and notify that a source file was shown.
window.emit(EVENTS.SOURCE_SHOWN, aSource);
deferred.resolve([aSource, aText, aContentType]);
},
([, aError]) => {
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -18,16 +18,17 @@ support-files =
code_function-search-01.js
code_function-search-02.js
code_function-search-03.js
code_location-changes.js
code_math.js
code_math.map
code_math.min.js
code_math_bogus_map.js
+ code_same-line-functions.js
code_script-switching-01.js
code_script-switching-02.js
code_test-editor-mode
code_tracing-01.js
code_ugly.js
code_ugly-2.js
code_ugly-3.js
code_ugly-4.js
@@ -69,16 +70,17 @@ support-files =
doc_no-page-sources.html
doc_pause-exceptions.html
doc_pretty-print.html
doc_pretty-print-2.html
doc_pretty-print-3.html
doc_pretty-print-on-paused.html
doc_random-javascript.html
doc_recursion-stack.html
+ doc_same-line-functions.html
doc_scope-variable.html
doc_scope-variable-2.html
doc_scope-variable-3.html
doc_scope-variable-4.html
doc_script-switching-01.html
doc_script-switching-02.html
doc_split-console-paused-reload.html
doc_step-out.html
@@ -156,16 +158,18 @@ skip-if = true # Bug 933950 (leaky test)
[browser_dbg_editor-mode.js]
[browser_dbg_event-listeners-01.js]
[browser_dbg_event-listeners-02.js]
[browser_dbg_event-listeners-03.js]
[browser_dbg_file-reload.js]
[browser_dbg_function-display-name.js]
[browser_dbg_global-method-override.js]
[browser_dbg_globalactor.js]
+[browser_dbg_hit-counts-01.js]
+[browser_dbg_hit-counts-02.js]
[browser_dbg_host-layout.js]
[browser_dbg_iframes.js]
[browser_dbg_instruments-pane-collapse.js]
[browser_dbg_interrupts.js]
[browser_dbg_listaddons.js]
[browser_dbg_listtabs-01.js]
[browser_dbg_listtabs-02.js]
[browser_dbg_listtabs-03.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_hit-counts-01.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Evaluating two functions on the same line and checking for correct hit count
+ * for both of them in CodeMirror's gutter.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
+const CODE_URL = "code_same-line-functions.js";
+
+let gTab, gDebuggee, gPanel, gDebugger;
+let gEditor;
+
+function test() {
+ Task.async(function* () {
+ yield pushPrefs(["devtools.debugger.tracer", true]);
+
+ initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ gTab = aTab;
+ gDebuggee = aDebuggee;
+ gPanel = aPanel;
+ gDebugger = gPanel.panelWin;
+ gEditor = gDebugger.DebuggerView.editor;
+
+ Task.async(function* () {
+ yield waitForSourceShown(gPanel, CODE_URL);
+ yield startTracing(gPanel);
+
+ clickButton();
+
+ yield waitForClientEvents(aPanel, "traces");
+
+ testHitCounts();
+
+ yield stopTracing(gPanel);
+ yield popPrefs();
+ yield closeDebuggerAndFinish(gPanel);
+ })();
+ });
+ })().catch(e => {
+ ok(false, "Got an error: " + e.message + "\n" + e.stack);
+ });
+}
+
+function clickButton() {
+ EventUtils.sendMouseEvent({ type: "click" },
+ gDebuggee.document.querySelector("button"),
+ gDebuggee);
+}
+
+function testHitCounts() {
+ let marker = gEditor.getMarker(0, 'hit-counts');
+
+ is(marker.innerHTML, "1\u00D7|1\u00D7",
+ "Both functions should be hit only once.");
+}
+
+registerCleanupFunction(function() {
+ gTab = null;
+ gDebuggee = null;
+ gPanel = null;
+ gDebugger = null;
+ gEditor = null;
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_hit-counts-02.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * When tracing is stopped all hit counters should be cleared.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
+const CODE_URL = "code_same-line-functions.js";
+
+let gTab, gDebuggee, gPanel, gDebugger;
+let gEditor;
+
+function test() {
+ Task.async(function* () {
+ yield pushPrefs(["devtools.debugger.tracer", true]);
+
+ initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ gTab = aTab;
+ gDebuggee = aDebuggee;
+ gPanel = aPanel;
+ gDebugger = gPanel.panelWin;
+ gEditor = gDebugger.DebuggerView.editor;
+
+ Task.async(function* () {
+ yield waitForSourceShown(gPanel, CODE_URL);
+ yield startTracing(gPanel);
+
+ clickButton();
+
+ yield waitForClientEvents(aPanel, "traces");
+
+ testHitCountsBeforeStopping();
+
+ yield stopTracing(gPanel);
+
+ testHitCountsAfterStopping();
+
+ yield popPrefs();
+ yield closeDebuggerAndFinish(gPanel);
+ })();
+ });
+ })().catch(e => {
+ ok(false, "Got an error: " + e.message + "\n" + e.stack);
+ });
+}
+
+function clickButton() {
+ EventUtils.sendMouseEvent({ type: "click" },
+ gDebuggee.document.querySelector("button"),
+ gDebuggee);
+}
+
+function testHitCountsBeforeStopping() {
+ let marker = gEditor.getMarker(0, 'hit-counts');
+ ok(marker, "A counter should exists.");
+}
+
+function testHitCountsAfterStopping() {
+ let marker = gEditor.getMarker(0, 'hit-counts');
+ is(marker, undefined, "A counter should be cleared.");
+}
+
+registerCleanupFunction(function() {
+ gTab = null;
+ gDebuggee = null;
+ gPanel = null;
+ gDebugger = null;
+ gEditor = null;
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_same-line-functions.js
@@ -0,0 +1,1 @@
+function first() { var a = "first"; second(); function second() { var a = "second"; } }
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_same-line-functions.html
@@ -0,0 +1,15 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Debugger Tracer test page</title>
+ </head>
+
+ <body>
+ <script src="code_same-line-functions.js"></script>
+ <button onclick="first()">Click me!</button>
+ </body>
+</html>
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -921,8 +921,19 @@ function doResume(aPanel) {
return rdpInvoke(threadClient, threadClient.resume);
}
function doInterrupt(aPanel) {
const threadClient = aPanel.panelWin.gThreadClient;
return rdpInvoke(threadClient, threadClient.interrupt);
}
+function pushPrefs(...aPrefs) {
+ let deferred = promise.defer();
+ SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
+ return deferred.promise;
+}
+
+function popPrefs() {
+ let deferred = promise.defer();
+ SpecialPowers.popPrefEnv(deferred.resolve);
+ return deferred.promise;
+}
\ No newline at end of file
--- a/browser/devtools/shared/test/browser_tableWidget_basic.js
+++ b/browser/devtools/shared/test/browser_tableWidget_basic.js
@@ -31,16 +31,17 @@ function test() {
col2: "Column 2",
col3: "Column 3",
col4: "Column 4"
},
uniqueId: "col1",
emptyText: "This is dummy empty text",
highlightUpdated: true,
removableColumns: true,
+ firstColumn: "col4"
});
startTests();
});
});
}
function endTests() {
table.destroy();
@@ -121,23 +122,53 @@ function populateTable() {
}
/**
* Test if the nodes are inserted correctly in the table.
*/
function testTreeItemInsertedCorrectly() {
is(table.tbody.children.length, 4*2 /* double because splitters */,
"4 columns exist");
- for (let i = 0; i < 4; i++) {
- is(table.tbody.children[i*2].firstChild.children.length, 9 + 1 /* header */,
+
+ // Test firstColumn option and check if the nodes are inserted correctly
+ is(table.tbody.children[0].firstChild.children.length, 9 + 1 /* header */,
+ "Correct rows in column 4");
+ is(table.tbody.children[0].firstChild.firstChild.value, "Column 4",
+ "Correct column header value");
+
+ for (let i = 1; i < 4; i++) {
+ is(table.tbody.children[i * 2].firstChild.children.length, 9 + 1 /* header */,
"Correct rows in column " + i);
- is(table.tbody.children[i*2].firstChild.firstChild.value, "Column " + (i + 1),
+ is(table.tbody.children[i * 2].firstChild.firstChild.value, "Column " + i,
"Correct column header value");
}
for (let i = 1; i < 10; i++) {
+ is(table.tbody.children[2].firstChild.children[i].value, "id" + i,
+ "Correct value in row " + i);
+ }
+
+ // Remove firstColumn option and reset the table
+ table.clear();
+ table.firstColumn = "";
+ table.setColumns({
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4"
+ });
+ populateTable();
+
+ // Check if the nodes are inserted correctly without firstColumn option
+ for (let i = 0; i < 4; i++) {
+ is(table.tbody.children[i * 2].firstChild.children.length, 9 + 1 /* header */,
+ "Correct rows in column " + i);
+ is(table.tbody.children[i * 2].firstChild.firstChild.value, "Column " + (i + 1),
+ "Correct column header value");
+ }
+ for (let i = 1; i < 10; i++) {
is(table.tbody.firstChild.firstChild.children[i].value, "id" + i,
"Correct value in row " + i);
}
}
/**
* Tests if the API exposed by TableWidget works properly
*/
--- a/browser/devtools/shared/widgets/TableWidget.js
+++ b/browser/devtools/shared/widgets/TableWidget.js
@@ -35,28 +35,30 @@ const MAX_VISIBLE_STRING_SIZE = 100;
* - initialColumns: map of key vs display name for initial columns of
* the table. See @setupColumns for more info.
* - uniqueId: the column which will be the unique identifier of each
* entry in the table. Default: name.
* - emptyText: text to display when no entries in the table to display.
* - highlightUpdated: true to highlight the changed/added row.
* - removableColumns: Whether columns are removeable. If set to true,
* the context menu in the headers will not appear.
+ * - firstColumn: key of the first column that should appear.
*/
function TableWidget(node, options={}) {
EventEmitter.decorate(this);
this.document = node.ownerDocument;
this.window = this.document.defaultView;
this._parent = node;
- let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns} =
- options;
+ let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns,
+ firstColumn} = options;
this.emptyText = emptyText || "";
this.uniqueId = uniqueId || "name";
+ this.firstColumn = firstColumn || "";
this.highlightUpdated = highlightUpdated || false;
this.removableColumns = removableColumns || false;
this.tbody = this.document.createElementNS(XUL_NS, "hbox");
this.tbody.className = "table-widget-body theme-body";
this.tbody.setAttribute("flex", "1");
this.tbody.setAttribute("tabindex", "0");
this._parent.appendChild(this.tbody);
@@ -232,20 +234,34 @@ TableWidget.prototype = {
}
this.columns.clear();
if (!(sortOn in columns)) {
sortOn = null;
}
+ if (!(this.firstColumn in columns)) {
+ this.firstColumn = null;
+ }
+
+ if (this.firstColumn) {
+ this.columns.set(this.firstColumn,
+ new Column(this, this.firstColumn, columns[this.firstColumn]));
+ }
+
for (let id in columns) {
if (!sortOn) {
sortOn = id;
}
+
+ if (this.firstColumn && id == this.firstColumn) {
+ continue;
+ }
+
this.columns.set(id, new Column(this, id, columns[id]));
if (hiddenColumns.indexOf(id) > -1) {
this.columns.get(id).toggleColumn();
}
}
this.sortedOn = sortOn;
this.sortBy(this.sortedOn);
this.populateMenuPopup();
--- a/browser/devtools/sourceeditor/codemirror/mozilla.css
+++ b/browser/devtools/sourceeditor/codemirror/mozilla.css
@@ -2,26 +2,41 @@
* 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/. */
.errors,
.breakpoints {
width: 16px;
}
+.hit-counts {
+ width: 6px;
+}
+
.error, .breakpoint, .debugLocation, .breakpoint-debugLocation {
display: inline-block;
margin-left: 5px;
width: 12px;
height: 12px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
+.hit-count {
+ display: inline-block;
+ height: 12px;
+ border: solid rgba(0,0,0,0.2);
+ border-width: 1px 1px 1px 0;
+ border-radius: 0 3px 3px 0;
+ padding: 0 3px;
+ font-size: 10px;
+ pointer-events: none;
+}
+
.error {
background-image: url("chrome://browser/skin/devtools/editor-error.png");
opacity: 0.75;
}
.breakpoint {
background-image: url("chrome://browser/skin/devtools/editor-breakpoint.png");
position: relative;
--- a/browser/devtools/sourceeditor/debugger.js
+++ b/browser/devtools/sourceeditor/debugger.js
@@ -110,16 +110,17 @@ function hasBreakpoint(ctx, line) {
// In some rare occasions CodeMirror might not be properly initialized yet, so
// return an exceptional value in that case.
if (cm.lineInfo(line) === null) {
return null;
}
let markers = cm.lineInfo(line).gutterMarkers;
return markers != null &&
+ markers.breakpoints &&
markers.breakpoints.classList.contains("breakpoint");
}
/**
* Adds a visual breakpoint for a specified line. Third
* parameter 'cond' can hold any object.
*
* After adding a breakpoint, this function makes Editor to
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -613,16 +613,42 @@ Editor.prototype = {
removeMarker: function (line, gutterName, markerClass) {
if (!this.hasMarker(line, gutterName, markerClass))
return;
let cm = editors.get(this);
cm.lineInfo(line).gutterMarkers[gutterName].classList.remove(markerClass);
},
+ /**
+ * Adds a marker with a specified class and an HTML content to a line's
+ * gutter. If another marker exists on that line, it is overwritten by a new
+ * marker.
+ */
+ addContentMarker: function (line, gutterName, markerClass, content) {
+ let cm = editors.get(this);
+ let info = cm.lineInfo(line);
+ if (!info)
+ return;
+
+ let marker = cm.getWrapperElement().ownerDocument.createElement("div");
+ marker.className = markerClass;
+ marker.innerHTML = content;
+ cm.setGutterMarker(info.line, gutterName, marker);
+ },
+
+ /**
+ * The reverse of addContentMarker. Removes any line's markers in the
+ * specified gutter.
+ */
+ removeContentMarker: function (line, gutterName) {
+ let cm = editors.get(this);
+ cm.setGutterMarker(info.line, gutterName, null);
+ },
+
getMarker: function(line, gutterName) {
let cm = editors.get(this);
let info = cm.lineInfo(line);
if (!info)
return null;
let gutterMarkers = info.gutterMarkers;
if (!gutterMarkers)
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -5,18 +5,22 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
loader.lazyImporter(this, "escapeHTML", "resource:///modules/devtools/VariablesView.jsm");
loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
-loader.lazyImporter(this, "Task","resource://gre/modules/Task.jsm");
+loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
+loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm");
+
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "TableWidget", "devtools/shared/widgets/TableWidget", true);
const Heritage = require("sdk/core/heritage");
const URI = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
@@ -76,16 +80,17 @@ const COMPAT = {
const CONSOLE_API_LEVELS_TO_SEVERITIES = {
error: "error",
exception: "error",
assert: "error",
warn: "warning",
info: "info",
log: "log",
trace: "log",
+ table: "log",
debug: "log",
dir: "log",
group: "log",
groupCollapsed: "log",
groupEnd: "log",
time: "log",
timeEnd: "log",
count: "log"
@@ -106,16 +111,22 @@ const RE_ALLOWED_STYLES = /^(?:-moz-)?(?
const RE_CLEANUP_STYLES = [
// url(), -moz-element()
/\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
// various URL protocols
/['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
];
+// Maximum number of rows to display in console.table().
+const TABLE_ROW_MAX_ITEMS = 1000;
+
+// Maximum number of columns to display in console.table().
+const TABLE_COLUMN_MAX_ITEMS = 10;
+
/**
* The ConsoleOutput object is used to manage output of messages in the Web
* Console.
*
* @constructor
* @param object owner
* The console output owner. This usually the WebConsoleFrame instance.
* Any other object can be used, as long as it has the following
@@ -1611,16 +1622,354 @@ Messages.ConsoleTrace.prototype = Herita
},
// no-op for the message location and .repeats elements.
// |this._renderStack| handles customized message output.
_renderLocation: function() { },
_renderRepeatNode: function() { },
}); // Messages.ConsoleTrace.prototype
+/**
+ * The ConsoleTable message is used for console.table() calls.
+ *
+ * @constructor
+ * @extends Messages.Extended
+ * @param object packet
+ * The Console API call packet received from the server.
+ */
+Messages.ConsoleTable = function(packet)
+{
+ let options = {
+ className: "cm-s-mozilla",
+ timestamp: packet.timeStamp,
+ category: "webdev",
+ severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
+ private: packet.private,
+ filterDuplicates: false,
+ location: {
+ url: packet.filename,
+ line: packet.lineNumber,
+ },
+ };
+
+ this._populateTableData = this._populateTableData.bind(this);
+ this._renderTable = this._renderTable.bind(this);
+ Messages.Extended.call(this, [this._renderTable], options);
+
+ this._repeatID.consoleApiLevel = packet.level;
+ this._arguments = packet.arguments;
+};
+
+Messages.ConsoleTable.prototype = Heritage.extend(Messages.Extended.prototype,
+{
+ /**
+ * Holds the arguments the content script passed to the console.table()
+ * method.
+ *
+ * @private
+ * @type array
+ */
+ _arguments: null,
+
+ /**
+ * Array of objects that holds the data to log in the table.
+ *
+ * @private
+ * @type array
+ */
+ _data: null,
+
+ /**
+ * Key value pair of the id and display name for the columns in the table.
+ * Refer to the TableWidget API.
+ *
+ * @private
+ * @type object
+ */
+ _columns: null,
+
+ /**
+ * A promise that resolves when the table data is ready or null if invalid
+ * arguments are provided.
+ *
+ * @private
+ * @type promise|null
+ */
+ _populatePromise: null,
+
+ init: function()
+ {
+ let result = Messages.Extended.prototype.init.apply(this, arguments);
+ this._data = [];
+ this._columns = {};
+
+ this._populatePromise = this._populateTableData();
+
+ return result;
+ },
+
+ /**
+ * Sets the key value pair of the id and display name for the columns in the
+ * table.
+ *
+ * @private
+ * @param array|string columns
+ * Either a string or array containing the names for the columns in
+ * the output table.
+ */
+ _setColumns: function(columns)
+ {
+ if (columns.class == "Array") {
+ let items = columns.preview.items;
+
+ for (let item of items) {
+ if (typeof item == "string") {
+ this._columns[item] = item;
+ }
+ }
+ } else if (typeof columns == "string" && columns) {
+ this._columns[columns] = columns;
+ }
+ },
+
+ /**
+ * Retrieves the table data and columns from the arguments received from the
+ * server.
+ *
+ * @return Promise|null
+ * Returns a promise that resolves when the table data is ready or
+ * null if the arguments are invalid.
+ */
+ _populateTableData: function()
+ {
+ let deferred = promise.defer();
+
+ if (this._arguments.length <= 0) {
+ return;
+ }
+
+ let data = this._arguments[0];
+ if (data.class != "Array" && data.class != "Object" &&
+ data.class != "Map" && data.class != "Set") {
+ return;
+ }
+
+ let hasColumnsArg = false;
+ if (this._arguments.length > 1) {
+ if (data.class == "Object" || data.class == "Array") {
+ this._columns["_index"] = l10n.getStr("table.index");
+ } else {
+ this._columns["_index"] = l10n.getStr("table.iterationIndex");
+ }
+
+ this._setColumns(this._arguments[1]);
+ hasColumnsArg = true;
+ }
+
+ if (data.class == "Object" || data.class == "Array") {
+ // Get the object properties, and parse the key and value properties into
+ // the table data and columns.
+ this.client = new ObjectClient(this.output.owner.jsterm.hud.proxy.client,
+ data);
+ this.client.getPrototypeAndProperties(aResponse => {
+ let {ownProperties} = aResponse;
+ let rowCount = 0;
+ let columnCount = 0;
+
+ for (let index of Object.keys(ownProperties || {})) {
+ // Avoid outputting the length property if the data argument provided
+ // is an array
+ if (data.class == "Array" && index == "length") {
+ continue;
+ }
+
+ if (!hasColumnsArg) {
+ this._columns["_index"] = l10n.getStr("table.index");
+ }
+
+ let property = ownProperties[index].value;
+ let item = { _index: index };
+
+ if (property.class == "Object" || property.class == "Array") {
+ let {preview} = property;
+ let entries = property.class == "Object" ?
+ preview.ownProperties : preview.items;
+
+ for (let key of Object.keys(entries)) {
+ let value = property.class == "Object" ?
+ preview.ownProperties[key].value : preview.items[key];
+
+ item[key] = this._renderValueGrip(value, { concise: true });
+
+ if (!hasColumnsArg && !(key in this._columns) &&
+ (++columnCount <= TABLE_COLUMN_MAX_ITEMS)) {
+ this._columns[key] = key;
+ }
+ }
+ } else {
+ // Display the value for any non-object data input.
+ item["_value"] = this._renderValueGrip(property, { concise: true });
+
+ if (!hasColumnsArg && !("_value" in this._columns)) {
+ this._columns["_value"] = l10n.getStr("table.value");
+ }
+ }
+
+ this._data.push(item);
+
+ if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+ break;
+ }
+ }
+
+ deferred.resolve();
+ });
+ } else if (data.class == "Map") {
+ let entries = data.preview.entries;
+
+ if (!hasColumnsArg) {
+ this._columns["_index"] = l10n.getStr("table.iterationIndex");
+ this._columns["_key"] = l10n.getStr("table.key");
+ this._columns["_value"] = l10n.getStr("table.value");
+ }
+
+ let rowCount = 0;
+ for (let index of Object.keys(entries || {})) {
+ let [key, value] = entries[index];
+ let item = {
+ _index: index,
+ _key: this._renderValueGrip(key, { concise: true }),
+ _value: this._renderValueGrip(value, { concise: true })
+ };
+
+ this._data.push(item);
+
+ if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+ break;
+ }
+ }
+
+ deferred.resolve();
+ } else if (data.class == "Set") {
+ let entries = data.preview.items;
+
+ if (!hasColumnsArg) {
+ this._columns["_index"] = l10n.getStr("table.iterationIndex");
+ this._columns["_value"] = l10n.getStr("table.value");
+ }
+
+ let rowCount = 0;
+ for (let index of Object.keys(entries || {})) {
+ let value = entries[index];
+ let item = {
+ _index : index,
+ _value: this._renderValueGrip(value, { concise: true })
+ };
+
+ this._data.push(item);
+
+ if (++rowCount == TABLE_ROW_MAX_ITEMS) {
+ break;
+ }
+ }
+
+ deferred.resolve();
+ }
+
+ return deferred.promise;
+ },
+
+ render: function()
+ {
+ Messages.Extended.prototype.render.apply(this, arguments);
+ this.element.setAttribute("open", true);
+ return this;
+ },
+
+ /**
+ * Render the table.
+ *
+ * @private
+ * @return DOMElement
+ */
+ _renderTable: function()
+ {
+ let cmvar = this.document.createElementNS(XHTML_NS, "span");
+ cmvar.className = "cm-variable";
+ cmvar.textContent = "console";
+
+ let cmprop = this.document.createElementNS(XHTML_NS, "span");
+ cmprop.className = "cm-property";
+ cmprop.textContent = "table";
+
+ let title = this.document.createElementNS(XHTML_NS, "span");
+ title.className = "message-body devtools-monospace";
+ title.appendChild(cmvar);
+ title.appendChild(this.document.createTextNode("."));
+ title.appendChild(cmprop);
+ title.appendChild(this.document.createTextNode("():"));
+
+ let repeatNode = Messages.Simple.prototype._renderRepeatNode.call(this);
+ let location = Messages.Simple.prototype._renderLocation.call(this);
+ if (location) {
+ location.target = "jsdebugger";
+ }
+
+ let body = this.document.createElementNS(XHTML_NS, "span");
+ body.className = "message-flex-body";
+ body.appendChild(title);
+ if (repeatNode) {
+ body.appendChild(repeatNode);
+ }
+ if (location) {
+ body.appendChild(location);
+ }
+ body.appendChild(this.document.createTextNode("\n"));
+
+ let result = this.document.createElementNS(XHTML_NS, "div");
+ result.appendChild(body);
+
+ if (this._populatePromise) {
+ this._populatePromise.then(() => {
+ if (this._data.length > 0) {
+ let widget = new Widgets.Table(this, this._data, this._columns).render();
+ result.appendChild(widget.element);
+ }
+
+ result.scrollIntoView();
+ this.output.owner.emit("messages-table-rendered");
+
+ // Release object actors
+ if (Array.isArray(this._arguments)) {
+ for (let arg of this._arguments) {
+ if (WebConsoleUtils.isActorGrip(arg)) {
+ this.output._releaseObject(arg.actor);
+ }
+ }
+ }
+ this._arguments = null;
+ });
+ }
+
+ return result;
+ },
+
+ _renderBody: function()
+ {
+ let body = Messages.Simple.prototype._renderBody.apply(this, arguments);
+ body.classList.remove("devtools-monospace", "message-body");
+ return body;
+ },
+
+ // no-op for the message location and .repeats elements.
+ // |this._renderTable| handles customized message output.
+ _renderLocation: function() { },
+ _renderRepeatNode: function() { },
+}); // Messages.ConsoleTable.prototype
+
let Widgets = {};
/**
* The base widget class.
*
* @constructor
* @param object message
* The owning message.
@@ -3007,16 +3356,73 @@ Widgets.Stacktrace.prototype = Heritage.
elem.appendChild(location);
elem.appendChild(this.document.createTextNode("\n"));
return elem;
},
}); // Widgets.Stacktrace.prototype
+/**
+ * The table widget.
+ *
+ * @constructor
+ * @extends Widgets.BaseWidget
+ * @param object message
+ * The owning message.
+ * @param array data
+ * Array of objects that holds the data to log in the table.
+ * @param object columns
+ * Object containing the key value pair of the id and display name for
+ * the columns in the table.
+ */
+Widgets.Table = function(message, data, columns)
+{
+ Widgets.BaseWidget.call(this, message);
+ this.data = data;
+ this.columns = columns;
+};
+
+Widgets.Table.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
+{
+ /**
+ * Array of objects that holds the data to output in the table.
+ * @type array
+ */
+ data: null,
+
+ /**
+ * Object containing the key value pair of the id and display name for
+ * the columns in the table.
+ * @type object
+ */
+ columns: null,
+
+ render: function() {
+ if (this.element) {
+ return this;
+ }
+
+ let result = this.element = this.document.createElementNS(XHTML_NS, "div");
+ result.className = "consoletable devtools-monospace";
+
+ this.table = new TableWidget(result, {
+ initialColumns: this.columns,
+ uniqueId: "_index",
+ firstColumn: "_index"
+ });
+
+ for (let row of this.data) {
+ this.table.push(row);
+ }
+
+ return this;
+ }
+}); // Widgets.Table.prototype
+
function gSequenceId()
{
return gSequenceId.n++;
}
gSequenceId.n = 0;
exports.ConsoleOutput = ConsoleOutput;
exports.Messages = Messages;
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -62,16 +62,17 @@ support-files =
test-closure-optimized-out.html
test-closures.html
test-console-assert.html
test-console-count.html
test-console-count-external-file.js
test-console-extras.html
test-console-replaced-api.html
test-console.html
+ test-console-table.html
test-console-output-02.html
test-console-output-03.html
test-console-output-04.html
test-console-output-dom-elements.html
test-console-output-events.html
test-consoleiframes.html
test-data.json
test-data.json^headers^
@@ -300,15 +301,16 @@ skip-if = buildapp == 'mulet'
[browser_webconsole_output_04.js]
[browser_webconsole_output_05.js]
[browser_webconsole_output_06.js]
[browser_webconsole_output_dom_elements_01.js]
[browser_webconsole_output_dom_elements_02.js]
[browser_webconsole_output_dom_elements_03.js]
[browser_webconsole_output_dom_elements_04.js]
[browser_webconsole_output_events.js]
+[browser_webconsole_output_table.js]
[browser_console_variables_view_highlighter.js]
[browser_webconsole_start_netmon_first.js]
[browser_webconsole_console_trace_duplicates.js]
[browser_webconsole_cd_iframe.js]
[browser_webconsole_autocomplete_crossdomain_iframe.js]
[browser_webconsole_console_custom_styles.js]
[browser_webconsole_console_api_stackframe.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_table.js
@@ -0,0 +1,158 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.table() works as intended.
+
+ "use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-table.html";
+
+const TEST_DATA = [
+ {
+ command: "console.table(languages1)",
+ data: [
+ { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+ { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name", fileExtension: "fileExtension" }
+ },
+ {
+ command: "console.table(languages1, 'name')",
+ data: [
+ { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+ { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name" }
+ },
+ {
+ command: "console.table(languages1, ['name'])",
+ data: [
+ { _index: "0", name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: "1", name: "Object", fileExtension: "\".ts\"" },
+ { _index: "2", name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name" }
+ },
+ {
+ command: "console.table(languages2)",
+ data: [
+ { _index: "csharp", name: "\"C#\"", paradigm: "\"object-oriented\"" },
+ { _index: "fsharp", name: "\"F#\"", paradigm: "\"functional\"" }
+ ],
+ columns: { _index: "(index)", name: "name", paradigm: "paradigm" }
+ },
+ {
+ command: "console.table([[1, 2], [3, 4]])",
+ data: [
+ { _index: "0", 0: "1", 1: "2" },
+ { _index: "1", 0: "3", 1: "4" }
+ ],
+ columns: { _index: "(index)", 0: "0", 1: "1" }
+ },
+ {
+ command: "console.table({a: [1, 2], b: [3, 4]})",
+ data: [
+ { _index: "a", 0: "1", 1: "2" },
+ { _index: "b", 0: "3", 1: "4" }
+ ],
+ columns: { _index: "(index)", 0: "0", 1: "1" }
+ },
+ {
+ command: "console.table(family)",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)", firstName: "firstName", lastName: "lastName", age: "age" }
+ },
+ {
+ command: "console.table(family, [])",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)" }
+ },
+ {
+ command: "console.table(family, ['firstName', 'lastName'])",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)", firstName: "firstName", lastName: "lastName" }
+ },
+ {
+ command: "console.table(mySet)",
+ data: [
+ { _index: "0", _value: "1" },
+ { _index: "1", _value: "5" },
+ { _index: "2", _value: "\"some text\"" },
+ { _index: "3", _value: "null" },
+ { _index: "4", _value: "undefined" }
+ ],
+ columns: { _index: "(iteration index)", _value: "Values" }
+ },
+ {
+ command: "console.table(myMap)",
+ data: [
+ { _index: "0", _key: "\"a string\"", _value: "\"value associated with 'a string'\"" },
+ { _index: "1", _key: "5", _value: "\"value associated with 5\"" },
+ ],
+ columns: { _index: "(iteration index)", _key: "Key", _value: "Values" }
+ }
+];
+
+let test = asyncTest(function*() {
+ const {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+
+ for (let testdata of TEST_DATA) {
+ hud.jsterm.clearOutput();
+
+ info("Executing " + testdata.command);
+
+ let onTableRender = once(hud.ui, "messages-table-rendered");
+ hud.jsterm.execute(testdata.command);
+ yield onTableRender;
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: testdata.command + " output",
+ consoleTable: true
+ }],
+ });
+
+ let node = [...result.matched][0];
+ ok(node, "found trace log node");
+
+ let obj = node._messageObject;
+ ok(obj, "console.trace message object");
+
+ ok(obj._data, "found table data object");
+
+ let data = obj._data.map(entries => {
+ let result = {};
+
+ for (let key of Object.keys(entries)) {
+ result[key] = entries[key] instanceof HTMLElement ?
+ entries[key].textContent : entries[key];
+ }
+
+ return result;
+ });
+
+ is(data.toSource(), testdata.data.toSource(), "table data is correct");
+ ok(obj._columns, "found table column object");
+ is(obj._columns.toSource(), testdata.columns.toSource(), "table column is correct");
+ }
+});
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -907,16 +907,18 @@ function openDebugger(aOptions = {})
* and/or line number in the trace message.
* - consoleTime: string that matches a console.time() timer name.
* Provide this if you want to match a console.time() message.
* - consoleTimeEnd: same as above, but for console.timeEnd().
* - consoleDir: boolean, set to |true| to match a console.dir()
* message.
* - consoleGroup: boolean, set to |true| to match a console.group()
* message.
+ * - consoleTable: boolean, set to |true| to match a console.table()
+ * message.
* - longString: boolean, set to |true} to match long strings in the
* message.
* - collapsible: boolean, set to |true| to match messages that can
* be collapsed/expanded.
* - type: match messages that are instances of the given object. For
* example, you can point to Messages.NavigationMarker to match any
* such message.
* - objects: boolean, set to |true| if you expect inspectable
@@ -965,16 +967,32 @@ function waitForMessages(aOptions)
result = aRule.test(aText);
}
else {
result = aRule == aText;
}
return result;
}
+ function checkConsoleTable(aRule, aElement)
+ {
+ let elemText = aElement.textContent;
+ let table = aRule.consoleTable;
+
+ if (!checkText("console.table():", elemText)) {
+ return false;
+ }
+
+ aRule.category = CATEGORY_WEBDEV;
+ aRule.severity = SEVERITY_LOG;
+ aRule.type = Messages.ConsoleTable;
+
+ return true;
+ }
+
function checkConsoleTrace(aRule, aElement)
{
let elemText = aElement.textContent;
let trace = aRule.consoleTrace;
if (!checkText("console.trace():", elemText)) {
return false;
}
@@ -1141,16 +1159,20 @@ function waitForMessages(aOptions)
if (aRule.text && !checkText(aRule.text, elemText)) {
return false;
}
if (aRule.noText && checkText(aRule.noText, elemText)) {
return false;
}
+ if (aRule.consoleTable && !checkConsoleTable(aRule, aElement)) {
+ return false;
+ }
+
if (aRule.consoleTrace && !checkConsoleTrace(aRule, aElement)) {
return false;
}
if (aRule.consoleTime && !checkConsoleTime(aRule, aElement)) {
return false;
}
@@ -1588,8 +1610,39 @@ function checkOutputForInputs(hud, input
ok(entry.expectedTab && entry.expectedTab == uri,
"opened tab '" + uri + "', expected tab '" + entry.expectedTab + "'");
return closeTab(tab);
}).then(resolve, reject);
}
return Task.spawn(runner);
}
+
+/**
+ * Wait for eventName on target.
+ * @param {Object} target An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture=false) {
+ info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+ let deferred = promise.defer();
+
+ for (let [add, remove] of [
+ ["addEventListener", "removeEventListener"],
+ ["addListener", "removeListener"],
+ ["on", "off"]
+ ]) {
+ if ((add in target) && (remove in target)) {
+ target[add](eventName, function onEvent(...aArgs) {
+ target[remove](eventName, onEvent, useCapture);
+ deferred.resolve.apply(deferred, aArgs);
+ }, useCapture);
+ break;
+ }
+ }
+
+ return deferred.promise;
+}
+
--- a/browser/devtools/webconsole/test/test-console-extras.html
+++ b/browser/devtools/webconsole/test/test-console-extras.html
@@ -2,19 +2,16 @@
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
<meta charset="utf-8">
<title>Console extended API test</title>
<script type="text/javascript">
function test() {
console.log("start");
console.clear()
console.dirxml()
- console.profile()
- console.profileEnd()
- console.table()
console.log("end");
}
</script>
</head>
<body>
<h1 id="header">Heads Up Display Demo</h1>
<button onclick="test();">Test Extended API</button>
<div id="myDiv"></div>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-table.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for Bug 899753 - console.table support</title>
+ <script>
+ var languages1 = [
+ { name: "JavaScript", fileExtension: [".js"] },
+ { name: { a: "TypeScript" }, fileExtension: ".ts" },
+ { name: "CoffeeScript", fileExtension: ".coffee" }
+ ];
+
+ var languages2 = {
+ csharp: { name: "C#", paradigm: "object-oriented" },
+ fsharp: { name: "F#", paradigm: "functional" }
+ };
+
+ function Person(firstName, lastName, age)
+ {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.age = age;
+ }
+
+ var family = {};
+ family.mother = new Person("Susan", "Doyle", 32);
+ family.father = new Person("John", "Doyle", 33);
+ family.daughter = new Person("Lily", "Doyle", 5);
+ family.son = new Person("Mike", "Doyle", 8);
+
+ var myMap = new Map();
+
+ myMap.set("a string", "value associated with 'a string'");
+ myMap.set(5, "value associated with 5");
+
+ var mySet = new Set();
+
+ mySet.add(1);
+ mySet.add(5);
+ mySet.add("some text");
+ mySet.add(null);
+ mySet.add(undefined);
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -118,16 +118,17 @@ const MESSAGE_PREFERENCE_KEYS = [
const LEVELS = {
error: SEVERITY_ERROR,
exception: SEVERITY_ERROR,
assert: SEVERITY_ERROR,
warn: SEVERITY_WARNING,
info: SEVERITY_INFO,
log: SEVERITY_LOG,
trace: SEVERITY_LOG,
+ table: SEVERITY_LOG,
debug: SEVERITY_LOG,
dir: SEVERITY_LOG,
group: SEVERITY_LOG,
groupCollapsed: SEVERITY_LOG,
groupEnd: SEVERITY_LOG,
time: SEVERITY_LOG,
timeEnd: SEVERITY_LOG,
count: SEVERITY_LOG
@@ -1207,16 +1208,21 @@ WebConsoleFrame.prototype = {
case "error":
case "exception":
case "assert":
case "debug": {
let msg = new Messages.ConsoleGeneric(aMessage);
node = msg.init(this.output).render().element;
break;
}
+ case "table": {
+ let msg = new Messages.ConsoleTable(aMessage);
+ node = msg.init(this.output).render().element;
+ break;
+ }
case "trace": {
let msg = new Messages.ConsoleTrace(aMessage);
node = msg.init(this.output).render().element;
break;
}
case "dir": {
body = { arguments: args };
let clipboardArray = [];
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -245,8 +245,15 @@ messageToggleDetails=Show/hide message d
# LOCALIZATION NOTE (emptySlotLabel): the text is displayed when an Array
# with empty slots is printed to the console.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of empty slots
# example: 1 empty slot
# example: 5 empty slots
emptySlotLabel=#1 empty slot;#1 empty slots
+
+# LOCALIZATION NOTE (table.index, table.iterationIndex, table.key, table.value):
+# the column header displayed in the console table widget.
+table.index=(index)
+table.iterationIndex=(iteration index)
+table.key=Key
+table.value=Values
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2659,16 +2659,17 @@ richlistitem[type~="action"][actiontype=
#historySwipeAnimationContainer {
background: url("chrome://browser/skin/subtle-pattern.png") #B3B9C1;
}
/* ----- SIDEBAR ELEMENTS ----- */
#sidebar,
sidebarheader {
+ -moz-appearance: -moz-mac-vibrancy-light;
background-color: #e2e7ed;
}
#sidebar:-moz-window-inactive,
sidebarheader:-moz-window-inactive {
background-color: #e8e8e8;
}
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -58,16 +58,20 @@ a {
}
}
.message > .message-body-wrapper {
flex: 1 1 100%;
margin: 3px;
}
+.message-body-wrapper .table-widget-body {
+ overflow: visible;
+}
+
/* The red bubble that shows the number of times a message is repeated */
.message-repeats {
-moz-user-select: none;
flex: none;
margin: 2px 6px;
padding: 0 6px;
height: 1.25em;
color: white;
@@ -218,16 +222,23 @@ a {
.theme-dark .console-string {
color: #d99b28;
}
.theme-light .console-string {
color: hsl(24,85%,39%);
}
+.theme-selected .console-string,
+.theme-selected .cm-number,
+.theme-selected .cm-variable,
+.theme-selected .kind-ArrayLike {
+ color: #f5f7fa !important; /* Selection Text Color */
+}
+
.message[category=network] > .indent {
-moz-border-end: solid #000 6px;
}
.message[category=network][severity=error] > .icon::before {
background-position: -8px 0;
}
@@ -424,16 +435,20 @@ a {
padding: 0 1em 0 1.5em;
margin: 5px 0 0 0;
max-height: 10em;
overflow-y: auto;
border: 1px solid rgb(200,200,200);
border-radius: 3px;
}
+.consoletable {
+ margin: 5px 0 0 0;
+}
+
.theme-light .message[severity=error] .stacktrace {
background-color: rgba(255, 255, 255, 0.5);
}
.theme-dark .message[severity=error] .stacktrace {
background-color: rgba(0, 0, 0, 0.5);
}
--- a/content/base/src/ChildIterator.h
+++ b/content/base/src/ChildIterator.h
@@ -39,16 +39,30 @@ public:
: mParent(aParent),
mChild(nullptr),
mDefaultChild(nullptr),
mIndexInInserted(0),
mIsFirst(aStartAtBeginning)
{
}
+ ExplicitChildIterator(const ExplicitChildIterator& aOther)
+ : mParent(aOther.mParent), mChild(aOther.mChild),
+ mDefaultChild(aOther.mDefaultChild),
+ mShadowIterator(aOther.mShadowIterator ?
+ new ExplicitChildIterator(*aOther.mShadowIterator) :
+ nullptr),
+ mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
+
+ ExplicitChildIterator(ExplicitChildIterator&& aOther)
+ : mParent(aOther.mParent), mChild(aOther.mChild),
+ mDefaultChild(aOther.mDefaultChild),
+ mShadowIterator(Move(aOther.mShadowIterator)),
+ mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
+
nsIContent* GetNextChild();
// Looks for aChildToFind respecting insertion points until aChildToFind
// or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
// whether aChildToFind was found as an explicit child prior to encountering
// aBound.
bool Seek(nsIContent* aChildToFind, nsIContent* aBound = nullptr)
{
@@ -113,25 +127,31 @@ class FlattenedChildIterator : public Ex
{
public:
FlattenedChildIterator(nsIContent* aParent)
: ExplicitChildIterator(aParent), mXBLInvolved(false)
{
Init(false);
}
+ FlattenedChildIterator(FlattenedChildIterator&& aOther)
+ : ExplicitChildIterator(Move(aOther)), mXBLInvolved(aOther.mXBLInvolved) {}
+
+ FlattenedChildIterator(const FlattenedChildIterator& aOther)
+ : ExplicitChildIterator(aOther), mXBLInvolved(aOther.mXBLInvolved) {}
+
bool XBLInvolved() { return mXBLInvolved; }
protected:
/**
* This constructor is a hack to help AllChildrenIterator which sometimes
* doesn't want to consider XBL.
*/
FlattenedChildIterator(nsIContent* aParent, bool aIgnoreXBL)
- : ExplicitChildIterator(aParent), mXBLInvolved(false)
+ : ExplicitChildIterator(aParent), mXBLInvolved(false)
{
Init(aIgnoreXBL);
}
void Init(bool aIgnoreXBL);
// For certain optimizations, nsCSSFrameConstructor needs to know if the
// child list of the element that we're iterating matches its .childNodes.
@@ -147,16 +167,26 @@ protected:
class AllChildrenIterator : private FlattenedChildIterator
{
public:
AllChildrenIterator(nsIContent* aNode, uint32_t aFlags) :
FlattenedChildIterator(aNode, (aFlags & nsIContent::eAllButXBL)),
mOriginalContent(aNode), mFlags(aFlags),
mPhase(eNeedBeforeKid) {}
+ AllChildrenIterator(AllChildrenIterator&& aOther)
+ : FlattenedChildIterator(Move(aOther)),
+ mOriginalContent(aOther.mOriginalContent),
+ mAnonKids(Move(aOther.mAnonKids)), mFlags(aOther.mFlags),
+ mPhase(aOther.mPhase)
+#ifdef DEBUG
+ , mMutationGuard(aOther.mMutationGuard)
+#endif
+ {}
+
#ifdef DEBUG
~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
#endif
nsIContent* GetNextChild();
private:
enum IteratorPhase
--- a/content/base/src/nsDOMAttributeMap.cpp
+++ b/content/base/src/nsDOMAttributeMap.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
/*
* Implementation of the |attributes| property of DOM Core's Element object.
*/
@@ -45,23 +46,27 @@ RemoveMapRef(nsAttrHashKey::KeyType aKey
{
aData->SetMap(nullptr);
return PL_DHASH_REMOVE;
}
nsDOMAttributeMap::~nsDOMAttributeMap()
{
- mAttributeCache.Enumerate(RemoveMapRef, nullptr);
+ if (mAttributeCache) {
+ mAttributeCache->Enumerate(RemoveMapRef, nullptr);
+ }
}
void
nsDOMAttributeMap::DropReference()
{
- mAttributeCache.Enumerate(RemoveMapRef, nullptr);
+ if (mAttributeCache) {
+ mAttributeCache->Enumerate(RemoveMapRef, nullptr);
+ }
mContent = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
tmp->DropReference();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -77,17 +82,19 @@ TraverseMapEntry(nsAttrHashKey::KeyType
static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
return PL_DHASH_NEXT;
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
- tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
+ if (tmp->mAttributeCache) {
+ tmp->mAttributeCache->Enumerate(TraverseMapEntry, &cb);
+ }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap)
if (tmp->IsBlack()) {
@@ -131,76 +138,79 @@ SetOwnerDocumentFunc(nsAttrHashKey::KeyT
nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
}
nsresult
nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
{
- uint32_t n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
- NS_ENSURE_TRUE(n == mAttributeCache.Count(), NS_ERROR_FAILURE);
-
+ if (mAttributeCache) {
+ uint32_t n = mAttributeCache->Enumerate(SetOwnerDocumentFunc, aDocument);
+ NS_ENSURE_TRUE(n == mAttributeCache->Count(), NS_ERROR_FAILURE);
+ }
return NS_OK;
}
void
nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
{
nsAttrKey attr(aNamespaceID, aLocalName);
- Attr *node = mAttributeCache.GetWeak(attr);
- if (node) {
- // Break link to map
- node->SetMap(nullptr);
+ if (mAttributeCache) {
+ Attr *node = mAttributeCache->GetWeak(attr);
+ if (node) {
+ // Break link to map
+ node->SetMap(nullptr);
- // Remove from cache
- mAttributeCache.Remove(attr);
+ // Remove from cache
+ mAttributeCache->Remove(attr);
+ }
}
}
already_AddRefed<Attr>
nsDOMAttributeMap::RemoveAttribute(mozilla::dom::NodeInfo* aNodeInfo)
{
NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nullptr!");
nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
nsRefPtr<Attr> node;
- if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
+ if (mAttributeCache && mAttributeCache->Get(attr, getter_AddRefs(node))) {
+ // Break link to map
+ node->SetMap(nullptr);
+
+ // Remove from cache
+ mAttributeCache->Remove(attr);
+ } else {
nsAutoString value;
// As we are removing the attribute we need to set the current value in
// the attribute node.
mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
node = new Attr(nullptr, ni.forget(), value, true);
}
- else {
- // Break link to map
- node->SetMap(nullptr);
-
- // Remove from cache
- mAttributeCache.Remove(attr);
- }
return node.forget();
}
Attr*
nsDOMAttributeMap::GetAttribute(mozilla::dom::NodeInfo* aNodeInfo, bool aNsAware)
{
NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nullptr!");
nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
- Attr* node = mAttributeCache.GetWeak(attr);
+ EnsureAttributeCache();
+ Attr* node = mAttributeCache->GetWeak(attr);
if (!node) {
nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
nsRefPtr<Attr> newAttr =
new Attr(this, ni.forget(), EmptyString(), aNsAware);
- mAttributeCache.Put(attr, newAttr);
+ mAttributeCache->Put(attr, newAttr);
node = newAttr;
}
return node;
}
Attr*
nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
@@ -236,16 +246,24 @@ nsDOMAttributeMap::GetNamedItem(const ns
{
NS_ENSURE_ARG_POINTER(aAttribute);
NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
return NS_OK;
}
+void
+nsDOMAttributeMap::EnsureAttributeCache()
+{
+ if (!mAttributeCache) {
+ mAttributeCache = MakeUnique<AttrCache>();
+ }
+}
+
NS_IMETHODIMP
nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
{
Attr* attribute = static_cast<Attr*>(aAttr);
NS_ENSURE_ARG(attribute);
ErrorResult rv;
*aReturn = SetNamedItem(*attribute, rv).take();
@@ -337,17 +355,18 @@ nsDOMAttributeMap::SetNamedItemInternal(
}
nsAutoString value;
aAttr.GetValue(value);
// Add the new attribute to the attribute map before updating
// its value in the element. @see bug 364413.
nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
- mAttributeCache.Put(attrkey, &aAttr);
+ EnsureAttributeCache();
+ mAttributeCache->Put(attrkey, &aAttr);
aAttr.SetMap(this);
rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
ni->GetPrefixAtom(), value, true);
if (NS_FAILED(rv)) {
aError.Throw(rv);
DropAttribute(ni->NamespaceID(), ni->NameAtom());
}
@@ -523,41 +542,43 @@ nsDOMAttributeMap::RemoveNamedItemNS(con
mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true);
return attr.forget();
}
uint32_t
nsDOMAttributeMap::Count() const
{
- return mAttributeCache.Count();
+ return mAttributeCache ? mAttributeCache->Count() : 0;
}
uint32_t
nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
void *aUserArg) const
{
- return mAttributeCache.EnumerateRead(aFunc, aUserArg);
+ return mAttributeCache ? mAttributeCache->EnumerateRead(aFunc, aUserArg) : 0;
}
size_t
AttrCacheSizeEnumerator(const nsAttrKey& aKey,
const nsRefPtr<Attr>& aValue,
MallocSizeOf aMallocSizeOf,
void* aUserArg)
{
return aMallocSizeOf(aValue.get());
}
size_t
nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
- n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
- aMallocSizeOf);
+ n += mAttributeCache
+ ? mAttributeCache->SizeOfExcludingThis(AttrCacheSizeEnumerator,
+ aMallocSizeOf)
+ : 0;
// NB: mContent is non-owning and thus not counted.
return n;
}
/* virtual */ JSObject*
nsDOMAttributeMap::WrapObject(JSContext* aCx)
{
--- a/content/base/src/nsDOMAttributeMap.h
+++ b/content/base/src/nsDOMAttributeMap.h
@@ -6,16 +6,17 @@
/*
* Implementation of the |attributes| property of DOM Core's Element object.
*/
#ifndef nsDOMAttributeMap_h
#define nsDOMAttributeMap_h
#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Attr.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMMozNamedAttrMap.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"
#include "nsWrapperCache.h"
@@ -178,19 +179,21 @@ public:
protected:
virtual ~nsDOMAttributeMap();
private:
nsCOMPtr<Element> mContent;
/**
- * Cache of Attrs.
+ * Cache of Attrs. It's usually empty, and thus initialized lazily.
*/
- AttrCache mAttributeCache;
+ mozilla::UniquePtr<AttrCache> mAttributeCache;
+
+ void EnsureAttributeCache();
/**
* SetNamedItem() (aWithNS = false) and SetNamedItemNS() (aWithNS =
* true) implementation.
*/
already_AddRefed<Attr>
SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError);
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -318,17 +318,17 @@ IsDirectShowSupportedType(const nsACStri
}
#endif
#ifdef MOZ_FMP4
static bool
IsMP4SupportedType(const nsACString& aType)
{
return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
- MP4Decoder::GetSupportedCodecs(aType, nullptr);
+ MP4Decoder::CanHandleMediaType(aType);
}
#endif
#ifdef MOZ_APPLEMEDIA
static const char * const gAppleMP3Types[] = {
"audio/mp3",
"audio/mpeg",
nullptr,
@@ -401,18 +401,19 @@ DecoderTraits::CanHandleMediaType(const
#endif
#if defined(MOZ_WEBM) && !defined(MOZ_OMX_WEBM_DECODER)
if (IsWebMType(nsDependentCString(aMIMEType))) {
codecList = gWebMCodecs;
result = CANPLAY_MAYBE;
}
#endif
#ifdef MOZ_FMP4
- if (IsMP4SupportedType(nsDependentCString(aMIMEType))) {
- result = aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
+ if (MP4Decoder::CanHandleMediaType(nsDependentCString(aMIMEType),
+ aRequestedCodecs)) {
+ return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
}
#endif
#ifdef MOZ_GSTREAMER
if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
if (aHaveRequestedCodecs)
return CANPLAY_YES;
return CANPLAY_MAYBE;
@@ -678,16 +679,24 @@ MediaDecoderReader* DecoderTraits::Creat
if (false) {} // dummy if to take care of the dangling else
return decoderReader;
}
/* static */
bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
{
+ // Forbid playing media in video documents if the user has opted
+ // not to, using either the legacy WMF specific pref, or the newer
+ // catch-all pref.
+ if (!Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true) ||
+ !Preferences::GetBool("media.play-stand-alone", true)) {
+ return false;
+ }
+
return
IsOggType(aType) ||
#ifdef MOZ_OMX_DECODER
// We support amr inside WebApps on firefoxOS but not in general web content.
// Ensure we dont create a VideoDocument when accessing amr URLs directly.
(IsOmxSupportedType(aType) && !aType.EqualsASCII("audio/amr")) ||
#endif
#ifdef MOZ_WEBM
@@ -698,18 +707,17 @@ bool DecoderTraits::IsSupportedInVideoDo
#endif
#ifdef MOZ_ANDROID_OMX
(MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) ||
#endif
#ifdef MOZ_FMP4
IsMP4SupportedType(aType) ||
#endif
#ifdef MOZ_WMF
- (IsWMFSupportedType(aType) &&
- Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true)) ||
+ IsWMFSupportedType(aType) ||
#endif
#ifdef MOZ_DIRECTSHOW
IsDirectShowSupportedType(aType) ||
#endif
#ifdef MOZ_APPLEMEDIA
IsAppleMediaSupportedType(aType) ||
#endif
#ifdef NECKO_PROTOCOL_rtsp
--- a/content/media/VideoUtils.cpp
+++ b/content/media/VideoUtils.cpp
@@ -193,9 +193,41 @@ IsValidVideoRegion(const nsIntSize& aFra
}
TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool()
{
return SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Decode"),
Preferences::GetUint("media.num-decode-threads", 25));
}
+bool
+ExtractH264CodecDetails(const nsAString& aCodec,
+ int16_t& aProfile,
+ int16_t& aLevel)
+{
+ // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
+ // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
+ // We ignore the constraint_set flags, as it's not clear from any
+ // documentation what constraints the platform decoders support.
+ // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
+ // for more details.
+ if (aCodec.Length() != strlen("avc1.PPCCLL")) {
+ return false;
+ }
+
+ // Verify the codec starts with "avc1.".
+ const nsAString& sample = Substring(aCodec, 0, 5);
+ if (!sample.EqualsASCII("avc1.")) {
+ return false;
+ }
+
+ // Extract the profile_idc, constrains, and level_idc.
+ nsresult rv = NS_OK;
+ aProfile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ aLevel = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return true;
+}
+
} // end namespace mozilla
--- a/content/media/VideoUtils.h
+++ b/content/media/VideoUtils.h
@@ -210,11 +210,50 @@ private:
};
class SharedThreadPool;
// Returns the thread pool that is shared amongst all decoder state machines
// for decoding streams.
TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool();
+enum H264_PROFILE {
+ H264_PROFILE_UNKNOWN = 0,
+ H264_PROFILE_BASE = 0x42,
+ H264_PROFILE_MAIN = 0x4D,
+ H264_PROFILE_EXTENDED = 0x58,
+ H264_PROFILE_HIGH = 0x64,
+};
+
+enum H264_LEVEL {
+ H264_LEVEL_1 = 10,
+ H264_LEVEL_1_b = 11,
+ H264_LEVEL_1_1 = 11,
+ H264_LEVEL_1_2 = 12,
+ H264_LEVEL_1_3 = 13,
+ H264_LEVEL_2 = 20,
+ H264_LEVEL_2_1 = 21,
+ H264_LEVEL_2_2 = 22,
+ H264_LEVEL_3 = 30,
+ H264_LEVEL_3_1 = 31,
+ H264_LEVEL_3_2 = 32,
+ H264_LEVEL_4 = 40,
+ H264_LEVEL_4_1 = 41,
+ H264_LEVEL_4_2 = 42,
+ H264_LEVEL_5 = 50,
+ H264_LEVEL_5_1 = 51,
+ H264_LEVEL_5_2 = 52
+};
+
+// Extracts the H.264/AVC profile and level from an H.264 codecs string.
+// H.264 codecs parameters have a type defined as avc1.PPCCLL, where
+// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
+// See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
+// for more details.
+// Returns false on failure.
+bool
+ExtractH264CodecDetails(const nsAString& aCodecs,
+ int16_t& aProfile,
+ int16_t& aLevel);
+
} // end namespace mozilla
#endif
--- a/content/media/fmp4/MP4Decoder.cpp
+++ b/content/media/fmp4/MP4Decoder.cpp
@@ -3,16 +3,17 @@
/* 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 "MP4Decoder.h"
#include "MP4Reader.h"
#include "MediaDecoderStateMachine.h"
#include "mozilla/Preferences.h"
+#include "nsCharSeparatedTokenizer.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "prlog.h"
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#endif
@@ -45,58 +46,87 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
caps.CallOnMainThreadWhenCapsAvailable(task);
}
return NS_OK;
}
#endif
+static bool
+IsSupportedAudioCodec(const nsAString& aCodec)
+{
+ // AAC-LC, HE-AAC or MP3 in M4A.
+ return aCodec.EqualsASCII("mp4a.40.2") ||
+#ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
+ aCodec.EqualsASCII("mp3") ||
+#endif
+ aCodec.EqualsASCII("mp4a.40.5");
+}
+
+static bool
+IsSupportedH264Codec(const nsAString& aCodec)
+{
+ int16_t profile = 0, level = 0;
+
+ if (!ExtractH264CodecDetails(aCodec, profile, level)) {
+ return false;
+ }
+
+ // Just assume what we can play on all platforms the codecs/formats that
+ // WMF can play, since we don't have documentation about what other
+ // platforms can play... According to the WMF documentation:
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
+ // "The Media Foundation H.264 video decoder is a Media Foundation Transform
+ // that supports decoding of Baseline, Main, and High profiles, up to level
+ // 5.1.". We also report that we can play Extended profile, as there are
+ // bitstreams that are Extended compliant that are also Baseline compliant.
+ return level >= H264_LEVEL_1 &&
+ level <= H264_LEVEL_5_1 &&
+ (profile == H264_PROFILE_BASE ||
+ profile == H264_PROFILE_MAIN ||
+ profile == H264_PROFILE_EXTENDED ||
+ profile == H264_PROFILE_HIGH);
+}
+
+/* static */
bool
-MP4Decoder::GetSupportedCodecs(const nsACString& aType,
- char const *const ** aCodecList)
+MP4Decoder::CanHandleMediaType(const nsACString& aType,
+ const nsAString& aCodecs)
{
if (!IsEnabled()) {
return false;
}
- // AAC in M4A.
- static char const *const aacAudioCodecs[] = {
- "mp4a.40.2", // AAC-LC
- // TODO: AAC-HE ?
- nullptr
- };
- if (aType.EqualsASCII("audio/mp4") ||
- aType.EqualsASCII("audio/x-m4a")) {
- if (aCodecList) {
- *aCodecList = aacAudioCodecs;
- }
- return true;
+ if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
+ return aCodecs.IsEmpty() || IsSupportedAudioCodec(aCodecs);
+ }
+
+ if (!aType.EqualsASCII("video/mp4")) {
+ return false;
}
- // H.264 + AAC in MP4.
- static char const *const h264Codecs[] = {
- "avc1.42E01E", // H.264 Constrained Baseline Profile Level 3.0
- "avc1.42001E", // H.264 Baseline Profile Level 3.0
- "avc1.58A01E", // H.264 Extended Profile Level 3.0
- "avc1.4D401E", // H.264 Main Profile Level 3.0
- "avc1.64001E", // H.264 High Profile Level 3.0
- "avc1.64001F", // H.264 High Profile Level 3.1
- "mp4a.40.2", // AAC-LC
- // TODO: There must be more profiles here?
- nullptr
- };
- if (aType.EqualsASCII("video/mp4")) {
- if (aCodecList) {
- *aCodecList = h264Codecs;
+ // Verify that all the codecs specifed are ones that we expect that
+ // we can play.
+ nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
+ bool expectMoreTokens = false;
+ while (tokenizer.hasMoreTokens()) {
+ const nsSubstring& token = tokenizer.nextToken();
+ expectMoreTokens = tokenizer.separatorAfterCurrentToken();
+ if (IsSupportedAudioCodec(token) || IsSupportedH264Codec(token)) {
+ continue;
}
- return true;
+ return false;
}
+ if (expectMoreTokens) {
+ // Last codec name was empty
+ return false;
+ }
+ return true;
- return false;
}
static bool
IsFFmpegAvailable()
{
#ifndef MOZ_FFMPEG
return false;
#else
@@ -146,23 +176,23 @@ HavePlatformMPEGDecoders()
{
return Preferences::GetBool("media.fragmented-mp4.use-blank-decoder") ||
#ifdef XP_WIN
// We have H.264/AAC platform decoders on Windows Vista and up.
IsVistaOrLater() ||
#endif
IsFFmpegAvailable() ||
IsAppleAvailable() ||
- IsGonkMP4DecoderAvailable() ||
+ IsGonkMP4DecoderAvailable() ||
// TODO: Other platforms...
false;
}
/* static */
bool
MP4Decoder::IsEnabled()
{
- return HavePlatformMPEGDecoders() &&
- Preferences::GetBool("media.fragmented-mp4.enabled");
+ return Preferences::GetBool("media.fragmented-mp4.enabled") &&
+ HavePlatformMPEGDecoders();
}
} // namespace mozilla
--- a/content/media/fmp4/MP4Decoder.h
+++ b/content/media/fmp4/MP4Decoder.h
@@ -23,22 +23,21 @@ public:
}
virtual MediaDecoderStateMachine* CreateStateMachine();
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
#endif
- // Returns true if aType is a MIME type that we can render with the
- // a MP4 platform decoder backend. If aCodecList is non null,
- // it is filled with a (static const) null-terminated list of strings
- // denoting the codecs we'll playback.
- static bool GetSupportedCodecs(const nsACString& aType,
- char const *const ** aCodecList);
+ // Returns true if aMIMEType is a type that we think we can render with the
+ // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
+ // with a comma-delimited list of codecs to check support for.
+ static bool CanHandleMediaType(const nsACString& aMIMEType,
+ const nsAString& aCodecs = EmptyString());
// Returns true if the MP4 backend is preffed on, and we're running on a
// platform that is likely to have decoders for the contained formats.
static bool IsEnabled();
};
} // namespace mozilla
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -769,17 +769,20 @@ MP4Reader::Seek(int64_t aTime,
return NS_OK;
}
void
MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
int64_t aOffset)
{
if (NS_IsMainThread()) {
- GetTaskQueue()->Dispatch(NS_NewRunnableMethod(this, &MP4Reader::UpdateIndex));
+ if (GetTaskQueue()) {
+ GetTaskQueue()->Dispatch(
+ NS_NewRunnableMethod(this, &MP4Reader::UpdateIndex));
+ }
} else {
UpdateIndex();
}
}
void
MP4Reader::UpdateIndex()
{
--- a/content/media/fmp4/apple/AppleVTDecoder.cpp
+++ b/content/media/fmp4/apple/AppleVTDecoder.cpp
@@ -140,25 +140,27 @@ AppleVTDecoder::Drain()
//
// Implementation details.
//
// Context object to hold a copy of sample metadata.
class FrameRef {
public:
- Microseconds timestamp;
+ Microseconds decode_timestamp;
+ Microseconds composition_timestamp;
Microseconds duration;
int64_t byte_offset;
bool is_sync_point;
explicit FrameRef(mp4_demuxer::MP4Sample* aSample)
{
MOZ_ASSERT(aSample);
- timestamp = aSample->composition_timestamp;
+ decode_timestamp = aSample->decode_timestamp;
+ composition_timestamp = aSample->composition_timestamp;
duration = aSample->duration;
byte_offset = aSample->byte_offset;
is_sync_point = aSample->is_sync_point;
}
};
// Callback passed to the VideoToolbox decoder for returning data.
// This needs to be static because the API takes a C-style pair of
@@ -175,19 +177,20 @@ PlatformCallback(void* decompressionOutp
{
LOG("AppleVideoDecoder %s status %d flags %d", __func__, status, flags);
AppleVTDecoder* decoder =
static_cast<AppleVTDecoder*>(decompressionOutputRefCon);
nsAutoPtr<FrameRef> frameRef =
nsAutoPtr<FrameRef>(static_cast<FrameRef*>(sourceFrameRefCon));
- LOG("mp4 output frame %lld pts %lld duration %lld us%s",
+ LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
frameRef->byte_offset,
- frameRef->timestamp,
+ frameRef->decode_timestamp,
+ frameRef->composition_timestamp,
frameRef->duration,
frameRef->is_sync_point ? " keyframe" : ""
);
// Validate our arguments.
if (status != noErr || !image) {
NS_WARNING("VideoToolbox decoder returned no data");
return;
@@ -297,53 +300,64 @@ AppleVTDecoder::OutputFrame(CVPixelBuffe
// Copy the image data into our own format.
nsAutoPtr<VideoData> data;
data =
VideoData::Create(info,
mImageContainer,
nullptr,
aFrameRef->byte_offset,
- aFrameRef->timestamp,
+ aFrameRef->composition_timestamp,
aFrameRef->duration,
buffer,
aFrameRef->is_sync_point,
- aFrameRef->timestamp,
+ aFrameRef->decode_timestamp,
visible);
// Unlock the returned image data.
CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
if (!data) {
NS_ERROR("Couldn't create VideoData for frame");
mCallback->Error();
return NS_ERROR_FAILURE;
}
// Frames come out in DTS order but we need to output them
// in composition order.
mReorderQueue.Push(data.forget());
- if (mReorderQueue.Length() > 2) {
+ // Assume a frame with a PTS <= current DTS is ready.
+ while (mReorderQueue.Length() > 0) {
VideoData* readyData = mReorderQueue.Pop();
- mCallback->Output(readyData);
+ if (readyData->mTime <= aFrameRef->decode_timestamp) {
+ LOG("returning queued frame with pts %lld", readyData->mTime);
+ mCallback->Output(readyData);
+ } else {
+ LOG("requeued frame with pts %lld > %lld",
+ readyData->mTime, aFrameRef->decode_timestamp);
+ mReorderQueue.Push(readyData);
+ break;
+ }
}
+ LOG("%llu decoded frames queued",
+ static_cast<unsigned long long>(mReorderQueue.Length()));
return NS_OK;
}
// Helper to fill in a timestamp structure.
static CMSampleTimingInfo
TimingInfoFromSample(mp4_demuxer::MP4Sample* aSample)
{
CMSampleTimingInfo timestamp;
timestamp.duration = CMTimeMake(aSample->duration, USECS_PER_S);
timestamp.presentationTimeStamp =
CMTimeMake(aSample->composition_timestamp, USECS_PER_S);
- // No DTS value available from libstagefright.
- timestamp.decodeTimeStamp = CMTimeMake(0, USECS_PER_S);
+ timestamp.decodeTimeStamp =
+ CMTimeMake(aSample->decode_timestamp, USECS_PER_S);
return timestamp;
}
nsresult
AppleVTDecoder::SubmitFrame(mp4_demuxer::MP4Sample* aSample)
{
// For some reason this gives me a double-free error with stagefright.
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
@@ -51,16 +51,17 @@ void
FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
{
AVPacket packet;
av_init_packet(&packet);
aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE);
packet.data = aSample->data;
packet.size = aSample->size;
+ packet.dts = aSample->decode_timestamp;
packet.pts = aSample->composition_timestamp;
packet.flags = aSample->is_sync_point ? AV_PKT_FLAG_KEY : 0;
packet.pos = aSample->byte_offset;
if (!PrepareFrame()) {
NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
mCallback->Error();
return;
deleted file mode 100644
--- a/content/media/test/can_play_type_mpeg.js
+++ /dev/null
@@ -1,51 +0,0 @@
-function check_mp4(v, enabled) {
- function check(type, expected) {
- var ex = enabled ? expected : "";
- is(v.canPlayType(type), ex, type + "='" + ex + "'");
- }
-
- check("video/mp4", "maybe");
- check("audio/mp4", "maybe");
- check("audio/x-m4a", "maybe");
-
- // Not the MIME type that other browsers respond to, so we won't either.
- check("audio/m4a", "");
- // Only Safari responds affirmatively to "audio/aac",
- // so we'll let x-m4a cover aac support.
- check("audio/aac", "");
-
- check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
- check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
- check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
- check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
- check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
- check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
-
- check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
- check("video/mp4; codecs=\"avc1.42001E\"", "probably");
- check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
- check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
- check("video/mp4; codecs=\"avc1.64001E\"", "probably");
- check("video/mp4; codecs=\"avc1.64001F\"", "probably");
-
- check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
- check("audio/mp4; codecs=mp4a.40.2", "probably");
- check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
- check("audio/x-m4a; codecs=mp4a.40.2", "probably");
-}
-
-function check_mp3(v, enabled) {
- function check(type, expected) {
- var ex = enabled ? expected : "";
- is(v.canPlayType(type), ex, type + "='" + ex + "'");
- }
-
- check("audio/mpeg", "maybe");
- check("audio/mp3", "maybe");
-
- check("audio/mpeg; codecs=\"mp3\"", "probably");
- check("audio/mpeg; codecs=mp3", "probably");
-
- check("audio/mp3; codecs=\"mp3\"", "probably");
- check("audio/mp3; codecs=mp3", "probably");
-}
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -103,17 +103,16 @@ support-files =
bug580982.webm
bug580982.webm^headers^
bug603918.webm
bug603918.webm^headers^
bug604067.webm
bug604067.webm^headers^
bug883173.vtt
can_play_type_dash.js
- can_play_type_mpeg.js
can_play_type_ogg.js
can_play_type_wave.js
can_play_type_webm.js
cancellable_request.sjs
chain.ogg
chain.ogg^headers^
chain.ogv
chain.ogv^headers^
--- a/content/media/test/test_can_play_type_mpeg.html
+++ b/content/media/test/test_can_play_type_mpeg.html
@@ -12,19 +12,92 @@ https://bugzilla.mozilla.org/show_bug.cg
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
-<script src="can_play_type_mpeg.js"></script>
<script>
+function check_mp4(v, enabled) {
+ function check(type, expected) {
+ var ex = enabled ? expected : "";
+ is(v.canPlayType(type), ex, type + "='" + ex + "'");
+ }
+
+ check("video/mp4", "maybe");
+ check("audio/mp4", "maybe");
+ check("audio/x-m4a", "maybe");
+
+ // Not the MIME type that other browsers respond to, so we won't either.
+ check("audio/m4a", "");
+ // Only Safari responds affirmatively to "audio/aac",
+ // so we'll let x-m4a cover aac support.
+ check("audio/aac", "");
+
+ // H.264 Constrained Baseline Profile Level 3.0, AAC-LC
+ check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
+
+ // H.264 Constrained Baseline Profile Level 3.0, mp3
+ check("video/mp4; codecs=\"avc1.42E01E, mp3\"", "probably");
+
+ check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
+ check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
+
+ const ProbablyIfNotLinux = !IsLinuxGStreamer() ? "probably" : "";
+
+ // H.264 Main Profile Level 3.0, AAC-LC
+ check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
+ // H.264 Main Profile Level 3.1, AAC-LC
+ check("video/mp4; codecs=\"avc1.4D401F, mp4a.40.2\"", ProbablyIfNotLinux);
+ // H.264 Main Profile Level 4.0, AAC-LC
+ check("video/mp4; codecs=\"avc1.4D4028, mp4a.40.2\"", ProbablyIfNotLinux);
+ // H.264 High Profile Level 3.0, AAC-LC
+ check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
+ // H.264 High Profile Level 3.1, AAC-LC
+ check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
+
+ check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
+ check("video/mp4; codecs=\"avc1.42001E\"", "probably");
+ check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
+ check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
+ check("video/mp4; codecs=\"avc1.64001F\"", "probably");
+
+ // AAC-LC
+ check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
+ check("audio/mp4; codecs=mp4a.40.2", "probably");
+ check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
+ check("audio/x-m4a; codecs=mp4a.40.2", "probably");
+
+ // HE-AAC v1
+ check("audio/mp4; codecs=\"mp4a.40.5\"", ProbablyIfNotLinux);
+ check("audio/mp4; codecs=mp4a.40.5", ProbablyIfNotLinux);
+ check("audio/x-m4a; codecs=\"mp4a.40.5\"", ProbablyIfNotLinux);
+ check("audio/x-m4a; codecs=mp4a.40.5", ProbablyIfNotLinux);
+
+}
+
+function check_mp3(v, enabled) {
+ function check(type, expected) {
+ var ex = enabled ? expected : "";
+ is(v.canPlayType(type), ex, type + "='" + ex + "'");
+ }
+
+ check("audio/mpeg", "maybe");
+ check("audio/mp3", "maybe");
+
+ check("audio/mpeg; codecs=\"mp3\"", "probably");
+ check("audio/mpeg; codecs=mp3", "probably");
+
+ check("audio/mp3; codecs=\"mp3\"", "probably");
+ check("audio/mp3; codecs=mp3", "probably");
+}
+
function IsWindowsVistaOrLater() {
var re = /Windows NT (\d+\.\d)/;
var winver = navigator.userAgent.match(re);
return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
}
function IsMacOSLionOrLater() {
var re = /Mac OS X (\d+)\.(\d+)/;
@@ -40,16 +113,21 @@ function IsMacOSLionOrLater() {
function getPref(name) {
var pref = false;
try {
pref = SpecialPowers.getBoolPref(name);
} catch(ex) { }
return pref;
}
+function IsLinuxGStreamer() {
+ return /Linux/.test(navigator.userAgent) &&
+ getPref("media.gstreamer.enabled");
+}
+
// Check whether we should expect the new MP4Reader-based support to work.
function IsMP4ReaderAvailable() {
var prefs = getPref("media.fragmented-mp4.enabled") &&
getPref("media.fragmented-mp4.exposed");
return prefs && (IsWindowsVistaOrLater() || IsMacOSLionOrLater());
}
var haveMp4 = (getPref("media.windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
--- a/content/media/wmf/WMFDecoder.cpp
+++ b/content/media/wmf/WMFDecoder.cpp
@@ -49,48 +49,27 @@ IsSupportedH264Codec(const nsAString& aC
{
// According to the WMF documentation:
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
// "The Media Foundation H.264 video decoder is a Media Foundation Transform
// that supports decoding of Baseline, Main, and High profiles, up to level
// 5.1.". We also report that we can play Extended profile, as there are
// bitstreams that are Extended compliant that are also Baseline compliant.
- // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
- // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
- // We ignore the constraint_set flags, as it's not clear from the WMF
- // documentation what constraints the WMF H.264 decoder supports.
- // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
- // for more details.
- if (aCodec.Length() != strlen("avc1.PPCCLL")) {
- return false;
- }
-
- // Verify the codec starts with "avc1.".
- const nsAString& sample = Substring(aCodec, 0, 5);
- if (!sample.EqualsASCII("avc1.")) {
+ int16_t profile = 0, level = 0;
+ if (!ExtractH264CodecDetails(aCodec, profile, level)) {
return false;
}
- // Extract the profile_idc and level_idc. Note: the constraint_set flags
- // are ignored, it's not clear from the WMF documentation if they make a
- // difference.
- nsresult rv = NS_OK;
- const int32_t profile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
- NS_ENSURE_SUCCESS(rv, false);
-
- const int32_t level = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
- NS_ENSURE_SUCCESS(rv, false);
-
- return level >= eAVEncH264VLevel1 &&
- level <= eAVEncH264VLevel5_1 &&
- (profile == eAVEncH264VProfile_Base ||
- profile == eAVEncH264VProfile_Main ||
- profile == eAVEncH264VProfile_Extended ||
- profile == eAVEncH264VProfile_High);
+ return level >= H264_LEVEL_1 &&
+ level <= H264_LEVEL_5_1 &&
+ (profile == H264_PROFILE_BASE ||
+ profile == H264_PROFILE_MAIN ||
+ profile == H264_PROFILE_EXTENDED ||
+ profile == H264_PROFILE_HIGH);
}
bool
WMFDecoder::CanPlayType(const nsACString& aType,
const nsAString& aCodecs)
{
if (!MediaDecoder::IsWMFEnabled() ||
NS_FAILED(LoadDLLs())) {
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -619,16 +619,17 @@ Console::WrapObject(JSContext* aCx)
}
METHOD(Log, "log")
METHOD(Info, "info")
METHOD(Warn, "warn")
METHOD(Error, "error")
METHOD(Exception, "exception")
METHOD(Debug, "debug")
+METHOD(Table, "table")
void
Console::Trace(JSContext* aCx)
{
const Sequence<JS::Value> data;
Method(aCx, MethodTrace, NS_LITERAL_STRING("trace"), data);
}
--- a/dom/base/Console.h
+++ b/dom/base/Console.h
@@ -62,16 +62,19 @@ public:
void
Exception(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Debug(JSContext* aCx, const Sequence<JS::Value>& aData);
void
+ Table(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+ void
Trace(JSContext* aCx);
void
Dir(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Group(JSContext* aCx, const Sequence<JS::Value>& aData);
@@ -106,16 +109,17 @@ private:
enum MethodName
{
MethodLog,
MethodInfo,
MethodWarn,
MethodError,
MethodException,
MethodDebug,
+ MethodTable,
MethodTrace,
MethodDir,
MethodGroup,
MethodGroupCollapsed,
MethodGroupEnd,
MethodTime,
MethodTimeEnd,
MethodAssert,
--- a/dom/base/test/test_console.xul
+++ b/dom/base/test/test_console.xul
@@ -10,16 +10,17 @@
<iframe id="iframe" />
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
ok("console" in window, "Console exists");
window.console.log(42);
+ ok("table" in console, "Console has the 'table' method.");
window.console = 42;
is(window.console, 42, "Console is replacable");
var frame = document.getElementById("iframe");
ok(frame, "Frame must exist");
frame.src="http://mochi.test:8888/tests/dom/base/test/file_empty.html";
frame.onload = function() {
ok("console" in frame.contentWindow, "Console exists in the iframe");
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -24,18 +24,17 @@ namespace dom {
jsid s_length_id = JSID_VOID;
bool
DefineStaticJSVals(JSContext* cx)
{
return InternJSString(cx, s_length_id, "length");
}
-
-const char HandlerFamily = 0;
+const char DOMProxyHandler::family = 0;
js::DOMProxyShadowsResult
DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
{
JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
if (v.isObject()) {
bool hasOwn;
Rooted<JSObject*> object(cx, &v.toObject());
@@ -55,17 +54,17 @@ DOMProxyShadows(JSContext* cx, JS::Handl
return hasOwn ? js::Shadows : js::DoesntShadowUnique;
}
// Store the information for the specialized ICs.
struct SetDOMProxyInformation
{
SetDOMProxyInformation() {
- js::SetDOMProxyInformation((const void*) &HandlerFamily,
+ js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
}
};
SetDOMProxyInformation gSetDOMProxyInformation;
// static
JSObject*
@@ -358,10 +357,30 @@ IdToInt32(JSContext* cx, JS::Handle<jsid
bool
DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp, bool *done) const
{
*done = false;
return true;
}
+//static
+JSObject *
+DOMProxyHandler::GetExpandoObject(JSObject *obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isObject()) {
+ return &v.toObject();
+ }
+
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ v = expandoAndGeneration->expando;
+ return v.isUndefined() ? nullptr : &v.toObject();
+}
+
} // namespace dom
} // namespace mozilla
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -19,27 +19,16 @@ namespace mozilla {
namespace dom {
enum {
JSPROXYSLOT_EXPANDO = 0
};
template<typename T> struct Prefable;
-// This variable exists solely to provide a unique address for use as an identifier.
-extern const char HandlerFamily;
-inline const void* ProxyFamily() { return &HandlerFamily; }
-
-inline bool IsDOMProxy(JSObject *obj)
-{
- const js::Class* clasp = js::GetObjectClass(obj);
- return clasp->isProxy() &&
- js::GetProxyHandler(obj)->family() == ProxyFamily();
-}
-
class BaseDOMProxyHandler : public js::BaseProxyHandler
{
public:
explicit BaseDOMProxyHandler(const void* aProxyFamily, bool aHasPrototype = false)
: js::BaseProxyHandler(aProxyFamily, aHasPrototype)
{}
// Implementations of traps that can be implemented in terms of
@@ -84,17 +73,17 @@ protected:
bool ignoreNamedProps,
JS::MutableHandle<JSPropertyDescriptor> desc) const = 0;
};
class DOMProxyHandler : public BaseDOMProxyHandler
{
public:
DOMProxyHandler()
- : BaseDOMProxyHandler(ProxyFamily())
+ : BaseDOMProxyHandler(&family)
{
}
bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE
{
bool unused;
@@ -116,39 +105,33 @@ public:
/*
* If assigning to proxy[id] hits a named setter with OverrideBuiltins or
* an indexed setter, call it and set *done to true on success. Otherwise, set
* *done to false.
*/
virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp, bool *done) const;
- static JSObject* GetExpandoObject(JSObject* obj)
- {
- MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
- JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
- if (v.isObject()) {
- return &v.toObject();
- }
+ static JSObject* GetExpandoObject(JSObject* obj);
- if (v.isUndefined()) {
- return nullptr;
- }
-
- js::ExpandoAndGeneration* expandoAndGeneration =
- static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
- v = expandoAndGeneration->expando;
- return v.isUndefined() ? nullptr : &v.toObject();
- }
/* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */
static JSObject* GetAndClearExpandoObject(JSObject* obj);
static JSObject* EnsureExpandoObject(JSContext* cx,
JS::Handle<JSObject*> obj);
+
+ static const char family;
};
+inline bool IsDOMProxy(JSObject *obj)
+{
+ const js::Class* clasp = js::GetObjectClass(obj);
+ return clasp->isProxy() &&
+ js::GetProxyHandler(obj)->family() == &DOMProxyHandler::family;
+}
+
inline const DOMProxyHandler*
GetDOMProxyHandler(JSObject* obj)
{
MOZ_ASSERT(IsDOMProxy(obj));
return static_cast<const DOMProxyHandler*>(js::GetProxyHandler(obj));
}
extern jsid s_length_id;
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -67,16 +67,18 @@
#include "mozilla/EnumeratedArrayCycleCollection.h"
#include "Layers.h"
#ifdef MOZ_WIDGET_GONK
#include "mozilla/layers/ShadowLayers.h"
#endif
+#include <queue>
+
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla::layers;
WebGLObserver::WebGLObserver(WebGLContext* aContext)
: mContext(aContext)
@@ -430,23 +432,21 @@ WebGLContext::SetContextOptions(JSContex
WebGLContextOptions newOpts;
newOpts.stencil = attributes.mStencil;
newOpts.depth = attributes.mDepth;
newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
newOpts.antialias = attributes.mAntialias;
newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
+
if (attributes.mAlpha.WasPassed()) {
- newOpts.alpha = attributes.mAlpha.Value();
+ newOpts.alpha = attributes.mAlpha.Value();
}
- // enforce that if stencil is specified, we also give back depth
- newOpts.depth |= newOpts.stencil;
-
// Don't do antialiasing if we've disabled MSAA.
if (!gfxPrefs::MSAALevel()) {
newOpts.antialias = false;
}
#if 0
GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
newOpts.antialias ? 1 : 0,
@@ -466,266 +466,467 @@ WebGLContext::SetContextOptions(JSContex
mOptions = newOpts;
return NS_OK;
}
#ifdef DEBUG
int32_t
WebGLContext::GetWidth() const
{
- return mWidth;
+ return mWidth;
}
int32_t
WebGLContext::GetHeight() const
{
- return mHeight;
+ return mHeight;
}
#endif
+/* So there are a number of points of failure here. We might fail based
+ * on EGL vs. WGL, or we might fail to alloc a too-large size, or we
+ * might not be able to create a context with a certain combo of context
+ * creation attribs.
+ *
+ * We don't want to test the complete fallback matrix. (for now, at
+ * least) Instead, attempt creation in this order:
+ * 1. By platform API. (e.g. EGL vs. WGL)
+ * 2. By context creation attribs.
+ * 3. By size.
+ *
+ * That is, try to create headless contexts based on the platform API.
+ * Next, create dummy-sized backbuffers for the contexts with the right
+ * caps. Finally, resize the backbuffer to an acceptable size given the
+ * requested size.
+ */
+
+static bool
+IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
+{
+ int32_t status;
+ if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
+ return false;
+
+ return status != nsIGfxInfo::FEATURE_STATUS_OK;
+}
+
+static already_AddRefed<GLContext>
+CreateHeadlessNativeGL(bool forceEnabled,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ WebGLContext* webgl)
+{
+ if (!forceEnabled &&
+ IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
+ {
+ webgl->GenerateWarning("Refused to create native OpenGL context"
+ " because of blacklisting.");
+ return nullptr;
+ }
+
+ nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless();
+ if (!gl) {
+ webgl->GenerateWarning("Error during native OpenGL init.");
+ return nullptr;
+ }
+ MOZ_ASSERT(!gl->IsANGLE());
+
+ return gl.forget();
+}
+
+// Note that we have a separate call for ANGLE and EGL, even though
+// right now, we get ANGLE implicitly by using EGL on Windows.
+// Eventually, we want to be able to pick ANGLE-EGL or native EGL.
+static already_AddRefed<GLContext>
+CreateHeadlessANGLE(bool forceEnabled,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ WebGLContext* webgl)
+{
+ nsRefPtr<GLContext> gl;
+
+#ifdef XP_WIN
+ if (!forceEnabled &&
+ IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_ANGLE))
+ {
+ webgl->GenerateWarning("Refused to create ANGLE OpenGL context"
+ " because of blacklisting.");
+ return nullptr;
+ }
+
+ gl = gl::GLContextProviderEGL::CreateHeadless();
+ if (!gl) {
+ webgl->GenerateWarning("Error during ANGLE OpenGL init.");
+ return nullptr;
+ }
+ MOZ_ASSERT(gl->IsANGLE());
+#endif
+
+ return gl.forget();
+}
+
+static already_AddRefed<GLContext>
+CreateHeadlessEGL(bool forceEnabled,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ WebGLContext* webgl)
+{
+ nsRefPtr<GLContext> gl;
+
+#ifdef ANDROID
+ gl = gl::GLContextProviderEGL::CreateHeadless();
+ if (!gl) {
+ webgl->GenerateWarning("Error during EGL OpenGL init.");
+ return nullptr;
+ }
+ MOZ_ASSERT(!gl->IsANGLE());
+#endif
+
+ return gl.forget();
+}
+
+
+static already_AddRefed<GLContext>
+CreateHeadlessGL(bool forceEnabled,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ WebGLContext* webgl)
+{
+ bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
+ bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
+
+ if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
+ disableANGLE = true;
+ }
+
+ nsRefPtr<GLContext> gl;
+
+ if (preferEGL)
+ gl = CreateHeadlessEGL(forceEnabled, gfxInfo, webgl);
+
+ if (!gl && !disableANGLE)
+ gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, webgl);
+
+ if (!gl)
+ gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo, webgl);
+
+ return gl.forget();
+}
+
+// Try to create a dummy offscreen with the given caps.
+static bool
+CreateOffscreenWithCaps(GLContext* gl, const SurfaceCaps& caps)
+{
+ gfx::IntSize dummySize(16, 16);
+ return gl->InitOffscreen(dummySize, caps);
+}
+
+static void
+PopulateCapFallbackQueue(const SurfaceCaps& baseCaps,
+ std::queue<SurfaceCaps>* fallbackCaps)
+{
+ fallbackCaps->push(baseCaps);
+
+ // Dropping antialias drops our quality, but not our correctness.
+ // The user basically doesn't have to handle if this fails, they
+ // just get reduced quality.
+ if (baseCaps.antialias) {
+ SurfaceCaps nextCaps(baseCaps);
+ nextCaps.antialias = false;
+ PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+ }
+
+ // If we have to drop one of depth or stencil, we'd prefer to keep
+ // depth. However, the client app will need to handle if this
+ // doesn't work.
+ if (baseCaps.stencil) {
+ SurfaceCaps nextCaps(baseCaps);
+ nextCaps.stencil = false;
+ PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+ }
+
+ if (baseCaps.depth) {
+ SurfaceCaps nextCaps(baseCaps);
+ nextCaps.depth = false;
+ PopulateCapFallbackQueue(nextCaps, fallbackCaps);
+ }
+}
+
+static bool
+CreateOffscreen(GLContext* gl,
+ const WebGLContextOptions& options,
+ const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+ WebGLContext* webgl,
+ layers::ISurfaceAllocator* surfAllocator)
+{
+ SurfaceCaps baseCaps;
+
+ baseCaps.color = true;
+ baseCaps.alpha = options.alpha;
+ baseCaps.antialias = options.antialias;
+ baseCaps.depth = options.depth;
+ baseCaps.preserve = options.preserveDrawingBuffer;
+ baseCaps.stencil = options.stencil;
+
+ // we should really have this behind a
+ // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
+ // for now it's just behind a pref for testing/evaluation.
+ baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
+
+#ifdef MOZ_WIDGET_GONK
+ baseCaps.surfaceAllocator = surfAllocator;
+#endif
+
+ // Done with baseCaps construction.
+
+ bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
+ if (!forceAllowAA &&
+ IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
+ {
+ webgl->GenerateWarning("Disallowing antialiased backbuffers due"
+ " to blacklisting.");
+ baseCaps.antialias = false;
+ }
+
+ std::queue<SurfaceCaps> fallbackCaps;
+ PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
+
+ bool created = false;
+ while (!fallbackCaps.empty()) {
+ SurfaceCaps& caps = fallbackCaps.front();
+
+ created = CreateOffscreenWithCaps(gl, caps);
+ if (created)
+ break;
+
+ fallbackCaps.pop();
+ }
+
+ return created;
+}
+
+bool
+WebGLContext::CreateOffscreenGL(bool forceEnabled)
+{
+ nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+
+ layers::ISurfaceAllocator* surfAllocator = nullptr;
+#ifdef MOZ_WIDGET_GONK
+ nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
+ if (docWidget) {
+ layers::LayerManager* layerManager = docWidget->GetLayerManager();
+ if (layerManager) {
+ // XXX we really want "AsSurfaceAllocator" here for generality
+ layers::ShadowLayerForwarder* forwarder = layerManager->AsShadowForwarder();
+ if (forwarder) {
+ surfAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
+ }
+ }
+ }
+#endif
+
+ gl = CreateHeadlessGL(forceEnabled, gfxInfo, this);
+
+ do {
+ if (!gl)
+ break;
+
+ if (!CreateOffscreen(gl, mOptions, gfxInfo, this, surfAllocator))
+ break;
+
+ if (!InitAndValidateGL())
+ break;
+
+ return true;
+ } while (false);
+
+ gl = nullptr;
+ return false;
+}
+
+// Fallback for resizes:
+bool
+WebGLContext::ResizeBackbuffer(uint32_t requestedWidth, uint32_t requestedHeight)
+{
+ uint32_t width = requestedWidth;
+ uint32_t height = requestedHeight;
+
+ bool resized = false;
+ while (width || height) {
+ width = width ? width : 1;
+ height = height ? height : 1;
+
+ gfx::IntSize curSize(width, height);
+ if (gl->ResizeOffscreen(curSize)) {
+ resized = true;
+ break;
+ }
+
+ width /= 2;
+ height /= 2;
+ }
+
+ if (!resized)
+ return false;
+
+ mWidth = gl->OffscreenSize().width;
+ mHeight = gl->OffscreenSize().height;
+ MOZ_ASSERT((uint32_t)mWidth == width);
+ MOZ_ASSERT((uint32_t)mHeight == height);
+
+ if (width != requestedWidth ||
+ height != requestedHeight)
+ {
+ GenerateWarning("Requested size %dx%d was too large, but resize"
+ " to %dx%d succeeded.",
+ requestedWidth, requestedHeight,
+ width, height);
+ }
+ return true;
+}
+
+
NS_IMETHODIMP
-WebGLContext::SetDimensions(int32_t width, int32_t height)
+WebGLContext::SetDimensions(int32_t sWidth, int32_t sHeight)
{
// Early error return cases
+ if (!GetCanvas())
+ return NS_ERROR_FAILURE;
- if (width < 0 || height < 0) {
+ if (sWidth < 0 || sHeight < 0) {
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
return NS_ERROR_OUT_OF_MEMORY;
}
- if (!GetCanvas())
- return NS_ERROR_FAILURE;
+ uint32_t width = sWidth;
+ uint32_t height = sHeight;
// Early success return cases
-
GetCanvas()->InvalidateCanvas();
- if (gl && mWidth == width && mHeight == height)
- return NS_OK;
-
// Zero-sized surfaces can cause problems.
if (width == 0) {
width = 1;
}
if (height == 0) {
height = 1;
}
// If we already have a gl context, then we just need to resize it
if (gl) {
+ if ((uint32_t)mWidth == width &&
+ (uint32_t)mHeight == height)
+ {
+ return NS_OK;
+ }
+
+ if (IsContextLost())
+ return NS_OK;
+
MakeContextCurrent();
// If we've already drawn, we should commit the current buffer.
PresentScreenBuffer();
// ResizeOffscreen scraps the current prod buffer before making a new one.
- gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
- // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
+ if (!ResizeBackbuffer(width, height)) {
+ GenerateWarning("WebGL context failed to resize.");
+ ForceLoseContext();
+ return NS_OK;
+ }
// everything's good, we're done here
- mWidth = gl->OffscreenSize().width;
- mHeight = gl->OffscreenSize().height;
mResetLayer = true;
-
mBackbufferNeedsClear = true;
return NS_OK;
}
// End of early return cases.
// At this point we know that we're not just resizing an existing context,
// we are initializing a new context.
// if we exceeded either the global or the per-principal limit for WebGL contexts,
// lose the oldest-used context now to free resources. Note that we can't do that
// in the WebGLContext constructor as we don't have a canvas element yet there.
// Here is the right place to do so, as we are about to create the OpenGL context
// and that is what can fail if we already have too many.
LoseOldestWebGLContextIfLimitExceeded();
- // Get some prefs for some preferred/overriden things
- NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
-
-#ifdef XP_WIN
- bool preferEGL =
- Preferences::GetBool("webgl.prefer-egl", false);
- bool preferOpenGL =
- Preferences::GetBool("webgl.prefer-native-gl", false);
-#endif
- bool forceEnabled =
- Preferences::GetBool("webgl.force-enabled", false);
- bool disabled =
- Preferences::GetBool("webgl.disabled", false);
- bool prefer16bit =
- Preferences::GetBool("webgl.prefer-16bpp", false);
-
- ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
-
- if (disabled)
- return NS_ERROR_FAILURE;
-
// We're going to create an entirely new context. If our
// generation is not 0 right now (that is, if this isn't the first
// context we're creating), we may have to dispatch a context lost
// event.
// If incrementing the generation would cause overflow,
// don't allow it. Allowing this would allow us to use
// resource handles created from older context generations.
- if (!(mGeneration + 1).isValid())
+ if (!(mGeneration + 1).isValid()) {
+ GenerateWarning("Too many WebGL contexts created this run.");
return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
-
- SurfaceCaps caps;
-
- caps.color = true;
- caps.alpha = mOptions.alpha;
- caps.depth = mOptions.depth;
- caps.stencil = mOptions.stencil;
-
- // we should really have this behind a
- // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
- // for now it's just behind a pref for testing/evaluation.
- caps.bpp16 = prefer16bit;
-
- caps.preserve = mOptions.preserveDrawingBuffer;
-
-#ifdef MOZ_WIDGET_GONK
- nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
- if (docWidget) {
- layers::LayerManager *layerManager = docWidget->GetLayerManager();
- if (layerManager) {
- // XXX we really want "AsSurfaceAllocator" here for generality
- layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder();
- if (forwarder) {
- caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
- }
- }
- }
-#endif
-
- bool forceMSAA =
- Preferences::GetBool("webgl.msaa-force", false);
-
- int32_t status;
- nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
- if (mOptions.antialias &&
- gfxInfo &&
- NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
- if (status == nsIGfxInfo::FEATURE_STATUS_OK || forceMSAA) {
- caps.antialias = true;
- }
}
-#ifdef XP_WIN
- if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
- preferEGL = true;
- }
-#endif
-
- // Ask GfxInfo about what we should use
- bool useOpenGL = true;
-
-#ifdef XP_WIN
- bool useANGLE = true;
-#endif
+ // Get some prefs for some preferred/overriden things
+ NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
- if (gfxInfo && !forceEnabled) {
- if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
- if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
- useOpenGL = false;
- }
- }
-#ifdef XP_WIN
- if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
- if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
- useANGLE = false;
- }
- }
-#endif
+ bool disabled = Preferences::GetBool("webgl.disabled", false);
+ if (disabled) {
+ GenerateWarning("WebGL creation is disabled, and so disallowed here.");
+ return NS_ERROR_FAILURE;
}
-#ifdef XP_WIN
- // allow forcing GL and not EGL/ANGLE
- if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
- preferEGL = false;
- useANGLE = false;
- useOpenGL = true;
- }
-#endif
-
- gfxIntSize size(width, height);
+ // Alright, now let's start trying.
+ bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
+ ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
-#ifdef XP_WIN
- // if we want EGL, try it now
- if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
- gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps);
- if (!gl || !InitAndValidateGL()) {
- GenerateWarning("Error during ANGLE OpenGL ES initialization");
- return NS_ERROR_FAILURE;
- }
+ if (!CreateOffscreenGL(forceEnabled)) {
+ GenerateWarning("WebGL creation failed.");
+ return NS_ERROR_FAILURE;
}
-#endif
+ MOZ_ASSERT(gl);
- // try the default provider, whatever that is
- if (!gl && useOpenGL) {
- gl = gl::GLContextProvider::CreateOffscreen(size, caps);
- if (gl && !InitAndValidateGL()) {
- GenerateWarning("Error during OpenGL initialization");
- return NS_ERROR_FAILURE;
- }
- }
-
- if (!gl) {
- GenerateWarning("Can't get a usable WebGL context");
+ if (!ResizeBackbuffer(width, height)) {
+ GenerateWarning("Initializing WebGL backbuffer failed.");
return NS_ERROR_FAILURE;
}
#ifdef DEBUG
if (gl->DebugMode()) {
printf_stderr("--- WebGL context created: %p\n", gl.get());
}
#endif
- mWidth = width;
- mHeight = height;
- mViewportWidth = width;
- mViewportHeight = height;
mResetLayer = true;
mOptionsFrozen = true;
// increment the generation number
++mGeneration;
-#if 0
- if (mGeneration > 0) {
- // XXX dispatch context lost event
- }
-#endif
MakeContextCurrent();
+ gl->fViewport(0, 0, mWidth, mHeight);
+ mViewportWidth = mWidth;
+ mViewportHeight = mHeight;
+
// Make sure that we clear this out, otherwise
// we'll end up displaying random memory
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
AssertCachedBindings();
AssertCachedState();
// Clear immediately, because we need to present the cleared initial
// buffer.
mBackbufferNeedsClear = true;
ClearBackbufferIfNeeded();
mShouldPresent = true;
- MOZ_ASSERT(gl->Caps().color == caps.color);
- MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
- MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
- MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
- MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
- MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
+ MOZ_ASSERT(gl->Caps().color);
+ MOZ_ASSERT(gl->Caps().alpha == mOptions.alpha);
+ MOZ_ASSERT(gl->Caps().depth == mOptions.depth || !gl->Caps().depth);
+ MOZ_ASSERT(gl->Caps().stencil == mOptions.stencil || !gl->Caps().stencil);
+ MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias || !gl->Caps().antialias);
+ MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
AssertCachedBindings();
AssertCachedState();
reporter.SetSuccessful();
return NS_OK;
}
@@ -1206,17 +1407,17 @@ WebGLContext::PresentScreenBuffer()
if (!mShouldPresent) {
return false;
}
gl->MakeCurrent();
MOZ_ASSERT(!mBackbufferNeedsClear);
if (!gl->PublishFrame()) {
- this->ForceLoseContext();
+ ForceLoseContext();
return false;
}
if (!mOptions.preserveDrawingBuffer) {
mBackbufferNeedsClear = true;
}
mShouldPresent = false;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1031,17 +1031,19 @@ protected:
bool InitWebGL2();
// -------------------------------------------------------------------------
// Validation functions (implemented in WebGLContextValidate.cpp)
GLenum BaseTexFormat(GLenum internalFormat) const;
+ bool CreateOffscreenGL(bool forceEnabled);
bool InitAndValidateGL();
+ bool ResizeBackbuffer(uint32_t width, uint32_t height);
bool ValidateBlendEquationEnum(GLenum cap, const char *info);
bool ValidateBlendFuncDstEnum(GLenum mode, const char *info);
bool ValidateBlendFuncSrcEnum(GLenum mode, const char *info);
bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info);
bool ValidateTextureTargetEnum(GLenum target, const char *info);
bool ValidateComparisonEnum(GLenum target, const char *info);
bool ValidateStencilOpEnum(GLenum action, const char *info);
bool ValidateFaceEnum(GLenum face, const char *info);
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -170,20 +170,20 @@ pref(webgl.force-layers-readback,true) r
# Check that our experimental prefs still work:
# 16bpp:
skip-if(winWidget) pref(webgl.prefer-16bpp,true) random-if(Android&&AndroidVersion<15) == webgl-color-test.html?16bpp wrapper.html?colors.png
skip-if(winWidget) pref(webgl.prefer-16bpp,true) pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15) == webgl-color-test.html?16bpp&readback wrapper.html?colors.png
# Force native GL (Windows):
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true) == webgl-clear-test.html?native-gl wrapper.html?green.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true) == webgl-orientation-test.html?native-gl wrapper.html?white-top-left.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true) == webgl-color-test.html?native-gl wrapper.html?colors.png
-skip-if(!winWidget) pref(webgl.prefer-native-gl,true) pref(webgl.prefer-16bpp,true) == webgl-color-test.html?native-gl&16bpp wrapper.html?colors.png
+skip-if(!winWidget) pref(webgl.disable-angle,true) == webgl-clear-test.html?native-gl wrapper.html?green.png
+skip-if(!winWidget) pref(webgl.disable-angle,true) == webgl-orientation-test.html?native-gl wrapper.html?white-top-left.png
+skip-if(!winWidget) pref(webgl.disable-angle,true) == webgl-color-test.html?native-gl wrapper.html?colors.png
+skip-if(!winWidget) pref(webgl.disable-angle,true) pref(webgl.prefer-16bpp,true) == webgl-color-test.html?native-gl&16bpp wrapper.html?colors.png
# Non-WebGL Reftests!
# Do we correctly handle multiple clip paths?
!= clip-multiple-paths.html clip-multiple-paths-badref.html
# Bug 815648
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -2276,16 +2276,44 @@ PeerConnectionWrapper.prototype = {
ok(numRemoteCandidates, "Have remotecandidate stat(s)");
} else {
is(numLocalCandidates, 0, "Have no localcandidate stats");
is(numRemoteCandidates, 0, "Have no remotecandidate stats");
}
},
/**
+ * Property-matching function for finding a certain stat in passed-in stats
+ *
+ * @param {object} stats
+ * The stats to check from this PeerConnectionWrapper
+ * @param {object} props
+ * The properties to look for
+ * @returns {boolean} Whether an entry containing all match-props was found.
+ */
+ hasStat : function PCW_hasStat(stats, props) {
+ for (var key in stats) {
+ if (stats.hasOwnProperty(key)) {
+ var res = stats[key];
+ var match = true;
+ for (var prop in props) {
+ if (res[prop] !== props[prop]) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+
+ /**
* Closes the connection
*/
close : function PCW_close() {
// It might be that a test has already closed the pc. In those cases
// we should not fail.
try {
this._pc.close();
info(this + ": Closed connection.");
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -408,16 +408,184 @@ var commandsPeerConnection = [
[
'PC_REMOTE_CHECK_STATS',
function (test) {
test.pcRemote.getStats(null, function(stats) {
test.pcRemote.checkStats(stats);
test.next();
});
}
+ ],
+ [
+ 'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+ function (test) {
+ var pc = test.pcLocal;
+ var stream = pc._pc.getLocalStreams()[0];
+ var track = stream && stream.getAudioTracks()[0];
+ if (track) {
+ var msg = "pcLocal.HasStat outbound audio rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+ function (test) {
+ var pc = test.pcLocal;
+ var stream = pc._pc.getLocalStreams()[0];
+ var track = stream && stream.getVideoTracks()[0];
+ if (track) {
+ var msg = "pcLocal.HasStat outbound video rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+ function (test) {
+ var pc = test.pcLocal;
+ var stream = pc._pc.getRemoteStreams()[0];
+ var track = stream && stream.getAudioTracks()[0];
+ if (track) {
+ var msg = "pcLocal.HasStat inbound audio rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+ function (test) {
+ var pc = test.pcLocal;
+ var stream = pc._pc.getRemoteStreams()[0];
+ var track = stream && stream.getVideoTracks()[0];
+ if (track) {
+ var msg = "pcLocal.HasStat inbound video rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+ function (test) {
+ var pc = test.pcRemote;
+ var stream = pc._pc.getLocalStreams()[0];
+ var track = stream && stream.getAudioTracks()[0];
+ if (track) {
+ var msg = "pcRemote.HasStat outbound audio rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+ function (test) {
+ var pc = test.pcRemote;
+ var stream = pc._pc.getLocalStreams()[0];
+ var track = stream && stream.getVideoTracks()[0];
+ if (track) {
+ var msg = "pcRemote.HasStat outbound audio rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+ function (test) {
+ var pc = test.pcRemote;
+ var stream = pc._pc.getRemoteStreams()[0];
+ var track = stream && stream.getAudioTracks()[0];
+ if (track) {
+ var msg = "pcRemote.HasStat inbound audio rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
+ ],
+ [
+ 'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+ function (test) {
+ var pc = test.pcRemote;
+ var stream = pc._pc.getRemoteStreams()[0];
+ var track = stream && stream.getVideoTracks()[0];
+ if (track) {
+ var msg = "pcRemote.HasStat inbound video rtp ";
+ pc.getStats(track, function(stats) {
+ ok(pc.hasStat(stats,
+ { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+ msg + "1");
+ ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+ ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+ test.next();
+ });
+ } else {
+ test.next();
+ }
+ }
]
];
/**
* Default list of commands to execute for a Datachannel test.
*/
var commandsDataChannel = [
--- a/dom/plugins/ipc/hangui/moz.build
+++ b/dom/plugins/ipc/hangui/moz.build
@@ -13,16 +13,17 @@ UNIFIED_SOURCES += [
'PluginHangUIChild.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
DEFINES['NS_NO_XPCOM'] = True
DEFINES['_HAS_EXCEPTIONS'] = 0
DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
if CONFIG['GNU_CC']:
WIN32_EXE_LDFLAGS += ['-municode']
RCINCLUDE = 'HangUIDlg.rc'
OS_LIBS += [
'comctl32',
--- a/dom/plugins/test/mochitest/dialog_watcher.js
+++ b/dom/plugins/test/mochitest/dialog_watcher.js
@@ -92,22 +92,32 @@ DialogWatcher.prototype.init = function(
if (!this.getWindowTextW) {
this.getWindowTextW = user32.declare("GetWindowTextW",
ctypes.winapi_abi,
ctypes.int,
ctypes.uintptr_t,
ctypes.jschar.ptr,
ctypes.int);
}
+ if (!this.messageBox) {
+ // Handy for debugging this code
+ this.messageBox = user32.declare("MessageBoxW",
+ ctypes.winapi_abi,
+ ctypes.int,
+ ctypes.uintptr_t,
+ ctypes.jschar.ptr,
+ ctypes.jschar.ptr,
+ ctypes.uint32_t);
+ }
};
DialogWatcher.prototype.getWindowText = function(hwnd) {
var bufType = ctypes.ArrayType(ctypes.jschar);
var buffer = new bufType(256);
-
+
if (this.getWindowTextW(hwnd, buffer, buffer.length)) {
return buffer.readString();
}
};
DialogWatcher.prototype.processWindowEvents = function(timeout) {
var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
var nhwnd = Number(hwnd)
@@ -149,23 +159,25 @@ DialogWatcher.prototype.processWindowEve
}
if (!timeout) {
timeout = INFINITE;
}
var waitStatus = WAIT_OBJECT_0;
var expectingStart = this.onDialogStart && this.hwnd === undefined;
+ var startWaitTime = Date.now();
while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) {
waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ?
INFINITE : timeout, 0);
if (waitStatus == WAIT_OBJECT_0) {
var msg = new this.msgType;
this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE);
- } else if (waitStatus == WAIT_TIMEOUT) {
+ }
+ if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) {
break;
}
}
this.unhookWinEvent(hook);
// Returns true if the hook was successful, something was found, and we never timed out
return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0;
};
--- a/dom/plugins/test/mochitest/test_hangui.xul
+++ b/dom/plugins/test/mochitest/test_hangui.xul
@@ -4,26 +4,29 @@
type="text/css"?>
<window title="Basic Plugin Tests"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<title>Plugin Hang UI Test</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
+ src="utils.js" />
+ <script type="application/javascript"
src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
<script type="application/javascript"
src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
<body xmlns="http://www.w3.org/1999/xhtml">
<iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
</body>
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
Components.utils.import("resource://gre/modules/Services.jsm");
const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
const timeoutPref = "dom.ipc.plugins.timeoutSecs";
var worker = new ChromeWorker("hangui_iface.js");
@@ -95,17 +98,18 @@ function finishTest() {
function runTests() {
if (!SimpleTest.testPluginIsOOP()) {
ok(true, "Skipping this test when test plugin is not OOP.");
SimpleTest.finish();
}
resetVars();
- hanguiExpect("Prime ChromeWorker", false, false, "test1");
+ hanguiOperation("Prime ChromeWorker", 0, false, false, HANGUIOP_NOTHING, 0,
+ false, "test1");
}
window.frameLoaded = runTests;
var obsCount = 0;
function onPluginCrashedHangUI(aEvent) {
ok(true, "Plugin crashed notification received");
@@ -239,17 +243,17 @@ function test4() {
}
function test3() {
hanguiContinue("test3: Continue button works", false, "test4");
p.stall(STALL_DURATION);
}
function test2() {
- // This test is identical to test1 because there were some bugs where the
+ // This test is identical to test1 because there were some bugs where the
// Hang UI would show on the first hang but not on subsequent hangs
hanguiExpect("test2: Plugin Hang UI is showing", true, true, "test3");
p.stall(STALL_DURATION);
}
function test1() {
SpecialPowers.setIntPref(hangUITimeoutPref, 1);
SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -491,16 +491,27 @@ nsGeolocationRequest::StopTimeoutTimer()
void
nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
{
if (mShutdown) {
// Ignore SendLocationEvents issued before we were cleared.
return;
}
+ if (mOptions && mOptions->mMaximumAge > 0) {
+ DOMTimeStamp positionTime_ms;
+ aPosition->GetTimestamp(&positionTime_ms);
+ const uint32_t maximumAge_ms = mOptions->mMaximumAge;
+ const bool isTooOld =
+ DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
+ if (isTooOld) {
+ return;
+ }
+ }
+
nsRefPtr<Position> wrapped;
if (aPosition) {
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
aPosition->GetCoords(getter_AddRefs(coords));
if (coords) {
wrapped = new Position(ToSupports(mLocator), aPosition);
}
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -14,16 +14,17 @@
* limitations under the License.
*/
#include "GonkGPSGeolocationProvider.h"
#include <pthread.h>
#include <hardware/gps.h>
+#include "mozilla/Constants.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsContentUtils.h"
#include "nsGeoPosition.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsINetworkManager.h"
#include "nsIObserverService.h"
#include "nsJSUtils.h"
@@ -76,18 +77,18 @@ GonkGPSGeolocationProvider::LocationCall
class UpdateLocationEvent : public nsRunnable {
public:
UpdateLocationEvent(nsGeoPosition* aPosition)
: mPosition(aPosition)
{}
NS_IMETHOD Run() {
nsRefPtr<GonkGPSGeolocationProvider> provider =
GonkGPSGeolocationProvider::GetSingleton();
- provider->mLastGPSDerivedLocationTime = PR_Now();
nsCOMPtr<nsIGeolocationUpdate> callback = provider->mLocationCallback;
+ provider->mLastGPSPosition = mPosition;
if (callback) {
callback->Update(mPosition);
}
return NS_OK;
}
private:
nsRefPtr<nsGeoPosition> mPosition;
};
@@ -96,17 +97,24 @@ GonkGPSGeolocationProvider::LocationCall
nsRefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
location->longitude,
location->altitude,
location->accuracy,
location->accuracy,
location->bearing,
location->speed,
- location->timestamp);
+ PR_Now() / PR_USEC_PER_MSEC);
+ // Note above: Can't use location->timestamp as the time from the satellite is a
+ // minimum of 16 secs old (see http://leapsecond.com/java/gpsclock.htm).
+ // All code from this point on expects the gps location to be timestamped with the
+ // current time, most notably: the geolocation service which respects maximumAge
+ // set in the DOM JS.
+
+
NS_DispatchToMainThread(new UpdateLocationEvent(somewhere));
}
void
GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
{
}
@@ -694,51 +702,82 @@ GonkGPSGeolocationProvider::NetworkLocat
return NS_ERROR_FAILURE;
}
double lat, lon, acc;
coords->GetLatitude(&lat);
coords->GetLongitude(&lon);
coords->GetAccuracy(&acc);
- double delta = MAXFLOAT;
+ double delta = -1.0;
static double sLastMLSPosLat = 0;
static double sLastMLSPosLon = 0;
if (0 != sLastMLSPosLon || 0 != sLastMLSPosLat) {
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
// Should the following be a utility function? Others might need this calc.
- const double radsInDeg = 3.14159265 / 180.0;
+ const double radsInDeg = M_PI / 180.0;
const double rNewLat = lat * radsInDeg;
const double rNewLon = lon * radsInDeg;
const double rOldLat = sLastMLSPosLat * radsInDeg;
const double rOldLon = sLastMLSPosLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
- delta = acos( (sin(rNewLat) * sin(rOldLat)) +
- (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)) )
- * 6378137;
+ double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
+ (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
+ if (cosDelta > 1.0) {
+ cosDelta = 1.0;
+ } else if (cosDelta < -1.0) {
+ cosDelta = -1.0;
+ }
+ delta = acos(cosDelta) * 6378137;
}
sLastMLSPosLat = lat;
sLastMLSPosLon = lon;
// if the MLS coord change is smaller than this arbitrarily small value
// assume the MLS coord is unchanged, and stick with the GPS location
const double kMinMLSCoordChangeInMeters = 10;
- // if we haven't seen anything from the GPS device for 10s,
- // use this network derived location.
- const int kMaxGPSDelayBeforeConsideringMLS = 10000;
- int64_t diff = PR_Now() - provider->mLastGPSDerivedLocationTime;
- if (provider->mLocationCallback && diff > kMaxGPSDelayBeforeConsideringMLS
- && delta > kMinMLSCoordChangeInMeters)
- {
- provider->mLocationCallback->Update(position);
+ DOMTimeStamp time_ms = 0;
+ if (provider->mLastGPSPosition) {
+ provider->mLastGPSPosition->GetTimestamp(&time_ms);
+ }
+ const int64_t diff_ms = (PR_Now() / PR_USEC_PER_MSEC) - time_ms;
+
+ // We want to distinguish between the GPS being inactive completely
+ // and temporarily inactive. In the former case, we would use a low
+ // accuracy network location; in the latter, we only want a network
+ // location that appears to updating with movement.
+
+ const bool isGPSFullyInactive = diff_ms > 1000 * 60 * 2; // two mins
+ const bool isGPSTempInactive = diff_ms > 1000 * 10; // 10 secs
+
+ if (provider->mLocationCallback) {
+ if (isGPSFullyInactive ||
+ (isGPSTempInactive && delta > kMinMLSCoordChangeInMeters))
+ {
+ if (gGPSDebugging) {
+ nsContentUtils::LogMessageToConsole("geo: Using MLS, GPS age:%fs, MLS Delta:%fm\n",
+ diff_ms / 1000.0, delta);
+ }
+ provider->mLocationCallback->Update(position);
+ } else if (provider->mLastGPSPosition) {
+ if (gGPSDebugging) {
+ nsContentUtils::LogMessageToConsole("geo: Using old GPS age:%fs\n",
+ diff_ms / 1000.0);
+ }
+
+ // This is a fallback case so that the GPS provider responds with its last
+ // location rather than waiting for a more recent GPS or network location.
+ // The service decides if the location is too old, not the provider.
+ provider->mLocationCallback->Update(provider->mLastGPSPosition);
+ }
}
provider->InjectLocation(lat, lon, acc);
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::NetworkLocationUpdate::LocationUpdatePending()
@@ -774,17 +813,16 @@ GonkGPSGeolocationProvider::Startup()
if (mNetworkLocationProvider) {
nsresult rv = mNetworkLocationProvider->Startup();
if (NS_SUCCEEDED(rv)) {
nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
mNetworkLocationProvider->Watch(update);
}
}
- mLastGPSDerivedLocationTime = 0;
mStarted = true;
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/system/gonk/GonkGPSGeolocationProvider.h
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.h
@@ -16,16 +16,17 @@
#ifndef GonkGPSGeolocationProvider_h
#define GonkGPSGeolocationProvider_h
#include <hardware/gps.h> // for GpsInterface
#include "nsCOMPtr.h"
#include "nsIGeolocationProvider.h"
#include "nsIObserver.h"
+#include "nsIDOMGeoPosition.h"
#ifdef MOZ_B2G_RIL
#include "nsIRadioInterfaceLayer.h"
#endif
#include "nsISettingsService.h"
class nsIThread;
#define GONK_GPS_GEOLOCATION_PROVIDER_CID \
@@ -106,19 +107,19 @@ private:
const GpsInterface* mGpsInterface;
#ifdef MOZ_B2G_RIL
const AGpsInterface* mAGpsInterface;
const AGpsRilInterface* mAGpsRilInterface;
nsCOMPtr<nsIRadioInterface> mRadioInterface;
#endif
nsCOMPtr<nsIGeolocationUpdate> mLocationCallback;
- PRTime mLastGPSDerivedLocationTime;
nsCOMPtr<nsIThread> mInitThread;
nsCOMPtr<nsIGeolocationProvider> mNetworkLocationProvider;
+ nsCOMPtr<nsIDOMGeoPosition> mLastGPSPosition;
class NetworkLocationUpdate : public nsIGeolocationUpdate
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGEOLOCATIONUPDATE
NetworkLocationUpdate() {}
--- a/dom/tests/mochitest/general/test_consoleAPI.html
+++ b/dom/tests/mochitest/general/test_consoleAPI.html
@@ -32,16 +32,17 @@ function doTest() {
"groupCollapsed": "function",
"groupEnd": "function",
"time": "function",
"timeEnd": "function",
"profile": "function",
"profileEnd": "function",
"assert": "function",
"count": "function",
+ "table": "function",
"__noSuchMethod__": "function"
};
var foundProps = 0;
for (var prop in console) {
foundProps++;
is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists");
}
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -8,16 +8,17 @@
Exposed=(Window,Worker)]
interface Console {
void log(any... data);
void info(any... data);
void warn(any... data);
void error(any... data);
void _exception(any... data);
void debug(any... data);
+ void table(any... data);
void trace();
void dir(any... data);
void group(any... data);
void groupCollapsed(any... data);
void groupEnd(any... data);
void time(optional any time);
void timeEnd(optional any time);
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -22,16 +22,17 @@ enum RTCStatsType {
dictionary RTCStats {
DOMHighResTimeStamp timestamp;
RTCStatsType type;
DOMString id;
};
dictionary RTCRTPStreamStats : RTCStats {
DOMString ssrc;
+ DOMString mediaType;
DOMString remoteId;
boolean isRemote = false;
DOMString mediaTrackId;
DOMString transportId;
DOMString codecId;
// Video encoder/decoder measurements (absent for rtcp)
double bitrateMean;
--- a/gfx/angle/src/libGLESv2/renderer/d3d9/RenderTarget9.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d9/RenderTarget9.cpp
@@ -75,17 +75,19 @@ RenderTarget9::RenderTarget9(Renderer *r
{
requiresInitialization = gl_d3d9::RequiresTextureDataInitialization(internalFormat);
result = device->CreateRenderTarget(width, height, renderFormat,
gl_d3d9::GetMultisampleType(supportedSamples),
0, FALSE, &mRenderTarget, NULL);
}
- if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
+ if (result == D3DERR_OUTOFVIDEOMEMORY ||
+ result == E_INVALIDARG ||
+ result == E_OUTOFMEMORY)
{
gl::error(GL_OUT_OF_MEMORY);
return;
}
ASSERT(SUCCEEDED(result));
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -33,29 +33,34 @@ RenderbufferStorageBySamples(GLContext*
aSize.width, aSize.height);
} else {
aGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
aInternalFormat,
aSize.width, aSize.height);
}
}
-
GLuint
CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
GLenum aType, const gfx::IntSize& aSize, bool linear)
{
GLuint tex = 0;
aGL->fGenTextures(1, &tex);
ScopedBindTexture autoTex(aGL, tex);
- aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
- aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
- aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
- aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR
+ : LOCAL_GL_NEAREST);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
+ LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR
+ : LOCAL_GL_NEAREST);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,
aInternalFormat,
aSize.width, aSize.height,
0,
aFormat,
aType,
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -272,17 +272,17 @@ GLContext::GLContext(const SurfaceCaps&
mIsOffscreen(isOffscreen),
mContextLost(false),
mVersion(0),
mProfile(ContextProfile::Unknown),
mVendor(GLVendor::Other),
mRenderer(GLRenderer::Other),
mHasRobustness(false),
#ifdef DEBUG
- mGLError(LOCAL_GL_NO_ERROR),
+ mIsInLocalErrorCheck(false),
#endif
mSharedContext(sharedContext),
mCaps(caps),
mScreen(nullptr),
mLockedSurface(nullptr),
mMaxTextureSize(0),
mMaxCubeMapTextureSize(0),
mMaxTextureImageSize(0),
@@ -1107,33 +1107,33 @@ GLContext::InitWithPrefix(const char *pr
}
if (mInitialized) {
raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
raw_fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mScissorRect);
raw_fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
raw_fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxCubeMapTextureSize);
raw_fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
+ raw_fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
#ifdef XP_MACOSX
if (mWorkAroundDriverBugs) {
if (mVendor == GLVendor::Intel) {
// see bug 737182 for 2D textures, bug 684882 for cube map textures.
mMaxTextureSize = std::min(mMaxTextureSize, 4096);
mMaxCubeMapTextureSize = std::min(mMaxCubeMapTextureSize, 512);
// for good measure, we align renderbuffers on what we do for 2D textures
mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 4096);
mNeedsTextureSizeChecks = true;
} else if (mVendor == GLVendor::NVIDIA) {
if (nsCocoaFeatures::OnMountainLionOrLater()) {
// See bug 879656. 8192 fails, 8191 works.
mMaxTextureSize = std::min(mMaxTextureSize, 8191);
mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 8191);
- }
- else {
+ } else {
// See bug 877949.
mMaxTextureSize = std::min(mMaxTextureSize, 4096);
mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 4096);
}
// Part of the bug 879656, but it also doesn't hurt the 877949
mNeedsTextureSizeChecks = true;
}
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -7,16 +7,17 @@
#ifndef GLCONTEXT_H_
#define GLCONTEXT_H_
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <map>
#include <bitset>
+#include <queue>
#ifdef DEBUG
#include <string.h>
#endif
#ifdef WIN32
#include <windows.h>
#endif
@@ -500,38 +501,33 @@ private:
/**
* Is this feature supported using the core (unsuffixed) symbols?
*/
bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
// -----------------------------------------------------------------------------
// Robustness handling
public:
-
bool HasRobustness() const {
return mHasRobustness;
}
/**
* The derived class is expected to provide information on whether or not it
* supports robustness.
*/
virtual bool SupportsRobustness() const = 0;
-
private:
bool mHasRobustness;
-
// -----------------------------------------------------------------------------
// Error handling
public:
-
- static const char* GLErrorToString(GLenum aError)
- {
+ static const char* GLErrorToString(GLenum aError) {
switch (aError) {
case LOCAL_GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case LOCAL_GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case LOCAL_GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case LOCAL_GL_STACK_OVERFLOW:
@@ -544,57 +540,124 @@ public:
return "GL_TABLE_TOO_LARGE";
case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
default:
return "";
}
}
-
/** \returns the first GL error, and guarantees that all GL error flags are cleared,
* i.e. that a subsequent GetError call will return NO_ERROR
*/
- GLenum GetAndClearError()
- {
+ GLenum GetAndClearError() {
// the first error is what we want to return
GLenum error = fGetError();
if (error) {
// clear all pending errors
while(fGetError()) {}
}
return error;
}
-
- /*** In GL debug mode, we completely override glGetError ***/
-
- GLenum fGetError()
- {
-#ifdef DEBUG
- // debug mode ends up eating the error in AFTER_GL_CALL
- if (DebugMode()) {
- GLenum err = mGLError;
- mGLError = LOCAL_GL_NO_ERROR;
+private:
+ GLenum raw_fGetError() {
+ return mSymbols.fGetError();
+ }
+
+ std::queue<GLenum> mGLErrorQueue;
+
+public:
+ GLenum fGetError() {
+ if (!mGLErrorQueue.empty()) {
+ GLenum err = mGLErrorQueue.front();
+ mGLErrorQueue.pop();
return err;
}
-#endif // DEBUG
-
- return mSymbols.fGetError();
+
+ return GetUnpushedError();
+ }
+
+private:
+ GLenum GetUnpushedError() {
+ return raw_fGetError();
+ }
+
+ void ClearUnpushedErrors() {
+ while (GetUnpushedError()) {
+ // Discard errors.
+ }
+ }
+
+ GLenum GetAndClearUnpushedErrors() {
+ GLenum err = GetUnpushedError();
+ if (err) {
+ ClearUnpushedErrors();
+ }
+ return err;
+ }
+
+ void PushError(GLenum err) {
+ mGLErrorQueue.push(err);
+ }
+
+ void GetAndPushAllErrors() {
+ while (true) {
+ GLenum err = GetUnpushedError();
+ if (!err)
+ break;
+
+ PushError(err);
+ }
}
-
-#ifdef DEBUG
+ ////////////////////////////////////
+ // Use this safer option.
private:
-
- GLenum mGLError;
-#endif // DEBUG
-
+#ifdef DEBUG
+ bool mIsInLocalErrorCheck;
+#endif
+
+public:
+ class ScopedLocalErrorCheck {
+ GLContext* const mGL;
+ bool mHasBeenChecked;
+
+ public:
+ ScopedLocalErrorCheck(GLContext* gl)
+ : mGL(gl)
+ , mHasBeenChecked(false)
+ {
+#ifdef DEBUG
+ MOZ_ASSERT(!mGL->mIsInLocalErrorCheck);
+ mGL->mIsInLocalErrorCheck = true;
+#endif
+ mGL->GetAndPushAllErrors();
+ }
+
+ GLenum GetLocalError() {
+#ifdef DEBUG
+ MOZ_ASSERT(mGL->mIsInLocalErrorCheck);
+ mGL->mIsInLocalErrorCheck = false;
+#endif
+
+ MOZ_ASSERT(!mHasBeenChecked);
+ mHasBeenChecked = true;
+
+ return mGL->GetAndClearUnpushedErrors();
+ }
+
+ ~ScopedLocalErrorCheck() {
+ MOZ_ASSERT(mHasBeenChecked);
+ }
+ };
+
+private:
static void GLAPIENTRY StaticDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const GLvoid* userParam);
void DebugCallback(GLenum source,
@@ -619,18 +682,17 @@ private:
# define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
# elif defined(_MSC_VER)
# define MOZ_FUNCTION_NAME __FUNCTION__
# else
# define MOZ_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
# endif
#endif
- void BeforeGLCall(const char* glFunction)
- {
+ void BeforeGLCall(const char* glFunction) {
MOZ_ASSERT(IsCurrent());
if (DebugMode()) {
GLContext *currentGLContext = nullptr;
currentGLContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
if (DebugMode() & DebugTrace)
printf_stderr("[gl:%p] > %s\n", this, glFunction);
@@ -638,31 +700,33 @@ private:
printf_stderr("Fatal: %s called on non-current context %p. "
"The current context for this thread is %p.\n",
glFunction, this, currentGLContext);
NS_ABORT();
}
}
}
- void AfterGLCall(const char* glFunction)
- {
+ void AfterGLCall(const char* glFunction) {
if (DebugMode()) {
// calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
// the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
// tend to be meaningless
mSymbols.fFinish();
- mGLError = mSymbols.fGetError();
+ GLenum err = GetUnpushedError();
+ PushError(err);
+
if (DebugMode() & DebugTrace)
- printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, mGLError);
- if (mGLError != LOCAL_GL_NO_ERROR) {
+ printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, err);
+
+ if (err != LOCAL_GL_NO_ERROR) {
printf_stderr("GL ERROR: %s generated GL error %s(0x%04x)\n",
glFunction,
- GLErrorToString(mGLError),
- mGLError);
+ GLErrorToString(err),
+ err);
if (DebugMode() & DebugAbortOnError)
NS_ABORT();
}
}
}
GLContext *TrackingContext()
{
@@ -2980,16 +3044,17 @@ protected:
GLint mViewportRect[4];
GLint mScissorRect[4];
GLint mMaxTextureSize;
GLint mMaxCubeMapTextureSize;
GLint mMaxTextureImageSize;
GLint mMaxRenderbufferSize;
+ GLint mMaxViewportDims[2];
GLsizei mMaxSamples;
bool mNeedsTextureSizeChecks;
bool mWorkAroundDriverBugs;
bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
if (mNeedsTextureSizeChecks) {
// some drivers incorrectly handle some large texture sizes that are below the
// max texture size that they report. So we check ourselves against our own values
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -243,34 +243,42 @@ CreateOffscreenFBOContext(bool aShare =
SurfaceCaps dummyCaps = SurfaceCaps::Any();
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(dummyCaps, shareContext, context, true);
return glContext.forget();
}
already_AddRefed<GLContext>
+GLContextProviderCGL::CreateHeadless()
+{
+ nsRefPtr<GLContextCGL> glContext = CreateOffscreenFBOContext();
+ if (!glContext)
+ return nullptr;
+
+ if (!glContext->Init())
+ return nullptr;
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size,
const SurfaceCaps& caps)
{
- nsRefPtr<GLContextCGL> glContext = CreateOffscreenFBOContext();
- if (glContext &&
- glContext->Init() &&
- glContext->InitOffscreen(ToIntSize(size), caps))
- {
- return glContext.forget();
- }
+ nsRefPtr<GLContext> glContext = CreateHeadless();
+ if (!glContext->InitOffscreen(ToIntSize(size), caps))
+ return nullptr;
- // everything failed
- return nullptr;
+ return glContext.forget();
}
static nsRefPtr<GLContext> gGlobalContext;
-GLContext *
+GLContext*
GLContextProviderCGL::GetGlobalContext()
{
if (!sCGLLibrary.EnsureInitialized()) {
return nullptr;
}
if (!gGlobalContext) {
// There are bugs in some older drivers with pbuffers less
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -873,43 +873,52 @@ GLContextEGL::CreateEGLPixmapOffscreenCo
return nullptr;
}
glContext->HoldSurface(thebesSurface);
return glContext.forget();
}
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateHeadless()
+{
+ if (!sEGLLibrary.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ gfxIntSize dummySize = gfxIntSize(16, 16);
+ nsRefPtr<GLContext> glContext;
+ glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize);
+ if (!glContext)
+ return nullptr;
+
+ return glContext.forget();
+}
+
// Under EGL, on Android, pbuffers are supported fine, though
// often without the ability to texture from them directly.
already_AddRefed<GLContext>
GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size,
const SurfaceCaps& caps)
{
- if (!sEGLLibrary.EnsureInitialized()) {
- return nullptr;
- }
-
- gfxIntSize dummySize = gfxIntSize(16, 16);
- nsRefPtr<GLContextEGL> glContext;
- glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize);
-
+ nsRefPtr<GLContext> glContext = CreateHeadless();
if (!glContext)
return nullptr;
if (!glContext->InitOffscreen(ToIntSize(size), caps))
return nullptr;
return glContext.forget();
}
// Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
// and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
// and 3) each EGL context eats 750k on B2G (bug 813783)
-GLContext *
+GLContext*
GLContextProviderEGL::GetGlobalContext()
{
return nullptr;
}
void
GLContextProviderEGL::Shutdown()
{
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1208,23 +1208,31 @@ DONE_CREATING_PIXMAP:
true,
xsurface);
}
return glContext.forget();
}
already_AddRefed<GLContext>
+GLContextProviderGLX::CreateHeadless()
+{
+ gfxIntSize dummySize = gfxIntSize(16, 16);
+ nsRefPtr<GLContext> glContext = CreateOffscreenPixmapContext(dummySize);
+ if (!glContext)
+ return nullptr;
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size,
const SurfaceCaps& caps)
{
- gfxIntSize dummySize = gfxIntSize(16, 16);
- nsRefPtr<GLContextGLX> glContext =
- CreateOffscreenPixmapContext(dummySize);
-
+ nsRefPtr<GLContext> glContext = CreateHeadless();
if (!glContext)
return nullptr;
if (!glContext->InitOffscreen(ToIntSize(size), caps))
return nullptr;
return glContext.forget();
}
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -55,16 +55,20 @@ public:
* @param aFormat The ContextFormat for this offscreen context.
*
* @return Context to use for offscreen rendering
*/
static already_AddRefed<GLContext>
CreateOffscreen(const gfxIntSize& size,
const SurfaceCaps& caps);
+ // Just create a context. We'll add offscreen stuff ourselves.
+ static already_AddRefed<GLContext>
+ CreateHeadless();
+
/**
* Create wrapping Gecko GLContext for external gl context.
*
* @param aContext External context which will be wrapped by Gecko GLContext.
* @param aSurface External surface which is used for external context.
*
* @return Wrapping Context to use for rendering
*/
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -17,18 +17,23 @@ GLContextProviderNull::CreateForWindow(n
already_AddRefed<GLContext>
GLContextProviderNull::CreateWrappingExisting(void*, void*)
{
return nullptr;
}
already_AddRefed<GLContext>
GLContextProviderNull::CreateOffscreen(const gfxIntSize&,
- const SurfaceCaps&,
- ContextFlags)
+ const SurfaceCaps&)
+{
+ return nullptr;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderNull::CreateHeadless()
{
return nullptr;
}
GLContext*
GLContextProviderNull::GetGlobalContext(ContextFlags)
{
return nullptr;
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -602,18 +602,17 @@ CreateWindowOffscreenContext()
nsRefPtr<GLContextWGL> glContext = new GLContextWGL(caps,
shareContext, true,
dc, context, win);
return glContext.forget();
}
already_AddRefed<GLContext>
-GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
- const SurfaceCaps& caps)
+GLContextProviderWGL::CreateHeadless()
{
if (!sWGLLib.EnsureInitialized()) {
return nullptr;
}
nsRefPtr<GLContextWGL> glContext;
// Always try to create a pbuffer context first, because we
@@ -631,16 +630,28 @@ GLContextProviderWGL::CreateOffscreen(co
}
if (!glContext ||
!glContext->Init())
{
return nullptr;
}
+ nsRefPtr<GLContext> retGL = glContext;
+ return retGL.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size,
+ const SurfaceCaps& caps)
+{
+ nsRefPtr<GLContext> glContext = CreateHeadless();
+ if (!glContext)
+ return nullptr;
+
if (!glContext->InitOffscreen(ToIntSize(size), caps))
return nullptr;
return glContext.forget();
}
static nsRefPtr<GLContextWGL> gGlobalContext;
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -563,27 +563,30 @@ DrawBuffer::Create(GLContext* const gl,
} else {
if (!formats.depth)
pDepthRB = nullptr;
if (!formats.stencil)
pStencilRB = nullptr;
}
+ GLContext::ScopedLocalErrorCheck localError(gl);
+
CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias,
pColorMSRB, pDepthRB, pStencilRB);
GLuint fb = 0;
gl->fGenFramebuffers(1, &fb);
gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb);
UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB,
depthRB, stencilRB) );
- if (!gl->IsFramebufferComplete(fb))
+ GLenum err = localError.GetLocalError();
+ if (err || !gl->IsFramebufferComplete(fb))
return false;
*out_buffer = Move(ret);
return true;
}
DrawBuffer::~DrawBuffer()
{
@@ -619,16 +622,18 @@ ReadBuffer::Create(GLContext* gl,
}
GLuint depthRB = 0;
GLuint stencilRB = 0;
GLuint* pDepthRB = caps.depth ? &depthRB : nullptr;
GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
+ GLContext::ScopedLocalErrorCheck localError(gl);
+
CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
nullptr, pDepthRB, pStencilRB);
GLuint colorTex = 0;
GLuint colorRB = 0;
GLenum target = 0;
switch (surf->mAttachType) {
@@ -646,17 +651,19 @@ ReadBuffer::Create(GLContext* gl,
GLuint fb = 0;
gl->fGenFramebuffers(1, &fb);
gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
gl->mFBOMapping[fb] = surf;
UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB,
stencilRB, surf) );
- if (!gl->IsFramebufferComplete(fb)) {
+
+ GLenum err = localError.GetLocalError();
+ if (err || !gl->IsFramebufferComplete(fb)) {
ret = nullptr;
}
return Move(ret);
}
ReadBuffer::~ReadBuffer()
{
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -139,30 +139,38 @@ ChooseConfig(GLContext* gl,
if (gl->DebugMode()) {
egl->DumpEGLConfig(config);
}
return config;
}
-// Returns EGL_NO_SURFACE on error.
+// Returns `EGL_NO_SURFACE` (`0`) on error.
static EGLSurface
CreatePBufferSurface(GLLibraryEGL* egl,
EGLDisplay display,
EGLConfig config,
const gfx::IntSize& size)
{
+ auto width = size.width;
+ auto height = size.height;
+
EGLint attribs[] = {
- LOCAL_EGL_WIDTH, size.width,
- LOCAL_EGL_HEIGHT, size.height,
+ LOCAL_EGL_WIDTH, width,
+ LOCAL_EGL_HEIGHT, height,
LOCAL_EGL_NONE
};
+ DebugOnly<EGLint> preCallErr = egl->fGetError();
+ MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
+ EGLint err = egl->fGetError();
+ if (err != LOCAL_EGL_SUCCESS)
+ return 0;
return surface;
}
/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
EGLContext context, EGLConfig config,
const gfx::IntSize& size, bool hasAlpha)
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -18,21 +18,29 @@ using gfx::IntSize;
using gfx::SurfaceFormat;
/*static*/ UniquePtr<SharedSurface_Basic>
SharedSurface_Basic::Create(GLContext* gl,
const GLFormats& formats,
const IntSize& size,
bool hasAlpha)
{
+ UniquePtr<SharedSurface_Basic> ret;
gl->MakeCurrent();
+
+ GLContext::ScopedLocalErrorCheck localError(gl);
GLuint tex = CreateTexture(gl, formats.color_texInternalFormat,
formats.color_texFormat,
formats.color_texType,
size);
+ GLenum err = localError.GetLocalError();
+ if (err) {
+ gl->fDeleteTextures(1, &tex);
+ return Move(ret);
+ }
SurfaceFormat format = SurfaceFormat::B8G8R8X8;
switch (formats.color_texInternalFormat) {
case LOCAL_GL_RGB:
case LOCAL_GL_RGB8:
if (formats.color_texType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
format = SurfaceFormat::R5G6B5;
else
@@ -41,18 +49,17 @@ SharedSurface_Basic::Create(GLContext* g
case LOCAL_GL_RGBA:
case LOCAL_GL_RGBA8:
format = SurfaceFormat::B8G8R8A8;
break;
default:
MOZ_CRASH("Unhandled Tex format.");
}
- typedef SharedSurface_Basic ptrT;
- UniquePtr<ptrT> ret( new ptrT(gl, size, hasAlpha, format, tex) );
+ ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, format, tex) );
return Move(ret);
}
SharedSurface_Basic::SharedSurface_Basic(GLContext* gl,
const IntSize& size,
bool hasAlpha,
SurfaceFormat format,
GLuint tex)
@@ -69,21 +76,18 @@ SharedSurface_Basic::SharedSurface_Basic
ScopedBindFramebuffer autoFB(mGL, mFB);
mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
LOCAL_GL_COLOR_ATTACHMENT0,
LOCAL_GL_TEXTURE_2D,
mTex,
0);
- GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
- if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
- mGL->fDeleteFramebuffers(1, &mFB);
- mFB = 0;
- }
+ DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
int32_t stride = gfx::GetAlignedStride<4>(size.width * BytesPerPixel(format));
mData = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
// Leave the extra return for clarity, in case we decide more code should
// be added after this check, that should run even if mData is null.
if (NS_WARN_IF(!mData)) {
return;
}
@@ -128,24 +132,34 @@ SharedSurface_GLTexture::Create(GLContex
MOZ_ASSERT(!consGL || prodGL->SharesWith(consGL));
prodGL->MakeCurrent();
GLuint tex = texture;
bool ownsTex = false;
+ UniquePtr<SharedSurface_GLTexture> ret;
+
if (!tex) {
+ GLContext::ScopedLocalErrorCheck localError(prodGL);
+
tex = CreateTextureForOffscreen(prodGL, formats, size);
+
+ GLenum err = localError.GetLocalError();
+ if (err) {
+ prodGL->fDeleteTextures(1, &tex);
+ return Move(ret);
+ }
+
ownsTex = true;
}
- typedef SharedSurface_GLTexture ptrT;
- UniquePtr<ptrT> ret( new ptrT(prodGL, consGL, size, hasAlpha, tex,
- ownsTex) );
+ ret.reset( new SharedSurface_GLTexture(prodGL, consGL, size,
+ hasAlpha, tex, ownsTex) );
return Move(ret);
}
SharedSurface_GLTexture::~SharedSurface_GLTexture()
{
if (!mGL->MakeCurrent())
return;
--- a/gfx/gl/SurfaceTypes.cpp
+++ b/gfx/gl/SurfaceTypes.cpp
@@ -7,52 +7,52 @@
#include "mozilla/layers/ISurfaceAllocator.h"
namespace mozilla {
namespace gl {
SurfaceCaps::SurfaceCaps()
{
- Clear();
+ Clear();
}
SurfaceCaps::SurfaceCaps(const SurfaceCaps& other)
{
- *this = other;
+ *this = other;
}
SurfaceCaps&
SurfaceCaps::operator=(const SurfaceCaps& other)
{
- any = other.any;
- color = other.color;
- alpha = other.alpha;
- bpp16 = other.bpp16;
- depth = other.depth;
- stencil = other.stencil;
- antialias = other.antialias;
- preserve = other.preserve;
- surfaceAllocator = other.surfaceAllocator;
+ any = other.any;
+ color = other.color;
+ alpha = other.alpha;
+ bpp16 = other.bpp16;
+ depth = other.depth;
+ stencil = other.stencil;
+ antialias = other.antialias;
+ preserve = other.preserve;
+ surfaceAllocator = other.surfaceAllocator;
- return *this;
+ return *this;
}
void
SurfaceCaps::Clear()
{
- any = false;
- color = false;
- alpha = false;
- bpp16 = false;
- depth = false;
- stencil = false;
- antialias = false;
- preserve = false;
- surfaceAllocator = nullptr;
+ any = false;
+ color = false;
+ alpha = false;
+ bpp16 = false;
+ depth = false;
+ stencil = false;
+ antialias = false;
+ preserve = false;
+ surfaceAllocator = nullptr;
}
SurfaceCaps::~SurfaceCaps()
{
}
} // namespace gl
} // namespace mozilla
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -100,20 +100,27 @@ Compositor::DrawDiagnostics(DiagnosticFl
return;
}
DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
aFlashCounter);
}
RenderTargetRect
-Compositor::ClipRectInLayersCoordinates(RenderTargetIntRect aClip) const {
+Compositor::ClipRectInLayersCoordinates(Layer* aLayer, RenderTargetIntRect aClip) const {
+ ContainerLayer* parent = aLayer->AsContainerLayer() ? aLayer->AsContainerLayer() : aLayer->GetParent();
+ while (!parent->UseIntermediateSurface() && parent->GetParent()) {
+ parent = parent->GetParent();
+ }
+
+ RenderTargetIntPoint renderTargetOffset = RenderTargetIntRect::FromUntyped(
+ parent->GetEffectiveVisibleRegion().GetBounds()).TopLeft();
+
RenderTargetRect result;
- aClip = aClip + RenderTargetIntPoint(GetCurrentRenderTarget()->GetOrigin().x,
- GetCurrentRenderTarget()->GetOrigin().y);
+ aClip = aClip + renderTargetOffset;
RenderTargetIntSize destSize = RenderTargetIntSize(GetWidgetSize().width,
GetWidgetSize().height);
switch (mScreenRotation) {
case ROTATION_0:
result = RenderTargetRect(aClip.x, aClip.y, aClip.width, aClip.height);
break;
case ROTATION_90:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -493,17 +493,17 @@ public:
// On b2g the clip rect is in the coordinate space of the physical screen
// independently of its rotation, while the coordinate space of the layers,
// on the other hand, depends on the screen orientation.
// This only applies to b2g as with other platforms, orientation is handled
// at the OS level rather than in Gecko.
// In addition, the clip rect needs to be offset by the rendering origin.
// This becomes important if intermediate surfaces are used.
- RenderTargetRect ClipRectInLayersCoordinates(RenderTargetIntRect aClip) const;
+ RenderTargetRect ClipRectInLayersCoordinates(Layer* aLayer, RenderTargetIntRect aClip) const;
protected:
void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
const gfx::Rect& aVisibleRect,
const gfx::Rect& aClipRect,
const gfx::Matrix4x4& transform,
uint32_t aFlashCounter);
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -340,29 +340,32 @@ ClientTiledThebesLayer::RenderLayer()
mValidRegion = nsIntRegion();
}
TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
nsIntRegion neededRegion = mVisibleRegion;
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
+ // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
if (MayResample()) {
// If we're resampling then bilinear filtering can read up to 1 pixel
// outside of our texture coords. Make the visible region a single rect,
// and pad it out by 1 pixel (restricted to tile boundaries) so that
// we always have valid content or transparent pixels to sample from.
nsIntRect bounds = neededRegion.GetBounds();
nsIntRect wholeTiles = bounds;
wholeTiles.Inflate(nsIntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()));
nsIntRect padded = bounds;
padded.Inflate(1);
padded.IntersectRect(padded, wholeTiles);
neededRegion = padded;
}
+#endif
nsIntRegion invalidRegion;
invalidRegion.Sub(neededRegion, mValidRegion);
if (invalidRegion.IsEmpty()) {
EndPaint();
return;
}
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -315,31 +315,32 @@ ClientTiledLayerBuffer::GetContentType(S
gfxContentType content =
mThebesLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
gfxContentType::COLOR_ALPHA;
SurfaceMode mode = mThebesLayer->GetSurfaceMode();
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
#if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
#else
if (!mThebesLayer->GetParent() ||
!mThebesLayer->GetParent()->SupportsComponentAlphaChildren() ||
!gfxPrefs::TiledDrawTargetEnabled()) {
mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
} else {
content = gfxContentType::COLOR;
}
-#endif
} else if (mode == SurfaceMode::SURFACE_OPAQUE) {
if (mThebesLayer->MayResample()) {
mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
content = gfxContentType::COLOR_ALPHA;
}
}
+#endif
if (aMode) {
*aMode = mode;
}
return content;
}
gfxMemorySharedReadLock::gfxMemorySharedReadLock()
@@ -1149,22 +1150,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
if (backBufferOnWhite && !mCompositableClient->AddTextureClient(backBufferOnWhite)) {
NS_WARNING("Failed to add tile TextureClient.");
aTile.DiscardFrontBuffer();
aTile.DiscardBackBuffer();
return aTile;
}
}
- if (backBuffer->HasInternalBuffer()) {
- // If our new buffer has an internal buffer, we don't want to keep another
- // TextureClient around unnecessarily, so discard the back-buffer.
- aTile.DiscardBackBuffer();
- }
-
// prepare an array of Moz2D tiles that will be painted into in PostValidate
gfx::Tile moz2DTile;
RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
RefPtr<DrawTarget> dtOnWhite;
if (backBufferOnWhite) {
dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
} else {
@@ -1186,18 +1181,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
dirtyRect->height);
drawRect.Scale(mResolution);
gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution),
NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
drawRect.width,
drawRect.height);
gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
- // Mark the newly updated area as invalid in the front buffer
- aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
+ // Mark the newly updated area as invalid in the back buffer
+ aTile.mInvalidBack.Or(aTile.mInvalidBack, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
} else if (content == gfxContentType::COLOR_ALPHA) {
dt->ClearRect(drawRect);
}
}
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -171,16 +171,26 @@ ContainerPrepare(ContainerT* aContainer,
}
RenderTargetIntRect clipRect = layerToRender->GetLayer()->
CalculateScissorRect(aClipRect, &aManager->GetWorldTransform());
if (clipRect.IsEmpty()) {
continue;
}
+ RenderTargetRect quad = layerToRender->GetLayer()->
+ TransformRectToRenderTarget(LayerPixel::FromUntyped(
+ layerToRender->GetLayer()->GetEffectiveVisibleRegion().GetBounds()));
+
+ Compositor* compositor = aManager->GetCompositor();
+ if (!layerToRender->GetLayer()->AsContainerLayer() &&
+ !quad.Intersects(compositor->ClipRectInLayersCoordinates(layerToRender->GetLayer(), clipRect))) {
+ continue;
+ }
+
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
nsIntRegion savedVisibleRegion;
bool restoreVisibleRegion = false;
gfx::Matrix matrix;
bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
if (i + 1 < children.Length() &&
is2D && !matrix.HasNonIntegerTranslation()) {
@@ -264,17 +274,17 @@ RenderLayers(ContainerT* aContainer,
// drawing anything for it. Hopefully the layers behind, if any, will
// provide suitable content for the overscroll effect.
if (color.a != 0.0) {
EffectChain effectChain(aContainer);
effectChain.mPrimaryEffect = new EffectSolidColor(ToColor(color));
gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
compositor->DrawQuad(
RenderTargetPixel::ToUnknown(
- compositor->ClipRectInLayersCoordinates(aClipRect)),
+ compositor->ClipRectInLayersCoordinates(aContainer, aClipRect)),
clipRect, effectChain, opacity, Matrix4x4());
}
}
}
for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
LayerComposite* layerToRender = preparedData.mLayer;
@@ -358,17 +368,16 @@ RenderIntermediate(ContainerT* aContaine
RefPtr<CompositingRenderTarget> surface)
{
Compositor* compositor = aManager->GetCompositor();
RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
if (!surface) {
return;
}
-
compositor->SetRenderTarget(surface);
// pre-render all of the layers into our temporary
RenderLayers(aContainer, aManager, RenderTargetPixel::FromUntyped(aClipRect));
// Unbind the current surface and rebind the previous one.
compositor->SetRenderTarget(previousTarget);
}
template<class ContainerT> void
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -374,26 +374,26 @@ TiledContentHost::Composite(EffectChain&
}
}
}
float lowPrecisionOpacityReduction =
(aOpacity == 1.0f && backgroundColor.a == 1.0f)
? gfxPrefs::LowPrecisionOpacity() : 1.0f;
nsIntRegion tmpRegion;
- const nsIntRegion* renderRegion;
+ const nsIntRegion* renderRegion = aVisibleRegion;
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
if (PaintWillResample()) {
// If we're resampling, then the texture image will contain exactly the
// entire visible region's bounds, and we should draw it all in one quad
// to avoid unexpected aliasing.
tmpRegion = aVisibleRegion->GetBounds();
renderRegion = &tmpRegion;
- } else {
- renderRegion = aVisibleRegion;
}
+#endif
// Render the low and high precision buffers.
RenderLayerBuffer(mLowPrecisionTiledBuffer,
lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
aEffectChain, lowPrecisionOpacityReduction * aOpacity,
aFilter, aClipRect, *renderRegion, aTransform);
RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
aClipRect, *renderRegion, aTransform);
@@ -430,18 +430,18 @@ TiledContentHost::RenderTile(const TileH
// to warn, the texture update would have already caught this.
return;
}
nsIntRect screenBounds = aScreenRegion.GetBounds();
Rect layerQuad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
RenderTargetRect quad = RenderTargetRect::FromUnknown(aTransform.TransformBounds(layerQuad));
- if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(
- RenderTargetIntRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height)))) {
+ if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(mLayer,
+ RenderTargetIntRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height)))) {
return;
}
if (aBackgroundColor) {
aEffectChain.mPrimaryEffect = new EffectSolidColor(ToColor(*aBackgroundColor));
nsIntRegionRectIterator it(aScreenRegion);
for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {
Rect graphicsRect(rect->x, rect->y, rect->width, rect->height);
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -1108,33 +1108,35 @@ nsRect nsRegion::GetLargestRectangle (co
yaxis.StopAt(bestRectIndices.top));
bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
}
return bestRect;
}
+std::ostream& operator<<(std::ostream& stream, const nsRegion& m) {
+ stream << "[";
+
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&m.mImpl), &n);
+ for (int i=0; i<n; i++) {
+ if (i != 0) {
+ stream << "; ";
+ }
+ stream << boxes[i].x1 << "," << boxes[i].y1 << "," << boxes[i].x2 << "," << boxes[i].y2;
+ }
+
+ stream << "]";
+ return stream;
+}
+
nsCString
nsRegion::ToString() const {
- nsCString result;
- result.Append('[');
-
- int n;
- pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&mImpl), &n);
- for (int i=0; i<n; i++) {
- if (i != 0) {
- result.AppendLiteral("; ");
- }
- result.Append(nsPrintfCString("%d,%d,%d,%d", boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2));
-
- }
- result.Append(']');
-
- return result;
+ return nsCString(mozilla::ToString(this).c_str());
}
nsRegion nsIntRegion::ToAppUnits (nscoord aAppUnitsPerPixel) const
{
nsRegion result;
nsIntRegionRectIterator rgnIter(*this);
const nsIntRect* currentRect;
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -5,16 +5,17 @@
#ifndef nsRegion_h__
#define nsRegion_h__
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint64_t
#include <sys/types.h> // for int32_t
#include "gfxCore.h" // for NS_GFX
+#include "mozilla/ToString.h" // for mozilla::ToString
#include "nsCoord.h" // for nscoord
#include "nsError.h" // for nsresult
#include "nsPoint.h" // for nsIntPoint, nsPoint
#include "nsRect.h" // for nsIntRect, nsRect
#include "nsMargin.h" // for nsIntMargin
#include "nsStringGlue.h" // for nsCString
#include "xpcom-config.h" // for CPP_THROW_NEW
#include "mozilla/TypedEnum.h" // for the VisitEdges typed enum
@@ -63,16 +64,18 @@ public:
~nsRegion () { pixman_region32_fini(&mImpl); }
nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; }
nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; }
bool operator==(const nsRegion& aRgn) const
{
return IsEqual(aRgn);
}
+ friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
+
void Swap(nsRegion* aOther)
{
pixman_region32_t tmp = mImpl;
mImpl = aOther->mImpl;
aOther->mImpl = tmp;
}
static
@@ -457,16 +460,20 @@ public:
nsIntRegion& operator = (const nsIntRect& aRect) { mImpl = ToRect (aRect); return *this; }
nsIntRegion& operator = (const nsIntRegion& aRegion) { mImpl = aRegion.mImpl; return *this; }
bool operator==(const nsIntRegion& aRgn) const
{
return IsEqual(aRgn);
}
+ friend std::ostream& operator<<(std::ostream& stream, const nsIntRegion& m) {
+ return stream << m.mImpl;
+ }
+
void Swap(nsIntRegion* aOther)
{
mImpl.Swap(&aOther->mImpl);
}
void AndWith(const nsIntRegion& aOther)
{
And(*this, aOther);
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -277,8 +277,10 @@
#define NS_THEME_WINDOW_BUTTON_MAXIMIZE 238
#define NS_THEME_WINDOW_BUTTON_RESTORE 239
#define NS_THEME_WINDOW_BUTTON_BOX 240
#define NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED 241
// moz-apperance style used in setting proper glass margins
#define NS_THEME_WIN_EXCLUDE_GLASS 242
+#define NS_THEME_MAC_VIBRANCY_LIGHT 243
+#define NS_THEME_MAC_VIBRANCY_DARK 244
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -31,16 +31,37 @@ class gfxMatrix;
class gfx3DMatrix
{
public:
/**
* Create matrix.
*/
gfx3DMatrix(void);
+ friend std::ostream& operator<<(std::ostream& stream, const gfx3DMatrix& m) {
+ if (m.IsIdentity()) {
+ return stream << "[ I ]";
+ }
+
+ if (m.Is2D()) {
+ return stream << "["
+ << m._11 << " " << m._12 << "; "
+ << m._21 << " " << m._22 << "; "
+ << m._41 << " " << m._42
+ << "]";
+ }
+
+ return stream << "["
+ << m._11 << " " << m._12 << " " << m._13 << " " << m._14 << "; "
+ << m._21 << " " << m._22 << " " << m._23 << " " << m._24 << "; "
+ << m._31 << " " << m._32 << " " << m._33 << " " << m._34 << "; "
+ << m._41 << " " << m._42 << " " << m._43 << " " << m._44
+ << "]";
+ }
+
/**
* Matrix multiplication.
*/
gfx3DMatrix operator*(const gfx3DMatrix &aMatrix) const;
gfx3DMatrix& operator*=(const gfx3DMatrix &aMatrix);
gfxPointH3D& operator[](int aIndex)
{
--- a/gfx/thebes/gfxMatrix.h
+++ b/gfx/thebes/gfxMatrix.h
@@ -45,16 +45,28 @@ public:
* Initializes the matrix from individual components. See the class
* description for the layout of the matrix.
*/
gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) :
_11(a), _12(b),
_21(c), _22(d),
_31(tx), _32(ty) { }
+ friend std::ostream& operator<<(std::ostream& stream, const gfxMatrix& m) {
+ if (m.IsIdentity()) {
+ return stream << "[identity]";
+ }
+
+ return stream << "["
+ << m._11 << " " << m._12
+ << m._21 << " " << m._22
+ << m._31 << " " << m._32
+ << "]";
+ }
+
/**
* Post-multiplies m onto the matrix.
*/
const gfxMatrix& operator *= (const gfxMatrix& m);
/**
* Multiplies *this with m and returns the result.
*/
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -31,18 +31,24 @@ LOCAL_INCLUDES += [
if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
# For sandbox includes and the include dependencies those have
LOCAL_INCLUDES += [
'/security',
'/security/sandbox',
'/security/sandbox/chromium',
]
USE_LIBS += [
- 'sandbox_s',
+ 'sandbox_staticruntime_s',
]
+ DELAYLOAD_DLLS += [
+ 'mozalloc.dll',
+ 'nss3.dll',
+ 'xul.dll'
+ ]
+ USE_STATIC_LIBS = True
if CONFIG['_MSC_VER']:
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']]
@@ -52,15 +58,17 @@ LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECU
#
# The default heap size is 1MB on Win32.
# The heap will grow if need be.
#
# Set it to 256k. See bug 127069.
if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
LDFLAGS += ['/HEAP:0x40000']
-FAIL_ON_WARNINGS = True
+# Windows builds have dll linkage warnings due to USE_STATIC_LIBS
+if CONFIG['OS_ARCH'] != 'WINNT':
+ FAIL_ON_WARNINGS = True
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
OS_LIBS += [
'binder',
'utils',
]
--- a/ipc/glue/CrossProcessMutex.h
+++ b/ipc/glue/CrossProcessMutex.h
@@ -37,17 +37,17 @@ typedef HANDLE CrossProcessMutexHandle;
#elif defined(OS_LINUX)
typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessMutexHandle;
#else
// Stub for other platforms. We can't use uintptr_t here since different
// processes could disagree on its size.
typedef uintptr_t CrossProcessMutexHandle;
#endif
-class NS_COM_GLUE CrossProcessMutex
+class CrossProcessMutex
{
public:
/**
* CrossProcessMutex
* @param name A name which can reference this lock (currently unused)
**/
explicit CrossProcessMutex(const char* aName);
/**
--- a/ipc/ipdl/test/cxx/app/TestIPDL.cpp
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -1,16 +1,17 @@
/* 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 "nsXULAppAPI.h"
#if defined(XP_WIN)
#include <windows.h>
+#define XRE_DONT_SUPPORT_XPSP2 // this app doesn't ship
#include "nsWindowsWMain.cpp"
#endif
int
main(int argc, char** argv)
{
// the first argument specifies which IPDL test case/suite to load
if (argc < 2)
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -40,23 +40,21 @@ WrapperOwner::idOf(JSObject *obj)
ObjectId objId = BitwiseCast<uint64_t>(v.toDouble());
MOZ_ASSERT(findCPOWById(objId) == obj);
MOZ_ASSERT(objId);
return objId;
}
-int sCPOWProxyHandler;
-
class CPOWProxyHandler : public BaseProxyHandler
{
public:
CPOWProxyHandler()
- : BaseProxyHandler(&sCPOWProxyHandler) {}
+ : BaseProxyHandler(&family) {}
virtual ~CPOWProxyHandler() {}
virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE {
return false;
}
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
@@ -81,19 +79,21 @@ class CPOWProxyHandler : public BaseProx
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
JSContext *cx) const MOZ_OVERRIDE;
virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
+ static const char family;
static const CPOWProxyHandler singleton;
};
+const char CPOWProxyHandler::family = 0;
const CPOWProxyHandler CPOWProxyHandler::singleton;
#define FORWARD(call, args) \
WrapperOwner *owner = OwnerOf(proxy); \
if (!owner->active()) { \
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
return false; \
} \
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -596,17 +596,17 @@ JS_GetCustomIteratorCount(JSContext *cx)
JS_FRIEND_API(bool)
JS_IsDeadWrapper(JSObject *obj)
{
if (!obj->is<ProxyObject>()) {
return false;
}
- return obj->as<ProxyObject>().handler()->family() == &DeadObjectProxy::sDeadObjectFamily;
+ return obj->as<ProxyObject>().handler()->family() == &DeadObjectProxy::family;
}
void
js::TraceWeakMaps(WeakMapTracer *trc)
{
WeakMapBase::traceAllMappings(trc);
WatchpointMap::traceAll(trc);
}
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -792,16 +792,17 @@ class ScriptedIndirectProxyHandler : pub
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) const MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
virtual bool isScripted() const MOZ_OVERRIDE { return true; }
+ static const char family;
static const ScriptedIndirectProxyHandler singleton;
};
/*
* Old-style indirect proxies allow callers to specify distinct scripted
* [[Call]] and [[Construct]] traps. We use an intermediate object so that we
* can stash this information in a single reserved slot on the proxy object.
*
@@ -813,20 +814,20 @@ class ScriptedIndirectProxyHandler : pub
static const Class CallConstructHolder = {
"CallConstructHolder",
JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
};
} /* anonymous namespace */
// This variable exists solely to provide a unique address for use as an identifier.
-static const char sScriptedIndirectProxyHandlerFamily = 0;
+const char ScriptedIndirectProxyHandler::family = 0;
ScriptedIndirectProxyHandler::ScriptedIndirectProxyHandler()
- : BaseProxyHandler(&sScriptedIndirectProxyHandlerFamily)
+ : BaseProxyHandler(&family)
{
}
ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler()
{
}
bool
@@ -1113,29 +1114,27 @@ class ScriptedDirectProxyHandler : publi
/* ES6 Harmony traps */
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool isScripted() const MOZ_OVERRIDE { return true; }
+ static const char family;
static const ScriptedDirectProxyHandler singleton;
// The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set
// this at revocation time.
static const int HANDLER_EXTRA = 0;
// The "function extended" slot index in which the revocation object is stored. Per spec, this
// is to be cleared during the first revocation.
static const int REVOKE_SLOT = 0;
};
-// This variable exists solely to provide a unique address for use as an identifier.
-static const char sScriptedDirectProxyHandlerFamily = 0;
-
static inline bool
IsDataDescriptor(const PropertyDescriptor &desc)
{
return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER));
}
static inline bool
IsAccessorDescriptor(const PropertyDescriptor &desc)
@@ -1384,17 +1383,17 @@ ArrayToIdVector(JSContext *cx, HandleObj
}
}
// step n
return true;
}
ScriptedDirectProxyHandler::ScriptedDirectProxyHandler()
- : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily)
+ : DirectProxyHandler(&family)
{
}
ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler()
{
}
// ES6 (22 May, 2014) 9.5.4 Proxy.[[PreventExtensions]]()
@@ -2203,16 +2202,17 @@ ScriptedDirectProxyHandler::construct(JS
// step 10
if (!args.rval().isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
return false;
}
return true;
}
+const char ScriptedDirectProxyHandler::family = 0;
const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \
JS_BEGIN_MACRO \
RootedObject proto(cx); \
if (!JSObject::getProto(cx, proxy, &proto)) \
return false; \
if (!proto) \
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -82,16 +82,24 @@ class JS_FRIEND_API(Wrapper);
* this class to define any custom behavior they want.
*
* Important: If you add a trap here, you should probably also add a Proxy::foo
* entry point with an AutoEnterPolicy. If you don't, you need an explicit
* override for the trap in SecurityWrapper. See bug 945826 comment 0.
*/
class JS_FRIEND_API(BaseProxyHandler)
{
+ /*
+ * Sometimes it's desirable to designate groups of proxy handlers as "similar".
+ * For this, we use the notion of a "family": A consumer-provided opaque pointer
+ * that designates the larger group to which this proxy belongs.
+ *
+ * If it will never be important to differentiate this proxy from others as
+ * part of a distinct group, nullptr may be used instead.
+ */
const void *mFamily;
/*
* Proxy handlers can use mHasPrototype to request the following special
* treatment from the JS engine:
*
* - When mHasPrototype is true, the engine never calls these methods:
* getPropertyDescriptor, has, set, enumerate, iterate. Instead, for
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -15,18 +15,16 @@
#include "vm/ErrorObject.h"
#include "vm/WrapperObject.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::gc;
-const char js::sWrapperFamily = 0;
-
/*
* Wrapper forwards this call directly to the wrapped object for efficiency
* and transparency. In particular, the hint is needed to properly stringify
* Date objects in certain cases - see bug 646129. Note also the
* SecurityWrapper overrides this trap to avoid information leaks. See bug
* 720619.
*/
bool
@@ -126,25 +124,26 @@ js::UnwrapOneChecked(JSObject *obj, bool
bool
js::IsCrossCompartmentWrapper(JSObject *obj)
{
return IsWrapper(obj) &&
!!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
}
Wrapper::Wrapper(unsigned flags, bool hasPrototype, bool hasSecurityPolicy)
- : DirectProxyHandler(&sWrapperFamily, hasPrototype, hasSecurityPolicy),
+ : DirectProxyHandler(&family, hasPrototype, hasSecurityPolicy),
mFlags(flags)
{
}
Wrapper::~Wrapper()
{
}
+const char Wrapper::family = 0;
const Wrapper Wrapper::singleton((unsigned)0);
const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
/* Compartments. */
extern JSObject *
js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
@@ -743,17 +742,17 @@ SecurityWrapper<Base>::unwatch(JSContext
return false;
}
template class js::SecurityWrapper<Wrapper>;
template class js::SecurityWrapper<CrossCompartmentWrapper>;
DeadObjectProxy::DeadObjectProxy()
- : BaseProxyHandler(&sDeadObjectFamily)
+ : BaseProxyHandler(&family)
{
}
bool
DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
{
// This is kind of meaningless, but dead-object semantics aside,
// [[Extensible]] always being true is consistent with other proxy types.
@@ -880,18 +879,18 @@ DeadObjectProxy::defaultValue(JSContext
bool
DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
{
protop.set(nullptr);
return true;
}
+const char DeadObjectProxy::family = 0;
const DeadObjectProxy DeadObjectProxy::singleton;
-const char DeadObjectProxy::sDeadObjectFamily = 0;
bool
js::IsDeadProxyObject(JSObject *obj)
{
return obj->is<ProxyObject>() &&
obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
}
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -82,16 +82,17 @@ class JS_FRIEND_API(Wrapper) : public Di
}
explicit Wrapper(unsigned flags, bool hasPrototype = false, bool hasSecurityPolicy = false);
virtual ~Wrapper();
virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE;
+ static const char family;
static const Wrapper singleton;
static const Wrapper singletonWithPrototype;
static JSObject *defaultProto;
};
inline JSObject *
WrapperOptions::proto() const
@@ -202,19 +203,16 @@ class JS_FRIEND_API(SecurityWrapper) : p
};
typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;
typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
{
public:
- // This variable exists solely to provide a unique address for use as an identifier.
- static const char sDeadObjectFamily;
-
explicit DeadObjectProxy();
/* ES5 Harmony fundamental wrapper traps. */
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
@@ -238,32 +236,28 @@ class JS_FRIEND_API(DeadObjectProxy) : p
virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
MutableHandleValue vp) const MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) const MOZ_OVERRIDE;
+ static const char family;
static const DeadObjectProxy singleton;
};
extern JSObject *
TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
HandleObject parent);
-// Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by
-// jsfriendapi users.
-// This variable exists solely to provide a unique address for use as an identifier.
-extern JS_FRIEND_DATA(const char) sWrapperFamily;
-
inline bool
IsWrapper(JSObject *obj)
{
- return IsProxy(obj) && GetProxyHandler(obj)->family() == &sWrapperFamily;
+ return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
}
// Given a JSObject, returns that object stripped of wrappers. If
// stopAtOuter is true, then this returns the outer window if it was
// previously wrapped. Otherwise, this returns the first object for
// which JSObject::isWrapper returns false.
JS_FRIEND_API(JSObject *)
UncheckedUnwrap(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = nullptr);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1318,17 +1318,17 @@ class DebugScopeProxy : public BaseProxy
if (!maybeScope)
return true;
argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame()));
return !!argsObj;
}
public:
- static int family;
+ static const char family;
static const DebugScopeProxy singleton;
DebugScopeProxy() : BaseProxyHandler(&family) {}
bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE
{
// always [[Extensible]], can't be made non-[[Extensible]], like most
// proxies
@@ -1587,17 +1587,17 @@ class DebugScopeProxy : public BaseProxy
RootedValue idval(cx, IdToValue(id));
return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_CANT_DELETE,
JSDVG_IGNORE_STACK, idval, NullPtr(), nullptr, nullptr);
}
};
} /* anonymous namespace */
-int DebugScopeProxy::family = 0;
+const char DebugScopeProxy::family = 0;
const DebugScopeProxy DebugScopeProxy::singleton;
/* static */ DebugScopeObject *
DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing)
{
JS_ASSERT(scope.compartment() == cx->compartment());
RootedValue priv(cx, ObjectValue(scope));
JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -15,16 +15,17 @@
#include "xpcshellMacUtils.h"
#endif
#ifdef XP_WIN
#include <windows.h>
#include <shlobj.h>
// we want a wmain entry point
#define XRE_DONT_PROTECT_DLL_LOAD
+#define XRE_DONT_SUPPORT_XPSP2 // xpcshell does not ship
#define XRE_WANT_ENVIRON
#include "nsWindowsWMain.cpp"
#endif
int
main(int argc, char** argv, char** envp)
{
#ifdef XP_MACOSX
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2434,16 +2434,18 @@ nsDisplayThemedBackground::nsDisplayThem
// Perform necessary RegisterThemeGeometry
switch (disp->mAppearance) {
case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
case NS_THEME_TOOLBAR:
case NS_THEME_WINDOW_TITLEBAR:
case NS_THEME_WINDOW_BUTTON_BOX:
case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ case NS_THEME_MAC_VIBRANCY_LIGHT:
+ case NS_THEME_MAC_VIBRANCY_DARK:
RegisterThemeGeometry(aBuilder, aFrame);
break;
case NS_THEME_WIN_BORDERLESS_GLASS:
case NS_THEME_WIN_GLASS:
aBuilder->SetGlassDisplayItem(this);
break;
}
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1836,17 +1836,23 @@ GetDisplayFlagsForFlexItem(nsIFrame* aFr
return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
}
void
nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
+ // XXXdholbert hacky temporary band-aid for bug 1059138: Trivially pass this
+ // assertion (skip it, basically) if the first child is part of a shadow DOM.
+ // (IsOrderLEQWithDOMFallback doesn't know how to compare tree-position of a
+ // shadow-DOM element vs. a non-shadow-DOM element.)
NS_ASSERTION(
+ (!mFrames.IsEmpty() &&
+ mFrames.FirstChild()->GetContent()->GetContainingShadow()) ||
nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
"Child frames aren't sorted correctly");
DisplayBorderBackgroundOutline(aBuilder, aLists);
// Our children are all block-level, so their borders/backgrounds all go on
// the BlockBorderBackgrounds list.
nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1043,17 +1043,17 @@ ScrollFrameHelper::WantAsyncScroll() con
bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || isFocused);
bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || isFocused);
return isVAsyncScrollable || isHAsyncScrollable;
}
static nsRect
GetOnePixelRangeAroundPoint(nsPoint aPoint, bool aIsHorizontal)
{
- nsRect allowedRange;
+ nsRect allowedRange(aPoint, nsSize());
nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
if (aIsHorizontal) {
allowedRange.x = aPoint.x - halfPixel;
allowedRange.width = halfPixel*2 - 1;
} else {
allowedRange.y = aPoint.y - halfPixel;
allowedRange.height = halfPixel*2 - 1;
}
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1645,19 +1645,19 @@ fuzzy-if(Android&&AndroidVersion>=15,8,3
== 630835-1.html about:blank
== 631352-1.html 631352-1-ref.html
skip-if(!haveTestPlugin) skip-if(B2G) fails-if(Android) fuzzy-if(winWidget&&!layersGPUAccelerated,102,535) == 632423-1.html 632423-1-ref.html
skip-if(Android||B2G) random-if(winWidget) == 632781-verybig.html 632781-ref.html
== 632781-normalsize.html 632781-ref.html
== 633344-1.html 633344-1-ref.html
== 634232-1.html 634232-1-ref.html
fails-if(Android&&AndroidVersion<17&&AndroidVersion!=10) == 635302-1.html 635302-1-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,11) == 635373-1.html 635373-1-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,15) == 635373-2.html 635373-2-ref.html
-skip-if(B2G) random-if(d2d) fuzzy-if(winWidget&&!d2d,1,15) == 635373-3.html 635373-3-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,11) == 635373-1.html 635373-1-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,15) == 635373-2.html 635373-2-ref.html
+skip-if(B2G) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,15) == 635373-3.html 635373-3-ref.html
HTTP(..) == 635639-1.html 635639-1-ref.html
HTTP(..) == 635639-2.html 635639-2-ref.html
random == 637597-1.html 637597-1-ref.html # bug 637597 was never really fixed!
fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-1.html 637852-1-ref.html
fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-2.html 637852-2-ref.html
fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-3.html 637852-3-ref.html
skip-if(B2G) == 641770-1.html 641770-1-ref.html
== 641856-1.html 641856-1-ref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -683,16 +683,18 @@ CSS_KEY(-moz-window-button-close, _moz_w
CSS_KEY(-moz-window-button-minimize, _moz_window_button_minimize)
CSS_KEY(-moz-window-button-maximize, _moz_window_button_maximize)
CSS_KEY(-moz-window-button-restore, _moz_window_button_restore)
CSS_KEY(-moz-window-button-box, _moz_window_button_box)
CSS_KEY(-moz-window-button-box-maximized, _moz_window_button_box_maximized)
CSS_KEY(-moz-mac-help-button, _moz_mac_help_button)
CSS_KEY(-moz-mac-unified-toolbar, _moz_mac_unified_toolbar)
CSS_KEY(-moz-win-exclude-glass, _moz_win_exclude_glass)
+CSS_KEY(-moz-mac-vibrancy-light, _moz_mac_vibrancy_light)
+CSS_KEY(-moz-mac-vibrancy-dark, _moz_mac_vibrancy_dark)
CSS_KEY(alphabetic, alphabetic)
CSS_KEY(bevel, bevel)
CSS_KEY(butt, butt)
CSS_KEY(central, central)
CSS_KEY(crispedges, crispedges)
//CSS_KEY(end, end)
CSS_KEY(evenodd, evenodd)
CSS_KEY(geometricprecision, geometricprecision)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -723,16 +723,18 @@ const KTableValue nsCSSProps::kAppearanc
eCSSKeyword__moz_window_frame_bottom, NS_THEME_WINDOW_FRAME_BOTTOM,
eCSSKeyword__moz_window_button_close, NS_THEME_WINDOW_BUTTON_CLOSE,
eCSSKeyword__moz_window_button_minimize, NS_THEME_WINDOW_BUTTON_MINIMIZE,
eCSSKeyword__moz_window_button_maximize, NS_THEME_WINDOW_BUTTON_MAXIMIZE,
eCSSKeyword__moz_window_button_restore, NS_THEME_WINDOW_BUTTON_RESTORE,
eCSSKeyword__moz_window_button_box, NS_THEME_WINDOW_BUTTON_BOX,
eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED,
eCSSKeyword__moz_win_exclude_glass, NS_THEME_WIN_EXCLUDE_GLASS,
+ eCSSKeyword__moz_mac_vibrancy_light, NS_THEME_MAC_VIBRANCY_LIGHT,
+ eCSSKeyword__moz_mac_vibrancy_dark, NS_THEME_MAC_VIBRANCY_DARK,
eCSSKeyword_UNKNOWN,-1
};
const KTableValue nsCSSProps::kBackfaceVisibilityKTable[] = {
eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE,
eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
eCSSKeyword_UNKNOWN,-1
};
--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -181,16 +181,17 @@ VideoDecoderConfig::Update(sp<MetaData>&
bool
VideoDecoderConfig::IsValid()
{
return display_width > 0 && display_height > 0;
}
MP4Sample::MP4Sample()
: mMediaBuffer(nullptr)
+ , decode_timestamp(0)
, composition_timestamp(0)
, duration(0)
, byte_offset(0)
, is_sync_point(0)
, data(nullptr)
, size(0)
{
}
@@ -201,16 +202,17 @@ MP4Sample::~MP4Sample()
mMediaBuffer->release();
}
}
void
MP4Sample::Update()
{
sp<MetaData> m = mMediaBuffer->meta_data();
+ decode_timestamp = FindInt64(m, kKeyDecodingTime);
composition_timestamp = FindInt64(m, kKeyTime);
duration = FindInt64(m, kKeyDuration);
byte_offset = FindInt64(m, kKey64BitFileOffset);
is_sync_point = FindInt32(m, kKeyIsSyncFrame);
data = reinterpret_cast<uint8_t*>(mMediaBuffer->data());
size = mMediaBuffer->range_length();
crypto.Update(m);
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -132,16 +132,17 @@ class MP4Sample
public:
MP4Sample();
~MP4Sample();
void Update();
void Pad(size_t aPaddingBytes);
stagefright::MediaBuffer* mMediaBuffer;
+ Microseconds decode_timestamp;
Microseconds composition_timestamp;
Microseconds duration;
int64_t byte_offset;
bool is_sync_point;
uint8_t* data;
size_t size;
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -3227,27 +3227,28 @@ status_t MPEG4Source::read(
mBuffer = NULL;
}
// fall through
}
off64_t offset = 0;
size_t size = 0;
+ uint32_t dts = 0;
uint32_t cts = 0;
uint32_t duration = 0;
bool isSyncSample = false;
bool newBuffer = false;
if (mBuffer == NULL) {
newBuffer = true;
status_t err =
mSampleTable->getMetaDataForSample(
mCurrentSampleIndex, &offset, &size, &cts, &duration,
- &isSyncSample);
+ &isSyncSample, &dts);
if (err != OK) {
return err;
}
int32_t max_size;
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
mBuffer = new MediaBuffer(max_size);
@@ -3269,16 +3270,18 @@ status_t MPEG4Source::read(
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
if (!mTimescale) {
return ERROR_MALFORMED;
}
mBuffer->meta_data()->setInt64(
+ kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
mBuffer->meta_data()->setInt64(
kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
@@ -3396,16 +3399,18 @@ status_t MPEG4Source::read(
}
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
if (!mTimescale) {
return ERROR_MALFORMED;
}
mBuffer->meta_data()->setInt64(
+ kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
mBuffer->meta_data()->setInt64(
kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
@@ -3475,16 +3480,17 @@ status_t MPEG4Source::fragmentedRead(
mBuffer = NULL;
}
// fall through
}
off64_t offset = 0;
size_t size = 0;
+ uint32_t dts = 0;
uint32_t cts = 0;
uint32_t duration = 0;
bool isSyncSample = false;
bool newBuffer = false;
if (mBuffer == NULL) {
newBuffer = true;
if (mCurrentSampleIndex >= mCurrentSamples.size()) {
@@ -3514,16 +3520,17 @@ status_t MPEG4Source::fragmentedRead(
if (mTrackFragmentData.mPresent) {
mCurrentTime = mTrackFragmentData.mBaseMediaDecodeTime;
}
}
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
offset = smpl->offset;
size = smpl->size;
+ dts = mCurrentTime;
cts = mCurrentTime + smpl->ctsOffset;
duration = smpl->duration;
mCurrentTime += smpl->duration;
isSyncSample = (mCurrentSampleIndex == 0); // XXX
int32_t max_size;
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
mBuffer = new MediaBuffer(max_size);
@@ -3559,16 +3566,18 @@ status_t MPEG4Source::fragmentedRead(
}
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
if (!mTimescale) {
return ERROR_MALFORMED;
}
mBuffer->meta_data()->setInt64(
+ kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
mBuffer->meta_data()->setInt64(
kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
@@ -3686,16 +3695,18 @@ status_t MPEG4Source::fragmentedRead(
CHECK(mBuffer != NULL);
mBuffer->set_range(0, dstOffset);
}
if (!mTimescale) {
return ERROR_MALFORMED;
}
mBuffer->meta_data()->setInt64(
+ kKeyDecodingTime, ((int64_t)dts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
mBuffer->meta_data()->setInt64(
kKeyDuration, ((int64_t)duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
@@ -136,17 +136,18 @@ status_t SampleIterator::seekTo(uint32_t
status_t err;
if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
ALOGE("findSampleTime return error");
return err;
}
// mTTSDuration is set by findSampleTime()
mCurrentSampleDuration = mTTSDuration;
-
+ mCurrentSampleDecodeTime = mTTSSampleTime + mTTSDuration * (sampleIndex -
+ mTTSSampleIndex);
mCurrentSampleIndex = sampleIndex;
mInitialized = true;
return OK;
}
status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
@@ -775,17 +775,18 @@ status_t SampleTable::getSampleSize_l(
}
status_t SampleTable::getMetaDataForSample(
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
uint32_t *compositionTime,
uint32_t *duration,
- bool *isSyncSample) {
+ bool *isSyncSample,
+ uint32_t *decodeTime) {
Mutex::Autolock autoLock(mLock);
status_t err;
if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
return err;
}
if (offset) {
@@ -795,16 +796,20 @@ status_t SampleTable::getMetaDataForSamp
if (size) {
*size = mSampleIterator->getSampleSize();
}
if (compositionTime) {
*compositionTime = mSampleIterator->getSampleTime();
}
+ if (decodeTime) {
+ *decodeTime = mSampleIterator->getSampleDecodeTime();
+ }
+
if (duration) {
*duration = mSampleIterator->getSampleDuration();
}
if (isSyncSample) {
*isSyncSample = false;
if (mSyncSampleOffset < 0) {
// Every sample is a sync sample.
--- a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
@@ -28,16 +28,17 @@ struct SampleIterator {
status_t seekTo(uint32_t sampleIndex);
uint32_t getChunkIndex() const { return mCurrentChunkIndex; }
uint32_t getDescIndex() const { return mChunkDesc; }
off64_t getSampleOffset() const { return mCurrentSampleOffset; }
size_t getSampleSize() const { return mCurrentSampleSize; }
uint32_t getSampleTime() const { return mCurrentSampleTime; }
+ uint32_t getSampleDecodeTime() const { return mCurrentSampleDecodeTime; }
uint32_t getSampleDuration() const { return mCurrentSampleDuration; }
status_t getSampleSizeDirect(
uint32_t sampleIndex, size_t *size);
private:
SampleTable *mTable;
@@ -60,16 +61,17 @@ private:
uint32_t mTTSSampleTime;
uint32_t mTTSCount;
uint32_t mTTSDuration;
uint32_t mCurrentSampleIndex;
off64_t mCurrentSampleOffset;
size_t mCurrentSampleSize;
uint32_t mCurrentSampleTime;
+ uint32_t mCurrentSampleDecodeTime;
uint32_t mCurrentSampleDuration;
void reset();
status_t findChunkRange(uint32_t sampleIndex);
status_t getChunkOffset(uint32_t chunk, off64_t *offset);
status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
SampleIterator(const SampleIterator &);
--- a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
@@ -62,17 +62,18 @@ public:
status_t getMaxSampleSize(size_t *size);
status_t getMetaDataForSample(
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
uint32_t *compositionTime,
uint32_t *duration = NULL,
- bool *isSyncSample = NULL);
+ bool *isSyncSample = NULL,
+ uint32_t *decodeTime = NULL);
enum {
kFlagBefore,
kFlagAfter,
kFlagClosest
};
status_t findSampleAtTime(
uint32_t req_time, uint32_t *sample_index, uint32_t flags);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1303,29 +1303,16 @@ public:
mTransportStats.Construct();
mIceComponentStats.Construct();
mIceCandidatePairStats.Construct();
mIceCandidateStats.Construct();
mCodecStats.Construct();
mTimestamp.Construct(now);
}
};
-
-// Specialized helper - push map[key] if specified or all map values onto array
-
-static void
-PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst,
- const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc,
- TrackID aKey = 0) {
- auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin;
- for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end();
- it != end; ++it) {
- aDst.AppendElement(it->second);
- }
-}
#endif
NS_IMETHODIMP
PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
PC_AUTO_ENTER_API_CALL(true);
#ifdef MOZILLA_INTERNAL_API
if (!mMedia) {
@@ -2272,36 +2259,58 @@ PeerConnectionImpl::BuildStatsQuery_m(
if (query->internalStats) {
query->report->mLocalSdp.Construct(
NS_ConvertASCIItoUTF16(mLocalSDP.c_str()));
query->report->mRemoteSdp.Construct(
NS_ConvertASCIItoUTF16(mRemoteSDP.c_str()));
}
// Gather up pipelines from mMedia so they may be inspected on STS
- TrackID trackId = aSelector ? aSelector->GetTrackID() : 0;
for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
- PushBackSelect(query->pipelines,
- mMedia->GetLocalStream(i)->GetPipelines(),
- trackId);
+ auto& pipelines = mMedia->GetLocalStream(i)->GetPipelines();
+ if (aSelector) {
+ if (mMedia->GetLocalStream(i)->GetMediaStream()->HasTrack(*aSelector)) {
+ // XXX use type instead of TrackID - bug 1056650
+ for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+ if (it->second->IsVideo() == !!aSelector->AsVideoStreamTrack()) {
+ query->pipelines.AppendElement(it->second);
+ }
+ }
+ }
+ } else {
+ for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+ query->pipelines.AppendElement(it->second);
+ }
+ }
}
for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
- PushBackSelect(query->pipelines,
- mMedia->GetRemoteStream(i)->GetPipelines(),
- trackId);
+ auto& pipelines = mMedia->GetRemoteStream(i)->GetPipelines();
+ if (aSelector) {
+ if (mMedia->GetRemoteStream(i)->GetMediaStream()->HasTrack(*aSelector)) {
+ for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+ if (it->second->trackid() == aSelector->GetTrackID()) {
+ query->pipelines.AppendElement(it->second);
+ }
+ }
+ }
+ } else {
+ for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
+ query->pipelines.AppendElement(it->second);
+ }
+ }
}
query->iceCtx = mMedia->ice_ctx();
// From the list of MediaPipelines, determine the set of NrIceMediaStreams
// we are interested in.
std::set<size_t> levelsToGrab;
- if (trackId) {
+ if (aSelector) {
for (size_t p = 0; p < query->pipelines.Length(); ++p) {
size_t level = query->pipelines[p]->level();
MOZ_ASSERT(level);
levelsToGrab.insert(level);
}
} else {
// We want to grab all streams, so ignore the pipelines (this also ends up
// grabbing DataChannel streams, which is what we want)
@@ -2414,18 +2423,20 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
ASSERT_ON_THREAD(query->iceCtx->thread());
// Gather stats from pipelines provided (can't touch mMedia + stream on STS)
for (size_t p = 0; p < query->pipelines.Length(); ++p) {
const MediaPipeline& mp = *query->pipelines[p];
bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
- nsString idstr = isAudio ?
- NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
+ nsString mediaType = isAudio ?
+ NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video");
+ nsString idstr = mediaType;
+ idstr.AppendLiteral("_");
idstr.AppendInt(mp.trackid());
// Gather pipeline stats.
switch (mp.direction()) {
case MediaPipeline::TRANSMIT: {
nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
nsString remoteId;
nsString ssrc;
@@ -2451,16 +2462,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr;
RTCInboundRTPStreamStats s;
s.mTimestamp.Construct(timestamp);
s.mId.Construct(remoteId);
s.mType.Construct(RTCStatsType::Inboundrtp);
if (ssrc.Length()) {
s.mSsrc.Construct(ssrc);
}
+ s.mMediaType.Construct(mediaType);
s.mJitter.Construct(double(jitterMs)/1000);
s.mRemoteId.Construct(localId);
s.mIsRemote = true;
s.mPacketsReceived.Construct(packetsReceived);
s.mBytesReceived.Construct(bytesReceived);
s.mPacketsLost.Construct(packetsLost);
s.mMozRtt.Construct(rtt);
query->report->mInboundRTPStreamStats.Value().AppendElement(s);
@@ -2470,16 +2482,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
{
RTCOutboundRTPStreamStats s;
s.mTimestamp.Construct(query->now);
s.mId.Construct(localId);
s.mType.Construct(RTCStatsType::Outboundrtp);
if (ssrc.Length()) {
s.mSsrc.Construct(ssrc);
}
+ s.mMediaType.Construct(mediaType);
s.mRemoteId.Construct(remoteId);
s.mIsRemote = false;
s.mPacketsSent.Construct(mp.rtp_packets_sent());
s.mBytesSent.Construct(mp.rtp_bytes_sent());
// Lastly, fill in video encoder stats if this is video
if (!isAudio) {
double framerateMean;
@@ -2521,31 +2534,33 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr;
RTCOutboundRTPStreamStats s;
s.mTimestamp.Construct(timestamp);
s.mId.Construct(remoteId);
s.mType.Construct(RTCStatsType::Outboundrtp);
if (ssrc.Length()) {
s.mSsrc.Construct(ssrc);
}
+ s.mMediaType.Construct(mediaType);
s.mRemoteId.Construct(localId);
s.mIsRemote = true;
s.mPacketsSent.Construct(packetsSent);
s.mBytesSent.Construct(bytesSent);
query->report->mOutboundRTPStreamStats.Value().AppendElement(s);
}
}
// Then, fill in local side (with cross-link to remote only if present)
RTCInboundRTPStreamStats s;
s.mTimestamp.Construct(query->now);
s.mId.Construct(localId);
s.mType.Construct(RTCStatsType::Inboundrtp);
if (ssrc.Length()) {
s.mSsrc.Construct(ssrc);
}
+ s.mMediaType.Construct(mediaType);
unsigned int jitterMs, packetsLost;
if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
s.mJitter.Construct(double(jitterMs)/1000);
s.mPacketsLost.Construct(packetsLost);
}
if (remoteId.Length()) {
s.mRemoteId.Construct(remoteId);
}
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -184,16 +184,20 @@ public:
SourceStreamInfo(already_AddRefed<DOMMediaStream>& aMediaStream,
PeerConnectionMedia *aParent)
: mMediaStream(aMediaStream),
mParent(aParent) {
MOZ_ASSERT(mMediaStream);
}
+ DOMMediaStream* GetMediaStream() const {
+ return mMediaStream;
+ }
+
// This method exists for stats and the unittests.
// It allows visibility into the pipelines and flows.
const std::map<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>>&
GetPipelines() const { return mPipelines; }
mozilla::RefPtr<mozilla::MediaPipeline> GetPipelineByLevel_m(int level);
protected:
std::map<mozilla::TrackID, mozilla::RefPtr<mozilla::MediaPipeline>> mPipelines;
@@ -209,19 +213,16 @@ class LocalSourceStreamInfo : public Sou
}
public:
typedef mozilla::DOMMediaStream DOMMediaStream;
LocalSourceStreamInfo(DOMMediaStream *aMediaStream,
PeerConnectionMedia *aParent)
: SourceStreamInfo(aMediaStream, aParent) {}
- DOMMediaStream* GetMediaStream() {
- return mMediaStream;
- }
// Returns the mPipelines index for the track or -1.
#if 0
int HasTrack(DOMMediaStream* aStream, mozilla::TrackID aTrack);
#endif
int HasTrackType(DOMMediaStream* aStream, bool aIsVideo);
// XXX NOTE: does not change mMediaStream, even if it replaces the last track
// in a LocalSourceStreamInfo. Revise when we have support for multiple tracks
// of a type.
@@ -257,19 +258,16 @@ class RemoteSourceStreamInfo : public So
public:
typedef mozilla::DOMMediaStream DOMMediaStream;
RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
PeerConnectionMedia *aParent)
: SourceStreamInfo(aMediaStream, aParent),
mTrackTypeHints(0) {}
- DOMMediaStream* GetMediaStream() {
- return mMediaStream;
- }
void StorePipeline(int aTrack, bool aIsVideo,
mozilla::RefPtr<mozilla::MediaPipelineReceive> aPipeline);
bool SetUsingBundle_m(int aLevel, bool decision);
void DetachTransport_s();
void DetachMedia_m();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm
@@ -31,20 +31,22 @@ DesktopDeviceInfoMac::DesktopDeviceInfoM
DesktopDeviceInfoMac::~DesktopDeviceInfoMac() {
}
#if !defined(MULTI_MONITOR_SCREENSHARE)
void DesktopDeviceInfoMac::MultiMonitorScreenshare()
{
DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
if (pDesktopDeviceInfo) {
- pDesktopDeviceInfo->setScreenId(0);
+ pDesktopDeviceInfo->setScreenId(CGMainDisplayID());
pDesktopDeviceInfo->setDeviceName("Primary Monitor");
- pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+ char idStr[64];
+ snprintf(idStr, sizeof(idStr), "%ld", pDesktopDeviceInfo->getScreenId());
+ pDesktopDeviceInfo->setUniqueIdName(idStr);
desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
}
}
#endif
void DesktopDeviceInfoMac::InitializeScreenList() {
#if !defined(MULTI_MONITOR_SCREENSHARE)
MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc
@@ -31,20 +31,22 @@ DesktopDeviceInfoWin::DesktopDeviceInfoW
DesktopDeviceInfoWin::~DesktopDeviceInfoWin() {
}
#if !defined(MULTI_MONITOR_SCREENSHARE)
void DesktopDeviceInfoWin::MultiMonitorScreenshare()
{
DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
if (pDesktopDeviceInfo) {
- pDesktopDeviceInfo->setScreenId(0);
+ pDesktopDeviceInfo->setScreenId(webrtc::kFullDesktopScreenId);
pDesktopDeviceInfo->setDeviceName("Primary Monitor");
- pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+ char idStr[64];
+ _snprintf_s(idStr, sizeof(idStr), sizeof(idStr) - 1, "%ld", pDesktopDeviceInfo->getScreenId());
+ pDesktopDeviceInfo->setUniqueIdName(idStr);
desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
}
}
#endif
void DesktopDeviceInfoWin::InitializeScreenList() {
#if !defined(MULTI_MONITOR_SCREENSHARE)
MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc
@@ -30,20 +30,22 @@ DesktopDeviceInfoX11::DesktopDeviceInfoX
DesktopDeviceInfoX11::~DesktopDeviceInfoX11() {
}
#if !defined(MULTI_MONITOR_SCREENSHARE)
void DesktopDeviceInfoX11::MultiMonitorScreenshare()
{
DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
if (pDesktopDeviceInfo) {
- pDesktopDeviceInfo->setScreenId(0);
+ pDesktopDeviceInfo->setScreenId(webrtc::kFullDesktopScreenId);
pDesktopDeviceInfo->setDeviceName("Primary Monitor");
- pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+ char idStr[64];
+ snprintf(idStr, sizeof(idStr), "%ld", idStr);
+ pDesktopDeviceInfo->setUniqueIdName(idStr);
desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
}
}
#endif
void DesktopDeviceInfoX11::InitializeScreenList() {
#if !defined(MULTI_MONITOR_SCREENSHARE)
MultiMonitorScreenshare();
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -377,17 +377,17 @@ int32_t DesktopCaptureImpl::Init(const c
MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, webrtc::kFullDesktopScreenId);
desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pAppCapturer, pMouseCursorMonitor));
} else if (type == Screen) {
ScreenCapturer *pScreenCapturer = ScreenCapturer::Create(options);
if (!pScreenCapturer) {
return -1;
}
- ScreenId screenid = webrtc::kFullDesktopScreenId;
+ ScreenId screenid = atoi(uniqueId);
pScreenCapturer->SelectScreen(screenid);
pScreenCapturer->SetMouseShapeObserver(this);
MouseCursorMonitor *pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(options, screenid);
desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pScreenCapturer, pMouseCursorMonitor));
} else if (type == Window) {
WindowCapturer *pWindowCapturer = WindowCapturer::Create();
if (!pWindowCapturer) {
--- a/mobile/android/base/sync/setup/activities/SendTabActivity.java
+++ b/mobile/android/base/sync/setup/activities/SendTabActivity.java
@@ -141,18 +141,17 @@ public class SendTabActivity extends Loc
TextView textView = (TextView) findViewById(R.id.title);
textView.setText(sendTabData.title);
textView = (TextView) findViewById(R.id.uri);
textView.setText(sendTabData.uri);
enableSend(false);
- // will enableSend if appropriate.
- updateClientList();
+ // Sending will be enabled in onResume, if appropriate.
}
protected static SendTabData getSendTabData(Intent intent) throws IllegalArgumentException {
if (intent == null) {
Logger.warn(LOG_TAG, "intent was null; aborting without sending tab.");
throw new IllegalArgumentException();
}
@@ -179,37 +178,37 @@ public class SendTabActivity extends Loc
return sendTabData;
}
/**
* Ensure that the view's list of clients is backed by a recently populated
* array adapter.
*/
- protected synchronized void updateClientList() {
+ protected synchronized void updateClientList(final TabSender sender, final ClientRecordArrayAdapter adapter) {
// Fetching the client list hits the clients database, so we spin this onto
// a background task.
new AsyncTask<Void, Void, Collection<ClientRecord>>() {
@Override
protected Collection<ClientRecord> doInBackground(Void... params) {
- return getOtherClients();
+ return getOtherClients(sender);
}
@Override
protected void onPostExecute(final Collection<ClientRecord> clientArray) {
// We're allowed to update the UI from here.
Logger.debug(LOG_TAG, "Got " + clientArray.size() + " clients.");
- arrayAdapter.setClientRecordList(clientArray);
+ adapter.setClientRecordList(clientArray);
if (clientArray.size() == 1) {
- arrayAdapter.checkItem(0, true);
+ adapter.checkItem(0, true);
}
- enableSend(arrayAdapter.getNumCheckedGUIDs() > 0);
+ enableSend(adapter.getNumCheckedGUIDs() > 0);
}
}.execute();
}
@Override
public void onResume() {
ActivityUtils.prepareLogging();
Logger.info(LOG_TAG, "Called SendTabActivity.onResume.");
@@ -230,25 +229,31 @@ public class SendTabActivity extends Loc
Logger.warn(LOG_TAG, "Firefox Account named like " + fxAccount.getObfuscatedEmail() +
" needs action before it can send a tab; redirecting to status activity.");
redirectToNewTask(FxAccountStatusActivity.class, false);
return;
}
this.tabSender = new FxAccountTabSender(applicationContext, fxAccount);
+ // will enableSend if appropriate.
+ updateClientList(tabSender, this.arrayAdapter);
+
Logger.info(LOG_TAG, "Allowing tab send for Firefox Account.");
registerDisplayURICommand();
return;
}
final Account[] syncAccounts = accountManager.getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
if (syncAccounts.length > 0) {
this.tabSender = new Sync11TabSender(applicationContext, syncAccounts[0], accountManager);
+ // will enableSend if appropriate.
+ updateClientList(tabSender, this.arrayAdapter);
+
Logger.info(LOG_TAG, "Allowing tab send for Sync account.");
registerDisplayURICommand();
return;
}
// Offer to set up a Firefox Account, and finish this activity.
redirectToNewTask(FxAccountGetStartedActivity.class, false);
}
@@ -355,28 +360,28 @@ public class SendTabActivity extends Loc
} finally {
db.close();
}
}
/**
* @return a collection of client records, excluding our own.
*/
- protected Collection<ClientRecord> getOtherClients() {
+ protected Collection<ClientRecord> getOtherClients(final TabSender sender) {
+ if (sender == null) {
+ Logger.warn(LOG_TAG, "No tab sender when fetching other client IDs.");
+ return new ArrayList<ClientRecord>(0);
+ }
+
final Map<String, ClientRecord> all = getAllClients();
if (all == null) {
return new ArrayList<ClientRecord>(0);
}
- if (this.tabSender == null) {
- Logger.warn(LOG_TAG, "No tab sender when fetching other client IDs.");
- return new ArrayList<ClientRecord>(0);
- }
-
- final String ourGUID = this.tabSender.getAccountGUID();
+ final String ourGUID = sender.getAccountGUID();
if (ourGUID == null) {
return all.values();
}
final ArrayList<ClientRecord> out = new ArrayList<ClientRecord>(all.size());
for (Entry<String, ClientRecord> entry : all.entrySet()) {
if (ourGUID.equals(entry.getKey())) {
continue;
--- a/mobile/android/search/java/org/mozilla/search/MainActivity.java
+++ b/mobile/android/search/java/org/mozilla/search/MainActivity.java
@@ -54,16 +54,18 @@ public class MainActivity extends Fragme
private AsyncQueryHandler queryHandler;
// Main views in layout.
private ClearableEditText editText;
private View preSearch;
private View postSearch;
+ private View settingsButton;
+
private View suggestions;
private SuggestionsFragment suggestionsFragment;
private static final int SUGGESTION_TRANSITION_DURATION = 300;
private static final Interpolator SUGGESTION_TRANSITION_INTERPOLATOR =
new AccelerateDecelerateInterpolator();
// Views used for suggestion animation.
@@ -114,16 +116,26 @@ public class MainActivity extends Fragme
public void onFocusChange(boolean hasFocus) {
setEditState(hasFocus ? EditState.EDITING : EditState.WAITING);
}
});
preSearch = findViewById(R.id.presearch);
postSearch = findViewById(R.id.postsearch);
+ settingsButton = findViewById(R.id.settings_button);
+
+ // Apply click handler to settings button.
+ settingsButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(MainActivity.this, SearchPreferenceActivity.class));
+ }
+ });
+
suggestions = findViewById(R.id.suggestions);
suggestionsFragment = (SuggestionsFragment) getSupportFragmentManager().findFragmentById(R.id.suggestions);
animationText = (TextView) findViewById(R.id.animation_text);
animationCard = findViewById(R.id.animation_card);
cardPaddingX = getResources().getDimensionPixelSize(R.dimen.card_background_padding_x);
cardPaddingY = getResources().getDimensionPixelSize(R.dimen.card_background_padding_y);
@@ -150,16 +162,17 @@ public class MainActivity extends Fragme
@Override
protected void onDestroy() {
super.onDestroy();
queryHandler = null;
editText = null;
preSearch = null;
postSearch = null;
+ settingsButton = null;
suggestionsFragment = null;
suggestions = null;
animationText = null;
animationCard = null;
}
@Override
protected void onStart() {
@@ -190,17 +203,17 @@ public class MainActivity extends Fragme
outState.putString(KEY_SEARCH_STATE, searchState.toString());
outState.putString(KEY_EDIT_STATE, editState.toString());
outState.putString(KEY_QUERY, editText.getText());
}
@Override
public void onSuggest(String query) {
- editText.setText(query);
+ editText.setText(query);
}
@Override
public void onSearch(String query) {
onSearch(query, null);
}
@Override
@@ -218,18 +231,18 @@ public class MainActivity extends Fragme
setEditState(EditState.WAITING);
setSearchState(SearchState.POSTSEARCH);
}
}
/**
* Animates search suggestion to search bar. This animation has 2 main parts:
*
- * 1) Vertically translate query text from suggestion card to search bar.
- * 2) Expand suggestion card to fill the results view area.
+ * 1) Vertically translate query text from suggestion card to search bar.
+ * 2) Expand suggestion card to fill the results view area.
*
* @param query
* @param suggestionAnimation
*/
private void animateSuggestion(final String query, final SuggestionAnimation suggestionAnimation) {
animationText.setText(query);
final Rect startBounds = suggestionAnimation.getStartBounds();
@@ -291,30 +304,43 @@ public class MainActivity extends Fragme
}
private void setEditState(EditState editState) {
if (this.editState == editState) {
return;
}
this.editState = editState;
+ updateSettingsButtonVisibility();
+
editText.setActive(editState == EditState.EDITING);
suggestions.setVisibility(editState == EditState.EDITING ? View.VISIBLE : View.INVISIBLE);
}
private void setSearchState(SearchState searchState) {
if (this.searchState == searchState) {
return;
}
this.searchState = searchState;
+ updateSettingsButtonVisibility();
+
preSearch.setVisibility(searchState == SearchState.PRESEARCH ? View.VISIBLE : View.INVISIBLE);
postSearch.setVisibility(searchState == SearchState.POSTSEARCH ? View.VISIBLE : View.INVISIBLE);
}
+ private void updateSettingsButtonVisibility() {
+ // Show button on launch screen when keyboard is down.
+ if (searchState == SearchState.PRESEARCH && editState == EditState.WAITING) {
+ settingsButton.setVisibility(View.VISIBLE);
+ } else {
+ settingsButton.setVisibility(View.INVISIBLE);
+ }
+ }
+
@Override
public void onBackPressed() {
if (editState == EditState.EDITING) {
setEditState(EditState.WAITING);
} else if (searchState == SearchState.POSTSEARCH) {
setSearchState(SearchState.PRESEARCH);
} else {
super.onBackPressed();
--- a/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java
@@ -1,16 +1,15 @@
/* 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/. */
package org.mozilla.search;
import android.app.Activity;
-import android.content.Intent;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -110,23 +109,16 @@ public class PreSearchFragment extends F
public Rect getStartBounds() {
return startBounds;
}
});
}
}
});
- // Apply click handler to settings button.
- mainView.findViewById(R.id.settings_button).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivity(new Intent(getActivity(), SearchPreferenceActivity.class));
- }
- });
return mainView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
listView.setAdapter(null);
listView = null;
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java
@@ -0,0 +1,36 @@
+/* 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/. */
+
+package org.mozilla.search.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+/**
+ * An EditText subclass that loses focus when the keyboard
+ * is dismissed.
+ */
+public class BackCaptureEditText extends EditText {
+ public BackCaptureEditText(Context context) {
+ super(context);
+ }
+
+ public BackCaptureEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BackCaptureEditText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+ clearFocus();
+ }
+ return super.onKeyPreIme(keyCode, event);
+ }
+}
--- a/mobile/android/search/res/layout/clearable_edit_text.xml
+++ b/mobile/android/search/res/layout/clearable_edit_text.xml
@@ -1,15 +1,15 @@
<!-- 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/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <EditText
+ <org.mozilla.search.ui.BackCaptureEditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:imeOptions="actionSearch"
android:inputType="textNoSuggestions"
android:drawableLeft="@drawable/search_icon_inactive"
android:drawablePadding="5dp"
--- a/mobile/android/search/res/layout/search_activity_main.xml
+++ b/mobile/android/search/res/layout/search_activity_main.xml
@@ -37,16 +37,27 @@
<fragment
android:id="@+id/suggestions"
android:name="org.mozilla.search.autocomplete.SuggestionsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/search_bar_height"
android:layout_gravity="top"/>
+
+ <ImageButton
+ android:id="@+id/settings_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:padding="15dp"
+ android:src="@drawable/ic_action_settings"
+ android:layout_gravity="bottom|right"
+ android:contentDescription="@string/search_pref_button_content_description"/>
+
<View
android:id="@+id/animation_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/search_bar_height"
android:background="@color/card_background"
android:visibility="invisible"
android:layout_gravity="top"/>
--- a/mobile/android/search/res/layout/search_fragment_pre_search.xml
+++ b/mobile/android/search/res/layout/search_fragment_pre_search.xml
@@ -11,19 +11,9 @@
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"/>
- <ImageButton
- android:id="@+id/settings_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent"
- android:padding="15dp"
- android:src="@drawable/ic_action_settings"
- android:layout_gravity="bottom|right"
- android:contentDescription="@string/search_pref_button_content_description"/>
-
</FrameLayout>
--- a/mobile/android/search/search_activity_sources.mozbuild
+++ b/mobile/android/search/search_activity_sources.mozbuild
@@ -12,9 +12,10 @@ search_activity_sources = [
'java/org/mozilla/search/Constants.java',
'java/org/mozilla/search/MainActivity.java',
'java/org/mozilla/search/PostSearchFragment.java',
'java/org/mozilla/search/PreSearchFragment.java',
'java/org/mozilla/search/providers/SearchEngine.java',
'java/org/mozilla/search/providers/SearchEngineManager.java',
'java/org/mozilla/search/SearchPreferenceActivity.java',
'java/org/mozilla/search/SearchWidget.java',
+ 'java/org/mozilla/search/ui/BackCaptureEditText.java',
]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -229,20 +229,23 @@ pref("print.shrink-to-fit.scale-limit-pe
pref("media.cache_size", 512000);
// Master HTML5 media volume scale.
pref("media.volume_scale", "1.0");
// Timeout for wakelock release
pref("media.wakelock_timeout", 2000);
+// Whether we should play videos opened in a "video document", i.e. videos
+// opened as top-level documents, as opposed to inside a media element.
+pref("media.play-stand-alone", true);
+
#ifdef MOZ_WMF
pref("media.windows-media-foundation.enabled", true);
pref("media.windows-media-foundation.use-dxva", true);
-pref("media.windows-media-foundation.play-stand-alone", true);
#endif
#ifdef MOZ_DIRECTSHOW
pref("media.directshow.enabled", true);
#endif
#ifdef MOZ_FMP4
pref("media.fragmented-mp4.enabled", true);
pref("media.fragmented-mp4.ffmpeg.enabled", false);
#if defined(XP_WIN) && defined(MOZ_WMF) || defined(XP_MACOSX)
@@ -3707,17 +3710,17 @@ pref("image.onload.decode.limit", 0);
// Disable MSAA on mobile.
pref("gl.msaa-level", 0);
#else
pref("gl.msaa-level", 2);
#endif
pref("webgl.force-enabled", false);
pref("webgl.disabled", false);
pref("webgl.shader_validator", true);
-pref("webgl.prefer-native-gl", false);
+pref("webgl.disable-angle", false);
pref("webgl.min_capability_mode", false);
pref("webgl.disable-extensions", false);
pref("webgl.msaa-force", false);
pref("webgl.prefer-16bpp", false);
pref("webgl.default-no-alpha", false);
pref("webgl.force-layers-readback", false);
pref("webgl.lose-context-on-memory-preasure", false);
pref("webgl.can-lose-context-in-foreground", true);
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -358,16 +358,30 @@ struct StaticFingerprints {
};
struct StaticPinset {
const StaticFingerprints* sha1;
const StaticFingerprints* sha256;
};
/* PreloadedHPKPins.json pinsets */
+static const char* kPinset_facebook_sha256_Data[] = {
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_facebook_sha256 = {
+ sizeof(kPinset_facebook_sha256_Data) / sizeof(const char*),
+ kPinset_facebook_sha256_Data
+};
+
+static const StaticPinset kPinset_facebook = {
+ nullptr,
+ &kPinset_facebook_sha256
+};
+
static const char* kPinset_google_root_pems_sha256_Data[] = {
kEquifax_Secure_CAFingerprint,
kAmerica_Online_Root_Certification_Authority_2Fingerprint,
kComodo_Trusted_Services_rootFingerprint,
kCOMODO_ECC_Certification_AuthorityFingerprint,
kStartCom_Certification_AuthorityFingerprint,
kStartCom_Certification_AuthorityFingerprint,
kThawte_Premium_Server_CAFingerprint,
@@ -769,16 +783,17 @@ static const TransportSecurityPreload kP
{ "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
{ "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "dropbox.com", false, false, false, -1, &kPinset_dropbox },
{ "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
+ { "facebook.com", true, true, false, -1, &kPinset_facebook },
{ "g.co", true, false, false, -1, &kPinset_google_root_pems },
{ "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
{ "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
{ "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
{ "google.ac", true, false, false, -1, &kPinset_google_root_pems },
{ "google.ad", true, false, false, -1, &kPinset_google_root_pems },
{ "google.ae", true, false, false, -1, &kPinset_google_root_pems },
@@ -1062,13 +1077,13 @@ static const TransportSecurityPreload kP
{ "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
{ "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
{ "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
{ "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
{ "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
{ "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
};
-// Pinning Preload List Length = 329;
+// Pinning Preload List Length = 330;
static const int32_t kUnknownId = -1;
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1417256260438000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1417640422391000);
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/ClientAuthServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/ClientAuthServer.cpp
@@ -31,17 +31,17 @@ struct ClientAuthHost
};
// Hostname, cert nickname pairs.
static const ClientAuthHost sClientAuthHosts[] =
{
{ "noclientauth.example.com", false, false },
{ "requestclientauth.example.com", true, false },
{ "requireclientauth.example.com", true, true },
- { nullptr, nullptr }
+ { nullptr, false, false }
};
static const unsigned char sClientCertFingerprint[] =
{
0xD2, 0x2F, 0x00, 0x9A, 0x9E, 0xED, 0x79, 0xDC,
0x8D, 0x17, 0x98, 0x8E, 0xEC, 0x76, 0x05, 0x91,
0xA5, 0xF6, 0xC9, 0xFA, 0x16, 0x8B, 0xD2, 0x5F,
0xE1, 0x52, 0x04, 0x7C, 0xF4, 0x76, 0x42, 0x9D
--- a/security/manager/tools/PreloadedHPKPins.json
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -180,16 +180,25 @@
"Verisign Class 3 Public Primary Certification Authority - G2",
"Verisign Class 3 Public Primary Certification Authority - G3",
"VeriSign Class 3 Public Primary Certification Authority - G4",
"VeriSign Class 3 Public Primary Certification Authority - G5",
"Verisign Class 4 Public Primary Certification Authority - G3",
"VeriSign Universal Root Certification Authority",
"XRamp Global CA Root"
]
+ },
+ // For pinning tests on pinning.example.com, the certificate must be 'End
+ // Entity Test Cert'
+ {
+ "name": "facebook",
+ "sha256_hashes": [
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "DigiCert High Assurance EV Root CA"
+ ]
}
],
"entries": [
// Only domains that are operationally crucial to Firefox can have per-host
// telemetry reporting (the "id") field
{ "name": "addons.mozilla.org", "include_subdomains": true,
"pins": "mozilla", "test_mode": false, "id": 1 },
@@ -215,11 +224,14 @@
"include_subdomains": false, "pins": "mozilla_test",
"test_mode": false, "id": 0 },
{ "name": "test-mode.pinning.example.com", "include_subdomains": true,
"pins": "mozilla_test", "test_mode": true },
// Expand twitter's pinset to include all of *.twitter.com and use
// twitterCDN. More specific rules take precedence because we search for
// exact domain name first.
{ "name": "twitter.com", "include_subdomains": true,
- "pins": "twitterCDN", "test_mode": false }
+ "pins": "twitterCDN", "test_mode": false },
+ // Facebook (not pinned by Chrome)
+ { "name": "facebook.com", "include_subdomains": true,
+ "pins": "facebook", "test_mode": true }
]
}
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_17_RTM
+NSS_3_17_1_BETA1
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
/*
* A dummy header file that is a dependency for all the object files.
* Used to force a full recompilation of NSS in Mozilla's Tinderbox
* depend builds. See comments in rules.mk.
*/
#error "Do not include this header file."
-
--- a/security/nss/lib/certhigh/certvfypkix.c
+++ b/security/nss/lib/certhigh/certvfypkix.c
@@ -22,30 +22,16 @@
#include "secasn1.h"
#include "secder.h"
#include "pkit.h"
#include "pkix_pl_common.h"
extern PRLogModuleInfo *pkixLog;
-#ifdef DEBUG_volkov
-/* Temporary declarations of functioins. Will be removed with fix for
- * 391183 */
-extern char *
-pkix_Error2ASCII(PKIX_Error *error, void *plContext);
-
-extern void
-cert_PrintCert(PKIX_PL_Cert *pkixCert, void *plContext);
-
-extern PKIX_Error *
-cert_PrintCertChain(PKIX_List *pkixCertChain, void *plContext);
-
-#endif /* DEBUG */
-
#ifdef PKIX_OBJECT_LEAK_TEST
extern PKIX_UInt32
pkix_pl_lifecycle_ObjectLeakCheck(int *);
extern SECStatus
pkix_pl_lifecycle_ObjectTableUpdate(int *objCountTable);
@@ -893,21 +879,16 @@ cert_GetLogFromVerifyNode(
PKIX_ENTER(CERTVFYPKIX, "cert_GetLogFromVerifyNode");
children = node->children;
if (children == NULL) {
PKIX_ERRORCODE errCode = PKIX_ANCHORDIDNOTCHAINTOCERT;
if (node->error && node->error->errCode != errCode) {
-#ifdef DEBUG_volkov
- char *string = pkix_Error2ASCII(node->error, plContext);
- fprintf(stderr, "Branch search finished with error: \t%s\n", string);
- PKIX_PL_Free(string, NULL);
-#endif
if (log != NULL) {
SECErrorCodes nssErrorCode = 0;
CERTCertificate *cert = NULL;
cert = node->verifyCert->nssCert;
PKIX_CHECK(
cert_PkixErrorToNssCode(node->error, &nssErrorCode,
@@ -998,32 +979,24 @@ cert_GetBuildResults(
void *plContext)
{
PKIX_ValidateResult *validResult = NULL;
CERTCertList *validChain = NULL;
CERTCertificate *trustedRoot = NULL;
PKIX_TrustAnchor *trustAnchor = NULL;
PKIX_PL_Cert *trustedCert = NULL;
PKIX_List *pkixCertChain = NULL;
-#ifdef DEBUG_volkov
- PKIX_Error *tmpPkixError = NULL;
-#endif /* DEBUG */
PKIX_ENTER(CERTVFYPKIX, "cert_GetBuildResults");
if (buildResult == NULL && error == NULL) {
PKIX_ERROR(PKIX_NULLARGUMENT);
}
if (error) {
SECErrorCodes nssErrorCode = 0;
-#ifdef DEBUG_volkov
- char *temp = pkix_Error2ASCII(error, plContext);
- fprintf(stderr, "BUILD ERROR:\n%s\n", temp);
- PKIX_PL_Free(temp, NULL);
-#endif /* DEBUG */
if (verifyNode) {
PKIX_Error *tmpError =
cert_GetLogFromVerifyNode(log, verifyNode, plContext);
if (tmpError) {
PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
}
}
cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
@@ -1032,23 +1005,16 @@ cert_GetBuildResults(
}
if (pvalidChain) {
PKIX_CHECK(
PKIX_BuildResult_GetCertChain(buildResult, &pkixCertChain,
plContext),
PKIX_BUILDRESULTGETCERTCHAINFAILED);
-#ifdef DEBUG_volkov
- tmpPkixError = cert_PrintCertChain(pkixCertChain, plContext);
- if (tmpPkixError) {
- PKIX_PL_Object_DecRef((PKIX_PL_Object*)tmpPkixError, plContext);
- }
-#endif
-
PKIX_CHECK(
cert_PkixToNssCertsChain(pkixCertChain, &validChain, plContext),
PKIX_CERTCHAINTONSSCHAINFAILED);
}
if (ptrustedRoot) {
PKIX_CHECK(
PKIX_BuildResult_GetValidateResult(buildResult, &validResult,
@@ -1060,23 +1026,17 @@ cert_GetBuildResults(
plContext),
PKIX_VALIDATERESULTGETTRUSTANCHORFAILED);
PKIX_CHECK(
PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustedCert,
plContext),
PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED);
-#ifdef DEBUG_volkov
- if (pvalidChain == NULL) {
- cert_PrintCert(trustedCert, plContext);
- }
-#endif
-
- PKIX_CHECK(
+ PKIX_CHECK(
PKIX_PL_Cert_GetCERTCertificate(trustedCert, &trustedRoot,
plContext),
PKIX_CERTGETCERTCERTIFICATEFAILED);
}
PORT_Assert(!PKIX_ERROR_RECEIVED);
if (trustedRoot) {
@@ -1153,20 +1113,16 @@ cert_VerifyCertChainPkix(
{
PKIX_ProcessingParams *procParams = NULL;
PKIX_BuildResult *result = NULL;
PKIX_VerifyNode *verifyNode = NULL;
PKIX_Error *error = NULL;
SECStatus rv = SECFailure;
void *plContext = NULL;
-#ifdef DEBUG_volkov
- CERTCertificate *trustedRoot = NULL;
- CERTCertList *validChain = NULL;
-#endif /* DEBUG */
#ifdef PKIX_OBJECT_LEAK_TEST
int leakedObjNum = 0;
int memLeakLoopCount = 0;
int objCountTable[PKIX_NUMTYPES];
int fnInvLocalCount = 0;
PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;
@@ -1191,20 +1147,16 @@ cert_VerifyCertChainPkix(
do {
rv = SECFailure;
plContext = NULL;
procParams = NULL;
result = NULL;
verifyNode = NULL;
error = NULL;
-#ifdef DEBUG_volkov
- trustedRoot = NULL;
- validChain = NULL;
-#endif /* DEBUG */
errorGenerated = PKIX_FALSE;
stackPosition = 0;
if (leakedObjNum) {
pkix_pl_lifecycle_ObjectTableUpdate(objCountTable);
}
memLeakLoopCount += 1;
#endif /* PKIX_OBJECT_LEAK_TEST */
@@ -1237,39 +1189,21 @@ do {
}
if (pSigerror) {
/* Currently always PR_FALSE. Will be fixed as a part of 394077 */
*pSigerror = PR_FALSE;
}
rv = SECSuccess;
cleanup:
- error = cert_GetBuildResults(result, verifyNode, error, log,
-#ifdef DEBUG_volkov
- &trustedRoot, &validChain,
-#else
- NULL, NULL,
-#endif /* DEBUG */
+ error = cert_GetBuildResults(result, verifyNode, error, log, NULL, NULL,
plContext);
if (error) {
-#ifdef DEBUG_volkov
- char *temp = pkix_Error2ASCII(error, plContext);
- fprintf(stderr, "GET BUILD RES ERRORS:\n%s\n", temp);
- PKIX_PL_Free(temp, NULL);
-#endif /* DEBUG */
PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
}
-#ifdef DEBUG_volkov
- if (trustedRoot) {
- CERT_DestroyCertificate(trustedRoot);
- }
- if (validChain) {
- CERT_DestroyCertList(validChain);
- }
-#endif /* DEBUG */
if (procParams) {
PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);
}
if (plContext) {
PKIX_PL_NssContext_Destroy(plContext);
}
#ifdef PKIX_OBJECT_LEAK_TEST
deleted file mode 100644
--- a/security/nss/lib/certhigh/certvfypkixprint.c
+++ /dev/null
@@ -1,206 +0,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/. */
-/*
- * nss_pkix_proxy.h
- *
- * PKIX - NSS proxy functions
- *
- */
-#include "cert.h"
-#include "pkix_pl_common.h"
-
-#ifdef DEBUG
-
-char *
-pkix_Error2ASCII(PKIX_Error *error, void *plContext)
-{
- PKIX_UInt32 length;
- char *asciiString = NULL;
- PKIX_PL_String *pkixString = NULL;
- PKIX_Error *errorResult = NULL;
-
- errorResult = PKIX_PL_Object_ToString
- ((PKIX_PL_Object*)error, &pkixString, plContext);
- if (errorResult) goto cleanup;
-
- errorResult = PKIX_PL_String_GetEncoded
- (pkixString,
- PKIX_ESCASCII,
- (void **)&asciiString,
- &length,
- plContext);
-
-cleanup:
-
- if (pkixString){
- if (PKIX_PL_Object_DecRef
- ((PKIX_PL_Object*)pkixString, plContext)){
- return (NULL);
- }
- }
-
- if (errorResult){
- PKIX_PL_Object_DecRef((PKIX_PL_Object*)errorResult, plContext);
- return (NULL);
- }
-
- return (asciiString);
-}
-
-char *
-pkix_Object2ASCII(PKIX_PL_Object *object)
-{
- PKIX_UInt32 length;
- char *asciiString = NULL;
- PKIX_PL_String *pkixString = NULL;
- PKIX_Error *errorResult = NULL;
-
- errorResult = PKIX_PL_Object_ToString
- (object, &pkixString, NULL);
- if (errorResult) goto cleanup;
-
- errorResult = PKIX_PL_String_GetEncoded
- (pkixString, PKIX_ESCASCII, (void **)&asciiString, &length, NULL);
-
-cleanup:
-
- if (pkixString){
- if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixString, NULL)){
- return (NULL);
- }
- }
-
- if (errorResult){
- return (NULL);
- }
-
- return (asciiString);
-}
-
-char *
-pkix_Cert2ASCII(PKIX_PL_Cert *cert)
-{
- PKIX_PL_X500Name *issuer = NULL;
- void *issuerAscii = NULL;
- PKIX_PL_X500Name *subject = NULL;
- void *subjectAscii = NULL;
- void *asciiString = NULL;
- PKIX_Error *errorResult = NULL;
- PKIX_UInt32 numChars;
- PKIX_UInt32 refCount = 0;
-
- /* Issuer */
- errorResult = PKIX_PL_Cert_GetIssuer(cert, &issuer, NULL);
- if (errorResult) goto cleanup;
-
- issuerAscii = pkix_Object2ASCII((PKIX_PL_Object*)issuer);
-
- /* Subject */
- errorResult = PKIX_PL_Cert_GetSubject(cert, &subject, NULL);
- if (errorResult) goto cleanup;
-
- if (subject){
- subjectAscii = pkix_Object2ASCII((PKIX_PL_Object*)subject);
- }
-
-/* errorResult = PKIX_PL_Object_GetRefCount((PKIX_PL_Object*)cert, &refCount, NULL); */
- if (errorResult) goto cleanup;
-
- errorResult = PKIX_PL_Malloc(200, &asciiString, NULL);
- if (errorResult) goto cleanup;
-
- numChars =
- PR_snprintf
- (asciiString,
- 200,
- "Ref: %d Subject=%s\nIssuer=%s\n",
- refCount,
- subjectAscii,
- issuerAscii);
-
- if (!numChars) goto cleanup;
-
-cleanup:
-
- if (issuer){
- if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)issuer, NULL)){
- return (NULL);
- }
- }
-
- if (subject){
- if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)subject, NULL)){
- return (NULL);
- }
- }
-
- if (PKIX_PL_Free((PKIX_PL_Object*)issuerAscii, NULL)){
- return (NULL);
- }
-
- if (PKIX_PL_Free((PKIX_PL_Object*)subjectAscii, NULL)){
- return (NULL);
- }
-
- if (errorResult){
- return (NULL);
- }
-
- return (asciiString);
-}
-
-PKIX_Error *
-cert_PrintCertChain(
- PKIX_List *pkixCertChain,
- void *plContext)
-{
- PKIX_PL_Cert *cert = NULL;
- PKIX_UInt32 numCerts = 0, i = 0;
- char *asciiResult = NULL;
-
- PKIX_ENTER(CERTVFYPKIX, "cert_PrintCertChain");
-
- PKIX_CHECK(
- PKIX_List_GetLength(pkixCertChain, &numCerts, plContext),
- PKIX_LISTGETLENGTHFAILED);
-
- fprintf(stderr, "\n");
-
- for (i = 0; i < numCerts; i++){
- PKIX_CHECK
- (PKIX_List_GetItem
- (pkixCertChain, i, (PKIX_PL_Object**)&cert, plContext),
- PKIX_LISTGETITEMFAILED);
-
- asciiResult = pkix_Cert2ASCII(cert);
-
- fprintf(stderr, "CERT[%d]:\n%s\n", i, asciiResult);
-
- PKIX_PL_Free(asciiResult, plContext);
- asciiResult = NULL;
-
- PKIX_DECREF(cert);
- }
-
-cleanup:
- PKIX_DECREF(cert);
-
- PKIX_RETURN(CERTVFYPKIX);
-}
-
-void
-cert_PrintCert(
- PKIX_PL_Cert *pkixCert,
- void *plContext)
-{
- char *asciiResult = NULL;
-
- asciiResult = pkix_Cert2ASCII(pkixCert);
-
- fprintf(stderr, "CERT[0]:\n%s\n", asciiResult);
-
- PKIX_PL_Free(asciiResult, plContext);
-}
-
-#endif /* DEBUG */
--- a/security/nss/lib/certhigh/manifest.mn
+++ b/security/nss/lib/certhigh/manifest.mn
@@ -20,16 +20,15 @@ CSRCS = \
certhtml.c \
certreq.c \
crlv2.c \
ocsp.c \
ocspsig.c \
certhigh.c \
certvfy.c \
certvfypkix.c \
- certvfypkixprint.c \
xcrldist.c \
$(NULL)
LIBRARY_NAME = certhi
# This part of the code, including all sub-dirs, can be optimized for size
export ALLOW_OPT_CODE_SIZE = 1
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -28,22 +28,22 @@
/*
* NSS's major version, minor version, patch level, build number, and whether
* this is a beta release.
*
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
*/
-#define NSS_VERSION "3.17" _NSS_ECC_STRING _NSS_CUSTOMIZED
+#define NSS_VERSION "3.17.1" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
#define NSS_VMAJOR 3
#define NSS_VMINOR 17
-#define NSS_VPATCH 0
+#define NSS_VPATCH 1
#define NSS_VBUILD 0
-#define NSS_BETA PR_FALSE
+#define NSS_BETA PR_TRUE
#ifndef RC_INVOKED
#include "seccomon.h"
typedef struct NSSInitParametersStr NSSInitParameters;
/*
--- a/security/nss/lib/pk11wrap/dev3hack.c
+++ b/security/nss/lib/pk11wrap/dev3hack.c
@@ -87,24 +87,24 @@ nssSlot_CreateSession
}
NSS_IMPLEMENT PRStatus
nssSession_Destroy
(
nssSession *s
)
{
- CK_RV ckrv = CKR_OK;
+ PRStatus rv = PR_SUCCESS;
if (s) {
if (s->isRW) {
PK11_RestoreROSession(s->slot->pk11slot, s->handle);
}
- nss_ZFreeIf(s);
+ rv = nss_ZFreeIf(s);
}
- return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE;
+ return rv;
}
static NSSSlot *
nssSlot_CreateFromPK11SlotInfo(NSSTrustDomain *td, PK11SlotInfo *nss3slot)
{
NSSSlot *rvSlot;
NSSArena *arena;
arena = nssArena_Create();
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -20,16 +20,16 @@
/*
* Softoken's major version, minor version, patch level, build number,
* and whether this is a beta release.
*
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
*/
-#define SOFTOKEN_VERSION "3.17" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION "3.17.1" SOFTOKEN_ECC_STRING " Beta"
#define SOFTOKEN_VMAJOR 3
#define SOFTOKEN_VMINOR 17
-#define SOFTOKEN_VPATCH 0
+#define SOFTOKEN_VPATCH 1
#define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_FALSE
+#define SOFTOKEN_BETA PR_TRUE
#endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -413,8 +413,12 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR
ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
"Incorrect signature algorithm specified in a digitally-signed element.")
ER3(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK, (SSL_ERROR_BASE + 129),
"The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.")
ER3(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL, (SSL_ERROR_BASE + 130),
"The server supports no protocols that the client advertises in the ALPN extension.")
+
+ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 131),
+"The server rejected the handshake because the client downgraded to a lower "
+"TLS version than the server supports.")
--- a/security/nss/lib/ssl/config.mk
+++ b/security/nss/lib/ssl/config.mk
@@ -2,16 +2,21 @@
# 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/.
ifdef NISCC_TEST
DEFINES += -DNISCC_TEST
endif
+# Allow build-time configuration of TLS 1.3 (Experimental)
+ifdef NSS_ENABLE_TLS_1_3
+DEFINES += -DNSS_ENABLE_TLS_1_3
+endif
+
ifdef NSS_NO_PKCS11_BYPASS
DEFINES += -DNO_PKCS11_BYPASS
else
CRYPTOLIB=$(SOFTOKEN_LIB_DIR)/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
EXTRA_LIBS += \
$(CRYPTOLIB) \
$(NULL)
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -47,26 +47,30 @@ static const ssl3CipherSuite nonDTLSSuit
};
/* Map back and forth between TLS and DTLS versions in wire format.
* Mapping table is:
*
* TLS DTLS
* 1.1 (0302) 1.0 (feff)
* 1.2 (0303) 1.2 (fefd)
+ * 1.3 (0304) 1.3 (fefc)
*/
SSL3ProtocolVersion
dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv)
{
if (tlsv == SSL_LIBRARY_VERSION_TLS_1_1) {
return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
}
if (tlsv == SSL_LIBRARY_VERSION_TLS_1_2) {
return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
}
+ if (tlsv == SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SSL_LIBRARY_VERSION_DTLS_1_3_WIRE;
+ }
/* Anything other than TLS 1.1 or 1.2 is an error, so return
* the invalid version 0xffff. */
return 0xffff;
}
/* Map known DTLS versions to known TLS versions.
* - Invalid versions (< 1.0) return a version of 0
@@ -80,16 +84,19 @@ dtls_DTLSVersionToTLSVersion(SSL3Protoco
}
if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) {
return SSL_LIBRARY_VERSION_TLS_1_1;
}
if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) {
return SSL_LIBRARY_VERSION_TLS_1_2;
}
+ if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_3_WIRE) {
+ return SSL_LIBRARY_VERSION_TLS_1_3;
+ }
/* Return a fictional higher version than we know of */
return SSL_LIBRARY_VERSION_TLS_1_2 + 1;
}
/* On this socket, Disable non-DTLS cipher suites in the argument's list */
SECStatus
ssl3_DisableNonDTLSSuites(sslSocket * ss)
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -183,16 +183,19 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
#define SSL_ENABLE_ALPN 26
/* SSL_REUSE_SERVER_ECDHE_KEY controls whether the ECDHE server key is
* reused for multiple handshakes or generated each time.
* SSL_REUSE_SERVER_ECDHE_KEY is currently enabled by default.
*/
#define SSL_REUSE_SERVER_ECDHE_KEY 27
+#define SSL_ENABLE_FALLBACK_SCSV 28 /* Send fallback SCSV in
+ * handshakes. */
+
#ifdef SSL_DEPRECATED_FUNCTION
/* Old deprecated function names */
SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRBool on);
#endif
/* New function names */
SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRBool on);
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -210,17 +210,20 @@ static const int compressionMethodsCount
static PRBool
compressionEnabled(sslSocket *ss, SSLCompressionMethod compression)
{
switch (compression) {
case ssl_compression_null:
return PR_TRUE; /* Always enabled */
#ifdef NSS_ENABLE_ZLIB
case ssl_compression_deflate:
- return ss->opt.enableDeflate;
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return ss->opt.enableDeflate;
+ }
+ return PR_FALSE;
#endif
default:
return PR_FALSE;
}
}
static const /*SSL3ClientCertificateType */ PRUint8 certificate_types [] = {
ct_RSA_sign,
@@ -632,24 +635,26 @@ ssl3_CipherSuiteAllowedForVersionRange(
* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5: never implemented
* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA: never implemented
*/
return vrange->min <= SSL_LIBRARY_VERSION_TLS_1_0;
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
case TLS_RSA_WITH_AES_256_CBC_SHA256:
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
- case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
- case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
- case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case TLS_RSA_WITH_AES_128_CBC_SHA256:
case TLS_RSA_WITH_AES_128_GCM_SHA256:
case TLS_RSA_WITH_NULL_SHA256:
+ return vrange->max == SSL_LIBRARY_VERSION_TLS_1_2;
+
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_2;
/* RFC 4492: ECC cipher suites need TLS extensions to negotiate curves and
* point formats.*/
case TLS_ECDH_ECDSA_WITH_NULL_SHA:
case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
@@ -664,20 +669,21 @@ ssl3_CipherSuiteAllowedForVersionRange(
case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case TLS_ECDHE_RSA_WITH_NULL_SHA:
case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0;
+ return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_0 &&
+ vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
default:
- return PR_TRUE;
+ return vrange->min < SSL_LIBRARY_VERSION_TLS_1_3;
}
}
/* return pointer to ssl3CipherSuiteDef for suite, or NULL */
/* XXX This does a linear search. A binary search would be better. */
static const ssl3CipherSuiteDef *
ssl_LookupCipherSuiteDef(ssl3CipherSuite suite)
{
@@ -3347,16 +3353,19 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe
case no_certificate: error = SSL_ERROR_NO_CERTIFICATE; break;
case bad_certificate: error = SSL_ERROR_BAD_CERT_ALERT; break;
case unsupported_certificate:error = SSL_ERROR_UNSUPPORTED_CERT_ALERT;break;
case certificate_revoked: error = SSL_ERROR_REVOKED_CERT_ALERT; break;
case certificate_expired: error = SSL_ERROR_EXPIRED_CERT_ALERT; break;
case certificate_unknown: error = SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT;
break;
case illegal_parameter: error = SSL_ERROR_ILLEGAL_PARAMETER_ALERT;break;
+ case inappropriate_fallback:
+ error = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
+ break;
/* All alerts below are TLS only. */
case unknown_ca: error = SSL_ERROR_UNKNOWN_CA_ALERT; break;
case access_denied: error = SSL_ERROR_ACCESS_DENIED_ALERT; break;
case decode_error: error = SSL_ERROR_DECODE_ERROR_ALERT; break;
case decrypt_error: error = SSL_ERROR_DECRYPT_ERROR_ALERT; break;
case export_restriction: error = SSL_ERROR_EXPORT_RESTRICTION_ALERT;
break;
@@ -4868,16 +4877,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
sslSessionID * sid;
ssl3CipherSpec * cwSpec;
SECStatus rv;
int i;
int length;
int num_suites;
int actual_count = 0;
PRBool isTLS = PR_FALSE;
+ PRBool requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE;
PRInt32 total_exten_len = 0;
unsigned paddingExtensionLen;
unsigned numCompressionMethods;
PRInt32 flags;
SSL_TRC(3, ("%d: SSL3[%d]: send client_hello handshake", SSL_GETPID(),
ss->fd));
@@ -5010,16 +5020,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
if (ss->sec.uncache)
(*ss->sec.uncache)(sid);
ssl_FreeSID(sid);
sid = NULL;
}
}
if (sid) {
+ requestingResume = PR_TRUE;
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
ss->ssl3.policy = sid->u.ssl3.policy;
} else {
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_misses );
@@ -5124,18 +5135,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
}
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
if (!num_suites) {
if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return SECFailure; /* count_cipher_suites has set error code. */
}
+
+ fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume ||
+ ss->version < sid->version);
+ /* make room for SCSV */
if (ss->ssl3.hs.sendingSCSV) {
- ++num_suites; /* make room for SCSV */
+ ++num_suites;
+ }
+ if (fallbackSCSV) {
+ ++num_suites;
}
/* count compression methods */
numCompressionMethods = 0;
for (i = 0; i < compressionMethodsCount; i++) {
if (compressionEnabled(ss, compressions[i]))
numCompressionMethods++;
}
@@ -5231,16 +5249,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
}
+ if (fallbackSCSV) {
+ rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV,
+ sizeof(ssl3CipherSuite));
+ if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+ actual_count++;
+ }
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
actual_count++;
if (actual_count > num_suites) {
if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
/* set error card removal/insertion error */
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
@@ -7706,22 +7733,41 @@ ssl3_HandleClientHello(sslSocket *ss, SS
}
/* grab the list of cipher suites. */
rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
if (rv != SECSuccess) {
goto loser; /* malformed */
}
+ /* If the ClientHello version is less than our maximum version, check for a
+ * TLS_FALLBACK_SCSV and reject the connection if found. */
+ if (ss->vrange.max > ss->clientHelloVersion) {
+ for (i = 0; i + 1 < suites.len; i += 2) {
+ PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+ if (suite_i != TLS_FALLBACK_SCSV)
+ continue;
+ desc = inappropriate_fallback;
+ errCode = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
+ goto alert_loser;
+ }
+ }
+
/* grab the list of compression methods. */
rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length);
if (rv != SECSuccess) {
goto loser; /* malformed */
}
+ /* TLS 1.3 requires that compression be empty */
+ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+ if (comps.len != 1 || comps.data[0] != ssl_compression_null) {
+ goto loser;
+ }
+ }
desc = handshake_failure;
/* Handle TLS hello extensions for SSL3 & TLS. We do not know if
* we are restarting a previous session until extensions have been
* parsed, since we might have received a SessionTicket extension.
* Note: we allow extensions even when negotiating SSL3 for the sake
* of interoperability (and backwards compatibility).
*/
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -77,16 +77,21 @@ static SECStatus ssl3_ClientHandleStatus
SECItem *data);
static PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket * ss, PRBool append,
PRUint32 maxBytes);
static PRInt32 ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append,
PRUint32 maxBytes);
static SECStatus ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type,
SECItem *data);
+static PRInt32 ssl3_ClientSendDraftVersionXtn(sslSocket *ss, PRBool append,
+ PRUint32 maxBytes);
+static SECStatus ssl3_ServerHandleDraftVersionXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data);
+
/*
* Write bytes. Using this function means the SECItem structure
* cannot be freed. The caller is expected to call this function
* on a shallow copy of the structure.
*/
static SECStatus
ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
{
@@ -240,16 +245,17 @@ static const ssl3HelloExtensionHandler c
#endif
{ ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
{ ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
{ ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn },
{ ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn },
{ ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn },
{ ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn },
+ { ssl_tls13_draft_version_xtn, &ssl3_ServerHandleDraftVersionXtn },
{ -1, NULL }
};
/* These two tables are used by the client, to handle server hello
* extensions. */
static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
{ ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
/* TODO: add a handler for ssl_ec_point_formats_xtn */
@@ -281,17 +287,18 @@ ssl3HelloExtensionSender clientHelloSend
{ ssl_elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
{ ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
#endif
{ ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn },
{ ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn },
{ ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn },
{ ssl_use_srtp_xtn, &ssl3_SendUseSRTPXtn },
{ ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn },
- { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }
+ { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn },
+ { ssl_tls13_draft_version_xtn, &ssl3_ClientSendDraftVersionXtn },
/* any extra entries will appear as { 0, NULL } */
};
static const
ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
{ ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
/* any extra entries will appear as { 0, NULL } */
};
@@ -2416,8 +2423,98 @@ ssl3_AppendPaddingExtension(sslSocket *s
return -1;
if (SECSuccess != ssl3_AppendHandshakeNumber(ss, paddingLen, 2))
return -1;
if (SECSuccess != ssl3_AppendHandshake(ss, padding, paddingLen))
return -1;
return extensionLen;
}
+
+/* ssl3_ClientSendDraftVersionXtn sends the TLS 1.3 temporary draft
+ * version extension.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+static PRInt32
+ssl3_ClientSendDraftVersionXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes)
+{
+ PRInt32 extension_length;
+
+ if (ss->version != SSL_LIBRARY_VERSION_TLS_1_3) {
+ return 0;
+ }
+
+ extension_length = 6; /* Type + length + number */
+ if (append && maxBytes >= extension_length) {
+ SECStatus rv;
+ rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_draft_version_xtn, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ rv = ssl3_AppendHandshakeNumber(ss, TLS_1_3_DRAFT_VERSION, 2);
+ if (rv != SECSuccess)
+ goto loser;
+ ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+ ssl_tls13_draft_version_xtn;
+ } else if (maxBytes < extension_length) {
+ PORT_Assert(0);
+ return 0;
+ }
+
+ return extension_length;
+
+loser:
+ return -1;
+}
+
+/* ssl3_ServerHandleDraftVersionXtn handles the TLS 1.3 temporary draft
+ * version extension.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+static SECStatus
+ssl3_ServerHandleDraftVersionXtn(sslSocket * ss, PRUint16 ex_type,
+ SECItem *data)
+{
+ PRInt32 draft_version;
+
+ /* Ignore this extension if we aren't doing TLS 1.3 */
+ if (ss->version != SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ if (data->len != 2)
+ goto loser;
+
+ /* Get the draft version out of the handshake */
+ draft_version = ssl3_ConsumeHandshakeNumber(ss, 2,
+ &data->data, &data->len);
+ if (draft_version < 0) {
+ goto loser;
+ }
+
+ /* Keep track of negotiated extensions. */
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ /* Compare the version */
+ if (draft_version != TLS_1_3_DRAFT_VERSION) {
+ SSL_TRC(30, ("%d: SSL3[%d]: Incompatible version of TLS 1.3 (%d), "
+ "expected %d",
+ SSL_GETPID(), ss->fd, draft_version, TLS_1_3_DRAFT_VERSION));
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ /*
+ * Incompatible/broken TLS 1.3 implementation. Fall back to TLS 1.2.
+ * TODO(ekr@rtfm.com): It's not entirely clear it's safe to roll back
+ * here. Need to double-check.
+ * TODO(ekr@rtfm.com): Currently we fall back even on broken extensions.
+ * because SECFailure does not cause handshake failures. See bug
+ * 753136.
+ */
+ SSL_TRC(30, ("%d: SSL3[%d]: Rolling back to TLS 1.2", SSL_GETPID(), ss->fd));
+ ss->version = SSL_LIBRARY_VERSION_TLS_1_2;
+
+ return SECSuccess;
+}
+
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -9,16 +9,21 @@
#ifndef __ssl3proto_h_
#define __ssl3proto_h_
typedef PRUint8 SSL3Opaque;
typedef PRUint16 SSL3ProtocolVersion;
/* version numbers are defined in sslproto.h */
+/* The TLS 1.3 draft version. Used to avoid negotiating
+ * between incompatible pre-standard TLS 1.3 drafts.
+ * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
+#define TLS_1_3_DRAFT_VERSION 3
+
typedef PRUint16 ssl3CipherSuite;
/* The cipher suites are defined in sslproto.h */
#define MAX_CERT_TYPES 10
#define MAX_COMPRESSION_METHODS 10
#define MAX_MAC_LENGTH 64
#define MAX_PADDING_LENGTH 64
#define MAX_KEY_LENGTH 64
@@ -93,16 +98,17 @@ typedef enum {
unknown_ca = 48,
access_denied = 49,
decode_error = 50,
decrypt_error = 51,
export_restriction = 60,
protocol_version = 70,
insufficient_security = 71,
internal_error = 80,
+ inappropriate_fallback = 86, /* could also be sent for SSLv3 */
user_canceled = 90,
no_renegotiation = 100,
/* Alerts for client hello extensions */
unsupported_extension = 110,
certificate_unobtainable = 111,
unrecognized_name = 112,
bad_certificate_status_response = 113,
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -191,13 +191,15 @@ SSL_ERROR_RX_UNEXPECTED_CERT_STATUS
SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM = (SSL_ERROR_BASE + 126),
SSL_ERROR_DIGEST_FAILURE = (SSL_ERROR_BASE + 127),
SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK = (SSL_ERROR_BASE + 129),
SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL = (SSL_ERROR_BASE + 130),
+SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 131),
+
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
#endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -322,16 +322,17 @@ typedef struct sslOptionsStr {
unsigned int enableRenegotiation : 2; /* 20-21 */
unsigned int requireSafeNegotiation : 1; /* 22 */
unsigned int enableFalseStart : 1; /* 23 */
unsigned int cbcRandomIV : 1; /* 24 */
unsigned int enableOCSPStapling : 1; /* 25 */
unsigned int enableNPN : 1; /* 26 */
unsigned int enableALPN : 1; /* 27 */
unsigned int reuseServerECDHEKey : 1; /* 28 */
+ unsigned int enableFallbackSCSV : 1; /* 29 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
sslHandshakingAsClient,
sslHandshakingAsServer
} sslHandshakingType;
typedef struct sslServerCertsStr {
@@ -1530,17 +1531,21 @@ extern PRInt32 ssl3_SendRecord(sslSock
*/
#define SSL_LIBRARY_VERSION_NONE 0
/* SSL_LIBRARY_VERSION_MAX_SUPPORTED is the maximum version that this version
* of libssl supports. Applications should use SSL_VersionRangeGetSupported at
* runtime to determine which versions are supported by the version of libssl
* in use.
*/
+#ifdef NSS_ENABLE_TLS_1_3
+#define SSL_LIBRARY_VERSION_MAX_SUPPORTED SSL_LIBRARY_VERSION_TLS_1_3
+#else
#define SSL_LIBRARY_VERSION_MAX_SUPPORTED SSL_LIBRARY_VERSION_TLS_1_2
+#endif
/* Rename this macro SSL_ALL_VERSIONS_DISABLED when SSL 2.0 is removed. */
#define SSL3_ALL_VERSIONS_DISABLED(vrange) \
((vrange)->min == SSL_LIBRARY_VERSION_NONE)
extern PRBool ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant,
SSL3ProtocolVersion version);
--- a/security/nss/lib/ssl/sslproto.h
+++ b/security/nss/lib/ssl/sslproto.h
@@ -11,26 +11,30 @@
#define __sslproto_h_
/* All versions less than 3_0 are treated as SSL version 2 */
#define SSL_LIBRARY_VERSION_2 0x0002
#define SSL_LIBRARY_VERSION_3_0 0x0300
#define SSL_LIBRARY_VERSION_TLS_1_0 0x0301
#define SSL_LIBRARY_VERSION_TLS_1_1 0x0302
#define SSL_LIBRARY_VERSION_TLS_1_2 0x0303
+#define SSL_LIBRARY_VERSION_TLS_1_3 0x0304
+
/* Note: this is the internal format, not the wire format */
#define SSL_LIBRARY_VERSION_DTLS_1_0 0x0302
#define SSL_LIBRARY_VERSION_DTLS_1_2 0x0303
+#define SSL_LIBRARY_VERSION_DTLS_1_3 0x0304
/* deprecated old name */
#define SSL_LIBRARY_VERSION_3_1_TLS SSL_LIBRARY_VERSION_TLS_1_0
/* The DTLS versions used in the spec */
#define SSL_LIBRARY_VERSION_DTLS_1_0_WIRE ((~0x0100) & 0xffff)
#define SSL_LIBRARY_VERSION_DTLS_1_2_WIRE ((~0x0102) & 0xffff)
+#define SSL_LIBRARY_VERSION_DTLS_1_3_WIRE ((~0x0103) & 0xffff)
/* Header lengths of some of the messages */
#define SSL_HL_ERROR_HBYTES 3
#define SSL_HL_CLIENT_HELLO_HBYTES 9
#define SSL_HL_CLIENT_MASTER_KEY_HBYTES 10
#define SSL_HL_CLIENT_FINISHED_HBYTES 1
#define SSL_HL_SERVER_HELLO_HBYTES 11
#define SSL_HL_SERVER_VERIFY_HBYTES 1
@@ -203,16 +207,21 @@
#define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2
/* TLS "Signaling Cipher Suite Value" (SCSV). May be requested by client.
* Must NEVER be chosen by server. SSL 3.0 server acknowledges by sending
* back an empty Renegotiation Info (RI) server hello extension.
*/
#define TLS_EMPTY_RENEGOTIATION_INFO_SCSV 0x00FF
+/* TLS_FALLBACK_SCSV is a signaling cipher suite value that indicates that a
+ * handshake is the result of TLS version fallback.
+ */
+#define TLS_FALLBACK_SCSV 0x5600
+
/* Cipher Suite Values starting with 0xC000 are defined in informational
* RFCs.
*/
#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001
#define TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002
#define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003
#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004
#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -76,17 +76,18 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableDeflate */
2, /* enableRenegotiation (default: requires extension) */
PR_FALSE, /* requireSafeNegotiation */
PR_FALSE, /* enableFalseStart */
PR_TRUE, /* cbcRandomIV */
PR_FALSE, /* enableOCSPStapling */
PR_TRUE, /* enableNPN */
PR_FALSE, /* enableALPN */
- PR_TRUE /* reuseServerECDHEKey */
+ PR_TRUE, /* reuseServerECDHEKey */
+ PR_FALSE /* enableFallbackSCSV */
};
/*
* default range of enabled SSL/TLS protocols
*/
static SSLVersionRange versions_defaults_stream = {
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_0
@@ -784,16 +785,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
case SSL_ENABLE_ALPN:
ss->opt.enableALPN = on;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
ss->opt.reuseServerECDHEKey = on;
break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ss->opt.enableFallbackSCSV = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
/* We can't use the macros for releasing the locks here,
* because ss->opt.noLocks might have changed just above.
* We must release these locks (monitors) here, if we aquired them above,
@@ -858,16 +863,17 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
on = ss->opt.requireSafeNegotiation; break;
case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
case SSL_CBC_RANDOM_IV: on = ss->opt.cbcRandomIV; break;
case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
case SSL_ENABLE_NPN: on = ss->opt.enableNPN; break;
case SSL_ENABLE_ALPN: on = ss->opt.enableALPN; break;
case SSL_REUSE_SERVER_ECDHE_KEY:
on = ss->opt.reuseServerECDHEKey; break;
+ case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
@@ -924,16 +930,19 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
case SSL_ENABLE_OCSP_STAPLING:
on = ssl_defaults.enableOCSPStapling;
break;
case SSL_ENABLE_NPN: on = ssl_defaults.enableNPN; break;
case SSL_ENABLE_ALPN: on = ssl_defaults.enableALPN; break;
case SSL_REUSE_SERVER_ECDHE_KEY:
on = ssl_defaults.reuseServerECDHEKey;
break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ on = ssl_defaults.enableFallbackSCSV;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
*pOn = on;
return rv;
@@ -1103,16 +1112,20 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
case SSL_ENABLE_ALPN:
ssl_defaults.enableALPN = on;
break;
case SSL_REUSE_SERVER_ECDHE_KEY:
ssl_defaults.reuseServerECDHEKey = on;
break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ssl_defaults.enableFallbackSCSV = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
return SECSuccess;
}
/* function tells us if the cipher suite is one that we no longer support. */
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -186,14 +186,15 @@ typedef enum {
ssl_ec_point_formats_xtn = 11,
#endif
ssl_signature_algorithms_xtn = 13,
ssl_use_srtp_xtn = 14,
ssl_app_layer_protocol_xtn = 16,
ssl_padding_xtn = 21,
ssl_session_ticket_xtn = 35,
ssl_next_proto_nego_xtn = 13172,
- ssl_renegotiation_info_xtn = 0xff01 /* experimental number */
+ ssl_renegotiation_info_xtn = 0xff01,
+ ssl_tls13_draft_version_xtn = 0xff02 /* experimental number */
} SSLExtensionType;
-#define SSL_MAX_EXTENSIONS 10 /* doesn't include ssl_padding_xtn. */
+#define SSL_MAX_EXTENSIONS 11 /* doesn't include ssl_padding_xtn. */
#endif /* __sslt_h_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
/*
* NSS utilities's major version, minor version, patch level, build number,
* and whether this is a beta release.
*
* The format of the version string should be
* "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
*/
-#define NSSUTIL_VERSION "3.17"
+#define NSSUTIL_VERSION "3.17.1 Beta"
#define NSSUTIL_VMAJOR 3
#define NSSUTIL_VMINOR 17
-#define NSSUTIL_VPATCH 0
+#define NSSUTIL_VPATCH 1
#define NSSUTIL_VBUILD 0
-#define NSSUTIL_BETA PR_FALSE
+#define NSSUTIL_BETA PR_TRUE
SEC_BEGIN_PROTOS
/*
* Returns a const string of the UTIL library version.
*/
extern const char *NSSUTIL_GetVersion(void);
--- a/security/nss/tests/pkcs11/netscape/suites/security/ssl/sslt.c
+++ b/security/nss/tests/pkcs11/netscape/suites/security/ssl/sslt.c
@@ -1,14 +1,14 @@
/* 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/. */
-#define VERION_MAJOR 1
-#define VERION_MINOR 0
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
#define VERSION_POINT 7
/* NSPR header files */
#include <prinit.h>
#include <prprf.h>
#include <prsystem.h>
#include <prmem.h>
#include <plstr.h>
#include <prnetdb.h>
@@ -169,17 +169,17 @@ PRIntn main(PRIntn ac, char **av, char *
int c;
if( ac == 1 ) {
PR_fprintf(PR_STDERR,
"\nSSL Test Suite Version %d.%d.%d\n\
All Rights Reserved\n\
Usage: sslt [-c client_nickname] [-n server_nickname] [-p passwd] [-d] testid\n",
-VERION_MAJOR, VERION_MINOR, VERSION_POINT);
+VERSION_MAJOR, VERSION_MINOR, VERSION_POINT);
exit(0);
}
for (c = 1; c<ac; c++) {
if (!PL_strcmp(av[c],"-c")) {
c++;
--- a/security/sandbox/moz.build
+++ b/security/sandbox/moz.build
@@ -8,132 +8,23 @@ if CONFIG['OS_ARCH'] == 'Linux':
DIRS += ['linux']
elif CONFIG['OS_ARCH'] == 'Darwin':
DIRS += ['mac']
elif CONFIG['OS_ARCH'] == 'WINNT':
LIBRARY_NAME = 'sandbox_s'
FORCE_STATIC_LIB = True
DIRS += [
+ 'staticruntime',
'win/src/sandboxbroker',
'win/src/sandboxtarget',
]
- SOURCES += [
- 'chromium/base/at_exit.cc',
- 'chromium/base/base_switches.cc',
- 'chromium/base/callback_internal.cc',
- 'chromium/base/cpu.cc',
- 'chromium/base/debug/alias.cc',
- 'chromium/base/debug/profiler.cc',
- 'chromium/base/lazy_instance.cc',
- 'chromium/base/location.cc',
- 'chromium/base/memory/ref_counted.cc',
- 'chromium/base/memory/singleton.cc',
- 'chromium/base/shim/base/logging.cpp',
- 'chromium/base/strings/nullable_string16.cc',
- 'chromium/base/strings/string_number_conversions.cc',
- 'chromium/base/strings/string_piece.cc',
- 'chromium/base/strings/string_util_constants.cc',
- 'chromium/base/strings/string_util_stripped.cc',
- 'chromium/base/strings/stringprintf.cc',
- 'chromium/base/strings/utf_string_conversion_utils.cc',
- 'chromium/base/strings/utf_string_conversions.cc',
- 'chromium/base/synchronization/lock.cc',
- 'chromium/base/synchronization/lock_impl_win.cc',
- 'chromium/base/third_party/dmg_fp/dtoa.cc',
- 'chromium/base/third_party/dmg_fp/g_fmt.cc',
- 'chromium/base/third_party/icu/icu_utf.cc',
- 'chromium/base/threading/platform_thread_win.cc',
- 'chromium/base/threading/thread_collision_warner.cc',
- 'chromium/base/threading/thread_id_name_manager.cc',
- 'chromium/base/threading/thread_local_win.cc',
- 'chromium/base/threading/thread_restrictions.cc',
- 'chromium/base/time/time.cc',
- 'chromium/base/time/time_win.cc',
- 'chromium/base/win/event_trace_provider.cc',
- 'chromium/base/win/pe_image.cc',
- 'chromium/base/win/registry.cc',
- 'chromium/base/win/scoped_handle.cc',
- 'chromium/base/win/scoped_process_information.cc',
- 'chromium/base/win/startup_information.cc',
- 'chromium/base/win/windows_version.cc',
- 'win/src/acl.cc',
- 'win/src/app_container.cc',
- 'win/src/broker_services.cc',
- 'win/src/crosscall_server.cc',
- 'win/src/eat_resolver.cc',
- 'win/src/filesystem_dispatcher.cc',
- 'win/src/filesystem_interception.cc',
- 'win/src/filesystem_policy.cc',
- 'win/src/handle_closer.cc',
- 'win/src/handle_closer_agent.cc',
- 'win/src/handle_dispatcher.cc',
- 'win/src/handle_interception.cc',
- 'win/src/handle_policy.cc',
- 'win/src/handle_table.cc',
- 'win/src/interception.cc',
- 'win/src/interception_agent.cc',
- 'win/src/job.cc',
- 'win/src/named_pipe_dispatcher.cc',
- 'win/src/named_pipe_interception.cc',
- 'win/src/named_pipe_policy.cc',
- 'win/src/policy_broker.cc',
- 'win/src/policy_engine_opcodes.cc',
- 'win/src/policy_engine_processor.cc',
- 'win/src/policy_low_level.cc',
- 'win/src/policy_target.cc',
- 'win/src/process_mitigations.cc',
- 'win/src/process_thread_dispatcher.cc',
- 'win/src/process_thread_interception.cc',
- 'win/src/process_thread_policy.cc',
- 'win/src/registry_dispatcher.cc',
- 'win/src/registry_interception.cc',
- 'win/src/registry_policy.cc',
- 'win/src/resolver.cc',
- 'win/src/restricted_token.cc',
- 'win/src/restricted_token_utils.cc',
- 'win/src/sandbox.cc',
- 'win/src/sandbox_nt_util.cc',
- 'win/src/sandbox_policy_base.cc',
- 'win/src/sandbox_utils.cc',
- 'win/src/service_resolver.cc',
- 'win/src/shared_handles.cc',
- 'win/src/sharedmem_ipc_client.cc',
- 'win/src/sharedmem_ipc_server.cc',
- 'win/src/sid.cc',
- 'win/src/sync_dispatcher.cc',
- 'win/src/sync_interception.cc',
- 'win/src/sync_policy.cc',
- 'win/src/target_interceptions.cc',
- 'win/src/target_process.cc',
- 'win/src/target_services.cc',
- 'win/src/win2k_threadpool.cc',
- 'win/src/win_utils.cc',
- 'win/src/window.cc',
- ]
-
- if CONFIG['CPU_ARCH'] == 'x86_64':
- SOURCES += [
- 'win/src/interceptors_64.cc',
- 'win/src/resolver_64.cc',
- 'win/src/service_resolver_64.cc',
- 'win/src/Wow64_64.cc',
- ]
- else:
- SOURCES += [
- 'win/src/resolver_32.cc',
- 'win/src/service_resolver_32.cc',
- 'win/src/sidestep/ia32_modrm_map.cpp',
- 'win/src/sidestep/ia32_opcode_map.cpp',
- 'win/src/sidestep/mini_disassembler.cpp',
- 'win/src/sidestep/preamble_patcher_with_stub.cpp',
- 'win/src/sidestep_resolver.cc',
- 'win/src/Wow64.cc',
- ]
+ include('objs.mozbuild')
+ SOURCES += security_sandbox_cppsrcs
for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
DEFINES[var] = True
LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
LOCAL_INCLUDES += ['/security/sandbox/chromium']
LOCAL_INCLUDES += ['/security']
copy from security/sandbox/moz.build
copy to security/sandbox/objs.mozbuild
--- a/security/sandbox/moz.build
+++ b/security/sandbox/objs.mozbuild
@@ -1,28 +1,16 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
-if CONFIG['OS_ARCH'] == 'Linux':
- DIRS += ['linux']
-elif CONFIG['OS_ARCH'] == 'Darwin':
- DIRS += ['mac']
-elif CONFIG['OS_ARCH'] == 'WINNT':
- LIBRARY_NAME = 'sandbox_s'
- FORCE_STATIC_LIB = True
-
- DIRS += [
- 'win/src/sandboxbroker',
- 'win/src/sandboxtarget',
- ]
-
- SOURCES += [
+if CONFIG['OS_ARCH'] == 'WINNT':
+ security_sandbox_lcppsrcs = [
'chromium/base/at_exit.cc',
'chromium/base/base_switches.cc',
'chromium/base/callback_internal.cc',
'chromium/base/cpu.cc',
'chromium/base/debug/alias.cc',
'chromium/base/debug/profiler.cc',
'chromium/base/lazy_instance.cc',
'chromium/base/location.cc',
@@ -107,46 +95,30 @@ elif CONFIG['OS_ARCH'] == 'WINNT':
'win/src/target_process.cc',
'win/src/target_services.cc',
'win/src/win2k_threadpool.cc',
'win/src/win_utils.cc',
'win/src/window.cc',
]
if CONFIG['CPU_ARCH'] == 'x86_64':
- SOURCES += [
+ security_sandbox_lcppsrcs += [
'win/src/interceptors_64.cc',
'win/src/resolver_64.cc',
'win/src/service_resolver_64.cc',
'win/src/Wow64_64.cc',
]
else:
- SOURCES += [
+ security_sandbox_lcppsrcs += [
'win/src/resolver_32.cc',
'win/src/service_resolver_32.cc',
'win/src/sidestep/ia32_modrm_map.cpp',
'win/src/sidestep/ia32_opcode_map.cpp',
'win/src/sidestep/mini_disassembler.cpp',
'win/src/sidestep/preamble_patcher_with_stub.cpp',
'win/src/sidestep_resolver.cc',
'win/src/Wow64.cc',
]
- for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
- 'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
- DEFINES[var] = True
-
- LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
- LOCAL_INCLUDES += ['/security/sandbox/chromium']
- LOCAL_INCLUDES += ['/security']
- LOCAL_INCLUDES += ['/nsprpub']
-
- DISABLE_STL_WRAPPING = True
-
- # Suppress warnings in third-party code.
- if CONFIG['_MSC_VER']:
- CXXFLAGS += [
- '-wd4275', # non dll-interface class exception used as base for dll-interface class
- '-wd4717', # recursive on all control paths, function will cause runtime stack overflow
- '-wd4996', # 'GetVersionExW': was declared deprecated
- ]
-
-FAIL_ON_WARNINGS = True
+ security_sandbox_cppsrcs = [
+ '%s/security/sandbox/%s' % (TOPSRCDIR, s)
+ for s in sorted(security_sandbox_lcppsrcs)
+ ]
new file mode 100644
--- /dev/null
+++ b/security/sandbox/staticruntime/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ LIBRARY_NAME = 'sandbox_staticruntime_s'
+ FORCE_STATIC_LIB = True
+ USE_STATIC_LIBS = True
+
+ include('../objs.mozbuild')
+ SOURCES += security_sandbox_cppsrcs
+
+ for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS',
+ 'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'):
+ DEFINES[var] = True
+
+ LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
+ LOCAL_INCLUDES += ['/security/sandbox/chromium']
+ LOCAL_INCLUDES += ['/security/sandbox']
+ LOCAL_INCLUDES += ['/security']
+ LOCAL_INCLUDES += ['/nsprpub']
+
+ DISABLE_STL_WRAPPING = True
+
+ # Suppress warnings in third-party code.
+ if CONFIG['_MSC_VER']:
+ CXXFLAGS += [
+ '-wd4275', # non dll-interface class exception used as base for dll-interface class
+ '-wd4717', # recursive on all control paths, function will cause runtime stack overflow
+ '-wd4996', # 'GetVersionExW': was declared deprecated
+ ]
+
+FAIL_ON_WARNINGS = True
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -183,17 +183,21 @@ this.BrowserIDManager.prototype = {
this._log.trace("initializeWithCurrentIdentity");
// Reset the world before we do anything async.
this.whenReadyToAuthenticate = Promise.defer();
this.whenReadyToAuthenticate.promise.then(null, (err) => {
this._log.error("Could not authenticate: " + err);
});
- this._shouldHaveSyncKeyBundle = false;
+ // initializeWithCurrentIdentity() can be called after the
+ // identity module was first initialized, e.g., after the
+ // user completes a force authentication, so we should make
+ // sure all credentials are reset before proceeding.
+ this.resetCredentials();
this._authFailureReason = null;
return this._fxaService.getSignedInUser().then(accountData => {
if (!accountData) {
this._log.info("initializeWithCurrentIdentity has no user logged in");
this.account = null;
// and we are as ready as we can ever be for auth.
this._shouldHaveSyncKeyBundle = true;
@@ -574,19 +578,20 @@ this.BrowserIDManager.prototype = {
// set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
this._authFailureReason = LOGIN_FAILED_LOGIN_REJECTED;
} else {
this._log.error("Non-authentication error in _fetchTokenForUser: "
+ (err.message || err));
// for now assume it is just a transient network related problem.
this._authFailureReason = LOGIN_FAILED_NETWORK_ERROR;
}
- // Drop the sync key bundle, but still expect to have one.
- // This will arrange for us to be in the right 'currentAuthState'
- // such that UI will show the right error.
+ // this._authFailureReason being set to be non-null in the above if clause
+ // ensures we are in the correct currentAuthState, and
+ // this._shouldHaveSyncKeyBundle being true ensures everything that cares knows
+ // that there is no authentication dance still under way.
this._shouldHaveSyncKeyBundle = true;
Weave.Status.login = this._authFailureReason;
Services.obs.notifyObservers(null, "weave:service:login:error", null);
throw err;
});
},
// Returns a promise that is resolved when we have a valid token for the
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -247,17 +247,19 @@ class MochiRemote(Mochitest):
def cleanup(self, options):
if self._dm.fileExists(self.remoteLog):
self._dm.getFile(self.remoteLog, self.localLog)
self._dm.removeFile(self.remoteLog)
else:
self.log.warning("Unable to retrieve log file (%s) from remote device" % self.remoteLog)
self._dm.removeDir(self.remoteProfile)
- self._dm.getDirectory(self.remoteNSPR, os.environ["MOZ_UPLOAD_DIR"])
+ blobberUploadDir = os.environ.get('MOZ_UPLOAD_DIR', None)
+ if blobberUploadDir:
+ self._dm.getDirectory(self.remoteNSPR, blobberUploadDir)
Mochitest.cleanup(self, options)
def findPath(self, paths, filename = None):
for path in paths:
p = path
if filename:
p = os.path.join(p, filename)
if os.path.exists(self.getFullPath(p)):
--- a/testing/mozbase/mozrunner/mozrunner/base/device.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/device.py
@@ -67,19 +67,16 @@ class DeviceRunner(BaseRunner):
self.device.connect()
self.device.setup_profile(self.profile)
# TODO: this doesn't work well when the device is running but dropped
# wifi for some reason. It would be good to probe the state of the device
# to see if we have the homescreen running, or something, before waiting here
self.device.wait_for_net()
- if not isinstance(self.device, Emulator):
- self.device.reboot()
-
if not self.device.wait_for_net():
raise Exception("Network did not come up when starting device")
self.app_ctx.stop_application()
# In this case we need to pass in env as part of the command.
# Make this empty so BaseRunner doesn't pass anything into the
# process class.
self._env = self.env
--- a/toolkit/components/places/ColorAnalyzer_worker.js
+++ b/toolkit/components/places/ColorAnalyzer_worker.js
@@ -114,17 +114,17 @@ onmessage = function(event) {
weight *= chroma / CHROMA_WEIGHT_MIDDLE;
metadata.desirability *= weight;
return metadata;
});
// only send back the most desirable colors
mergedColors.sort(function(a, b) {
- return b.desirability - a.desirability;
+ return b.desirability != a.desirability ? b.desirability - a.desirability : b.color - a.color;
});
mergedColors = mergedColors.map(function(metadata) {
return metadata.color;
}).slice(0, maxColors);
postMessage({ colors: mergedColors });
};
/**
@@ -211,17 +211,17 @@ function mergeColors(colorFrequencies, n
};
});
let merged = clusterlib.hcluster(items, distance, merge, threshold);
return merged;
}
function descendingFreqSort(a, b) {
- return b.freq - a.freq;
+ return b.freq != a.freq ? b.freq - a.freq : b.color - a.color;
}
/**
* Given two items for a pair of clusters (as created in mergeColors above),
* determine the distance between them so we know if we should merge or not.
* Uses the euclidean distance between their mean colors in the lab color
* space, weighted so larger items are harder to merge.
*
--- a/toolkit/components/places/tests/browser/browser_colorAnalyzer.js
+++ b/toolkit/components/places/tests/browser/browser_colorAnalyzer.js
@@ -325,21 +325,21 @@ tests.push(function test_perfBigImage()
const filePrefix = getRootDirectory(gTestPath) + "colorAnalyzer/";
tests.push(function test_categoryDiscover() {
frcTest(filePrefix + "category-discover.png", 0xB28D3A,
"category-discover analysis returns red");
});
tests.push(function test_localeGeneric() {
- frcTest(filePrefix + "localeGeneric.png", 0x00A400,
- "localeGeneric analysis returns orange");
+ frcTest(filePrefix + "localeGeneric.png", 0x3EC23E,
+ "localeGeneric analysis returns green");
});
tests.push(function test_dictionaryGeneric() {
- frcTest(filePrefix + "dictionaryGeneric-16.png", 0x502E1E,
- "dictionaryGeneric-16 analysis returns blue");
+ frcTest(filePrefix + "dictionaryGeneric-16.png", 0x854C30,
+ "dictionaryGeneric-16 analysis returns brown");
});
tests.push(function test_extensionGeneric() {
frcTest(filePrefix + "extensionGeneric-16.png", 0x53BA3F,
"extensionGeneric-16 analysis returns green");
});
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -682,17 +682,21 @@ this.SocialService = {
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.setComplexValue(getPrefnameFromOrigin(manifest.origin), Ci.nsISupportsString, string);
// overwrite the existing provider then notify the front end so it can
// handle any reload that might be necessary.
if (ActiveProviders.has(manifest.origin)) {
- let provider = new SocialProvider(manifest);
+ // unload the worker prior to replacing the provider instance, also
+ // ensures the workerapi instance is terminated.
+ let provider = SocialServiceInternal.providers[manifest.origin];
+ provider.enabled = false;
+ provider = new SocialProvider(manifest);
SocialServiceInternal.providers[provider.origin] = provider;
// update the cache and ui, reload provider if necessary
this.getOrderedProviderList(providers => {
this._notifyProviderListeners("provider-update", provider.origin, providers);
});
}
},
@@ -751,18 +755,20 @@ function SocialProvider(input) {
this.domain = etld.getBaseDomainFromHost(originUri.host);
} catch(e) {
this.domain = originUri.host;
}
}
SocialProvider.prototype = {
reload: function() {
- this._terminate();
- this._activate();
+ // calling terminate/activate does not set the enabled state whereas setting
+ // enabled will call terminate/activate
+ this.enabled = false;
+ this.enabled = true;
Services.obs.notifyObservers(null, "social:provider-reload", this.origin);
},
// Provider enabled/disabled state. Disabled providers do not have active
// connections to their FrameWorkers.
_enabled: false,
get enabled() {
return this._enabled;
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -754,19 +754,19 @@ nsTypeAheadFind::GetSearchContainers(nsI
// Hack for XMLPrettyPrinter. nsFind can't handle complex anonymous content.
// If the root node has an XBL binding then there's not much we can do in
// in general, but we can try searching the binding's first child, which
// in the case of XMLPrettyPrinter contains the visible pretty-printed
// content.
nsXBLBinding* binding = rootContent->GetXBLBinding();
if (binding) {
- nsIContent* child = binding->GetAnonymousContent()->GetFirstChild();
- if (child) {
- searchRootNode = do_QueryInterface(child);
+ nsIContent* anonContent = binding->GetAnonymousContent();
+ if (anonContent) {
+ searchRootNode = do_QueryInterface(anonContent->GetFirstChild());
}
}
mSearchRange->SelectNodeContents(searchRootNode);
if (!mStartPointRange) {
mStartPointRange = new nsRange(doc);
}
mStartPointRange->SetStart(searchRootNode, 0);
--- a/toolkit/components/xulstore/XULStore.js
+++ b/toolkit/components/xulstore/XULStore.js
@@ -21,17 +21,18 @@ const STOREDB_FILENAME = "xulstore.json"
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
function XULStore() {
- this.init();
+ if (!Services.appinfo.inSafeMode)
+ this.load();
}
XULStore.prototype = {
classID: XULSTORE_CID,
classInfo: XPCOMUtils.generateCI({classID: XULSTORE_CID,
contractID: XULSTORE_CONTRACTID,
classDescription: "XULStore",
interfaces: [Ci.nsIXULStore]}),
@@ -53,34 +54,33 @@ XULStore.prototype = {
* ...
* }
*/
_data: {},
_storeFile: null,
_needsSaving: false,
_saveAllowed: true,
_writeTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
- _writeTimerInitialized: false,
- init: function () {
+ load: function () {
Services.obs.addObserver(this, "profile-before-change", true);
this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
this._storeFile.append(STOREDB_FILENAME);
if (!this._storeFile.exists()) {
this.import();
} else {
this.readFile();
}
},
observe: function(subject, topic, data) {
+ this.writeFile();
if (topic == "profile-before-change") {
- this.writeFile();
this._saveAllowed = false;
}
},
/*
* Internal function for logging debug messages to the Error Console window
*/
log: function (message) {
@@ -177,28 +177,22 @@ XULStore.prototype = {
{ tmpPath: this._storeFile.path + ".tmp" });
} catch (e) {
this.log("Failed to write xulstore.json: " + e);
throw e;
}
}),
markAsChanged: function() {
- this._needsSaving = true;
- if (this._writeTimerInitialized)
+ if (this._needsSaving || !this._storeFile)
return;
- let callback = () => {
- this._writeTimerInitialized = false;
- this.writeFile();
- };
-
// Don't write the file more than once every 30 seconds.
- this._writeTimerInitialized = true;
- this._writeTimer.initWithCallback(callback, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
+ this._needsSaving = true;
+ this._writeTimer.init(this, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
},
/* ---------- interface implementation ---------- */
setValue: function (docURI, id, attr, value) {
this.log("Saving " + attr + "=" + value + " for id=" + id + ", doc=" + docURI);
if (!this._saveAllowed) {
--- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build
+++ b/toolkit/crashreporter/breakpad-windows-libxul/moz.build
@@ -1,14 +1,18 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
+DIRS += [
+ 'staticruntime'
+]
+
SOURCES += [
'../google-breakpad/src/common/windows/http_upload.cc',
]
LIBRARY_NAME = 'google_breakpad_libxul_s'
FINAL_LIBRARY = 'xul'
copy from toolkit/crashreporter/breakpad-windows-libxul/moz.build
copy to toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build
--- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build
+++ b/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build
@@ -1,21 +1,19 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
SOURCES += [
- '../google-breakpad/src/common/windows/http_upload.cc',
+ '../../google-breakpad/src/common/windows/http_upload.cc',
]
-LIBRARY_NAME = 'google_breakpad_libxul_s'
-
-FINAL_LIBRARY = 'xul'
+LIBRARY_NAME = 'google_breakpad_libxul_staticruntime_s'
for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD', 'NOMINMAX'):
DEFINES[var] = True
LOCAL_INCLUDES += [
'/toolkit/crashreporter/google-breakpad/src',
]
@@ -25,8 +23,9 @@ include('/toolkit/crashreporter/google-b
include('/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mozbuild')
SOURCES += objs_common
SOURCES += objs_crash_generation
SOURCES += objs_handler
SOURCES += objs_sender
DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -17,24 +17,25 @@ LOCAL_INCLUDES += [
if CONFIG['OS_ARCH'] == 'WINNT':
UNIFIED_SOURCES += [
'crashreporter_win.cpp',
]
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
USE_LIBS += [
- 'google_breakpad_libxul_s',
+ 'google_breakpad_libxul_staticruntime_s',
]
OS_LIBS += [
'comctl32',
'shell32',
'wininet',
'shlwapi',
]
+ USE_STATIC_LIBS = True
elif CONFIG['OS_ARCH'] == 'Darwin':
UNIFIED_SOURCES += [
'crashreporter_osx.mm',
'crashreporter_unix_common.cpp',
]
LOCAL_INCLUDES += [
'../google-breakpad/src/common/mac',
]
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -1787,17 +1787,23 @@ ThreadClient.prototype = {
}
// Otherwise, force a pause in order to set the breakpoint.
this.interrupt((aResponse) => {
if (aResponse.error) {
// Can't set the breakpoint if pausing failed.
aOnResponse(aResponse);
return;
}
- doSetBreakpoint(this.resume.bind(this));
+
+ const { type, why } = aResponse;
+ const cleanUp = type == "paused" && why.type == "interrupted"
+ ? () => this.resume()
+ : noop;
+
+ doSetBreakpoint(cleanUp);
});
},
/**
* Release multiple thread-lifetime object actors. If any pause-lifetime
* actors are included in the request, a |notReleasable| error will return,
* but all the thread-lifetime ones will have been released.
*
--- a/toolkit/devtools/server/actors/common.js
+++ b/toolkit/devtools/server/actors/common.js
@@ -161,8 +161,32 @@ ActorPool.prototype = {
actor.disconnect();
}
this._cleanups = {};
}
}
exports.ActorPool = ActorPool;
+// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when it is
+// implemented.
+exports.getOffsetColumn = function getOffsetColumn(aOffset, aScript) {
+ let bestOffsetMapping = null;
+ for (let offsetMapping of aScript.getAllColumnOffsets()) {
+ if (!bestOffsetMapping ||
+ (offsetMapping.offset <= aOffset &&
+ offsetMapping.offset > bestOffsetMapping.offset)) {
+ bestOffsetMapping = offsetMapping;
+ }
+ }
+
+ if (!bestOffsetMapping) {
+ // XXX: Try not to completely break the experience of using the debugger for
+ // the user by assuming column 0. Simultaneously, report the error so that
+ // there is a paper trail if the assumption is bad and the debugging
+ // experience becomes wonky.
+ reportError(new Error("Could not find a column for offset " + aOffset
+ + " in the script " + aScript));
+ return 0;
+ }
+
+ return bestOffsetMapping.columnNumber;
+}
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3,17 +3,17 @@
/* 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/. */
"use strict";
const Services = require("Services");
const { Cc, Ci, Cu, components, ChromeWorker } = require("chrome");
-const { ActorPool } = require("devtools/server/actors/common");
+const { ActorPool, getOffsetColumn } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { dbg_assert, dumpn, update } = DevToolsUtils;
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
const promise = require("promise");
const Debugger = require("Debugger");
const xpcInspector = require("xpcInspector");
const mapURIToAddonID = require("./utils/map-uri-to-addon-id");
@@ -5221,41 +5221,16 @@ ThreadSources.prototype = {
}
}
};
exports.ThreadSources = ThreadSources;
// Utility functions.
-// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when it is
-// implemented.
-function getOffsetColumn(aOffset, aScript) {
- let bestOffsetMapping = null;
- for (let offsetMapping of aScript.getAllColumnOffsets()) {
- if (!bestOffsetMapping ||
- (offsetMapping.offset <= aOffset &&
- offsetMapping.offset > bestOffsetMapping.offset)) {
- bestOffsetMapping = offsetMapping;
- }
- }
-
- if (!bestOffsetMapping) {
- // XXX: Try not to completely break the experience of using the debugger for
- // the user by assuming column 0. Simultaneously, report the error so that
- // there is a paper trail if the assumption is bad and the debugging
- // experience becomes wonky.
- reportError(new Error("Could not find a column for offset " + aOffset
- + " in the script " + aScript));
- return 0;
- }
-
- return bestOffsetMapping.columnNumber;
-}
-
/**
* Return the non-source-mapped location of the given Debugger.Frame. If the
* frame does not have a script, the location's properties are all null.
*
* @param Debugger.Frame aFrame
* The frame whose location we are getting.
* @returns Object
* Returns an object of the form { url, line, column }
--- a/toolkit/devtools/server/actors/tracer.js
+++ b/toolkit/devtools/server/actors/tracer.js
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
const Debugger = require("Debugger");
+const { getOffsetColumn } = require("devtools/server/actors/common");
// TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
// once it is implemented.
if (!Object.getOwnPropertyDescriptor(Debugger.Frame.prototype, "depth")) {
Debugger.Frame.prototype._depth = null;
Object.defineProperty(Debugger.Frame.prototype, "depth", {
get: function () {
if (this._depth === null) {
@@ -55,16 +56,17 @@ const MAX_PROPERTIES = 3;
*/
const TRACE_TYPES = new Set([
"time",
"return",
"throw",
"yield",
"name",
"location",
+ "hitCount",
"callsite",
"parameterNames",
"arguments",
"depth"
]);
/**
* Creates a TracerActor. TracerActor provides a stream of function
@@ -76,16 +78,17 @@ function TracerActor(aConn, aParent)
this._parent = aParent;
this._attached = false;
this._activeTraces = new MapStack();
this._totalTraces = 0;
this._startTime = 0;
this._sequence = 0;
this._bufferSendTimer = null;
this._buffer = [];
+ this._hitCounts = new WeakMap();
// Keep track of how many different trace requests have requested what kind of
// tracing info. This way we can minimize the amount of data we are collecting
// at any given time.
this._requestsForTraceType = Object.create(null);
for (let type of TRACE_TYPES) {
this._requestsForTraceType[type] = 0;
}
@@ -231,16 +234,21 @@ TracerActor.prototype = {
name = this._activeTraces.peekKey();
stoppedTraceTypes = this._activeTraces.pop();
}
for (let traceType of stoppedTraceTypes) {
this._requestsForTraceType[traceType]--;
}
+ // Clear hit counts if no trace is requesting them.
+ if (!this._requestsForTraceType.hitCount) {
+ this._hitCounts.clear();
+ }
+
if (this.idle) {
this.dbg.enabled = false;
}
return {
type: "stoppedTrace",
why: "requested",
name
@@ -267,26 +275,36 @@ TracerActor.prototype = {
};
if (this._requestsForTraceType.name) {
packet.name = aFrame.callee
? aFrame.callee.displayName || "(anonymous function)"
: "(" + aFrame.type + ")";
}
- if (this._requestsForTraceType.location && aFrame.script) {
- // We should return the location of the start of the script, but
- // Debugger.Script does not provide complete start locations (bug
- // 901138). Instead, return the current offset (the location of the first
- // statement in the function).
- packet.location = {
- url: aFrame.script.url,
- line: aFrame.script.startLine,
- column: getOffsetColumn(aFrame.offset, aFrame.script)
- };
+ if (aFrame.script) {
+ if (this._requestsForTraceType.hitCount) {
+ // Increment hit count.
+ let previousHitCount = this._hitCounts.get(aFrame.script) || 0;
+ this._hitCounts.set(aFrame.script, previousHitCount + 1);
+
+ packet.hitCount = this._hitCounts.get(aFrame.script);
+ }
+
+ if (this._requestsForTraceType.location) {
+ // We should return the location of the start of the script, but
+ // Debugger.Script does not provide complete start locations (bug
+ // 901138). Instead, return the current offset (the location of the first
+ // statement in the function).
+ packet.location = {
+ url: aFrame.script.url,
+ line: aFrame.script.startLine,
+ column: getOffsetColumn(aFrame.offset, aFrame.script)
+ };
+ }
}
if (this._parent.threadActor && aFrame.script) {
packet.blackBoxed = this._parent.threadActor.sources.isBlackBoxed(aFrame.script.url);
} else {
packet.blackBoxed = false;
}
@@ -492,22 +510,16 @@ MapStack.prototype = {
let keyIndex = this._stack.lastIndexOf(aKey);
this._stack.splice(keyIndex, 1);
delete this._map[aKey];
}
return value;
}
};
-// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when
-// it is implemented.
-function getOffsetColumn(aOffset, aScript) {
- return 0;
-}
-
// Serialization helper functions. Largely copied from script.js and modified
// for use in serialization rather than object actor requests.
/**
* Create a grip for the given debuggee value.
*
* @param aValue Debugger.Object|primitive
* The value to describe with the created grip.
--- a/toolkit/devtools/server/tests/unit/test_trace_actor-05.js
+++ b/toolkit/devtools/server/tests/unit/test_trace_actor-05.js
@@ -83,19 +83,20 @@ function test_enter_exit_frame()
.then(function() {
let url = getFileUrl("tracerlocations.js");
check_location(traces[0].location, { url: url, line: 1, column: 0 });
do_check_eq(traces[1].name, "foo");
// XXX: foo's definition is at tracerlocations.js:3:0, but Debugger.Script
- // does not provide complete definition locations (bug 901138). |column|
- // will always be 0 until we can get bug 863089 fixed.
- check_location(traces[1].location, { url: url, line: 3, column: 0 });
+ // does not provide complete definition locations (bug 901138). Therefore,
+ // we use the first statement in the function (tracerlocations.js:4:2) for
+ // a column approximation.
+ check_location(traces[1].location, { url: url, line: 3, column: 2 });
check_location(traces[1].callsite, { url: url, line: 8, column: 0 });
do_check_eq(typeof traces[1].parameterNames, "object");
do_check_eq(traces[1].parameterNames.length, 1);
do_check_eq(traces[1].parameterNames[0], "x");
do_check_eq(typeof traces[1].arguments, "object");
do_check_true(Array.isArray(traces[1].arguments));
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_trace_actor-11.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that hit counts from tracer count function frames correctly, even after
+ * restarting the trace.
+ */
+
+var gDebuggee;
+var gClient;
+var gTraceClient;