This commit is contained in:
Wes Kocher 2014-10-24 14:50:33 -07:00
Родитель 5f4a602935 4bb0f491d6
Коммит f71f3c9a6a
44 изменённых файлов: 1238 добавлений и 257 удалений

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

@ -1613,7 +1613,7 @@ pref("loop.soft_start_hostname", "soft-start.loop.services.mozilla.com");
pref("loop.server", "https://loop.services.mozilla.com");
pref("loop.seenToS", "unseen");
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
pref("loop.legal.ToS_url", "https://call.mozilla.com/legal/terms/");
pref("loop.legal.ToS_url", "https://hello.firefox.com/legal/terms/");
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/");
pref("loop.do_not_disturb", false);
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");

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

@ -16,20 +16,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "LOOP_SESSION_TYPE",
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
"resource:///modules/loop/MozLoopPushHandler.jsm");
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).ConsoleAPI;
let consoleOptions = {
maxLogLevel: Services.prefs.getCharPref(PREF_LOG_LEVEL).toLowerCase(),
prefix: "Loop",
};
return new ConsoleAPI(consoleOptions);
});
this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
let gRoomsListFetched = false;
let gRooms = new Map();
let gCallbacks = new Map();
/**
* Callback used to indicate changes to rooms data on the LoopServer.
@ -59,7 +50,7 @@ let LoopRoomsInternal = {
// identifier.
for (let room of rooms) {
let id = MozLoopService.generateLocalID();
room.localRoomID = id;
room.localRoomId = id;
// Next, request the detailed information for each room.
// If the request fails the room data will not be added to the map.
try {
@ -67,22 +58,24 @@ let LoopRoomsInternal = {
for (let attr in details) {
room[attr] = details[attr]
}
delete room.currSize; //This attribute will be eliminated in the next revision.
gRooms.set(id, room);
}
catch (error) {log.warn("failed GETing room details for roomToken = " + room.roomToken + ": ", error)}
catch (error) {MozLoopService.log.warn(
"failed GETing room details for roomToken = " + room.roomToken + ": ", error)}
}
callback(null, [...gRooms.values()]);
return;
}.bind(this)).catch((error) => {log.error("getAll error:", error);
}.bind(this)).catch((error) => {MozLoopService.log.error("getAll error:", error);
callback(error)});
return;
},
getRoomData: function(roomID, callback) {
if (gRooms.has(roomID)) {
callback(null, gRooms.get(roomID));
getRoomData: function(localRoomId, callback) {
if (gRooms.has(localRoomId)) {
callback(null, gRooms.get(localRoomId));
} else {
callback(new Error("Room data not found or not fetched yet for room with ID " + roomID));
callback(new Error("Room data not found or not fetched yet for room with ID " + localRoomId));
}
return;
},
@ -130,6 +123,150 @@ let LoopRoomsInternal = {
onNotification: function(version, channelID) {
return;
},
createRoom: function(props, callback) {
// Always create a basic room record and launch the window, attaching
// the localRoomId. Later errors will be returned via the registered callback.
let localRoomId = MozLoopService.generateLocalID((id) => {gRooms.has(id)})
let room = {localRoomId : localRoomId};
for (let prop in props) {
room[prop] = props[prop]
}
gRooms.set(localRoomId, room);
this.addCallback(localRoomId, "RoomCreated", callback);
MozLoopService.openChatWindow(null, "", "about:loopconversation#room/" + localRoomId);
if (!"roomName" in props ||
!"expiresIn" in props ||
!"roomOwner" in props ||
!"maxSize" in props) {
this.postCallback(localRoomId, "RoomCreated",
new Error("missing required room create property"));
return localRoomId;
}
let sessionType = MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA :
LOOP_SESSION_TYPE.GUEST;
MozLoopService.hawkRequest(sessionType, "/rooms", "POST", props).then(
(response) => {
let data = JSON.parse(response.body);
for (let attr in data) {
room[attr] = data[attr]
}
delete room.expiresIn; //Do not keep this value - it is a request to the server
this.postCallback(localRoomId, "RoomCreated", null, room);
},
(error) => {
this.postCallback(localRoomId, "RoomCreated", error);
});
return localRoomId;
},
/**
* Send an update to the callbacks registered for a specific localRoomId
* for a callback type.
*
* The result set is always saved. Then each
* callback function that has been registered when this function is
* called will be called with the result set. Any new callback that
* is regsitered via addCallback will receive a copy of the last
* saved result set when registered. This allows the posting operation
* to complete before the callback is registered in an asynchronous
* operation.
*
* Callbacsk must be of the form:
* function (error, success) {...}
*
* @param {String} localRoomId Local room identifier.
* @param {String} callbackName callback type
* @param {?Error} error result or null.
* @param {?Object} success result if error argument is null.
*/
postCallback: function(localRoomId, callbackName, error, success) {
let roomCallbacks = gCallbacks.get(localRoomId);
if (!roomCallbacks) {
// No callbacks have been registered or results posted for this room.
// Initialize a record for this room and callbackName, saving the
// result set.
gCallbacks.set(localRoomId, new Map([[
callbackName,
{ callbackList: [], result: { error: error, success: success } }]]));
return;
}
let namedCallback = roomCallbacks.get(callbackName);
// A callback of this name has not been registered.
if (!namedCallback) {
roomCallbacks.set(
callbackName,
{callbackList: [], result: {error: error, success: success}});
return;
}
// Record the latest result set.
namedCallback.result = {error: error, success: success};
// Call each registerd callback passing the new result posted.
namedCallback.callbackList.forEach((callback) => {
callback(error, success);
});
},
addCallback: function(localRoomId, callbackName, callback) {
let roomCallbacks = gCallbacks.get(localRoomId);
if (!roomCallbacks) {
// No callbacks have been registered or results posted for this room.
// Initialize a record for this room and callbackName.
gCallbacks.set(localRoomId, new Map([[
callbackName,
{callbackList: [callback]}]]));
return;
}
let namedCallback = roomCallbacks.get(callbackName);
// A callback of this name has not been registered.
if (!namedCallback) {
roomCallbacks.set(
callbackName,
{callbackList: [callback]});
return;
}
// Add this callback if not already in the array
if (namedCallback.callbackList.indexOf(callback) >= 0) {
return;
}
namedCallback.callbackList.push(callback);
// If a result has been posted for this callback
// send it using this new callback function.
let result = namedCallback.result;
if (result) {
callback(result.error, result.success);
}
},
deleteCallback: function(localRoomId, callbackName, callback) {
let roomCallbacks = gCallbacks.get(localRoomId);
if (!roomCallbacks) {
return;
}
let namedCallback = roomCallbacks.get(callbackName);
if (!namedCallback) {
return;
}
let i = namedCallback.callbackList.indexOf(callback);
if (i >= 0) {
namedCallback.callbackList.splice(i, 1);
}
return;
},
};
Object.freeze(LoopRoomsInternal);
@ -156,15 +293,51 @@ this.LoopRooms = {
/**
* Return the current stored version of the data for the indicated room.
*
* @param {String} roomID Local room identifier
* @param {String} localRoomId Local room identifier
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`. The second argument will
* be the list of rooms, if it was fetched successfully.
*/
getRoomData: function(roomID, callback) {
return LoopRoomsInternal.getRoomData(roomID, callback);
getRoomData: function(localRoomId, callback) {
return LoopRoomsInternal.getRoomData(localRoomId, callback);
},
/**
* Create a room. Will both open a chat window for the new room
* and perform an exchange with the LoopServer to create the room.
* for a callback type. Callback must be of the form:
* function (error, success) {...}
*
* @param {Object} room properties to be sent to the LoopServer
* @param {Function} callback Must be of the form: function (error, success) {...}
*
* @returns {String} localRoomId assigned to this new room.
*/
createRoom: function(roomProps, callback) {
return LoopRoomsInternal.createRoom(roomProps, callback);
},
/**
* Register a callback of a specified type with a localRoomId.
*
* @param {String} localRoomId Local room identifier.
* @param {String} callbackName callback type
* @param {Function} callback Must be of the form: function (error, success) {...}
*/
addCallback: function(localRoomId, callbackName, callback) {
return LoopRoomsInternal.addCallback(localRoomId, callbackName, callback);
},
/**
* Un-register and delete a callback of a specified type for a localRoomId.
*
* @param {String} localRoomId Local room identifier.
* @param {String} callbackName callback type
* @param {Function} callback Previously passed to addCallback().
*/
deleteCallback: function(localRoomId, callbackName, callback) {
return LoopRoomsInternal.deleteCallback(localRoomId, callbackName, callback);
},
};
Object.freeze(LoopRooms);

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

@ -178,6 +178,35 @@ loop.conversation = (function(mozL10n) {
}
});
/**
* Incoming Call failed view. Displayed when a call fails.
*
* XXX Based on CallFailedView, but built specially until we flux-ify the
* incoming call views (bug 1088672).
*/
var IncomingCallFailedView = React.createClass({displayName: 'IncomingCallFailedView',
propTypes: {
cancelCall: React.PropTypes.func.isRequired
},
render: function() {
document.title = mozL10n.get("generic_failure_title");
return (
React.DOM.div({className: "call-window"},
React.DOM.h2(null, mozL10n.get("generic_failure_title")),
React.DOM.div({className: "btn-group call-action-group"},
React.DOM.button({className: "btn btn-cancel",
onClick: this.props.cancelCall},
mozL10n.get("cancel_button")
)
)
)
);
}
});
/**
* This view manages the incoming conversation views - from
* call initiation through to the actual conversation and call end.
@ -254,11 +283,13 @@ loop.conversation = (function(mozL10n) {
case "end": {
// XXX To be handled with the "failed" view state when bug 1047410 lands
if (this.state.callFailed) {
document.title = mozL10n.get("generic_failure_title");
} else {
document.title = mozL10n.get("conversation_has_ended");
return IncomingCallFailedView({
cancelCall: this.closeWindow.bind(this)}
)
}
document.title = mozL10n.get("conversation_has_ended");
var feebackAPIBaseUrl = navigator.mozLoop.getLoopCharPref(
"feedback.baseUrl");
@ -394,19 +425,25 @@ loop.conversation = (function(mozL10n) {
if (progressData.state !== "terminated")
return;
if (progressData.reason === "cancel" ||
progressData.reason === "closed") {
// XXX This would be nicer in the _abortIncomingCall function, but we need to stop
// it here for now due to server-side issues that are being fixed in bug 1088351.
// This is before the abort call to ensure that it happens before the window is
// closed.
navigator.mozLoop.stopAlerting();
// If we hit any of the termination reasons, and the user hasn't accepted
// then it seems reasonable to close the window/abort the incoming call.
//
// If the user has accepted the call, and something's happened, display
// the call failed view.
//
// https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
if (previousState === "init" || previousState === "alerting") {
this._abortIncomingCall();
return;
} else {
this.setState({callFailed: true, callStatus: "end"});
}
if (progressData.reason === "timeout") {
if (previousState === "init" || previousState === "alerting") {
this._abortIncomingCall();
} else {
this.setState({callFailed: true, callStatus: "end"});
}
}
},
/**
@ -414,7 +451,6 @@ loop.conversation = (function(mozL10n) {
* closes the websocket.
*/
_abortIncomingCall: function() {
navigator.mozLoop.stopAlerting();
this._websocket.close();
// Having a timeout here lets the logging for the websocket complete and be
// displayed on the console if both are on.
@ -644,6 +680,7 @@ loop.conversation = (function(mozL10n) {
AppControllerView: AppControllerView,
IncomingConversationView: IncomingConversationView,
IncomingCallView: IncomingCallView,
IncomingCallFailedView: IncomingCallFailedView,
init: init
};
})(document.mozL10n);

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

@ -178,6 +178,35 @@ loop.conversation = (function(mozL10n) {
}
});
/**
* Incoming Call failed view. Displayed when a call fails.
*
* XXX Based on CallFailedView, but built specially until we flux-ify the
* incoming call views (bug 1088672).
*/
var IncomingCallFailedView = React.createClass({
propTypes: {
cancelCall: React.PropTypes.func.isRequired
},
render: function() {
document.title = mozL10n.get("generic_failure_title");
return (
<div className="call-window">
<h2>{mozL10n.get("generic_failure_title")}</h2>
<div className="btn-group call-action-group">
<button className="btn btn-cancel"
onClick={this.props.cancelCall}>
{mozL10n.get("cancel_button")}
</button>
</div>
</div>
);
}
});
/**
* This view manages the incoming conversation views - from
* call initiation through to the actual conversation and call end.
@ -254,11 +283,13 @@ loop.conversation = (function(mozL10n) {
case "end": {
// XXX To be handled with the "failed" view state when bug 1047410 lands
if (this.state.callFailed) {
document.title = mozL10n.get("generic_failure_title");
} else {
document.title = mozL10n.get("conversation_has_ended");
return <IncomingCallFailedView
cancelCall={this.closeWindow.bind(this)}
/>
}
document.title = mozL10n.get("conversation_has_ended");
var feebackAPIBaseUrl = navigator.mozLoop.getLoopCharPref(
"feedback.baseUrl");
@ -394,19 +425,25 @@ loop.conversation = (function(mozL10n) {
if (progressData.state !== "terminated")
return;
if (progressData.reason === "cancel" ||
progressData.reason === "closed") {
// XXX This would be nicer in the _abortIncomingCall function, but we need to stop
// it here for now due to server-side issues that are being fixed in bug 1088351.
// This is before the abort call to ensure that it happens before the window is
// closed.
navigator.mozLoop.stopAlerting();
// If we hit any of the termination reasons, and the user hasn't accepted
// then it seems reasonable to close the window/abort the incoming call.
//
// If the user has accepted the call, and something's happened, display
// the call failed view.
//
// https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
if (previousState === "init" || previousState === "alerting") {
this._abortIncomingCall();
return;
} else {
this.setState({callFailed: true, callStatus: "end"});
}
if (progressData.reason === "timeout") {
if (previousState === "init" || previousState === "alerting") {
this._abortIncomingCall();
} else {
this.setState({callFailed: true, callStatus: "end"});
}
}
},
/**
@ -414,7 +451,6 @@ loop.conversation = (function(mozL10n) {
* closes the websocket.
*/
_abortIncomingCall: function() {
navigator.mozLoop.stopAlerting();
this._websocket.close();
// Having a timeout here lets the logging for the websocket complete and be
// displayed on the console if both are on.
@ -644,6 +680,7 @@ loop.conversation = (function(mozL10n) {
AppControllerView: AppControllerView,
IncomingConversationView: IncomingConversationView,
IncomingCallView: IncomingCallView,
IncomingCallFailedView: IncomingCallFailedView,
init: init
};
})(document.mozL10n);

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

@ -431,87 +431,7 @@ describe("loop.conversation", function() {
sandbox.stub(window, "close");
});
describe("progress - terminated - cancel", function() {
it("should stop alerting", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
done();
});
});
it("should close the websocket", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sinon.assert.calledOnce(icView._websocket.close);
done();
});
});
it("should close the window", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "cancel"
});
sandbox.clock.tick(1);
sinon.assert.calledOnce(window.close);
done();
});
});
});
describe("progress - terminated - closed", function() {
it("should stop alerting", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "closed"
});
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
done();
});
});
it("should close the websocket", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "closed"
});
sinon.assert.calledOnce(icView._websocket.close);
done();
});
});
it("should close the window", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "closed"
});
sandbox.clock.tick(1);
sinon.assert.calledOnce(window.close);
done();
});
});
});
describe("progress - terminated - timeout (previousState = alerting)", function() {
describe("progress - terminated (previousState = alerting)", function() {
it("should stop alerting", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
@ -528,7 +448,7 @@ describe("loop.conversation", function() {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
reason: "closed"
}, "alerting");
sinon.assert.calledOnce(icView._websocket.close);
@ -540,7 +460,7 @@ describe("loop.conversation", function() {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
reason: "answered-elsewhere"
}, "alerting");
sandbox.clock.tick(1);
@ -551,21 +471,33 @@ describe("loop.conversation", function() {
});
});
describe("progress - terminated - timeout (previousState not init" +
describe("progress - terminated (previousState not init" +
" nor alerting)",
function() {
it("should set the state to end", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "timeout"
reason: "media-fail"
}, "connecting");
expect(icView.state.callStatus).eql("end");
done();
});
});
});
it("should stop alerting", function(done) {
promise.then(function() {
icView._websocket.trigger("progress", {
state: "terminated",
reason: "media-fail"
}, "connecting");
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
done();
});
});
});
});
});
});
@ -761,12 +693,12 @@ describe("loop.conversation", function() {
});
describe("session:network-disconnected", function() {
it("should navigate to call/feedback when network disconnects",
it("should navigate to call failed when network disconnects",
function() {
conversation.trigger("session:network-disconnected");
TestUtils.findRenderedComponentWithType(icView,
sharedView.FeedbackView);
loop.conversation.IncomingCallFailedView);
});
it("should update the conversation window toolbar title",

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

@ -0,0 +1,103 @@
/* 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/. */
Cu.import("resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
"resource:///modules/Chat.jsm");
let hasTheseProps = function(a, b) {
for (let prop in a) {
if (a[prop] != b[prop]) {
return false;
}
}
return true;
}
let openChatOrig = Chat.open;
add_test(function test_openRoomsWindow() {
let roomProps = {roomName: "UX Discussion",
expiresIn: 5,
roomOwner: "Alexis",
maxSize: 2}
let roomData = {roomToken: "_nxD4V4FflQ",
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
expiresAt: 1405534180}
loopServer.registerPathHandler("/rooms", (request, response) => {
if (!request.bodyInputStream) {
do_throw("empty request body");
}
let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
let data = JSON.parse(body);
do_check_true(hasTheseProps(roomProps, data));
response.setStatusLine(null, 200, "OK");
response.write(JSON.stringify(roomData));
response.processAsync();
response.finish();
});
MozLoopService.register(mockPushHandler).then(() => {
let opened = false;
let created = false;
let urlPieces = [];
Chat.open = function(contentWindow, origin, title, url) {
urlPieces = url.split('/');
do_check_eq(urlPieces[0], "about:loopconversation#room");
opened = true;
};
let returnedID = LoopRooms.createRoom(roomProps, (error, data) => {
do_check_false(error);
do_check_true(data);
do_check_true(hasTheseProps(roomData, data));
do_check_eq(data.localRoomId, urlPieces[1]);
created = true;
});
waitForCondition(function() created && opened).then(() => {
do_check_true(opened, "should open a chat window");
do_check_eq(returnedID, urlPieces[1]);
// Verify that a delayed callback, when attached,
// received the same data.
LoopRooms.addCallback(
urlPieces[1], "RoomCreated",
(error, data) => {
do_check_false(error);
do_check_true(data);
do_check_true(hasTheseProps(roomData, data));
do_check_eq(data.localRoomId, urlPieces[1]);
});
run_next_test();
}, () => {
do_throw("should have opened a chat window");
});
});
});
function run_test()
{
setupFakeLoopServer();
mockPushHandler.registrationPushURL = kEndPointUrl;
loopServer.registerPathHandler("/registration", (request, response) => {
response.setStatusLine(null, 200, "OK");
response.processAsync();
response.finish();
});
do_register_cleanup(function() {
// Revert original Chat.open implementation
Chat.open = openChatOrig;
});
run_next_test();
}

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

@ -94,14 +94,16 @@ add_test(function test_getAllRooms() {
do_check_eq(rooms[2].roomName, "Third Room Name");
let room = rooms[0];
do_check_true(room.localRoomID);
do_check_true(room.localRoomId);
do_check_false(room.currSize);
delete roomList[0].currSize;
do_check_true(hasTheseProps(roomList[0], room));
delete roomDetail.roomName;
delete room.participants;
delete roomDetail.participants;
do_check_true(hasTheseProps(roomDetail, room));
LoopRooms.getRoomData(room.localRoomID, (error, roomData) => {
LoopRooms.getRoomData(room.localRoomId, (error, roomData) => {
do_check_false(error);
do_check_true(hasTheseProps(room, roomData));

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

@ -22,3 +22,4 @@ skip-if = toolkit == 'gonk'
[test_loopservice_token_validation.js]
[test_loopservice_busy.js]
[test_rooms_getdata.js]
[test_rooms_create.js]

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

@ -1623,11 +1623,25 @@ BrowserGlue.prototype = {
// Reset homepage pref for users who have it set to start.mozilla.org
// or google.com/firefox.
const HOMEPAGE_PREF = "browser.startup.homepage";
let uri = Services.prefs.getComplexValue(HOMEPAGE_PREF,
Ci.nsIPrefLocalizedString).data;
if (uri && (uri.startsWith("http://start.mozilla.org") ||
/^https?:\/\/(www\.)?google\.[a-z.]+\/firefox/i.test(uri))) {
Services.prefs.clearUserPref(HOMEPAGE_PREF);
if (Services.prefs.prefHasUserValue(HOMEPAGE_PREF)) {
const DEFAULT =
Services.prefs.getDefaultBranch(HOMEPAGE_PREF)
.getComplexValue("", Ci.nsIPrefLocalizedString).data;
let value =
Services.prefs.getComplexValue(HOMEPAGE_PREF, Ci.nsISupportsString);
let updated =
value.data.replace(/https?:\/\/start\.mozilla\.org[^|]*/i, DEFAULT)
.replace(/https?:\/\/(www\.)?google\.[a-z.]+\/firefox[^|]*/i,
DEFAULT);
if (updated != value.data) {
if (updated == DEFAULT) {
Services.prefs.clearUserPref(HOMEPAGE_PREF);
} else {
value.data = updated;
Services.prefs.setComplexValue(HOMEPAGE_PREF,
Ci.nsISupportsString, value);
}
}
}
}

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

@ -92,6 +92,7 @@ function parseDeclarations(inputString) {
case "FUNCTION":
current += token.value + "(";
break;
case "(":
case ")":
current += token.tokenType;
break;

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

@ -163,7 +163,9 @@ const TEST_DATA = [
{name: "content", value: '"a not s\
o very long title"', priority: ""}
]
}
},
// Test calc with nested parentheses
{input: "width: calc((100% - 3em) / 2)", expected: [{name: "width", value: "calc((100% - 3em) / 2)", priority: ""}]},
];
function run_test() {

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

@ -23,7 +23,7 @@ def main(file):
appdata = dict(("%s:%s" % (s, o), config.get(s, o)) for s in config.sections() for o in config.options(s))
appdata['flags'] = ' | '.join(flags) if flags else '0'
appdata['App:profile'] = '"%s"' % appdata['App:profile'] if 'App:profile' in appdata else 'NULL'
expected = ('App:vendor', 'App:name', 'App:version', 'App:buildid',
expected = ('App:vendor', 'App:name', 'App:remotingname', 'App:version', 'App:buildid',
'App:id', 'Gecko:minversion', 'Gecko:maxversion')
missing = [var for var in expected if var not in appdata]
if missing:
@ -40,6 +40,7 @@ def main(file):
NULL, // directory
"%(App:vendor)s",
"%(App:name)s",
"%(App:remotingname)s",
"%(App:version)s",
"%(App:buildid)s",
"%(App:id)s",

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

@ -18,6 +18,7 @@
[App]
Vendor=@MOZ_APP_VENDOR@
Name=@MOZ_APP_BASENAME@
RemotingName=@MOZ_APP_REMOTINGNAME@
#ifdef MOZ_APP_DISPLAYNAME
CodeName=@MOZ_APP_DISPLAYNAME@
#endif

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

@ -27,7 +27,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
for var in ('GRE_MILESTONE', 'MOZ_APP_VERSION', 'MOZ_APP_BASENAME',
'MOZ_APP_VENDOR', 'MOZ_APP_ID', 'MAR_CHANNEL_ID',
'ACCEPTED_MAR_CHANNEL_IDS'):
'ACCEPTED_MAR_CHANNEL_IDS', 'MOZ_APP_REMOTINGNAME'):
DEFINES[var] = CONFIG[var]
if CONFIG['MOZ_APP_DISPLAYNAME'] != CONFIG['MOZ_APP_BASENAME']:

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

@ -8683,6 +8683,8 @@ AC_SUBST(MOZ_CHILD_PROCESS_BUNDLE)
# - MOZ_APP_VERSION: Defines the application version number.
# - MOZ_APP_NAME: Used for e.g. the binary program file name. If not set,
# defaults to a lowercase form of MOZ_APP_BASENAME.
# - MOZ_APP_REMOTINGNAME: Used for the internal program name, which affects
# profile name and remoting. If not set, defaults to MOZ_APP_NAME.
# - MOZ_APP_PROFILE: When set, used for application.ini's
# "Profile" field, which controls profile location.
# - MOZ_APP_ID: When set, used for application.ini's "ID" field, and
@ -8693,6 +8695,10 @@ if test -z "$MOZ_APP_NAME"; then
MOZ_APP_NAME=`echo $MOZ_APP_BASENAME | tr A-Z a-z`
fi
if test -z "$MOZ_APP_REMOTINGNAME"; then
MOZ_APP_REMOTINGNAME=$MOZ_APP_NAME
fi
# For extensions and langpacks, we require a max version that is compatible
# across security releases. MOZ_APP_MAXVERSION is our method for doing that.
# 24.0a1 and 24.0a2 aren't affected
@ -8712,6 +8718,7 @@ AC_DEFINE_UNQUOTED(MOZ_B2G_VERSION,"$MOZ_B2G_VERSION")
AC_DEFINE_UNQUOTED(MOZ_B2G_OS_NAME,"$MOZ_B2G_OS_NAME")
AC_SUBST(MOZ_APP_NAME)
AC_SUBST(MOZ_APP_REMOTINGNAME)
AC_SUBST(MOZ_APP_DISPLAYNAME)
AC_SUBST(MOZ_APP_BASENAME)
AC_SUBST(MOZ_APP_VENDOR)
@ -9004,6 +9011,10 @@ if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK" ; then
AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION)
fi
if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then
AC_DEFINE(MOZ_DEV_EDITION)
fi
if test "$MOZ_DEBUG"; then
A11Y_LOG=1
fi
@ -9308,6 +9319,7 @@ export MOZ_NATIVE_ZLIB
export MOZ_ZLIB_CFLAGS
export MOZ_ZLIB_LIBS
export MOZ_APP_NAME
export MOZ_APP_REMOTINGNAME
export DONT_POPULATE_VIRTUALENV=1
export PYTHON
export MOZILLA_CENTRAL_PATH=$_topsrcdir

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

@ -588,6 +588,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 775227
[test_htmlcopyencoder.html]
[test_htmlcopyencoder.xhtml]
[test_ipc_messagemanager_blob.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
[test_meta_viewport0.html]
[test_meta_viewport1.html]
[test_meta_viewport2.html]

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

@ -58,6 +58,7 @@ skip-if = (toolkit == 'gonk' && debug) #debug-only failure; bug 926546
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_input_sanitization.html]
[test_input_textarea_set_value_no_scroll.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_input_typing_sanitization.html]
skip-if = buildapp == 'mulet'
[test_input_untrusted_key_events.html]

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

@ -306,20 +306,27 @@ skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
[test_aspectratio_mp4.html]
[test_audio1.html]
[test_audio2.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_audioDocumentTitle.html]
skip-if = true # bug 475110 - disabled since we don't play Wave files standalone
[test_autoplay.html]
[test_autoplay_contentEditable.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_buffered.html]
[test_bug448534.html]
skip-if = buildapp == 'mulet' || os == 'win' # bug 894922
skip-if = buildapp == 'mulet' || os == 'win' || (toolkit == 'android' && processor == 'x86') # bug 894922 #x86 only bug 914439
[test_bug463162.xhtml]
[test_bug465498.html]
skip-if = (toolkit == 'android' && processor == 'x86')
[test_bug493187.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug495145.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug495300.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug654550.html]
[test_bug686942.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_bug726904.html]
[test_bug874897.html]
[test_bug883173.html]
@ -330,12 +337,13 @@ skip-if = buildapp == 'mulet' || os == 'win' # bug 894922
[test_bug1018933.html]
[test_can_play_type.html]
[test_can_play_type_mpeg.html]
skip-if = buildapp == 'b2g' # bug 1021675
skip-if = buildapp == 'b2g' || (toolkit == 'android' && processor == 'x86') # bug 1021675 #x86 only bug 914439
[test_can_play_type_no_ogg.html]
[test_can_play_type_ogg.html]
[test_chaining.html]
skip-if = toolkit == 'gonk' && debug
[test_clone_media_element.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_closing_connections.html]
[test_constants.html]
[test_contentDuration1.html]
@ -358,6 +366,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1
skip-if = toolkit == 'android' # bug 608634
[test_error_on_404.html]
[test_fastSeek.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_fastSeek-forwards.html]
[test_imagecapture.html]
[test_info_leak.html]
@ -365,12 +374,18 @@ skip-if = toolkit == 'android' # bug 608634
[test_invalid_reject_play.html]
[test_invalid_seek.html]
[test_load.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_load_candidates.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_load_same_resource.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_load_source.html]
[test_loop.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_media_selection.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_media_sniffer.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_mediarecorder_avoid_recursion.html]
[test_mediarecorder_creation.html]
[test_mediarecorder_creation_fail.html]
@ -408,18 +423,25 @@ skip-if = true # bug 567954 and intermittent leaks
[test_mozHasAudio.html]
[test_networkState.html]
[test_new_audio.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_no_load_event.html]
[test_paused.html]
[test_paused_after_ended.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_play_events.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_play_events_2.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_play_twice.html]
# Seamonkey: Bug 598252, B2G: Bug 982100, Android: Bug 758476, bug 981086
skip-if = appname == "seamonkey" || toolkit == 'gonk' || toolkit == 'android'
[test_playback.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_playback_errors.html]
[test_playback_rate.html]
skip-if = (toolkit == 'android' && processor == 'x86') #bug 845162
[test_playback_rate_playpause.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_played.html]
skip-if = true # bug 1021794
[test_preload_actions.html]
@ -427,15 +449,20 @@ skip-if = true # bug 1021794
[test_preload_suspend.html]
skip-if = true # bug 493692
[test_progress.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_reactivate.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_readyState.html]
[test_referer.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_replay_metadata.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_reset_events_async.html]
[test_reset_src.html]
[test_resume.html]
skip-if = true # bug 1021673
[test_seek_out_of_range.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_seek-1.html]
[test_seek-2.html]
[test_seek-3.html]
@ -450,6 +477,7 @@ skip-if = true # bug 1021673
[test_seek-12.html]
[test_seek-13.html]
[test_seekable1.html]
skip-if = (toolkit == 'android' && processor == 'x86') #timeout x86 only bug 914439
[test_seekable2.html]
[test_seekable3.html]
[test_seekLies.html]
@ -458,9 +486,12 @@ skip-if = true # bug 1021673
[test_source_null.html]
[test_source_write.html]
[test_standalone.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_streams_autoplay.html]
[test_streams_element_capture.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_streams_element_capture_createObjectURL.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_streams_element_capture_playback.html]
[test_streams_element_capture_reset.html]
[test_streams_gc.html]
@ -472,10 +503,13 @@ skip-if = buildapp == 'b2g' # bug 1021682
[test_texttracklist.html]
[test_texttrackregion.html]
[test_timeupdate_small_files.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_trackelementevent.html]
[test_trackevent.html]
[test_unseekable.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_video_to_canvas.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_video_in_audio_element.html]
[test_videoDocumentTitle.html]
[test_VideoPlaybackQuality.html]

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

@ -35,6 +35,7 @@ support-files =
[test_audioBufferSourceNodeLoopStartEnd.html]
[test_audioBufferSourceNodeLoopStartEndSame.html]
[test_audioBufferSourceNodeNeutered.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_audioBufferSourceNodeNoStart.html]
[test_audioBufferSourceNodeNullBuffer.html]
[test_audioBufferSourceNodeOffset.html]

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

@ -38,7 +38,7 @@ skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || toolkit == 'and
[test_child.html]
[test_grandchild.html]
[test_not-opener.html]
skip-if = buildapp == 'b2g'
skip-if = buildapp == 'b2g' || (toolkit == 'android' && processor == 'x86') #x86 only
[test_opener.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_popup-navigates-children.html]

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

@ -31,7 +31,9 @@ support-files =
[test_marketplace_pkg_install.html]
skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
[test_packaged_app_install.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_packaged_app_update.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_receipt_operations.html]
[test_signed_pkg_install.html]
[test_uninstall_errors.html]

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

@ -153,6 +153,7 @@ disabled = bug 1022281
[test_browserElement_inproc_FrameWrongURI.html]
skip-if = (toolkit == 'gonk' && !debug)
[test_browserElement_inproc_GetScreenshot.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_browserElement_inproc_GetScreenshotDppx.html]
[test_browserElement_inproc_Iconchange.html]
[test_browserElement_inproc_KeyEvents.html]
@ -200,6 +201,7 @@ skip-if = (toolkit == 'gonk' && !debug)
[test_browserElement_inproc_XFrameOptionsDeny.html]
[test_browserElement_inproc_XFrameOptionsSameOrigin.html]
[test_browserElement_oop_NextPaint.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
# Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
[test_browserElement_inproc_Reload.html]
disabled = bug 774100

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

@ -25,27 +25,49 @@ support-files =
[test_2d.clearRect.image.offscreen.html]
[test_2d.clip.winding.html]
[test_2d.composite.canvas.color-burn.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.color-dodge.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.color.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.darken.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.destination-atop.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.destination-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.difference.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.exclusion.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.hard-light.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.hue.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.lighten.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.luminosity.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.multiply.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.overlay.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.saturation.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.screen.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.soft-light.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.source-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.canvas.source-out.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.image.destination-atop.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.image.destination-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.image.source-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.image.source-out.html]
# xor and lighter aren't well handled by cairo; they mostly work, but we don't want
# to test that
@ -112,8 +134,11 @@ skip-if = toolkit != 'cocoa'
[test_2d.composite.uncovered.fill.destination-atop.html]
skip-if = toolkit != 'cocoa'
[test_2d.composite.uncovered.image.destination-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.uncovered.image.source-in.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
[test_2d.composite.uncovered.image.source-out.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 913662
# Tests that fail on non-Mac (bug 407107)
[test_2d.composite.uncovered.pattern.source-in.html]
skip-if = toolkit != 'cocoa'

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

@ -35,12 +35,15 @@ support-files =
[test_app_install.html]
[test_readonly.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
[test_basic.html]
[test_basic_worker.html]
[test_changes.html]
[test_arrays.html]
[test_oop.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
[test_sync.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
[test_sync_worker.html]
[test_bug924104.html]
[test_certifiedApp.html]

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

@ -66,21 +66,27 @@ support-files =
# [test_bug478398.html]
# disabled - See bug 579139
[test_bug490949.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_bug496292.html]
[test_bug497665.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_bug512435.html]
[test_bug552605-1.html]
[test_bug552605-2.html]
[test_bug553982.html]
[test_bug601470.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_bug614392.html]
[test_bug657191.html]
[test_bug671906.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_bug733553.html]
[test_bug767779.html]
[test_bug865919.html]
[test_bug89419-1.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_bug89419-2.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_animation_operators.html]
[test_drawDiscardedImage.html]
skip-if = toolkit == "gonk" #Bug 997034 - canvas.toDataURL() often causes lost connection to device.

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

@ -9,6 +9,6 @@
# project structure.
# Project target.
target=@ANDROID_TARGET_SDK@
target=android-@ANDROID_TARGET_SDK@
@IDE_PROJECT_LIBRARY_SETTING@
@IDE_PROJECT_LIBRARY_REFERENCES@

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

@ -1,89 +1,17 @@
{
"runtests": {},
"excludetests": {
"content/base/test/test_ipc_messagemanager_blob.html": "x86 only bug 936226",
"content/base/test/test_copypaste.xul": "bug 904183",
"content/html/content/test/forms/test_input_textarea_set_value_no_scroll.html": "x86 only",
"content/media/test/test_clone_media_element.html": "x86 only bug 914439",
"content/media/test/test_fastSeek.html": "x86 only bug 914439",
"content/media/test/test_media_selection.html": "x86 only bug 914439",
"content/media/test/test_bug495300.html": "x86 only bug 914439",
"content/media/test/test_new_audio.html": "x86 only bug 914439",
"content/media/test/test_seeked.html": "x86 only bug 914439",
"content/media/test/test_played.html": "bug 751539",
"content/media/test/test_playback_rate.html": "bug 845162",
"content/media/test/test_playback_rate_playpause.html": "x86 only bug 914439",
"content/media/test/test_loop.html": "x86 only bug 914439",
"content/media/test/test_play_twice.html": "x86 only bug 914439",
"content/media/test/test_bug495145.html": "x86 only bug 914439",
"content/media/test/test_bug465498.html": "",
"content/media/test/test_play_events_2.html": "x86 only bug 914439",
"content/media/test/test_media_sniffer.html": "x86 only bug 914439",
"content/media/test/test_seek_out_of_range.html": "x86 only bug 914439",
"content/media/test/test_load_same_resource.html": "x86 only bug 914439",
"content/media/test/test_bug493187.html": "x86 only bug 914439",
"content/media/test/test_autoplay_contentEditable.html": "x86 only",
"content/media/test/test_audio2.html": "x86 only bug 914439",
"content/media/test/test_can_play_type_mpeg.html": "x86 only bug 914439",
"content/media/test/test_bug686942.html": "x86 only bug 914439",
"content/media/test/test_standalone.html": "x86 only bug 914439",
"content/media/test/test_bug448534.html": "x86 only bug 914439",
"content/media/test/test_video_to_canvas.html": "x86 only bug 914439",
"content/media/test/test_load_candidates.html": "x86 only bug 914439",
"content/media/test/test_streams_element_capture_createObjectURL.html": "x86 only bug 914439",
"content/media/test/test_progress.html": "x86 only bug 914439",
"content/media/test/test_seekable1.html": "timeout x86 only bug 914439",
"content/media/test/test_play_events.html": "x86 only bug 914439",
"content/media/test/test_reactivate.html": "x86 only bug 914439",
"content/media/test/test_referer.html": "x86 only",
"content/media/webaudio/test/test_audioBufferSourceNodeNeutered.html": "x86 only",
"content/media/test/test_load.html": "x86 only bug 914439",
"content/media/test/test_streams_element_capture.html": "x86 only bug 914439",
"content/media/test/test_replay_metadata.html": "x86 only bug 914439",
"content/media/test/test_unseekable.html": "x86 only",
"content/media/test/test_paused_after_ended.html": "x86 only bug 914439",
"content/media/test/test_timeupdate_small_files.html": "x86 only bug 914439",
"content/media/test/test_playback.html": "x86 only bug 914439",
"docshell/test/navigation/test_not-opener.html": "x86 only",
"dom/apps/tests/test_packaged_app_update.html": "x86 only",
"dom/apps/tests/test_packaged_app_install.html": "x86 only",
"dom/browser-element/mochitest/test_browserElement_oop_NextPaint.html": "x86 only bug 936226",
"dom/browser-element/mochitest/test_browserElement_inproc_GetScreenshot.html": "x86 only",
"dom/browser-element/mochitest/test_browserElement_NoWhitelist.html": "x86 only bug 936226",
"dom/browser-element/mochitest/test_browserElement_oop_SecurityChange.html": "TIMED_OUT, bug 766586",
"dom/canvas/test/webgl-conformance": "bug 865443 - separate suite -- mochitest-gl",
"dom/canvas/test/test_2d.composite.canvas.luminosity.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.source-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.uncovered.image.destination-atop.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.image.source-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.multiply.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.lighten.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.color-dodge.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.destination-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.screen.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.image.source-out.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.exclusion.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.source-out.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.soft-light.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.destination-atop.html": "x86 only bug 913662",
"dom/canvas/test/test_canvas.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.overlay.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.darken.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.uncovered.image.source-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.saturation.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.image.destination-atop.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.uncovered.image.source-out.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.color.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.image.destination-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.hue.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.difference.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.color-burn.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.uncovered.image.destination-in.html": "x86 only bug 913662",
"dom/canvas/test/test_2d.composite.canvas.hard-light.html": "x86 only bug 913662",
"dom/contacts/tests/test_contacts_getall.html": "x86 only",
"dom/datastore/tests/test_oop.html": "x86 only bug 936226",
"dom/datastore/tests/test_sync.html": "x86 only bug 936226",
"dom/datastore/tests/test_readonly.html": "x86 only bug 936226",
"dom/devicestorage": "bug 781789 & bug 782275",
"dom/imptests/editing/selecttest/test_addRange.html": "bug 775227",
"dom/imptests/editing/conformancetest/test_runtest.html": "",
@ -93,18 +21,9 @@
"dom/tests/mochitest/geolocation/test_timeoutWatch.html": "TIMED_OUT",
"editor/libeditor/tests/test_bug569988.html": "TIMED_OUT",
"editor/libeditor/tests/test_texteditor_keyevent_handling.html": "",
"Harness_sanity/test_sanityWindowSnapshot.html": "x86 only",
"image/test/mochitest/test_bug671906.html": "x86 only",
"image/test/mochitest/test_bug89419-2.html": "x86 only",
"image/test/mochitest/test_bug490949.html": "x86 only",
"image/test/mochitest/test_bug497665.html": "x86 only",
"image/test/mochitest/test_bug601470.html": "x86 only",
"image/test/mochitest/test_bug89419-1.html": "x86 only",
"layout/generic": "CRASH_DUMP, RANDOM, ONLY IN CHUNK 10",
"MochiKit-1.4.2/tests/test_MochiKit-Async.html": "x86 only",
"robocop": "TIMED_OUT",
"toolkit/components/satchel/test/test_form_autocomplete.html": "TIMED_OUT",
"toolkit/components/alerts/test/test_alerts_noobserve.html": "x86 only",
"toolkit/components/places/tests/test_bug_411966.html": "RANDOM",
"toolkit/components/passwordmgr/test/test_privbrowsing_perwindowpb.html": "",
"toolkit/content/tests/widgets/test_menubar.xul": "W/SharedBufferStack(21799",

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

@ -7,6 +7,7 @@ skip-if = true #depends on fix for bug 1048446
[test_sanityException2.html]
[test_sanityParams.html]
[test_sanityWindowSnapshot.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_SpecialPowersExtension.html]
[test_SpecialPowersExtension2.html]
support-files = file_SpecialPowersFrame1.html

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

@ -13,6 +13,7 @@ support-files =
test_Signal.js
[test_MochiKit-Async.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_MochiKit-Base.html]
[test_MochiKit-Color.html]
[test_MochiKit-DateTime.html]

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

@ -6,4 +6,5 @@ skip-if = buildapp == 'b2g' || e10s
[test_alerts.html]
skip-if = toolkit == 'android'
[test_alerts_noobserve.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
[test_multiple_alerts.html]

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

@ -14,11 +14,11 @@
* following properties:
* - guid: (string)
* The globally unique id of the page.
* - uri: (URL)
* - url: (URL)
* or (nsIURI)
* or (string)
* The full URI of the page. Note that `PageInfo` values passed as
* argument may hold `nsIURI` or `string` values for property `uri`,
* argument may hold `nsIURI` or `string` values for property `url`,
* but `PageInfo` objects returned by this module always hold `URL`
* values.
* - title: (string)
@ -75,7 +75,33 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gNotifier",
"@mozilla.org/browser/nav-history-service;1",
Ci.nsPIPlacesHistoryListenersNotifier);
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
Cu.importGlobalProperties(["URL"]);
/**
* Shared connection
*/
XPCOMUtils.defineLazyGetter(this, "DBConnPromised",
() => new Promise((resolve) => {
Sqlite.wrapStorageConnection({ connection: PlacesUtils.history.DBConnection } )
.then(db => {
try {
Sqlite.shutdown.addBlocker("Places History.jsm: Closing database wrapper",
() => db.close());
} catch (ex) {
// It's too late to block shutdown of Sqlite, so close the connection
// immediately.
db.close();
throw ex;
}
resolve(db);
});
})
);
this.History = Object.freeze({
/**
@ -111,7 +137,7 @@ this.History = Object.freeze({
*
* @param infos: (PageInfo)
* Information on a page. This `PageInfo` MUST contain
* - either a property `guid` or a property `uri`, as specified
* - either a property `guid` or a property `url`, as specified
* by the definition of `PageInfo`;
* - a property `visits`, as specified by the definition of
* `PageInfo`, which MUST contain at least one visit.
@ -135,14 +161,14 @@ this.History = Object.freeze({
* (i.e. if page entries were updated but not created).
*
* @throws (Error)
* If the `uri` specified was for a protocol that should not be
* If the `url` specified was for a protocol that should not be
* stored (e.g. "chrome:", "mailbox:", "about:", "imap:", "news:",
* "moz-anno:", "view-source:", "resource:", "data:", "wyciwyg:",
* "javascript:", "blob:").
* @throws (Error)
* If `infos` has an unexpected type.
* @throws (Error)
* If a `PageInfo` has neither `guid` nor `uri`,
* If a `PageInfo` has neither `guid` nor `url`.
* @throws (Error)
* If a `guid` property provided is not a valid GUID.
* @throws (Error)
@ -177,12 +203,42 @@ this.History = Object.freeze({
* A promise resoled once the operation is complete.
* @resolve (bool)
* `true` if at least one page was removed, `false` otherwise.
* @throws (Error)
* @throws (TypeError)
* If `pages` has an unexpected type or if a string provided
* is neither a valid GUID nor a valid URI.
* is neither a valid GUID nor a valid URI or if `pages`
* is an empty array.
*/
remove: function (pages, onResult) {
throw new Error("Method not implemented");
remove: function (pages, onResult = null) {
// Normalize and type-check arguments
if (Array.isArray(pages)) {
if (pages.length == 0) {
throw new TypeError("Expected at least one page");
}
} else {
pages = [pages];
}
let guids = [];
let urls = [];
for (let page of pages) {
// Normalize to URL or GUID, or throw if `page` cannot
// be normalized.
let normalized = normalizeToURLOrGUID(page);
if (typeof normalized === "string") {
guids.push(normalized);
} else {
urls.push(normalized.href);
}
}
// At this stage, we know that either `guids` is not-empty
// or `urls` is not-empty.
if (onResult && typeof onResult != "function") {
throw new TypeError("Invalid function: " + onResult);
}
// Now perform queries
return remove({guids: guids, urls: urls}, onResult);
},
/**
@ -257,3 +313,154 @@ this.History = Object.freeze({
TRANSITION_FRAMED_LINK: Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
});
/**
* Normalize a key to either a string (if it is a valid GUID) or an
* instance of `URL` (if it is a `URL`, `nsIURI`, or a string
* representing a valid url).
*
* @throws (TypeError)
* If the key is neither a valid guid nor a valid url.
*/
function normalizeToURLOrGUID(key) {
if (typeof key === "string") {
// A string may be a URL or a guid
if (/^[a-zA-Z0-9\-_]{12}$/.test(key)) {
return key;
}
return new URL(key);
}
if (key instanceof URL) {
return key;
}
if (key instanceof Ci.nsIURI) {
return new URL(key.spec);
}
throw new TypeError("Invalid url or guid: " + key);
}
/**
* Convert a list of strings or numbers to its SQL
* representation as a string.
*/
function sqlList(list) {
return list.map(JSON.stringify).join();
}
/**
* Invalidate and recompute the frecency of a list of pages,
* informing frecency observers.
*
* @param db: (Sqlite connection)
* @param idList: (Array)
* The `moz_places` identifiers for the places to invalidate.
* @return (Promise)
*/
let invalidateFrecencies = Task.async(function*(db, idList) {
if (idList.length == 0) {
return;
}
let ids = sqlList(idList);
yield db.execute(
`UPDATE moz_places
SET frecency = NOTIFY_FRECENCY(
CALCULATE_FRECENCY(id), url, guid, hidden, last_visit_date
) WHERE id in (${ ids })`
);
yield db.execute(
`UPDATE moz_places
SET hidden = 0
WHERE id in (${ ids })
AND frecency <> 0`
);
});
// Inner implementation of History.remove.
let remove = Task.async(function*({guids, urls}, onResult = null) {
let db = yield DBConnPromised;
// 1. Find out what needs to be removed
let query =
`SELECT id, url, guid, foreign_count, title, frecency FROM moz_places
WHERE guid IN (${ sqlList(guids) })
OR url IN (${ sqlList(urls) })
`;
let pages = [];
let hasPagesToKeep = false;
let hasPagesToRemove = false;
yield db.execute(query, null, Task.async(function*(row) {
let toRemove = row.getResultByName("foreign_count") == 0;
if (toRemove) {
hasPagesToRemove = true;
} else {
hasPagesToKeep = true;
}
let id = row.getResultByName("id");
let guid = row.getResultByName("guid");
let url = row.getResultByName("url");
let page = {
id: id,
guid: guid,
toRemove: toRemove,
uri: NetUtil.newURI(url),
};
pages.push(page);
if (onResult) {
let pageInfo = {
guid: guid,
title: row.getResultByName("title"),
frecency: row.getResultByName("frecency"),
url: new URL(url)
};
try {
yield onResult(pageInfo);
} catch (ex) {
// Errors should be reported but should not stop `remove`.
Promise.reject(ex);
}
}
}));
if (pages.length == 0) {
// Nothing to do
return false;
}
yield db.executeTransaction(function*() {
// 2. Remove all visits to these pages.
yield db.execute(`DELETE FROM moz_historyvisits
WHERE place_id IN (${ sqlList([p.id for (p of pages)]) })
`);
// 3. For pages that should not be removed, invalidate frecencies.
if (hasPagesToKeep) {
yield invalidateFrecencies(db, [p.id for (p of pages) if (!p.toRemove)]);
}
// 4. For pages that should be removed, remove page.
if (hasPagesToRemove) {
let ids = [p.id for (p of pages) if (p.toRemove)];
yield db.execute(`DELETE FROM moz_places
WHERE id IN (${ sqlList(ids) })
`);
}
// 5. Notify observers.
for (let {guid, uri, toRemove} of pages) {
gNotifier.notifyOnPageExpired(
uri, // uri
0, // visitTime - There are no more visits
toRemove, // wholeEntry
guid, // guid
Ci.nsINavHistoryObserver.REASON_DELETED, // reason
-1 // transition
);
}
});
PlacesUtils.history.clearEmbedVisits();
return hasPagesToRemove;
});

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

@ -1176,7 +1176,7 @@ interface nsINavHistoryQueryOptions : nsISupports
nsINavHistoryQueryOptions clone();
};
[scriptable, uuid(4b6963bf-763a-4f39-9fec-25670d354dd9)]
[scriptable, uuid(47f7b08b-71e0-492e-a2be-9a9fbfc75250)]
interface nsINavHistoryService : nsISupports
{
/**
@ -1391,6 +1391,11 @@ interface nsINavHistoryService : nsISupports
* history is disabled if the places.history.enabled pref is false.
*/
readonly attribute boolean historyDisabled;
/**
* Clear all TRANSITION_EMBED visits.
*/
void clearEmbedVisits();
};
/**

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

@ -3757,6 +3757,12 @@ nsNavHistory::clearEmbedVisits() {
mEmbedVisits.Clear();
}
NS_IMETHODIMP
nsNavHistory::ClearEmbedVisits() {
clearEmbedVisits();
return NS_OK;
}
// nsNavHistory::CheckIsRecentEvent
//
// Sees if this URL happened "recently."

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

@ -16,6 +16,7 @@ XPCSHELL_TESTS_MANIFESTS += [
'network/xpcshell.ini',
'queries/xpcshell.ini',
'unifiedcomplete/xpcshell.ini',
'unit/history/xpcshell.ini',
'unit/xpcshell.ini',
'xpcshell.ini',
]

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

@ -0,0 +1,19 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
// Import common head.
let (commonFile = do_get_file("../../head_common.js", false)) {
let uri = Services.io.newFileURI(commonFile);
Services.scriptloader.loadSubScript(uri.spec, this);
};

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

@ -0,0 +1,342 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// Tests for `History.remove`, as implemented in History.jsm
"use strict";
Cu.importGlobalProperties(["URL"]);
// Test removing a single page
add_task(function* test_remove_single() {
let WITNESS_URI = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());
yield promiseAddVisits(WITNESS_URI);
Assert.ok(page_in_database(WITNESS_URI));
let remover = Task.async(function*(name, filter, options) {
do_print(name);
do_print(JSON.stringify(options));
do_print("Setting up visit");
let uri = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());
let title = "Visit " + Math.random();
yield promiseAddVisits({uri: uri, title: title});
Assert.ok(visits_in_database(uri), "History entry created");
let removeArg = yield filter(uri);
if (options.addBookmark) {
PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId,
uri,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"test bookmark");
}
let shouldRemove = !options.addBookmark;
let observer;
let promiseObserved = new Promise((resolve, reject) => {
observer = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisit: function(uri) {
reject(new Error("Unexpected call to onVisit " + uri.spec));
},
onTitleChanged: function(uri) {
reject(new Error("Unexpected call to onTitleChanged " + uri.spec));
},
onClearHistory: function() {
reject("Unexpected call to onClearHistory");
},
onPageChanged: function(uri) {
reject(new Error("Unexpected call to onPageChanged " + uri.spec));
},
onFrecencyChanged: function(aURI) {
try {
Assert.ok(!shouldRemove, "Observing onFrecencyChanged");
Assert.equal(aURI.spec, uri.spec, "Observing effect on the right uri");
} finally {
resolve();
}
},
onManyFrecenciesChanged: function() {
try {
Assert.ok(!shouldRemove, "Observing onManyFrecenciesChanged");
} finally {
resolve();
}
},
onDeleteURI: function(aURI) {
try {
Assert.ok(shouldRemove, "Observing onDeleteURI");
Assert.equal(aURI.spec, uri.spec, "Observing effect on the right uri");
} finally {
resolve();
}
},
onDeleteVisits: function(aURI) {
Assert.equal(aURI.spec, uri.spec, "Observing onDeleteVisits on the right uri");
}
};
});
PlacesUtils.history.addObserver(observer, false);
do_print("Performing removal");
let removed = false;
if (options.useCallback) {
let onRowCalled = false;
removed = yield PlacesUtils.history.remove(removeArg, page => {
Assert.equal(onRowCalled, false, "Callback has not been called yet");
onRowCalled = true;
Assert.equal(page.url.href, uri.spec, "Callback provides the correct url");
Assert.equal(page.guid, do_get_guid_for_uri(uri), "Callback provides the correct guid");
Assert.equal(page.title, title, "Callback provides the correct title");
Assert.equal(page.frecency, frecencyForUrl(uri), "Callback provides the correct frecency");
});
Assert.ok(onRowCalled, "Callback has been called");
} else {
removed = yield PlacesUtils.history.remove(removeArg);
}
yield promiseObserved;
PlacesUtils.history.removeObserver(observer);
Assert.equal(visits_in_database(uri), 0, "History entry has disappeared");
Assert.notEqual(visits_in_database(WITNESS_URI), 0, "Witness URI still has visits");
Assert.notEqual(page_in_database(WITNESS_URI), 0, "Witness URI is still here");
if (shouldRemove) {
Assert.ok(removed, "Something was removed");
Assert.equal(page_in_database(uri), 0, "Page has disappeared");
} else {
Assert.ok(!removed, "The page was not removed, as there was a bookmark");
Assert.notEqual(page_in_database(uri), 0, "The page is still present");
}
});
try {
for (let useCallback of [false, true]) {
for (let addBookmark of [false, true]) {
let options = { useCallback: useCallback, addBookmark: addBookmark };
yield remover("Testing History.remove() with a single URI", x => x, options);
yield remover("Testing History.remove() with a single string url", x => x.spec, options);
yield remover("Testing History.remove() with a single string guid", x => do_get_guid_for_uri(x), options);
yield remover("Testing History.remove() with a single URI in an array", x => [x], options);
yield remover("Testing History.remove() with a single string url in an array", x => [x.spec], options);
yield remover("Testing History.remove() with a single string guid in an array", x => [do_get_guid_for_uri(x)], options);
}
}
} finally {
yield promiseClearHistory();
}
return;
});
// Test removing a list of pages
add_task(function* test_remove_many() {
const SIZE = 10;
do_print("Adding a witness page");
let WITNESS_URI = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());;
yield promiseAddVisits(WITNESS_URI);
Assert.ok(page_in_database(WITNESS_URI), "Witness page added");
do_print("Generating samples");
let pages = [];
for (let i = 0; i < SIZE; ++i) {
let uri = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove?sample=" + i + "&salt=" + Math.random());
let title = "Visit " + i + ", " + Math.random();
let hasBookmark = i % 3 == 0;
let resolve;
let page = {
uri: uri,
title: title,
hasBookmark: hasBookmark,
// `true` once `onResult` has been called for this page
onResultCalled: false,
// `true` once `onDeleteVisits` has been called for this page
onDeleteVisitsCalled: false,
// `true` once `onFrecencyChangedCalled` has been called for this page
onFrecencyChangedCalled: false,
// `true` once `onDeleteURI` has been called for this page
onDeleteURICalled: false,
};
do_print("Pushing: " + uri.spec);
pages.push(page);
yield promiseAddVisits(page);
page.guid = do_get_guid_for_uri(uri);
if (hasBookmark) {
PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId,
uri,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"test bookmark " + i);
}
Assert.ok(page_in_database(uri), "Page added");
}
do_print("Mixing key types and introducing dangling keys");
let keys = [];
for (let i = 0; i < SIZE; ++i) {
if (i % 4 == 0) {
keys.push(pages[i].uri);
keys.push(NetUtil.newURI("http://example.org/dangling/nsIURI/" + i));
} else if (i % 4 == 1) {
keys.push(new URL(pages[i].uri.spec));
keys.push(new URL("http://example.org/dangling/URL/" + i));
} else if (i % 4 == 2) {
keys.push(pages[i].uri.spec);
keys.push("http://example.org/dangling/stringuri/" + i);
} else {
keys.push(pages[i].guid);
keys.push(("guid_" + i + "_01234567890").substr(0, 12));
}
}
let observer = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisit: function(aURI) {
Assert.ok(false, "Unexpected call to onVisit " + aURI.spec);
},
onTitleChanged: function(aURI) {
Assert.ok(false, "Unexpected call to onTitleChanged " + aURI.spec);
},
onClearHistory: function() {
Assert.ok(false, "Unexpected call to onClearHistory");
},
onPageChanged: function(aURI) {
Assert.ok(false, "Unexpected call to onPageChanged " + aURI.spec);
},
onFrecencyChanged: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(origin.hasBookmark, "Observing onFrecencyChanged on a page with a bookmark");
origin.onFrecencyChangedCalled = true;
// We do not make sure that `origin.onFrecencyChangedCalled` is `false`, as
},
onManyFrecenciesChanged: function() {
Assert.ok(false, "Observing onManyFrecenciesChanges, this is most likely correct but not covered by this test");
},
onDeleteURI: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(!origin.hasBookmark, "Observing onDeleteURI on a page without a bookmark");
Assert.ok(!origin.onDeleteURICalled, "Observing onDeleteURI for the first time");
origin.onDeleteURICalled = true;
},
onDeleteVisits: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(!origin.onDeleteVisitsCalled, "Observing onDeleteVisits for the first time");
origin.onDeleteVisitsCalled = true;
}
};
PlacesUtils.history.addObserver(observer, false);
do_print("Removing the pages and checking the callbacks");
let removed = yield PlacesUtils.history.remove(keys, page => {
let origin = pages.find(candidate => candidate.uri.spec == page.url.href);
Assert.ok(origin, "onResult has a valid page");
Assert.ok(!origin.onResultCalled, "onResult has not seen this page yet");
origin.onResultCalled = true;
Assert.equal(page.guid, origin.guid, "onResult has the right guid");
Assert.equal(page.title, origin.title, "onResult has the right title");
});
Assert.ok(removed, "Something was removed");
PlacesUtils.history.removeObserver(observer);
do_print("Checking out results");
// By now the observers should have been called.
for (let i = 0; i < pages.length; ++i) {
let page = pages[i];
do_print("Page: " + i);
Assert.ok(page.onResultCalled, "We have reached the page from the callback");
Assert.ok(visits_in_database(page.uri) == 0, "History entry has disappeared");
Assert.equal(page_in_database(page.uri) != 0, page.hasBookmark, "Page is present only if it also has bookmarks");
Assert.equal(page.onFrecencyChangedCalled, page.onDeleteVisitsCalled, "onDeleteVisits was called iff onFrecencyChanged was called");
Assert.ok(page.onFrecencyChangedCalled ^ page.onDeleteURICalled, "Either onFrecencyChanged or onDeleteURI was called");
}
Assert.notEqual(visits_in_database(WITNESS_URI), 0, "Witness URI still has visits");
Assert.notEqual(page_in_database(WITNESS_URI), 0, "Witness URI is still here");
do_print("Cleaning up");
yield promiseClearHistory();
});
// Test the various error cases
add_task(function* test_error_cases() {
Assert.throws(
() => PlacesUtils.history.remove(),
/TypeError: Invalid url/,
"History.remove with no argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(null),
/TypeError: Invalid url/,
"History.remove with `null` should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(undefined),
/TypeError: Invalid url/,
"History.remove with `undefined` should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove("not a guid, obviously"),
/TypeError: .* is not a valid URL/,
"History.remove with an ill-formed guid/url argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove({"not the kind of object we know how to handle": true}),
/TypeError: Invalid url/,
"History.remove with an unexpected object should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove([]),
/TypeError: Expected at least one page/,
"History.remove with an empty array should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove([null]),
/TypeError: Invalid url or guid/,
"History.remove with an array containing null should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["http://example.org", "not a guid, obviously"]),
/TypeError: .* is not a valid URL/,
"History.remove with an array containing an ill-formed guid/url argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["0123456789ab"/*valid guid*/, null]),
/TypeError: Invalid url or guid: null/,
"History.remove with an array containing a guid and a second argument that is null should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["http://example.org", {"not the kind of object we know how to handle": true}]),
/TypeError: Invalid url/,
"History.remove with an array containing an unexpected objecgt should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove("http://example.org", "not a function, obviously"),
/TypeError: Invalid function/,
"History.remove with a second argument that is not a function argument should throw a TypeError"
);
try {
PlacesUtils.history.remove("http://example.org/I/have/clearly/not/been/added", null);
Assert.ok(true, "History.remove should ignore `null` as a second argument");
} catch (ex) {
Assert.ok(false, "History.remove should ignore `null` as a second argument");
}
});
function run_test() {
run_next_test();
}

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

@ -0,0 +1,5 @@
[DEFAULT]
head = head_history.js
tail =
[test_remove.js]

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

@ -18,8 +18,20 @@ interface nsIToolkitProfileService : nsISupports
readonly attribute nsISimpleEnumerator /*nsIToolkitProfile*/ profiles;
/**
* The currently selected profile (the one used or about to be used by the
* browser).
*/
attribute nsIToolkitProfile selectedProfile;
/**
* The default profile (the one used or about to be used by the
* browser if no other profile is specified at runtime). This is the profile
* marked with Default=1 in profiles.ini and is usually the same as
* selectedProfile, except on Developer Edition.
*/
attribute nsIToolkitProfile defaultProfile;
/**
* Get a profile by name. This is mainly for use by the -P
* commandline flag.

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

@ -139,6 +139,7 @@ private:
nsRefPtr<nsToolkitProfile> mFirst;
nsCOMPtr<nsIToolkitProfile> mChosen;
nsCOMPtr<nsIToolkitProfile> mDefault;
nsCOMPtr<nsIFile> mAppData;
nsCOMPtr<nsIFile> mTempData;
nsCOMPtr<nsIFile> mListFile;
@ -424,6 +425,7 @@ nsToolkitProfileService::Init()
nsToolkitProfile* currentProfile = nullptr;
unsigned int c = 0;
bool foundAuroraDefault = false;
for (c = 0; true; ++c) {
nsAutoCString profileID("Profile");
profileID.AppendInt(c);
@ -441,7 +443,9 @@ nsToolkitProfileService::Init()
continue;
}
rv = parser.GetString(profileID.get(), "Name", buffer);
nsAutoCString name;
rv = parser.GetString(profileID.get(), "Name", name);
if (NS_FAILED(rv)) {
NS_ERROR("Malformed profiles.ini: Name= not found");
continue;
@ -470,15 +474,48 @@ nsToolkitProfileService::Init()
localDir = rootDir;
}
currentProfile = new nsToolkitProfile(buffer,
currentProfile = new nsToolkitProfile(name,
rootDir, localDir,
currentProfile, false);
NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
rv = parser.GetString(profileID.get(), "Default", buffer);
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1"))
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1") && !foundAuroraDefault) {
mChosen = currentProfile;
this->SetDefaultProfile(currentProfile);
}
#ifdef MOZ_DEV_EDITION
// Use the dev-edition-default profile if this is an Aurora build.
if (name.EqualsLiteral("dev-edition-default")) {
mChosen = currentProfile;
foundAuroraDefault = true;
}
#endif
}
#ifdef MOZ_DEV_EDITION
// Check if we are running Firefox, as we don't want to create a profile
// on webapprt.
bool isFirefox = strcmp(gAppData->ID,
"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") == 0;
if (!foundAuroraDefault && isFirefox) {
// If a single profile exists, it may not be already marked as default.
// Do it now to avoid problems when we create the dev-edition-default profile.
if (!mChosen && mFirst && !mFirst->mNext)
this->SetDefaultProfile(mFirst);
// Create a default profile for aurora, if none was found.
nsCOMPtr<nsIToolkitProfile> profile;
rv = CreateProfile(nullptr,
NS_LITERAL_CSTRING("dev-edition-default"),
getter_AddRefs(profile));
if (NS_FAILED(rv)) return rv;
mChosen = profile;
rv = Flush();
if (NS_FAILED(rv)) return rv;
}
#endif
if (!mChosen && mFirst && !mFirst->mNext) // only one profile
mChosen = mFirst;
return NS_OK;
@ -569,6 +606,25 @@ nsToolkitProfileService::SetSelectedProfile(nsIToolkitProfile* aProfile)
return NS_OK;
}
NS_IMETHODIMP
nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile* *aResult)
{
if (!mDefault) return NS_ERROR_FAILURE;
NS_ADDREF(*aResult = mDefault);
return NS_OK;
}
NS_IMETHODIMP
nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile)
{
if (mDefault != aProfile) {
mDefault = aProfile;
mDirty = true;
}
return NS_OK;
}
NS_IMETHODIMP
nsToolkitProfileService::GetProfileByName(const nsACString& aName,
nsIToolkitProfile* *aResult)
@ -932,7 +988,9 @@ nsToolkitProfileService::Flush()
pCount, cur->mName.get(),
isRelative ? "1" : "0", path.get());
if (mChosen == cur) {
nsCOMPtr<nsIToolkitProfile> profile;
rv = this->GetDefaultProfile(getter_AddRefs(profile));
if (NS_SUCCEEDED(rv) && profile == cur) {
end += sprintf(end, "Default=1\n");
}

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

@ -101,13 +101,14 @@ XRE_ParseAppData(nsIFile* aINIFile, nsXREAppData *aAppData)
nsCString str;
ReadString strings[] = {
{ "App", "Vendor", &aAppData->vendor },
{ "App", "Name", &aAppData->name },
{ "App", "Version", &aAppData->version },
{ "App", "BuildID", &aAppData->buildID },
{ "App", "ID", &aAppData->ID },
{ "App", "Copyright", &aAppData->copyright },
{ "App", "Profile", &aAppData->profile },
{ "App", "Vendor", &aAppData->vendor },
{ "App", "Name", &aAppData->name },
{ "App", "RemotingName", &aAppData->remotingName },
{ "App", "Version", &aAppData->version },
{ "App", "BuildID", &aAppData->buildID },
{ "App", "ID", &aAppData->ID },
{ "App", "Copyright", &aAppData->copyright },
{ "App", "Profile", &aAppData->profile },
{ nullptr }
};
ReadStrings(parser, strings);

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

@ -1594,7 +1594,7 @@ RemoteCommandLine(const char* aDesktopStartupID)
nsresult rv;
ArgResult ar;
nsAutoCString program(gAppData->name);
nsAutoCString program(gAppData->remotingName);
ToLowerCase(program);
const char *username = getenv("LOGNAME");
@ -4078,7 +4078,7 @@ XREMain::XRE_mainRun()
if (!mDisableRemote)
mRemoteService = do_GetService("@mozilla.org/toolkit/remote-service;1");
if (mRemoteService)
mRemoteService->Startup(mAppData->name, mProfileName.get());
mRemoteService->Startup(mAppData->remotingName, mProfileName.get());
#endif /* MOZ_ENABLE_XREMOTE */
mNativeApp->Enable();
@ -4126,6 +4126,9 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
mAppData = new ScopedAppData(aAppData);
if (!mAppData)
return 1;
if (!mAppData->remotingName) {
SetAllocatedString(mAppData->remotingName, mAppData->name);
}
// used throughout this file
gAppData = mAppData;

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

@ -484,7 +484,7 @@ struct MessageWindow {
::_snwprintf(classNameBuffer,
128, // size of classNameBuffer in PRUnichars
L"%s%s",
NS_ConvertUTF8toUTF16(gAppData->name).get(),
NS_ConvertUTF8toUTF16(gAppData->remotingName).get(),
L"MessageWindow" );
mClassName = classNameBuffer;
}

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

@ -46,6 +46,13 @@ struct nsXREAppData
*/
const char* name;
/**
* The internal name of the application for remoting purposes. When left
* unspecified, "name" is used instead. This must be ASCII, and is normally
* lowercase, e.g. "firefox". Optional (may be null but not an empty string).
*/
const char* remotingName;
/**
* The major version, e.g. "0.8.0+". Optional (may be null), but
* required for advanced application features such as the extension

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

@ -43,6 +43,7 @@ ScopedAppData::ScopedAppData(const nsXREAppData* aAppData)
SetAllocatedString(this->vendor, aAppData->vendor);
SetAllocatedString(this->name, aAppData->name);
SetAllocatedString(this->remotingName, aAppData->remotingName);
SetAllocatedString(this->version, aAppData->version);
SetAllocatedString(this->buildID, aAppData->buildID);
SetAllocatedString(this->ID, aAppData->ID);
@ -70,6 +71,7 @@ ScopedAppData::~ScopedAppData()
{
SetAllocatedString(this->vendor, nullptr);
SetAllocatedString(this->name, nullptr);
SetAllocatedString(this->remotingName, nullptr);
SetAllocatedString(this->version, nullptr);
SetAllocatedString(this->buildID, nullptr);
SetAllocatedString(this->ID, nullptr);