зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central a=merge
This commit is contained in:
Коммит
a1c13d0acf
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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>
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче