merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-10-26 10:56:02 +01:00
Родитель 19587912ba 4befdd85f0
Коммит a1c13d0acf
126 изменённых файлов: 2696 добавлений и 22317 удалений

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

@ -215,7 +215,7 @@ const Tab = Class({
if (frame.frameElement != browser(this))
return;
listener.off("attach", listener);
frames.off("attach", listener);
attach(frame);
};
frames.on("attach", listener);

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

@ -539,6 +539,9 @@ if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
function gKeywordURIFixup(fixupInfo) {
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
if (!fixupInfo.consumer) {
return;
}
// Ignore info from other docshells
let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;

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

@ -5,6 +5,6 @@ support-files =
[browser_notification_do_not_disturb.js]
[browser_notification_open_settings.js]
[browser_notification_remove_permission.js]
skip-if = e10s
[browser_notification_tab_switching.js]
skip-if = buildapp == 'mulet' || e10s # Bug 1100662 - content access causing uncaught exception - Error: cannot ipc non-cpow object at chrome://mochitests/content/browser/browser/base/content/test/general/browser_notification_tab_switching.js:32 (or in RemoteAddonsChild.jsm)

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

@ -1,6 +1,19 @@
"use strict";
add_task(function* test_settingsOpen() {
var notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
function waitForCloseWindow(window) {
return new Promise(function(resolve) {
Services.ww.registerNotification(function observer(subject, topic, data) {
if (topic == "domwindowclosed" && subject == window) {
Services.ww.unregisterNotification(observer);
resolve();
}
});
});
}
add_task(function* test_settingsOpen_observer() {
info("Opening a dummy tab so openPreferences=>switchToTabHavingURI doesn't use the blank tab.");
yield BrowserTestUtils.withNewTab({
gBrowser,
@ -16,3 +29,43 @@ add_task(function* test_settingsOpen() {
yield BrowserTestUtils.removeTab(tab);
});
});
add_task(function* test_settingsOpen_button() {
let pm = Services.perms;
info("Adding notification permission");
pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
try {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: notificationURL
}, function* tabTask(aBrowser) {
let notification = aBrowser.contentWindow.wrappedJSObject.showNotification2();
info("Waiting for notification");
yield BrowserTestUtils.waitForEvent(notification, "show");
let alertWindow = Services.wm.getMostRecentWindow("alert:alert");
if (!alertWindow) {
ok(true, "Notifications don't use XUL windows on all platforms.");
notification.close();
return;
}
let closePromise = waitForCloseWindow(alertWindow);
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#content");
let openSettingsMenuItem = alertWindow.document.getElementById("openSettingsMenuItem");
openSettingsMenuItem.click();
info("Waiting for notification settings tab");
let tab = yield tabPromise;
ok(tab, "The notification settings tab opened");
yield closePromise;
yield BrowserTestUtils.removeTab(tab);
});
} finally {
info("Removing notification permission");
pm.remove(makeURI(notificationURL), "desktop-notification");
}
});

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

@ -8,7 +8,7 @@
},
"engines": {
"node": "0.10.x",
"npm": "1.3.x"
"npm": "2.14.x"
},
"dependencies": {},
"devDependencies": {

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

@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.1.527
Current extension version is: 1.1.551

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

@ -607,8 +607,8 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
// If we are in range request mode, this means we manually issued xhr
// requests, which we need to abort when we leave the page
domWindow.addEventListener('unload', function unload(e) {
self.networkManager.abortAllRequests();
domWindow.removeEventListener(e.type, unload);
self.abortLoading();
});
}
@ -636,7 +636,7 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
}, '*');
};
this.dataListener.oncomplete = function () {
delete self.dataListener;
self.dataListener = null;
};
}
@ -680,6 +680,15 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
});
};
proto.abortLoading = function RangedChromeActions_abortLoading() {
this.networkManager.abortAllRequests();
if (this.originalRequest) {
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
this.originalRequest = null;
}
this.dataListener = null;
};
return RangedChromeActions;
})();
@ -689,9 +698,10 @@ var StandardChromeActions = (function StandardChromeActionsClosure() {
* This is for a single network stream
*/
function StandardChromeActions(domWindow, contentDispositionFilename,
dataListener) {
originalRequest, dataListener) {
ChromeActions.call(this, domWindow, contentDispositionFilename);
this.originalRequest = originalRequest;
this.dataListener = dataListener;
}
@ -717,20 +727,29 @@ var StandardChromeActions = (function StandardChromeActionsClosure() {
}, '*');
};
this.dataListener.oncomplete = function ChromeActions_dataListenerComplete(
data, errorCode) {
this.dataListener.oncomplete =
function StandardChromeActions_dataListenerComplete(data, errorCode) {
self.domWindow.postMessage({
pdfjsLoadAction: 'complete',
data: data,
errorCode: errorCode
}, '*');
delete self.dataListener;
self.dataListener = null;
self.originalRequest = null;
};
return true;
};
proto.abortLoading = function StandardChromeActions_abortLoading() {
if (this.originalRequest) {
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
this.originalRequest = null;
}
this.dataListener = null;
};
return StandardChromeActions;
})();
@ -969,7 +988,7 @@ PdfStreamConverter.prototype = {
rangeRequest, streamRequest, dataListener);
} else {
actions = new StandardChromeActions(
domWindow, contentDispositionFilename, dataListener);
domWindow, contentDispositionFilename, aRequest, dataListener);
}
var requestListener = new RequestListener(actions);
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {

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

@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '1.1.527';
PDFJS.build = '2096a2a';
PDFJS.version = '1.1.551';
PDFJS.build = '2a5616c';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
@ -225,6 +225,11 @@ function warn(msg) {
}
}
// Deprecated API function -- treated as warnings.
function deprecated(details) {
warn('Deprecated API usage: ' + details);
}
// Fatal errors that should trigger the fallback UI and halt execution by
// throwing an exception.
function error(msg) {
@ -1557,12 +1562,19 @@ PDFJS.getDocument = function getDocument(src,
var task = new PDFDocumentLoadingTask();
// Support of the obsolete arguments (for compatibility with API v1.0)
if (arguments.length > 1) {
deprecated('getDocument is called with pdfDataRangeTransport, ' +
'passwordCallback or progressCallback argument');
}
if (pdfDataRangeTransport) {
if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) {
// Not a PDFDataRangeTransport instance, trying to add missing properties.
pdfDataRangeTransport = Object.create(pdfDataRangeTransport);
pdfDataRangeTransport.length = src.length;
pdfDataRangeTransport.initialData = src.initialData;
if (!pdfDataRangeTransport.abort) {
pdfDataRangeTransport.abort = function () {};
}
}
src = Object.create(src);
src.range = pdfDataRangeTransport;
@ -1622,6 +1634,7 @@ PDFJS.getDocument = function getDocument(src,
workerInitializedCapability.promise.then(function transportInitialized() {
transport.fetchDocument(task, params);
});
task._transport = transport;
return task;
};
@ -1634,6 +1647,7 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
/** @constructs PDFDocumentLoadingTask */
function PDFDocumentLoadingTask() {
this._capability = createPromiseCapability();
this._transport = null;
/**
* Callback to request a password if wrong or no password was provided.
@ -1659,7 +1673,14 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
return this._capability.promise;
},
// TODO add cancel or abort method
/**
* Aborts all network requests and destroys worker.
* @return {Promise} A promise that is resolved after destruction activity
* is completed.
*/
destroy: function () {
return this._transport.destroy();
},
/**
* Registers callbacks to indicate the document loading completion.
@ -1746,6 +1767,9 @@ var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() {
requestDataRange:
function PDFDataRangeTransport_requestDataRange(begin, end) {
throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
},
abort: function PDFDataRangeTransport_abort() {
}
};
return PDFDataRangeTransport;
@ -1759,9 +1783,10 @@ PDFJS.PDFDataRangeTransport = PDFDataRangeTransport;
* @class
*/
var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
function PDFDocumentProxy(pdfInfo, transport) {
function PDFDocumentProxy(pdfInfo, transport, loadingTask) {
this.pdfInfo = pdfInfo;
this.transport = transport;
this.loadingTask = loadingTask;
}
PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ {
/**
@ -1884,7 +1909,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
* Destroys current document instance and terminates worker.
*/
destroy: function PDFDocumentProxy_destroy() {
this.transport.destroy();
return this.transport.destroy();
}
};
return PDFDocumentProxy;
@ -1961,8 +1986,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
this.commonObjs = transport.commonObjs;
this.objs = new PDFObjects();
this.cleanupAfterRender = false;
this.pendingDestroy = false;
this.pendingCleanup = false;
this.intentStates = {};
this.destroyed = false;
}
PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ {
/**
@ -2026,7 +2052,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
// If there was a pending destroy cancel it so no cleanup happens during
// this call to render.
this.pendingDestroy = false;
this.pendingCleanup = false;
var renderingIntent = (params.intent === 'print' ? 'print' : 'display');
@ -2067,13 +2093,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
// Obsolete parameter support
if (params.continueCallback) {
deprecated('render is used with continueCallback parameter');
renderTask.onContinue = params.continueCallback;
}
var self = this;
intentState.displayReadyCapability.promise.then(
function pageDisplayReadyPromise(transparency) {
if (self.pendingDestroy) {
if (self.pendingCleanup) {
complete();
return;
}
@ -2093,9 +2120,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
}
if (self.cleanupAfterRender) {
self.pendingDestroy = true;
self.pendingCleanup = true;
}
self._tryDestroy();
self._tryCleanup();
if (error) {
internalRenderTask.capability.reject(error);
@ -2156,20 +2183,52 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
pageIndex: this.pageNumber - 1
});
},
/**
* Destroys resources allocated by the page.
* Destroys page object.
*/
destroy: function PDFPageProxy_destroy() {
this.pendingDestroy = true;
this._tryDestroy();
_destroy: function PDFPageProxy_destroy() {
this.destroyed = true;
this.transport.pageCache[this.pageIndex] = null;
var waitOn = [];
Object.keys(this.intentStates).forEach(function(intent) {
var intentState = this.intentStates[intent];
intentState.renderTasks.forEach(function(renderTask) {
var renderCompleted = renderTask.capability.promise.
catch(function () {}); // ignoring failures
waitOn.push(renderCompleted);
renderTask.cancel();
});
}, this);
this.objs.clear();
this.annotationsPromise = null;
this.pendingCleanup = false;
return Promise.all(waitOn);
},
/**
* Cleans up resources allocated by the page. (deprecated)
*/
destroy: function() {
deprecated('page destroy method, use cleanup() instead');
this.cleanup();
},
/**
* Cleans up resources allocated by the page.
*/
cleanup: function PDFPageProxy_cleanup() {
this.pendingCleanup = true;
this._tryCleanup();
},
/**
* For internal use only. Attempts to clean up if rendering is in a state
* where that's possible.
* @ignore
*/
_tryDestroy: function PDFPageProxy__destroy() {
if (!this.pendingDestroy ||
_tryCleanup: function PDFPageProxy_tryCleanup() {
if (!this.pendingCleanup ||
Object.keys(this.intentStates).some(function(intent) {
var intentState = this.intentStates[intent];
return (intentState.renderTasks.length !== 0 ||
@ -2183,7 +2242,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
}, this);
this.objs.clear();
this.annotationsPromise = null;
this.pendingDestroy = false;
this.pendingCleanup = false;
},
/**
* For internal use only.
@ -2221,7 +2280,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
if (operatorListChunk.lastChunk) {
intentState.receivingOperatorList = false;
this._tryDestroy();
this._tryCleanup();
}
}
};
@ -2239,6 +2298,8 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.commonObjs = new PDFObjects();
this.loadingTask = null;
this.destroyed = false;
this.destroyCapability = null;
this.pageCache = [];
this.pagePromises = [];
@ -2297,15 +2358,40 @@ var WorkerTransport = (function WorkerTransportClosure() {
}
WorkerTransport.prototype = {
destroy: function WorkerTransport_destroy() {
if (this.destroyCapability) {
return this.destroyCapability.promise;
}
this.destroyed = true;
this.destroyCapability = createPromiseCapability();
var waitOn = [];
// We need to wait for all renderings to be completed, e.g.
// timeout/rAF can take a long time.
this.pageCache.forEach(function (page) {
if (page) {
waitOn.push(page._destroy());
}
});
this.pageCache = [];
this.pagePromises = [];
var self = this;
this.messageHandler.sendWithPromise('Terminate', null).then(function () {
// We also need to wait for the worker to finish its long running tasks.
var terminated = this.messageHandler.sendWithPromise('Terminate', null);
waitOn.push(terminated);
Promise.all(waitOn).then(function () {
FontLoader.clear();
if (self.worker) {
self.worker.terminate();
}
});
if (self.pdfDataRangeTransport) {
self.pdfDataRangeTransport.abort();
self.pdfDataRangeTransport = null;
}
self.messageHandler = null;
self.destroyCapability.resolve();
}, this.destroyCapability.reject);
return this.destroyCapability.promise;
},
setupFakeWorker: function WorkerTransport_setupFakeWorker() {
@ -2379,9 +2465,10 @@ var WorkerTransport = (function WorkerTransportClosure() {
messageHandler.on('GetDoc', function transportDoc(data) {
var pdfInfo = data.pdfInfo;
this.numPages = data.pdfInfo.numPages;
var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
var loadingTask = this.loadingTask;
var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask);
this.pdfDocument = pdfDocument;
this.loadingTask._capability.resolve(pdfDocument);
loadingTask._capability.resolve(pdfDocument);
}, this);
messageHandler.on('NeedPassword',
@ -2582,6 +2669,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
},
fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) {
if (this.destroyed) {
loadingTask._capability.reject(new Error('Loading aborted'));
this.destroyCapability.resolve();
return;
}
this.loadingTask = loadingTask;
source.disableAutoFetch = PDFJS.disableAutoFetch;
@ -2620,6 +2713,9 @@ var WorkerTransport = (function WorkerTransportClosure() {
var promise = this.messageHandler.sendWithPromise('GetPage', {
pageIndex: pageIndex
}).then(function (pageInfo) {
if (this.destroyed) {
throw new Error('Transport destroyed');
}
var page = new PDFPageProxy(pageIndex, pageInfo, this);
this.pageCache[pageIndex] = page;
return page;
@ -2677,7 +2773,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
var page = this.pageCache[i];
if (page) {
page.destroy();
page.cleanup();
}
}
this.commonObjs.clear();
@ -5101,6 +5197,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.fillRect(0, 0, 1, 1);
},
paintXObject: function CanvasGraphics_paintXObject() {
UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown);
warn('Unsupported \'paintXObject\' command.');
},
// Marked content
markPoint: function CanvasGraphics_markPoint(tag) {

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

@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '1.1.527';
PDFJS.build = '2096a2a';
PDFJS.version = '1.1.551';
PDFJS.build = '2a5616c';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
@ -225,6 +225,11 @@ function warn(msg) {
}
}
// Deprecated API function -- treated as warnings.
function deprecated(details) {
warn('Deprecated API usage: ' + details);
}
// Fatal errors that should trigger the fallback UI and halt execution by
// throwing an exception.
function error(msg) {
@ -1603,7 +1608,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
this.chunksNeededByRequest = {};
this.requestsByChunk = {};
this.callbacksByRequest = {};
this.promisesByRequest = {};
this.progressiveDataLength = 0;
this._loadedStreamCapability = createPromiseCapability();
@ -1622,12 +1627,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
// contiguous ranges to load in as few requests as possible
requestAllChunks: function ChunkedStreamManager_requestAllChunks() {
var missingChunks = this.stream.getMissingChunks();
this.requestChunks(missingChunks);
this._requestChunks(missingChunks);
return this._loadedStreamCapability.promise;
},
requestChunks: function ChunkedStreamManager_requestChunks(chunks,
callback) {
_requestChunks: function ChunkedStreamManager_requestChunks(chunks) {
var requestId = this.currRequestId++;
var chunksNeeded;
@ -1640,13 +1644,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
}
if (isEmptyObj(chunksNeeded)) {
if (callback) {
callback();
}
return;
return Promise.resolve();
}
this.callbacksByRequest[requestId] = callback;
var capability = createPromiseCapability();
this.promisesByRequest[requestId] = capability;
var chunksToRequest = [];
for (var chunk in chunksNeeded) {
@ -1659,7 +1661,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
}
if (!chunksToRequest.length) {
return;
return capability.promise;
}
var groupedChunksToRequest = this.groupChunks(chunksToRequest);
@ -1670,6 +1672,8 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
this.sendRequest(begin, end);
}
return capability.promise;
},
getStream: function ChunkedStreamManager_getStream() {
@ -1677,8 +1681,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
},
// Loads any chunks in the requested range that are not yet loaded
requestRange: function ChunkedStreamManager_requestRange(
begin, end, callback) {
requestRange: function ChunkedStreamManager_requestRange(begin, end) {
end = Math.min(end, this.length);
@ -1690,11 +1693,10 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
chunks.push(chunk);
}
this.requestChunks(chunks, callback);
return this._requestChunks(chunks);
},
requestRanges: function ChunkedStreamManager_requestRanges(ranges,
callback) {
requestRanges: function ChunkedStreamManager_requestRanges(ranges) {
ranges = ranges || [];
var chunksToRequest = [];
@ -1709,7 +1711,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
}
chunksToRequest.sort(function(a, b) { return a - b; });
this.requestChunks(chunksToRequest, callback);
return this._requestChunks(chunksToRequest);
},
// Groups a sorted array of chunks into as few contiguous larger
@ -1808,17 +1810,15 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
}
if (isInt(nextEmptyChunk)) {
this.requestChunks([nextEmptyChunk]);
this._requestChunks([nextEmptyChunk]);
}
}
for (i = 0; i < loadedRequests.length; ++i) {
requestId = loadedRequests[i];
var callback = this.callbacksByRequest[requestId];
delete this.callbacksByRequest[requestId];
if (callback) {
callback();
}
var capability = this.promisesByRequest[requestId];
delete this.promisesByRequest[requestId];
capability.resolve();
}
this.msgHandler.send('DocProgress', {
@ -1839,6 +1839,16 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
getEndChunk: function ChunkedStreamManager_getEndChunk(end) {
var chunk = Math.floor((end - 1) / this.chunkSize) + 1;
return chunk;
},
abort: function ChunkedStreamManager_abort() {
if (this.networkManager) {
this.networkManager.abortAllRequests();
}
for(var requestId in this.promisesByRequest) {
var capability = this.promisesByRequest[requestId];
capability.reject(new Error('Request was aborted'));
}
}
};
@ -2010,7 +2020,8 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() {
reject(e);
return;
}
pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper);
pdfManager.streamManager.requestRange(e.begin, e.end).
then(ensureHelper, reject);
}
}
@ -2020,11 +2031,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() {
NetworkPdfManager.prototype.requestRange =
function NetworkPdfManager_requestRange(begin, end) {
return new Promise(function (resolve) {
this.streamManager.requestRange(begin, end, function() {
resolve();
});
}.bind(this));
return this.streamManager.requestRange(begin, end);
};
NetworkPdfManager.prototype.requestLoadedStream =
@ -2044,7 +2051,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() {
NetworkPdfManager.prototype.terminate =
function NetworkPdfManager_terminate() {
this.streamManager.networkManager.abortAllRequests();
this.streamManager.abort();
};
return NetworkPdfManager;
@ -2189,7 +2196,7 @@ var Page = (function PageClosure() {
}.bind(this));
},
getOperatorList: function Page_getOperatorList(handler, intent) {
getOperatorList: function Page_getOperatorList(handler, task, intent) {
var self = this;
var pdfManager = this.pdfManager;
@ -2222,8 +2229,8 @@ var Page = (function PageClosure() {
pageIndex: self.pageIndex,
intent: intent
});
return partialEvaluator.getOperatorList(contentStream, self.resources,
opList).then(function () {
return partialEvaluator.getOperatorList(contentStream, task,
self.resources, opList).then(function () {
return opList;
});
});
@ -2240,7 +2247,7 @@ var Page = (function PageClosure() {
}
var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageOpList, pdfManager, partialEvaluator, intent);
annotations, pageOpList, pdfManager, partialEvaluator, task, intent);
return annotationsReadyPromise.then(function () {
pageOpList.flush(true);
return pageOpList;
@ -2248,7 +2255,7 @@ var Page = (function PageClosure() {
});
},
extractTextContent: function Page_extractTextContent() {
extractTextContent: function Page_extractTextContent(task) {
var handler = {
on: function nullHandlerOn() {},
send: function nullHandlerSend() {}
@ -2277,6 +2284,7 @@ var Page = (function PageClosure() {
self.fontCache);
return partialEvaluator.getTextContent(contentStream,
task,
self.resources);
});
},
@ -2689,7 +2697,8 @@ var Dict = (function DictClosure() {
// Same as get(), but dereferences all elements if the result is an Array.
getArray: function Dict_getArray(key1, key2, key3) {
var value = this.get(key1, key2, key3);
if (!isArray(value)) {
var xref = this.xref;
if (!isArray(value) || !xref) {
return value;
}
value = value.slice(); // Ensure that we don't modify the Dict data.
@ -2697,7 +2706,7 @@ var Dict = (function DictClosure() {
if (!isRef(value[i])) {
continue;
}
value[i] = this.xref.fetch(value[i]);
value[i] = xref.fetch(value[i]);
}
return value;
},
@ -3928,9 +3937,9 @@ var XRef = (function XRefClosure() {
resolve(xref.fetch(ref, suppressEncryption));
} catch (e) {
if (e instanceof MissingDataException) {
streamManager.requestRange(e.begin, e.end, function () {
streamManager.requestRange(e.begin, e.end).then(function () {
tryFetch(resolve, reject);
});
}, reject);
return;
}
reject(e);
@ -4206,6 +4215,7 @@ var ObjectLoader = (function() {
this.keys = keys;
this.xref = xref;
this.refSet = null;
this.capability = null;
}
ObjectLoader.prototype = {
@ -4226,11 +4236,11 @@ var ObjectLoader = (function() {
nodesToVisit.push(this.obj[keys[i]]);
}
this.walk(nodesToVisit);
this._walk(nodesToVisit);
return this.capability.promise;
},
walk: function ObjectLoader_walk(nodesToVisit) {
_walk: function ObjectLoader_walk(nodesToVisit) {
var nodesToRevisit = [];
var pendingRequests = [];
// DFS walk of the object graph.
@ -4277,7 +4287,7 @@ var ObjectLoader = (function() {
}
if (pendingRequests.length) {
this.xref.stream.manager.requestRanges(pendingRequests,
this.xref.stream.manager.requestRanges(pendingRequests).then(
function pendingRequestCallback() {
nodesToVisit = nodesToRevisit;
for (var i = 0; i < nodesToRevisit.length; i++) {
@ -4288,8 +4298,8 @@ var ObjectLoader = (function() {
this.refSet.remove(node);
}
}
this.walk(nodesToVisit);
}.bind(this));
this._walk(nodesToVisit);
}.bind(this), this.capability.reject);
return;
}
// Everything is loaded.
@ -4663,7 +4673,7 @@ var Annotation = (function AnnotationClosure() {
}.bind(this));
},
getOperatorList: function Annotation_getOperatorList(evaluator) {
getOperatorList: function Annotation_getOperatorList(evaluator, task) {
if (!this.appearance) {
return Promise.resolve(new OperatorList());
@ -4690,7 +4700,8 @@ var Annotation = (function AnnotationClosure() {
return resourcesPromise.then(function(resources) {
var opList = new OperatorList();
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
return evaluator.getOperatorList(self.appearance, resources, opList).
return evaluator.getOperatorList(self.appearance, task,
resources, opList).
then(function () {
opList.addOp(OPS.endAnnotation, []);
self.appearance.reset();
@ -4701,7 +4712,7 @@ var Annotation = (function AnnotationClosure() {
};
Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
annotations, opList, pdfManager, partialEvaluator, intent) {
annotations, opList, pdfManager, partialEvaluator, task, intent) {
function reject(e) {
annotationsReadyCapability.reject(e);
@ -4714,7 +4725,7 @@ var Annotation = (function AnnotationClosure() {
if (intent === 'display' && annotations[i].isViewable() ||
intent === 'print' && annotations[i].isPrintable()) {
annotationPromises.push(
annotations[i].getOperatorList(partialEvaluator));
annotations[i].getOperatorList(partialEvaluator, task));
}
}
Promise.all(annotationPromises).then(function(datas) {
@ -4946,9 +4957,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
}
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator,
task) {
if (this.appearance) {
return Annotation.prototype.getOperatorList.call(this, evaluator);
return Annotation.prototype.getOperatorList.call(this, evaluator, task);
}
var opList = new OperatorList();
@ -4961,7 +4973,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
}
var stream = new Stream(stringToBytes(data.defaultAppearance));
return evaluator.getOperatorList(stream, this.fieldResources, opList).
return evaluator.getOperatorList(stream, task,
this.fieldResources, opList).
then(function () {
return opList;
});
@ -9176,9 +9189,10 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
if (pdfAlgorithm.checkUserPassword(password, userValidationSalt,
userPassword)) {
return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption);
} else if (pdfAlgorithm.checkOwnerPassword(password, ownerValidationSalt,
uBytes,
ownerPassword)) {
} else if (password.length && pdfAlgorithm.checkOwnerPassword(password,
ownerValidationSalt,
uBytes,
ownerPassword)) {
return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes,
ownerEncryption);
}
@ -10348,6 +10362,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
xobj, smask,
operatorList,
task,
initialState) {
var matrix = xobj.dict.getArray('Matrix');
var bbox = xobj.dict.getArray('BBox');
@ -10380,7 +10395,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
return this.getOperatorList(xobj,
return this.getOperatorList(xobj, task,
(xobj.dict.get('Resources') || resources), operatorList, initialState).
then(function () {
operatorList.addOp(OPS.paintFormXObjectEnd, []);
@ -10492,7 +10507,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
handleSMask: function PartialEvaluator_handleSmask(smask, resources,
operatorList,
operatorList, task,
stateManager) {
var smaskContent = smask.get('G');
var smaskOptions = {
@ -10500,18 +10515,22 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
backdrop: smask.get('BC')
};
return this.buildFormXObject(resources, smaskContent, smaskOptions,
operatorList, stateManager.state.clone());
operatorList, task, stateManager.state.clone());
},
handleTilingType:
function PartialEvaluator_handleTilingType(fn, args, resources,
pattern, patternDict,
operatorList) {
operatorList, task) {
// Create an IR of the pattern code.
var tilingOpList = new OperatorList();
return this.getOperatorList(pattern,
(patternDict.get('Resources') || resources), tilingOpList).
then(function () {
// Merge the available resources, to prevent issues when the patternDict
// is missing some /Resources entries (fixes issue6541.pdf).
var resourcesArray = [patternDict.get('Resources'), resources];
var patternResources = Dict.merge(this.xref, resourcesArray);
return this.getOperatorList(pattern, task, patternResources,
tilingOpList).then(function () {
// Add the dependencies to the parent operator list so they are
// resolved before sub operator list is executed synchronously.
operatorList.addDependencies(tilingOpList.dependencies);
@ -10524,7 +10543,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
handleSetFont:
function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef,
operatorList, state) {
operatorList, task, state) {
// TODO(mack): Not needed?
var fontName;
if (fontArgs) {
@ -10538,8 +10557,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!translated.font.isType3Font) {
return translated;
}
return translated.loadType3Data(self, resources, operatorList).then(
function () {
return translated.loadType3Data(self, resources, operatorList, task).
then(function () {
return translated;
});
}).then(function (translated) {
@ -10586,8 +10605,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
setGState: function PartialEvaluator_setGState(resources, gState,
operatorList, xref,
stateManager) {
operatorList, task,
xref, stateManager) {
// This array holds the converted/processed state data.
var gStateObj = [];
var gStateMap = gState.map;
@ -10611,8 +10630,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
break;
case 'Font':
promise = promise.then(function () {
return self.handleSetFont(resources, null, value[0],
operatorList, stateManager.state).
return self.handleSetFont(resources, null, value[0], operatorList,
task, stateManager.state).
then(function (loadedName) {
operatorList.addDependency(loadedName);
gStateObj.push([key, [loadedName, value[1]]]);
@ -10631,7 +10650,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (isDict(dict)) {
promise = promise.then(function () {
return self.handleSMask(dict, resources, operatorList,
stateManager);
task, stateManager);
});
gStateObj.push([key, true]);
} else {
@ -10812,7 +10831,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args,
cs, patterns, resources, xref) {
cs, patterns, resources, task, xref) {
// compile tiling patterns
var patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors
@ -10825,7 +10844,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (typeNum === TILING_PATTERN) {
var color = cs.base ? cs.base.getRgb(args, 0) : null;
return this.handleTilingType(fn, color, resources, pattern,
dict, operatorList);
dict, operatorList, task);
} else if (typeNum === SHADING_PATTERN) {
var shading = dict.get('Shading');
var matrix = dict.get('Matrix');
@ -10842,6 +10861,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
getOperatorList: function PartialEvaluator_getOperatorList(stream,
task,
resources,
operatorList,
initialState) {
@ -10860,6 +10880,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var timeSlotManager = new TimeSlotManager();
return new Promise(function next(resolve, reject) {
task.ensureNotTerminated();
timeSlotManager.reset();
var stop, operation = {}, i, ii, cs;
while (!(stop = timeSlotManager.check())) {
@ -10902,7 +10923,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (type.name === 'Form') {
stateManager.save();
return self.buildFormXObject(resources, xobj, null,
operatorList,
operatorList, task,
stateManager.state.clone()).
then(function () {
stateManager.restore();
@ -10926,8 +10947,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
case OPS.setFont:
var fontSize = args[1];
// eagerly collect all fonts
return self.handleSetFont(resources, args, null,
operatorList, stateManager.state).
return self.handleSetFont(resources, args, null, operatorList,
task, stateManager.state).
then(function (loadedName) {
operatorList.addDependency(loadedName);
operatorList.addOp(OPS.setFont, [loadedName, fontSize]);
@ -11033,7 +11054,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
cs = stateManager.state.fillColorSpace;
if (cs.name === 'Pattern') {
return self.handleColorN(operatorList, OPS.setFillColorN,
args, cs, patterns, resources, xref).then(function() {
args, cs, patterns, resources, task, xref).then(function() {
next(resolve, reject);
}, reject);
}
@ -11044,7 +11065,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
cs = stateManager.state.strokeColorSpace;
if (cs.name === 'Pattern') {
return self.handleColorN(operatorList, OPS.setStrokeColorN,
args, cs, patterns, resources, xref).then(function() {
args, cs, patterns, resources, task, xref).then(function() {
next(resolve, reject);
}, reject);
}
@ -11078,8 +11099,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}
var gState = extGState.get(dictName.name);
return self.setGState(resources, gState, operatorList, xref,
stateManager).then(function() {
return self.setGState(resources, gState, operatorList, task,
xref, stateManager).then(function() {
next(resolve, reject);
}, reject);
case OPS.moveTo:
@ -11093,13 +11114,31 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
case OPS.rectangle:
self.buildPath(operatorList, fn, args);
continue;
case OPS.markPoint:
case OPS.markPointProps:
case OPS.beginMarkedContent:
case OPS.beginMarkedContentProps:
case OPS.endMarkedContent:
case OPS.beginCompat:
case OPS.endCompat:
// Ignore operators where the corresponding handlers are known to
// be no-op in CanvasGraphics (display/canvas.js). This prevents
// serialization errors and is also a bit more efficient.
// We could also try to serialize all objects in a general way,
// e.g. as done in https://github.com/mozilla/pdf.js/pull/6266,
// but doing so is meaningless without knowing the semantics.
continue;
default:
// Note: Let's hope that the ignored operator does not have any
// non-serializable arguments, otherwise postMessage will throw
// "An object could not be cloned.".
}
operatorList.addOp(fn, args);
}
if (stop) {
deferred.then(function () {
next(resolve, reject);
});
}, reject);
return;
}
// Some PDFs don't close all restores inside object/form.
@ -11111,7 +11150,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
});
},
getTextContent: function PartialEvaluator_getTextContent(stream, resources,
getTextContent: function PartialEvaluator_getTextContent(stream, task,
resources,
stateManager) {
stateManager = (stateManager || new StateManager(new TextState()));
@ -11289,6 +11329,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var timeSlotManager = new TimeSlotManager();
return new Promise(function next(resolve, reject) {
task.ensureNotTerminated();
timeSlotManager.reset();
var stop, operation = {}, args = [];
while (!(stop = timeSlotManager.check())) {
@ -11444,7 +11485,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
stateManager.transform(matrix);
}
return self.getTextContent(xobj,
return self.getTextContent(xobj, task,
xobj.dict.get('Resources') || resources, stateManager).
then(function (formTextContent) {
Util.appendToArray(bidiTexts, formTextContent.items);
@ -11484,7 +11525,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (stop) {
deferred.then(function () {
next(resolve, reject);
});
}, reject);
return;
}
resolve(textContent);
@ -12049,7 +12090,7 @@ var TranslatedFont = (function TranslatedFontClosure() {
]);
this.sent = true;
},
loadType3Data: function (evaluator, resources, parentOperatorList) {
loadType3Data: function (evaluator, resources, parentOperatorList, task) {
assert(this.font.isType3Font);
if (this.type3Loaded) {
@ -12066,7 +12107,7 @@ var TranslatedFont = (function TranslatedFontClosure() {
loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
var glyphStream = charProcs[key];
var operatorList = new OperatorList();
return evaluator.getOperatorList(glyphStream, fontResources,
return evaluator.getOperatorList(glyphStream, task, fontResources,
operatorList).then(function () {
charProcOperatorList[key] = operatorList.getIR();
@ -33629,9 +33670,58 @@ var NullStream = (function NullStreamClosure() {
})();
var WorkerTask = (function WorkerTaskClosure() {
function WorkerTask(name) {
this.name = name;
this.terminated = false;
this._capability = createPromiseCapability();
}
WorkerTask.prototype = {
get finished() {
return this._capability.promise;
},
finish: function () {
this._capability.resolve();
},
terminate: function () {
this.terminated = true;
},
ensureNotTerminated: function () {
if (this.terminated) {
throw new Error('Worker task was terminated');
}
}
};
return WorkerTask;
})();
var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfManager;
var terminated = false;
var cancelXHRs = null;
var WorkerTasks = [];
function ensureNotTerminated() {
if (terminated) {
throw new Error('Worker was terminated');
}
}
function startWorkerTask(task) {
WorkerTasks.push(task);
}
function finishWorkerTask(task) {
task.finish();
var i = WorkerTasks.indexOf(task);
WorkerTasks.splice(i, 1);
}
function loadDocument(recoveryMode) {
var loadDocumentCapability = createPromiseCapability();
@ -33668,13 +33758,14 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
function getPdfManager(data) {
var pdfManagerCapability = createPromiseCapability();
var pdfManager;
var source = data.source;
var disableRange = data.disableRange;
if (source.data) {
try {
pdfManager = new LocalPdfManager(source.data, source.password);
pdfManagerCapability.resolve();
pdfManagerCapability.resolve(pdfManager);
} catch (ex) {
pdfManagerCapability.reject(ex);
}
@ -33682,7 +33773,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
} else if (source.chunkedViewerLoading) {
try {
pdfManager = new NetworkPdfManager(source, handler);
pdfManagerCapability.resolve();
pdfManagerCapability.resolve(pdfManager);
} catch (ex) {
pdfManagerCapability.reject(ex);
}
@ -33743,6 +33834,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
} catch (ex) {
pdfManagerCapability.reject(ex);
}
cancelXHRs = null;
},
onProgressiveData: source.disableStream ? null :
@ -33783,10 +33875,11 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
// the data is array, instantiating directly from it
try {
pdfManager = new LocalPdfManager(pdfFile, source.password);
pdfManagerCapability.resolve();
pdfManagerCapability.resolve(pdfManager);
} catch (ex) {
pdfManagerCapability.reject(ex);
}
cancelXHRs = null;
},
onError: function onError(status) {
@ -33801,6 +33894,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
') while retrieving PDF "' + source.url + '".', status);
handler.send('UnexpectedResponse', exception);
}
cancelXHRs = null;
},
onProgress: function onProgress(evt) {
@ -33811,6 +33905,10 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}
});
cancelXHRs = function () {
networkManager.abortRequest(fullRequestXhrId);
};
return pdfManagerCapability.promise;
}
@ -33843,8 +33941,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
var onSuccess = function(doc) {
ensureNotTerminated();
handler.send('GetDoc', { pdfInfo: doc });
};
@ -33867,6 +33965,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}
};
ensureNotTerminated();
PDFJS.maxImageSize = data.maxImageSize === undefined ?
-1 : data.maxImageSize;
PDFJS.disableFontFace = data.disableFontFace;
@ -33876,13 +33976,26 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
null : data.cMapUrl;
PDFJS.cMapPacked = data.cMapPacked === true;
getPdfManager(data).then(function () {
getPdfManager(data).then(function (newPdfManager) {
if (terminated) {
// We were in a process of setting up the manager, but it got
// terminated in the middle.
newPdfManager.terminate();
throw new Error('Worker was terminated');
}
pdfManager = newPdfManager;
handler.send('PDFManagerReady', null);
pdfManager.onLoadedStream().then(function(stream) {
handler.send('DataLoaded', { length: stream.bytes.byteLength });
});
}).then(function pdfManagerReady() {
ensureNotTerminated();
loadDocument(false).then(onSuccess, function loadFailure(ex) {
ensureNotTerminated();
// Try again with recoveryMode == true
if (!(ex instanceof XRefParseException)) {
if (ex instanceof PasswordException) {
@ -33897,6 +34010,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
pdfManager.requestLoadedStream();
pdfManager.onLoadedStream().then(function() {
ensureNotTerminated();
loadDocument(true).then(onSuccess, onFailure);
});
}, onFailure);
@ -33987,17 +34102,25 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
});
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
pdfManager.getPage(data.pageIndex).then(function(page) {
var pageIndex = data.pageIndex;
pdfManager.getPage(pageIndex).then(function(page) {
var task = new WorkerTask('RenderPageRequest: page ' + pageIndex);
startWorkerTask(task);
var pageNum = data.pageIndex + 1;
var pageNum = pageIndex + 1;
var start = Date.now();
// Pre compile the pdf page and fetch the fonts/images.
page.getOperatorList(handler, data.intent).then(function(operatorList) {
page.getOperatorList(handler, task, data.intent).then(
function(operatorList) {
finishWorkerTask(task);
info('page=' + pageNum + ' - getOperatorList: time=' +
(Date.now() - start) + 'ms, len=' + operatorList.fnArray.length);
}, function(e) {
finishWorkerTask(task);
if (task.terminated) {
return; // ignoring errors from the terminated thread
}
var minimumStackMessage =
'worker.js: while trying to getPage() and getOperatorList()';
@ -34032,13 +34155,23 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}, this);
handler.on('GetTextContent', function wphExtractText(data) {
return pdfManager.getPage(data.pageIndex).then(function(page) {
var pageNum = data.pageIndex + 1;
var pageIndex = data.pageIndex;
return pdfManager.getPage(pageIndex).then(function(page) {
var task = new WorkerTask('GetTextContent: page ' + pageIndex);
startWorkerTask(task);
var pageNum = pageIndex + 1;
var start = Date.now();
return page.extractTextContent().then(function(textContent) {
return page.extractTextContent(task).then(function(textContent) {
finishWorkerTask(task);
info('text indexing: page=' + pageNum + ' - time=' +
(Date.now() - start) + 'ms');
return textContent;
}, function (reason) {
finishWorkerTask(task);
if (task.terminated) {
return; // ignoring errors from the terminated thread
}
throw reason;
});
});
});
@ -34048,7 +34181,22 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
});
handler.on('Terminate', function wphTerminate(data) {
pdfManager.terminate();
terminated = true;
if (pdfManager) {
pdfManager.terminate();
pdfManager = null;
}
if (cancelXHRs) {
cancelXHRs();
}
var waitOn = [];
WorkerTasks.forEach(function (task) {
waitOn.push(task.finished);
task.terminate();
});
return Promise.all(waitOn).then(function () {});
});
}
};

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

@ -3491,7 +3491,7 @@ var PDFPageView = (function PDFPageViewClosure() {
this.zoomLayer = null;
this.reset();
if (this.pdfPage) {
this.pdfPage.destroy();
this.pdfPage.cleanup();
}
},
@ -3834,9 +3834,9 @@ var PDFPageView = (function PDFPageViewClosure() {
canvasContext: ctx,
viewport: this.viewport,
// intent: 'default', // === 'display'
continueCallback: renderContinueCallback
};
var renderTask = this.renderTask = this.pdfPage.render(renderContext);
renderTask.onContinue = renderContinueCallback;
this.renderTask.promise.then(
function pdfPageRenderCallback() {
@ -5541,10 +5541,10 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
var renderContext = {
canvasContext: ctx,
viewport: drawViewport,
continueCallback: renderContinueCallback
viewport: drawViewport
};
var renderTask = this.renderTask = this.pdfPage.render(renderContext);
renderTask.onContinue = renderContinueCallback;
renderTask.promise.then(
function pdfPageRenderCallback() {
@ -6036,6 +6036,7 @@ var PDFViewerApplication = {
initialized: false,
fellback: false,
pdfDocument: null,
pdfLoadingTask: null,
sidebarOpen: false,
printing: false,
/** @type {PDFViewer} */
@ -6347,6 +6348,11 @@ var PDFViewerApplication = {
function FirefoxComDataRangeTransport_requestDataRange(begin, end) {
FirefoxCom.request('requestDataRange', { begin: begin, end: end });
};
FirefoxComDataRangeTransport.prototype.abort =
function FirefoxComDataRangeTransport_abort() {
// Sync call to ensure abort is really started.
FirefoxCom.requestSync('abortLoading', null);
};
var pdfDataRangeTransport;
@ -6366,8 +6372,8 @@ var PDFViewerApplication = {
pdfDataRangeTransport =
new FirefoxComDataRangeTransport(args.length, args.data);
PDFViewerApplication.open(args.pdfUrl, 0, undefined,
pdfDataRangeTransport);
PDFViewerApplication.open(args.pdfUrl,
{range: pdfDataRangeTransport});
if (args.length) {
PDFViewerApplication.pdfDocumentProperties
@ -6392,7 +6398,7 @@ var PDFViewerApplication = {
'An error occurred while loading the PDF.'), e);
break;
}
PDFViewerApplication.open(args.data, 0);
PDFViewerApplication.open(args.data);
break;
}
});
@ -6418,36 +6424,76 @@ var PDFViewerApplication = {
document.title = title;
},
/**
* Closes opened PDF document.
* @returns {Promise} - Returns the promise, which is resolved when all
* destruction is completed.
*/
close: function pdfViewClose() {
var errorWrapper = document.getElementById('errorWrapper');
errorWrapper.setAttribute('hidden', 'true');
if (!this.pdfDocument) {
return;
if (!this.pdfLoadingTask) {
return Promise.resolve();
}
this.pdfDocument.destroy();
this.pdfDocument = null;
var promise = this.pdfLoadingTask.destroy();
this.pdfLoadingTask = null;
this.pdfThumbnailViewer.setDocument(null);
this.pdfViewer.setDocument(null);
this.pdfLinkService.setDocument(null, null);
if (this.pdfDocument) {
this.pdfDocument = null;
this.pdfThumbnailViewer.setDocument(null);
this.pdfViewer.setDocument(null);
this.pdfLinkService.setDocument(null, null);
}
if (typeof PDFBug !== 'undefined') {
PDFBug.cleanup();
}
return promise;
},
// TODO(mack): This function signature should really be pdfViewOpen(url, args)
open: function pdfViewOpen(file, scale, password,
pdfDataRangeTransport, args) {
if (this.pdfDocument) {
// Reload the preferences if a document was previously opened.
Preferences.reload();
/**
* Opens PDF document specified by URL or array with additional arguments.
* @param {string|TypedArray|ArrayBuffer} file - PDF location or binary data.
* @param {Object} args - (optional) Additional arguments for the getDocument
* call, e.g. HTTP headers ('httpHeaders') or
* alternative data transport ('range').
* @returns {Promise} - Returns the promise, which is resolved when document
* is opened.
*/
open: function pdfViewOpen(file, args) {
var scale = 0;
if (arguments.length > 2 || typeof args === 'number') {
console.warn('Call of open() with obsolete signature.');
if (typeof args === 'number') {
scale = args; // scale argument was found
}
args = arguments[4] || null;
if (arguments[3] && typeof arguments[3] === 'object') {
// The pdfDataRangeTransport argument is present.
args = Object.create(args);
args.range = arguments[3];
}
if (typeof arguments[2] === 'string') {
// The password argument is present.
args = Object.create(args);
args.password = arguments[2];
}
}
this.close();
var parameters = {password: password};
if (this.pdfLoadingTask) {
// We need to destroy already opened document.
return this.close().then(function () {
// Reload the preferences if a document was previously opened.
Preferences.reload();
// ... and repeat the open() call.
return this.open(file, args);
}.bind(this));
}
var parameters = Object.create(null);
if (typeof file === 'string') { // URL
this.setTitleUsingUrl(file);
parameters.url = file;
@ -6466,18 +6512,20 @@ var PDFViewerApplication = {
var self = this;
self.downloadComplete = false;
var passwordNeeded = function passwordNeeded(updatePassword, reason) {
var loadingTask = PDFJS.getDocument(parameters);
this.pdfLoadingTask = loadingTask;
loadingTask.onPassword = function passwordNeeded(updatePassword, reason) {
PasswordPrompt.updatePassword = updatePassword;
PasswordPrompt.reason = reason;
PasswordPrompt.open();
};
function getDocumentProgress(progressData) {
loadingTask.onProgress = function getDocumentProgress(progressData) {
self.progress(progressData.loaded / progressData.total);
}
};
PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
getDocumentProgress).then(
var result = loadingTask.promise.then(
function getDocumentCallback(pdfDocument) {
self.load(pdfDocument, scale);
},
@ -6503,12 +6551,15 @@ var PDFViewerApplication = {
message: message
};
self.error(loadingErrorMessage, moreInfo);
throw new Error(loadingErrorMessage);
}
);
if (args && args.length) {
PDFViewerApplication.pdfDocumentProperties.setFileSize(args.length);
}
return result;
},
download: function pdfViewDownload() {
@ -6879,6 +6930,9 @@ var PDFViewerApplication = {
},
cleanup: function pdfViewCleanup() {
if (!this.pdfDocument) {
return; // run cleanup when document is loaded
}
this.pdfViewer.cleanup();
this.pdfThumbnailViewer.cleanup();
this.pdfDocument.cleanup();

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

@ -30,6 +30,7 @@ marker.label.garbageCollection.nonIncremental=Non-incremental GC
marker.label.cycleCollection=Cycle Collection
marker.label.cycleCollection.forgetSkippable=CC Graph Reduction
marker.label.timestamp=Timestamp
marker.label.worker=Worker
marker.label.unknown=Unknown
# LOCALIZATION NOTE (marker.label.javascript.*):
@ -75,6 +76,11 @@ marker.field.restyleHint=Restyle Hint:
marker.field.causeName=Cause:
# General "type" for a marker (Cycle Collection, Garbage Collection)
marker.field.type=Type:
# The type of operation performed by a Worker.
marker.worker.serializeDataOffMainThread=Serialize data in Worker
marker.worker.serializeDataOnMainThread=Serialize data on the main thread
marker.worker.deserializeDataOffMainThread=Deserialize data in Worker
marker.worker.deserializeDataOnMainThread=Deserialize data on the main thread
# Strings used in the waterfall sidebar as values.
marker.value.unknownFrame=<unknown location>

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

@ -102,3 +102,5 @@
<!ENTITY clearOutputCtrl.key "L">
<!ENTITY openInVarViewCmd.label "Open in Variables View">
<!ENTITY openInVarViewCmd.accesskey "V">
<!ENTITY storeAsGlobalVar.label "Store as global variable">
<!ENTITY storeAsGlobalVar.accesskey "S">

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

@ -433,6 +433,7 @@ case "$target" in
AC_SUBST(ANDROID_TOOLS)
MOZ_ANDROID_AAR(appcompat-v7, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(design, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(recyclerview-v7, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(support-v4, 23.0.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)

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

@ -28,7 +28,7 @@ add_task(function* () {
let jsterm = hud.jsterm;
let jstermInput = jsterm.hud.document.querySelector(".jsterm-input-node");
ok(jstermInput.value === "temp0", "first console variable is named temp0");
is(jstermInput.value, "temp0", "first console variable is named temp0");
let result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var">'), -1, "variable temp0 references correct node");
@ -37,7 +37,7 @@ add_task(function* () {
dispatchCommandEvent(useInConsoleNode);
yield inspector.once("console-var-ready");
ok(jstermInput.value === "temp1", "second console variable is named temp1");
is(jstermInput.value, "temp1", "second console variable is named temp1");
result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var-multi">'), -1, "variable temp1 references correct node");

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

@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { actions, ALLOCATION_RECORDING_OPTIONS } = require("../constants");
exports.toggleRecordingAllocationStacks = function (front) {
return function* (dispatch, getState) {
dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_START });
if (getState().recordingAllocationStacks) {
yield front.stopRecordingAllocations();
} else {
yield front.startRecordingAllocations(ALLOCATION_RECORDING_OPTIONS);
}
dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_END });
};
};

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

@ -4,6 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'allocations.js',
'breakdown.js',
'snapshot.js',
)

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

@ -122,4 +122,3 @@ const selectSnapshot = exports.selectSnapshot = function (snapshot) {
snapshot
};
};

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

@ -4,8 +4,9 @@
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { selectSnapshotAndRefresh, takeSnapshotAndCensus } = require("./actions/snapshot");
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
const { setBreakdownAndRefresh } = require("./actions/breakdown");
const { selectSnapshotAndRefresh, takeSnapshotAndCensus } = require("./actions/snapshot");
const { breakdownNameToSpec, getBreakdownDisplayData } = require("./utils");
const Toolbar = createFactory(require("./components/toolbar"));
const List = createFactory(require("./components/list"));
@ -31,7 +32,15 @@ const App = createClass({
},
render() {
let { dispatch, snapshots, front, heapWorker, breakdown } = this.props;
let {
dispatch,
snapshots,
front,
heapWorker,
breakdown,
allocations,
} = this.props;
let selectedSnapshot = snapshots.find(s => s.selected);
return (
@ -42,6 +51,9 @@ const App = createClass({
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
onBreakdownChange: breakdown =>
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
onToggleRecordAllocationStacks: () =>
dispatch(toggleRecordingAllocationStacks(front)),
allocations
}),
dom.div({ id: "memory-tool-container" }, [
@ -66,7 +78,10 @@ const App = createClass({
* and passed to components.
*/
function mapStateToProps (state) {
return { snapshots: state.snapshots };
return {
allocations: state.allocations,
snapshots: state.snapshots
};
}
module.exports = connect(mapStateToProps)(App);

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

@ -4,6 +4,8 @@
const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const models = require("../models");
const Toolbar = module.exports = createClass({
displayName: "toolbar",
propTypes: {
@ -13,17 +15,38 @@ const Toolbar = module.exports = createClass({
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
onBreakdownChange: PropTypes.func.isRequired,
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
allocations: models.allocations
},
render() {
let { onTakeSnapshotClick, onBreakdownChange, breakdowns } = this.props;
let {
onTakeSnapshotClick,
onBreakdownChange,
breakdowns,
onToggleRecordAllocationStacks,
allocations,
} = this.props;
return (
DOM.div({ className: "devtools-toolbar" }, [
DOM.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
DOM.select({
className: `select-breakdown`,
onChange: e => onBreakdownChange(e.target.value),
}, breakdowns.map(({ name, displayName }) => DOM.option({ value: name }, displayName)))
}, breakdowns.map(({ name, displayName }) => DOM.option({ value: name }, displayName))),
DOM.label({}, [
DOM.input({
type: "checkbox",
checked: allocations.recording,
disabled: allocations.togglingInProgress,
onChange: onToggleRecordAllocationStacks,
}),
// TODO bug 1214799
"Record allocation stacks"
])
])
);
}

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

@ -19,9 +19,19 @@ actions.READ_SNAPSHOT_END = "read-snapshot-end";
actions.TAKE_CENSUS_START = "take-census-start";
actions.TAKE_CENSUS_END = "take-census-end";
// When requesting that the server start/stop recording allocation stacks.
actions.TOGGLE_RECORD_ALLOCATION_STACKS_START = "toggle-record-allocation-stacks-start";
actions.TOGGLE_RECORD_ALLOCATION_STACKS_END = "toggle-record-allocation-stacks-end";
// Fired by UI to select a snapshot to view.
actions.SELECT_SNAPSHOT = "select-snapshot";
// Options passed to MemoryFront's startRecordingAllocations never change.
exports.ALLOCATION_RECORDING_OPTIONS = {
probability: 1,
maxLogLength: 1
};
const COUNT = { by: "count", count: true, bytes: true };
const INTERNAL_TYPE = { by: "internalType", then: COUNT };
const ALLOCATION_STACK = { by: "allocationStack", then: COUNT, noStack: COUNT };

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

@ -51,9 +51,19 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
},
});
let allocationsModel = exports.allocations = PropTypes.shape({
// True iff we are recording allocation stacks right now.
recording: PropTypes.bool.isRequired,
// True iff we are in the process of toggling the recording of allocation
// stacks on or off right now.
togglingInProgress: PropTypes.bool.isRequired,
});
let appModel = exports.app = {
// {MemoryFront} Used to communicate with platform
front: PropTypes.instanceOf(MemoryFront),
// Allocations recording related data.
allocations: allocationsModel,
// {HeapAnalysesClient} Used to interface with snapshots
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
// The breakdown object DSL describing how we want

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

@ -1,7 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
exports.allocations = require("./reducers/allocations");
exports.snapshots = require("./reducers/snapshots");
exports.breakdown = require("./reducers/breakdown");
exports.errors = require("./reducers/errors");

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

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { assert } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
let handlers = Object.create(null);
handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_START] = function (state, action) {
assert(!state.togglingInProgress,
"Changing recording state must not be reentrant.");
return {
recording: !state.recording,
togglingInProgress: true,
};
};
handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_END] = function (state, action) {
assert(state.togglingInProgress,
"Should not complete changing recording state if we weren't changing "
+ "recording state already.");
return {
recording: state.recording,
togglingInProgress: false,
};
};
const DEFAULT_ALLOCATIONS_STATE = {
recording: false,
togglingInProgress: false
};
module.exports = function (state = DEFAULT_ALLOCATIONS_STATE, action) {
let handle = handlers[action.type];
if (handle) {
return handle(state, action);
}
return state;
};

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

@ -4,6 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'allocations.js',
'breakdown.js',
'errors.js',
'snapshots.js',

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

@ -28,6 +28,8 @@ function initDebugger () {
}
function StubbedMemoryFront () {
this.state = "detached";
this.recordingAllocations = false;
this.dbg = initDebugger();
}
@ -43,6 +45,14 @@ StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached", Task.asy
return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
}), "saveHeapSnapshot");
StubbedMemoryFront.prototype.startRecordingAllocations = expectState("attached", Task.async(function* () {
this.recordingAllocations = true;
}));
StubbedMemoryFront.prototype.stopRecordingAllocations = expectState("attached", Task.async(function* () {
this.recordingAllocations = false;
}));
function waitUntilState (store, predicate) {
let deferred = promise.defer();
let unsubscribe = store.subscribe(check);

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

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the action creator `setBreakdown()` for breakdown changing.
* Does not test refreshing the census information, check `setBreakdownAndRefresh` action
* for that.
*/
let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
equal(getState().allocations.recording, false, "not recording by default");
equal(getState().allocations.togglingInProgress, false,
"not in the process of toggling by default");
dispatch(toggleRecordingAllocationStacks(front));
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
ok(true, "`togglingInProgress` set to true when toggling on");
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
equal(getState().allocations.recording, true, "now we are recording");
ok(front.recordingAllocations, "front is recording too");
dispatch(toggleRecordingAllocationStacks(front));
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
ok(true, "`togglingInProgress` set to true when toggling off");
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
equal(getState().allocations.recording, false, "now we are not recording");
ok(front.recordingAllocations, "front is not recording anymore");
});

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

@ -5,6 +5,7 @@ tail =
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-toggle-recording-allocations.js]
[test_action-select-snapshot.js]
[test_action-set-breakdown.js]
[test_action-set-breakdown-and-refresh-01.js]

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

@ -144,3 +144,29 @@ A marker generated via `console.timeStamp(label)`.
## Parse HTML
## Parse XML
## Worker
Emitted whenever there's an operation dealing with Workers (any kind of worker,
Web Workers, Service Workers etc.). Currently there are 4 types of operations
being tracked: serializing/deserializing data on the main thread, and also
serializing/deserializing data off the main thread.
* ProfileTimelineWorkerOperationType operationType - the type of operation
being done by the Worker or the main thread when dealing with workers.
Can be one of the following enums defined in ProfileTimelineMarker.webidl
* "serializeDataOffMainThread"
* "serializeDataOnMainThread"
* "deserializeDataOffMainThread"
* "deserializeDataOnMainThread"
## Composite
Composite markers trace the actual time an inner composite operation
took on the compositor thread. Currently, these markers are only especially
interesting for Gecko platform developers, and thus disabled by default.
## CompositeForwardTransaction
Markers generated when the IPC request was made to the compositor from
the child process's main thread.

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

@ -439,6 +439,13 @@ const Formatters = {
[L10N.getStr("marker.field.type")]: marker.name.replace(/nsCycleCollector::/g, "")
};
},
WorkerFields: function(marker) {
return {
[L10N.getStr("marker.field.type")]:
L10N.getStr(`marker.worker.${marker.workerOperation}`)
};
}
};
/**

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

@ -125,6 +125,12 @@ const TIMELINE_BLUEPRINT = {
label: L10N.getStr("marker.label.cycleCollection.forgetSkippable"),
fields: Formatters.CycleCollectionFields,
},
"Worker": {
group: 1,
colorName: "graphs-orange",
label: L10N.getStr("marker.label.worker"),
fields: Formatters.WorkerFields
},
/* Group 2 - User Controlled */
"ConsoleTime": {

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

@ -6,6 +6,8 @@ support-files =
doc_innerHTML.html
doc_markers.html
doc_simple-test.html
doc_worker.html
js_simpleWorker.js
head.js
[browser_aaa-run-first-leaktest.js]
@ -145,3 +147,4 @@ skip-if = true # Bug 1176370
skip-if = true # Bug 1170105
[browser_timeline-waterfall-sidebar.js]
skip-if = true # Bug 1161817
[browser_timeline-waterfall-workers.js]

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

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the sidebar is properly updated with worker markers.
*/
function* spawnTest() {
let { panel } = yield initPerformance(WORKER_URL);
let { PerformanceController } = panel.panelWin;
loadFrameScripts();
yield startRecording(panel);
ok(true, "Recording has started.");
evalInDebuggee("performWork()");
yield waitUntil(() => {
// Wait until we get the worker markers.
let markers = PerformanceController.getCurrentRecording().getMarkers();
if (!markers.some(m => m.name == "Worker") ||
!markers.some(m => m.workerOperation == "serializeDataOffMainThread") ||
!markers.some(m => m.workerOperation == "serializeDataOnMainThread") ||
!markers.some(m => m.workerOperation == "deserializeDataOffMainThread") ||
!markers.some(m => m.workerOperation == "deserializeDataOnMainThread")) {
return false;
}
testWorkerMarker(markers.find(m => m.name == "Worker"));
return true;
});
yield stopRecording(panel);
ok(true, "Recording has ended.");
yield teardown(panel);
finish();
}
function testWorkerMarker(marker) {
ok(true, "Found a worker marker.");
ok("start" in marker,
"The start time is specified in the worker marker.");
ok("end" in marker,
"The end time is specified in the worker marker.");
ok("workerOperation" in marker,
"The worker operation is specified in the worker marker.");
}
/**
* Takes a string `script` and evaluates it directly in the content
* in potentially a different process.
*/
function evalInDebuggee (script) {
let { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
let deferred = Promise.defer();
if (!mm) {
throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
}
let id = generateUUID().toString();
mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
mm.addMessageListener("devtools:test:eval:response", handler);
function handler ({ data }) {
if (id !== data.id) {
return;
}
mm.removeMessageListener("devtools:test:eval:response", handler);
deferred.resolve(data.value);
}
return deferred.promise;
}

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

@ -0,0 +1,26 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance test page</title>
</head>
<body>
<script type="text/javascript">
function performWork() {
var worker = new Worker("js_simpleWorker.js");
worker.addEventListener("message", function(e) {
console.log(e.data);
console.timeStamp("Done");
}, false);
worker.postMessage("Hello World");
}
</script>
</body>
</html>

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

@ -29,6 +29,7 @@ const EXAMPLE_URL = "http://example.com/browser/devtools/client/performance/test
const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
const MARKERS_URL = EXAMPLE_URL + "doc_markers.html";
const ALLOCS_URL = EXAMPLE_URL + "doc_allocs.html";
const WORKER_URL = EXAMPLE_URL + "doc_worker.html";
const MEMORY_SAMPLE_PROB_PREF = "devtools.performance.memory.sample-probability";
const MEMORY_MAX_LOG_LEN_PREF = "devtools.performance.memory.max-log-length";

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

@ -0,0 +1,4 @@
self.addEventListener('message', function(e) {
self.postMessage(e.data);
self.close()
}, false);

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

@ -3,26 +3,6 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
const devtools = Cu.import("resource://devtools/shared/Loader.jsm", {}).devtools;
const { joinURI } = devtools.require("devtools/shared/path");
var appConstants;
// Some of the services that the system module requires is not
// available in xpcshell tests. This is ok, we can easily polyfill the
// values that we need.
try {
const system = devtools.require("devtools/shared/system");
appConstants = system.constants;
}
catch(e) {
// We are in a testing environment most likely. There isn't much
// risk to this defaulting to true because the dev version of React
// will be loaded if this is true, and that file doesn't get built
// into the release version of Firefox, so this will only work with
// dev environments.
appConstants = {
DEBUG_JS_MODULES: true
};
}
const VENDOR_CONTENT_URL = "resource://devtools/client/shared/vendor";
/*
@ -51,20 +31,12 @@ const VENDOR_CONTENT_URL = "resource://devtools/client/shared/vendor";
* - require: a function to require modules with
*/
function BrowserLoader(baseURI, window) {
const loaderOptions = devtools.require('@loader/options');
let dynamicPaths = {};
if (appConstants.DEBUG_JS_MODULES) {
// Load in the dev version of React
dynamicPaths["devtools/shared/vendor/react"] =
"resource://devtools/vendor/react-dev.js";
}
const loaderOptions = devtools.require("@loader/options");
const opts = {
id: "browser-loader",
sharedGlobal: true,
sandboxPrototype: window,
paths: Object.assign({}, loaderOptions.paths, dynamicPaths),
paths: Object.assign({}, loaderOptions.paths),
invisibleToDebugger: loaderOptions.invisibleToDebugger,
require: (id, require) => {
const uri = require.resolve(id);

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

@ -3,12 +3,16 @@ sanity checks and better errors, but at a slight perf cost. The prod
version available on the web is by default minified, but we don't want
a minified version, so we need to build it ourselves.
The react.js and react-dev.js were generated with the following steps:
In bug 1217979, we are only using react in development environment for now until
we can think of a way to conditionally build these different versions, so
the `react.js` here is the dev environment one and generated with the following steps:
* git clone https://github.com/facebook/react.git && cd react
* npm install
* grunt build
* cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react-dev.js
* cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react.js
For production, which we do not currently have:
* NODE_ENV=production grunt build
* cp build/react-with-addons.js <gecko-dev>/devtools/client/shared/vendor/react.js

16
devtools/client/shared/vendor/moz.build поставляемый
Просмотреть файл

@ -4,20 +4,8 @@
# 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/.
modules = []
if CONFIG['DEBUG_JS_MODULES']:
modules += [
'react-dev.js',
]
modules += [
DevToolsModules(
'react-redux.js',
'react.js',
'redux.js',
]
# Currently `DevToolsModules` can only be called once per moz.build, so we build
# a list manually above. Bug 1198013 tracks fixing this to make it more like
# other moz.build constructs.
DevToolsModules(*modules)
)

20669
devtools/client/shared/vendor/react-dev.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

644
devtools/client/shared/vendor/react.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -653,8 +653,6 @@ box.requests-menu-status[code^="5"] {
transition-property: max-width, -moz-padding-end, -moz-padding-start;
transition-duration: 250ms;
transition-timing-function: ease;
margin-left: 3px;
margin-right: 3px;
}
#requests-menu-filter-freetext-text:not([focused]):not([filled]) > .textbox-input-box {

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

@ -569,6 +569,10 @@ menuitem.marker-color-graphs-yellow:before,
.marker-color-graphs-yellow {
background-color: var(--theme-graphs-yellow);
}
menuitem.marker-color-graphs-orange:before,
.marker-color-graphs-orange {
background-color: var(--theme-graphs-orange);
}
menuitem.marker-color-graphs-red:before,
.marker-color-graphs-red {
background-color: var(--theme-graphs-red);

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

@ -328,7 +328,7 @@
.devtools-textinput,
.devtools-searchinput {
-moz-appearance: none;
margin: 1px 0;
margin: 1px 3px;
border: 1px solid;
%ifdef XP_MACOSX
border-radius: 20px;
@ -370,6 +370,14 @@
display: flex;
flex: 1;
position: relative;
padding: 0 3px;
}
/* The spacing is accomplished with a padding on the searchbox */
.devtools-searchbox > .devtools-textinput,
.devtools-searchbox > .devtools-searchinput {
margin-left: 0;
margin-right: 0;
}
.devtools-rule-searchbox {

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

@ -48,6 +48,7 @@
--theme-graphs-bluegrey: #0072ab;
--theme-graphs-purple: #b693eb;
--theme-graphs-yellow: #efc052;
--theme-graphs-orange: #d97e00;
--theme-graphs-red: #e57180;
--theme-graphs-grey: #cccccc;
}
@ -86,6 +87,7 @@
--theme-graphs-bluegrey: #5e88b0;
--theme-graphs-purple: #df80ff;
--theme-graphs-yellow: #d99b28;
--theme-graphs-orange: #d96629;
--theme-graphs-red: #eb5368;
--theme-graphs-grey: #757873;
}

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

@ -2534,6 +2534,25 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
});
},
storeObjectInWindow: function()
{
let evalString = `{ let i = 0;
while (this.hasOwnProperty("temp" + i) && i < 1000) {
i++;
}
this["temp" + i] = _self;
"temp" + i;
}`;
let options = {
selectedObjectActor: this.objectActor.actor,
};
this.output.owner.jsterm.requestEvaluation(evalString, options).then((res) => {
this.output.owner.jsterm.focus();
this.output.owner.jsterm.setInputValue(res.result);
});
},
/**
* The click event handler for objects shown inline.
* @private
@ -2549,6 +2568,7 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
// https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
let doc = ev.target.ownerDocument;
let cmPopup = doc.getElementById("output-contextmenu");
let openInVarViewCmd = doc.getElementById("menu_openInVarView");
let openVarView = this.openObjectInVariablesView.bind(this);
openInVarViewCmd.addEventListener("command", openVarView);
@ -2558,6 +2578,22 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
openInVarViewCmd.removeEventListener("command", openVarView);
openInVarViewCmd.setAttribute("disabled", "true");
});
// 'Store as global variable' command isn't supported on pre-44 servers,
// so remove it from the menu in that case.
let storeInGlobalCmd = doc.getElementById("menu_storeAsGlobal");
if (!this.output.webConsoleClient.traits.selectedObjectActor) {
storeInGlobalCmd.remove();
} else if (storeInGlobalCmd) {
let storeObjectInWindow = this.storeObjectInWindow.bind(this);
storeInGlobalCmd.addEventListener("command", storeObjectInWindow);
storeInGlobalCmd.removeAttribute("disabled");
cmPopup.addEventListener("popuphiding", function onPopupHiding() {
cmPopup.removeEventListener("popuphiding", onPopupHiding);
storeInGlobalCmd.removeEventListener("command", storeObjectInWindow);
storeInGlobalCmd.setAttribute("disabled", "true");
});
}
},
/**

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

@ -32,12 +32,7 @@ WebConsolePanel.prototype = {
*/
focusInput: function WCP_focusInput()
{
let inputNode = this.hud.jsterm.inputNode;
if (!inputNode.getAttribute("focused"))
{
inputNode.focus();
}
this.hud.jsterm.focus();
},
/**

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

@ -389,3 +389,4 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_netmonitor_shows_reqs_in_webconsole.js]
[browser_webconsole_bug_1050691_click_function_to_source.js]
[browser_webconsole_context_menu_open_in_var_view.js]
[browser_webconsole_context_menu_store_as_global.js]

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

@ -13,7 +13,7 @@ const TEST_URI = `data:text/html,<script>
console.log("foo", window);
</script>`;
var test = asyncTest(function*() {
add_task(function*() {
yield loadTab(TEST_URI);
let hud = yield openConsole();

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

@ -0,0 +1,66 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests the "Store as global variable" context menu item feature.
// It should be work, and be enabled only for objects
"use strict";
const TEST_URI = `data:text/html,<script>
window.bar = { baz: 1 };
console.log("foo");
console.log("foo", window.bar);
</script>`;
add_task(function*() {
yield loadTab(TEST_URI);
let hud = yield openConsole();
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
count: 2,
text: /foo/
}],
});
let [msgWithText, msgWithObj] = [...result.matched];
ok(msgWithText && msgWithObj, "Two messages should have appeared");
let contextMenu = hud.iframeWindow.document
.getElementById("output-contextmenu");
let storeAsGlobalItem = contextMenu.querySelector("#menu_storeAsGlobal");
let obj = msgWithObj.querySelector(".cm-variable");
let text = msgWithText.querySelector(".console-string");
let onceInputSet = hud.jsterm.once("set-input-value");
info("Waiting for context menu on the object");
yield waitForContextMenu(contextMenu, obj, () => {
ok(storeAsGlobalItem.disabled === false, "The \"Store as global\" " +
"context menu item should be available for objects");
storeAsGlobalItem.click();
}, () => {
ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " +
"context menu item should be disabled on popup hiding");
});
info("Waiting for context menu on the text node");
yield waitForContextMenu(contextMenu, text, () => {
ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " +
"context menu item should be disabled for texts");
});
info("Waiting for input to be set");
yield onceInputSet;
is(hud.jsterm.inputNode.value, "temp0", "Input was set");
let executedResult = yield hud.jsterm.execute();
ok(executedResult.textContent.includes("{ baz: 1 }"),
"Correct variable assigned into console");
});

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

@ -3230,6 +3230,13 @@ JSTerm.prototype = {
this.lastInputValue && this.setInputValue(this.lastInputValue);
},
focus: function() {
let inputNode = this.inputNode;
if (!inputNode.getAttribute("focused")) {
inputNode.focus();
}
},
/**
* The JavaScript evaluation response handler.
*
@ -3444,6 +3451,7 @@ JSTerm.prototype = {
bindObjectActor: aOptions.bindObjectActor,
frameActor: frameActor,
selectedNodeActor: aOptions.selectedNodeActor,
selectedObjectActor: aOptions.selectedObjectActor,
};
this.webConsoleClient.evaluateJSAsync(aString, onResult, evalOptions);
@ -3945,6 +3953,7 @@ JSTerm.prototype = {
this.completeNode.value = "";
this.resizeInput();
this._inputChanged = true;
this.emit("set-input-value");
},
/**

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

@ -82,6 +82,8 @@ function goUpdateConsoleCommands() {
selection="network" selectionType="single"/>
<menuitem id="menu_openInVarView" label="&openInVarViewCmd.label;"
accesskey="&openInVarViewCmd.accesskey;" disabled="true"/>
<menuitem id="menu_storeAsGlobal" label="&storeAsGlobalVar.label;"
accesskey="&storeAsGlobalVar.accesskey;"/>
<menuitem id="cMenu_copy"/>
<menuitem id="cMenu_selectAll"/>
</menupopup>

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

@ -91,7 +91,8 @@ function WebConsoleActor(aConnection, aParentActor)
this.traits = {
customNetworkRequest: !this._parentIsContentActor,
evaluateJSAsync: true,
transferredResponseSize: true
transferredResponseSize: true,
selectedObjectActor: true, // 44+
};
}
@ -832,6 +833,7 @@ WebConsoleActor.prototype =
frameActor: aRequest.frameActor,
url: aRequest.url,
selectedNodeActor: aRequest.selectedNodeActor,
selectedObjectActor: aRequest.selectedObjectActor,
};
let evalInfo = this.evalWithDebugger(input, evalOptions);
@ -1095,6 +1097,8 @@ WebConsoleActor.prototype =
* |evalWithBindings()| will be called with one additional binding:
* |_self| which will point to the Debugger.Object of the given
* ObjectActor.
* - selectedObjectActor: Like bindObjectActor, but executes with the
* top level window as the global.
* - frameActor: the FrameActor ID to use for evaluation. The given
* debugger frame is used for evaluation, instead of the global window.
* - selectedNodeActor: the NodeActor ID of the currently selected node
@ -1151,8 +1155,9 @@ WebConsoleActor.prototype =
// If we have an object to bind to |_self|, create a Debugger.Object
// referring to that object, belonging to dbg.
let bindSelf = null;
if (aOptions.bindObjectActor) {
let objActor = this.getActorByID(aOptions.bindObjectActor);
if (aOptions.bindObjectActor || aOptions.selectedObjectActor) {
let objActor = this.getActorByID(aOptions.bindObjectActor ||
aOptions.selectedObjectActor);
if (objActor) {
let jsObj = objActor.obj.unsafeDereference();
// If we use the makeDebuggeeValue method of jsObj's own global, then
@ -1160,8 +1165,12 @@ WebConsoleActor.prototype =
// that is, without wrappers. The evalWithBindings call will then wrap
// jsObj appropriately for the evaluation compartment.
let global = Cu.getGlobalForObject(jsObj);
dbgWindow = dbg.makeGlobalObjectReference(global);
let _dbgWindow = dbg.makeGlobalObjectReference(global);
bindSelf = dbgWindow.makeDebuggeeValue(jsObj);
if (aOptions.bindObjectActor) {
dbgWindow = _dbgWindow;
}
}
}

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

@ -255,6 +255,7 @@ WebConsoleClient.prototype = {
frameActor: aOptions.frameActor,
url: aOptions.url,
selectedNodeActor: aOptions.selectedNodeActor,
selectedObjectActor: aOptions.selectedObjectActor,
};
this._client.request(packet, aOnResponse);
},
@ -279,6 +280,7 @@ WebConsoleClient.prototype = {
frameActor: aOptions.frameActor,
url: aOptions.url,
selectedNodeActor: aOptions.selectedNodeActor,
selectedObjectActor: aOptions.selectedObjectActor,
};
this._client.request(packet, response => {

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

@ -11,6 +11,8 @@
<body>
<p>Test for JavaScript terminal functionality</p>
<iframe id="content-iframe" src="http://example.com/chrome/devtools/shared/webconsole/test/sandboxed_iframe.html"></iframe>
<script class="testbody" type="text/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
@ -21,12 +23,14 @@ let {MAX_AUTOCOMPLETE_ATTEMPTS,MAX_AUTOCOMPLETIONS} = require("devtools/shared/w
// This test runs all of its assertions twice - once with
// evaluateJS and once with evaluateJSAsync.
let evaluatingSync = true;
function evaluateJS(input, callback) {
if (evaluatingSync) {
gState.client.evaluateJS(input, callback);
} else {
gState.client.evaluateJSAsync(input, callback);
}
function evaluateJS(input, options = {}) {
return new Promise((resolve, reject) => {
if (evaluatingSync) {
gState.client.evaluateJS(input, resolve, options);
} else {
gState.client.evaluateJSAsync(input, resolve, options);
}
});
}
function startTest()
@ -63,18 +67,20 @@ function onAttach(aState, aResponse)
let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3,
doAutocomplete4, doAutocompleteLarge1, doAutocompleteLarge2,
doSimpleEval, doWindowEval, doEvalWithException,
doEvalWithHelper, doEvalString, doEvalLongString];
doEvalWithHelper, doEvalString, doEvalLongString,
doEvalWithBinding, doEvalWithBindingFrame].map(t => {
return Task.async(t);
});
runTests(tests, testEnd);
}
function doAutocomplete1()
{
function doAutocomplete1() {
info("test autocomplete for 'window.foo'");
gState.client.autocomplete("window.foo", 10, onAutocomplete1);
}
function onAutocomplete1(aResponse)
{
function onAutocomplete1(aResponse) {
let matches = aResponse.matches;
is(aResponse.matchProp, "foo", "matchProp");
@ -84,14 +90,12 @@ function onAutocomplete1(aResponse)
nextTest();
}
function doAutocomplete2()
{
function doAutocomplete2() {
info("test autocomplete for 'window.foobarObject.'");
gState.client.autocomplete("window.foobarObject.", 20, onAutocomplete2);
}
function onAutocomplete2(aResponse)
{
function onAutocomplete2(aResponse) {
let matches = aResponse.matches;
ok(!aResponse.matchProp, "matchProp");
@ -102,15 +106,13 @@ function onAutocomplete2(aResponse)
nextTest();
}
function doAutocomplete3()
{
function doAutocomplete3() {
// Check that completion suggestions are offered inside the string.
info("test autocomplete for 'dump(window.foobarObject.)'");
gState.client.autocomplete("dump(window.foobarObject.)", 25, onAutocomplete3);
}
function onAutocomplete3(aResponse)
{
function onAutocomplete3(aResponse) {
let matches = aResponse.matches;
ok(!aResponse.matchProp, "matchProp");
@ -121,31 +123,27 @@ function onAutocomplete3(aResponse)
nextTest();
}
function doAutocomplete4()
{
function doAutocomplete4() {
// Check that completion requests can have no suggestions.
info("test autocomplete for 'dump(window.foobarObject.)'");
gState.client.autocomplete("dump(window.foobarObject.)", 26, onAutocomplete4);
}
function onAutocomplete4(aResponse)
{
function onAutocomplete4(aResponse) {
ok(!aResponse.matchProp, "matchProp");
is(aResponse.matches.length, 0, "matches.length");
nextTest();
}
function doAutocompleteLarge1()
{
function doAutocompleteLarge1() {
// Check that completion requests with too large objects will
// have no suggestions.
info("test autocomplete for 'window.largeObject1.'");
gState.client.autocomplete("window.largeObject1.", 20, onAutocompleteLarge1);
}
function onAutocompleteLarge1(aResponse)
{
function onAutocompleteLarge1(aResponse) {
ok(!aResponse.matchProp, "matchProp");
info (aResponse.matches.join("|"));
is(aResponse.matches.length, 0, "Bailed out with too many properties");
@ -153,51 +151,39 @@ function onAutocompleteLarge1(aResponse)
nextTest();
}
function doAutocompleteLarge2()
{
function doAutocompleteLarge2() {
// Check that completion requests with pretty large objects will
// have MAX_AUTOCOMPLETIONS suggestions
info("test autocomplete for 'window.largeObject2.'");
gState.client.autocomplete("window.largeObject2.", 20, onAutocompleteLarge2);
}
function onAutocompleteLarge2(aResponse)
{
function onAutocompleteLarge2(aResponse) {
ok(!aResponse.matchProp, "matchProp");
is(aResponse.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS");
nextTest();
}
function doSimpleEval()
{
function* doSimpleEval() {
info("test eval '2+2'");
evaluateJS("2+2", onSimpleEval);
}
function onSimpleEval(aResponse)
{
checkObject(aResponse, {
let response = yield evaluateJS("2+2");
checkObject(response, {
from: gState.actor,
input: "2+2",
result: 4,
});
ok(!aResponse.exception, "no eval exception");
ok(!aResponse.helperResult, "no helper result");
ok(!response.exception, "no eval exception");
ok(!response.helperResult, "no helper result");
nextTest();
}
function doWindowEval()
{
function* doWindowEval() {
info("test eval 'document'");
evaluateJS("document", onWindowEval);
}
function onWindowEval(aResponse)
{
checkObject(aResponse, {
let response = yield evaluateJS("document");
checkObject(response, {
from: gState.actor,
input: "document",
result: {
@ -207,21 +193,16 @@ function onWindowEval(aResponse)
},
});
ok(!aResponse.exception, "no eval exception");
ok(!aResponse.helperResult, "no helper result");
ok(!response.exception, "no eval exception");
ok(!response.helperResult, "no helper result");
nextTest();
}
function doEvalWithException()
{
function* doEvalWithException() {
info("test eval with exception");
evaluateJS("window.doTheImpossible()", onEvalWithException);
}
function onEvalWithException(aResponse)
{
checkObject(aResponse, {
let response = yield evaluateJS("window.doTheImpossible()");
checkObject(response, {
from: gState.actor,
input: "window.doTheImpossible()",
result: {
@ -230,21 +211,16 @@ function onEvalWithException(aResponse)
exceptionMessage: /doTheImpossible/,
});
ok(aResponse.exception, "js eval exception");
ok(!aResponse.helperResult, "no helper result");
ok(response.exception, "js eval exception");
ok(!response.helperResult, "no helper result");
nextTest();
}
function doEvalWithHelper()
{
function* doEvalWithHelper() {
info("test eval with helper");
evaluateJS("clear()", onEvalWithHelper);
}
function onEvalWithHelper(aResponse)
{
checkObject(aResponse, {
let response = yield evaluateJS("clear()");
checkObject(response, {
from: gState.actor,
input: "clear()",
result: {
@ -253,19 +229,14 @@ function onEvalWithHelper(aResponse)
helperResult: { type: "clearOutput" },
});
ok(!aResponse.exception, "no eval exception");
ok(!response.exception, "no eval exception");
nextTest();
}
function doEvalString()
{
evaluateJS("window.foobarObject.strfoo", onEvalString);
}
function onEvalString(aResponse)
{
checkObject(aResponse, {
function* doEvalString() {
let response = yield evaluateJS("window.foobarObject.strfoo");
checkObject(response, {
from: gState.actor,
input: "window.foobarObject.strfoo",
result: "foobarz",
@ -274,17 +245,12 @@ function onEvalString(aResponse)
nextTest();
}
function doEvalLongString()
{
evaluateJS("window.foobarObject.omgstr", onEvalLongString);
}
function onEvalLongString(aResponse)
{
function* doEvalLongString() {
let response = yield evaluateJS("window.foobarObject.omgstr");
let str = top.foobarObject.omgstr;
let initial = str.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
checkObject(aResponse, {
checkObject(response, {
from: gState.actor,
input: "window.foobarObject.omgstr",
result: {
@ -297,6 +263,58 @@ function onEvalLongString(aResponse)
nextTest();
}
function* doEvalWithBinding() {
let response = yield evaluateJS("document;");
let documentActor = response.result.actor;
info("running a command with _self as document using bindObjectActor");
let bindObjectSame = yield evaluateJS("_self === document", {
bindObjectActor: documentActor
});
checkObject(bindObjectSame, {
result: true
});
info("running a command with _self as document using selectedObjectActor");
let selectedObjectSame = yield evaluateJS("_self === document", {
selectedObjectActor: documentActor
});
checkObject(selectedObjectSame, {
result: true
});
nextTest();
}
function* doEvalWithBindingFrame() {
let frameWin = top.document.querySelector("iframe").contentWindow;
frameWin.fooFrame = { bar: 1 };
let response = yield evaluateJS(
"document.querySelector('iframe').contentWindow.fooFrame"
);
let iframeObjectActor = response.result.actor;
ok(iframeObjectActor, "There is an actor associated with the response");
let bindObjectGlobal = yield evaluateJS("this.temp0 = _self;", {
bindObjectActor: iframeObjectActor
});
ok(!top.temp0,
"Global doesn't match the top global with bindObjectActor");
ok(frameWin.temp0 && frameWin.temp0.bar === 1,
"Global matches the object's global with bindObjectActor");
let selectedObjectGlobal = yield evaluateJS("this.temp1 = _self;", {
selectedObjectActor: iframeObjectActor
});
ok(top.temp1 && top.temp1.bar === 1,
"Global matches the top global with bindObjectActor");
ok(!frameWin.temp1,
"Global doesn't match the object's global with bindObjectActor");
nextTest()
}
function testEnd()
{
// If this is the first run, reload the page and do it again.

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

@ -840,7 +840,7 @@ nsDocShell::nsDocShell()
nsDocShell::~nsDocShell()
{
MOZ_ASSERT(!IsObserved());
MOZ_ASSERT(!mObserved);
Destroy();
@ -2839,14 +2839,25 @@ NS_IMETHODIMP
nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
{
bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
if (currentValue != aValue) {
if (aValue) {
TimelineConsumers::AddConsumer(this);
UseEntryScriptProfiling();
} else {
TimelineConsumers::RemoveConsumer(this);
UnuseEntryScriptProfiling();
}
if (currentValue == aValue) {
return NS_OK;
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines) {
return NS_OK;
}
if (aValue) {
MOZ_ASSERT(!timelines->HasConsumer(this));
timelines->AddConsumer(this);
MOZ_ASSERT(timelines->HasConsumer(this));
UseEntryScriptProfiling();
} else {
MOZ_ASSERT(timelines->HasConsumer(this));
timelines->RemoveConsumer(this);
MOZ_ASSERT(!timelines->HasConsumer(this));
UnuseEntryScriptProfiling();
}
return NS_OK;
@ -2855,7 +2866,7 @@ nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
NS_IMETHODIMP
nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
{
*aValue = IsObserved();
*aValue = !!mObserved;
return NS_OK;
}
@ -2867,7 +2878,7 @@ nsDocShell::PopProfileTimelineMarkers(
nsTArray<dom::ProfileTimelineMarker> store;
SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
if (IsObserved()) {
if (mObserved) {
mObserved->PopMarkers(aCx, store);
}
@ -10650,7 +10661,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
/* Get the cache Key from SH */
nsCOMPtr<nsISupports> cacheKey;
if (mLSHE) {
@ -10658,7 +10669,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
} else if (mOSHE) { // for reload cases
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
}
if (uploadChannel) {
// figure out if we need to set the post data stream on the channel...
// right now, this is only done for http channels.....
@ -10709,7 +10720,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
}
}
}
if (httpChannel) {
if (aHeadersData) {
rv = AddHeadersToChannel(aHeadersData, httpChannel);
@ -13892,26 +13903,30 @@ nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
const char16_t* aFilename,
const uint32_t aLineNumber)
{
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
// If first start, mark interval start.
if (timelineOn && mJSRunToCompletionDepth == 0) {
UniquePtr<TimelineMarker> marker = MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(this, Move(marker));
if (mJSRunToCompletionDepth == 0) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(this)) {
timelines->AddMarkerForDocShell(this, Move(
MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START)));
}
}
mJSRunToCompletionDepth++;
}
void
nsDocShell::NotifyJSRunToCompletionStop()
{
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
mJSRunToCompletionDepth--;
// If last stop, mark interval end.
mJSRunToCompletionDepth--;
if (timelineOn && mJSRunToCompletionDepth == 0) {
TimelineConsumers::AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
if (mJSRunToCompletionDepth == 0) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(this)) {
timelines->AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
}
}
}

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

@ -275,7 +275,6 @@ public:
private:
// An observed docshell wrapper is created when recording markers is enabled.
mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
bool IsObserved() const { return !!mObserved; }
// It is necessary to allow adding a timeline marker wherever a docshell
// instance is available. This operation happens frequently and needs to
@ -290,8 +289,6 @@ private:
nsDocShell*, const char*, const TimeStamp&, MarkerTracingType);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
friend void mozilla::TimelineConsumers::AddOTMTMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&);
public:
// Tell the favicon service that aNewURI has the same favicon as aOldURI.

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

@ -35,6 +35,14 @@ AbstractTimelineMarker::Clone()
return nullptr;
}
bool
AbstractTimelineMarker::Equals(const AbstractTimelineMarker& aOther)
{
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(mName, aOther.mName) == 0;
}
AbstractTimelineMarker::~AbstractTimelineMarker()
{
MOZ_COUNT_DTOR(AbstractTimelineMarker);
@ -54,4 +62,10 @@ AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
}
void
AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime)
{
mTime = aTime;
}
} // namespace mozilla

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

@ -12,6 +12,7 @@
#include "mozilla/UniquePtr.h"
struct JSContext;
class JSObject;
namespace mozilla {
class TimeStamp;
@ -28,18 +29,18 @@ private:
void operator=(const AbstractTimelineMarker& aOther) = delete;
public:
AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType);
explicit AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType);
AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
explicit AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
virtual ~AbstractTimelineMarker();
virtual UniquePtr<AbstractTimelineMarker> Clone();
virtual bool Equals(const AbstractTimelineMarker& aOther);
virtual bool Equals(const AbstractTimelineMarker& aOther) = 0;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
virtual JSObject* GetStack() = 0;
@ -52,8 +53,10 @@ private:
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
protected:
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
void SetCustomTime(DOMHighResTimeStamp aTime);
};
} // namespace mozilla

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

@ -4,7 +4,7 @@
* 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/AutoGlobalTimelineMarker.h"
#include "AutoGlobalTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
@ -18,20 +18,24 @@ AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(NS_IsMainThread());
if (TimelineConsumers::IsEmpty()) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(mName, MarkerTracingType::START);
timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::START);
}
AutoGlobalTimelineMarker::~AutoGlobalTimelineMarker()
{
if (TimelineConsumers::IsEmpty()) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(mName, MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::END);
}
} // namespace mozilla

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

@ -8,9 +8,6 @@
#define mozilla_AutoGlobalTimelineMarker_h_
#include "mozilla/GuardObjects.h"
#include "mozilla/RefPtr.h"
class nsDocShell;
namespace mozilla {

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

@ -4,11 +4,10 @@
* 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/AutoTimelineMarker.h"
#include "AutoTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
#include "nsDocShell.h"
namespace mozilla {
@ -20,21 +19,33 @@ AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell || TimelineConsumers::IsEmpty()) {
if (!aDocShell) {
return;
}
mDocShell = static_cast<nsDocShell*>(aDocShell);
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::START);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || !timelines->HasConsumer(aDocShell)) {
return;
}
mDocShell = aDocShell;
timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::START);
}
AutoTimelineMarker::~AutoTimelineMarker()
{
if (!mDocShell || TimelineConsumers::IsEmpty()) {
MOZ_ASSERT(NS_IsMainThread());
if (!mDocShell) {
return;
}
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::END);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || !timelines->HasConsumer(mDocShell)) {
return;
}
timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::END);
}
} // namespace mozilla

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

@ -11,7 +11,6 @@
#include "mozilla/RefPtr.h"
class nsIDocShell;
class nsDocShell;
namespace mozilla {
@ -36,7 +35,7 @@ class MOZ_RAII AutoTimelineMarker
const char* mName;
// The docshell that is associated with this marker.
RefPtr<nsDocShell> mDocShell;
RefPtr<nsIDocShell> mDocShell;
public:
explicit AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName

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

@ -9,6 +9,7 @@
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "nsRegion.h"
namespace mozilla {
@ -20,9 +21,6 @@ public:
, mRegion(aRegion)
{}
~LayerTimelineMarker()
{}
void AddLayerRectangles(dom::Sequence<dom::ProfileTimelineLayerRect>& aRectangles)
{
nsIntRegionRectIterator it(mRegion);

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

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MarkersStorage.h"
#include "MainThreadUtils.h"
namespace mozilla {
MarkersStorage::MarkersStorage(const char* aMutexName)
: mLock(aMutexName)
{
MOZ_ASSERT(NS_IsMainThread());
}
MarkersStorage::~MarkersStorage()
{
MOZ_ASSERT(NS_IsMainThread());
}
Mutex&
MarkersStorage::GetLock()
{
return mLock;
}
} // namespace mozilla

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

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_MarkersStorage_h_
#define mozilla_MarkersStorage_h_
#include "TimelineMarkerEnums.h" // for MarkerReleaseRequest
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "nsTArray.h"
namespace mozilla {
class AbstractTimelineMarker;
namespace dom {
struct ProfileTimelineMarker;
}
class MarkersStorage : public LinkedListElement<MarkersStorage>
{
private:
MarkersStorage() = delete;
MarkersStorage(const MarkersStorage& aOther) = delete;
void operator=(const MarkersStorage& aOther) = delete;
public:
explicit MarkersStorage(const char* aMutexName);
virtual ~MarkersStorage();
virtual void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void ClearMarkers() = 0;
virtual void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) = 0;
protected:
Mutex& GetLock();
private:
Mutex mLock;
};
} // namespace mozilla
#endif /* mozilla_MarkersStorage_h_ */

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

@ -1,40 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_OTMTMarkerObserver_h_
#define mozilla_OTMTMarkerObserver_h_
#include "mozilla/Mutex.h"
namespace mozilla {
class AbstractTimelineMarker;
class OTMTMarkerReceiver
{
private:
OTMTMarkerReceiver() = delete;
OTMTMarkerReceiver(const OTMTMarkerReceiver& aOther) = delete;
void operator=(const OTMTMarkerReceiver& aOther) = delete;
public:
explicit OTMTMarkerReceiver(const char* aMutexName)
: mLock(aMutexName)
{
}
virtual ~OTMTMarkerReceiver() {}
virtual void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) = 0;
protected:
Mutex& GetLock() { return mLock; };
private:
Mutex mLock;
};
} // namespace mozilla
#endif /* mozilla_OTMTMarkerObserver_h_ */

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

@ -13,8 +13,8 @@
namespace mozilla {
ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
: OTMTMarkerReceiver("ObservedDocShellMutex")
ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
: MarkersStorage("ObservedDocShellMutex")
, mDocShell(aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
@ -23,24 +23,31 @@ ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
void
ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
{
// Only allow main thread markers to go into this list. No need to lock
// here since `mTimelineMarkers` will only be accessed or modified on the
// main thread only.
MOZ_ASSERT(NS_IsMainThread());
mTimelineMarkers.AppendElement(Move(aMarker));
}
void
ObservedDocShell::AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker)
ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
{
// Only allow off the main thread markers to go into this list. Since most
// of our markers come from the main thread, be a little more efficient and
// avoid dealing with multithreading scenarios until all the markers are
// actually cleared or popped in `ClearMarkers` or `PopMarkers`.
MOZ_ASSERT(!NS_IsMainThread());
MutexAutoLock lock(GetLock());
UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
mTimelineMarkers.AppendElement(Move(cloned));
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
mOffTheMainThreadTimelineMarkers.AppendElement(Move(aMarker));
}
void
ObservedDocShell::ClearMarkers()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
mTimelineMarkers.Clear();
mOffTheMainThreadTimelineMarkers.Clear();
}
void
@ -48,13 +55,19 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
// First, move all of our markers into a single array. We'll chose
// the `mTimelineMarkers` store because that's where we expect most of
// our markers to be.
mTimelineMarkers.AppendElements(Move(mOffTheMainThreadTimelineMarkers));
// If we see an unpaired START, we keep it around for the next call
// to ObservedDocShell::PopMarkers. We store the kept START objects here.
nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers[i];
UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers.ElementAt(i);
// If this is a TIMESTAMP marker, there's no corresponding END,
// as it's a single unit of time, not a duration.
@ -93,12 +106,13 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers[j];
UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers.ElementAt(j);
bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
// Look for "Layer" markers to stream out "Paint" markers.
if (startIsPaintType && endIsLayerType) {
LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(endPayload.get());
AbstractTimelineMarker* raw = endPayload.get();
LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(raw);
layerPayload->AddLayerRectangles(layerRectangles);
hasSeenLayerType = true;
}
@ -133,7 +147,7 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptStartMarkers.AppendElement(Move(mTimelineMarkers[i]));
keptStartMarkers.AppendElement(Move(mTimelineMarkers.ElementAt(i)));
mTimelineMarkers.RemoveElementAt(i);
--i;
}

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

@ -7,11 +7,12 @@
#ifndef mozilla_ObservedDocShell_h_
#define mozilla_ObservedDocShell_h_
#include "OTMTMarkerReceiver.h"
#include "nsTArray.h"
#include "MarkersStorage.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
class nsDocShell;
class nsIDocShell;
namespace mozilla {
class AbstractTimelineMarker;
@ -24,22 +25,25 @@ struct ProfileTimelineMarker;
//
// A wrapper around a docshell for which docshell-specific markers are
// allowed to exist. See TimelineConsumers for register/unregister logic.
class ObservedDocShell : public LinkedListElement<ObservedDocShell>,
public OTMTMarkerReceiver
class ObservedDocShell : public MarkersStorage
{
private:
RefPtr<nsDocShell> mDocShell;
RefPtr<nsIDocShell> mDocShell;
// Main thread only.
nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
// Off the main thread only.
nsTArray<UniquePtr<AbstractTimelineMarker>> mOffTheMainThreadTimelineMarkers;
public:
explicit ObservedDocShell(nsDocShell* aDocShell);
nsDocShell* operator*() const { return mDocShell.get(); }
explicit ObservedDocShell(nsIDocShell* aDocShell);
nsIDocShell* operator*() const { return mDocShell.get(); }
void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker);
void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) override;
void ClearMarkers();
void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore);
void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void ClearMarkers() override;
void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) override;
};
} // namespace mozilla

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

@ -4,79 +4,174 @@
* 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/TimelineConsumers.h"
#include "TimelineConsumers.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
#include "nsDocShell.h"
namespace mozilla {
unsigned long TimelineConsumers::sActiveConsumers = 0;
LinkedList<ObservedDocShell>* TimelineConsumers::sObservedDocShells = nullptr;
Mutex* TimelineConsumers::sLock = nullptr;
NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
LinkedList<ObservedDocShell>&
TimelineConsumers::GetOrCreateObservedDocShellsList()
StaticMutex TimelineConsumers::sMutex;
// Manually manage this singleton's lifetime and destroy it before shutdown.
// This avoids the leakchecker detecting false-positive memory leaks when
// using automatic memory management (i.e. statically instantiating this
// singleton inside the `Get` method), which would automatically destroy it on
// application shutdown, but too late for the leakchecker. Sigh...
StaticRefPtr<TimelineConsumers> TimelineConsumers::sInstance;
// This flag makes sure the singleton never gets instantiated while a shutdown
// is in progress. This can actually happen, and `ClearOnShutdown` doesn't work
// in these cases.
bool TimelineConsumers::sInShutdown = false;
already_AddRefed<TimelineConsumers>
TimelineConsumers::Get()
{
if (!sObservedDocShells) {
sObservedDocShells = new LinkedList<ObservedDocShell>();
// Using this class is not supported yet for other processes other than
// parent or content. To avoid accidental checks to methods like `IsEmpty`,
// which would probably always be true in those cases, assert here.
// Remember, there will be different singletons available to each process.
MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
// If we are shutting down, don't bother doing anything. Note: we can only
// know whether or not we're in shutdown if we're instantiated.
if (sInShutdown) {
return nullptr;
}
return *sObservedDocShells;
// Note: We don't simply check `sInstance` for null-ness here, since otherwise
// this can resurrect the TimelineConsumers pretty late during shutdown.
// We won't know if we're in shutdown or not though, because the singleton
// could have been destroyed or just never instantiated, so in the previous
// conditional `sInShutdown` would be false.
static bool firstTime = true;
if (firstTime) {
firstTime = false;
StaticMutexAutoLock lock(sMutex);
sInstance = new TimelineConsumers();
// Make sure the initialization actually suceeds, otherwise don't allow
// access by destroying the instance immediately.
if (sInstance->Init()) {
ClearOnShutdown(&sInstance);
} else {
NS_WARNING("TimelineConsumers could not be initialized.");
sInstance->RemoveObservers();
sInstance = nullptr;
}
}
RefPtr<TimelineConsumers> copy = sInstance.get();
return copy.forget();
}
Mutex&
TimelineConsumers::GetLock()
bool
TimelineConsumers::Init()
{
if (!sLock) {
sLock = new Mutex("TimelineConsumersMutex");
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return false;
}
return *sLock;
if (NS_WARN_IF(NS_FAILED(
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
return false;
}
return true;
}
bool
TimelineConsumers::RemoveObservers()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return false;
}
if (NS_WARN_IF(NS_FAILED(
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)))) {
return false;
}
return true;
}
nsresult
TimelineConsumers::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
sInShutdown = true;
RemoveObservers();
return NS_OK;
}
MOZ_ASSERT(false, "TimelineConsumers got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
TimelineConsumers::TimelineConsumers()
: mActiveConsumers(0)
{
}
void
TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(!observed);
sActiveConsumers++;
observed.reset(new ObservedDocShell(aDocShell));
GetOrCreateObservedDocShellsList().insertFront(observed.get());
mActiveConsumers++;
ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
MarkersStorage* storage = static_cast<MarkersStorage*>(obsDocShell);
observed.reset(obsDocShell);
mMarkersStores.insertFront(storage);
}
void
TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(observed);
sActiveConsumers--;
mActiveConsumers--;
// Clear all markers from the `mTimelineMarkers` store.
observed.get()->ClearMarkers();
// Remove self from the `mMarkersStores` store.
observed.get()->remove();
// Prepare for becoming a consumer later.
observed.reset(nullptr);
}
bool
TimelineConsumers::HasConsumer(nsIDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell) {
return false;
}
bool isTimelineRecording = false;
aDocShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
return isTimelineRecording;
}
bool
TimelineConsumers::IsEmpty()
{
MOZ_ASSERT(NS_IsMainThread());
return sActiveConsumers == 0;
}
bool
TimelineConsumers::GetKnownDocShells(Vector<RefPtr<nsDocShell>>& aStore)
{
MOZ_ASSERT(NS_IsMainThread());
const LinkedList<ObservedDocShell>& docShells = GetOrCreateObservedDocShellsList();
for (const ObservedDocShell* rds = docShells.getFirst();
rds != nullptr;
rds = rds->getNext()) {
if (!aStore.append(**rds)) {
return false;
}
}
return true;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers`.
return mActiveConsumers == 0;
}
void
@ -85,7 +180,7 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType)));
}
}
@ -97,7 +192,7 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType)));
}
}
@ -107,22 +202,11 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(aMarker));
}
}
void
TimelineConsumers::AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
if (aDocShell->IsObserved()) {
aDocShell->mObserved->AddOTMTMarkerClone(aMarker);
}
}
void
TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
@ -150,76 +234,23 @@ TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
}
void
TimelineConsumers::AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
AddOTMTMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aMarker);
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddMarkerForDocShell(range.front(), aName, aTracingType);
}
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddMarkerForDocShell(range.front(), aName, aTime, aTracingType);
}
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
AddMarkerForDocShell(range.front(), Move(cloned));
}
}
void
TimelineConsumers::AddOTMTMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddOTMTMarkerForDocShell(range.front(), aMarker);
}
}
void
TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aName, aTracingType);
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTracingType);
if (isMainThread) {
storage->AddMarker(Move(marker));
} else {
storage->AddOTMTMarker(Move(marker));
}
}
}
@ -228,31 +259,37 @@ TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aName, aTime, aTracingType);
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTime, aTracingType);
if (isMainThread) {
storage->AddMarker(Move(marker));
} else {
storage->AddOTMTMarker(Move(marker));
}
}
}
void
TimelineConsumers::AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aMarker);
}
}
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
void
TimelineConsumers::AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddOTMTMarkerForDocShellsList(docShells, aMarker);
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> clone = aMarker->Clone();
if (isMainThread) {
storage->AddMarker(Move(clone));
} else {
storage->AddOTMTMarker(Move(clone));
}
}
}

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

@ -1,4 +1,3 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -7,104 +6,113 @@
#ifndef mozilla_TimelineConsumers_h_
#define mozilla_TimelineConsumers_h_
#include "nsIObserver.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Vector.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Mutex.h"
#include "TimelineMarkerEnums.h"
#include "mozilla/StaticMutex.h"
#include "TimelineMarkerEnums.h" // for MarkerTracingType
class nsDocShell;
class nsIDocShell;
namespace mozilla {
class ObservedDocShell;
class TimeStamp;
class MarkersStorage;
class AbstractTimelineMarker;
class TimelineConsumers
class TimelineConsumers : public nsIObserver
{
private:
// Counter for how many timelines are currently interested in markers.
static unsigned long sActiveConsumers;
static LinkedList<ObservedDocShell>* sObservedDocShells;
static LinkedList<ObservedDocShell>& GetOrCreateObservedDocShellsList();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
// Lock used when adding off-the-main-thread markers.
static Mutex* sLock;
private:
TimelineConsumers();
TimelineConsumers(const TimelineConsumers& aOther) = delete;
void operator=(const TimelineConsumers& aOther) = delete;
virtual ~TimelineConsumers() = default;
bool Init();
bool RemoveObservers();
public:
static Mutex& GetLock();
static already_AddRefed<TimelineConsumers> Get();
static void AddConsumer(nsDocShell* aDocShell);
static void RemoveConsumer(nsDocShell* aDocShell);
static bool IsEmpty();
static bool GetKnownDocShells(Vector<RefPtr<nsDocShell>>& aStore);
// Methods for registering interested consumers (i.e. "devtools toolboxes").
// Each consumer should be directly focused on a particular docshell, but
// timeline markers don't necessarily have to be tied to that docshell.
// See the public `AddMarker*` methods below.
// Main thread only.
void AddConsumer(nsDocShell* aDocShell);
void RemoveConsumer(nsDocShell* aDocShell);
bool HasConsumer(nsIDocShell* aDocShell);
// Checks if there's any existing interested consumer.
// May be called from any thread.
bool IsEmpty();
// Methods for adding markers relevant for particular docshells, or generic
// (meaning that they either can't be tied to a particular docshell, or one
// wasn't accessible in the part of the codebase where they're instantiated).
// These will only add markers if at least one docshell is currently being
// observed by a timeline. Markers tied to a particular docshell won't be
// created unless that docshell is specifically being currently observed.
// See nsIDocShell::recordProfileTimelineMarkers
// These methods create a custom marker from a name and some metadata,
// These methods create a basic TimelineMarker from a name and some metadata,
// relevant for a specific docshell.
static void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
// Main thread only.
void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
// These methods register and receive ownership of an already created marker,
// relevant for a specific docshell.
static void AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
// Main thread only.
void AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
// These methods create or clone markers relevant for a list of docshells.
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
// These methods create a basic marker from a name and some metadata,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
void AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
void AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker);
// These methods create or clone markers, none of which have to be tied to
// a particular docshell.
static void AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
// This method clones and registers an already instantiated marker,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
// Thread-safe versions of the above methods. Need to lock first using
// the mutex returned by `TimelineConsumers::GetLock()`.
static void AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
private:
static StaticRefPtr<TimelineConsumers> sInstance;
static bool sInShutdown;
// Counter for how many timelines are currently interested in markers,
// and a list of the MarkersStorage interfaces representing them.
unsigned long mActiveConsumers;
LinkedList<MarkersStorage> mMarkersStores;
// Protects this class's data structures.
static StaticMutex sMutex;
};
} // namespace mozilla

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

@ -25,14 +25,6 @@ TimelineMarker::TimelineMarker(const char* aName,
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
bool
TimelineMarker::Equals(const AbstractTimelineMarker& aOther)
{
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(GetName(), aOther.GetName()) == 0;
}
void
TimelineMarker::AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker)
{

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

@ -8,6 +8,7 @@
#define mozilla_TimelineMarker_h_
#include "AbstractTimelineMarker.h"
#include "js/RootingAPI.h"
namespace mozilla {
@ -17,16 +18,15 @@ namespace mozilla {
class TimelineMarker : public AbstractTimelineMarker
{
public:
TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
explicit TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
TimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
explicit TimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
virtual bool Equals(const AbstractTimelineMarker& aOther) override;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
virtual JSObject* GetStack() override;

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

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_WorkerTimelineMarker_h_
#define mozilla_WorkerTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class WorkerTimelineMarker : public TimelineMarker
{
public:
explicit WorkerTimelineMarker(ProfileTimelineWorkerOperationType aOperationType,
MarkerTracingType aTracingType)
: TimelineMarker("Worker", aTracingType, MarkerStackRequest::NO_STACK)
, mOperationType(aOperationType)
{}
virtual UniquePtr<AbstractTimelineMarker> Clone() override
{
WorkerTimelineMarker* clone = new WorkerTimelineMarker(mOperationType, GetTracingType());
clone->SetCustomTime(GetTime());
return UniquePtr<AbstractTimelineMarker>(clone);
}
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
{
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mWorkerOperation.Construct(mOperationType);
}
}
private:
ProfileTimelineWorkerOperationType mOperationType;
};
} // namespace mozilla
#endif /* mozilla_WorkerTimelineMarker_h_ */

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

@ -12,19 +12,21 @@ EXPORTS.mozilla += [
'EventTimelineMarker.h',
'JavascriptTimelineMarker.h',
'LayerTimelineMarker.h',
'MarkersStorage.h',
'ObservedDocShell.h',
'OTMTMarkerReceiver.h',
'RestyleTimelineMarker.h',
'TimelineConsumers.h',
'TimelineMarker.h',
'TimelineMarkerEnums.h',
'TimestampTimelineMarker.h',
'WorkerTimelineMarker.h',
]
UNIFIED_SOURCES += [
'AbstractTimelineMarker.cpp',
'AutoGlobalTimelineMarker.cpp',
'AutoTimelineMarker.cpp',
'MarkersStorage.cpp',
'ObservedDocShell.cpp',
'TimelineConsumers.cpp',
'TimelineMarker.cpp',

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

@ -0,0 +1,97 @@
#Timeline
The files in this directory are concerned with providing the backend platform features required for the developer tools interested in tracking down operations done in Gecko. The mechanism we use to define these operations are `markers`.
Examples of traced operations include:
* Style Recalculation
* Layout
* Painting
* JavaScript run-to-completion
* HTML parsing
* etc.
The traced operations are displayed in the DevTools Performance tool's timeline.
This is an overview of how everything works and can be extended.
##MarkersStorage
A `MarkersStorage` is an abstract class defining a place where timeline markers may be held. It defines an interface with pure virtual functions to highlight how this storage can be interacted with:
- `AddMarker`: adding a marker, from the main thread only
- `AddOTMTMarker`: adding a marker off the main thread only
- `ClearMarkers`: clearing all accumulated markers (both from the main thread and off it)
- `PopMarkers`: popping all accumulated markers (both from the main thread and off it).
Note on why we handle on/off the main thread markers separately: since most of our markers will come from the main thread, we can be a little more efficient and avoid dealing with multithreading scenarios until all the markers are actually cleared or popped in `ClearMarkers` or `PopMarkers`. Main thread markers may only be added via `AddMarker`, while off the main thread markers may only be added via `AddOTMTMarker`. Clearing and popping markers will yield until all operations involving off the main thread markers finish. When popping, the markers accumulated off the main thread will be moved over. We expect popping to be fairly infrequent (every few hundred milliseconds, currently we schedule this to happen every 200ms).
##ObservedDocShell
The only implementation of a MarkersStorage we have right now is an `ObservedDocShell`.
Instances of `ObservedDocShell` accumulate markers that are *mostly* about a particular docshell. At a high level, for example, an `ObservedDocshell` would be created when a timeline tool is opened on a page. It is reasonable to assume that most operations which are interesting for that particular page happen on the main thread. However certain operations may happen outside of it, yet are interesting for its developers, for which markers can be created as well (e.g. web audio stuff, service workers etc.). It is also reasonable to assume that a docshell may sometimes not be easily accessible from certain parts of the platform code, but for which markers still need to be created.
Therefore, the following scenarios arise:
- a). creating a marker on the main thread about a particular dochsell
- b). creating a marker on the main thread without pinpointing to an affected docshell (unlikely, but allowed; in this case such a marker would have to be stored in all currently existing `ObservedDocShell` instances)
- c). creating a marker off the main thread about a particular dochsell (impossible; docshells can't be referenced outside the main thread, in which case some other type of identification mechanism needs to be put in place).
- d). creating a marker off the main thread without pinpointing to a particular docshell (same path as c. here, such a marker would have to be stored in all currently existing `ObservedDocShell` instances).
An observed docshell (in other words, "a docshell for which a timeline tool was opened") can thus receive both main thread and off the main thread markers.
Cross-process markers are unnecessary at the moment, but tracked in bug 1200120.
##TimelineConsumers
A `TimelineConsumer` is a singleton that facilitates access to `ObservedDocShell` instances. This is where a docshell can register/unregister itself as being observed via the `AddConsumer` and `RemoveConsumer` methods.
All markers may only be stored via this singleton. Certain helper methods are available:
* Main thread only
`AddMarkerForDocShell(nsDocShell*, const char*, MarkerTracingType)`
`AddMarkerForDocShell(nsDocShell*, const char*, const TimeStamp&, MarkerTracingType)`
`AddMarkerForDocShell(nsDocShell*, UniquePtr<AbstractTimelineMarker>&&)`
* Any thread
`AddMarkerForAllObservedDocShells(const char*, MarkerTracingType)`
`AddMarkerForAllObservedDocShells(const char*, const TimeStamp&, MarkerTracingType)`
`AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>&)`
The "main thread only" methods deal with point a). described above. The "any thread" methods deal with points b). and d).
##AbstractTimelineMarker
All markers inherit from this abstract class, providing a simple thread-safe extendable blueprint.
Markers are readonly after instantiation, and will always be identified by a name, a timestamp and their tracing type (`START`, `END`, `TIMESTAMP`). It *should not* make sense to modify their data after their creation.
There are only two accessible constructors:
`AbstractTimelineMarker(const char*, MarkerTracingType)`
`AbstractTimelineMarker(const char*, const TimeStamp&, MarkerTracingType)`
which create a marker with a name and a tracing type. If unspecified, the corresponding timestamp will be the current instantiation time. Instantiating a marker *much later* after a particular operation is possible, but be careful providing the correct timestamp.
The `AddDetails` virtual method should be implemented by subclasses when creating WebIDL versions of these markers, which will be sent over to a JavaScript frontend.
##TimelineMarker
A `TimelineMarker` is the main `AbstractTimelineMarker` implementation. They allow attaching a JavaScript stack on `START` and `TIMESTAMP` markers.
These markers will be created when using the `TimelineConsumers` helper methods which take in a string, a tracing type and (optionally) a timestamp. For more complex markers, subclasses are encouraged. See `EventTimelineMarker` or `ConsoleTimelineMarker` for some examples.
##RAII
### mozilla::AutoTimelineMarker
The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the `mozilla::AutoTimelineMarker` RAII class. It automatically adds the start marker on construction, and adds the end marker on destruction. Don't worry too much about potential performance impact! It only actually adds the markers when the given docshell is being observed by a timeline consumer, so essentially nothing will happen if a tool to inspect those markers isn't specifically open.
This class may only be used on the main thread, and pointer to a docshell is necessary. If the docshell is a nullptr, nothing happens and this operation fails silently.
Example: `AutoTimelineMarker marker(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");`
### mozilla::AutoGlobalTimelineMarker`
Similar to the previous RAII class, but doesn't expect a specific docshell, and the marker will be visible in all timeline consumers. This is useful for generic operations that don't involve a particular dochsell, or where a docshell isn't accessible. May also only be used on the main thread.
Example: `AutoGlobalTimelineMarker marker("Some global operation");`

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

@ -105,6 +105,7 @@ var TESTS = [{
content.console.timeStamp(undefined);
},
check: function (markers) {
markers = markers.filter(e => e.name != "Worker");
is(markers.length, 4, "Got 4 markers");
is(markers[0].name, "TimeStamp", "Got Timestamp marker");
is(markers[0].causeName, "paper", "Got Timestamp label value");

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

@ -1065,42 +1065,41 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
callData->mMonotonicTimer = performance->Now();
// 'time' and 'timeEnd' are displayed in the devtools timeline if active.
bool isTimelineRecording = false;
nsDocShell* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
// 'timeStamp' recordings do not need an argument; use empty string
// if no arguments passed in
// The 'timeStamp' recordings do not need an argument; use empty string
// if no arguments passed in.
if (isTimelineRecording && aMethodName == MethodTimeStamp) {
JS::Rooted<JS::Value> value(aCx, aData.Length() == 0 ?
JS_GetEmptyStringValue(aCx) : aData[0]);
JS::Rooted<JS::Value> value(aCx, aData.Length() == 0
? JS_GetEmptyStringValue(aCx)
: aData[0]);
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
nsAutoJSString key;
if (jsString) {
key.init(aCx, jsString);
}
UniquePtr<TimelineMarker> marker = MakeUnique<TimestampTimelineMarker>(key);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<TimestampTimelineMarker>(key)));
}
// For `console.time(foo)` and `console.timeEnd(foo)`
// For `console.time(foo)` and `console.timeEnd(foo)`.
else if (isTimelineRecording && aData.Length() == 1) {
JS::Rooted<JS::Value> value(aCx, aData[0]);
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
if (jsString) {
nsAutoJSString key;
if (key.init(aCx, jsString)) {
UniquePtr<TimelineMarker> marker = MakeUnique<ConsoleTimelineMarker>(
key, aMethodName == MethodTime ? MarkerTracingType::START
: MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<ConsoleTimelineMarker>(
key, aMethodName == MethodTime ? MarkerTracingType::START
: MarkerTracingType::END)));
}
}
}
} else {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);

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

@ -1114,35 +1114,35 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
// Maybe add a marker to the docshell's timeline, but only
// bother with all the logic if some docshell is recording.
nsCOMPtr<nsIDocShell> docShell;
bool isTimelineRecording = false;
nsDocShell* docShell;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool needsEndEventMarker = false;
if (mIsMainThreadELM &&
!TimelineConsumers::IsEmpty() &&
listener->mListenerType != Listener::eNativeListener) {
docShell = GetDocShellForTarget();
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
if (isTimelineRecording) {
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
nsAutoString typeStr;
(*aDOMEvent)->GetType(typeStr);
uint16_t phase;
(*aDOMEvent)->GetEventPhase(&phase);
UniquePtr<TimelineMarker> marker = MakeUnique<EventTimelineMarker>(
typeStr, phase, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(ds, Move(marker));
nsCOMPtr<nsIDocShell> docShellComPtr = GetDocShellForTarget();
if (docShellComPtr) {
docShell = static_cast<nsDocShell*>(docShellComPtr.get());
if (timelines && timelines->HasConsumer(docShell)) {
needsEndEventMarker = true;
nsAutoString typeStr;
(*aDOMEvent)->GetType(typeStr);
uint16_t phase;
(*aDOMEvent)->GetEventPhase(&phase);
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<EventTimelineMarker>(
typeStr, phase, MarkerTracingType::START)));
}
}
}
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent,
aCurrentTarget))) {
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
aEvent->mFlags.mExceptionHasBeenRisen = true;
}
if (isTimelineRecording) {
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
TimelineConsumers::AddMarkerForDocShell(ds, "DOMEvent", MarkerTracingType::END);
if (needsEndEventMarker) {
timelines->AddMarkerForDocShell(
docShell, "DOMEvent", MarkerTracingType::END);
}
}
}

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

@ -47,6 +47,7 @@
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeySystemAccess.h"
#endif
#include "mozilla/dom/Notification.h"
#include "mozilla/dom/NuwaParent.h"
#include "mozilla/dom/PContentBridgeParent.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
@ -3181,7 +3182,9 @@ ContentParent::Observe(nsISupports* aSubject,
// listening for alert notifications
else if (!strcmp(aTopic, "alertfinished") ||
!strcmp(aTopic, "alertclickcallback") ||
!strcmp(aTopic, "alertshow") ) {
!strcmp(aTopic, "alertshow") ||
!strcmp(aTopic, "alertdisablecallback") ||
!strcmp(aTopic, "alertsettingscallback")) {
if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
nsDependentString(aData)))
return NS_ERROR_NOT_AVAILABLE;
@ -4270,6 +4273,20 @@ ContentParent::RecvLoadURIExternal(const URIParams& uri)
return true;
}
bool
ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal)
{
#ifdef MOZ_CHILD_PERMISSIONS
uint32_t permission = mozilla::CheckPermission(this, aPrincipal,
"desktop-notification");
if (permission != nsIPermissionManager::ALLOW_ACTION) {
return false;
}
#endif /* MOZ_CHILD_PERMISSIONS */
return true;
}
bool
ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
const nsString& aText, const bool& aTextClickable,
@ -4279,13 +4296,9 @@ ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsStri
const IPC::Principal& aPrincipal,
const bool& aInPrivateBrowsing)
{
#ifdef MOZ_CHILD_PERMISSIONS
uint32_t permission = mozilla::CheckPermission(this, aPrincipal,
"desktop-notification");
if (permission != nsIPermissionManager::ALLOW_ACTION) {
if (!HasNotificationPermission(aPrincipal)) {
return true;
}
#endif /* MOZ_CHILD_PERMISSIONS */
nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
if (sysAlerts) {
@ -4300,13 +4313,9 @@ bool
ContentParent::RecvCloseAlert(const nsString& aName,
const IPC::Principal& aPrincipal)
{
#ifdef MOZ_CHILD_PERMISSIONS
uint32_t permission = mozilla::CheckPermission(this, aPrincipal,
"desktop-notification");
if (permission != nsIPermissionManager::ALLOW_ACTION) {
if (!HasNotificationPermission(aPrincipal)) {
return true;
}
#endif
nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
if (sysAlerts) {
@ -4316,6 +4325,24 @@ ContentParent::RecvCloseAlert(const nsString& aName,
return true;
}
bool
ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal)
{
if (HasNotificationPermission(aPrincipal)) {
unused << Notification::RemovePermission(aPrincipal);
}
return true;
}
bool
ContentParent::RecvOpenNotificationSettings(const IPC::Principal& aPrincipal)
{
if (HasNotificationPermission(aPrincipal)) {
unused << Notification::OpenSettings(aPrincipal);
}
return true;
}
bool
ContentParent::RecvSyncMessage(const nsString& aMsg,
const ClonedMessageData& aData,

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

@ -756,6 +756,8 @@ private:
virtual bool RecvSetURITitle(const URIParams& uri,
const nsString& title) override;
bool HasNotificationPermission(const IPC::Principal& aPrincipal);
virtual bool RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
const nsString& aText, const bool& aTextClickable,
const nsString& aCookie, const nsString& aName,
@ -767,6 +769,10 @@ private:
virtual bool RecvCloseAlert(const nsString& aName,
const IPC::Principal& aPrincipal) override;
virtual bool RecvDisableNotifications(const IPC::Principal& aPrincipal) override;
virtual bool RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) override;
virtual bool RecvLoadURIExternal(const URIParams& uri) override;
virtual bool RecvSyncMessage(const nsString& aMsg,

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

@ -842,6 +842,10 @@ parent:
CloseAlert(nsString name, Principal principal);
DisableNotifications(Principal principal);
OpenNotificationSettings(Principal principal);
PPSMContentDownloader(uint32_t aCertType);
PExternalHelperApp(OptionalURIParams uri,

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

@ -10,6 +10,7 @@
#include "nsIMutable.h"
#include "nsIXPConnect.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/File.h"
@ -81,7 +82,7 @@ StructuredCloneData::Write(JSContext* aCx,
}
void
StructuredCloneData::WriteIPCParams(Message* aMsg) const
StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
{
WriteParam(aMsg, DataLength());

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

@ -2975,15 +2975,20 @@ void
TabChild::DidRequestComposite(const TimeStamp& aCompositeReqStart,
const TimeStamp& aCompositeReqEnd)
{
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (!docShell) {
nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation());
if (!docShellComPtr) {
return;
}
TimelineConsumers::AddMarkerForDocShell(docShell.get(),
"CompositeForwardTransaction", aCompositeReqStart, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell.get(),
"CompositeForwardTransaction", aCompositeReqEnd, MarkerTracingType::END);
nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(docShell)) {
timelines->AddMarkerForDocShell(docShell,
"CompositeForwardTransaction", aCompositeReqStart, MarkerTracingType::START);
timelines->AddMarkerForDocShell(docShell,
"CompositeForwardTransaction", aCompositeReqEnd, MarkerTracingType::END);
}
}
void

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

@ -16,8 +16,10 @@
#include "nsAccessibilityService.h"
#endif
#include "mozilla/BrowserElementParent.h"
#include "mozilla/dom/ContentBridgeParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/indexedDB/ActorsParent.h"
#include "mozilla/plugins/PluginWidgetParent.h"
#include "mozilla/EventStateManager.h"

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

@ -20,6 +20,7 @@
#include "mozilla/unused.h"
#include "nsFrameMessageManager.h"
#include "nsIWebBrowserChrome.h"
#include "nsPrintfCString.h"
#include "xpcpublic.h"

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

@ -14,6 +14,7 @@
#include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/NotificationEvent.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/Promise.h"
@ -40,6 +41,7 @@
#include "nsServiceManagerUtils.h"
#include "nsStructuredCloneContainer.h"
#include "nsToolkitCompsCID.h"
#include "nsXULAppAPI.h"
#include "ServiceWorkerManager.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
@ -1154,21 +1156,23 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
AssertIsOnMainThread();
if (!strcmp("alertdisablecallback", aTopic)) {
nsCOMPtr<nsIPermissionManager> permissionManager =
mozilla::services::GetPermissionManager();
if (!permissionManager) {
return NS_ERROR_FAILURE;
if (XRE_IsParentProcess()) {
return Notification::RemovePermission(mPrincipal);
}
permissionManager->RemoveFromPrincipal(mPrincipal, "desktop-notification");
// Permissions can't be removed from the content process. Send a message
// to the parent; `ContentParent::RecvDisableNotifications` will call
// `RemovePermission`.
ContentChild::GetSingleton()->SendDisableNotifications(
IPC::Principal(mPrincipal));
return NS_OK;
} else if (!strcmp("alertsettingscallback", aTopic)) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_FAILURE;
if (XRE_IsParentProcess()) {
return Notification::OpenSettings(mPrincipal);
}
// Notify other observers so they can show settings UI.
obs->NotifyObservers(mPrincipal, "notifications-open-settings", nullptr);
// `ContentParent::RecvOpenNotificationSettings` notifies observers in the
// parent process.
ContentChild::GetSingleton()->SendOpenNotificationSettings(
IPC::Principal(mPrincipal));
return NS_OK;
}
@ -2411,6 +2415,32 @@ Notification::CreateAndShow(nsIGlobalObject* aGlobal,
return notification.forget();
}
/* static */ nsresult
Notification::RemovePermission(nsIPrincipal* aPrincipal)
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIPermissionManager> permissionManager =
mozilla::services::GetPermissionManager();
if (!permissionManager) {
return NS_ERROR_FAILURE;
}
permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification");
return NS_OK;
}
/* static */ nsresult
Notification::OpenSettings(nsIPrincipal* aPrincipal)
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_FAILURE;
}
// Notify other observers so they can show settings UI.
obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

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

@ -275,6 +275,9 @@ public:
bool DispatchClickEvent();
bool DispatchNotificationClickEvent();
static nsresult RemovePermission(nsIPrincipal* aPrincipal);
static nsresult OpenSettings(nsIPrincipal* aPrincipal);
protected:
Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
const nsAString& aTitle, const nsAString& aBody,

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

@ -25,20 +25,35 @@ dictionary ProfileTimelineLayerRect {
long height = 0;
};
enum ProfileTimelineWorkerOperationType {
"serializeDataOffMainThread",
"serializeDataOnMainThread",
"deserializeDataOffMainThread",
"deserializeDataOnMainThread",
};
dictionary ProfileTimelineMarker {
DOMString name = "";
DOMHighResTimeStamp start = 0;
DOMHighResTimeStamp end = 0;
object? stack = null;
/* For ConsoleTime, Timestamp and Javascript markers. */
DOMString causeName;
/* For ConsoleTime markers. */
object? endStack = null;
/* For DOMEvent markers. */
DOMString type;
unsigned short eventPhase;
/* For Paint markers. */
sequence<ProfileTimelineLayerRect> rectangles;
/* For Style markers. */
DOMString restyleHint;
/* For Worker markers. */
ProfileTimelineWorkerOperationType workerOperation;
};

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

@ -65,6 +65,8 @@
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
#include "nsCycleCollector.h"
@ -638,7 +640,30 @@ public:
JS::Rooted<JS::Value> messageData(aCx);
ErrorResult rv;
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::START);
}
Read(parent, aCx, &messageData, rv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(aCx, rv.StealNSResult());
return false;
@ -2796,7 +2821,29 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
new MessageEventRunnable(ParentAsWorkerPrivate(),
WorkerRunnable::WorkerThreadModifyBusyCount);
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::START);
}
runnable->Write(aCx, aMessage, transferable, aRv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -5385,7 +5432,29 @@ WorkerPrivate::PostMessageToParentInternal(
new MessageEventRunnable(this,
WorkerRunnable::ParentThreadUnchangedBusyCount);
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::START);
}
runnable->Write(aCx, aMessage, transferable, aRv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(aRv.Failed())) {
return;
}

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

@ -5919,11 +5919,11 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer,
if (presContext && presContext->GetDocShell() && isActiveLayerManager) {
nsDocShell* docShell = static_cast<nsDocShell*>(presContext->GetDocShell());
bool isRecording;
docShell->GetRecordProfileTimelineMarkers(&isRecording);
if (isRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<LayerTimelineMarker>(aRegionToDraw);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(docShell)) {
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<LayerTimelineMarker>(aRegionToDraw)));
}
}

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

@ -218,12 +218,9 @@ RestyleTracker::DoProcessRestyles()
PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
js::ProfileEntry::Category::CSS, "(%s)", docURL.get());
bool isTimelineRecording = false;
nsDocShell* docShell =
static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
// Create a AnimationsWithDestroyedFrame during restyling process to
// stop animations on elements that have no frame at the end of the
@ -341,9 +338,9 @@ RestyleTracker::DoProcessRestyles()
}
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::START)));
}
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
@ -357,9 +354,9 @@ RestyleTracker::DoProcessRestyles()
AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::END)));
}
}
@ -402,9 +399,9 @@ RestyleTracker::DoProcessRestyles()
}
#endif
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::START)));
}
ProcessOneRestyle(currentRestyle->mElement,
@ -413,9 +410,9 @@ RestyleTracker::DoProcessRestyles()
currentRestyle->mRestyleHintData);
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::END)));
}
}
}

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

@ -8970,8 +8970,11 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
js::ProfileEntry::Category::GRAPHICS, "(%s)", docURL.get());
nsDocShell* docShell = static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
if (docShell) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::START);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
if (isTimelineRecording) {
timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::START);
}
if (mReflowContinueTimer) {
@ -9148,9 +9151,10 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
tp->Accumulate();
}
if (docShell) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
if (isTimelineRecording) {
timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
}
return !interrupted;
}

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

@ -1266,13 +1266,20 @@ HasPendingAnimations(nsIPresShell* aShell)
static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
nsTArray<nsDocShell*>& aShells)
{
if (!aRootDocShell || TimelineConsumers::IsEmpty()) {
if (!aRootDocShell) {
return;
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
nsCOMPtr<nsISimpleEnumerator> enumerator;
nsresult rv = aRootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
nsIDocShell::ENUMERATE_BACKWARDS, getter_AddRefs(enumerator));
nsresult rv = aRootDocShell->GetDocShellEnumerator(
nsIDocShellTreeItem::typeAll,
nsIDocShell::ENUMERATE_BACKWARDS,
getter_AddRefs(enumerator));
if (NS_FAILED(rv)) {
return;
@ -1682,13 +1689,18 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
mPresShellsToInvalidateIfHidden.Clear();
if (mViewManagerFlushIsPending) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
nsTArray<nsDocShell*> profilingDocShells;
GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
for (nsDocShell* docShell : profilingDocShells) {
// For the sake of the profile timeline's simplicity, this is flagged as
// paint even if it includes creating display lists
TimelineConsumers::AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
MOZ_ASSERT(timelines);
MOZ_ASSERT(timelines->HasConsumer(docShell));
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
}
#ifdef MOZ_DUMP_PAINTING
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
printf_stderr("Starting ProcessPendingUpdates\n");
@ -1698,13 +1710,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
mViewManagerFlushIsPending = false;
RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
vm->ProcessPendingUpdates();
#ifdef MOZ_DUMP_PAINTING
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
printf_stderr("Ending ProcessPendingUpdates\n");
}
#endif
for (nsDocShell* docShell : profilingDocShells) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
MOZ_ASSERT(timelines);
MOZ_ASSERT(timelines->HasConsumer(docShell));
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
}
if (nsContentUtils::XPConnect()) {

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

@ -266,175 +266,6 @@ public class BrowserApp extends GeckoApp
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
private DragHelper mDragHelper;
private class DragHelper implements OuterLayout.DragCallback {
private int[] mToolbarLocation = new int[2]; // to avoid creation every time we need to check for toolbar location.
// When dragging horizontally, the area of mainlayout between left drag bound and right drag bound can
// be dragged. A touch on the right of that area will automatically close the view.
private int mStatusBarHeight;
public DragHelper() {
// If a layout round happens from the root, the offset placed by viewdraghelper gets forgotten and
// main layout gets replaced to offset 0.
((MainLayout) mMainLayout).setLayoutInterceptor(new LayoutInterceptor() {
@Override
public void onLayout() {
if (mRootLayout.isMoving()) {
mRootLayout.restoreTargetViewPosition();
}
}
});
}
@Override
public void onDragProgress(float progress) {
mBrowserToolbar.setToolBarButtonsAlpha(1.0f - progress);
mTabsPanel.translateInRange(progress);
}
@Override
public View getViewToDrag() {
return mMainLayout;
}
/**
* Since pressing the tabs button slides the main layout, whereas draghelper changes its offset, here we
* restore the position of mainlayout as if it was opened by pressing the button. This allows the closing
* mechanism to work.
*/
@Override
public void startDrag(boolean wasOpen) {
if (wasOpen) {
mTabsPanel.setHWLayerEnabled(true);
mMainLayout.offsetTopAndBottom(getDragRange());
mMainLayout.scrollTo(0, 0);
} else {
prepareTabsToShow();
mBrowserToolbar.hideVirtualKeyboard();
}
mBrowserToolbar.setContextMenuEnabled(false);
}
@Override
public void stopDrag(boolean stoppingToOpen) {
if (stoppingToOpen) {
mTabsPanel.setHWLayerEnabled(false);
mMainLayout.offsetTopAndBottom(-getDragRange());
mMainLayout.scrollTo(0, -getDragRange());
} else {
mTabsPanel.hideImmediately();
mTabsPanel.setHWLayerEnabled(false);
}
// Re-enabling context menu only while stopping to close.
if (stoppingToOpen) {
mBrowserToolbar.setContextMenuEnabled(false);
} else {
mBrowserToolbar.setContextMenuEnabled(true);
}
}
@Override
public int getDragRange() {
return mTabsPanel.getVerticalPanelHeight();
}
@Override
public int getOrderedChildIndex(int index) {
// See ViewDragHelper's findTopChildUnder method. ViewDragHelper looks for the topmost view in z order
// to understand what needs to be dragged. Here we are tampering Toast's index in case it's hidden,
// otherwise draghelper would try to drag it.
int mainLayoutIndex = mRootLayout.indexOfChild(mMainLayout);
if (index > mainLayoutIndex && (mToast == null || !mToast.isVisible())) {
return mainLayoutIndex;
} else {
return index;
}
}
@Override
public boolean canDrag(MotionEvent event) {
if (!AppConstants.MOZ_DRAGGABLE_URLBAR) {
return false;
}
// if no current tab is active.
if (Tabs.getInstance().getSelectedTab() == null) {
return false;
}
// currently disabled for tablets.
if (HardwareUtils.isTablet()) {
return false;
}
// not enabled in editing mode.
if (mBrowserToolbar.isEditing()) {
return false;
}
return isInToolbarBounds((int) event.getRawY());
}
@Override
public boolean canInterceptEventWhileOpen(MotionEvent event) {
if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
return false;
}
// Need to check if are intercepting a touch on main layout since we might hit a visible toast.
if (mRootLayout.findTopChildUnder(event) == mMainLayout &&
isInToolbarBounds((int) event.getRawY())) {
return true;
}
return false;
}
private boolean isInToolbarBounds(int y) {
mBrowserToolbar.getLocationOnScreen(mToolbarLocation);
final int upperLimit = mToolbarLocation[1] + mBrowserToolbar.getMeasuredHeight();
final int lowerLimit = mToolbarLocation[1];
return (y > lowerLimit && y < upperLimit);
}
public void prepareTabsToShow() {
if (ensureTabsPanelExists()) {
// If we've just inflated the tabs panel, only show it once the current
// layout pass is done to avoid displayed temporary UI states during
// relayout.
final ViewTreeObserver vto = mTabsPanel.getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
prepareTabsToShow();
}
});
}
} else {
mTabsPanel.prepareToDrag();
}
}
public int getLowerLimit() {
return getStatusBarHeight();
}
private int getStatusBarHeight() {
if (mStatusBarHeight != 0) {
return mStatusBarHeight;
}
final int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
mStatusBarHeight = getResources().getDimensionPixelSize(resourceId);
return mStatusBarHeight;
}
Log.e(LOGTAG, "Unable to find statusbar height");
return 0;
}
}
@Override
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
final View view;
@ -898,9 +729,6 @@ public class BrowserApp extends GeckoApp
}
});
mDragHelper = new DragHelper();
mRootLayout.setDraggableCallback(mDragHelper);
// Set the maximum bits-per-pixel the favicon system cares about.
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
@ -1677,7 +1505,6 @@ public class BrowserApp extends GeckoApp
invalidateOptionsMenu();
if (mTabsPanel != null) {
mRootLayout.reset();
mTabsPanel.refresh();
}
@ -2163,13 +1990,11 @@ public class BrowserApp extends GeckoApp
if (!areTabsShown()) {
mTabsPanel.setVisibility(View.INVISIBLE);
mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mRootLayout.setClosed();
mBrowserToolbar.setContextMenuEnabled(true);
} else {
// Cancel editing mode to return to page content when the TabsPanel closes. We cancel
// it here because there are graphical glitches if it's canceled while it's visible.
mBrowserToolbar.cancelEdit();
mRootLayout.setOpen();
}
mTabsPanel.finishTabsAnimation();

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

@ -71,6 +71,7 @@ import android.os.Process;
import android.os.StrictMode;
import android.provider.ContactsContract;
import android.provider.MediaStore.Images.Media;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Base64;
@ -165,7 +166,7 @@ public abstract class GeckoApp
// after a version upgrade.
private static final int CLEANUP_DEFERRAL_SECONDS = 15;
protected OuterLayout mRootLayout;
protected RelativeLayout mRootLayout;
protected RelativeLayout mMainLayout;
protected RelativeLayout mGeckoLayout;
@ -644,6 +645,16 @@ public abstract class GeckoApp
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
} else if ("Snackbar:Show".equals(event)) {
final String msg = message.getString("message");
final int duration = message.getInt("duration");
NativeJSObject action = message.optObject("action", null);
showSnackbar(msg,
duration,
action != null ? action.optString("label", null) : null,
callback);
} else if ("SystemUI:Visibility".equals(event)) {
setSystemUiVisible(message.getBoolean("visible"));
@ -833,6 +844,47 @@ public abstract class GeckoApp
return mToast;
}
void showSnackbar(final String message, final int duration, final String action, final EventCallback callback) {
final Snackbar snackbar = Snackbar.make(mRootLayout, message, duration);
if (!TextUtils.isEmpty(action)) {
final SnackbarEventCallback snackbarCallback = new SnackbarEventCallback(callback);
snackbar.setAction(action, snackbarCallback);
snackbar.setCallback(snackbarCallback);
}
snackbar.show();
}
private static class SnackbarEventCallback extends Snackbar.Callback implements View.OnClickListener {
private EventCallback callback;
public SnackbarEventCallback(EventCallback callback) {
this.callback = callback;
}
@Override
public synchronized void onClick(View view) {
if (callback == null) {
return;
}
callback.sendSuccess(null);
callback = null;
}
@Override
public synchronized void onDismissed(Snackbar snackbar, int event) {
if (callback == null || event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
return;
}
callback.sendError(null);
callback = null;
}
}
void showButtonToast(final String message, final String duration,
final String buttonText, final String buttonIcon,
final String buttonId) {
@ -1259,6 +1311,7 @@ public abstract class GeckoApp
"PrivateBrowsing:Data",
"Session:StatePurged",
"Share:Text",
"Snackbar:Show",
"SystemUI:Visibility",
"Toast:Show",
"ToggleChrome:Focus",
@ -1298,7 +1351,7 @@ public abstract class GeckoApp
setContentView(getLayout());
// Set up Gecko layout.
mRootLayout = (OuterLayout) findViewById(R.id.root_layout);
mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
mLayerView = (LayerView) findViewById(R.id.layer_view);

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

@ -58,6 +58,7 @@ JAVA_CLASSPATH += \
$(ANDROID_SUPPORT_V4_AAR_LIB) \
$(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
$(ANDROID_DESIGN_AAR_LIB) \
$(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
$(NULL)
@ -81,6 +82,7 @@ java_bundled_libs := \
$(ANDROID_SUPPORT_V4_AAR_LIB) \
$(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
$(ANDROID_DESIGN_AAR_LIB) \
$(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
$(NULL)
@ -363,6 +365,7 @@ generated/org/mozilla/gecko/R.java: .aapt.deps ;
# If native devices are enabled, add Google Play Services, build their resources
generated/android/support/v4/R.java: .aapt.deps ;
generated/android/support/v7/appcompat/R.java: .aapt.deps ;
generated/android/support/design/R.java: .aapt.deps ;
generated/android/support/v7/mediarouter/R.java: .aapt.deps ;
generated/android/support/v7/recyclerview/R.java: .aapt.deps ;
generated/com/google/android/gms/R.java: .aapt.deps ;

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

@ -1,254 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
/* Outerlayout is the container layout of all the main views. It allows mainlayout to be dragged while targeting
the toolbar and it's responsible for handling the dragprocess. It relies on ViewDragHelper to ease the drag process.
*/
public class OuterLayout extends RelativeLayout {
private final double AUTO_OPEN_SPEED_LIMIT = 800.0;
private ViewDragHelper mDragHelper;
private int mDraggingBorder;
private int mDragRange;
private boolean mIsOpen = false;
private int mDraggingState = ViewDragHelper.STATE_IDLE;
private DragCallback mDragCallback;
public static interface DragCallback {
public void startDrag(boolean wasOpen);
public void stopDrag(boolean stoppingToOpen);
public int getDragRange();
public int getOrderedChildIndex(int index);
public boolean canDrag(MotionEvent event);
public boolean canInterceptEventWhileOpen(MotionEvent event);
public void onDragProgress(float progress);
public View getViewToDrag();
public int getLowerLimit();
}
private class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public void onViewDragStateChanged(int newState) {
if (newState == mDraggingState) { // no change
return;
}
// if the view stopped moving.
if ((mDraggingState == ViewDragHelper.STATE_DRAGGING || mDraggingState == ViewDragHelper.STATE_SETTLING) &&
newState == ViewDragHelper.STATE_IDLE) {
final float rangeToCheck = mDragRange;
final float lowerLimit = mDragCallback.getLowerLimit();
if (mDraggingBorder == lowerLimit) {
mIsOpen = false;
mDragCallback.onDragProgress(0);
} else if (mDraggingBorder == rangeToCheck) {
mIsOpen = true;
mDragCallback.onDragProgress(1);
}
mDragCallback.stopDrag(mIsOpen);
}
// The view was previuosly moving.
if (newState == ViewDragHelper.STATE_DRAGGING && !isMoving()) {
mDragCallback.startDrag(mIsOpen);
updateRanges();
}
mDraggingState = newState;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mDraggingBorder = top;
final float progress = Math.min(1, ((float) top) / mDragRange);
mDragCallback.onDragProgress(progress);
}
@Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
@Override
public int getOrderedChildIndex(int index) {
return mDragCallback.getOrderedChildIndex(index);
}
@Override
public boolean tryCaptureView(View view, int i) {
return (view.getId() == mDragCallback.getViewToDrag().getId());
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
final float rangeToCheck = mDragRange;
final float speedToCheck = yvel;
if (mDraggingBorder == mDragCallback.getLowerLimit()) {
return;
}
if (mDraggingBorder == rangeToCheck) {
return;
}
boolean settleToOpen = false;
// Speed has priority over position.
if (speedToCheck > AUTO_OPEN_SPEED_LIMIT) {
settleToOpen = true;
} else if (speedToCheck < -AUTO_OPEN_SPEED_LIMIT) {
settleToOpen = false;
} else if (mDraggingBorder > rangeToCheck / 2) {
settleToOpen = true;
} else if (mDraggingBorder < rangeToCheck / 2) {
settleToOpen = false;
}
final int settleDestX;
final int settleDestY;
if (settleToOpen) {
settleDestX = 0;
settleDestY = mDragRange;
} else {
settleDestX = 0;
settleDestY = mDragCallback.getLowerLimit();
}
if(mDragHelper.settleCapturedViewAt(settleDestX, settleDestY)) {
ViewCompat.postInvalidateOnAnimation(OuterLayout.this);
}
}
}
public OuterLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
private void updateRanges() {
// Need to wait for the tabs to show in order to fetch the right sizes.
mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit();
}
private void updateOrientation() {
mDragHelper.setEdgeTrackingEnabled(0);
}
@Override
protected void onFinishInflate() {
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
mIsOpen = false;
super.onFinishInflate();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mDragCallback.canDrag(event)) {
if (mDragHelper.shouldInterceptTouchEvent(event)) {
return true;
}
// Because while open the target layout is translated and draghelper does not catch it.
if (mIsOpen && mDragCallback.canInterceptEventWhileOpen(event)) {
return true;
}
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// touch events can be passed to the helper if we target the toolbar or we are already dragging.
if (mDragCallback.canDrag(event) || mDraggingState == ViewDragHelper.STATE_DRAGGING) {
mDragHelper.processTouchEvent(event);
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// The first time fennec is started, tabs might not have been created while we drag. In that case we need
// an arbitrary range to start dragging that will be updated as soon as the tabs are created.
if (mDragRange == 0) {
mDragRange = h / 2;
}
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public void computeScroll() { // needed for automatic settling.
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
/**
* To be called when closing the tabs from outside (i.e. when touching the main layout).
*/
public void setClosed() {
mIsOpen = false;
mDragHelper.abort();
}
/**
* To be called when opening the tabs from outside (i.e. when clicking on the tabs button).
*/
public void setOpen() {
mIsOpen = true;
mDragHelper.abort();
}
public void setDraggableCallback(DragCallback dragCallback) {
mDragCallback = dragCallback;
updateOrientation();
}
// If a change happens while we are dragging, we abort the dragging and set to open state.
public void reset() {
updateOrientation();
if (isMoving()) {
mDragHelper.abort();
if (mDragCallback != null) {
mDragCallback.stopDrag(false);
mDragCallback.onDragProgress(0f);
}
}
}
public void updateDragHelperParameters() {
mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit();
updateOrientation();
}
public boolean isMoving() {
return (mDraggingState == ViewDragHelper.STATE_DRAGGING ||
mDraggingState == ViewDragHelper.STATE_SETTLING);
}
public boolean isOpen() {
return mIsOpen;
}
public View findTopChildUnder(MotionEvent event) {
return mDragHelper.findTopChildUnder((int) event.getX(), (int) event.getY());
}
public void restoreTargetViewPosition() {
mDragCallback.getViewToDrag().offsetTopAndBottom(mDraggingBorder);
}
}

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

@ -10,11 +10,11 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.activities.FxAccountWebFlowActivity;
public class SyncPanel extends FirstrunPanel {
// XXX: To simplify localization, this uses the pref_sync string. If this is used in the final product, add a new string to Nightly.
@ -29,8 +29,10 @@ public class SyncPanel extends FirstrunPanel {
public void onClick(View v) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-sync");
final Intent accountIntent = new Intent(getActivity(), FxAccountGetStartedActivity.class);
startActivity(accountIntent);
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
intent.putExtra(FxAccountWebFlowActivity.EXTRA_ENDPOINT, FxAccountConstants.ENDPOINT_FIRSTRUN);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
close();
}

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

@ -52,6 +52,10 @@ if CONFIG['ANDROID_APPCOMPAT_V7_AAR']:
ANDROID_EXTRA_PACKAGES += ['android.support.v7.appcompat']
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_V7_AAR_RES']]
resjar.generated_sources += ['android/support/v7/appcompat/R.java']
if CONFIG['ANDROID_DESIGN_AAR']:
ANDROID_EXTRA_PACKAGES += ['android.support.design']
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_DESIGN_AAR_RES']]
resjar.generated_sources += ['android/support/design/R.java']
if CONFIG['ANDROID_RECYCLERVIEW_V7_AAR']:
ANDROID_EXTRA_PACKAGES += ['android.support.v7.recyclerview']
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_RES']]
@ -409,7 +413,6 @@ gbjar.sources += [
'NotificationService.java',
'NSSBridge.java',
'OrderedBroadcastHelper.java',
'OuterLayout.java',
'preferences/AlignRightLinkPreference.java',
'preferences/AndroidImport.java',
'preferences/AndroidImportPreference.java',
@ -689,6 +692,7 @@ if CONFIG['MOZ_NATIVE_DEVICES']:
resjar.generated_sources += ['com/google/android/gms/cast/R.java']
gbjar.extra_jars += [CONFIG['ANDROID_APPCOMPAT_V7_AAR_LIB']]
gbjar.extra_jars += [CONFIG['ANDROID_DESIGN_AAR_LIB']]
gbjar.extra_jars += [CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB']]
gbjar.javac_flags += ['-Xlint:all,-deprecation,-fallthrough', '-J-Xmx512m', '-J-Xms128m']

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

@ -3,8 +3,7 @@
- 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/. -->
<org.mozilla.gecko.OuterLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -151,4 +150,4 @@
android:layout="@layout/button_toast"
style="@style/Toast"/>
</org.mozilla.gecko.OuterLayout>
</RelativeLayout>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше