Merge mozilla-central to b2g-inbound
@ -121,7 +121,7 @@ skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test un
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
[browser_alltabslistener.js]
|
||||
[browser_autocomplete_a11y_label.js]
|
||||
skip-if = e10s # Bug ????? - no e10s switch-to-tab support yet
|
||||
skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
|
||||
[browser_backButtonFitts.js]
|
||||
skip-if = os != "win" || e10s # The Fitts Law back button is only supported on Windows (bug 571454) / e10s - Bug 1099154: test touches content (attempts to add an event listener directly to the contentWindow)
|
||||
[browser_blob-channelname.js]
|
||||
@ -158,7 +158,7 @@ skip-if = true # bug 428712
|
||||
[browser_bug424101.js]
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_bug427559.js]
|
||||
skip-if = e10s # Bug ?????? - "content window is focused - Got [object ChromeWindow], expected [object XrayWrapper [object Window]]"
|
||||
skip-if = e10s # Bug 1102015 - "content window is focused - Got [object ChromeWindow], expected [object CPOW [object Window]]"
|
||||
[browser_bug431826.js]
|
||||
[browser_bug432599.js]
|
||||
[browser_bug435035.js]
|
||||
@ -171,7 +171,7 @@ skip-if = e10s
|
||||
[browser_bug460146.js]
|
||||
skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
|
||||
[browser_bug462289.js]
|
||||
skip-if = toolkit == "cocoa" || e10s # Bug ?????? - not sure why this is timing out and crashing!!
|
||||
skip-if = toolkit == "cocoa" || e10s # Bug 1102017 - middle-button mousedown on selected tab2 does not activate tab - Didn't expect [object XULElement], but got it
|
||||
[browser_bug462673.js]
|
||||
skip-if = e10s # Bug 1093404 - test expects sync window opening from content and is disappointed in that expectation
|
||||
[browser_bug477014.js]
|
||||
@ -179,7 +179,7 @@ skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoade
|
||||
[browser_bug479408.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_bug481560.js]
|
||||
skip-if = e10s # Bug ????? - This bug attached an event listener directly to the content
|
||||
skip-if = e10s # Bug 1102018 - This bug attaches an event listener directly to the content, which then never gets called.
|
||||
[browser_bug484315.js]
|
||||
skip-if = e10s
|
||||
[browser_bug491431.js]
|
||||
@ -196,7 +196,7 @@ skip-if = e10s # Bug ?????? - some weird timing issue with progress listeners th
|
||||
[browser_bug537013.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls replaceTabWithWindow)
|
||||
[browser_bug537474.js]
|
||||
skip-if = e10s # Bug ?????? - test doesn't wait for document to be created before it checks it
|
||||
skip-if = e10s # Bug 1102020 - test tries to use browserDOMWindow.openURI to open a link, and gets a null rv where it expects a window
|
||||
[browser_bug550565.js]
|
||||
[browser_bug553455.js]
|
||||
skip-if = true # Bug 1094312
|
||||
@ -331,7 +331,7 @@ skip-if = buildapp == 'mulet'
|
||||
[browser_identity_UI.js]
|
||||
skip-if = e10s # Bug ?????? - this test fails for obscure reasons on non-windows builds only.
|
||||
[browser_keywordBookmarklets.js]
|
||||
skip-if = e10s # Bug ?????? - this test fails for obscure reasons on non-windows builds only.
|
||||
skip-if = e10s # Bug 1102025 - different principals for the bookmarklet only in e10s mode (unclear if test or 'real' issue)
|
||||
[browser_keywordSearch.js]
|
||||
skip-if = e10s # Bug 921957 - remote webprogress doesn't supply cancel method on the request object
|
||||
[browser_keywordSearch_postData.js]
|
||||
@ -369,7 +369,7 @@ skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1100707 - test fails in e10s because it can't get accel-w to close the popup (?)
|
||||
[browser_popup_blocker.js]
|
||||
[browser_printpreview.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug ?????? - timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1101973 - breaks the next test in e10s, and may be responsible for later timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
|
||||
[browser_private_browsing_window.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_private_no_prompt.js]
|
||||
|
@ -160,10 +160,6 @@ let LoopRoomsInternal = {
|
||||
}
|
||||
|
||||
Task.spawn(function* () {
|
||||
let deferredInitialization = Promise.defer();
|
||||
MozLoopService.delayedInitialize(deferredInitialization);
|
||||
yield deferredInitialization.promise;
|
||||
|
||||
if (!gDirty) {
|
||||
callback(null, [...this.rooms.values()]);
|
||||
return;
|
||||
@ -200,6 +196,12 @@ let LoopRoomsInternal = {
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no rooms in the list, remove the guest created room flag, so that
|
||||
// we don't keep registering for guest when we don't need to.
|
||||
if (this.sessionType == LOOP_SESSION_TYPE.GUEST && !this.rooms.size) {
|
||||
this.setGuestCreatedRoom(false);
|
||||
}
|
||||
|
||||
// Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
|
||||
gDirty = false;
|
||||
callback(null, [...this.rooms.values()]);
|
||||
@ -269,11 +271,39 @@ let LoopRoomsInternal = {
|
||||
delete room.expiresIn;
|
||||
this.rooms.set(room.roomToken, room);
|
||||
|
||||
if (this.sessionType == LOOP_SESSION_TYPE.GUEST) {
|
||||
this.setGuestCreatedRoom(true);
|
||||
}
|
||||
|
||||
eventEmitter.emit("add", room);
|
||||
callback(null, room);
|
||||
}, error => callback(error)).catch(error => callback(error));
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets whether or not the user has created a room in guest mode.
|
||||
*
|
||||
* @param {Boolean} created If the user has created the room.
|
||||
*/
|
||||
setGuestCreatedRoom: function(created) {
|
||||
if (created) {
|
||||
Services.prefs.setBoolPref("loop.createdRoom", created);
|
||||
} else {
|
||||
Services.prefs.clearUserPref("loop.createdRoom");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the user has a created room in guest mode.
|
||||
*/
|
||||
getGuestCreatedRoom: function() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("loop.createdRoom");
|
||||
} catch (x) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
open: function(roomToken) {
|
||||
let windowData = {
|
||||
roomToken: roomToken,
|
||||
@ -486,6 +516,10 @@ this.LoopRooms = {
|
||||
return LoopRoomsInternal.rename(roomToken, newRoomName, callback);
|
||||
},
|
||||
|
||||
getGuestCreatedRoom: function() {
|
||||
return LoopRoomsInternal.getGuestCreatedRoom();
|
||||
},
|
||||
|
||||
promise: function(method, ...params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this[method](...params, (error, result) => {
|
||||
|
@ -367,32 +367,6 @@ function injectLoopAPI(targetWindow) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Call to ensure that any necessary registrations for the Loop Service
|
||||
* have taken place.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType
|
||||
* @param {Function} callback Will be called once registration is complete,
|
||||
* or straight away if registration has already
|
||||
* happened.
|
||||
*/
|
||||
ensureRegistered: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(sessionType, callback) {
|
||||
// We translate from a promise to a callback, as we can't pass promises from
|
||||
// Promise.jsm across the priv versus unpriv boundary.
|
||||
MozLoopService.promiseRegisteredWithServers(sessionType).then(() => {
|
||||
callback(null);
|
||||
}, err => {
|
||||
callback(cloneValueInto(err, targetWindow));
|
||||
}).catch(Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to note a call url expiry time. If the time is later than the current
|
||||
* latest expiry time, then the stored expiry time is increased. For times
|
||||
|
@ -419,7 +419,8 @@ let MozLoopServiceInternal = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs a hawk based request to the loop server.
|
||||
* Performs a hawk based request to the loop server - there is no pre-registration
|
||||
* for this request, if this is required, use hawkRequest.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session to use for the request.
|
||||
* This is one of the LOOP_SESSION_TYPE members.
|
||||
@ -433,8 +434,7 @@ let MozLoopServiceInternal = {
|
||||
* as JSON and contains an 'error' property, the promise will be
|
||||
* rejected with this JSON-parsed response.
|
||||
*/
|
||||
hawkRequest: function(sessionType, path, method, payloadObj) {
|
||||
log.debug("hawkRequest: " + path, sessionType);
|
||||
hawkRequestInternal: function(sessionType, path, method, payloadObj) {
|
||||
if (!gHawkClient) {
|
||||
gHawkClient = new HawkClient(this.loopServerUri);
|
||||
}
|
||||
@ -479,6 +479,32 @@ let MozLoopServiceInternal = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs a hawk based request to the loop server, registering if necessary.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session to use for the request.
|
||||
* This is one of the LOOP_SESSION_TYPE members.
|
||||
* @param {String} path The path to make the request to.
|
||||
* @param {String} method The request method, e.g. 'POST', 'GET'.
|
||||
* @param {Object} payloadObj An object which is converted to JSON and
|
||||
* transmitted with the request.
|
||||
* @returns {Promise}
|
||||
* Returns a promise that resolves to the response of the API call,
|
||||
* or is rejected with an error. If the server response can be parsed
|
||||
* as JSON and contains an 'error' property, the promise will be
|
||||
* rejected with this JSON-parsed response.
|
||||
*/
|
||||
hawkRequest: function(sessionType, path, method, payloadObj) {
|
||||
log.debug("hawkRequest: " + path, sessionType);
|
||||
return new Promise((resolve, reject) => {
|
||||
MozLoopService.promiseRegisteredWithServers(sessionType).then(() => {
|
||||
this.hawkRequestInternal(sessionType, path, method, payloadObj).then(resolve, reject);
|
||||
}, err => {
|
||||
reject(err);
|
||||
}).catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Generic hawkRequest onError handler for the hawkRequest promise.
|
||||
*
|
||||
@ -581,7 +607,7 @@ let MozLoopServiceInternal = {
|
||||
rooms: roomsPushURL,
|
||||
},
|
||||
};
|
||||
return this.hawkRequest(sessionType, "/registration", "POST", msg)
|
||||
return this.hawkRequestInternal(sessionType, "/registration", "POST", msg)
|
||||
.then((response) => {
|
||||
// If this failed we got an invalid token.
|
||||
if (!this.storeSessionToken(sessionType, response.headers)) {
|
||||
@ -637,7 +663,7 @@ let MozLoopServiceInternal = {
|
||||
}
|
||||
|
||||
let unregisterURL = "/registration?simplePushURL=" + encodeURIComponent(pushURL);
|
||||
return this.hawkRequest(sessionType, unregisterURL, "DELETE")
|
||||
return this.hawkRequestInternal(sessionType, unregisterURL, "DELETE")
|
||||
.then(() => {
|
||||
log.debug("Successfully unregistered from server for sessionType", sessionType);
|
||||
},
|
||||
@ -826,7 +852,7 @@ let MozLoopServiceInternal = {
|
||||
*/
|
||||
promiseFxAOAuthParameters: function() {
|
||||
const SESSION_TYPE = LOOP_SESSION_TYPE.FXA;
|
||||
return this.hawkRequest(SESSION_TYPE, "/fxa-oauth/params", "POST").then(response => {
|
||||
return this.hawkRequestInternal(SESSION_TYPE, "/fxa-oauth/params", "POST").then(response => {
|
||||
if (!this.storeSessionToken(SESSION_TYPE, response.headers)) {
|
||||
throw new Error("Invalid FxA hawk token returned");
|
||||
}
|
||||
@ -1026,6 +1052,7 @@ this.MozLoopService = {
|
||||
// If expiresTime is not in the future and the user hasn't
|
||||
// previously authenticated then skip registration.
|
||||
if (!MozLoopServiceInternal.urlExpiryTimeIsInFuture() &&
|
||||
!LoopRooms.getGuestCreatedRoom() &&
|
||||
!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
return Promise.resolve("registration not needed");
|
||||
}
|
||||
@ -1059,7 +1086,8 @@ this.MozLoopService = {
|
||||
});
|
||||
|
||||
try {
|
||||
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
|
||||
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture() ||
|
||||
LoopRooms.getGuestCreatedRoom()) {
|
||||
yield this.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
} else {
|
||||
log.debug("delayedInitialize: URL expiry time isn't in the future so not registering as a guest");
|
||||
|
@ -82,40 +82,29 @@ loop.Client = (function($) {
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures the client is registered with the push server.
|
||||
* Requests a call URL from the Loop server. It will note the
|
||||
* expiry time for the url with the mozLoop api. It will select the
|
||||
* appropriate hawk session to use based on whether or not the user
|
||||
* is currently logged into a Firefox account profile.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType Guest or FxA
|
||||
* @param {Function} cb Callback(err)
|
||||
*/
|
||||
_ensureRegistered: function(sessionType, cb) {
|
||||
this.mozLoop.ensureRegistered(sessionType, function(error) {
|
||||
if (error) {
|
||||
console.log("Error registering with Loop server, code: " + error);
|
||||
cb(error);
|
||||
return;
|
||||
} else {
|
||||
cb(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal handler for requesting a call url from the server.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
* - err null on successful request, non-null otherwise.
|
||||
* - callUrlData an object of the obtained call url data if successful:
|
||||
* -- callUrl: The url of the call
|
||||
* -- expiresAt: The amount of hours until expiry of the url
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType
|
||||
* @param {String} simplepushUrl a registered Simple Push URL
|
||||
* @param {string} nickname the nickname of the future caller
|
||||
* @param {Function} cb Callback(err, callUrlData)
|
||||
*/
|
||||
_requestCallUrlInternal: function(sessionType, nickname, cb) {
|
||||
requestCallUrl: function(nickname, cb) {
|
||||
var sessionType;
|
||||
if (this.mozLoop.userProfile) {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
this.mozLoop.hawkRequest(sessionType, "/call-url/", "POST",
|
||||
{callerId: nickname},
|
||||
function (error, responseText) {
|
||||
@ -154,17 +143,6 @@ loop.Client = (function($) {
|
||||
* it does not make sense to display an error.
|
||||
**/
|
||||
deleteCallUrl: function(token, sessionType, cb) {
|
||||
this._ensureRegistered(sessionType, function(err) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this._deleteCallUrlInternal(token, sessionType, cb);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_deleteCallUrlInternal: function(token, sessionType, cb) {
|
||||
function deleteRequestCallback(error, responseText) {
|
||||
if (error) {
|
||||
this._failureHandler(cb, error);
|
||||
@ -184,40 +162,6 @@ loop.Client = (function($) {
|
||||
deleteRequestCallback.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests a call URL from the Loop server. It will note the
|
||||
* expiry time for the url with the mozLoop api. It will select the
|
||||
* appropriate hawk session to use based on whether or not the user
|
||||
* is currently logged into a Firefox account profile.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
* - callUrlData an object of the obtained call url data if successful:
|
||||
* -- callUrl: The url of the call
|
||||
* -- expiresAt: The amount of hours until expiry of the url
|
||||
*
|
||||
* @param {String} simplepushUrl a registered Simple Push URL
|
||||
* @param {string} nickname the nickname of the future caller
|
||||
* @param {Function} cb Callback(err, callUrlData)
|
||||
*/
|
||||
requestCallUrl: function(nickname, cb) {
|
||||
var sessionType;
|
||||
if (this.mozLoop.userProfile) {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
this._ensureRegistered(sessionType, function(err) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this._requestCallUrlInternal(sessionType, nickname, cb);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up an outgoing call, getting the relevant data from the server.
|
||||
*
|
||||
|
@ -32,7 +32,6 @@ describe("loop.Client", function() {
|
||||
.returns(null)
|
||||
.withArgs("hawk-session-token")
|
||||
.returns(fakeToken),
|
||||
ensureRegistered: sinon.stub().callsArgWith(1, null),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
hawkRequest: sinon.stub(),
|
||||
LOOP_SESSION_TYPE: {
|
||||
@ -55,21 +54,6 @@ describe("loop.Client", function() {
|
||||
|
||||
describe("loop.Client", function() {
|
||||
describe("#deleteCallUrl", function() {
|
||||
it("should ensure loop is registered", function() {
|
||||
client.deleteCallUrl("fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, callback);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.ensureRegistered);
|
||||
});
|
||||
|
||||
it("should send an error when registration fails", function() {
|
||||
mozLoop.ensureRegistered.callsArgWith(1, "offline");
|
||||
|
||||
client.deleteCallUrl("fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithExactly(callback, "offline");
|
||||
});
|
||||
|
||||
it("should make a delete call to /call-url/{fakeToken}", function() {
|
||||
client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.GUEST, callback);
|
||||
|
||||
@ -106,21 +90,6 @@ describe("loop.Client", function() {
|
||||
});
|
||||
|
||||
describe("#requestCallUrl", function() {
|
||||
it("should ensure loop is registered", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.ensureRegistered);
|
||||
});
|
||||
|
||||
it("should send an error when registration fails", function() {
|
||||
mozLoop.ensureRegistered.callsArgWith(1, "offline");
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithExactly(callback, "offline");
|
||||
});
|
||||
|
||||
it("should post to /call-url/", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
|
@ -551,7 +551,7 @@ describe("loop.conversation", function() {
|
||||
});
|
||||
|
||||
describe("#blocked", function() {
|
||||
var mozLoop;
|
||||
var mozLoop, deleteCallUrlStub;
|
||||
|
||||
beforeEach(function() {
|
||||
icView = mountTestComponent();
|
||||
@ -568,6 +568,9 @@ describe("loop.conversation", function() {
|
||||
FXA: 2
|
||||
}
|
||||
};
|
||||
|
||||
deleteCallUrlStub = sandbox.stub(loop.Client.prototype,
|
||||
"deleteCallUrl");
|
||||
});
|
||||
|
||||
it("should call mozLoop.stopAlerting", function() {
|
||||
@ -582,12 +585,10 @@ describe("loop.conversation", function() {
|
||||
.withArgs("sessionType")
|
||||
.returns(mozLoop.LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
var deleteCallUrl = sandbox.stub(loop.Client.prototype,
|
||||
"deleteCallUrl");
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.calledOnce(deleteCallUrl);
|
||||
sinon.assert.calledWithExactly(deleteCallUrl,
|
||||
sinon.assert.calledOnce(deleteCallUrlStub);
|
||||
sinon.assert.calledWithExactly(deleteCallUrlStub,
|
||||
"fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, sinon.match.func);
|
||||
});
|
||||
|
||||
@ -606,9 +607,7 @@ describe("loop.conversation", function() {
|
||||
var fakeError = {
|
||||
error: true
|
||||
};
|
||||
sandbox.stub(loop.Client.prototype, "deleteCallUrl", function(_, __, cb) {
|
||||
cb(fakeError);
|
||||
});
|
||||
deleteCallUrlStub.callsArgWith(2, fakeError);
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.calledOnce(log);
|
||||
|
@ -37,7 +37,7 @@ add_task(function* setup_server() {
|
||||
|
||||
add_task(function* error_offline() {
|
||||
Services.io.offline = true;
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
MozLoopServiceInternal.setError("testing", error);
|
||||
@ -58,7 +58,7 @@ add_task(cleanup_between_tests);
|
||||
add_task(function* guest_401() {
|
||||
Services.prefs.setCharPref("loop.hawk-session-token", "guest");
|
||||
Services.prefs.setCharPref("loop.hawk-session-token.fxa", "fxa");
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/401", "POST").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/401", "POST").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
Assert.strictEqual(Services.prefs.getPrefType("loop.hawk-session-token"),
|
||||
@ -83,7 +83,7 @@ add_task(cleanup_between_tests);
|
||||
add_task(function* fxa_401() {
|
||||
Services.prefs.setCharPref("loop.hawk-session-token", "guest");
|
||||
Services.prefs.setCharPref("loop.hawk-session-token.fxa", "fxa");
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.FXA, "/401", "POST").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.FXA, "/401", "POST").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
Assert.strictEqual(Services.prefs.getCharPref("loop.hawk-session-token"),
|
||||
@ -105,7 +105,7 @@ add_task(function* fxa_401() {
|
||||
add_task(cleanup_between_tests);
|
||||
|
||||
add_task(function* error_404() {
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/404", "GET").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/404", "GET").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
MozLoopServiceInternal.setError("testing", error);
|
||||
@ -122,7 +122,7 @@ add_task(function* error_404() {
|
||||
add_task(cleanup_between_tests);
|
||||
|
||||
add_task(function* error_500() {
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
MozLoopServiceInternal.setError("testing", error);
|
||||
@ -139,7 +139,7 @@ add_task(function* error_500() {
|
||||
add_task(cleanup_between_tests);
|
||||
|
||||
add_task(function* profile_500() {
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/500", "GET").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
MozLoopServiceInternal.setError("profile", error);
|
||||
@ -156,7 +156,7 @@ add_task(function* profile_500() {
|
||||
add_task(cleanup_between_tests);
|
||||
|
||||
add_task(function* error_503() {
|
||||
yield MozLoopService.hawkRequest(LOOP_SESSION_TYPE.GUEST, "/503", "GET").then(
|
||||
yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/503", "GET").then(
|
||||
() => Assert.ok(false, "Should have rejected"),
|
||||
(error) => {
|
||||
MozLoopServiceInternal.setError("testing", error);
|
||||
|
@ -76,7 +76,10 @@ function runTests1(aTab) {
|
||||
|
||||
gDevTools.unregisterTool(toolId1);
|
||||
|
||||
runTests2();
|
||||
// Wait for unregisterTool to select the next tool before calling runTests2,
|
||||
// otherwise we will receive the wrong select event when waiting for
|
||||
// unregisterTool to select the next tool in continueTests below.
|
||||
toolbox.once("select", runTests2);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -92,8 +92,10 @@ VIAddVersionKey "ProductVersion" "${AppVersion}"
|
||||
# VIAddVersionKey "Comments" "Comments"
|
||||
|
||||
# It isn't possible to get the size of the installation prior to downloading
|
||||
# so the stub installer uses an estimate.
|
||||
!define APPROXIMATE_REQUIRED_SPACE_MB "42.2"
|
||||
# so the stub installer uses an estimate. The size is derived from the size of
|
||||
# the complete installer, the size of the extracted complete installer, and at
|
||||
# least 15 MB additional for working room.
|
||||
!define APPROXIMATE_REQUIRED_SPACE_MB "145"
|
||||
|
||||
# Control positions in Dialog Units so they are placed correctly with
|
||||
# non-default DPI settings
|
||||
|
@ -1747,44 +1747,24 @@ Function UpdateFreeSpaceLabel
|
||||
|
||||
${If} $0 > 1024
|
||||
${OrIf} $0 < 0
|
||||
; Multiply by 10 so it is possible to display a decimal in the size
|
||||
System::Int64Op $0 * 10
|
||||
Pop $0
|
||||
System::Int64Op $0 / 1024
|
||||
Pop $0
|
||||
StrCpy $1 "$(KILO)$(BYTE)"
|
||||
${If} $0 > 10240
|
||||
${If} $0 > 1024
|
||||
${OrIf} $0 < 0
|
||||
System::Int64Op $0 / 1024
|
||||
Pop $0
|
||||
StrCpy $1 "$(MEGA)$(BYTE)"
|
||||
${If} $0 > 10240
|
||||
${If} $0 > 1024
|
||||
${OrIf} $0 < 0
|
||||
System::Int64Op $0 / 1024
|
||||
Pop $0
|
||||
StrCpy $1 "$(GIGA)$(BYTE)"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
StrLen $3 "$0"
|
||||
${If} $3 > 1
|
||||
StrCpy $2 "$0" -1 ; All characters except the last one
|
||||
StrCpy $0 "$0" "" -1 ; The last character
|
||||
${If} "$0" == "0"
|
||||
StrCpy $0 "$2" ; Don't display the decimal if it is 0
|
||||
${Else}
|
||||
StrCpy $0 "$2.$0"
|
||||
${EndIf}
|
||||
${ElseIf} $3 == 1
|
||||
StrCpy $0 "0.$0"
|
||||
${Else}
|
||||
; This should never happen
|
||||
System::Int64Op $0 / 10
|
||||
Pop $0
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
SendMessage $LabelFreeSpace ${WM_SETTEXT} 0 "STR:$0 $1"
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function OnChange_DirRequest
|
||||
|
@ -550,59 +550,6 @@ ConvertActorsToBlobs(IDBDatabase* aDatabase,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DispatchSuccessEvent(ResultHelper* aResultHelper,
|
||||
nsIDOMEvent* aEvent = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aResultHelper);
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"DispatchSuccessEvent",
|
||||
js::ProfileEntry::Category::STORAGE);
|
||||
|
||||
nsRefPtr<IDBRequest> request = aResultHelper->Request();
|
||||
MOZ_ASSERT(request);
|
||||
request->AssertIsOnOwningThread();
|
||||
|
||||
nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> successEvent;
|
||||
if (!aEvent) {
|
||||
successEvent = CreateGenericEvent(request,
|
||||
nsDependentString(kSuccessEventType),
|
||||
eDoesNotBubble,
|
||||
eNotCancelable);
|
||||
if (NS_WARN_IF(!successEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent = successEvent;
|
||||
}
|
||||
|
||||
request->SetResultCallback(aResultHelper);
|
||||
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT_IF(transaction, transaction->IsOpen());
|
||||
|
||||
bool dummy;
|
||||
nsresult rv = request->DispatchEvent(aEvent, &dummy);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(transaction,
|
||||
transaction->IsOpen() || transaction->IsAborted());
|
||||
|
||||
WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
|
||||
MOZ_ASSERT(internalEvent);
|
||||
|
||||
if (transaction &&
|
||||
transaction->IsOpen() &&
|
||||
internalEvent->mFlags.mExceptionHasBeenRisen) {
|
||||
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DispatchErrorEvent(IDBRequest* aRequest,
|
||||
nsresult aErrorCode,
|
||||
@ -662,6 +609,64 @@ DispatchErrorEvent(IDBRequest* aRequest,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DispatchSuccessEvent(ResultHelper* aResultHelper,
|
||||
nsIDOMEvent* aEvent = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aResultHelper);
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"DispatchSuccessEvent",
|
||||
js::ProfileEntry::Category::STORAGE);
|
||||
|
||||
nsRefPtr<IDBRequest> request = aResultHelper->Request();
|
||||
MOZ_ASSERT(request);
|
||||
request->AssertIsOnOwningThread();
|
||||
|
||||
nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
|
||||
|
||||
if (transaction && transaction->IsAborted()) {
|
||||
DispatchErrorEvent(request, transaction->AbortCode(), transaction);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> successEvent;
|
||||
if (!aEvent) {
|
||||
successEvent = CreateGenericEvent(request,
|
||||
nsDependentString(kSuccessEventType),
|
||||
eDoesNotBubble,
|
||||
eNotCancelable);
|
||||
if (NS_WARN_IF(!successEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent = successEvent;
|
||||
}
|
||||
|
||||
request->SetResultCallback(aResultHelper);
|
||||
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT_IF(transaction, transaction->IsOpen());
|
||||
|
||||
bool dummy;
|
||||
nsresult rv = request->DispatchEvent(aEvent, &dummy);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(transaction,
|
||||
transaction->IsOpen() || transaction->IsAborted());
|
||||
|
||||
WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
|
||||
MOZ_ASSERT(internalEvent);
|
||||
|
||||
if (transaction &&
|
||||
transaction->IsOpen() &&
|
||||
internalEvent->mFlags.mExceptionHasBeenRisen) {
|
||||
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -189,6 +189,13 @@ public:
|
||||
return NS_FAILED(mAbortCode);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AbortCode() const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
return mAbortCode;
|
||||
}
|
||||
|
||||
void
|
||||
GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
|
||||
|
||||
|
@ -1669,6 +1669,7 @@ MediaCache::NoteSeek(MediaCacheStream* aStream, int64_t aOldOffset)
|
||||
std::min<int64_t>((aOldOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
|
||||
aStream->mBlocks.Length());
|
||||
while (blockIndex < endIndex) {
|
||||
MOZ_ASSERT(endIndex > 0);
|
||||
int32_t cacheBlockIndex = aStream->mBlocks[endIndex - 1];
|
||||
if (cacheBlockIndex >= 0) {
|
||||
BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
|
||||
|
@ -418,14 +418,10 @@ void
|
||||
MediaSource::DurationChange(double aOldDuration, double aNewDuration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("MediaSource(%p)::DurationChange(aNewDuration=%f)", this, aNewDuration);
|
||||
MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
|
||||
|
||||
if (aNewDuration < aOldDuration) {
|
||||
ErrorResult rv;
|
||||
mSourceBuffers->Remove(aNewDuration, aOldDuration, rv);
|
||||
if (rv.Failed()) {
|
||||
return;
|
||||
}
|
||||
mSourceBuffers->RangeRemoval(aNewDuration, aOldDuration);
|
||||
}
|
||||
// TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
|
||||
}
|
||||
|
@ -188,10 +188,19 @@ SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
|
||||
if (mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
|
||||
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
|
||||
}
|
||||
RangeRemoval(aStart, aEnd);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::RangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
StartUpdating();
|
||||
/// TODO: Run coded frame removal algorithm.
|
||||
|
||||
|
@ -108,6 +108,9 @@ public:
|
||||
double GetBufferedStart();
|
||||
double GetBufferedEnd();
|
||||
|
||||
// Runs the range removal algorithm as defined by the MSE spec.
|
||||
void RangeRemoval(double aStart, double aEnd);
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Dump(const char* aPath);
|
||||
#endif
|
||||
|
@ -108,15 +108,12 @@ SourceBufferList::AnyUpdating()
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferList::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
SourceBufferList::RangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBufferList(%p)::Remove(aStart=%f, aEnd=%f", this, aStart, aEnd);
|
||||
MSE_DEBUG("SourceBufferList(%p)::RangeRemoval(aStart=%f, aEnd=%f", this, aStart, aEnd);
|
||||
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
|
||||
mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
mSourceBuffers[i]->RangeRemoval(aStart, aEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,9 +66,8 @@ public:
|
||||
// Returns true if updating is true on any SourceBuffers in the list.
|
||||
bool AnyUpdating();
|
||||
|
||||
// Calls Remove(aStart, aEnd) on each SourceBuffer in the list. Aborts on
|
||||
// first error, with result returned in aRv.
|
||||
void Remove(double aStart, double aEnd, ErrorResult& aRv);
|
||||
// Runs the range removal steps from the MSE specification on each SourceBuffer.
|
||||
void RangeRemoval(double aStart, double aEnd);
|
||||
|
||||
// Mark all SourceBuffers input buffers as ended.
|
||||
void Ended();
|
||||
|
@ -9,6 +9,8 @@ support-files =
|
||||
[test_MediaSource_disabled.html]
|
||||
[test_BufferedSeek.html]
|
||||
[test_BufferingWait.html]
|
||||
[test_EndOfStream.html]
|
||||
skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187
|
||||
[test_FrameSelection.html]
|
||||
[test_HaveMetadataUnbufferedSeek.html]
|
||||
[test_LoadedMetadataFired.html]
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var updateCount = 0;
|
||||
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
@ -19,7 +21,13 @@ runWithMSE(function (ms, v) {
|
||||
fetchWithXHR("seek.webm", function (arrayBuffer) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer));
|
||||
sb.addEventListener("updateend", function () {
|
||||
ms.endOfStream()
|
||||
updateCount++;
|
||||
/* Ensure that we endOfStream on the first update event only as endOfStream can
|
||||
raise more if the duration of the last buffered range and the intial duration
|
||||
differ. See bug 1065207 */
|
||||
if (updateCount == 1) {
|
||||
ms.endOfStream();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
50
dom/media/mediasource/test/test_EndOfStream.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>MSE: endOfStream call after an appendBuffer</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="mediasource.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
runWithMSE(function () {
|
||||
var ms = new MediaSource();
|
||||
|
||||
var v = document.createElement("video");
|
||||
v.src = URL.createObjectURL(ms);
|
||||
document.body.appendChild(v);
|
||||
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
|
||||
fetchWithXHR("seek.webm", function (arrayBuffer) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 88966));
|
||||
var count = 0;
|
||||
sb.addEventListener("updateend", function () {
|
||||
++count;
|
||||
if (count == 1) {
|
||||
setTimeout(function() {
|
||||
var fail = false;
|
||||
try {
|
||||
ms.endOfStream();
|
||||
} catch (e) {
|
||||
fail = true;
|
||||
}
|
||||
ok(!fail, "MediaSource.endOfStream succeeded");
|
||||
SimpleTest.finish();
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -12,6 +12,8 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var updateCount = 0;
|
||||
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
@ -34,13 +36,13 @@ runWithMSE(function (ms, v) {
|
||||
fetchWithXHR("seek_lowres.webm", function (arrayBuffer) {
|
||||
// Append initialization segment.
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 438));
|
||||
var first = true;
|
||||
sb.addEventListener("updateend", function () {
|
||||
if (first) {
|
||||
updateCount++;
|
||||
if (updateCount == 1) {
|
||||
// Append media segment covering range [2, 4].
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 51003));
|
||||
first = false;
|
||||
} else {
|
||||
}
|
||||
else if (updateCount == 2) {
|
||||
ms.endOfStream();
|
||||
target = targets.shift();
|
||||
v.currentTime = target.currentTime;
|
||||
|
@ -57,7 +57,12 @@ runWithMSE(function () {
|
||||
sb.addEventListener("update", function () {
|
||||
is(sb.updating, false, "SourceBuffer.updating is expected value in update event");
|
||||
updateCount++;
|
||||
ms.endOfStream();
|
||||
/* Ensure that we endOfStream on the first update event only as endOfStream can
|
||||
raise more if the duration of the last buffered range and the intial duration
|
||||
differ. See bug 1065207 */
|
||||
if (updateCount == 1) {
|
||||
ms.endOfStream();
|
||||
}
|
||||
});
|
||||
|
||||
sb.addEventListener("updatestart", function () {
|
||||
@ -84,9 +89,10 @@ runWithMSE(function () {
|
||||
// XXX: Duration should be exactly 4.0, see bug 1065207.
|
||||
ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
|
||||
ok(Math.abs(v.currentTime - 4) <= 0.002, "Video has played to end");
|
||||
is(updateCount, 1, "update event received");
|
||||
is(updateendCount, 1, "updateend event received");
|
||||
is(updatestartCount, 1, "updatestart event received");
|
||||
// XXX: 2 update events can be received dueto duration differences, see bug 1065207.
|
||||
ok(updateCount == 1 || updateCount == 2, "update event received");
|
||||
ok(updateendCount == 1 || updateendCount == 2, "updateend event received");
|
||||
ok(updatestartCount == 1 || updatestartCount == 2, "updatestart event received");
|
||||
is(loadedmetadataCount, 1, "loadedmetadata event received");
|
||||
v.parentNode.removeChild(v);
|
||||
SimpleTest.finish();
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var updateCount = 0;
|
||||
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
@ -19,7 +21,13 @@ runWithMSE(function (ms, v) {
|
||||
fetchWithXHR("seek.webm", function (arrayBuffer) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer));
|
||||
sb.addEventListener("updateend", function () {
|
||||
ms.endOfStream()
|
||||
updateCount++;
|
||||
/* Ensure that we endOfStream on the first update event only as endOfStream can
|
||||
raise more if the duration of the last buffered range and the intial duration
|
||||
differ. See bug 1065207 */
|
||||
if (updateCount == 1) {
|
||||
ms.endOfStream();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -24,7 +24,7 @@ runWithMSE(function (ms, v) {
|
||||
if (updateCount == 1) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
|
||||
}
|
||||
else {
|
||||
else if (updateCount == 2) {
|
||||
ms.endOfStream();
|
||||
}
|
||||
});
|
||||
|
@ -12,18 +12,20 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var updateCount = 0;
|
||||
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
|
||||
fetchWithXHR("seek.webm", function (arrayBuffer) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
|
||||
var first = true;
|
||||
sb.addEventListener("updateend", function () {
|
||||
if (first) {
|
||||
updateCount++;
|
||||
if (updateCount == 1) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
|
||||
first = false;
|
||||
} else {
|
||||
}
|
||||
else if (updateCount == 2) {
|
||||
ms.endOfStream();
|
||||
}
|
||||
});
|
||||
|
@ -12,20 +12,22 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var updateCount = 0;
|
||||
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
|
||||
fetchWithXHR("seek.webm", function (arrayBuffer) {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
|
||||
var first = true;
|
||||
sb.addEventListener("updateend", function () {
|
||||
if (first) {
|
||||
updateCount++;
|
||||
if (updateCount == 1) {
|
||||
window.setTimeout(function () {
|
||||
sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
|
||||
first = false;
|
||||
}, 1000);
|
||||
} else {
|
||||
}
|
||||
else if (updateCount == 2) {
|
||||
ms.endOfStream();
|
||||
}
|
||||
});
|
||||
|
@ -121,6 +121,15 @@ function PlayFragmented(test, elem, token)
|
||||
var curFragment = 0;
|
||||
|
||||
function addNextFragment() {
|
||||
/* We can get another updateevent as a result of calling ms.endOfStream() if
|
||||
the highest end time of our source buffers is different from that of the
|
||||
media source duration. Due to bug 1065207 this can happen because of
|
||||
inaccuracies in the frame duration calculations. Check if we are already
|
||||
"ended" and ignore the update event */
|
||||
if (ms.readyState == "ended") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (curFragment >= test.fragments.length) {
|
||||
Log(token, "addNextFragment() end of stream");
|
||||
ms.endOfStream();
|
||||
|
18
dom/plugins/ipc/PluginHelperQt.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PluginHelperQt.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QEventLoop>
|
||||
|
||||
static const int kMaxtimeToProcessEvents = 30;
|
||||
|
||||
bool
|
||||
PluginHelperQt::AnswerProcessSomeEvents()
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
|
||||
return true;
|
||||
}
|
16
dom/plugins/ipc/PluginHelperQt.h
Normal file
@ -0,0 +1,16 @@
|
||||
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PluginHelperQt_h_
|
||||
#define PluginHelperQt_h_
|
||||
|
||||
class PluginHelperQt
|
||||
{
|
||||
public:
|
||||
static bool AnswerProcessSomeEvents();
|
||||
};
|
||||
|
||||
#endif // PluginHelperQt_h_
|
@ -5,10 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifdef MOZ_WIDGET_QT
|
||||
// Must be included first to avoid conflicts.
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include "NestedLoopTimer.h"
|
||||
#include "PluginHelperQt.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
@ -1646,13 +1643,11 @@ PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsSca
|
||||
#endif // #if defined(XP_MACOSX)
|
||||
|
||||
#if defined(MOZ_WIDGET_QT)
|
||||
static const int kMaxtimeToProcessEvents = 30;
|
||||
bool
|
||||
PluginModuleParent::AnswerProcessSomeEvents()
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
|
||||
|
||||
PluginHelperQt::AnswerProcessSomeEvents();
|
||||
PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
|
||||
|
||||
return true;
|
||||
|
@ -70,8 +70,9 @@ if CONFIG['MOZ_ENABLE_QT']:
|
||||
GENERATED_SOURCES += [
|
||||
'moc_NestedLoopTimer.cpp',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
SOURCES += [
|
||||
'NestedLoopTimer.cpp',
|
||||
'PluginHelperQt.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -1764,7 +1764,7 @@ public:
|
||||
// Note that we don't need to check mDispatchInputEvent here. We need
|
||||
// to check it only when the editor requests to dispatch the input event.
|
||||
|
||||
if (!mTarget->IsInDoc()) {
|
||||
if (!mTarget->IsInComposedDoc()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -850,6 +850,16 @@ DrawTargetD2D1::factory()
|
||||
return mFactory;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::CleanupD2D()
|
||||
{
|
||||
if (mFactory) {
|
||||
RadialGradientEffectD2D1::Unregister(mFactory);
|
||||
mFactory->Release();
|
||||
mFactory = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::MarkChanged()
|
||||
{
|
||||
|
@ -609,6 +609,11 @@ Factory::SetDirect3D11Device(ID3D11Device *aDevice)
|
||||
{
|
||||
mD3D11Device = aDevice;
|
||||
|
||||
if (mD2D1Device) {
|
||||
mD2D1Device->Release();
|
||||
mD2D1Device = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Factory1> factory = D2DFactory1();
|
||||
|
||||
RefPtr<IDXGIDevice> device;
|
||||
@ -656,7 +661,12 @@ Factory::GetD2DVRAMUsageSourceSurface()
|
||||
void
|
||||
Factory::D2DCleanup()
|
||||
{
|
||||
if (mD2D1Device) {
|
||||
mD2D1Device->Release();
|
||||
mD2D1Device = nullptr;
|
||||
}
|
||||
DrawTargetD2D::CleanupD2D();
|
||||
DrawTargetD2D1::CleanupD2D();
|
||||
}
|
||||
|
||||
#endif // XP_WIN
|
||||
|
@ -284,6 +284,12 @@ RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
|
||||
return hr;
|
||||
}
|
||||
|
||||
void
|
||||
RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
|
||||
{
|
||||
aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
|
||||
}
|
||||
|
||||
HRESULT __stdcall
|
||||
RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
|
||||
{
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo);
|
||||
|
||||
static HRESULT Register(ID2D1Factory1* aFactory);
|
||||
static void Unregister(ID2D1Factory1* aFactory);
|
||||
static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
|
||||
|
||||
HRESULT SetStopCollection(IUnknown *aStopCollection);
|
||||
|
@ -89,17 +89,21 @@ if CONFIG['INTEL_ARCHITECTURE']:
|
||||
if CONFIG['_MSC_VER'] != '1400':
|
||||
SOURCES += [
|
||||
'BlurSSE2.cpp',
|
||||
'convolverSSE2.cpp',
|
||||
'FilterProcessingSSE2.cpp',
|
||||
'ImageScalingSSE2.cpp',
|
||||
]
|
||||
if CONFIG['MOZ_ENABLE_SKIA']:
|
||||
SOURCES += [
|
||||
'convolverSSE2.cpp',
|
||||
]
|
||||
DEFINES['USE_SSE2'] = True
|
||||
# The file uses SSE2 intrinsics, so it needs special compile flags on some
|
||||
# compilers.
|
||||
SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
if CONFIG['MOZ_ENABLE_SKIA']:
|
||||
SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Blur.cpp',
|
||||
|
@ -402,19 +402,6 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
|
||||
return apzc;
|
||||
}
|
||||
|
||||
static EventRegions
|
||||
EventRegionsFor(const LayerMetricsWrapper& aLayer)
|
||||
{
|
||||
// This is a workaround for bug 1082594. We should be able to replace this
|
||||
// with just a call to aLayer.GetEventRegions() once that bug is fixed.
|
||||
if (aLayer.IsScrollInfoLayer()) {
|
||||
EventRegions regions(ParentLayerIntRect::ToUntyped(RoundedIn(aLayer.Metrics().mCompositionBounds)));
|
||||
regions.mDispatchToContentHitRegion = regions.mHitRegion;
|
||||
return regions;
|
||||
}
|
||||
return aLayer.GetEventRegions();
|
||||
}
|
||||
|
||||
AsyncPanZoomController*
|
||||
APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
|
||||
const LayerMetricsWrapper& aLayer,
|
||||
@ -497,7 +484,7 @@ APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
|
||||
// region as we loop backwards through the children.
|
||||
nsIntRegion childRegion;
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
childRegion = EventRegionsFor(child).mHitRegion;
|
||||
childRegion = child.GetEventRegions().mHitRegion;
|
||||
} else {
|
||||
childRegion = child.GetVisibleRegion();
|
||||
}
|
||||
@ -531,7 +518,7 @@ APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
|
||||
// we count the children as obscuring the parent or not.
|
||||
|
||||
EventRegions unobscured;
|
||||
unobscured.Sub(EventRegionsFor(aLayer), obscured);
|
||||
unobscured.Sub(aLayer.GetEventRegions(), obscured);
|
||||
APZCTM_LOG("Picking up unobscured hit region %s from layer %p\n", Stringify(unobscured).c_str(), aLayer.GetLayer());
|
||||
|
||||
// Take the hit region of the |aLayer|'s subtree (which has already been
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <QWindow>
|
||||
#ifdef MOZ_X11
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#endif
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/platform_thread.h"
|
||||
|
||||
|
18
js/ipc/CPOWTimer.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=80:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "CPOWTimer.h"
|
||||
|
||||
CPOWTimer::~CPOWTimer() {
|
||||
/* This is a best effort to find the compartment responsible for this CPOW call */
|
||||
xpc::CompartmentPrivate* compartment = xpc::CompartmentPrivate::Get(js::GetObjectCompartment(mozilla::dom::GetIncumbentGlobal()
|
||||
->GetGlobalJSObject()));
|
||||
PRIntervalTime time = PR_IntervalNow() - startInterval;
|
||||
compartment->CPOWTime += time;
|
||||
}
|
24
js/ipc/CPOWTimer.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=80:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CPOWTIMER_H
|
||||
#define CPOWTIMER_H
|
||||
|
||||
#include "prinrval.h"
|
||||
|
||||
class JSObject;
|
||||
|
||||
class MOZ_STACK_CLASS CPOWTimer {
|
||||
public:
|
||||
CPOWTimer(): startInterval(PR_IntervalNow()) {}
|
||||
~CPOWTimer();
|
||||
|
||||
private:
|
||||
PRIntervalTime startInterval;
|
||||
};
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "CPOWTimer.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "nsIRemoteTagService.h"
|
||||
@ -134,7 +135,10 @@ const CPOWProxyHandler CPOWProxyHandler::singleton;
|
||||
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
|
||||
return false; \
|
||||
} \
|
||||
return owner->call args;
|
||||
{ \
|
||||
CPOWTimer timer; \
|
||||
return owner->call args; \
|
||||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CPOWTimer.cpp',
|
||||
'JavaScriptChild.cpp',
|
||||
'JavaScriptParent.cpp',
|
||||
'JavaScriptShared.cpp',
|
||||
|
@ -52,23 +52,35 @@ class ChunkPool
|
||||
|
||||
size_t count() const { return count_; }
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline Chunk *get(JSRuntime *rt);
|
||||
Chunk *head() { MOZ_ASSERT(head_); return head_; }
|
||||
Chunk *pop();
|
||||
void push(Chunk *chunk);
|
||||
Chunk *remove(Chunk *chunk);
|
||||
|
||||
/* Must be called either during the GC or with the GC lock taken. */
|
||||
inline void put(Chunk *chunk);
|
||||
#ifdef DEBUG
|
||||
bool contains(Chunk *chunk) const;
|
||||
bool verify() const;
|
||||
#endif
|
||||
|
||||
class Enum {
|
||||
// Pool mutation does not invalidate an Iter unless the mutation
|
||||
// is of the Chunk currently being visited by the Iter.
|
||||
class Iter {
|
||||
public:
|
||||
explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.head_) {}
|
||||
bool empty() { return !*chunkp; }
|
||||
Chunk *front();
|
||||
inline void popFront();
|
||||
inline void removeAndPopFront();
|
||||
explicit Iter(ChunkPool &pool) : current_(pool.head_) {}
|
||||
bool done() const { return !current_; }
|
||||
void next();
|
||||
Chunk *get() const { return current_; }
|
||||
operator Chunk *() const { return get(); }
|
||||
Chunk *operator->() const { return get(); }
|
||||
private:
|
||||
ChunkPool &pool;
|
||||
Chunk **chunkp;
|
||||
Chunk *current_;
|
||||
};
|
||||
|
||||
private:
|
||||
// ChunkPool controls external resources with interdependencies on the
|
||||
// JSRuntime and related structs, so must not be copied.
|
||||
ChunkPool(const ChunkPool &) MOZ_DELETE;
|
||||
ChunkPool operator=(const ChunkPool &) MOZ_DELETE;
|
||||
};
|
||||
|
||||
// Performs extra allocation off the main thread so that when memory is
|
||||
@ -463,10 +475,11 @@ class GCRuntime
|
||||
inline void updateOnArenaFree(const ChunkInfo &info);
|
||||
|
||||
GCChunkSet::Range allChunks() { return chunkSet.all(); }
|
||||
Chunk **getAvailableChunkList();
|
||||
void moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock);
|
||||
bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
|
||||
ChunkPool &availableChunks(const AutoLockGC &lock) { return availableChunks_; }
|
||||
ChunkPool &emptyChunks(const AutoLockGC &lock) { return emptyChunks_; }
|
||||
const ChunkPool &availableChunks(const AutoLockGC &lock) const { return availableChunks_; }
|
||||
const ChunkPool &emptyChunks(const AutoLockGC &lock) const { return emptyChunks_; }
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
@ -629,8 +642,8 @@ class GCRuntime
|
||||
* During the GC when all arenas in a chunk become free, that chunk is
|
||||
* removed from the list and scheduled for release.
|
||||
*/
|
||||
js::gc::Chunk *availableChunkListHead;
|
||||
js::gc::ChunkPool emptyChunks_;
|
||||
ChunkPool availableChunks_;
|
||||
ChunkPool emptyChunks_;
|
||||
|
||||
js::RootedValueMap rootsHash;
|
||||
|
||||
|
@ -746,7 +746,7 @@ static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t),
|
||||
struct ChunkInfo
|
||||
{
|
||||
Chunk *next;
|
||||
Chunk **prevp;
|
||||
Chunk *prev;
|
||||
|
||||
/* Free arenas are linked together with aheader.next. */
|
||||
ArenaHeader *freeArenasHead;
|
||||
@ -945,10 +945,6 @@ struct Chunk
|
||||
return info.numArenasFree != 0;
|
||||
}
|
||||
|
||||
inline void addToAvailableList(JSRuntime *rt);
|
||||
inline void insertToAvailableList(Chunk **insertPoint);
|
||||
inline void removeFromAvailableList();
|
||||
|
||||
ArenaHeader *allocateArena(JSRuntime *rt, JS::Zone *zone, AllocKind kind,
|
||||
const AutoLockGC &lock);
|
||||
|
||||
@ -962,22 +958,6 @@ struct Chunk
|
||||
|
||||
void decommitAllArenas(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* Assuming that the info.prevp points to the next field of the previous
|
||||
* chunk in a doubly-linked list, get that chunk.
|
||||
*/
|
||||
Chunk *getPrevious() {
|
||||
MOZ_ASSERT(info.prevp);
|
||||
return fromPointerToNext(info.prevp);
|
||||
}
|
||||
|
||||
/* Get the chunk from a pointer to its info.next field. */
|
||||
static Chunk *fromPointerToNext(Chunk **nextFieldPtr) {
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(nextFieldPtr);
|
||||
MOZ_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next));
|
||||
return reinterpret_cast<Chunk *>(addr - offsetof(Chunk, info.next));
|
||||
}
|
||||
|
||||
private:
|
||||
inline void init(JSRuntime *rt);
|
||||
|
||||
|
14
js/src/jit-test/tests/ion/bug1101576.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Random chosen test: js/src/jit-test/tests/ion/bug928423.js
|
||||
o = {
|
||||
a: 1,
|
||||
b: 1
|
||||
}
|
||||
print(1);
|
||||
for (var x = 0; x < 2; x++) {
|
||||
print(2);
|
||||
o["a1".substr(0, 1)]
|
||||
o["b1".substr(0, 1)]
|
||||
}
|
||||
print(3);
|
||||
// jsfunfuzz
|
||||
"a" + "b"
|
@ -6058,6 +6058,8 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
Register length = ToRegister(lir->length());
|
||||
Register output = ToRegister(lir->output());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register temp2 = ToRegister(lir->temp2());
|
||||
Register temp3 = ToRegister(lir->temp3());
|
||||
Address stringFlags(string, JSString::offsetOfFlags());
|
||||
|
||||
Label isLatin1, notInline, nonZero, isInlinedLatin1;
|
||||
@ -6102,9 +6104,10 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
Address(output, JSString::offsetOfFlags()));
|
||||
masm.computeEffectiveAddress(stringStorage, temp);
|
||||
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
|
||||
masm.computeEffectiveAddress(chars, begin);
|
||||
masm.computeEffectiveAddress(chars, temp2);
|
||||
masm.computeEffectiveAddress(outputStorage, temp);
|
||||
CopyStringChars(masm, temp, begin, length, string, sizeof(char16_t), sizeof(char16_t));
|
||||
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
|
||||
masm.load32(Address(output, JSString::offsetOfLength()), length);
|
||||
masm.store16(Imm32(0), Address(temp, 0));
|
||||
masm.jump(done);
|
||||
}
|
||||
@ -6112,11 +6115,12 @@ bool CodeGenerator::visitSubstr(LSubstr *lir)
|
||||
{
|
||||
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
|
||||
Address(output, JSString::offsetOfFlags()));
|
||||
masm.computeEffectiveAddress(stringStorage, temp);
|
||||
masm.computeEffectiveAddress(stringStorage, temp2);
|
||||
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
|
||||
masm.addPtr(temp, begin);
|
||||
masm.addPtr(begin, temp2);
|
||||
masm.computeEffectiveAddress(outputStorage, temp);
|
||||
CopyStringChars(masm, temp, begin, length, string, sizeof(char), sizeof(char));
|
||||
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
|
||||
masm.load32(Address(output, JSString::offsetOfLength()), length);
|
||||
masm.store8(Imm32(0), Address(temp, 0));
|
||||
masm.jump(done);
|
||||
}
|
||||
|
@ -3450,18 +3450,20 @@ class LStringSplit : public LCallInstructionHelper<1, 2, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LSubstr : public LInstructionHelper<1, 3, 1>
|
||||
class LSubstr : public LInstructionHelper<1, 3, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Substr)
|
||||
|
||||
LSubstr(const LAllocation &string, const LAllocation &begin, const LAllocation &length,
|
||||
const LDefinition &temp)
|
||||
const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3)
|
||||
{
|
||||
setOperand(0, string);
|
||||
setOperand(1, begin);
|
||||
setOperand(2, length);
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
setTemp(2, temp3);
|
||||
}
|
||||
const LAllocation *string() {
|
||||
return getOperand(0);
|
||||
@ -3475,6 +3477,12 @@ class LSubstr : public LInstructionHelper<1, 3, 1>
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition *temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp3() {
|
||||
return getTemp(2);
|
||||
}
|
||||
const MStringSplit *mir() const {
|
||||
return mir_->toStringSplit();
|
||||
}
|
||||
|
@ -2184,10 +2184,15 @@ LIRGenerator::visitStringReplace(MStringReplace *ins)
|
||||
bool
|
||||
LIRGenerator::visitSubstr(MSubstr *ins)
|
||||
{
|
||||
LSubstr *lir = new (alloc()) LSubstr(useFixed(ins->string(), CallTempReg1),
|
||||
// The last temporary need to be a register that can handle 8bit moves, but
|
||||
// there is no way to signal that to register allocator, except to give a
|
||||
// fixed temporary that is able to do this.
|
||||
LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
||||
useRegister(ins->begin()),
|
||||
useRegister(ins->length()),
|
||||
temp());
|
||||
temp(),
|
||||
temp(),
|
||||
tempFixed(CallTempReg1));
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ UNIFIED_SOURCES += [
|
||||
'testFuncCallback.cpp',
|
||||
'testFunctionProperties.cpp',
|
||||
'testGCAllocator.cpp',
|
||||
'testGCChunkPool.cpp',
|
||||
'testGCExactRooting.cpp',
|
||||
'testGCFinalizeCallback.cpp',
|
||||
'testGCHeapPostBarriers.cpp',
|
||||
|
71
js/src/jsapi-tests/testGCChunkPool.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include "gc/GCRuntime.h"
|
||||
#include "gc/Heap.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
BEGIN_TEST(testGCChunkPool)
|
||||
{
|
||||
const int N = 10;
|
||||
js::gc::ChunkPool pool;
|
||||
|
||||
// Create.
|
||||
for (int i = 0; i < N; ++i) {
|
||||
js::gc::Chunk *chunk = js::gc::Chunk::allocate(rt);
|
||||
CHECK(chunk);
|
||||
pool.push(chunk);
|
||||
}
|
||||
MOZ_ASSERT(pool.verify());
|
||||
|
||||
// Iterate.
|
||||
uint32_t i = 0;
|
||||
for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), ++i)
|
||||
CHECK(iter.get());
|
||||
CHECK(i == pool.count());
|
||||
MOZ_ASSERT(pool.verify());
|
||||
|
||||
// Push/Pop.
|
||||
for (int i = 0; i < N; ++i) {
|
||||
js::gc::Chunk *chunkA = pool.pop();
|
||||
js::gc::Chunk *chunkB = pool.pop();
|
||||
js::gc::Chunk *chunkC = pool.pop();
|
||||
pool.push(chunkA);
|
||||
pool.push(chunkB);
|
||||
pool.push(chunkC);
|
||||
}
|
||||
MOZ_ASSERT(pool.verify());
|
||||
|
||||
// Remove.
|
||||
js::gc::Chunk *chunk = nullptr;
|
||||
int offset = N / 2;
|
||||
for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), --offset) {
|
||||
if (offset == 0) {
|
||||
chunk = pool.remove(iter.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK(chunk);
|
||||
MOZ_ASSERT(!pool.contains(chunk));
|
||||
MOZ_ASSERT(pool.verify());
|
||||
pool.push(chunk);
|
||||
|
||||
// Destruct.
|
||||
js::AutoLockGC lock(rt);
|
||||
for (js::gc::ChunkPool::Iter iter(pool); !iter.done();) {
|
||||
js::gc::Chunk *chunk = iter.get();
|
||||
iter.next();
|
||||
pool.remove(chunk);
|
||||
js::gc::UnmapPages(chunk, js::gc::ChunkSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCChunkPool)
|
195
js/src/jsgc.cpp
@ -663,53 +663,81 @@ FreeChunk(JSRuntime *rt, Chunk *p)
|
||||
UnmapPages(static_cast<void *>(p), ChunkSize);
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline Chunk *
|
||||
ChunkPool::get(JSRuntime *rt)
|
||||
Chunk *
|
||||
ChunkPool::pop()
|
||||
{
|
||||
Chunk *chunk = head_;
|
||||
if (!chunk) {
|
||||
MOZ_ASSERT(!count_);
|
||||
MOZ_ASSERT(bool(head_) == bool(count_));
|
||||
if (!count_)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(count_);
|
||||
head_ = chunk->info.next;
|
||||
--count_;
|
||||
return chunk;
|
||||
return remove(head_);
|
||||
}
|
||||
|
||||
/* Must be called either during the GC or with the GC lock taken. */
|
||||
inline void
|
||||
ChunkPool::put(Chunk *chunk)
|
||||
void
|
||||
ChunkPool::push(Chunk *chunk)
|
||||
{
|
||||
MOZ_ASSERT(!chunk->info.next);
|
||||
MOZ_ASSERT(!chunk->info.prev);
|
||||
|
||||
chunk->info.age = 0;
|
||||
chunk->info.next = head_;
|
||||
if (head_)
|
||||
head_->info.prev = chunk;
|
||||
head_ = chunk;
|
||||
count_++;
|
||||
++count_;
|
||||
|
||||
MOZ_ASSERT(verify());
|
||||
}
|
||||
|
||||
inline Chunk *
|
||||
ChunkPool::Enum::front()
|
||||
Chunk *
|
||||
ChunkPool::remove(Chunk *chunk)
|
||||
{
|
||||
Chunk *chunk = *chunkp;
|
||||
MOZ_ASSERT_IF(chunk, pool.count() != 0);
|
||||
MOZ_ASSERT(count_ > 0);
|
||||
MOZ_ASSERT(contains(chunk));
|
||||
|
||||
if (head_ == chunk)
|
||||
head_ = chunk->info.next;
|
||||
if (chunk->info.prev)
|
||||
chunk->info.prev->info.next = chunk->info.next;
|
||||
if (chunk->info.next)
|
||||
chunk->info.next->info.prev = chunk->info.prev;
|
||||
chunk->info.next = chunk->info.prev = nullptr;
|
||||
--count_;
|
||||
|
||||
MOZ_ASSERT(verify());
|
||||
return chunk;
|
||||
}
|
||||
|
||||
inline void
|
||||
ChunkPool::Enum::popFront()
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
ChunkPool::contains(Chunk *chunk) const
|
||||
{
|
||||
MOZ_ASSERT(!empty());
|
||||
chunkp = &front()->info.next;
|
||||
verify();
|
||||
for (Chunk *cursor = head_; cursor; cursor = cursor->info.next) {
|
||||
if (cursor == chunk)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void
|
||||
ChunkPool::Enum::removeAndPopFront()
|
||||
bool
|
||||
ChunkPool::verify() const
|
||||
{
|
||||
MOZ_ASSERT(!empty());
|
||||
*chunkp = front()->info.next;
|
||||
--pool.count_;
|
||||
MOZ_ASSERT(bool(head_) == bool(count_));
|
||||
uint32_t count = 0;
|
||||
for (Chunk *cursor = head_; cursor; cursor = cursor->info.next, ++count) {
|
||||
MOZ_ASSERT_IF(cursor->info.prev, cursor->info.prev->info.next == cursor);
|
||||
MOZ_ASSERT_IF(cursor->info.next, cursor->info.next->info.prev == cursor);
|
||||
}
|
||||
MOZ_ASSERT(count_ == count);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
ChunkPool::Iter::next()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
current_ = current_->info.next;
|
||||
}
|
||||
|
||||
Chunk *
|
||||
@ -723,15 +751,17 @@ GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
|
||||
*/
|
||||
Chunk *freeList = nullptr;
|
||||
unsigned freeChunkCount = 0;
|
||||
for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); ) {
|
||||
Chunk *chunk = e.front();
|
||||
for (ChunkPool::Iter iter(emptyChunks(lock)); !iter.done();) {
|
||||
Chunk *chunk = iter.get();
|
||||
iter.next();
|
||||
|
||||
MOZ_ASSERT(chunk->unused());
|
||||
MOZ_ASSERT(!chunkSet.has(chunk));
|
||||
if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
|
||||
(freeChunkCount >= tunables.minEmptyChunkCount() &&
|
||||
(shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
|
||||
{
|
||||
e.removeAndPopFront();
|
||||
emptyChunks(lock).remove(chunk);
|
||||
prepareToFreeChunk(chunk->info);
|
||||
chunk->info.next = freeList;
|
||||
freeList = chunk;
|
||||
@ -739,7 +769,6 @@ GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
|
||||
/* Keep the chunk but increase its age. */
|
||||
++freeChunkCount;
|
||||
++chunk->info.age;
|
||||
e.popFront();
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
|
||||
@ -750,9 +779,10 @@ GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
|
||||
static void
|
||||
FreeChunkPool(JSRuntime *rt, ChunkPool &pool)
|
||||
{
|
||||
for (ChunkPool::Enum e(pool); !e.empty();) {
|
||||
Chunk *chunk = e.front();
|
||||
e.removeAndPopFront();
|
||||
for (ChunkPool::Iter iter(pool); !iter.done();) {
|
||||
Chunk *chunk = iter.get();
|
||||
iter.next();
|
||||
pool.remove(chunk);
|
||||
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
|
||||
FreeChunk(rt, chunk);
|
||||
}
|
||||
@ -787,7 +817,7 @@ Chunk::allocate(JSRuntime *rt)
|
||||
}
|
||||
|
||||
/* Must be called with the GC lock taken. */
|
||||
inline void
|
||||
void
|
||||
GCRuntime::releaseChunk(Chunk *chunk)
|
||||
{
|
||||
MOZ_ASSERT(chunk);
|
||||
@ -840,6 +870,8 @@ Chunk::init(JSRuntime *rt)
|
||||
|
||||
/* Initialize the chunk info. */
|
||||
info.age = 0;
|
||||
info.next = nullptr;
|
||||
info.prev = nullptr;
|
||||
info.trailer.storeBuffer = nullptr;
|
||||
info.trailer.location = ChunkLocationBitTenuredHeap;
|
||||
info.trailer.runtime = rt;
|
||||
@ -847,47 +879,6 @@ Chunk::init(JSRuntime *rt)
|
||||
/* The rest of info fields are initialized in pickChunk. */
|
||||
}
|
||||
|
||||
inline Chunk **
|
||||
GCRuntime::getAvailableChunkList()
|
||||
{
|
||||
return &availableChunkListHead;
|
||||
}
|
||||
|
||||
inline void
|
||||
Chunk::addToAvailableList(JSRuntime *rt)
|
||||
{
|
||||
insertToAvailableList(rt->gc.getAvailableChunkList());
|
||||
}
|
||||
|
||||
inline void
|
||||
Chunk::insertToAvailableList(Chunk **insertPoint)
|
||||
{
|
||||
MOZ_ASSERT(hasAvailableArenas());
|
||||
MOZ_ASSERT(!info.prevp);
|
||||
MOZ_ASSERT(!info.next);
|
||||
info.prevp = insertPoint;
|
||||
Chunk *insertBefore = *insertPoint;
|
||||
if (insertBefore) {
|
||||
MOZ_ASSERT(insertBefore->info.prevp == insertPoint);
|
||||
insertBefore->info.prevp = &info.next;
|
||||
}
|
||||
info.next = insertBefore;
|
||||
*insertPoint = this;
|
||||
}
|
||||
|
||||
inline void
|
||||
Chunk::removeFromAvailableList()
|
||||
{
|
||||
MOZ_ASSERT(info.prevp);
|
||||
*info.prevp = info.next;
|
||||
if (info.next) {
|
||||
MOZ_ASSERT(info.next->info.prevp == &info.next);
|
||||
info.next->info.prevp = info.prevp;
|
||||
}
|
||||
info.prevp = nullptr;
|
||||
info.next = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for and return the next decommitted Arena. Our goal is to keep
|
||||
* lastDecommittedArenaOffset "close" to a free arena. We do this by setting
|
||||
@ -955,7 +946,7 @@ Chunk::allocateArena(JSRuntime *rt, Zone *zone, AllocKind thingKind, const AutoL
|
||||
: fetchNextDecommittedArena();
|
||||
aheader->init(zone, thingKind);
|
||||
if (MOZ_UNLIKELY(!hasAvailableArenas()))
|
||||
removeFromAvailableList();
|
||||
rt->gc.availableChunks(lock).remove(this);
|
||||
return aheader;
|
||||
}
|
||||
|
||||
@ -1006,14 +997,14 @@ Chunk::releaseArena(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock,
|
||||
}
|
||||
|
||||
if (info.numArenasFree == 1) {
|
||||
MOZ_ASSERT(!info.prevp);
|
||||
MOZ_ASSERT(!info.prev);
|
||||
MOZ_ASSERT(!info.next);
|
||||
addToAvailableList(rt);
|
||||
rt->gc.availableChunks(lock).push(this);
|
||||
} else if (!unused()) {
|
||||
MOZ_ASSERT(info.prevp);
|
||||
MOZ_ASSERT(rt->gc.availableChunks(lock).contains(this));
|
||||
} else {
|
||||
MOZ_ASSERT(unused());
|
||||
removeFromAvailableList();
|
||||
rt->gc.availableChunks(lock).remove(this);
|
||||
decommitAllArenas(rt);
|
||||
rt->gc.moveChunkToFreePool(this, lock);
|
||||
}
|
||||
@ -1025,7 +1016,7 @@ GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
|
||||
MOZ_ASSERT(chunk->unused());
|
||||
MOZ_ASSERT(chunkSet.has(chunk));
|
||||
chunkSet.remove(chunk);
|
||||
emptyChunks(lock).put(chunk);
|
||||
emptyChunks(lock).push(chunk);
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -1079,12 +1070,10 @@ Chunk *
|
||||
GCRuntime::pickChunk(const AutoLockGC &lock,
|
||||
AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
|
||||
{
|
||||
Chunk **listHeadp = getAvailableChunkList();
|
||||
Chunk *chunk = *listHeadp;
|
||||
if (chunk)
|
||||
return chunk;
|
||||
if (availableChunks(lock).count())
|
||||
return availableChunks(lock).head();
|
||||
|
||||
chunk = emptyChunks(lock).get(rt);
|
||||
Chunk *chunk = emptyChunks(lock).pop();
|
||||
if (!chunk) {
|
||||
chunk = Chunk::allocate(rt);
|
||||
if (!chunk)
|
||||
@ -1111,9 +1100,7 @@ GCRuntime::pickChunk(const AutoLockGC &lock,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
chunk->info.prevp = nullptr;
|
||||
chunk->info.next = nullptr;
|
||||
chunk->addToAvailableList(rt);
|
||||
availableChunks(lock).push(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
@ -1168,7 +1155,6 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
||||
stats(rt),
|
||||
marker(rt),
|
||||
usage(nullptr),
|
||||
availableChunkListHead(nullptr),
|
||||
maxMallocBytes(0),
|
||||
numArenasFreeCommitted(0),
|
||||
verifyPreData(nullptr),
|
||||
@ -1412,7 +1398,13 @@ GCRuntime::finish()
|
||||
|
||||
zones.clear();
|
||||
|
||||
availableChunkListHead = nullptr;
|
||||
for (ChunkPool::Iter iter(availableChunks_); !iter.done();) {
|
||||
Chunk *chunk = iter.get();
|
||||
iter.next();
|
||||
MOZ_ASSERT(chunkSet.has(chunk));
|
||||
availableChunks_.remove(chunk);
|
||||
}
|
||||
|
||||
if (chunkSet.initialized()) {
|
||||
for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
|
||||
releaseChunk(r.front());
|
||||
@ -3407,7 +3399,7 @@ void
|
||||
GCRuntime::decommitAllWithoutUnlocking(const AutoLockGC &lock)
|
||||
{
|
||||
MOZ_ASSERT(emptyChunks(lock).count() == 0);
|
||||
for (Chunk *chunk = *getAvailableChunkList(); chunk; chunk = chunk->info.next) {
|
||||
for (ChunkPool::Iter chunk(availableChunks(lock)); !chunk.done(); chunk.next()) {
|
||||
for (size_t i = 0; i < ArenasPerChunk; ++i) {
|
||||
if (chunk->decommittedArenas.get(i) || chunk->arenas[i].aheader.allocated())
|
||||
continue;
|
||||
@ -3418,21 +3410,23 @@ GCRuntime::decommitAllWithoutUnlocking(const AutoLockGC &lock)
|
||||
}
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(availableChunks(lock).verify());
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::decommitArenas(const AutoLockGC &lock)
|
||||
{
|
||||
// Verify that all entries in the empty chunks pool are decommitted.
|
||||
for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); e.popFront())
|
||||
MOZ_ASSERT(e.front()->info.numArenasFreeCommitted == 0);
|
||||
for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
|
||||
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
|
||||
|
||||
// Build a Vector of all current available Chunks. Since we release the
|
||||
// gc lock while doing the decommit syscall, it is dangerous to iterate
|
||||
// the available list directly, as concurrent operations can modify it.
|
||||
mozilla::Vector<Chunk *> toDecommit;
|
||||
for (Chunk *chunk = availableChunkListHead; chunk; chunk = chunk->info.next) {
|
||||
if (!toDecommit.append(chunk)) {
|
||||
MOZ_ASSERT(availableChunks(lock).verify());
|
||||
for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
|
||||
if (!toDecommit.append(iter.get())) {
|
||||
// The OOM handler does a full, immediate decommit, so there is
|
||||
// nothing more to do here in any case.
|
||||
return onOutOfMallocMemory(lock);
|
||||
@ -3462,6 +3456,7 @@ GCRuntime::decommitArenas(const AutoLockGC &lock)
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(availableChunks(lock).verify());
|
||||
}
|
||||
|
||||
void
|
||||
@ -3654,7 +3649,7 @@ BackgroundAllocTask::run()
|
||||
if (!chunk)
|
||||
break;
|
||||
}
|
||||
chunkPool_.put(chunk);
|
||||
chunkPool_.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,12 +369,17 @@ class DebugScript
|
||||
*/
|
||||
uint32_t stepMode;
|
||||
|
||||
/* Number of breakpoint sites at opcodes in the script. */
|
||||
/*
|
||||
* Number of breakpoint sites at opcodes in the script. This is the number
|
||||
* of populated entries in DebugScript::breakpoints, below.
|
||||
*/
|
||||
uint32_t numSites;
|
||||
|
||||
/*
|
||||
* Array with all breakpoints installed at opcodes in the script, indexed
|
||||
* by the offset of the opcode into the script.
|
||||
* Breakpoints set in our script. For speed and simplicity, this array is
|
||||
* parallel to script->code(): the BreakpointSite for the opcode at
|
||||
* script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
|
||||
* this array's true length is script->length().
|
||||
*/
|
||||
BreakpointSite *breakpoints[1];
|
||||
};
|
||||
|
@ -241,6 +241,7 @@ class Debugger::FrameRange
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*** Breakpoints *********************************************************************************/
|
||||
|
||||
BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
|
||||
@ -343,6 +344,7 @@ Breakpoint::nextInSite()
|
||||
return (link == &site->breakpoints) ? nullptr : fromSiteLinks(link);
|
||||
}
|
||||
|
||||
|
||||
/*** Debugger hook dispatch **********************************************************************/
|
||||
|
||||
Debugger::Debugger(JSContext *cx, NativeObject *dbg)
|
||||
@ -2968,8 +2970,11 @@ Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global)
|
||||
}
|
||||
|
||||
/*
|
||||
* Each debugger-debuggee relation must be stored in up to three places.
|
||||
* JSCompartment::addDebuggee enables debug mode if needed.
|
||||
* For global to become this js::Debugger's debuggee:
|
||||
* - global must be in this->debuggees,
|
||||
* - this js::Debugger must be in global->getDebuggers(), and
|
||||
* - JSCompartment::isDebuggee()'s bit must be set.
|
||||
* All three indications must be kept consistent.
|
||||
*/
|
||||
AutoCompartment ac(cx, global);
|
||||
GlobalObject::DebuggerVector *v = GlobalObject::getOrCreateDebuggers(cx, global);
|
||||
@ -5814,6 +5819,7 @@ static const JSFunctionSpec DebuggerFrame_methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
||||
/*** Debugger.Object *****************************************************************************/
|
||||
|
||||
static void
|
||||
|
@ -3629,6 +3629,7 @@ public:
|
||||
, skipWriteToGlobalPrototype(false)
|
||||
, universalXPConnectEnabled(false)
|
||||
, forcePermissiveCOWs(false)
|
||||
, CPOWTime(0)
|
||||
, skipCOWCallableChecks(false)
|
||||
, scriptability(c)
|
||||
, scope(nullptr)
|
||||
@ -3682,6 +3683,9 @@ public:
|
||||
// Using it in production is inherently unsafe.
|
||||
bool forcePermissiveCOWs;
|
||||
|
||||
// A running count of how much time we've spent processing CPOWs.
|
||||
PRIntervalTime CPOWTime;
|
||||
|
||||
// Disables the XPConnect security checks that deny access to callables and
|
||||
// accessor descriptors on COWs. Do not use this unless you are bholley.
|
||||
bool skipCOWCallableChecks;
|
||||
|
@ -878,8 +878,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
|
||||
#ifdef DEBUG
|
||||
// reget frame from content since it may have been regenerated...
|
||||
if (changeData->mContent) {
|
||||
if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
|
||||
!nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
|
||||
if (!css::CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) {
|
||||
nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
DebugVerifyStyleTree(frame);
|
||||
|
@ -2890,6 +2890,12 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
|
||||
{
|
||||
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
|
||||
}
|
||||
|
||||
nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aCaretFrame)
|
||||
: nsDisplayItem(aBuilder, aCaretFrame)
|
||||
|
@ -2583,6 +2583,11 @@ public:
|
||||
// this layer. aFrame must have the same reference frame as mFrame.
|
||||
void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
|
||||
|
||||
// Indicate that an inactive scrollframe's scrollport should be added to the
|
||||
// dispatch-to-content region, to ensure that APZ lets content create a
|
||||
// displayport.
|
||||
void AddInactiveScrollPort(const nsRect& aRect);
|
||||
|
||||
const nsRegion& HitRegion() { return mHitRegion; }
|
||||
const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
|
||||
const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
|
||||
|
@ -2952,6 +2952,17 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
(!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
|
||||
}
|
||||
|
||||
if (aBuilder->IsPaintingToWindow() &&
|
||||
!mShouldBuildScrollableLayer &&
|
||||
shouldBuildLayer)
|
||||
{
|
||||
if (nsDisplayLayerEventRegions *eventRegions = aBuilder->GetLayerEventRegions()) {
|
||||
// Make sure that APZ will dispatch events back to content so we can
|
||||
// create a displayport for this frame.
|
||||
eventRegions->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
|
||||
}
|
||||
}
|
||||
|
||||
mScrollParentID = aBuilder->GetCurrentScrollParentId();
|
||||
|
||||
nsDisplayListCollection scrolledContent;
|
||||
|
@ -1447,6 +1447,39 @@ nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
|
||||
return LAYER_ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ nsRegion
|
||||
nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap)
|
||||
{
|
||||
*aSnap = true;
|
||||
bool animated;
|
||||
if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated &&
|
||||
mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
|
||||
// OK, the entire region painted by the image is opaque. But what is that
|
||||
// region? It's the image's "dest rect" (the rect where a full copy of
|
||||
// the image is mapped), clipped to the container's content box (which is
|
||||
// what GetBounds() returns). So, we grab those rects and intersect them.
|
||||
const nsRect frameContentBox = GetBounds(aSnap);
|
||||
|
||||
// Note: To get the "dest rect", we have to provide the "constraint rect"
|
||||
// (which is the content-box, with the effects of fragmentation undone).
|
||||
nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
|
||||
nsRect constraintRect(frameContentBox.TopLeft(),
|
||||
imageFrame->mComputedSize);
|
||||
constraintRect.y -= imageFrame->GetContinuationOffset();
|
||||
|
||||
const nsRect destRect =
|
||||
nsLayoutUtils::ComputeObjectDestRect(constraintRect,
|
||||
imageFrame->mIntrinsicSize,
|
||||
imageFrame->mIntrinsicRatio,
|
||||
imageFrame->StylePosition());
|
||||
|
||||
return nsRegion(destRect.Intersect(frameContentBox));
|
||||
}
|
||||
return nsRegion();
|
||||
}
|
||||
|
||||
already_AddRefed<Layer>
|
||||
nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
|
@ -399,20 +399,14 @@ public:
|
||||
return imageFrame->GetInnerArea() + ToReferenceFrame();
|
||||
}
|
||||
|
||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
|
||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) MOZ_OVERRIDE
|
||||
{
|
||||
return GetBounds(aSnap);
|
||||
}
|
||||
|
||||
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap)
|
||||
{
|
||||
*aSnap = true;
|
||||
bool animated;
|
||||
if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated && mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
|
||||
return nsRegion(GetBounds(aSnap));
|
||||
}
|
||||
return nsRegion();
|
||||
}
|
||||
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
||||
bool* aSnap) MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
.test {
|
||||
background: salmon;
|
||||
padding: 4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
<embed class="test" src="blue-32x32.png">
|
||||
<object class="test" data="blue-32x32.png"></object>
|
||||
<video class="test" poster="blue-32x32.png"></video>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!--
|
||||
This testcase ensures that we paint the background around an opaque image,
|
||||
when the image is kept from filling the container via 'object-fit'. This
|
||||
is an interesting case because, by default, images fill their container,
|
||||
which means we can often optimize away the background completely. BUT, if
|
||||
"object-fit" prevents the image from filling its container, we can't
|
||||
optimize away the background; it need to be painted in the uncovered area.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
.test {
|
||||
background: salmon;
|
||||
object-fit: none;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
<embed class="test" src="blue-32x32.png">
|
||||
<object class="test" data="blue-32x32.png"></object>
|
||||
<video class="test" poster="blue-32x32.png"></video>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html class="reftest-print">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
.fakeBackground {
|
||||
background: salmon;
|
||||
height: 3in;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
img.test {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block; /* Required for fragmentation */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="fakeBackground"></div>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
<div class="fakeBackground"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!--
|
||||
This testcase ensures that we paint the background around an opaque image,
|
||||
when the image is kept from filling the container via 'object-fit' (and
|
||||
the img element is fragmented). This is an interesting case because, by
|
||||
default, images fill their container, which means we can often optimize
|
||||
away the background completely. BUT, if "object-fit" prevents the image
|
||||
from filling its container, we can't optimize away the background; it need
|
||||
to be painted in the uncovered area.
|
||||
-->
|
||||
<html class="reftest-print">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
img.test {
|
||||
background: salmon;
|
||||
object-fit: none;
|
||||
width: 32px;
|
||||
/* We make the height 6in larger than the image's intrinsic height,
|
||||
* which gives us the following happy results:
|
||||
* (1) the <img> will split over several 3in tall reftest-print cards
|
||||
* (so, we get to test fragmentation).
|
||||
* (2) the image pixels end up on the second fragment (not the first),
|
||||
* so we get to test image-data painting on later fragments.
|
||||
* (3) the reference case can easily match us using a simple img
|
||||
* with 3in-tall divs before & after it.
|
||||
*/
|
||||
height: calc(32px + 6in);
|
||||
display: block; /* Required for fragmentation */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
.test {
|
||||
background: salmon;
|
||||
padding-top: 5px;
|
||||
padding-left: 5px;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
<embed class="test" src="blue-32x32.png">
|
||||
<object class="test" data="blue-32x32.png"></object>
|
||||
<video class="test" poster="blue-32x32.png"></video>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!--
|
||||
This testcase ensures that we paint the background around an opaque image,
|
||||
when the image is offset from the container via 'object-position'. This is
|
||||
an interesting case because, by default, images fill their container,
|
||||
which means we can often optimize away the background completely. BUT, if
|
||||
"object-position" offsets the image from its container's content-box, we
|
||||
can't optimize away the background; it need to be painted in the uncovered
|
||||
area.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
.test {
|
||||
background: salmon;
|
||||
object-position: 5px 5px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
<embed class="test" src="blue-32x32.png">
|
||||
<object class="test" data="blue-32x32.png"></object>
|
||||
<video class="test" poster="blue-32x32.png"></video>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html class="reftest-print">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
img.test {
|
||||
background: salmon;
|
||||
padding-left: 10px;
|
||||
padding-top: 20px;
|
||||
width: 22px;
|
||||
height: calc(5in - 20px);
|
||||
display: block; /* Required for fragmentation */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!--
|
||||
This testcase ensures that we paint the background around an opaque image,
|
||||
when the image is offset from the container via 'object-position' (and
|
||||
the img element is fragmented). This is an interesting case because, by
|
||||
default, images fill their container, which means we can often optimize
|
||||
away the background completely. BUT, if "object-position" offsets the
|
||||
image from its container's content-box, we can't optimize away the
|
||||
background; it need to be painted in the uncovered area.
|
||||
-->
|
||||
<html class="reftest-print">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
img.test {
|
||||
background: salmon;
|
||||
object-position: 10px 20px;
|
||||
width: 32px;
|
||||
height: 5in;
|
||||
display: block; /* Required for fragmentation */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img class="test" src="blue-32x32.png">
|
||||
</body>
|
||||
</html>
|
@ -11,6 +11,12 @@ random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1b.htm
|
||||
random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1c.html sync-image-switch-1-ref.html # bug 855050 for WinXP
|
||||
random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1d.html sync-image-switch-1-ref.html # bug 855050 for WinXP
|
||||
|
||||
# Tests for "object-fit" & "object-position"
|
||||
test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-1.html image-object-fit-with-background-1-ref.html
|
||||
test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-2.html image-object-fit-with-background-2-ref.html
|
||||
test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-1.html image-object-position-with-background-1-ref.html
|
||||
test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-2.html image-object-position-with-background-2-ref.html
|
||||
|
||||
# Tests for image-orientation used with 'from-image' (note that all
|
||||
# image-orientation tests are fuzzy because the JPEG images do not perfectly
|
||||
# reproduce blocks of solid color, even at maximum quality):
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "nsDisplayList.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "RestyleManager.h"
|
||||
#include "nsRuleProcessorData.h"
|
||||
#include "nsStyleSet.h"
|
||||
#include "nsStyleChangeList.h"
|
||||
|
||||
@ -186,6 +187,50 @@ CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
nsIStyleRule *rule =
|
||||
GetAnimationRule(aData->mElement,
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement);
|
||||
if (rule) {
|
||||
aData->mRuleWalker->Forward(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
|
||||
aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Do we really want to be the only thing keeping a
|
||||
// pseudo-element alive? I *think* the non-animation restyle should
|
||||
// handle that, but should add a test.
|
||||
nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
|
||||
if (rule) {
|
||||
aData->mRuleWalker->Forward(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
CommonAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
/* virtual */ void
|
||||
CommonAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* virtual */ size_t
|
||||
CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
@ -257,6 +302,106 @@ CommonAnimationManager::ExtractComputedValueForTransition(
|
||||
return result;
|
||||
}
|
||||
|
||||
AnimationPlayerCollection*
|
||||
CommonAnimationManager::GetAnimationPlayers(dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded)
|
||||
{
|
||||
if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
|
||||
// Early return for the most common case.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIAtom *propName;
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
propName = GetAnimationsAtom();
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
|
||||
propName = GetAnimationsBeforeAtom();
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
|
||||
propName = GetAnimationsAfterAtom();
|
||||
} else {
|
||||
NS_ASSERTION(!aCreateIfNeeded,
|
||||
"should never try to create transitions for pseudo "
|
||||
"other than :before or :after");
|
||||
return nullptr;
|
||||
}
|
||||
AnimationPlayerCollection* collection =
|
||||
static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
|
||||
if (!collection && aCreateIfNeeded) {
|
||||
// FIXME: Consider arena-allocating?
|
||||
collection =
|
||||
new AnimationPlayerCollection(aElement, propName, this);
|
||||
nsresult rv =
|
||||
aElement->SetProperty(propName, collection,
|
||||
&AnimationPlayerCollection::PropertyDtor, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SetProperty failed");
|
||||
delete collection;
|
||||
return nullptr;
|
||||
}
|
||||
if (propName == nsGkAtoms::animationsProperty ||
|
||||
propName == nsGkAtoms::transitionsProperty) {
|
||||
aElement->SetMayHaveAnimations();
|
||||
}
|
||||
|
||||
AddElementCollection(collection);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
nsIStyleRule*
|
||||
CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_before ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_after,
|
||||
"forbidden pseudo type");
|
||||
|
||||
if (!mPresContext->IsDynamic()) {
|
||||
// For print or print preview, ignore animations.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AnimationPlayerCollection* collection =
|
||||
GetAnimationPlayers(aElement, aPseudoType, false);
|
||||
if (!collection) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RestyleManager* restyleManager = mPresContext->RestyleManager();
|
||||
if (restyleManager->SkipAnimationRules()) {
|
||||
// During the non-animation part of processing restyles, we don't
|
||||
// add the animation rule.
|
||||
|
||||
if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
|
||||
collection->PostRestyleForAnimation(mPresContext);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Animations should already be refreshed, but transitions may not be.
|
||||
// Note that this is temporary, we would like both animations and transitions
|
||||
// to both be refreshed by this point.
|
||||
if (IsAnimationManager()) {
|
||||
NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
|
||||
collection->mStyleRuleRefreshTime ==
|
||||
mPresContext->RefreshDriver()->MostRecentRefresh(),
|
||||
"should already have refreshed style rule");
|
||||
} else {
|
||||
// FIXME: Remove this assignment. See bug 1061364.
|
||||
collection->mNeedsRefreshes = true;
|
||||
collection->EnsureStyleRuleFor(
|
||||
mPresContext->RefreshDriver()->MostRecentRefresh(),
|
||||
EnsureStyleRule_IsNotThrottled);
|
||||
}
|
||||
|
||||
return collection->mStyleRule;
|
||||
}
|
||||
|
||||
/* static */ const CommonAnimationManager::LayerAnimationRecord
|
||||
CommonAnimationManager::sLayerAnimationInfo[] =
|
||||
{ { eCSSProperty_transform,
|
||||
|
@ -53,6 +53,12 @@ public:
|
||||
virtual nsRestyleHint
|
||||
HasAttributeDependentStyle(AttributeRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#ifdef MOZ_XUL
|
||||
virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#endif
|
||||
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
@ -68,10 +74,23 @@ public:
|
||||
// elements.
|
||||
void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
|
||||
|
||||
virtual AnimationPlayerCollection*
|
||||
AnimationPlayerCollection*
|
||||
GetAnimationPlayers(dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded) = 0;
|
||||
bool aCreateIfNeeded);
|
||||
|
||||
// Returns true if aContent or any of its ancestors has an animation
|
||||
// or transition.
|
||||
static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
|
||||
do {
|
||||
if (aContent->GetProperty(nsGkAtoms::animationsProperty) ||
|
||||
aContent->GetProperty(nsGkAtoms::transitionsProperty)) {
|
||||
return true;
|
||||
}
|
||||
} while ((aContent = aContent->GetParent()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notify this manager that one of its collections of animation players,
|
||||
// has been updated.
|
||||
@ -82,6 +101,9 @@ public:
|
||||
Cannot_Throttle
|
||||
};
|
||||
|
||||
nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType);
|
||||
|
||||
static bool ExtractComputedValueForTransition(
|
||||
nsCSSProperty aProperty,
|
||||
nsStyleContext* aStyleContext,
|
||||
@ -114,6 +136,14 @@ protected:
|
||||
// Check to see if we should stop or start observing the refresh driver
|
||||
void CheckNeedsRefresh();
|
||||
|
||||
virtual nsIAtom* GetAnimationsAtom() = 0;
|
||||
virtual nsIAtom* GetAnimationsBeforeAtom() = 0;
|
||||
virtual nsIAtom* GetAnimationsAfterAtom() = 0;
|
||||
|
||||
virtual bool IsAnimationManager() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When this returns a value other than nullptr, it also,
|
||||
// as a side-effect, notifies the ActiveLayerTracker.
|
||||
static AnimationPlayerCollection*
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
|
||||
#include "nsPresContext.h"
|
||||
#include "nsRuleProcessorData.h"
|
||||
#include "nsStyleSet.h"
|
||||
#include "nsStyleChangeList.h"
|
||||
#include "nsCSSRules.h"
|
||||
@ -198,97 +197,6 @@ nsAnimationManager::QueueEvents(AnimationPlayerCollection* aCollection,
|
||||
}
|
||||
}
|
||||
|
||||
AnimationPlayerCollection*
|
||||
nsAnimationManager::GetAnimationPlayers(dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded)
|
||||
{
|
||||
if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
|
||||
// Early return for the most common case.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIAtom *propName;
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
propName = nsGkAtoms::animationsProperty;
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
|
||||
propName = nsGkAtoms::animationsOfBeforeProperty;
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
|
||||
propName = nsGkAtoms::animationsOfAfterProperty;
|
||||
} else {
|
||||
NS_ASSERTION(!aCreateIfNeeded,
|
||||
"should never try to create transitions for pseudo "
|
||||
"other than :before or :after");
|
||||
return nullptr;
|
||||
}
|
||||
AnimationPlayerCollection* collection =
|
||||
static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
|
||||
if (!collection && aCreateIfNeeded) {
|
||||
// FIXME: Consider arena-allocating?
|
||||
collection =
|
||||
new AnimationPlayerCollection(aElement, propName, this);
|
||||
nsresult rv =
|
||||
aElement->SetProperty(propName, collection,
|
||||
&AnimationPlayerCollection::PropertyDtor, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SetProperty failed");
|
||||
delete collection;
|
||||
return nullptr;
|
||||
}
|
||||
if (propName == nsGkAtoms::animationsProperty) {
|
||||
aElement->SetMayHaveAnimations();
|
||||
}
|
||||
|
||||
AddElementCollection(collection);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
nsIStyleRule *rule =
|
||||
GetAnimationRule(aData->mElement,
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement);
|
||||
if (rule) {
|
||||
aData->mRuleWalker->Forward(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
|
||||
aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Do we really want to be the only thing keeping a
|
||||
// pseudo-element alive? I *think* the non-animation restyle should
|
||||
// handle that, but should add a test.
|
||||
nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
|
||||
if (rule) {
|
||||
aData->mRuleWalker->Forward(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
/* virtual */ void
|
||||
nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* virtual */ size_t
|
||||
nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
@ -729,47 +637,6 @@ nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIStyleRule*
|
||||
nsAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_before ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_after,
|
||||
"forbidden pseudo type");
|
||||
|
||||
if (!mPresContext->IsDynamic()) {
|
||||
// For print or print preview, ignore animations.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AnimationPlayerCollection* collection =
|
||||
GetAnimationPlayers(aElement, aPseudoType, false);
|
||||
if (!collection) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RestyleManager* restyleManager = mPresContext->RestyleManager();
|
||||
if (restyleManager->SkipAnimationRules()) {
|
||||
// During the non-animation part of processing restyles, we don't
|
||||
// add the animation rule.
|
||||
|
||||
if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
|
||||
collection->PostRestyleForAnimation(mPresContext);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
|
||||
collection->mStyleRuleRefreshTime ==
|
||||
mPresContext->RefreshDriver()->MostRecentRefresh(),
|
||||
"should already have refreshed style rule");
|
||||
|
||||
return collection->mStyleRule;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
|
@ -161,17 +161,6 @@ public:
|
||||
aContent, nsGkAtoms::animationsProperty, aProperty);
|
||||
}
|
||||
|
||||
// Returns true if aContent or any of its ancestors has an animation.
|
||||
static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
|
||||
do {
|
||||
if (aContent->GetProperty(nsGkAtoms::animationsProperty)) {
|
||||
return true;
|
||||
}
|
||||
} while ((aContent = aContent->GetParent()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateStyleAndEvents(mozilla::AnimationPlayerCollection* aEA,
|
||||
mozilla::TimeStamp aRefreshTime,
|
||||
mozilla::EnsureStyleRuleFlags aFlags);
|
||||
@ -179,12 +168,6 @@ public:
|
||||
mozilla::EventArray &aEventsToDispatch);
|
||||
|
||||
// nsIStyleRuleProcessor (parts)
|
||||
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#ifdef MOZ_XUL
|
||||
virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#endif
|
||||
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
@ -223,12 +206,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual mozilla::AnimationPlayerCollection*
|
||||
GetAnimationPlayers(mozilla::dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded) MOZ_OVERRIDE;
|
||||
nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType);
|
||||
protected:
|
||||
virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::animationsProperty;
|
||||
}
|
||||
virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::animationsOfBeforeProperty;
|
||||
}
|
||||
virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::animationsOfAfterProperty;
|
||||
}
|
||||
virtual bool IsAnimationManager() MOZ_OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void BuildAnimations(nsStyleContext* aStyleContext,
|
||||
|
@ -1450,8 +1450,11 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_before ||
|
||||
aPseudoType == nsCSSPseudoElements::ePseudo_after) {
|
||||
PresContext()->TransitionManager()->
|
||||
WalkTransitionRule(aElement, aPseudoType, &ruleWalker);
|
||||
nsIStyleRule* rule = PresContext()->TransitionManager()->
|
||||
GetAnimationRule(aElement, aPseudoType);
|
||||
if (rule) {
|
||||
ruleWalker.ForwardOnPossiblyCSSRule(rule);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ nsTransitionManager::ConsiderStartingTransition(
|
||||
"Should have one animation property segment for a transition");
|
||||
if (haveCurrentTransition && haveValues &&
|
||||
oldPT->Properties()[0].mSegments[0].mToValue == endValue) {
|
||||
// WalkTransitionRule already called RestyleForAnimation.
|
||||
// GetAnimationRule already called RestyleForAnimation.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -460,7 +460,7 @@ nsTransitionManager::ConsiderStartingTransition(
|
||||
// |aElementTransitions| is now a dangling pointer!
|
||||
aElementTransitions = nullptr;
|
||||
}
|
||||
// WalkTransitionRule already called RestyleForAnimation.
|
||||
// GetAnimationRule already called RestyleForAnimation.
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -579,133 +579,10 @@ nsTransitionManager::ConsiderStartingTransition(
|
||||
aWhichStarted->AddProperty(aProperty);
|
||||
}
|
||||
|
||||
AnimationPlayerCollection*
|
||||
nsTransitionManager::GetAnimationPlayers(
|
||||
dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded)
|
||||
{
|
||||
if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
|
||||
// Early return for the most common case.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIAtom *propName;
|
||||
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
propName = nsGkAtoms::transitionsProperty;
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
|
||||
propName = nsGkAtoms::transitionsOfBeforeProperty;
|
||||
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
|
||||
propName = nsGkAtoms::transitionsOfAfterProperty;
|
||||
} else {
|
||||
NS_ASSERTION(!aCreateIfNeeded,
|
||||
"should never try to create transitions for pseudo "
|
||||
"other than :before or :after");
|
||||
return nullptr;
|
||||
}
|
||||
AnimationPlayerCollection* collection =
|
||||
static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
|
||||
if (!collection && aCreateIfNeeded) {
|
||||
// FIXME: Consider arena-allocating?
|
||||
collection = new AnimationPlayerCollection(aElement, propName, this);
|
||||
nsresult rv =
|
||||
aElement->SetProperty(propName, collection,
|
||||
&AnimationPlayerCollection::PropertyDtor, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SetProperty failed");
|
||||
delete collection;
|
||||
return nullptr;
|
||||
}
|
||||
if (propName == nsGkAtoms::transitionsProperty) {
|
||||
aElement->SetMayHaveAnimations();
|
||||
}
|
||||
|
||||
AddElementCollection(collection);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/*
|
||||
* nsIStyleRuleProcessor implementation
|
||||
*/
|
||||
|
||||
void
|
||||
nsTransitionManager::WalkTransitionRule(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsRuleWalker* aRuleWalker)
|
||||
{
|
||||
AnimationPlayerCollection* collection =
|
||||
GetAnimationPlayers(aElement, aPseudoType, false);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPresContext->IsDynamic()) {
|
||||
// For print or print preview, ignore animations.
|
||||
return;
|
||||
}
|
||||
|
||||
RestyleManager* restyleManager = mPresContext->RestyleManager();
|
||||
if (restyleManager->SkipAnimationRules()) {
|
||||
// If we're processing a normal style change rather than one from
|
||||
// animation, don't add the transition rule. This allows us to
|
||||
// compute the new style value rather than having the transition
|
||||
// override it, so that we can start transitioning differently.
|
||||
|
||||
if (restyleManager->PostAnimationRestyles()) {
|
||||
// We need to immediately restyle with animation
|
||||
// after doing this.
|
||||
collection->PostRestyleForAnimation(mPresContext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
collection->mNeedsRefreshes = true;
|
||||
collection->EnsureStyleRuleFor(
|
||||
mPresContext->RefreshDriver()->MostRecentRefresh(),
|
||||
EnsureStyleRule_IsNotThrottled);
|
||||
|
||||
if (collection->mStyleRule) {
|
||||
aRuleWalker->Forward(collection->mStyleRule);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsTransitionManager::RulesMatching(ElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
WalkTransitionRule(aData->mElement,
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement,
|
||||
aData->mRuleWalker);
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsTransitionManager::RulesMatching(PseudoElementRuleProcessorData* aData)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
|
||||
"pres context mismatch");
|
||||
|
||||
// Note: If we're the only thing keeping a pseudo-element frame alive
|
||||
// (per ProbePseudoStyleContext), we still want to keep it alive, so
|
||||
// this is ok.
|
||||
WalkTransitionRule(aData->mElement, aData->mPseudoType,
|
||||
aData->mRuleWalker);
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsTransitionManager::RulesMatching(AnonBoxRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
/* virtual */ void
|
||||
nsTransitionManager::RulesMatching(XULTreeRuleProcessorData* aData)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* virtual */ size_t
|
||||
nsTransitionManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
|
@ -99,23 +99,6 @@ public:
|
||||
|
||||
typedef mozilla::AnimationPlayerCollection AnimationPlayerCollection;
|
||||
|
||||
static AnimationPlayerCollection*
|
||||
GetTransitions(nsIContent* aContent) {
|
||||
return static_cast<AnimationPlayerCollection*>
|
||||
(aContent->GetProperty(nsGkAtoms::transitionsProperty));
|
||||
}
|
||||
|
||||
// Returns true if aContent or any of its ancestors has a transition.
|
||||
static bool ContentOrAncestorHasTransition(nsIContent* aContent) {
|
||||
do {
|
||||
if (GetTransitions(aContent)) {
|
||||
return true;
|
||||
}
|
||||
} while ((aContent = aContent->GetParent()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static AnimationPlayerCollection*
|
||||
GetAnimationsForCompositor(nsIContent* aContent, nsCSSProperty aProperty)
|
||||
{
|
||||
@ -148,13 +131,6 @@ public:
|
||||
mInAnimationOnlyStyleUpdate = aInAnimationOnlyUpdate;
|
||||
}
|
||||
|
||||
// nsIStyleRuleProcessor (parts)
|
||||
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#ifdef MOZ_XUL
|
||||
virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
|
||||
#endif
|
||||
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
@ -165,13 +141,16 @@ public:
|
||||
|
||||
void FlushTransitions(FlushFlags aFlags);
|
||||
|
||||
virtual AnimationPlayerCollection*
|
||||
GetAnimationPlayers(mozilla::dom::Element *aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
bool aCreateIfNeeded) MOZ_OVERRIDE;
|
||||
void WalkTransitionRule(mozilla::dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsRuleWalker* aRuleWalker);
|
||||
protected:
|
||||
virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::transitionsProperty;
|
||||
}
|
||||
virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::transitionsOfBeforeProperty;
|
||||
}
|
||||
virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
|
||||
return nsGkAtoms::transitionsOfAfterProperty;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
|
@ -464,6 +464,14 @@ status_t MPEG4Extractor::readMetaData() {
|
||||
status_t err;
|
||||
while (!mFirstTrack) {
|
||||
err = parseChunk(&offset, 0);
|
||||
// The parseChunk function returns UNKNOWN_ERROR to skip
|
||||
// some boxes we don't want to handle. Filter that error
|
||||
// code but return others so e.g. I/O errors propagate.
|
||||
if (err != OK && err != (status_t) UNKNOWN_ERROR) {
|
||||
ALOGW("Error %d parsing chuck at offset %lld looking for first track",
|
||||
err, (long long)offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mInitCheck == OK) {
|
||||
|
@ -42,18 +42,6 @@ static uint8_t kStunMessage[] = {
|
||||
};
|
||||
static size_t kStunMessageLen = sizeof(kStunMessage);
|
||||
|
||||
class DummySocket;
|
||||
|
||||
// Temporary whitelist for refcounted class dangerously exposing its destructor.
|
||||
// Filed bug 1028140 to address this class.
|
||||
namespace mozilla {
|
||||
template<>
|
||||
struct HasDangerousPublicDestructor<DummySocket>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
}
|
||||
|
||||
class DummySocket : public NrSocketBase {
|
||||
public:
|
||||
DummySocket()
|
||||
@ -207,6 +195,8 @@ class DummySocket : public NrSocketBase {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
|
||||
|
||||
private:
|
||||
~DummySocket() {}
|
||||
|
||||
DISALLOW_COPY_ASSIGN(DummySocket);
|
||||
|
||||
size_t writable_; // Amount we allow someone to write.
|
||||
@ -230,7 +220,7 @@ class BufferedStunSocketTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
ScopedDeletePtr<DummySocket> dummy(new DummySocket());
|
||||
nsRefPtr<DummySocket> dummy(new DummySocket());
|
||||
|
||||
int r = nr_socket_buffered_stun_create(
|
||||
dummy->get_nr_socket(),
|
||||
@ -251,7 +241,7 @@ class BufferedStunSocketTest : public ::testing::Test {
|
||||
nr_socket *socket() { return test_socket_; }
|
||||
|
||||
protected:
|
||||
DummySocket *dummy_;
|
||||
nsRefPtr<DummySocket> dummy_;
|
||||
nr_socket *test_socket_;
|
||||
nr_transport_addr remote_addr_;
|
||||
};
|
||||
|
@ -2737,7 +2737,7 @@ public class BrowserApp extends GeckoApp
|
||||
bookmark.setVisible(!GeckoProfile.get(this).inGuestMode());
|
||||
bookmark.setCheckable(true);
|
||||
bookmark.setChecked(tab.isBookmark());
|
||||
bookmark.setIcon(tab.isBookmark() ? R.drawable.ic_menu_bookmark_remove : R.drawable.ic_menu_bookmark_add);
|
||||
bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
|
||||
|
||||
back.setEnabled(tab.canDoBack());
|
||||
forward.setEnabled(tab.canDoForward());
|
||||
@ -2836,6 +2836,22 @@ public class BrowserApp extends GeckoApp
|
||||
return true;
|
||||
}
|
||||
|
||||
private int resolveBookmarkIconID(final boolean isBookmark) {
|
||||
if (NewTabletUI.isEnabled(this) && HardwareUtils.isLargeTablet()) {
|
||||
if (isBookmark) {
|
||||
return R.drawable.new_tablet_ic_menu_bookmark_remove;
|
||||
} else {
|
||||
return R.drawable.new_tablet_ic_menu_bookmark_add;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBookmark) {
|
||||
return R.drawable.ic_menu_bookmark_remove;
|
||||
} else {
|
||||
return R.drawable.ic_menu_bookmark_add;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
Tab tab = null;
|
||||
@ -2857,11 +2873,11 @@ public class BrowserApp extends GeckoApp
|
||||
if (item.isChecked()) {
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
|
||||
tab.removeBookmark();
|
||||
item.setIcon(R.drawable.ic_menu_bookmark_add);
|
||||
item.setIcon(resolveBookmarkIconID(false));
|
||||
} else {
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
|
||||
tab.addBookmark();
|
||||
item.setIcon(R.drawable.ic_menu_bookmark_remove);
|
||||
item.setIcon(resolveBookmarkIconID(true));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -228,6 +228,7 @@ public class GeckoAppShell
|
||||
private static Sensor gLightSensor;
|
||||
|
||||
private static final String GECKOREQUEST_RESPONSE_KEY = "response";
|
||||
private static final String GECKOREQUEST_ERROR_KEY = "error";
|
||||
|
||||
/*
|
||||
* Keep in sync with constants found here:
|
||||
@ -434,7 +435,7 @@ public class GeckoAppShell
|
||||
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
|
||||
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, event);
|
||||
if (!message.has(GECKOREQUEST_RESPONSE_KEY)) {
|
||||
request.onError();
|
||||
request.onError(message.getObject(GECKOREQUEST_ERROR_KEY));
|
||||
return;
|
||||
}
|
||||
request.onResponse(message.getObject(GECKOREQUEST_RESPONSE_KEY));
|
||||
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.menu;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@ -132,12 +133,20 @@ public class GeckoMenuInflater extends MenuInflater {
|
||||
item.hasSubMenu = false;
|
||||
|
||||
// TODO: (bug 1058909) Remove this branch when we remove old tablet. We do this to
|
||||
// avoid using a new menu resource for new tablet (which only has a new reload button).
|
||||
if (item.id == R.id.reload && NewTabletUI.isEnabled(mContext)) {
|
||||
item.iconRes = R.drawable.new_tablet_ic_menu_reload;
|
||||
// avoid using a new menu resource for new tablet.
|
||||
final int iconResID;
|
||||
if (!NewTabletUI.isEnabled(mContext)) {
|
||||
iconResID = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
|
||||
} else {
|
||||
item.iconRes = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
|
||||
if (item.id == R.id.reload) {
|
||||
iconResID = R.drawable.new_tablet_ic_menu_reload;
|
||||
} else if (HardwareUtils.isLargeTablet() && item.id == R.id.bookmark) {
|
||||
iconResID = R.drawable.new_tablet_ic_menu_bookmark_add;
|
||||
} else {
|
||||
iconResID = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
|
||||
}
|
||||
}
|
||||
item.iconRes = iconResID;
|
||||
|
||||
if (Versions.feature11Plus) {
|
||||
item.showAsAction = a.getInt(R.styleable.MenuItem_android_showAsAction, 0);
|
||||
|
Before Width: | Height: | Size: 771 B After Width: | Height: | Size: 775 B |
Before Width: | Height: | Size: 549 B After Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 995 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 897 B |
After Width: | Height: | Size: 641 B |
After Width: | Height: | Size: 629 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 744 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -107,15 +107,10 @@
|
||||
<!-- layout_width/height doesn't work here, likely because it's
|
||||
an ImageButton, so we use padding instead.
|
||||
|
||||
Because the curve of the reload button is lower than the end of the arrow and thus
|
||||
the top of the view, it is perceived to be slighly lower than the other buttons.
|
||||
Thus we use padding to fill a slightly smaller height than the toolbar and let
|
||||
android:gravity center the image with a slight offset (.5dp higher) instead. Note that
|
||||
this offset also affects the pressed state, but it's hardly noticeable and thus negligible.
|
||||
|
||||
Note: the above change is done on a generic style and thus any other items added
|
||||
to the menu bar may appear to be offset. -->
|
||||
<item name="android:paddingTop">20dp</item>
|
||||
Notes:
|
||||
* The bookmarks star is larger than the reload button
|
||||
* The reload button contains whitespace at the top of the image to lower it -->
|
||||
<item name="android:paddingTop">19dp</item>
|
||||
<item name="android:paddingBottom">21dp</item>
|
||||
<item name="android:paddingLeft">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
<item name="android:paddingRight">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
|