Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-11-20 12:40:14 +01:00
commit ef5c30dea6
115 changed files with 1449 additions and 859 deletions

View File

@ -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]

View File

@ -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) => {

View File

@ -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

View File

@ -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");

View File

@ -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.
*

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
});
}

View File

@ -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

View File

@ -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

View File

@ -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
/*******************************************************************************

View File

@ -189,6 +189,13 @@ public:
return NS_FAILED(mAbortCode);
}
nsresult
AbortCode() const
{
AssertIsOnOwningThread();
return mAbortCode;
}
void
GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;

View File

@ -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);

View File

@ -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.
}

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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]

View File

@ -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();
};
});
});

View 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>

View File

@ -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;

View File

@ -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();

View File

@ -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();
};
});
});

View File

@ -24,7 +24,7 @@ runWithMSE(function (ms, v) {
if (updateCount == 1) {
sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
}
else {
else if (updateCount == 2) {
ms.endOfStream();
}
});

View File

@ -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();
}
});

View File

@ -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();
}
});

View File

@ -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();

View 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;
}

View 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_

View File

@ -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;

View File

@ -70,8 +70,9 @@ if CONFIG['MOZ_ENABLE_QT']:
GENERATED_SOURCES += [
'moc_NestedLoopTimer.cpp',
]
UNIFIED_SOURCES += [
SOURCES += [
'NestedLoopTimer.cpp',
'PluginHelperQt.cpp',
]
UNIFIED_SOURCES += [

View File

@ -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;
}

View File

@ -850,6 +850,16 @@ DrawTargetD2D1::factory()
return mFactory;
}
void
DrawTargetD2D1::CleanupD2D()
{
if (mFactory) {
RadialGradientEffectD2D1::Unregister(mFactory);
mFactory->Release();
mFactory = nullptr;
}
}
void
DrawTargetD2D1::MarkChanged()
{

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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',

View File

@ -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

View File

@ -7,7 +7,6 @@
#include <QWindow>
#ifdef MOZ_X11
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#endif
#include <QGuiApplication>
#include <QScreen>

View File

@ -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
View 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
View 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

View File

@ -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,

View File

@ -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',

View File

@ -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;

View File

@ -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);

View 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"

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -34,6 +34,7 @@ UNIFIED_SOURCES += [
'testFuncCallback.cpp',
'testFunctionProperties.cpp',
'testGCAllocator.cpp',
'testGCChunkPool.cpp',
'testGCExactRooting.cpp',
'testGCFinalizeCallback.cpp',
'testGCHeapPostBarriers.cpp',

View 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)

View File

@ -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);
}
}

View File

@ -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];
};

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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; }

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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):

View File

@ -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,

View File

@ -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*

View File

@ -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)
{

View File

@ -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,

View File

@ -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;
}

View File

@ -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
{

View File

@ -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

View File

@ -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) {

View File

@ -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_;
};

View File

@ -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;

View File

@ -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));

View File

@ -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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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>

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