Merge m-c to elm.

This commit is contained in:
Nick Alexander 2013-09-24 09:17:43 -07:00
commit f8b82bf7ff
648 changed files with 18363 additions and 1850 deletions

View File

@ -23,6 +23,7 @@
#include "Relation.h"
#include "RootAccessible.h"
#include "States.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/Util.h"
#include "nsXPCOMStrings.h"

View File

@ -14,6 +14,7 @@
#include "nsFontMetrics.h"
#include "nsLayoutUtils.h"
#include "HyperTextAccessible.h"
#include "mozilla/AppUnits.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -504,7 +505,7 @@ TextAttrsMgr::FontSizeTextAttr::
//
// XXX todo: consider sharing this code with layout module? (bug 474621)
float px =
NSAppUnitsToFloatPixels(aValue, nsDeviceContext::AppUnitsPerCSSPixel());
NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
// Each pt is 4/3 of a CSS pixel.
int pts = NS_lround(px*3/4);

View File

@ -18,7 +18,7 @@
#include "nsIAccessibleStates.h"
#include "nsIContent.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"

View File

@ -74,10 +74,11 @@ DocAccessible::
DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
nsIPresShell* aPresShell) :
HyperTextAccessibleWrap(aRootContent, this),
mDocumentNode(aDocument), mScrollPositionChangedTicks(0),
// XXX aaronl should we use an algorithm for the initial cache size?
mAccessibleCache(kDefaultCacheSize),
mNodeToAccessibleMap(kDefaultCacheSize),
mDocumentNode(aDocument),
mScrollPositionChangedTicks(0),
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
mVirtualCursor(nullptr),
mPresShell(aPresShell)

View File

@ -498,90 +498,30 @@ var Output = {
speechHelper: {
EARCONS: ['chrome://global/content/accessibility/tick.wav'],
delayedActions: [],
earconsToLoad: -1, // -1: not inited, 1 or more: initing, 0: inited
earconBuffers: {},
webaudioEnabled: false,
inited: false,
webspeechEnabled: false,
doDelayedActionsIfLoaded: function doDelayedActionsIfLoaded(aToLoadCount) {
if (aToLoadCount === 0) {
this.outputActions(this.delayedActions);
this.delayedActions = [];
return true;
}
return false;
},
init: function init() {
if (this.earconsToLoad === 0) {
// Already inited.
return;
}
let window = Utils.win;
this.webaudioEnabled = !!window.AudioContext;
this.webspeechEnabled = !!window.speechSynthesis;
this.earconsToLoad = this.webaudioEnabled ? this.EARCONS.length : 0;
if (this.doDelayedActionsIfLoaded(this.earconsToLoad)) {
// Nothing to load
return;
}
this.audioContext = new window.AudioContext();
for (let earcon of this.EARCONS) {
let xhr = new window.XMLHttpRequest();
xhr.open('GET', earcon);
xhr.responseType = 'arraybuffer';
xhr.onerror = () => {
Logger.error('Error getting earcon:', xhr.statusText);
this.doDelayedActionsIfLoaded(--this.earconsToLoad);
};
xhr.onload = () => {
this.audioContext.decodeAudioData(
xhr.response,
(audioBuffer) => {
try {
let earconName = /.*\/(.*)\..*$/.exec(earcon)[1];
this.earconBuffers[earconName] = new WeakMap();
this.earconBuffers[earconName].set(window, audioBuffer);
this.doDelayedActionsIfLoaded(--this.earconsToLoad);
} catch (x) {
Logger.logException(x);
}
},
() => {
this.doDelayedActionsIfLoaded(--this.earconsToLoad);
Logger.error('Error decoding earcon');
});
};
xhr.send();
let earconName = /.*\/(.*)\..*$/.exec(earcon)[1];
this.earconBuffers[earconName] = new WeakMap();
this.earconBuffers[earconName].set(window, new window.Audio(earcon));
}
this.inited = true;
},
output: function output(aActions) {
if (this.earconsToLoad !== 0) {
// We did not load the earcons yet.
this.delayedActions.push.apply(this.delayedActions, aActions);
if (this.earconsToLoad < 0) {
// Loading did not start yet, start it.
this.init();
}
return;
if (!this.inited) {
this.init();
}
this.outputActions(aActions);
},
outputActions: function outputActions(aActions) {
for (let action of aActions) {
let window = Utils.win;
Logger.info('tts.' + action.method,
@ -595,13 +535,10 @@ var Output = {
if (action.method === 'speak' && this.webspeechEnabled) {
window.speechSynthesis.speak(
new window.SpeechSynthesisUtterance(action.data));
} else if (action.method === 'playEarcon' && this.webaudioEnabled) {
} else if (action.method === 'playEarcon') {
let audioBufferWeakMap = this.earconBuffers[action.data];
if (audioBufferWeakMap) {
let node = this.audioContext.createBufferSource();
node.connect(this.audioContext.destination);
node.buffer = audioBufferWeakMap.get(window);
node.start(0);
audioBufferWeakMap.get(window).cloneNode(false).play();
}
}
}
@ -798,6 +735,9 @@ var Input = {
case 'swipeleft1':
this.moveCursor('movePrevious', 'Simple', 'gesture');
break;
case 'exploreend1':
this.activateCurrent(null, true);
break;
case 'swiperight2':
this.sendScrollMessage(-1, true);
break;
@ -938,12 +878,13 @@ var Input = {
mm.sendAsyncMessage(type, aDetails);
},
activateCurrent: function activateCurrent(aData) {
activateCurrent: function activateCurrent(aData, aActivateIfKey = false) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
let offset = aData && typeof aData.keyIndex === 'number' ?
aData.keyIndex - Output.brailleState.startOffset : -1;
mm.sendAsyncMessage('AccessFu:Activate', {offset: offset});
mm.sendAsyncMessage('AccessFu:Activate',
{offset: offset, activateIfKey: aActivateIfKey});
},
sendContextMenuMessage: function sendContextMenuMessage() {

View File

@ -428,10 +428,10 @@ TouchPoint.prototype = {
}
// To be considered an explore...
if (!this.done &&
duration > TouchAdapter.SWIPE_MAX_DURATION &&
if (duration > TouchAdapter.SWIPE_MAX_DURATION &&
(this.distanceTraveled / this.dpi) > TouchAdapter.TAP_MAX_RADIUS) {
return {type: 'explore', x: this.x, y: this.y};
return {type: this.done ? 'exploreend' : 'explore',
x: this.x, y: this.y};
}
return null;

View File

@ -31,6 +31,7 @@ const ROLE_CHECK_MENU_ITEM = Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM;
const ROLE_PASSWORD_TEXT = Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT;
const ROLE_RADIO_MENU_ITEM = Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM;
const ROLE_TOGGLE_BUTTON = Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON;
const ROLE_KEY = Ci.nsIAccessibleRole.ROLE_KEY;
const ROLE_ENTRY = Ci.nsIAccessibleRole.ROLE_ENTRY;
const ROLE_LIST = Ci.nsIAccessibleRole.ROLE_LIST;
const ROLE_DEFINITION_LIST = Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST;
@ -104,6 +105,7 @@ var gSimpleTraversalRoles =
ROLE_RADIO_MENU_ITEM,
ROLE_TOGGLE_BUTTON,
ROLE_ENTRY,
ROLE_KEY,
ROLE_HEADER,
ROLE_HEADING,
// Used for traversing in to child OOP frames.

View File

@ -164,6 +164,12 @@ function forwardToChild(aMessage, aListener, aVCPosition) {
function activateCurrent(aMessage) {
Logger.debug('activateCurrent');
function activateAccessible(aAccessible) {
if (aMessage.json.activateIfKey &&
aAccessible.role != Ci.nsIAccessibleRole.ROLE_KEY) {
// Only activate keys, don't do anything on other objects.
return;
}
if (aAccessible.actionCount > 0) {
aAccessible.doAction(0);
} else {

View File

@ -44,6 +44,7 @@
#include "oleacc.h"
#include "nsIAccessibleTypes.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
using namespace mozilla;
using namespace mozilla::a11y;

View File

@ -31,7 +31,7 @@ this.Keyboard = {
if (this._messageManager && !Cu.isDeadWrapper(this._messageManager))
return this._messageManager;
throw Error('no message manager set');
return null;
},
set messageManager(mm) {
@ -92,6 +92,10 @@ this.Keyboard = {
// If we get a 'Keyboard:XXX' message, check that the sender has the
// keyboard permission.
if (msg.name.indexOf("Keyboard:") != -1) {
if (!this.messageManager) {
return;
}
let mm;
try {
mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)

View File

@ -199,15 +199,51 @@ MozKeyboard.prototype = {
}
};
const TESTING_ENABLED_PREF = "dom.mozInputMethod.testing";
/*
* A WeakMap to map input method iframe window to its active status.
*/
let WindowMap = {
// WeakMap of <window, boolean> pairs.
_map: null,
/*
* Check if the given window is active.
*/
isActive: function(win) {
if (!this._map || !win) {
return false;
}
return this._map.get(win, false);
},
/*
* Set the active status of the given window.
*/
setActive: function(win, isActive) {
if (!win) {
return;
}
if (!this._map) {
this._map = new WeakMap();
}
this._map.set(win, isActive);
}
};
/**
* ==============================================
* InputMethodManager
* ==============================================
*/
function MozInputMethodManager() { }
function MozInputMethodManager(win) {
this._window = win;
}
MozInputMethodManager.prototype = {
_supportsSwitching: false,
_window: null,
classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
@ -224,18 +260,30 @@ MozInputMethodManager.prototype = {
}),
showAll: function() {
if (!WindowMap.isActive(this._window)) {
return;
}
cpmm.sendAsyncMessage('Keyboard:ShowInputMethodPicker', {});
},
next: function() {
if (!WindowMap.isActive(this._window)) {
return;
}
cpmm.sendAsyncMessage('Keyboard:SwitchToNextInputMethod', {});
},
supportsSwitching: function() {
if (!WindowMap.isActive(this._window)) {
return false;
}
return this._supportsSwitching;
},
hide: function() {
if (!WindowMap.isActive(this._window)) {
return;
}
cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
}
};
@ -250,6 +298,7 @@ function MozInputMethod() { }
MozInputMethod.prototype = {
_inputcontext: null,
_layouts: {},
_window: null,
classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
@ -268,17 +317,26 @@ MozInputMethod.prototype = {
}),
init: function mozInputMethodInit(win) {
let principal = win.document.nodePrincipal;
let perm = Services.perms
.testExactPermissionFromPrincipal(principal, "keyboard");
if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
dump("No permission to use the keyboard API for " +
principal.origin + "\n");
return null;
// Check if we're in testing mode.
let isTesting = false;
try {
isTesting = Services.prefs.getBoolPref(TESTING_ENABLED_PREF);
} catch (e) {}
// Don't bypass the permission check if not in testing mode.
if (!isTesting) {
let principal = win.document.nodePrincipal;
let perm = Services.perms
.testExactPermissionFromPrincipal(principal, "keyboard");
if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
dump("No permission to use the keyboard API for " +
principal.origin + "\n");
return null;
}
}
this._window = win;
this._mgmt = new MozInputMethodManager();
this._mgmt = new MozInputMethodManager(win);
this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
@ -288,11 +346,6 @@ MozInputMethod.prototype = {
cpmm.addMessageListener('Keyboard:SelectionChange', this);
cpmm.addMessageListener('Keyboard:GetContext:Result:OK', this);
cpmm.addMessageListener('Keyboard:LayoutsChange', this);
// If there already is an active context, then this will trigger
// a GetContext:Result:OK event, and we can initialize ourselves.
// Otherwise silently ignored.
cpmm.sendAsyncMessage("Keyboard:GetContext", {});
},
uninit: function mozInputMethodUninit() {
@ -307,6 +360,10 @@ MozInputMethod.prototype = {
},
receiveMessage: function mozInputMethodReceiveMsg(msg) {
if (!WindowMap.isActive(this._window)) {
return;
}
let json = msg.json;
switch(msg.name) {
@ -338,11 +395,18 @@ MozInputMethod.prototype = {
},
get mgmt() {
if (!WindowMap.isActive(this._window)) {
return null;
}
return this._mgmt;
},
get inputcontext() {
return this._inputcontext;
if (!WindowMap.isActive(this._window)) {
return null;
}
return this._inputcontext;
},
set oninputcontextchange(handler) {
@ -372,6 +436,27 @@ MozInputMethod.prototype = {
let event = new this._window.Event("inputcontextchange",
ObjectWrapper.wrap({}, this._window));
this.__DOM_IMPL__.dispatchEvent(event);
},
setActive: function mozInputMethodSetActive(isActive) {
if (WindowMap.isActive(this._window) === isActive) {
return;
}
WindowMap.setActive(this._window, isActive);
if (isActive) {
// Activate current input method.
// If there is already an active context, then this will trigger
// a GetContext:Result:OK event, and we can initialize ourselves.
// Otherwise silently ignored.
cpmm.sendAsyncMessage("Keyboard:GetContext", {});
} else {
// Deactive current input method.
if (this._inputcontext) {
this.setInputContext(null);
}
}
}
};
@ -400,6 +485,7 @@ function MozInputContext(ctx) {
MozInputContext.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
_window: null,
_context: null,
_contextId: -1,
@ -452,6 +538,8 @@ MozInputContext.prototype = {
this._context[k] = null;
}
}
this._window = null;
},
receiveMessage: function ic_receiveMessage(msg) {
@ -558,8 +646,7 @@ MozInputContext.prototype = {
getText: function ic_getText(offset, length) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage('Keyboard:GetText', {
contextId: self._contextId,
requestId: resolverId,
@ -587,8 +674,7 @@ MozInputContext.prototype = {
setSelectionRange: function ic_setSelectionRange(start, length) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage("Keyboard:SetSelectionRange", {
contextId: self._contextId,
requestId: resolverId,
@ -616,8 +702,7 @@ MozInputContext.prototype = {
replaceSurroundingText: function ic_replaceSurrText(text, offset, length) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
contextId: self._contextId,
requestId: resolverId,
@ -634,8 +719,7 @@ MozInputContext.prototype = {
sendKey: function ic_sendKey(keyCode, charCode, modifiers) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage('Keyboard:SendKey', {
contextId: self._contextId,
requestId: resolverId,
@ -648,8 +732,7 @@ MozInputContext.prototype = {
setComposition: function ic_setComposition(text, cursor, clauses) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage('Keyboard:SetComposition', {
contextId: self._contextId,
requestId: resolverId,
@ -662,14 +745,26 @@ MozInputContext.prototype = {
endComposition: function ic_endComposition(text) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
return this._sendPromise(function(resolverId) {
cpmm.sendAsyncMessage('Keyboard:EndComposition', {
contextId: self._contextId,
requestId: resolverId,
text: text || ''
});
});
},
_sendPromise: function(callback) {
let self = this;
return this.createPromise(function(resolve, reject) {
let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
if (!WindowMap.isActive(self._window)) {
self.removePromiseResolver(resolverId);
reject('Input method is not active.');
return;
}
callback(resolverId);
});
}
};

View File

@ -1,4 +1,4 @@
{
"revision": "c062ff36671ed3b1249428ad098dc70615e5e612",
"revision": "e03c317f861e6c01078b358321ef8f576ca81377",
"repo_path": "/integration/gaia-central"
}

View File

@ -378,6 +378,8 @@
@BINPATH@/components/PeerConnection.js
@BINPATH@/components/PeerConnection.manifest
#endif
@BINPATH@/components/HttpDataUsage.manifest
@BINPATH@/components/HttpDataUsage.js
@BINPATH@/components/SiteSpecificUserAgent.js
@BINPATH@/components/SiteSpecificUserAgent.manifest
@BINPATH@/components/storage-Legacy.js

View File

@ -549,6 +549,7 @@ let gHistorySwipeAnimation = {
this.isLTR = document.documentElement.mozMatchesSelector(
":-moz-locale-dir(ltr)");
this._trackedSnapshots = [];
this._startingIndex = -1;
this._historyIndex = -1;
this._boxWidth = -1;
this._maxSnapshots = this._getMaxSnapshots();
@ -561,6 +562,7 @@ let gHistorySwipeAnimation = {
gBrowser.addEventListener("pagehide", this, false);
gBrowser.addEventListener("pageshow", this, false);
gBrowser.addEventListener("popstate", this, false);
gBrowser.addEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.addEventListener("TabClose", this, false);
}
},
@ -572,6 +574,7 @@ let gHistorySwipeAnimation = {
gBrowser.removeEventListener("pagehide", this, false);
gBrowser.removeEventListener("pageshow", this, false);
gBrowser.removeEventListener("popstate", this, false);
gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.removeEventListener("TabClose", this, false);
this.active = false;
@ -591,7 +594,8 @@ let gHistorySwipeAnimation = {
this._handleFastSwiping();
}
else {
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
this._historyIndex = this._startingIndex;
this._canGoBack = this.canGoBack();
this._canGoForward = this.canGoForward();
if (this.active) {
@ -622,20 +626,24 @@ let gHistorySwipeAnimation = {
if (!this.isAnimationRunning())
return;
// We use the following value to decrease the bounce effect when swiping
// back/forward past the browsing history. This value was determined
// experimentally.
let dampValue = 4;
if ((aVal >= 0 && this.isLTR) ||
(aVal <= 0 && !this.isLTR)) {
if (aVal > 1)
aVal = 1; // Cap value to avoid sliding the page further than allowed.
let tempDampValue = 1;
if (this._canGoBack)
this._prevBox.collapsed = false;
else
else {
tempDampValue = dampValue;
this._prevBox.collapsed = true;
}
// The current page is pushed to the right (LTR) or left (RTL),
// the intention is to go back.
// If there is a page to go back to, it should show in the background.
this._positionBox(this._curBox, aVal);
this._positionBox(this._curBox, aVal / tempDampValue);
// The forward page should be pushed offscreen all the way to the right.
this._positionBox(this._nextBox, 1);
@ -651,13 +659,14 @@ let gHistorySwipeAnimation = {
// For the backdrop to be visible in that case, the previous page needs
// to be hidden (if it exists).
if (this._canGoForward) {
this._nextBox.collapsed = false;
let offset = this.isLTR ? 1 : -1;
this._positionBox(this._curBox, 0);
this._positionBox(this._nextBox, offset + aVal); // aVal is negative
this._positionBox(this._nextBox, offset + aVal);
}
else {
this._prevBox.collapsed = true;
this._positionBox(this._curBox, aVal);
this._positionBox(this._curBox, aVal / dampValue);
}
}
},
@ -674,13 +683,14 @@ let gHistorySwipeAnimation = {
let browser = gBrowser.getBrowserForTab(aEvent.target);
this._removeTrackedSnapshot(-1, browser);
break;
case "DOMModalDialogClosed":
this.stopAnimation();
break;
case "pageshow":
case "popstate":
if (this.isAnimationRunning()) {
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
break;
this.stopAnimation();
}
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
break;
this.stopAnimation();
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
break;
case "pagehide":
@ -748,7 +758,7 @@ let gHistorySwipeAnimation = {
* any. This will also result in the animation overlay to be torn down.
*/
swipeEndEventReceived: function HSA_swipeEndEventReceived() {
if (this._lastSwipeDir != "")
if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
this._navigateToHistoryIndex();
else
this.stopAnimation();
@ -776,9 +786,10 @@ let gHistorySwipeAnimation = {
* |this|.
*/
_navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
if (this._doesIndexExistInHistory(this._historyIndex)) {
if (this._doesIndexExistInHistory(this._historyIndex))
gBrowser.webNavigation.gotoIndex(this._historyIndex);
}
else
this.stopAnimation();
},
/**
@ -1004,12 +1015,17 @@ let gHistorySwipeAnimation = {
return aBlob;
let img = new Image();
let url = URL.createObjectURL(aBlob);
img.onload = function() {
URL.revokeObjectURL(url);
};
img.src = url;
return img;
let url = "";
try {
url = URL.createObjectURL(aBlob);
img.onload = function() {
URL.revokeObjectURL(url);
};
}
finally {
img.src = url;
return img;
}
},
/**

View File

@ -912,7 +912,7 @@ var gBrowserInit = {
defaultHeight = screen.availHeight * .75;
}
#ifdef MOZ_WIDGET_GTK2
#if MOZ_WIDGET_GTK == 2
// On X, we're not currently able to account for the size of the window
// border. Use 28px as a guess (titlebar + bottom window border)
defaultHeight -= 28;

View File

@ -0,0 +1,7 @@
# 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/.
MOCHITEST_CHROME_FILES = \
test_aboutCrashed.xul \
$(NULL)

View File

@ -0,0 +1,6 @@
# -*- 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/.

View File

@ -0,0 +1,86 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<iframe type="content" id="frame1"/>
<iframe type="content" id="frame2" onload="doTest()"/>
<script type="application/javascript"><![CDATA[
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
SimpleTest.waitForExplicitFinish();
// Load error pages do not fire "load" events, so let's use a progressListener.
function waitForErrorPage(frame) {
let errorPageDeferred = Promise.defer();
let progressListener = {
onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress)
.removeProgressListener(progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION);
errorPageDeferred.resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress)
.addProgressListener(progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION);
return errorPageDeferred.promise;
}
function doTest() {
Task.spawn(function test_aboutCrashed() {
let frame1 = document.getElementById("frame1");
let frame2 = document.getElementById("frame2");
let uri1 = Services.io.newURI("http://www.example.com/1", null, null);
let uri2 = Services.io.newURI("http://www.example.com/2", null, null);
let errorPageReady = waitForErrorPage(frame1);
frame1.docShell.chromeEventHandler.setAttribute("crashedPageTitle", "pageTitle");
frame1.docShell.displayLoadError(Components.results.NS_ERROR_CONTENT_CRASHED, uri1, null);
yield errorPageReady;
frame1.docShell.chromeEventHandler.removeAttribute("crashedPageTitle");
SimpleTest.is(frame1.contentDocument.documentURI,
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/1&c=UTF-8&d=pageTitle",
"Correct about:tabcrashed displayed for page with title.");
errorPageReady = waitForErrorPage(frame2);
frame2.docShell.displayLoadError(Components.results.NS_ERROR_CONTENT_CRASHED, uri2, null);
yield errorPageReady;
SimpleTest.is(frame2.contentDocument.documentURI,
"about:tabcrashed?e=tabcrashed&u=http%3A//www.example.com/2&c=UTF-8&d=%20",
"Correct about:tabcrashed displayed for page with no title.");
SimpleTest.finish();
});
}
]]></script>
<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;" />
</window>

View File

@ -4,5 +4,5 @@
# 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 += ['general', 'newtab', 'social']
DIRS += ['chrome', 'general', 'newtab', 'social']

View File

@ -66,7 +66,7 @@ const PREF_AUDIO_FEED_SELECTED_READER = "browser.audioFeeds.handler.default";
const kActionUsePlugin = 5;
/*
#ifdef MOZ_WIDGET_GTK2
#if MOZ_WIDGET_GTK == 2
*/
const ICON_URL_APP = "moz-icon://dummy.exe?size=16";
/*

View File

@ -36,6 +36,9 @@ const DEFAULT_EDITOR_CONFIG = {
showOverviewRuler: true
};
//For telemetry
Cu.import("resource://gre/modules/Services.jsm")
/**
* Object defining the debugger view components.
*/
@ -276,6 +279,13 @@ let DebuggerView = {
if (this._editorSource.url == aSource.url && !aFlags.force) {
return this._editorSource.promise;
}
let transportType = DebuggerController.client.localTransport
? "_LOCAL"
: "_REMOTE";
//Telemetry probe
let histogramId = "DEVTOOLS_DEBUGGER_DISPLAY_SOURCE" + transportType + "_MS";
let histogram = Services.telemetry.getHistogramById(histogramId);
let startTime = +new Date();
let deferred = promise.defer();
@ -296,6 +306,8 @@ let DebuggerView = {
DebuggerView.Sources.selectedValue = aSource.url;
DebuggerController.Breakpoints.updateEditorBreakpoints();
histogram.add(+new Date() - startTime);
// Resolve and notify that a source file was shown.
window.emit(EVENTS.SOURCE_SHOWN, aSource);
deferred.resolve([aSource, aText]);

View File

@ -40,9 +40,6 @@ function OptionsPanel(iframeWindow, toolbox) {
this.toolbox = toolbox;
this.isReady = false;
// Make restart method available from xul
this.panelWin.restart = this.restart;
EventEmitter.decorate(this);
};
@ -57,7 +54,6 @@ OptionsPanel.prototype = {
this.setupToolsList();
this.populatePreferences();
this.prepareRestartPreferences();
this._disableJSClicked = this._disableJSClicked.bind(this);
@ -201,34 +197,6 @@ OptionsPanel.prototype = {
}
},
/**
* Handles checkbox click inside hbox with class "hidden-labels-box". The
* labels inside the hbox are shown again when the user click on the checkbox
* in the box.
*/
prepareRestartPreferences: function() {
let checkboxes = this.panelDoc.querySelectorAll(".hidden-labels-box > checkbox");
for (let checkbox of checkboxes) {
checkbox.addEventListener("command", function(target) {
target.parentNode.classList.toggle("visible");
}.bind(null, checkbox));
}
},
restart: function() {
let canceled = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
if (canceled.data) {
return;
}
// restart
Cc['@mozilla.org/toolkit/app-startup;1']
.getService(Ci.nsIAppStartup)
.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
},
/**
* Disables JavaScript for the currently loaded tab. We force a page refresh
* here because setting docShell.allowJavascript to true fails to block JS

View File

@ -74,21 +74,11 @@
<checkbox label="&options.enableChrome.label3;"
tooltiptext="&options.enableChrome.tooltip;"
data-pref="devtools.chrome.enabled"/>
<label class="options-citation-label"
value="&options.context.requiresRestart2;"/>
<label class="text-link"
onclick="restart()"
value="&options.restartButton.label;"/>
</hbox>
<hbox class="hidden-labels-box">
<checkbox label="&options.enableRemote.label3;"
tooltiptext="&options.enableRemote.tooltip;"
data-pref="devtools.debugger.remote-enabled"/>
<label class="options-citation-label"
value="&options.context.requiresRestart2;"/>
<label class="text-link"
onclick="restart()"
value="&options.restartButton.label;"/>
</hbox>
</vbox>
</vbox>

View File

@ -729,6 +729,15 @@ InspectorPanel.prototype = {
}
},
/**
* Trigger a high-priority layout change for things that need to be
* updated immediately
*/
immediateLayoutChange: function Inspector_immediateLayoutChange()
{
this.emit("layout-change");
},
/**
* Schedule a low-priority change event for things like paint
* and resize.

View File

@ -17,7 +17,7 @@ function test() {
}
function getInspectorProp(aName)
function getInspectorComputedProp(aName)
{
let computedview = inspector.sidebar.getWindowForTab("computedview").computedview.view;
for each (let view in computedview.propertyViews) {
@ -27,6 +27,18 @@ function test() {
}
return null;
}
function getInspectorRuleProp(aName)
{
let ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
let inlineStyles = ruleview._elementStyle.rules[0];
for each (let prop in inlineStyles.textProps) {
if (prop.name == aName) {
return prop;
}
}
return null;
}
function runInspectorTests(aInspector)
{
@ -40,50 +52,93 @@ function test() {
testDiv.style.fontSize = "10px";
// Start up the style inspector panel...
inspector.once("computed-view-refreshed", stylePanelTests);
inspector.once("computed-view-refreshed", computedStylePanelTests);
inspector.selection.setNode(testDiv);
});
}
function stylePanelTests()
function computedStylePanelTests()
{
let computedview = inspector.sidebar.getWindowForTab("computedview").computedview;
ok(computedview, "Style Panel has a cssHtmlTree");
let propView = getInspectorProp("font-size");
let propView = getInspectorComputedProp("font-size");
is(propView.value, "10px", "Style inspector should be showing the correct font size.");
inspector.once("computed-view-refreshed", stylePanelAfterChange);
inspector.once("computed-view-refreshed", computedStylePanelAfterChange);
testDiv.style.fontSize = "15px";
inspector.emit("layout-change");
testDiv.style.cssText = "font-size: 15px; color: red;";
}
function stylePanelAfterChange()
function computedStylePanelAfterChange()
{
let propView = getInspectorProp("font-size");
let propView = getInspectorComputedProp("font-size");
is(propView.value, "15px", "Style inspector should be showing the new font size.");
stylePanelNotActive();
let propView = getInspectorComputedProp("color");
is(propView.value, "#F00", "Style inspector should be showing the new color.");
computedStylePanelNotActive();
}
function stylePanelNotActive()
function computedStylePanelNotActive()
{
// Tests changes made while the style panel is not active.
inspector.sidebar.select("ruleview");
executeSoon(function() {
inspector.once("computed-view-refreshed", stylePanelAfterSwitch);
testDiv.style.fontSize = "20px";
inspector.sidebar.select("computedview");
});
testDiv.style.fontSize = "20px";
testDiv.style.color = "blue";
testDiv.style.textAlign = "center";
inspector.once("computed-view-refreshed", computedStylePanelAfterSwitch);
inspector.sidebar.select("computedview");
}
function stylePanelAfterSwitch()
function computedStylePanelAfterSwitch()
{
let propView = getInspectorProp("font-size");
is(propView.value, "20px", "Style inspector should be showing the newest font size.");
let propView = getInspectorComputedProp("font-size");
is(propView.value, "20px", "Style inspector should be showing the new font size.");
let propView = getInspectorComputedProp("color");
is(propView.value, "#00F", "Style inspector should be showing the new color.");
let propView = getInspectorComputedProp("text-align");
is(propView.value, "center", "Style inspector should be showing the new text align.");
rulePanelTests();
}
function rulePanelTests()
{
inspector.sidebar.select("ruleview");
let ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview;
ok(ruleview, "Style Panel has a ruleview");
let propView = getInspectorRuleProp("text-align");
is(propView.value, "center", "Style inspector should be showing the new text align.");
testDiv.style.textAlign = "right";
testDiv.style.color = "lightgoldenrodyellow";
testDiv.style.fontSize = "3em";
testDiv.style.textTransform = "uppercase";
inspector.once("rule-view-refreshed", rulePanelAfterChange);
}
function rulePanelAfterChange()
{
let propView = getInspectorRuleProp("text-align");
is(propView.value, "right", "Style inspector should be showing the new text align.");
let propView = getInspectorRuleProp("color");
is(propView.value, "#FAFAD2", "Style inspector should be showing the new color.")
let propView = getInspectorRuleProp("font-size");
is(propView.value, "3em", "Style inspector should be showing the new font size.");
let propView = getInspectorRuleProp("text-transform");
is(propView.value, "uppercase", "Style inspector should be showing the new text transform.");
finishTest();
}

View File

@ -397,6 +397,7 @@ MarkupView.prototype = {
*/
_mutationObserver: function MT__mutationObserver(aMutations)
{
let requiresLayoutChange = false;
for (let mutation of aMutations) {
let type = mutation.type;
let target = mutation.target;
@ -419,6 +420,11 @@ MarkupView.prototype = {
}
if (type === "attributes" || type === "characterData") {
container.update(false);
// Auto refresh style properties on selected node when they change.
if (type === "attributes" && container.selected) {
requiresLayoutChange = true;
}
} else if (type === "childList") {
container.childrenDirty = true;
// Update the children to take care of changes in the DOM
@ -427,6 +433,10 @@ MarkupView.prototype = {
this._updateChildren(container, {flash: true});
}
}
if (requiresLayoutChange) {
this._inspector.immediateLayoutChange();
}
this._waitForChildren().then(() => {
this._flashMutatedNodes(aMutations);
this._inspector.emit("markupmutation");

View File

@ -1283,13 +1283,13 @@ CssRuleView.prototype = {
{
// Ignore refreshes during editing or when no element is selected.
if (this.isEditing || !this._elementStyle) {
return promise.resolve(null);
return;
}
this._clearRules();
// Repopulate the element style.
return this._populate();
this._populate();
},
_populate: function() {

View File

@ -7,6 +7,7 @@ let doc;
let inspector;
let ruleView;
let testElement;
let rule;
function startTest(aInspector, aRuleView)
{
@ -43,25 +44,29 @@ function testRuleChanges()
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
// Change the id and refresh.
inspector.once("rule-view-refreshed", testRuleChange1);
testElement.setAttribute("id", "differentid");
promiseDone(ruleView.nodeChanged().then(() => {
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 2, "Two rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
}
testElement.setAttribute("id", "testid");
return ruleView.nodeChanged();
}).then(() => {
// Put the id back.
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 3, "Three rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
function testRuleChange1()
{
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 2, "Two rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf(".testclass"), 0, "Second item is class rule.");
testPropertyChanges();
}));
inspector.once("rule-view-refreshed", testRuleChange2);
testElement.setAttribute("id", "testid");
}
function testRuleChange2()
{
let selectors = ruleView.doc.querySelectorAll(".ruleview-selector");
is(selectors.length, 3, "Three rules visible.");
is(selectors[0].textContent.indexOf("element"), 0, "First item is inline style.");
is(selectors[1].textContent.indexOf("#testid"), 0, "Second item is id rule.");
is(selectors[2].textContent.indexOf(".testclass"), 0, "Third item is class rule.");
testPropertyChanges();
}
function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
@ -77,65 +82,86 @@ function validateTextProp(aProp, aEnabled, aName, aValue, aDesc)
function testPropertyChanges()
{
// Add a second margin-top value, just to make things interesting.
let rule = ruleView._elementStyle.rules[0];
rule = ruleView._elementStyle.rules[0];
let ruleEditor = ruleView._elementStyle.rules[0].editor;
inspector.once("rule-view-refreshed", testPropertyChange0);
// Add a second margin-top value, just to make things interesting.
ruleEditor.addProperty("margin-top", "5px", "");
promiseDone(expectRuleChange(rule).then(() => {
// Set the element style back to a 1px margin-top.
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
}
// Now set it back to 5px, the 5px value should be re-enabled.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
return ruleView.nodeChanged();
function testPropertyChange0()
{
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "Original margin property active");
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
inspector.once("rule-view-refreshed", testPropertyChange1);
testElement.setAttribute("style", "margin-top: 1px; padding-top: 5px");
}
function testPropertyChange1()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], true, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], false, "margin-top", "5px", "Second margin property disabled");
// Set the margin property to a value that doesn't exist in the editor.
// Should reuse the currently-enabled element (the second one.)
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
inspector.once("rule-view-refreshed", testPropertyChange2);
// Remove the padding-top attribute. Should disable the padding property but not remove it.
testElement.setAttribute("style", "margin-top: 5px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
// Now set it back to 5px, the 5px value should be re-enabled.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 5px;");
}
function testPropertyChange2()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "5px", "Second margin property disabled");
// Put the padding-top attribute back in, should re-enable the padding property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
inspector.once("rule-view-refreshed", testPropertyChange3);
// Add an entirely new property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
return ruleView.nodeChanged();
}).then(() => {
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
// Set the margin property to a value that doesn't exist in the editor.
// Should reuse the currently-enabled element (the second one.)
testElement.setAttribute("style", "margin-top: 15px; padding-top: 5px;");
}
function testPropertyChange3()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[0], false, "margin-top", "1px", "First margin property re-enabled");
validateTextProp(rule.textProps[2], true, "margin-top", "15px", "Second margin property disabled");
finishTest();
}));
inspector.once("rule-view-refreshed", testPropertyChange4);
// Remove the padding-top attribute. Should disable the padding property but not remove it.
testElement.setAttribute("style", "margin-top: 5px;");
}
function testPropertyChange4()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], false, "padding-top", "5px", "Padding property disabled");
inspector.once("rule-view-refreshed", testPropertyChange5);
// Put the padding-top attribute back in, should re-enable the padding property.
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px");
}
function testPropertyChange5()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 3, "Correct number of properties");
validateTextProp(rule.textProps[1], true, "padding-top", "25px", "Padding property enabled");
inspector.once("rule-view-refreshed", testPropertyChange6);
// Add an entirely new property
testElement.setAttribute("style", "margin-top: 5px; padding-top: 25px; padding-left: 20px;");
}
function testPropertyChange6()
{
is(rule.editor.element.querySelectorAll(".ruleview-property").length, 4, "Added a property");
validateTextProp(rule.textProps[3], true, "padding-left", "20px", "Padding property enabled");
finishTest();
}
function finishTest()
{
inspector = ruleView = null;
inspector = ruleView = rule = null;
doc = null;
gBrowser.removeCurrentTab();
finish();

View File

@ -224,12 +224,10 @@ function testGen() {
eventHandlers.push(variablesViewShown);
// Send the mousedown, mouseup and click events to check if the variables
// view opens.
EventUtils.sendMouseEvent({ type: "mousedown" }, messageBody, window);
EventUtils.sendMouseEvent({ type: "click" }, messageBody, window);
EventUtils.synthesizeMouse(messageBody, 2, 2, {}, HUD.iframeWindow);
if (showsVariablesView) {
info("messageBody tagName '" + messageBody.tagName + "' className '" + messageBody.className + "'");
yield undefined; // wait for the panel to open if we need to.
}

View File

@ -544,6 +544,9 @@
@BINPATH@/components/PeerConnection.manifest
#endif
@BINPATH@/components/HttpDataUsage.manifest
@BINPATH@/components/HttpDataUsage.js
@BINPATH@/chrome/marionette@JAREXT@
@BINPATH@/chrome/marionette.manifest
@BINPATH@/components/MarionetteComponents.manifest

View File

@ -76,7 +76,7 @@
<!ENTITY help.title "App Manager">
<!ENTITY help.close "Close">
<!ENTITY help.intro "This tool will help you build and install web apps on compatible devices (i.e Firefox OS). The <strong>Apps</strong> tab will assist you in the validation and installation process of your app. The <strong>Device</strong> tab will give you information about the connected device. Use the bottom toolbar to connect to a device or start the simulator.">
<!ENTITY help.intro "This tool will help you build and install web apps on compatible devices (i.e. Firefox OS). The <strong>Apps</strong> tab will assist you in the validation and installation process of your app. The <strong>Device</strong> tab will give you information about the connected device. Use the bottom toolbar to connect to a device or start the simulator.">
<!ENTITY help.usefullLinks "Useful links:">
<!ENTITY help.appMgrDoc "Documentation: Using the App Manager">
<!ENTITY help.configuringDevice "How to setup your Firefox OS device">

View File

@ -52,15 +52,6 @@
- -->
<!ENTITY options.defaultColorUnit.name "Color Names">
<!-- LOCALIZATION NOTE (options.context.requiresRestart2): This is the requires
- restart label at right of settings that require a browser restart to be
- effective. -->
<!ENTITY options.context.requiresRestart2 "Requires browser restart">
<!-- LOCALIZATION NOTE (options.restartButton.label): This is the label for the
- restart button next to options.context.requiresRestart2 label. -->
<!ENTITY options.restartButton.label "Restart now">
<!-- LOCALIZATION NOTE (options.context.triggersPageRefresh2): This is the
- triggers page refresh label next to the settings in the advanced settings
- group in the options panel which trigger page reload. -->

View File

@ -92,19 +92,18 @@ function removeMockSearchDefault(aTimeoutMs) {
function test() {
waitForExplicitFinish();
Task.spawn(function(){
yield addTab("about:blank");
}).then(runTests);
runTests();
}
function setUp() {
if (!gEdit)
gEdit = document.getElementById("urlbar-edit");
yield addTab("about:blank");
yield showNavBar();
}
function tearDown() {
yield removeMockSearchDefault();
Browser.closeTab(Browser.selectedTab, { forceClose: true });
}
@ -272,15 +271,17 @@ gTests.push({
let searchSubmission = gEngine.getSubmission(search, null);
let trimmedSubmission = gEdit.trimValue(searchSubmission.uri.spec);
is(gEdit.value, trimmedSubmission, "tap search option: search conducted");
yield removeMockSearchDefault();
}
});
gTests.push({
desc: "bug 897131 - url bar update after content tap + edge swipe",
setUp: setUp,
tearDown: tearDown,
run: function testUrlbarTyping() {
let tab = yield addTab("about:mozilla");
yield showNavBar();
sendElementTap(window, gEdit);
ok(gEdit.isEditing, "focus urlbar: in editing mode");
@ -305,3 +306,62 @@ gTests.push({
}
});
gTests.push({
desc: "Bug 916383 - Invisible autocomplete items selectable by keyboard when 'your results' not shown",
tearDown: tearDown,
run: function testBug916383() {
yield addTab("about:start");
yield showNavBar();
sendElementTap(window, gEdit);
let bookmarkItem = Browser.selectedBrowser.contentWindow.BookmarksStartView._grid.querySelector("richgriditem");
// Get the first bookmark item label to make sure it will show up in 'your results'
let label = bookmarkItem.getAttribute("label");
EventUtils.sendString(label, window);
let opened = yield waitForCondition(() => gEdit.popup.popupOpen);
yield waitForCondition(() => gEdit.popup._results.itemCount > 0);
ok(!gEdit.popup._resultsContainer.hidden, "'Your results' are visible");
ok(gEdit.popup._results.itemCount > 0, "'Your results' are populated");
// Append a string to make sure it doesn't match anything in 'your results'
EventUtils.sendString("zzzzzzzzzzzzzzzzzz", window);
yield waitForCondition(() => gEdit.popup._resultsContainer.hidden);
ok(gEdit.popup._resultsContainer.hidden, "'Your results' are hidden");
ok(gEdit.popup._results.itemCount === 0, "'Your results' are empty");
EventUtils.synthesizeKey("VK_DOWN", {}, window);
is(gEdit.popup._searches.selectedIndex, 0, "key select search: first search selected");
}
});
gTests.push({
desc: "Bug 891667 - Use up arrow too",
tearDown: tearDown,
run: function testBug891667() {
yield addTab("about:start");
yield showNavBar();
sendElementTap(window, gEdit);
let bookmarkItem = Browser.selectedBrowser.contentWindow.BookmarksStartView._grid.querySelector("richgriditem");
// Get the first bookmark item label to make sure it will show up in 'your results'
let label = bookmarkItem.getAttribute("label");
EventUtils.sendString(label, window);
yield waitForCondition(() => gEdit.popup.popupOpen);
yield waitForCondition(() => gEdit.popup._results.itemCount > 0);
ok(gEdit.popup._results.itemCount > 0, "'Your results' populated");
EventUtils.synthesizeKey("VK_UP", {}, window);
is(gEdit.popup._results.selectedIndex, 0, "Pressing arrow up selects first item.");
}
});

View File

@ -396,11 +396,12 @@ function waitForCondition(aCondition, aTimeoutMs, aIntervalMs) {
let timeoutMs = aTimeoutMs || kDefaultWait;
let intervalMs = aIntervalMs || kDefaultInterval;
let startTime = Date.now();
let stack = new Error().stack;
function testCondition() {
let now = Date.now();
if((now - startTime) > timeoutMs) {
deferred.reject( new Error("Timed out waiting for condition to be true") );
deferred.reject( new Error("Timed out waiting for condition to be true at " + stack) );
return;
}

View File

@ -64,6 +64,9 @@ this.TabCrashReporter = {
},
onAboutTabCrashedLoad: function (aBrowser) {
if (!this.childMap)
return;
let dumpID = this.childMap.get(this.browserMap.get(aBrowser));
if (!dumpID)
return;

View File

@ -87,7 +87,7 @@ browser.jar:
skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
* skin/classic/browser/downloads/indicator.css (downloads/indicator.css)
skin/classic/browser/downloads/indicator.css (downloads/indicator.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png)

View File

@ -294,7 +294,7 @@ case "$target" in
# The build tools got moved around to different directories in
# SDK Tools r22. Try to locate them.
android_build_tools=""
for suffix in android-4.3 18.0.1 18.0.0 17.0.0 android-4.2.2; do
for suffix in android-4.3 18.1.0 18.0.1 18.0.0 17.0.0 android-4.2.2; do
tools_directory="$android_sdk/../../build-tools/$suffix"
if test -d "$tools_directory" ; then
android_build_tools="$tools_directory"

View File

@ -8,6 +8,28 @@ import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2,
import re
from urlparse import urlparse
try:
import mozinfo
except ImportError:
# Stub out fake mozinfo since this is not importable on Android 4.0 Opt.
# This should be fixed; see
# https://bugzilla.mozilla.org/show_bug.cgi?id=650881
mozinfo = type('mozinfo', (), dict(info={}))()
mozinfo.isWin = mozinfo.isLinux = mozinfo.isUnix = mozinfo.isMac = False
# TODO! FILE: localautomation :/
# mapping from would-be mozinfo attr <-> sys.platform
mapping = {'isMac': ['mac', 'darwin'],
'isLinux': ['linux', 'linux2'],
'isWin': ['win32', 'win64'],
}
mapping = dict(sum([[(value, key) for value in values] for key, values in mapping.items()], []))
attr = mapping.get(sys.platform)
if attr:
setattr(mozinfo, attr, True)
if mozinfo.isLinux:
mozinfo.isUnix = True
__all__ = [
"ZipFileReader",
"addCommonOptions",
@ -18,6 +40,10 @@ __all__ = [
"DEBUGGER_INFO",
"replaceBackSlashes",
"wrapCommand",
'KeyValueParseError',
'parseKeyValue',
'systemMemory',
'environment'
]
# Map of debugging programs to information about them, like default arguments
@ -345,3 +371,105 @@ def wrapCommand(cmd):
return ["arch", "-arch", "i386"] + cmd
# otherwise just execute the command normally
return cmd
class KeyValueParseError(Exception):
"""error when parsing strings of serialized key-values"""
def __init__(self, msg, errors=()):
self.errors = errors
Exception.__init__(self, msg)
def parseKeyValue(strings, separator='=', context='key, value: '):
"""
parse string-serialized key-value pairs in the form of
`key = value`. Returns a list of 2-tuples.
Note that whitespace is not stripped.
"""
# syntax check
missing = [string for string in strings if separator not in string]
if missing:
raise KeyValueParseError("Error: syntax error in %s" % (context,
','.join(missing)),
errors=missing)
return [string.split(separator, 1) for string in strings]
def systemMemory():
"""
Returns total system memory in kilobytes.
Works only on unix-like platforms where `free` is in the path.
"""
return int(os.popen("free").readlines()[1].split()[1])
def environment(xrePath, env=None, crashreporter=True):
"""populate OS environment variables for mochitest"""
env = os.environ.copy() if env is None else env
assert os.path.isabs(xrePath)
ldLibraryPath = xrePath
envVar = None
if mozinfo.isUnix:
envVar = "LD_LIBRARY_PATH"
env['MOZILLA_FIVE_HOME'] = xrePath
elif mozinfo.isMac:
envVar = "DYLD_LIBRARY_PATH"
elif mozinfo.isWin:
envVar = "PATH"
if envVar:
envValue = ((env.get(envVar), str(ldLibraryPath))
if mozinfo.isWin
else (ldLibraryPath, env.get(envVar)))
env[envVar] = os.path.pathsep.join([path for path in envValue if path])
# crashreporter
env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
if crashreporter:
env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
env['MOZ_CRASHREPORTER'] = '1'
else:
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
# Additional temporary logging while we try to debug some intermittent
# WebRTC conditions. This is necessary to troubleshoot bugs 841496,
# 841150, and 839677 (at least)
# Also (temporary) bug 870002 (mediastreamgraph)
env.setdefault('NSPR_LOG_MODULES', 'signaling:5,mtransport:3')
env['R_LOG_LEVEL'] = '5'
env['R_LOG_DESTINATION'] = 'stderr'
env['R_LOG_VERBOSE'] = '1'
# ASan specific environment stuff
asan = bool(mozinfo.info.get("asan"))
if asan and (mozinfo.isLinux or mozinfo.isMac):
try:
totalMemory = systemMemory()
# Only 2 GB RAM or less available? Use custom ASan options to reduce
# the amount of resources required to do the tests. Standard options
# will otherwise lead to OOM conditions on the current test slaves.
#
# If we have more than 2 GB or RAM but still less than 4 GB, we need
# another set of options to prevent OOM in some memory-intensive
# tests.
message = "INFO | runtests.py | ASan running in %s configuration"
if totalMemory <= 1024 * 1024 * 2:
message = message % 'low-memory'
env["ASAN_OPTIONS"] = "quarantine_size=50331648:redzone=64"
elif totalMemory <= 1024 * 1024 * 4:
message = message % 'mid-memory'
env["ASAN_OPTIONS"] = "quarantine_size=100663296:redzone=64"
else:
message = message % 'default memory'
except OSError,err:
log.info("Failed determine available memory, disabling ASan low-memory configuration: %s", err.strerror)
except:
log.info("Failed determine available memory, disabling ASan low-memory configuration")
else:
log.info(message)
return env

View File

@ -0,0 +1,108 @@
.. _build_overview:
=====================
Build System Overview
=====================
This document provides an overview on how the build system works. It is
targeted at people wanting to learn about internals of the build system.
It is not meant for persons who casually interact with the build system.
That being said, knowledge empowers, so consider reading on.
The build system is composed of many different components working in
harmony to build the source tree. We begin with a graphic overview.
.. graphviz::
digraph build_components {
rankdir="LR";
"configure" -> "config.status" -> "build backend" -> "build output"
}
Phase 1: Configuration
======================
Phase 1 centers around the configure script, which is a bash shell script.
The file is generated from a file called configure.in which is written in M4
and processed using Autoconf 2.13 to create the final configure script.
You don't have to worry about how you obtain a configure file: the build system
does this for you.
The primary job of configure is to determine characteristics of the system and
compiler, apply options passed into it, and validate everything looks OK to
build. The primary output of the configure script is an executable file in the
object directory called config.status. configure also produces some additional
files (like autoconf.mk). However, the most important file in terms of
architecture is config.status.
The existence of a config.status file may be familiar to those who have worked
with Autoconf before. However, Mozilla's config.status is different from almost
any other config.status you've ever seen: it's written in Python! Instead of
having our configure script produce a shell script, we have it generating Python.
Now is as good a time as any to mention that Python is prevalent in our build
system. If we need to write code for the build system, we do it in Python.
That's just how we roll.
config.status contains 2 parts: data structures representing the output of
configure and a command-line interface for preparing/configuring/generating
an appropriate build backend. (A build backend is merely a tool used to build
the tree - like GNU Make or Tup). These data structures essentially describe
the current state of the system and what the existing build configuration looks
like. For example, it defines which compiler to use, how to invoke it, which
application features are enabled, etc. You are encouraged to open up
config.status to have a look for yourself!
Once we have emitted a config.status file, we pass into the realm of phase 2.
Phase 2: Build Backend Preparation and the Build Definition
===========================================================
Once configure has determined what the current build configuration is, we need
to apply this to the source tree so we can actually build.
What essentially happens is the automatically-produced config.status Python
script is executed as soon as configure has generated it. config.status is charged
with the task of tell a tool had to build the tree. To do this, config.status
must first scan the build system definition.
The build system definition consists of various moz.build files in the tree.
There is roughly one moz.build file per directory or pet set of related directories.
Each moz.build files defines how its part of the build config works. For example it
says I want these C++ files compiled or look for additional information in these
directories. config.status starts with the main moz.build file and then recurses
into all referenced files and directories. As the moz.build files are read, data
structures describing the overall build system definition are emitted. These data
structures are then read by a build backend generator which then converts them
into files, function calls, etc. In the case of a `make` backend, the generator
writes out Makefiles.
When config.status runs, you'll see the following output::
Reticulating splines...
Finished reading 1096 moz.build files into 1276 descriptors in 2.40s
Backend executed in 2.39s
2188 total backend files. 0 created; 1 updated; 2187 unchanged
Total wall time: 5.03s; CPU time: 3.79s; Efficiency: 75%
What this is saying is that a total of 1096 moz.build files were read. Altogether,
1276 data structures describing the build configuration were derived from them.
It took 2.40s wall time to just read these files and produce the data structures.
The 1276 data structures were fed into the build backend which then determined it
had to manage 2188 files derived from those data structures. Most of them
already existed and didn't need changed. However, 1 was updated as a result of
the new configuration. The whole process took 5.03s. Although, only 3.79s was in
CPU time. That likely means we spent roughly 25% of the time waiting on I/O.
Phase 3: Invokation of the Build Backend
========================================
When most people think of the build system, they think of phase 3. This is
where we take all the code in the tree and produce Firefox or whatever
application you are creating. Phase 3 effectively takes whatever was
generated by phase 2 and runs it. Since the dawn of Mozilla, this has been
make consuming Makefiles. However, with the transition to moz.build files,
you may soon see non-Make build backends, such as Tup or Visual Studio.
When building the tree, most of the time is spent in phase 3. This is when
header files are installed, C++ files are compiled, files are preprocessed, etc.

View File

@ -17,6 +17,7 @@ import mdn_theme
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.graphviz',
]
templates_path = ['_templates']

View File

@ -0,0 +1,44 @@
.. _environment_variables:
================================================
Environment Variables Impacting the Build System
================================================
Various environment variables have an impact on the behavior of the
build system. This document attempts to document them.
AUTOCLOBBER
If defines, the build system will automatically clobber as needed.
The default behavior is to print a message and error out when a
clobber is needed.
This variable is typically defined in a :ref:`mozconfig <mozconfig>`
file via ``mk_add_options``.
REBUILD_CHECK
If defined, the build system will print information about why
certain files were rebuilt.
This feature is disabled by default because it makes the build slower.
MACH_NO_TERMINAL_FOOTER
If defined, the terminal footer displayed when building with mach in
a TTY is disabled.
MACH_NO_WRITE_TIMES
If defined, mach commands will not prefix output lines with the
elapsed time since program start. This option is equivalent to
passing ``--log-no-times`` to mach.
MOZ_PSEUDO_DERECURSE
Activate an *experimental* build mode where make directory traversal
is derecursified. This mode should result in faster build times at
the expense of busted builds from time-to-time. The end goal is for
this build mode to be the default. At which time, this variable will
likely go away.
A value of ``1`` activates the mode with full optimizations.
A value of ``no-parallel-export`` activates the mode without
optimizations to the *export* tier, which are known to be slightly
buggy.

View File

@ -26,3 +26,19 @@ Glossary
generated build config and writes out files used to build the
tree. Traditionally, config.status writes out a bunch of
Makefiles.
install manifest
A file containing metadata describing file installation rules.
A large part of the build system consists of copying files
around to appropriate places. We write out special files
describing the set of required operations so we can process the
actions effeciently. These files are install manifests.
clobber build
A build performed with an initially empty object directory. All
build actions must be performed.
incremental build
A build performed with the result of a previous build in an
object directory. The build should not have to work as hard because
it will be able to reuse the work from previous builds.

View File

@ -15,8 +15,11 @@ Important Concepts
.. toctree::
:maxdepth: 1
build-overview
Mozconfig Files <mozconfigs>
Profile Guided Optimization <pgo>
slow
environment-variables
mozbuild
========

View File

@ -1,3 +1,5 @@
.. _mozconfig:
===============
mozconfig Files
===============

156
build/docs/slow.rst Normal file
View File

@ -0,0 +1,156 @@
.. _slow:
============================
Why the Build System is Slow
============================
A common complaint about the build system is that it's slow. There are
many reasons contributing to its slowness. We will attempt to document
them here.
First, it is important to distinguish between a :term:`clobber build`
and an :term:`incremental build`. The reasons for why each are slow can
be different.
The build does a lot of work
============================
It may not be obvious, but the main reason the build system is slow is
because it does a lot of work! The source tree consists of a few
thousand C++ files. On a modern machine, we spend over 120 minutes of CPU
core time compiling files! So, if you are looking for the root cause of
slow clobber builds, look at the sheer volume of C++ files in the tree.
You don't have enough CPU cores and MHz
=======================================
The build should be CPU bound. If the build system maintainers are
optimizing the build system perfectly, every CPU core in your machine
should be 100% saturated during a build. While this isn't currently the
case (keep reading below), generally speaking, the more CPU cores you
have in your machine and the more total MHz in your machine, the better.
**We highly recommend building with no fewer than 4 physical CPU
cores.** Please note the *physical* in this sentence. Hyperthreaded
cores (an Intel Core i7 will report 8 CPU cores but only 4 are physical
for example) only yield at most a 1.25x speedup per core.
We also recommend using the most modern CPU model possible. Haswell
chips deliver much more performance per CPU cycle than say Sandy Bridge
CPUs.
This cause impacts both clobber and incremental builds.
You are building with a slow I/O layer
======================================
The build system can be I/O bound if your I/O layer is slow. Linking
libxul on some platforms and build architectures can perform gigabytes
of I/O.
To minimize the impact of slow I/O on build performance, **we highly
recommend building with an SSD.** Power users with enough memory may opt
to build from a RAM disk. Mechanical disks should be avoided if at all
possible.
This cause impacts both clobber and incremental builds.
You don't have enough memory
============================
The build system allocates a lot of memory, especially when building
many things in parallel. If you don't have enough free system memory,
the build will cause swap activity, slowing down your system and the
build. Even if you never get to the point of swapping, the build system
performs a lot of I/O and having all accessed files in memory and the
page cache can significantly reduce the influence of the I/O layer on
the build system.
**We recommend building with no less than 8 GB of system memory.** As
always, the more memory you have, the better. For a bare bones machine
doing nothing more than building the source tree, anything more than 16
GB is likely entering the point of diminishing returns.
This cause impacts both clobber and incremental builds.
You are building with pymake
============================
Pymake is slower than GNU make. One reason is Python is generally slower
than C. The build system maintainers are consistently looking at
optimizing pymake. However, it is death by a thousand cuts.
This cause impacts both clobber and incremental builds.
You are building on Windows
===========================
Builds on Windows are slow for a few reasons. First, Windows builds use
pymake, not GNU make (because of compatibility issues with GNU make).
But, there are other sources of slowness.
New processes on Windows are about a magnitude slower to spawn than on
UNIX-y systems such as Linux. This is because Windows has optimized new
threads while the \*NIX platforms typically optimize new processes.
Anyway, the build system spawns thousands of new processes during a
build. Parts of the build that rely on rapid spawning of new processes
are slow on Windows as a result. This is most pronounced when running
*configure*. The configure file is a giant shell script and shell
scripts rely heavily on new processes. This is why configure on Windows
can run over a minute slower on Windows.
Another reason Windows builds are slower is because Windows lacks proper
symlink support. On systems that support symlinks, we can generate a
file into a staging area then symlink it into the final directory very
quickly. On Windows, we have to perform a full file copy. This incurs
much more I/O. And if done poorly, can muck with file modification
times, messing up build dependencies. As of the summer of 2013, the
impact of symlinks is being mitigated through the use
of an :term:`install manifest`.
These issues impact both clobber and incremental builds.
Recursive make traversal is slow
================================
The build system has traditionally been built by employing recursive
make. Recursive make involves make iterating through directories / make
files sequentially and executing each in turn. This is inefficient for
directories containing few targets/tasks because make could be *starved*
for work when processing these directories. Any time make is starved,
the build isn't using all available CPU cycles and the build is slower
as a result.
Work has started in bug 907365 to fix this issue by changing the way
make traverses all the make files.
The impact of slow recursive make traversal is mostly felt on
incremental builds. Traditionally, most of the wall time during a
no-op build is spent in make traversal.
make is inefficient
===================
Compared to modern build backends like Tup or Ninja, make is slow and
inefficient. We can only make make so fast. At some point, we'll hit a
performance plateau and will need to use a different tool to make builds
faster.
Please note that clobber and incremental builds are different. A clobber
build with make will likely be as fast as a clobber build with e.g. Tup.
However, Tup should vastly outperform make when it comes to incremental
builds. Therefore, this issue is mostly seen when performing incremental
builds.
C++ header dependency hell
==========================
Modifying a *.h* file can have significant impact on the build system.
If you modify a *.h* that is used by 1000 C++ files, all of those 1000
C++ files will be recompiled.
Our code base has traditionally been sloppy managing the impact of
changed headers on build performance. Bug 785103 tracks improving the
situation.
This issue mostly impacts the times of an :term:`incremental build`.

View File

@ -14,7 +14,7 @@ JAVAFILES = \
R.java \
$(NULL)
RES_FILES = \
ANDROID_RESFILES = \
res/values/strings.xml \
$(NULL)

View File

@ -21,7 +21,7 @@ JAVAFILES = \
R.java \
$(NULL)
RES_FILES = \
ANDROID_RESFILES = \
res/drawable/icon.png \
res/drawable/ateamlogo.png \
res/drawable/ic_stat_first.png \

View File

@ -12,7 +12,7 @@ JAVAFILES = \
R.java \
$(NULL)
RES_FILES = \
ANDROID_RESFILES = \
res/drawable-hdpi/icon.png \
res/drawable-ldpi/icon.png \
res/drawable-mdpi/icon.png \

View File

@ -12,7 +12,7 @@ JAVAFILES = \
R.java \
$(NULL)
RES_FILES = \
ANDROID_RESFILES = \
res/drawable-hdpi/icon.png \
res/drawable-ldpi/icon.png \
res/drawable-mdpi/icon.png \

View File

@ -13,7 +13,7 @@ JAVAFILES = \
WatcherService.java \
$(NULL)
RES_FILES = \
ANDROID_RESFILES = \
res/drawable-hdpi/icon.png \
res/drawable-hdpi/ateamlogo.png \
res/drawable-ldpi/icon.png \

View File

@ -16,7 +16,6 @@
#include "nsJSPrincipals.h"
#include "nsCOMPtr.h"
#include "nsPrincipal.h"
#include "nsIContentSecurityPolicy.h"
class nsIURI;
@ -54,7 +53,6 @@ public:
virtual ~nsNullPrincipal();
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
};
#endif // nsNullPrincipal_h__

View File

@ -149,7 +149,8 @@ nsNullPrincipal::GetHashValue(uint32_t *aResult)
NS_IMETHODIMP
nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
{
// We don't actually do security policy caching.
// We don't actually do security policy caching. And it's not like anyone
// can set a security policy for us anyway.
*aSecurityPolicy = nullptr;
return NS_OK;
}
@ -157,7 +158,8 @@ nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
NS_IMETHODIMP
nsNullPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
{
// We don't actually do security policy caching.
// We don't actually do security policy caching. And it's not like anyone
// can set a security policy for us anyway.
return NS_OK;
}
@ -170,20 +172,16 @@ nsNullPrincipal::GetURI(nsIURI** aURI)
NS_IMETHODIMP
nsNullPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
{
NS_IF_ADDREF(*aCsp = mCSP);
// CSP on a null principal makes no sense
*aCsp = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
{
// If CSP was already set, it should not be destroyed! Instead, it should
// get set anew when a new principal is created.
if (mCSP)
return NS_ERROR_ALREADY_INITIALIZED;
mCSP = aCsp;
return NS_OK;
// CSP on a null principal makes no sense
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP

View File

@ -7,7 +7,7 @@
#define mozilla_RegistryMessageUtils_h
#include "ipc/IPCMessageUtils.h"
#include "nsStringGlue.h"
#include "nsString.h"
struct SerializedURI
{

View File

@ -10,32 +10,20 @@
*/
#include "nsAutoPtr.h"
#include "nsChromeProtocolHandler.h"
#include "nsChromeRegistry.h"
#include "nsCOMPtr.h"
#include "nsContentCID.h"
#include "nsCRT.h"
#include "nsThreadUtils.h"
#include "nsIChannel.h"
#include "nsIChromeRegistry.h"
#include "nsIComponentManager.h"
#include "nsIFile.h"
#include "nsIFileURL.h"
#include "nsIFileChannel.h"
#include "nsIIOService.h"
#include "nsIJARChannel.h"
#include "nsIJARURI.h"
#include "nsILoadGroup.h"
#include "nsIObjectOutputStream.h"
#include "nsIScriptSecurityManager.h"
#include "nsIServiceManager.h"
#include "nsIStandardURL.h"
#include "nsIStreamListener.h"
#include "nsNetUtil.h"
#include "nsXPIDLString.h"
#include "nsString.h"
#include "prlog.h"
////////////////////////////////////////////////////////////////////////////////

View File

@ -8,33 +8,23 @@
#include "nsChromeRegistryChrome.h"
#include "nsChromeRegistryContent.h"
#include <string.h>
#include "prio.h"
#include "prprf.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsEscape.h"
#include "nsLayoutCID.h"
#include "nsNetUtil.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsCSSStyleSheet.h"
#include "nsIConsoleService.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDocShell.h"
#include "nsIDOMElement.h"
#include "nsIDOMLocation.h"
#include "nsIDOMWindowCollection.h"
#include "nsIDOMWindow.h"
#include "nsIIOService.h"
#include "nsIJARProtocolHandler.h"
#include "nsIObserverService.h"
#include "nsIPresShell.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIWindowMediator.h"

View File

@ -6,31 +6,24 @@
#ifndef nsChromeRegistry_h
#define nsChromeRegistry_h
#include "nsIChromeRegistry.h"
#include "nsIToolkitChromeRegistry.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsIPrefBranch.h"
#ifdef MOZ_XUL
#include "nsIXULOverlayProvider.h"
#endif
#include "pldhash.h"
#include "nsCOMArray.h"
#include "nsString.h"
#include "nsTHashtable.h"
#include "nsURIHashKey.h"
#include "nsInterfaceHashtable.h"
#include "nsXULAppAPI.h"
#include "nsIResProtocolHandler.h"
#include "nsIXPConnect.h"
#include "mozilla/Omnijar.h"
#include "mozilla/FileLocation.h"
class nsIDOMWindow;
class nsIPrefBranch;
class nsIURL;
// The chrome registry is actually split between nsChromeRegistryChrome and

View File

@ -13,35 +13,27 @@
#include <windows.h>
#elif defined(XP_MACOSX)
#include <CoreServices/CoreServices.h>
#elif defined(MOZ_WIDGET_GTK)
#include <gtk/gtk.h>
#endif
#include "nsArrayEnumerator.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManager.h"
#include "nsEnumeratorUtils.h"
#include "nsNetUtil.h"
#include "nsStringEnumerator.h"
#include "nsTextFormatter.h"
#include "nsUnicharUtils.h"
#include "nsXPCOMCIDInternal.h"
#include "nsZipArchive.h"
#include "mozilla/LookAndFeel.h"
#include "nsICommandLine.h"
#include "nsILocaleService.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "mozilla/Preferences.h"
#include "nsIResProtocolHandler.h"
#include "nsIScriptError.h"
#include "nsIVersionComparator.h"
#include "nsIXPConnect.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
#define UILOCALE_CMD_LINE_ARG "UILocale"

View File

@ -6,6 +6,7 @@
#ifndef nsChromeRegistryChrome_h
#define nsChromeRegistryChrome_h
#include "nsCOMArray.h"
#include "nsChromeRegistry.h"
#include "nsVoidArray.h"
#include "mozilla/Move.h"

View File

@ -4,11 +4,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RegistryMessageUtils.h"
#include "nsChromeRegistry.h"
#include "nsChromeRegistryContent.h"
#include "nsString.h"
#include "nsNetUtil.h"
#include "nsResProtocolHandler.h"
#include "nsIResProtocolHandler.h"
nsChromeRegistryContent::nsChromeRegistryContent()
{

View File

@ -7,10 +7,8 @@
#define nsChromeRegistryContent_h
#include "nsChromeRegistry.h"
#include "nsTArray.h"
#include "nsClassHashtable.h"
class nsCString;
struct ChromePackage;
struct ResourceMapping;
struct OverrideMapping;

View File

@ -797,8 +797,8 @@ PLY_INCLUDE = -I$(topsrcdir)/other-licenses/ply
export CL_INCLUDES_PREFIX
ifeq ($(MOZ_WIDGET_GTK),2)
MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk2/compat $(MOZ_GTK2_CFLAGS)
ifdef MOZ_GTK2_CFLAGS
MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)
endif
DEFINES += -DNO_NSPR_10_SUPPORT

View File

@ -7,7 +7,7 @@
ifndef INCLUDED_JAVA_BUILD_MK #{
ifdef RES_FILES #{
ifdef ANDROID_RESFILES #{
res-dep := .deps-copy-java-res
GENERATED_DIRS += res
@ -16,7 +16,7 @@ GARBAGE += $(res-dep)
export:: $(res-dep)
res-dep-preqs := \
$(addprefix $(srcdir)/,$(RES_FILES)) \
$(addprefix $(srcdir)/,$(ANDROID_RESFILES)) \
$(call mkdir_deps,res) \
$(if $(IS_LANGUAGE_REPACK),FORCE) \
$(NULL)

View File

@ -1225,7 +1225,7 @@ endif
###############################################################################
# Java rules
###############################################################################
ifneq (,$(value JAVAFILES)$(value RESFILES))
ifneq (,$(value JAVAFILES)$(value ANDROID_RESFILES))
include $(topsrcdir)/config/makefiles/java-build.mk
endif

View File

@ -16,7 +16,7 @@
#include "nsIScriptGlobalObject.h"
#include "nsIURI.h"
#include "nsIWeakReferenceUtils.h"
#include "nsStringGlue.h"
#include "nsString.h"
class nsIDOMDocument;

View File

@ -49,9 +49,9 @@ function ContentSecurityPolicy() {
this._request = "";
this._requestOrigin = "";
this._weakRequestPrincipal = null;
this._requestPrincipal = "";
this._referrer = "";
this._weakDocRequest = { get : function() { return null; } };
this._docRequest = null;
CSPdebug("CSP object initialized, no policies to enforce yet");
this._cache = { };
@ -249,7 +249,7 @@ ContentSecurityPolicy.prototype = {
return;
// Save the docRequest for fetching a policy-uri
this._weakDocRequest = Cu.getWeakReference(aChannel);
this._docRequest = aChannel;
// save the document URI (minus <fragment>) and referrer for reporting
let uri = aChannel.URI.cloneIgnoringRef();
@ -260,9 +260,8 @@ ContentSecurityPolicy.prototype = {
this._requestOrigin = uri;
//store a reference to the principal, that can later be used in shouldLoad
this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getChannelPrincipal(aChannel));
this._requestPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
getService(Components.interfaces.nsIScriptSecurityManager).getChannelPrincipal(aChannel);
if (aChannel.referrer) {
let referrer = aChannel.referrer.cloneIgnoringRef();
@ -311,13 +310,13 @@ ContentSecurityPolicy.prototype = {
if (aSpecCompliant) {
newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
selfURI,
this._weakDocRequest.get(),
this._docRequest,
this,
aReportOnly);
} else {
newpolicy = CSPRep.fromString(aPolicy,
selfURI,
this._weakDocRequest.get(),
this._docRequest,
this,
aReportOnly);
}
@ -435,8 +434,8 @@ ContentSecurityPolicy.prototype = {
// we need to set an nsIChannelEventSink on the channel object
// so we can tell it to not follow redirects when posting the reports
chan.notificationCallbacks = new CSPReportRedirectSink(policy);
if (this._weakDocRequest.get()) {
chan.loadGroup = this._weakDocRequest.get().loadGroup;
if (this._docRequest) {
chan.loadGroup = this._docRequest.loadGroup;
}
chan.QueryInterface(Ci.nsIUploadChannel)
@ -455,7 +454,7 @@ ContentSecurityPolicy.prototype = {
.getService(Ci.nsIContentPolicy);
if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_CSP_REPORT,
chan.URI, this._requestOrigin,
null, null, null, this._weakRequestPrincipal.get())
null, null, null, this._requestPrincipal)
!= Ci.nsIContentPolicy.ACCEPT) {
continue; // skip unauthorized URIs
}

View File

@ -12,7 +12,7 @@
#define nsAttrValue_h___
#include "nscore.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsStringBuffer.h"
#include "nsColor.h"
#include "nsCaseTreatment.h"

View File

@ -16,7 +16,7 @@
#include "nsContentListDeclarations.h"
#include "nsISupports.h"
#include "nsTArray.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsIHTMLCollection.h"
#include "nsIDOMNodeList.h"
#include "nsINodeList.h"

View File

@ -39,6 +39,7 @@
#include "nsGUIEvent.h"
#include "nsIFrame.h"
#include "nsIURI.h"
#include "nsISimpleEnumerator.h"
// image copy stuff
#include "nsIImageLoadingContent.h"

View File

@ -16,7 +16,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMMozNamedAttrMap.h"
#include "nsRefPtrHashtable.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsWrapperCache.h"
class nsIAtom;

View File

@ -2681,8 +2681,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
if (csp) {
// Copy into principal
nsIPrincipal* principal = GetPrincipal();
rv = principal->SetCsp(csp);
NS_ENSURE_SUCCESS(rv, rv);
principal->SetCsp(csp);
#ifdef PR_LOGGING
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("Inserted CSP into principal %p", principal));

View File

@ -20,6 +20,7 @@
#include "nsIMozBrowserFrame.h"
#include "nsDOMClassInfoID.h"
#include "mozilla/dom/StructuredCloneUtils.h"
#include "js/StructuredClone.h"
using mozilla::dom::StructuredCloneData;
using mozilla::dom::StructuredCloneClosure;

View File

@ -97,19 +97,6 @@ MOCHITEST_FILES := \
file_bug836922_npolicies.html^headers^ \
file_bug836922_npolicies_violation.sjs \
file_bug836922_npolicies_ro_violation.sjs \
test_bug886164.html \
file_bug886164.html \
file_bug886164.html^headers^ \
file_bug886164_2.html \
file_bug886164_2.html^headers^ \
file_bug886164_3.html \
file_bug886164_3.html^headers^ \
file_bug886164_4.html \
file_bug886164_4.html^headers^ \
file_bug886164_5.html \
file_bug886164_5.html^headers^ \
file_bug886164_6.html \
file_bug886164_6.html^headers^ \
test_CSP_bug916446.html \
file_CSP_bug916446.html \
file_CSP_bug916446.html^headers^ \

View File

@ -1,15 +0,0 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox="allow-same-origin" -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'self'

View File

@ -1,14 +0,0 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'self'

View File

@ -1,12 +0,0 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'none'

View File

@ -1,12 +0,0 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'none'

View File

@ -1,26 +0,0 @@
<!DOCTYPE HTML>
<html>
<head> <meta charset="utf-8"> </head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with only inline "allow-scripts"
<!-- sandbox="allow-scripts" -->
<!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
<!-- these should be stopped by CSP -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'none' 'unsafe-inline';

View File

@ -1,35 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
document.getElementById('a_form').submit();
// trigger the javascript: url test
sendMouseEvent({type:'click'}, 'a_link');
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with "allow-scripts"
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
<form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
First name: <input type="text" name="firstname">
Last name: <input type="text" name="lastname">
<input type="submit" onclick="doSubmit()" id="a_button">
</form>
<a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
</body>
</html>

View File

@ -1 +0,0 @@
Content-Security-Policy: default-src 'self' 'unsafe-inline';

View File

@ -1,185 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 886164 - Enforce CSP in sandboxed iframe</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:200px;height:200px;" id='cspframe' sandbox="allow-same-origin"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe4' sandbox></iframe>
<iframe style="width:200px;height:200px;" id='cspframe5' sandbox="allow-scripts"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe6' sandbox="allow-same-origin allow-scripts"></iframe>
<script class="testbody" type="text/javascript">
var path = "/tests/content/base/test/csp/";
// These are test results: -1 means it hasn't run,
// true/false is the pass/fail result.
window.tests = {
// sandbox allow-same-origin; 'self'
img_good: -1, // same origin
img_bad: -1, //example.com
// sandbox; 'self'
img2_bad: -1, //example.com
img2a_good: -1, // same origin & is image
// sandbox allow-same-origin; 'none'
img3_bad: -1,
img3a_bad: -1,
// sandbox; 'none'
img4_bad: -1,
img4a_bad: -1,
// sandbox allow-scripts; 'none' 'unsafe-inline'
img5_bad: -1,
img5a_bad: -1,
script5_bad: -1,
script5a_bad: -1,
// sandbox allow-same-origin allow-scripts; 'self' 'unsafe-inline'
img6_bad: -1,
script6_bad: -1,
};
// a postMessage handler that is used by sandboxed iframes without
// 'allow-same-origin' to communicate pass/fail back to this main page.
// it expects to be called with an object like {ok: true/false, desc:
// <description of the test> which it then forwards to ok()
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
ok_wrapper(event.data.ok, event.data.desc);
}
var cspTestsDone = false;
var iframeSandboxTestsDone = false;
// iframe related
var completedTests = 0;
var passedTests = 0;
function ok_wrapper(result, desc) {
ok(result, desc);
completedTests++;
if (result) {
passedTests++;
}
if (completedTests === 5) {
iframeSandboxTestsDone = true;
if (cspTestsDone) {
SimpleTest.finish();
}
}
}
//csp related
// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.
function examiner() {
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
SpecialPowers.addObserver(this, "http-on-modify-request", false);
}
examiner.prototype = {
observe: function(subject, topic, data) {
// subject should be an nsURI, and should be either allowed or blocked.
if (!SpecialPowers.can_QI(subject))
return;
var testpat = new RegExp("testid=([a-z0-9_]+)");
//_good things better be allowed!
//_bad things better be stopped!
if (topic === "http-on-modify-request") {
//these things were allowed by CSP
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_good/.test(testid),
asciiSpec + " allowed by csp");
}
if(topic === "csp-on-violate-policy") {
//these were blocked... record that they were blocked
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_bad/.test(testid),
asciiSpec + " blocked by \"" + data + "\"");
}
},
// must eventually call this to remove the listener,
// or mochitests might get borked.
remove: function() {
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
SpecialPowers.removeObserver(this, "http-on-modify-request");
}
}
window.examiner = new examiner();
window.testResult = function(testname, result, msg) {
//test already complete.... forget it... remember the first result.
if (window.tests[testname] != -1)
return;
window.tests[testname] = result;
is(result, true, testname + ' test: ' + msg);
// if any test is incomplete, keep waiting
for (var v in window.tests)
if(tests[v] == -1) {
console.log(v + " is not complete");
return;
}
// ... otherwise, finish
window.examiner.remove();
cspTestsDone = true;
if (iframeSandboxTestsDone) {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{'set':[["security.csp.speccompliant", true]]},
function() {
// save this for last so that our listeners are registered.
// ... this loads the testbed of good and bad requests.
document.getElementById('cspframe').src = 'file_bug886164.html';
document.getElementById('cspframe2').src = 'file_bug886164_2.html';
document.getElementById('cspframe3').src = 'file_bug886164_3.html';
document.getElementById('cspframe4').src = 'file_bug886164_4.html';
document.getElementById('cspframe5').src = 'file_bug886164_5.html';
document.getElementById('cspframe6').src = 'file_bug886164_6.html';
});
</script>
</pre>
</body>
</html>

View File

@ -23,6 +23,7 @@
#include "nsDOMEventTargetHelper.h"
#include "nsPIWindowRoot.h"
#include "nsGlobalWindow.h"
#include "nsDeviceContext.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -12,7 +12,8 @@
#include "nsLayoutUtils.h"
#include "nsEvent.h"
#include "mozilla/dom/UIEventBinding.h"
#include "Units.h"
#include "nsPresContext.h"
#include "nsDeviceContext.h"
class nsDOMUIEvent : public nsDOMEvent,
public nsIDOMUIEvent

View File

@ -530,6 +530,12 @@ public:
mTextTracks->AddTextTrack(aTextTrack);
}
void RemoveTextTrack(TextTrack* aTextTrack) {
if (mTextTracks) {
mTextTracks->RemoveTextTrack(*aTextTrack);
}
}
protected:
class MediaLoadListener;
class StreamListener;

View File

@ -290,8 +290,15 @@ HTMLTrackElement::BindToTree(nsIDocument* aDocument,
void
HTMLTrackElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
if (mMediaParent && aNullParent) {
mMediaParent = nullptr;
if (mMediaParent) {
// mTrack can be null if HTMLTrackElement::LoadResource has never been
// called.
if (mTrack) {
mMediaParent->RemoveTextTrack(mTrack);
}
if (aNullParent) {
mMediaParent = nullptr;
}
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);

View File

@ -73,15 +73,13 @@ TextTrack::SetMode(TextTrackMode aValue)
void
TextTrack::AddCue(TextTrackCue& aCue)
{
//XXX: If cue exists, remove. Bug 867823.
mCueList->AddCue(aCue);
}
void
TextTrack::RemoveCue(TextTrackCue& aCue)
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
{
//XXX: If cue does not exists throw NotFoundError. Bug 867823.
mCueList->RemoveCue(aCue);
mCueList->RemoveCue(aCue, aRv);
}
void

View File

@ -99,7 +99,7 @@ public:
void Update(double aTime);
void AddCue(TextTrackCue& aCue);
void RemoveCue(TextTrackCue& aCue);
void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
void CueChanged(TextTrackCue& aCue);
IMPL_EVENT_HANDLER(cuechange)

View File

@ -116,6 +116,21 @@ public:
CueChanged();
}
void GetRegionId(nsAString& aRegionId) const
{
aRegionId = mRegionId;
}
void SetRegionId(const nsAString& aRegionId)
{
if (mRegionId == aRegionId) {
return;
}
mRegionId = aRegionId;
CueChanged();
}
DirectionSetting Vertical() const
{
return mVertical;
@ -304,6 +319,7 @@ private:
int32_t mSize;
bool mPauseOnExit;
bool mSnapToLines;
nsString mRegionId;
DirectionSetting mVertical;
int mLine;
TextTrackCueAlign mAlign;

View File

@ -66,12 +66,19 @@ TextTrackCueList::GetCueById(const nsAString& aId)
void
TextTrackCueList::AddCue(TextTrackCue& cue)
{
if (mList.Contains(&cue)) {
return;
}
mList.AppendElement(&cue);
}
void
TextTrackCueList::RemoveCue(TextTrackCue& cue)
TextTrackCueList::RemoveCue(TextTrackCue& cue, ErrorResult& aRv)
{
if (!mList.Contains(&cue)) {
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
return;
}
mList.RemoveElement(&cue);
}

View File

@ -11,6 +11,7 @@
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/ErrorResult.h"
namespace mozilla {
namespace dom {
@ -47,7 +48,7 @@ public:
TextTrackCue* GetCueById(const nsAString& aId);
void AddCue(TextTrackCue& cue);
void RemoveCue(TextTrackCue& cue);
void RemoveCue(TextTrackCue& cue, ErrorResult& aRv);
private:
nsCOMPtr<nsISupports> mParent;

View File

@ -6,7 +6,7 @@
#include "mozilla/dom/TextTrack.h"
#include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/TextTrackRegionBinding.h"
#include "mozilla/dom/VTTRegionBinding.h"
namespace mozilla {
namespace dom {
@ -22,7 +22,7 @@ NS_INTERFACE_MAP_END
JSObject*
TextTrackRegion::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return TextTrackRegionBinding::Wrap(aCx, aScope, this);
return VTTRegionBinding::Wrap(aCx, aScope, this);
}
already_AddRefed<TextTrackRegion>

View File

@ -5,7 +5,7 @@
#include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/TextTrackRegionList.h"
#include "mozilla/dom/TextTrackRegionListBinding.h"
#include "mozilla/dom/VTTRegionListBinding.h"
namespace mozilla {
namespace dom {
@ -23,7 +23,7 @@ NS_INTERFACE_MAP_END
JSObject*
TextTrackRegionList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return TextTrackRegionListBinding::Wrap(aCx, aScope, this);
return VTTRegionListBinding::Wrap(aCx, aScope, this);
}
TextTrackRegionList::TextTrackRegionList(nsISupports* aGlobal)

View File

@ -5,6 +5,8 @@
#include "WebVTTListener.h"
#include "mozilla/dom/TextTrackCue.h"
#include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/VTTRegionBinding.h"
#include "mozilla/dom/HTMLTrackElement.h"
#include "nsIInputStream.h"
#include "nsIWebVTTParserWrapper.h"
@ -172,7 +174,17 @@ WebVTTListener::OnCue(const JS::Value &aCue, JSContext* aCx)
NS_IMETHODIMP
WebVTTListener::OnRegion(const JS::Value &aRegion, JSContext* aCx)
{
// TODO: Implement VTTRegions see bug 897504
if (!aRegion.isObject()) {
return NS_ERROR_FAILURE;
}
TextTrackRegion* region;
nsresult rv = UNWRAP_OBJECT(VTTRegion, aCx, &aRegion.toObject(),
region);
NS_ENSURE_SUCCESS(rv, rv);
mElement->mTrack->AddRegion(*region);
return NS_OK;
}

View File

@ -140,6 +140,7 @@ MOCHITEST_FILES = \
$(filter disabled-for-intermittent-failures--bug-608634, test_error_in_video_document.html) \
test_texttrack.html \
test_texttrackcue.html \
test_texttrackregion.html \
test_timeupdate_small_files.html \
test_unseekable.html \
test_VideoPlaybackQuality.html \
@ -272,6 +273,7 @@ MOCHITEST_FILES += \
notags.mp3 \
id3tags.mp3 \
basic.vtt \
region.vtt \
long.vtt \
$(NULL)

View File

@ -0,0 +1,5 @@
WEBVTT
Region: id=fred width=62% lines=5 regionanchor=4%,78% viewportanchor=10%,90% scroll=up
00:01.000 --> 00:02.000 region:fred
Test here.

View File

@ -84,40 +84,29 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
is(cue.text, "foo", "Cue's text should be foo.");
// Adding the same cue again should not increase the cue count.
// TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=867823
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrack-addcue
trackElement.track.addCue(vttCue);
todo_is(cueList.length, 5, "Cue list length should be 5.");
is(cueList.length, 5, "Cue list length should be 5.");
// Check that we are able to remove cues.
trackElement.track.removeCue(cue);
// TODO: Marked as todo as incorrect addition up top increases cue count
// to 4 -- https://bugzilla.mozilla.org/show_bug.cgi?id=867823
todo_is(cueList.length, 4, "Cue list length should be 4.");
is(cueList.length, 4, "Cue list length should be 4.");
var exceptionHappened = false;
try {
// We should not be able to remove a cue that is not in the list.
cue = new VTTCue(1, 2, "foo");
trackElement.removeCue(cue);
trackElement.track.removeCue(cue);
} catch (e) {
// "NotFoundError" should be thrown when trying to remove a cue that is
// not in the list.
// TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=867823
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrack-removecue
todo_is(e.name, "NotFoundError", "We should have caught an error.");
is(e.name, "NotFoundError", "Should have thrown NotFoundError.");
exceptionHappened = true;
}
// If this is false then we did not throw an error and probably removed a cue
// when we shouln't have.
ok(exceptionHappened, "Exception should have happened.");
// We should not have removed a cue so the cue list length should not
// have changed.
// TODO: Marked as todo as incorrect addition of cue up top increases
// count erroneously.
// https://bugzilla.mozilla.org/show_bug.cgi?id=867823
todo_is(cueList.length, 4, "Cue list length should be 4.");
is(cueList.length, 4, "Cue list length should be 4.");
SimpleTest.finish();
});

Some files were not shown because too many files have changed in this diff Show More