diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 621d2cc76fb0..a70d972b9cfa 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5080,6 +5080,7 @@ nsBrowserAccess.prototype = { fromExternal: aIsExternal, inBackground: loadInBackground, forceNotRemote: aForceNotRemote, + forceBrowserInsertion: true, opener: aOpener, }); let browser = win.gBrowser.getBrowserForTab(tab); diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index d4973e065ec7..7e96d4d409c7 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1514,6 +1514,7 @@ var aSameProcessAsFrameLoader; var aOriginPrincipal; var aOpener; + var aForceBrowserInsertion; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1537,6 +1538,7 @@ aOriginPrincipal = params.originPrincipal; aOpener = params.opener; aIsPrerendered = params.isPrerendered; + aForceBrowserInsertion = params.forceBrowserInsertion; } var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : @@ -1556,6 +1558,7 @@ skipAnimation: aSkipAnimation, allowMixedContent: aAllowMixedContent, forceNotRemote: aForceNotRemote, + forceBrowserInsertion: aForceBrowserInsertion, preferredRemoteType: aPreferredRemoteType, noReferrer: aNoReferrer, userContextId: aUserContextId, @@ -1710,6 +1713,9 @@ } let tab = this.getTabForBrowser(aBrowser); + // aBrowser needs to be inserted now if it hasn't been already. + this._insertBrowser(tab); + let evt = document.createEvent("Events"); evt.initEvent("BeforeTabRemotenessChange", true, false); tab.dispatchEvent(evt); @@ -1842,8 +1848,10 @@ // If we're in a LargeAllocation process, we prefer switching back // into a normal content process, as that way we can clean up the // L-A process. - let preferredRemoteType = aBrowser.remoteType; - if (aBrowser.remoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) { + let currentRemoteType = aBrowser.getAttribute("remoteType") || null; + let preferredRemoteType = currentRemoteType; + + if (currentRemoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) { preferredRemoteType = E10SUtils.DEFAULT_REMOTE_TYPE; } aOptions.remoteType = @@ -1854,7 +1862,7 @@ // If this URL can't load in the current browser then flip it to the // correct type. - if (aBrowser.remoteType != aOptions.remoteType || + if (currentRemoteType != aOptions.remoteType || aOptions.newFrameloader) { let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE; return this.updateBrowserRemoteness(aBrowser, remote, aOptions); @@ -1943,12 +1951,12 @@ - + + [ + "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload", + "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags", + "goHome", "homePage", "gotoIndex", "currentURI", "documentURI", + "preferences", "imageDocument", "isRemoteBrowser", "messageManager", + "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle", + "characterSet", "fullZoom", "textZoom", "webProgress", + "addProgressListener", "removeProgressListener", "audioPlaybackStarted", + "audioPlaybackStopped", "adjustPriority", "pauseMedia", "stopMedia", + "blockMedia", "resumeMedia", "audioMuted", "mute", "unmute", + "blockedPopups", "mIconURL", "lastURI", "userTypedValue", + "purgeSessionHistory", "stopScroll", "startScroll" + ] + + + + + { + this._insertBrowser(aTab); + return browser[name]; + }; + setter = (value) => { + this._insertBrowser(aTab); + return browser[name] = value; + }; + } + Object.defineProperty(browser, name, { + get: getter, + set: setter, + configurable: true, + enumerable: true + }); + } + ]]> + + + + - - into the DOM if necessary. if (!notificationbox.parentNode) { @@ -2124,8 +2165,6 @@ var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} }); aTab.dispatchEvent(evt); - - return { usingPreloadedContent }; ]]> @@ -2158,6 +2197,7 @@ var aOriginPrincipal; var aDisallowInheritPrincipal; var aOpener; + var aForceBrowserInsertion; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -2183,6 +2223,7 @@ aDisallowInheritPrincipal = params.disallowInheritPrincipal; aOpener = params.opener; aIsPrerendered = params.isPrerendered; + aForceBrowserInsertion = params.forceBrowserInsertion; } // if we're adding tabs, we're past interrupt mode, ditch the owner @@ -2245,7 +2286,6 @@ var position = this.tabs.length - 1; t._tPos = position; - t.permanentKey = {}; this.tabContainer._setPositionalAttributes(); this.tabContainer.updateVisibility(); @@ -2259,23 +2299,51 @@ gMultiProcessBrowser); } - // Currently in this incarnation of bug 906076, we are forcing the - // browser to immediately be linked. In future incarnations of this - // bug this will be removed so we can leave the tab in its "lazy" - // state to be exploited for startup optimization. Note that for - // now this must occur before "TabOpen" event is fired, as that will - // trigger SessionStore.jsm to run code that expects the existence - // of tab.linkedBrowser. - let browserParams = { - forceNotRemote: aForceNotRemote, - preferredRemoteType: aPreferredRemoteType, - userContextId: aUserContextId, - sameProcessAsFrameLoader: aSameProcessAsFrameLoader, - opener: aOpener, - isPrerendered: aIsPrerendered, - }; - let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams); - let b = t.linkedBrowser; + let remoteType = + aForceNotRemote ? E10SUtils.NOT_REMOTE + : E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser, + aPreferredRemoteType); + + let b; + let usingPreloadedContent = false; + + // If we open a new tab with the newtab URL in the default + // userContext, check if there is a preloaded browser ready. + // Private windows are not included because both the label and the + // icon for the tab would be set incorrectly (see bug 1195981). + if (aURI == BROWSER_NEW_TAB_URL && + !aUserContextId && + !PrivateBrowsingUtils.isWindowPrivate(window)) { + b = this._getPreloadedBrowser(); + if (b) { + usingPreloadedContent = true; + } + } + + if (!b) { + // No preloaded browser found, create one. + b = this._createBrowser({ remoteType, + uriIsAboutBlank, + userContextId: aUserContextId, + sameProcessAsFrameLoader: aSameProcessAsFrameLoader, + opener: aOpener, + isPrerendered: aIsPrerendered }); + } + + t.linkedBrowser = b; + this._tabForBrowser.set(b, t); + t.permanentKey = b.permanentKey; + t._browserParams = { uriIsAboutBlank, + remoteType, + usingPreloadedContent }; + + // If we're creating a blank tab, create a lazy browser. + // Otherwise insert the browser into the document now. + if (uriIsAboutBlank && !aForceBrowserInsertion) { + this._createLazyBrowser(t); + } else { + this._insertBrowser(t); + } // Dispatch a new tab notification. We do this once we're // entirely done, so that things are in a consistent state @@ -2495,6 +2563,9 @@ var skipPermitUnload = aParams.skipPermitUnload; } + // Ensure aTab's browser is inserted into the document. + this._insertBrowser(aTab); + window.maybeRecordAbandonmentTelemetry(aTab, "tabClosed"); // Handle requests for synchronously removing an already @@ -2994,6 +3065,9 @@ + + + + + + + diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 975b06fbe425..da0e38f01d95 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,3 +1,3 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.7.297 +Current extension version is: 1.7.312 diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 20b137d2ddd5..4b0b7fd115a5 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -221,6 +221,11 @@ var VERBOSITY_LEVELS = { warnings: 1, infos: 5 }; +var CMapCompressionType = { + NONE: 0, + BINARY: 1, + STREAM: 2 +}; var OPS = { dependency: 1, setLineWidth: 2, @@ -1166,6 +1171,12 @@ function isArrayBuffer(v) { function isSpace(ch) { return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A; } +function isNodeJS() { + if (typeof __pdfjsdev_webpack__ === 'undefined') { + return typeof process === 'object' && process + '' === '[object process]'; + } + return false; +} function createPromiseCapability() { var capability = {}; capability.promise = new Promise(function (resolve, reject) { @@ -1438,6 +1449,7 @@ exports.AnnotationFlag = AnnotationFlag; exports.AnnotationType = AnnotationType; exports.FontType = FontType; exports.ImageKind = ImageKind; +exports.CMapCompressionType = CMapCompressionType; exports.InvalidPDFException = InvalidPDFException; exports.MessageHandler = MessageHandler; exports.MissingDataException = MissingDataException; @@ -1474,6 +1486,7 @@ exports.isInt = isInt; exports.isNum = isNum; exports.isString = isString; exports.isSpace = isSpace; +exports.isNodeJS = isNodeJS; exports.isSameOrigin = isSameOrigin; exports.createValidAbsoluteUrl = createValidAbsoluteUrl; exports.isLittleEndian = isLittleEndian; @@ -1506,6 +1519,8 @@ var removeNullCharacters = sharedUtil.removeNullCharacters; var warn = sharedUtil.warn; var deprecated = sharedUtil.deprecated; var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; +var stringToBytes = sharedUtil.stringToBytes; +var CMapCompressionType = sharedUtil.CMapCompressionType; var DEFAULT_LINK_REL = 'noopener noreferrer nofollow'; function DOMCanvasFactory() { } @@ -1535,6 +1550,48 @@ DOMCanvasFactory.prototype = { canvasAndContextPair.context = null; } }; +var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() { + function DOMCMapReaderFactory(params) { + this.baseUrl = params.baseUrl || null; + this.isCompressed = params.isCompressed || false; + } + DOMCMapReaderFactory.prototype = { + fetch: function (params) { + if (!params.name) { + return Promise.reject(new Error('CMap name must be specified.')); + } + return new Promise(function (resolve, reject) { + var url = this.baseUrl + params.name; + var request = new XMLHttpRequest(); + if (this.isCompressed) { + url += '.bcmap'; + request.responseType = 'arraybuffer'; + } + request.onreadystatechange = function () { + if (request.readyState === XMLHttpRequest.DONE && (request.status === 200 || request.status === 0)) { + var data; + if (this.isCompressed && request.response) { + data = new Uint8Array(request.response); + } else if (!this.isCompressed && request.responseText) { + data = stringToBytes(request.responseText); + } + if (data) { + resolve({ + cMapData: data, + compressionType: this.isCompressed ? CMapCompressionType.BINARY : CMapCompressionType.NONE + }); + return; + } + reject(new Error('Unable to load ' + (this.isCompressed ? 'binary' : '') + ' CMap at: ' + url)); + } + }.bind(this); + request.open('GET', url, true); + request.send(null); + }.bind(this)); + } + }; + return DOMCMapReaderFactory; +}(); var CustomStyle = function CustomStyleClosure() { var prefixes = [ 'ms', @@ -1694,6 +1751,7 @@ exports.hasCanvasTypedArrays = hasCanvasTypedArrays; exports.getDefaultSetting = getDefaultSetting; exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL; exports.DOMCanvasFactory = DOMCanvasFactory; +exports.DOMCMapReaderFactory = DOMCMapReaderFactory; /***/ }), /* 2 */ @@ -2358,6 +2416,7 @@ var CanvasGraphics = displayCanvas.CanvasGraphics; var Metadata = displayMetadata.Metadata; var getDefaultSetting = displayDOMUtils.getDefaultSetting; var DOMCanvasFactory = displayDOMUtils.DOMCanvasFactory; +var DOMCMapReaderFactory = displayDOMUtils.DOMCMapReaderFactory; var DEFAULT_RANGE_CHUNK_SIZE = 65536; var isWorkerDisabled = false; var workerSrc; @@ -2431,6 +2490,7 @@ function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallb } params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE; params.disableNativeImageDecoder = params.disableNativeImageDecoder === true; + var CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory; if (!worker) { worker = new PDFWorker(); task._worker = worker; @@ -2445,7 +2505,7 @@ function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallb throw new Error('Loading aborted'); } var messageHandler = new MessageHandler(docId, workerId, worker.port); - var transport = new WorkerTransport(messageHandler, task, rangeTransport); + var transport = new WorkerTransport(messageHandler, task, rangeTransport, CMapReaderFactory); task._transport = transport; messageHandler.send('Ready', null); }); @@ -2468,8 +2528,6 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { source: source, disableRange: getDefaultSetting('disableRange'), maxImageSize: getDefaultSetting('maxImageSize'), - cMapUrl: getDefaultSetting('cMapUrl'), - cMapPacked: getDefaultSetting('cMapPacked'), disableFontFace: getDefaultSetting('disableFontFace'), disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'), postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled, @@ -3081,12 +3139,16 @@ var PDFWorker = function PDFWorkerClosure() { return PDFWorker; }(); var WorkerTransport = function WorkerTransportClosure() { - function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport) { + function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport, CMapReaderFactory) { this.messageHandler = messageHandler; this.loadingTask = loadingTask; this.pdfDataRangeTransport = pdfDataRangeTransport; this.commonObjs = new PDFObjects(); this.fontLoader = new FontLoader(loadingTask.docId); + this.CMapReaderFactory = new CMapReaderFactory({ + baseUrl: getDefaultSetting('cMapUrl'), + isCompressed: getDefaultSetting('cMapPacked') + }); this.destroyed = false; this.destroyCapability = null; this._passwordCapability = null; @@ -3371,6 +3433,12 @@ var WorkerTransport = function WorkerTransportClosure() { img.src = imageUrl; }); }, this); + messageHandler.on('FetchBuiltInCMap', function (data) { + if (this.destroyed) { + return Promise.reject(new Error('Worker was destroyed')); + } + return this.CMapReaderFactory.fetch({ name: data.name }); + }, this); }, getData: function WorkerTransport_getData() { return this.messageHandler.sendWithPromise('GetData', null); @@ -3632,8 +3700,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() { } }; }(); -exports.version = '1.7.297'; -exports.build = '425ad309'; +exports.version = '1.7.312'; +exports.build = 'cada411a'; exports.getDocument = getDocument; exports.PDFDataRangeTransport = PDFDataRangeTransport; exports.PDFWorker = PDFWorker; @@ -4650,8 +4718,8 @@ if (!globalScope.PDFJS) { globalScope.PDFJS = {}; } var PDFJS = globalScope.PDFJS; -PDFJS.version = '1.7.297'; -PDFJS.build = '425ad309'; +PDFJS.version = '1.7.312'; +PDFJS.build = 'cada411a'; PDFJS.pdfBug = false; if (PDFJS.verbosity !== undefined) { sharedUtil.setVerbosityLevel(PDFJS.verbosity); @@ -7143,8 +7211,8 @@ exports.TilingPattern = TilingPattern; "use strict"; -var pdfjsVersion = '1.7.297'; -var pdfjsBuild = '425ad309'; +var pdfjsVersion = '1.7.312'; +var pdfjsBuild = 'cada411a'; var pdfjsSharedUtil = __w_pdfjs_require__(0); var pdfjsDisplayGlobal = __w_pdfjs_require__(8); var pdfjsDisplayAPI = __w_pdfjs_require__(3); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 63170673249f..8c47f3c4b461 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -221,6 +221,11 @@ var VERBOSITY_LEVELS = { warnings: 1, infos: 5 }; +var CMapCompressionType = { + NONE: 0, + BINARY: 1, + STREAM: 2 +}; var OPS = { dependency: 1, setLineWidth: 2, @@ -1166,6 +1171,12 @@ function isArrayBuffer(v) { function isSpace(ch) { return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A; } +function isNodeJS() { + if (typeof __pdfjsdev_webpack__ === 'undefined') { + return typeof process === 'object' && process + '' === '[object process]'; + } + return false; +} function createPromiseCapability() { var capability = {}; capability.promise = new Promise(function (resolve, reject) { @@ -1438,6 +1449,7 @@ exports.AnnotationFlag = AnnotationFlag; exports.AnnotationType = AnnotationType; exports.FontType = FontType; exports.ImageKind = ImageKind; +exports.CMapCompressionType = CMapCompressionType; exports.InvalidPDFException = InvalidPDFException; exports.MessageHandler = MessageHandler; exports.MissingDataException = MissingDataException; @@ -1474,6 +1486,7 @@ exports.isInt = isInt; exports.isNum = isNum; exports.isString = isString; exports.isSpace = isSpace; +exports.isNodeJS = isNodeJS; exports.isSameOrigin = isSameOrigin; exports.createValidAbsoluteUrl = createValidAbsoluteUrl; exports.isLittleEndian = isLittleEndian; @@ -24787,6 +24800,7 @@ var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES; var ImageKind = sharedUtil.ImageKind; var OPS = sharedUtil.OPS; var TextRenderingMode = sharedUtil.TextRenderingMode; +var CMapCompressionType = sharedUtil.CMapCompressionType; var Util = sharedUtil.Util; var assert = sharedUtil.assert; var createPromiseCapability = sharedUtil.createPromiseCapability; @@ -24845,10 +24859,6 @@ var PartialEvaluator = function PartialEvaluatorClosure() { forceDataSchema: false, maxImageSize: -1, disableFontFace: false, - cMapOptions: { - url: null, - packed: false - }, disableNativeImageDecoder: false }; function NativeImageDecoder(xref, resources, handler, forceDataSchema) { @@ -24892,14 +24902,27 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res); return (cs.numComps === 1 || cs.numComps === 3) && cs.isDefaultDecode(dict.getArray('Decode', 'D')); }; - function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, options) { + function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options) { this.pdfManager = pdfManager; this.xref = xref; this.handler = handler; this.pageIndex = pageIndex; this.idFactory = idFactory; this.fontCache = fontCache; + this.builtInCMapCache = builtInCMapCache; this.options = options || DefaultPartialEvaluatorOptions; + this.fetchBuiltInCMap = function (name) { + var cachedCMap = builtInCMapCache[name]; + if (cachedCMap) { + return Promise.resolve(cachedCMap); + } + return handler.sendWithPromise('FetchBuiltInCMap', { name: name }).then(function (data) { + if (data.compressionType !== CMapCompressionType.NONE) { + builtInCMapCache[name] = data; + } + return data; + }); + }; } var TIME_SLOT_DURATION_MS = 20; var CHECK_TIME_EVERY = 100; @@ -26236,7 +26259,11 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var registry = properties.cidSystemInfo.registry; var ordering = properties.cidSystemInfo.ordering; var ucs2CMapName = Name.get(registry + '-' + ordering + '-UCS2'); - return CMapFactory.create(ucs2CMapName, this.options.cMapOptions, null).then(function (ucs2CMap) { + return CMapFactory.create({ + encoding: ucs2CMapName, + fetchBuiltInCMap: this.fetchBuiltInCMap, + useCMap: null + }).then(function (ucs2CMap) { var cMap = properties.cMap; toUnicode = []; cMap.forEach(function (charcode, cid) { @@ -26254,14 +26281,22 @@ var PartialEvaluator = function PartialEvaluatorClosure() { readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) { var cmapObj = toUnicode; if (isName(cmapObj)) { - return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(function (cmap) { + return CMapFactory.create({ + encoding: cmapObj, + fetchBuiltInCMap: this.fetchBuiltInCMap, + useCMap: null + }).then(function (cmap) { if (cmap instanceof IdentityCMap) { return new IdentityToUnicodeMap(0, 0xFFFF); } return new ToUnicodeMap(cmap.getMap()); }); } else if (isStream(cmapObj)) { - return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(function (cmap) { + return CMapFactory.create({ + encoding: cmapObj, + fetchBuiltInCMap: this.fetchBuiltInCMap, + useCMap: null + }).then(function (cmap) { if (cmap instanceof IdentityCMap) { return new IdentityToUnicodeMap(0, 0xFFFF); } @@ -26524,7 +26559,6 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var descriptor = preEvaluatedFont.descriptor; var type = preEvaluatedFont.type; var maxCharIndex = composite ? 0xFFFF : 0xFF; - var cMapOptions = this.options.cMapOptions; var properties; if (!descriptor) { if (type === 'Type3') { @@ -26619,7 +26653,11 @@ var PartialEvaluator = function PartialEvaluatorClosure() { if (isName(cidEncoding)) { properties.cidEncoding = cidEncoding.name; } - cMapPromise = CMapFactory.create(cidEncoding, cMapOptions, null).then(function (cMap) { + cMapPromise = CMapFactory.create({ + encoding: cidEncoding, + fetchBuiltInCMap: this.fetchBuiltInCMap, + useCMap: null + }).then(function (cMap) { properties.cMap = cMap; properties.vertical = properties.cMap.vertical; }); @@ -29872,6 +29910,7 @@ var Catalog = function CatalogClosure() { this.xref = xref; this.catDict = xref.getCatalogObj(); this.fontCache = new RefSetCache(); + this.builtInCMapCache = Object.create(null); assert(isDict(this.catDict), 'catalog object is not a dictionary'); this.pageFactory = pageFactory; this.pagePromises = []; @@ -30186,6 +30225,7 @@ var Catalog = function CatalogClosure() { delete font.translated; } this.fontCache.clear(); + this.builtInCMapCache = Object.create(null); }.bind(this)); }, getPage: function Catalog_getPage(pageIndex) { @@ -30193,7 +30233,7 @@ var Catalog = function CatalogClosure() { this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(function (a) { var dict = a[0]; var ref = a[1]; - return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache); + return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache); }.bind(this)); } return this.pagePromises[pageIndex]; @@ -33926,6 +33966,7 @@ var error = sharedUtil.error; var info = sharedUtil.info; var warn = sharedUtil.warn; var setVerbosityLevel = sharedUtil.setVerbosityLevel; +var isNodeJS = sharedUtil.isNodeJS; var Ref = corePrimitives.Ref; var LocalPdfManager = corePdfManager.LocalPdfManager; var NetworkPdfManager = corePdfManager.NetworkPdfManager; @@ -34403,15 +34444,10 @@ var WorkerMessageHandler = { }, onFailure); } ensureNotTerminated(); - var cMapOptions = { - url: data.cMapUrl === undefined ? null : data.cMapUrl, - packed: data.cMapPacked === true - }; var evaluatorOptions = { forceDataSchema: data.disableCreateObjectURL, maxImageSize: data.maxImageSize === undefined ? -1 : data.maxImageSize, disableFontFace: data.disableFontFace, - cMapOptions: cMapOptions, disableNativeImageDecoder: data.disableNativeImageDecoder }; getPdfManager(data, evaluatorOptions).then(function (newPdfManager) { @@ -34588,12 +34624,6 @@ function initializeWorker() { WorkerMessageHandler.setup(handler, self); handler.send('ready', null); } -function isNodeJS() { - if (typeof __pdfjsdev_webpack__ === 'undefined') { - return typeof process === 'object' && process + '' === '[object process]'; - } - return false; -} if (typeof window === 'undefined' && !isNodeJS()) { initializeWorker(); } @@ -36580,11 +36610,12 @@ var error = sharedUtil.error; var isInt = sharedUtil.isInt; var isString = sharedUtil.isString; var MissingDataException = sharedUtil.MissingDataException; +var CMapCompressionType = sharedUtil.CMapCompressionType; var isEOF = corePrimitives.isEOF; var isName = corePrimitives.isName; var isCmd = corePrimitives.isCmd; var isStream = corePrimitives.isStream; -var StringStream = coreStream.StringStream; +var Stream = coreStream.Stream; var Lexer = coreParser.Lexer; var BUILT_IN_CMAPS = [ 'Adobe-GB1-UCS2', @@ -36919,23 +36950,6 @@ var IdentityCMap = function IdentityCMapClosure() { return IdentityCMap; }(); var BinaryCMapReader = function BinaryCMapReaderClosure() { - function fetchBinaryData(url) { - return new Promise(function (resolve, reject) { - var request = new XMLHttpRequest(); - request.open('GET', url, true); - request.responseType = 'arraybuffer'; - request.onreadystatechange = function () { - if (request.readyState === XMLHttpRequest.DONE) { - if (!request.response || request.status !== 200 && request.status !== 0) { - reject(new Error('Unable to get binary cMap at: ' + url)); - } else { - resolve(new Uint8Array(request.response)); - } - } - }; - request.send(null); - }); - } function hexToInt(a, size) { var n = 0; for (var i = 0; i <= size; i++) { @@ -37045,8 +37059,8 @@ var BinaryCMapReader = function BinaryCMapReaderClosure() { return s; } }; - function processBinaryCMap(url, cMap, extend) { - return fetchBinaryData(url).then(function (data) { + function processBinaryCMap(data, cMap, extend) { + return new Promise(function (resolve, reject) { var stream = new BinaryCMapStream(data); var header = stream.readByte(); cMap.vertical = !!(header & 1); @@ -37177,19 +37191,20 @@ var BinaryCMapReader = function BinaryCMapReaderClosure() { } break; default: - error('Unknown type: ' + type); - break; + reject(new Error('processBinaryCMap: Unknown type: ' + type)); + return; } } if (useCMap) { - return extend(useCMap); + resolve(extend(useCMap)); + return; } - return cMap; + resolve(cMap); }); } function BinaryCMapReader() { } - BinaryCMapReader.prototype = { read: processBinaryCMap }; + BinaryCMapReader.prototype = { process: processBinaryCMap }; return BinaryCMapReader; }(); var CMapFactory = function CMapFactoryClosure() { @@ -37330,7 +37345,7 @@ var CMapFactory = function CMapFactoryClosure() { cMap.name = obj.name; } } - function parseCMap(cMap, lexer, builtInCMapParams, useCMap) { + function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) { var previous; var embededUseCMap; objLoop: @@ -37384,12 +37399,12 @@ var CMapFactory = function CMapFactoryClosure() { useCMap = embededUseCMap; } if (useCMap) { - return extendCMap(cMap, builtInCMapParams, useCMap); + return extendCMap(cMap, fetchBuiltInCMap, useCMap); } return Promise.resolve(cMap); } - function extendCMap(cMap, builtInCMapParams, useCMap) { - return createBuiltInCMap(useCMap, builtInCMapParams).then(function (newCMap) { + function extendCMap(cMap, fetchBuiltInCMap, useCMap) { + return createBuiltInCMap(useCMap, fetchBuiltInCMap).then(function (newCMap) { cMap.useCMap = newCMap; if (cMap.numCodespaceRanges === 0) { var useCodespaceRanges = cMap.useCMap.codespaceRanges; @@ -37406,14 +37421,7 @@ var CMapFactory = function CMapFactoryClosure() { return cMap; }); } - function parseBinaryCMap(name, builtInCMapParams) { - var url = builtInCMapParams.url + name + '.bcmap'; - var cMap = new CMap(true); - return new BinaryCMapReader().read(url, cMap, function (useCMap) { - return extendCMap(cMap, builtInCMapParams, useCMap); - }); - } - function createBuiltInCMap(name, builtInCMapParams) { + function createBuiltInCMap(name, fetchBuiltInCMap) { if (name === 'Identity-H') { return Promise.resolve(new IdentityCMap(false, 2)); } else if (name === 'Identity-V') { @@ -37422,40 +37430,33 @@ var CMapFactory = function CMapFactoryClosure() { if (BUILT_IN_CMAPS.indexOf(name) === -1) { return Promise.reject(new Error('Unknown cMap name: ' + name)); } - assert(builtInCMapParams, 'built-in cMap parameters are not provided'); - if (builtInCMapParams.packed) { - return parseBinaryCMap(name, builtInCMapParams); - } - return new Promise(function (resolve, reject) { - var url = builtInCMapParams.url + name; - var request = new XMLHttpRequest(); - request.onreadystatechange = function () { - if (request.readyState === XMLHttpRequest.DONE) { - if (request.status === 200 || request.status === 0) { - var cMap = new CMap(true); - var lexer = new Lexer(new StringStream(request.responseText)); - parseCMap(cMap, lexer, builtInCMapParams, null).then(function (parsedCMap) { - resolve(parsedCMap); - }); - } else { - reject(new Error('Unable to get cMap at: ' + url)); - } - } - }; - request.open('GET', url, true); - request.send(null); + assert(fetchBuiltInCMap, 'Built-in CMap parameters are not provided.'); + return fetchBuiltInCMap(name).then(function (data) { + var cMapData = data.cMapData, compressionType = data.compressionType; + var cMap = new CMap(true); + if (compressionType === CMapCompressionType.BINARY) { + return new BinaryCMapReader().process(cMapData, cMap, function (useCMap) { + return extendCMap(cMap, fetchBuiltInCMap, useCMap); + }); + } + assert(compressionType === CMapCompressionType.NONE, 'TODO: Only BINARY/NONE CMap compression is currently supported.'); + var lexer = new Lexer(new Stream(cMapData)); + return parseCMap(cMap, lexer, fetchBuiltInCMap, null); }); } return { - create: function (encoding, builtInCMapParams, useCMap) { + create: function (params) { + var encoding = params.encoding; + var fetchBuiltInCMap = params.fetchBuiltInCMap; + var useCMap = params.useCMap; if (isName(encoding)) { - return createBuiltInCMap(encoding.name, builtInCMapParams); + return createBuiltInCMap(encoding.name, fetchBuiltInCMap); } else if (isStream(encoding)) { var cMap = new CMap(); var lexer = new Lexer(encoding); - return parseCMap(cMap, lexer, builtInCMapParams, useCMap).then(function (parsedCMap) { + return parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap).then(function (parsedCMap) { if (parsedCMap.isIdentityCMap) { - return createBuiltInCMap(parsedCMap.name, builtInCMapParams); + return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap); } return parsedCMap; }); @@ -37520,13 +37521,14 @@ var Page = function PageClosure() { 612, 792 ]; - function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache) { + function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache, builtInCMapCache) { this.pdfManager = pdfManager; this.pageIndex = pageIndex; this.pageDict = pageDict; this.xref = xref; this.ref = ref; this.fontCache = fontCache; + this.builtInCMapCache = builtInCMapCache; this.evaluatorOptions = pdfManager.evaluatorOptions; this.resourcesPromise = null; var uniquePrefix = 'p' + this.pageIndex + '_'; @@ -37652,7 +37654,7 @@ var Page = function PageClosure() { 'XObject', 'Font' ]); - var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.evaluatorOptions); + var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.builtInCMapCache, this.evaluatorOptions); var dataPromises = Promise.all([ contentStreamPromise, resourcesPromise @@ -37708,7 +37710,7 @@ var Page = function PageClosure() { ]); return dataPromises.then(function (data) { var contentStream = data[0]; - var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.evaluatorOptions); + var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions); return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems); }); }, @@ -37901,8 +37903,8 @@ var PDFDocument = function PDFDocumentClosure() { this.xref.parse(recoveryMode); var self = this; var pageFactory = { - createPage: function (pageIndex, dict, ref, fontCache) { - return new Page(self.pdfManager, self.xref, pageIndex, dict, ref, fontCache); + createPage: function (pageIndex, dict, ref, fontCache, builtInCMapCache) { + return new Page(self.pdfManager, self.xref, pageIndex, dict, ref, fontCache, builtInCMapCache); } }; this.catalog = new Catalog(this.pdfManager, this.xref, pageFactory); @@ -49174,8 +49176,8 @@ exports.Type1Parser = Type1Parser; "use strict"; -var pdfjsVersion = '1.7.297'; -var pdfjsBuild = '425ad309'; +var pdfjsVersion = '1.7.312'; +var pdfjsBuild = 'cada411a'; var pdfjsCoreWorker = __w_pdfjs_require__(17); ; exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index e163c108658c..749355fed12c 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -321,8 +321,11 @@ function getVisibleElements(scrollEl, views, sortByVisibility) { function noContextMenuHandler(e) { e.preventDefault(); } -function getPDFFileNameFromURL(url) { - var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; +function getPDFFileNameFromURL(url, defaultFilename) { + if (typeof defaultFilename === 'undefined') { + defaultFilename = 'document.pdf'; + } + var reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i; var splitURI = reURI.exec(url); var suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]); @@ -335,7 +338,7 @@ function getPDFFileNameFromURL(url) { } } } - return suggestedFilename || 'document.pdf'; + return suggestedFilename || defaultFilename; } function normalizeWheelEventDelta(evt) { var delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY); @@ -1148,11 +1151,15 @@ var PDFViewerApplication = { setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) { this.url = url; this.baseUrl = url.split('#')[0]; - try { - this.setTitle(decodeURIComponent(pdfjsLib.getFilenameFromUrl(url)) || url); - } catch (e) { - this.setTitle(url); + var title = getPDFFileNameFromURL(url, ''); + if (!title) { + try { + title = decodeURIComponent(pdfjsLib.getFilenameFromUrl(url)) || url; + } catch (e) { + title = url; + } } + this.setTitle(title); }, setTitle: function pdfViewSetTitle(title) { if (this.isViewerEmbedded) { @@ -1251,7 +1258,7 @@ var PDFViewerApplication = { downloadManager.downloadUrl(url, filename); } var url = this.baseUrl; - var filename = getPDFFileNameFromURL(url); + var filename = getPDFFileNameFromURL(this.url); var downloadManager = this.downloadManager; downloadManager.onerror = function (err) { PDFViewerApplication.error('PDF failed to download.'); @@ -3896,6 +3903,18 @@ var PDFAttachmentViewer = function PDFAttachmentViewerClosure() { }); this._renderedCapability.resolve(); }, + _bindPdfLink: function PDFAttachmentViewer_bindPdfLink(button, content, filename) { + var blobUrl; + button.onclick = function () { + if (!blobUrl) { + blobUrl = pdfjsLib.createObjectURL(content, 'application/pdf', pdfjsLib.PDFJS.disableCreateObjectURL); + } + var viewerUrl; + viewerUrl = blobUrl + '?' + encodeURIComponent(filename); + window.open(viewerUrl); + return false; + }; + }, _bindLink: function PDFAttachmentViewer_bindLink(button, content, filename) { button.onclick = function downloadFile(e) { this.downloadManager.downloadData(content, filename, ''); @@ -3922,11 +3941,16 @@ var PDFAttachmentViewer = function PDFAttachmentViewerClosure() { for (var i = 0; i < attachmentsCount; i++) { var item = attachments[names[i]]; var filename = pdfjsLib.getFilenameFromUrl(item.filename); + filename = pdfjsLib.removeNullCharacters(filename); var div = document.createElement('div'); div.className = 'attachmentsItem'; var button = document.createElement('button'); - this._bindLink(button, item.content, filename); - button.textContent = pdfjsLib.removeNullCharacters(filename); + button.textContent = filename; + if (/\.pdf$/i.test(filename)) { + this._bindPdfLink(button, item.content, filename); + } else { + this._bindLink(button, item.content, filename); + } div.appendChild(button); this.container.appendChild(div); } diff --git a/build/templates.mozbuild b/build/templates.mozbuild index 9b8500f659b5..89240b5c5276 100644 --- a/build/templates.mozbuild +++ b/build/templates.mozbuild @@ -61,7 +61,7 @@ def Library(name): @template -def RustLibrary(name, features=None): +def RustLibrary(name, features=None, target_dir=None): '''Template for Rust libraries.''' Library(name) @@ -70,6 +70,9 @@ def RustLibrary(name, features=None): if features: RUST_LIBRARY_FEATURES = features + if target_dir: + RUST_LIBRARY_TARGET_DIR = target_dir + @template def SharedLibrary(name): diff --git a/caps/nsNullPrincipal.cpp b/caps/nsNullPrincipal.cpp index e1555e4794e1..20d713890c18 100644 --- a/caps/nsNullPrincipal.cpp +++ b/caps/nsNullPrincipal.cpp @@ -166,25 +166,43 @@ nsNullPrincipal::Read(nsIObjectInputStream* aStream) // that the Init() method has already been invoked by the time we deserialize. // This is in contrast to nsPrincipal, which uses NS_GENERIC_FACTORY_CONSTRUCTOR, // in which case ::Read needs to invoke Init(). - nsAutoCString suffix; - nsresult rv = aStream->ReadCString(suffix); + + nsAutoCString spec; + nsresult rv = aStream->ReadCString(spec); NS_ENSURE_SUCCESS(rv, rv); - bool ok = mOriginAttributes.PopulateFromSuffix(suffix); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), spec); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString suffix; + rv = aStream->ReadCString(suffix); + NS_ENSURE_SUCCESS(rv, rv); + + OriginAttributes attrs; + bool ok = attrs.PopulateFromSuffix(suffix); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - return NS_OK; + return Init(attrs, uri); } NS_IMETHODIMP nsNullPrincipal::Write(nsIObjectOutputStream* aStream) { + NS_ENSURE_STATE(mURI); + + nsAutoCString spec; + nsresult rv = mURI->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aStream->WriteStringZ(spec.get()); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoCString suffix; OriginAttributesRef().CreateSuffix(suffix); - nsresult rv = aStream->WriteStringZ(suffix.get()); + rv = aStream->WriteStringZ(suffix.get()); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } - diff --git a/config/rules.mk b/config/rules.mk index 1f974406b220..9bb17a2a8908 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -936,7 +936,7 @@ rustflags_override = RUSTFLAGS='$(rustflags)' endif CARGO_BUILD = env $(rustflags_override) \ - CARGO_TARGET_DIR=. \ + CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \ RUSTC=$(RUSTC) \ MOZ_DIST=$(ABS_DIST) \ LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH) \ diff --git a/devtools/client/inspector/layout/actions/box-model.js b/devtools/client/inspector/boxmodel/actions/box-model.js similarity index 100% rename from devtools/client/inspector/layout/actions/box-model.js rename to devtools/client/inspector/boxmodel/actions/box-model.js diff --git a/devtools/client/inspector/boxmodel/actions/index.js b/devtools/client/inspector/boxmodel/actions/index.js new file mode 100644 index 000000000000..45758428e733 --- /dev/null +++ b/devtools/client/inspector/boxmodel/actions/index.js @@ -0,0 +1,14 @@ +/* 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 { createEnum } = require("devtools/client/shared/enum"); + +createEnum([ + + // Update the layout state with the latest layout properties. + "UPDATE_LAYOUT", + +], module.exports); diff --git a/devtools/client/inspector/boxmodel/actions/moz.build b/devtools/client/inspector/boxmodel/actions/moz.build new file mode 100644 index 000000000000..2f98f6793d0c --- /dev/null +++ b/devtools/client/inspector/boxmodel/actions/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'box-model.js', + 'index.js', +) diff --git a/devtools/client/inspector/boxmodel/box-model.js b/devtools/client/inspector/boxmodel/box-model.js new file mode 100644 index 000000000000..0290d69af88a --- /dev/null +++ b/devtools/client/inspector/boxmodel/box-model.js @@ -0,0 +1,301 @@ +/* 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 { Task } = require("devtools/shared/task"); +const { getCssProperties } = require("devtools/shared/fronts/css-properties"); +const { ReflowFront } = require("devtools/shared/fronts/reflow"); + +const { InplaceEditor } = require("devtools/client/shared/inplace-editor"); + +const { updateLayout } = require("./actions/box-model"); + +const EditingSession = require("./utils/editing-session"); + +const NUMERIC = /^-?[\d\.]+$/; + +/** + * A singleton instance of the box model controllers. + * + * @param {Inspector} inspector + * An instance of the Inspector currently loaded in the toolbox. + * @param {Window} window + * The document window of the toolbox. + */ +function BoxModel(inspector, window) { + this.document = window.document; + this.inspector = inspector; + this.store = inspector.store; + + this.updateBoxModel = this.updateBoxModel.bind(this); + + this.onHideBoxModelHighlighter = this.onHideBoxModelHighlighter.bind(this); + this.onNewSelection = this.onNewSelection.bind(this); + this.onShowBoxModelEditor = this.onShowBoxModelEditor.bind(this); + this.onShowBoxModelHighlighter = this.onShowBoxModelHighlighter.bind(this); + this.onSidebarSelect = this.onSidebarSelect.bind(this); + + this.inspector.selection.on("new-node-front", this.onNewSelection); + this.inspector.sidebar.on("select", this.onSidebarSelect); +} + +BoxModel.prototype = { + + /** + * Destruction function called when the inspector is destroyed. Removes event listeners + * and cleans up references. + */ + destroy() { + this.inspector.selection.off("new-node-front", this.onNewSelection); + this.inspector.sidebar.off("select", this.onSidebarSelect); + + if (this.reflowFront) { + this.untrackReflows(); + this.reflowFront.destroy(); + this.reflowFront = null; + } + + this.document = null; + this.inspector = null; + this.walker = null; + }, + + /** + * Returns an object containing the box model's handler functions used in the box + * model's React component props. + */ + getComponentProps() { + return { + onHideBoxModelHighlighter: this.onHideBoxModelHighlighter, + onShowBoxModelEditor: this.onShowBoxModelEditor, + onShowBoxModelHighlighter: this.onShowBoxModelHighlighter, + }; + }, + + /** + * Returns true if the computed or layout panel is visible, and false otherwise. + */ + isPanelVisible() { + return this.inspector.toolbox.currentToolId === "inspector" && + this.inspector.sidebar && + (this.inspector.sidebar.getCurrentTabID() === "layoutview" || + this.inspector.sidebar.getCurrentTabID() === "computedview"); + }, + + /** + * Returns true if the layout panel is visible and the current node is valid to + * be displayed in the view. + */ + isPanelVisibleAndNodeValid() { + return this.isPanelVisible() && + this.inspector.selection.isConnected() && + this.inspector.selection.isElementNode(); + }, + + /** + * Starts listening to reflows in the current tab. + */ + trackReflows() { + if (!this.reflowFront) { + let { target } = this.inspector; + if (target.form.reflowActor) { + this.reflowFront = ReflowFront(target.client, + target.form); + } else { + return; + } + } + + this.reflowFront.on("reflows", this.updateBoxModel); + this.reflowFront.start(); + }, + + /** + * Stops listening to reflows in the current tab. + */ + untrackReflows() { + if (!this.reflowFront) { + return; + } + + this.reflowFront.off("reflows", this.updateBoxModel); + this.reflowFront.stop(); + }, + + /** + * Updates the box model panel by dispatching the new layout data. + * + * @param {String} reason + * Optional string describing the reason why the boxmodel is updated. + */ + updateBoxModel(reason) { + this._updateReasons = this._updateReasons || []; + if (reason) { + this._updateReasons.push(reason); + } + + let lastRequest = Task.spawn((function* () { + if (!(this.isPanelVisible() && + this.inspector.selection.isConnected() && + this.inspector.selection.isElementNode())) { + return null; + } + + let node = this.inspector.selection.nodeFront; + let layout = yield this.inspector.pageStyle.getLayout(node, { + autoMargins: true, + }); + let styleEntries = yield this.inspector.pageStyle.getApplied(node, {}); + this.elementRules = styleEntries.map(e => e.rule); + + // Update the redux store with the latest layout properties and update the box + // model view. + this.store.dispatch(updateLayout(layout)); + + // If a subsequent request has been made, wait for that one instead. + if (this._lastRequest != lastRequest) { + return this._lastRequest; + } + + this.inspector.emit("boxmodel-view-updated", this._updateReasons); + + this._lastRequest = null; + this._updateReasons = []; + + return null; + }).bind(this)).catch(console.error); + + this._lastRequest = lastRequest; + }, + + /** + * Selection 'new-node-front' event handler. + */ + onNewSelection: function () { + if (!this.isPanelVisibleAndNodeValid()) { + return; + } + + if (this.inspector.selection.isConnected() && + this.inspector.selection.isElementNode()) { + this.trackReflows(); + } + + this.updateBoxModel("new-selection"); + }, + + /** + * Hides the box-model highlighter on the currently selected element. + */ + onHideBoxModelHighlighter() { + let toolbox = this.inspector.toolbox; + toolbox.highlighterUtils.unhighlight(); + }, + + /** + * Shows the inplace editor when a box model editable value is clicked on the + * box model panel. + * + * @param {DOMNode} element + * The element that was clicked. + * @param {Event} event + * The event object. + * @param {String} property + * The name of the property. + */ + onShowBoxModelEditor(element, event, property) { + let session = new EditingSession({ + inspector: this.inspector, + doc: this.document, + elementRules: this.elementRules, + }); + let initialValue = session.getProperty(property); + + let editor = new InplaceEditor({ + element: element, + initial: initialValue, + contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, + property: { + name: property + }, + start: self => { + self.elt.parentNode.classList.add("boxmodel-editing"); + }, + change: value => { + if (NUMERIC.test(value)) { + value += "px"; + } + + let properties = [ + { name: property, value: value } + ]; + + if (property.substring(0, 7) == "border-") { + let bprop = property.substring(0, property.length - 5) + "style"; + let style = session.getProperty(bprop); + if (!style || style == "none" || style == "hidden") { + properties.push({ name: bprop, value: "solid" }); + } + } + + session.setProperties(properties).catch(e => console.error(e)); + }, + done: (value, commit) => { + editor.elt.parentNode.classList.remove("boxmodel-editing"); + if (!commit) { + session.revert().then(() => { + session.destroy(); + }, e => console.error(e)); + return; + } + + let node = this.inspector.selection.nodeFront; + this.inspector.pageStyle.getLayout(node, { + autoMargins: true, + }).then(layout => { + this.store.dispatch(updateLayout(layout)); + }, e => console.error(e)); + }, + contextMenu: this.inspector.onTextBoxContextMenu, + cssProperties: getCssProperties(this.inspector.toolbox) + }, event); + }, + + /** + * Shows the box-model highlighter on the currently selected element. + * + * @param {Object} options + * Options passed to the highlighter actor. + */ + onShowBoxModelHighlighter(options = {}) { + let toolbox = this.inspector.toolbox; + let nodeFront = this.inspector.selection.nodeFront; + + toolbox.highlighterUtils.highlightNodeFront(nodeFront, options); + }, + + /** + * Handler for the inspector sidebar select event. Starts listening for + * "grid-layout-changed" if the layout panel is visible. Otherwise, stop + * listening for grid layout changes. Finally, refresh the layout view if + * it is visible. + */ + onSidebarSelect() { + if (!this.isPanelVisible()) { + this.untrackReflows(); + return; + } + + if (this.inspector.selection.isConnected() && + this.inspector.selection.isElementNode()) { + this.trackReflows(); + } + + this.updateBoxModel(); + }, + +}; + +module.exports = BoxModel; diff --git a/devtools/client/inspector/layout/components/BoxModel.js b/devtools/client/inspector/boxmodel/components/BoxModel.js similarity index 100% rename from devtools/client/inspector/layout/components/BoxModel.js rename to devtools/client/inspector/boxmodel/components/BoxModel.js diff --git a/devtools/client/inspector/boxmodel/components/BoxModelApp.js b/devtools/client/inspector/boxmodel/components/BoxModelApp.js new file mode 100644 index 000000000000..2af27ac77c2f --- /dev/null +++ b/devtools/client/inspector/boxmodel/components/BoxModelApp.js @@ -0,0 +1,51 @@ +/* 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 { addons, createClass, createFactory, PropTypes } = + require("devtools/client/shared/vendor/react"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); + +const { LocalizationHelper } = require("devtools/shared/l10n"); + +const Accordion = + createFactory(require("devtools/client/inspector/layout/components/Accordion")); +const BoxModel = createFactory(require("./BoxModel")); + +const Types = require("../types"); + +const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties"; +const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI); + +const BoxModelApp = createClass({ + + displayName: "BoxModelApp", + + propTypes: { + boxModel: PropTypes.shape(Types.boxModel).isRequired, + showBoxModelProperties: PropTypes.bool.isRequired, + onHideBoxModelHighlighter: PropTypes.func.isRequired, + onShowBoxModelEditor: PropTypes.func.isRequired, + onShowBoxModelHighlighter: PropTypes.func.isRequired, + }, + + mixins: [ addons.PureRenderMixin ], + + render() { + return Accordion({ + items: [ + { + header: BOXMODEL_L10N.getStr("boxmodel.title"), + component: BoxModel, + componentProps: this.props, + opened: true, + } + ] + }); + }, + +}); + +module.exports = connect(state => state)(BoxModelApp); diff --git a/devtools/client/inspector/layout/components/BoxModelEditable.js b/devtools/client/inspector/boxmodel/components/BoxModelEditable.js similarity index 100% rename from devtools/client/inspector/layout/components/BoxModelEditable.js rename to devtools/client/inspector/boxmodel/components/BoxModelEditable.js diff --git a/devtools/client/inspector/layout/components/BoxModelInfo.js b/devtools/client/inspector/boxmodel/components/BoxModelInfo.js similarity index 100% rename from devtools/client/inspector/layout/components/BoxModelInfo.js rename to devtools/client/inspector/boxmodel/components/BoxModelInfo.js diff --git a/devtools/client/inspector/layout/components/BoxModelMain.js b/devtools/client/inspector/boxmodel/components/BoxModelMain.js similarity index 92% rename from devtools/client/inspector/layout/components/BoxModelMain.js rename to devtools/client/inspector/boxmodel/components/BoxModelMain.js index 42baa7427453..a3980c79d104 100644 --- a/devtools/client/inspector/layout/components/BoxModelMain.js +++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js @@ -37,7 +37,23 @@ module.exports = createClass({ return layout[property] ? parseFloat(layout[property]) : "-"; }, - getHeightOrWidthValue(property) { + getHeightValue(property) { + let { layout } = this.props.boxModel; + + if (property == undefined) { + return "-"; + } + + property -= parseFloat(layout["border-top-width"]) + + parseFloat(layout["border-bottom-width"]) + + parseFloat(layout["padding-top"]) + + parseFloat(layout["padding-bottom"]); + property = parseFloat(property.toPrecision(6)); + + return property; + }, + + getWidthValue(property) { let { layout } = this.props.boxModel; if (property == undefined) { @@ -83,7 +99,7 @@ module.exports = createClass({ render() { let { boxModel, onShowBoxModelEditor } = this.props; let { layout } = boxModel; - let { width, height } = layout; + let { height, width } = layout; let borderTop = this.getBorderOrPaddingValue("border-top-width"); let borderRight = this.getBorderOrPaddingValue("border-right-width"); @@ -100,8 +116,8 @@ module.exports = createClass({ let marginBottom = this.getMarginValue("margin-bottom", "bottom"); let marginLeft = this.getMarginValue("margin-left", "left"); - width = this.getHeightOrWidthValue(width); - height = this.getHeightOrWidthValue(height); + height = this.getHeightValue(height); + width = this.getWidthValue(width); return dom.div( { diff --git a/devtools/client/inspector/layout/components/BoxModelProperties.js b/devtools/client/inspector/boxmodel/components/BoxModelProperties.js similarity index 100% rename from devtools/client/inspector/layout/components/BoxModelProperties.js rename to devtools/client/inspector/boxmodel/components/BoxModelProperties.js diff --git a/devtools/client/inspector/layout/components/ComputedProperty.js b/devtools/client/inspector/boxmodel/components/ComputedProperty.js similarity index 100% rename from devtools/client/inspector/layout/components/ComputedProperty.js rename to devtools/client/inspector/boxmodel/components/ComputedProperty.js diff --git a/devtools/client/inspector/boxmodel/components/moz.build b/devtools/client/inspector/boxmodel/components/moz.build new file mode 100644 index 000000000000..02638ae7b291 --- /dev/null +++ b/devtools/client/inspector/boxmodel/components/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'BoxModel.js', + 'BoxModelApp.js', + 'BoxModelEditable.js', + 'BoxModelInfo.js', + 'BoxModelMain.js', + 'BoxModelProperties.js', + 'ComputedProperty.js', +) diff --git a/devtools/client/inspector/boxmodel/moz.build b/devtools/client/inspector/boxmodel/moz.build new file mode 100644 index 000000000000..6ef44c80c361 --- /dev/null +++ b/devtools/client/inspector/boxmodel/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DIRS += [ + 'actions', + 'components', + 'reducers', + 'utils', +] + +DevToolsModules( + 'box-model.js', + 'types.js', +) + +BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] diff --git a/devtools/client/inspector/layout/reducers/box-model.js b/devtools/client/inspector/boxmodel/reducers/box-model.js similarity index 100% rename from devtools/client/inspector/layout/reducers/box-model.js rename to devtools/client/inspector/boxmodel/reducers/box-model.js diff --git a/devtools/client/inspector/boxmodel/reducers/moz.build b/devtools/client/inspector/boxmodel/reducers/moz.build new file mode 100644 index 000000000000..e2402990ad80 --- /dev/null +++ b/devtools/client/inspector/boxmodel/reducers/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'box-model.js', +) diff --git a/devtools/client/inspector/components/test/.eslintrc.js b/devtools/client/inspector/boxmodel/test/.eslintrc.js similarity index 100% rename from devtools/client/inspector/components/test/.eslintrc.js rename to devtools/client/inspector/boxmodel/test/.eslintrc.js diff --git a/devtools/client/inspector/components/test/browser.ini b/devtools/client/inspector/boxmodel/test/browser.ini similarity index 94% rename from devtools/client/inspector/components/test/browser.ini rename to devtools/client/inspector/boxmodel/test/browser.ini index be937ecfd3d7..c9881c903937 100644 --- a/devtools/client/inspector/components/test/browser.ini +++ b/devtools/client/inspector/boxmodel/test/browser.ini @@ -21,9 +21,11 @@ support-files = [browser_boxmodel_editablemodel_stylerules.js] [browser_boxmodel_guides.js] [browser_boxmodel_navigation.js] +skip-if = true # Bug 1336198 [browser_boxmodel_rotate-labels-on-sides.js] [browser_boxmodel_sync.js] [browser_boxmodel_tooltips.js] +skip-if = true # Bug 1336198 [browser_boxmodel_update-after-navigation.js] [browser_boxmodel_update-after-reload.js] # [browser_boxmodel_update-in-iframes.js] diff --git a/devtools/client/inspector/components/test/browser_boxmodel.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js similarity index 58% rename from devtools/client/inspector/components/test/browser_boxmodel.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel.js index d26d9061ce52..9e085cafe232 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js @@ -10,118 +10,118 @@ // Expected values: var res1 = [ { - selector: "#old-boxmodel-element-size", + selector: ".boxmodel-element-size", value: "160" + "\u00D7" + "160.117" }, { - selector: ".old-boxmodel-size > span", + selector: ".boxmodel-size > span", value: "100" + "\u00D7" + "100.117" }, { - selector: ".old-boxmodel-margin.old-boxmodel-top > span", + selector: ".boxmodel-margin.boxmodel-top > span", value: 30 }, { - selector: ".old-boxmodel-margin.old-boxmodel-left > span", + selector: ".boxmodel-margin.boxmodel-left > span", value: "auto" }, { - selector: ".old-boxmodel-margin.old-boxmodel-bottom > span", + selector: ".boxmodel-margin.boxmodel-bottom > span", value: 30 }, { - selector: ".old-boxmodel-margin.old-boxmodel-right > span", + selector: ".boxmodel-margin.boxmodel-right > span", value: "auto" }, { - selector: ".old-boxmodel-padding.old-boxmodel-top > span", + selector: ".boxmodel-padding.boxmodel-top > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-left > span", + selector: ".boxmodel-padding.boxmodel-left > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-bottom > span", + selector: ".boxmodel-padding.boxmodel-bottom > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-right > span", + selector: ".boxmodel-padding.boxmodel-right > span", value: 20 }, { - selector: ".old-boxmodel-border.old-boxmodel-top > span", + selector: ".boxmodel-border.boxmodel-top > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-left > span", + selector: ".boxmodel-border.boxmodel-left > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-bottom > span", + selector: ".boxmodel-border.boxmodel-bottom > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-right > span", + selector: ".boxmodel-border.boxmodel-right > span", value: 10 }, ]; var res2 = [ { - selector: "#old-boxmodel-element-size", + selector: ".boxmodel-element-size", value: "190" + "\u00D7" + "210" }, { - selector: ".old-boxmodel-size > span", + selector: ".boxmodel-size > span", value: "100" + "\u00D7" + "150" }, { - selector: ".old-boxmodel-margin.old-boxmodel-top > span", + selector: ".boxmodel-margin.boxmodel-top > span", value: 30 }, { - selector: ".old-boxmodel-margin.old-boxmodel-left > span", + selector: ".boxmodel-margin.boxmodel-left > span", value: "auto" }, { - selector: ".old-boxmodel-margin.old-boxmodel-bottom > span", + selector: ".boxmodel-margin.boxmodel-bottom > span", value: 30 }, { - selector: ".old-boxmodel-margin.old-boxmodel-right > span", + selector: ".boxmodel-margin.boxmodel-right > span", value: "auto" }, { - selector: ".old-boxmodel-padding.old-boxmodel-top > span", + selector: ".boxmodel-padding.boxmodel-top > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-left > span", + selector: ".boxmodel-padding.boxmodel-left > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-bottom > span", + selector: ".boxmodel-padding.boxmodel-bottom > span", value: 20 }, { - selector: ".old-boxmodel-padding.old-boxmodel-right > span", + selector: ".boxmodel-padding.boxmodel-right > span", value: 50 }, { - selector: ".old-boxmodel-border.old-boxmodel-top > span", + selector: ".boxmodel-border.boxmodel-top > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-left > span", + selector: ".boxmodel-border.boxmodel-left > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-bottom > span", + selector: ".boxmodel-border.boxmodel-bottom > span", value: 10 }, { - selector: ".old-boxmodel-border.old-boxmodel-right > span", + selector: ".boxmodel-border.boxmodel-right > span", value: 10 }, ]; @@ -142,7 +142,7 @@ add_task(function* () { function* testInitialValues(inspector, view) { info("Test that the initial values of the box model are correct"); - let viewdoc = view.doc; + let viewdoc = view.document; for (let i = 0; i < res1.length; i++) { let elt = viewdoc.querySelector(res1[i].selector); @@ -153,7 +153,7 @@ function* testInitialValues(inspector, view) { function* testChangingValues(inspector, view, testActor) { info("Test that changing the document updates the box model"); - let viewdoc = view.doc; + let viewdoc = view.document; let onUpdated = waitForUpdate(inspector); yield testActor.setAttribute("div", "style", diff --git a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js similarity index 74% rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js index 5bb882dd6e7b..37736152011b 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js @@ -36,21 +36,21 @@ function* testEditingMargins(inspector, view, testActor) { "Should be no margin-top on the element."); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span"); is(span.textContent, 5, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "5px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("3", {}, view.doc.defaultView); + EventUtils.synthesizeKey("3", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "margin-top")), "3px", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "margin-top")), "", @@ -66,35 +66,35 @@ function* testKeyBindings(inspector, view, testActor) { "Should be no margin-top on the element."); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-left > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span"); is(span.textContent, 10, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "10px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_UP", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "11px", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "margin-left")), "11px", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_DOWN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_DOWN", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "10px", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "margin-left")), "10px", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.doc.defaultView); + EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "20px", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "margin-left")), "20px", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div1", "margin-left")), "20px", "Should be the right margin-top on the element."); @@ -109,22 +109,22 @@ function* testEscapeToUndo(inspector, view, testActor) { "Should be the right margin-top on the element."); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-left > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span"); is(span.textContent, 20, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "20px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "margin-left")), "", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "margin-left")), "20px", @@ -140,22 +140,22 @@ function* testDeletingValue(inspector, view, testActor) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-right > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-right > span"); is(span.textContent, 15, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "15px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "margin-right")), "", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div1", "margin-right")), "", "Should be the right margin-top on the element."); @@ -167,26 +167,26 @@ function* testRefocusingOnClick(inspector, view, testActor) { yield selectNode("#div4", inspector); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span"); is(span.textContent, 1, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); info("Click in the already opened editor input"); - EventUtils.synthesizeMouseAtCenter(editor, {}, view.doc.defaultView); - is(editor, view.doc.activeElement, + EventUtils.synthesizeMouseAtCenter(editor, {}, view.document.defaultView); + is(editor, view.document.activeElement, "Inplace editor input should still have focus."); info("Check the input can still be used as expected"); - EventUtils.synthesizeKey("VK_UP", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "2px", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div4", "margin-top")), "2px", "Should have updated the margin."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div4", "margin-top")), "2px", "Should be the right margin-top on the element."); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js similarity index 74% rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js index ca5194266319..ab49a191429c 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js @@ -32,22 +32,22 @@ function* testEditing(inspector, view, testActor) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-bottom > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span"); is(span.textContent, 5, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "5px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("7", {}, view.doc.defaultView); + EventUtils.synthesizeKey("7", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "7", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px", "Should have updated the padding"); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px", "Should be the right padding."); @@ -63,22 +63,22 @@ function* testEditingAndCanceling(inspector, view, testActor) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "5px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("8", {}, view.doc.defaultView); + EventUtils.synthesizeKey("8", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "8", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "padding-left")), "8px", "Should have updated the padding"); - EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "padding-left")), "5px", @@ -91,22 +91,22 @@ function* testDeleting(inspector, view, testActor) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "5px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "padding-left")), "", "Should have updated the padding"); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div1", "padding-left")), "", "Should be the right padding."); @@ -122,22 +122,22 @@ function* testDeletingAndCanceling(inspector, view, testActor) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "5px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "padding-left")), "", "Should have updated the padding"); - EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "padding-left")), "5px", diff --git a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js similarity index 69% rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js index f56f584ddf95..c251dfbc162f 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js @@ -30,45 +30,45 @@ add_task(function* () { function* testClickingOutsideEditor(view) { info("Test that clicking outside the editor blurs it"); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span"); is(span.textContent, 10, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); info("Click next to the opened editor input."); let onBlur = once(editor, "blur"); let rect = editor.getBoundingClientRect(); EventUtils.synthesizeMouse(editor, rect.width + 10, rect.height / 2, {}, - view.doc.defaultView); + view.document.defaultView); yield onBlur; - is(view.doc.querySelector(".styleinspector-propertyeditor"), null, + is(view.document.querySelector(".styleinspector-propertyeditor"), null, "Inplace editor has been removed."); } function* testClickingBelowContainer(view) { info("Test that clicking below the box-model container blurs it"); - let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span"); is(span.textContent, 10, "Should have the right value in the box model."); - info("Test that clicking below the old-boxmodel-container blurs the opened editor"); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + info("Test that clicking below the boxmodel-container blurs the opened editor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); let onBlur = once(editor, "blur"); - let container = view.doc.querySelector("#old-boxmodel-container"); + let container = view.document.querySelector(".boxmodel-container"); // Using getBoxQuads here because getBoundingClientRect (and therefore synthesizeMouse) // use an erroneous height of ~50px for the boxmodel-container. let bounds = container.getBoxQuads({relativeTo: view.doc})[0].bounds; EventUtils.synthesizeMouseAtPoint( bounds.left + 10, bounds.top + bounds.height + 10, - {}, view.doc.defaultView); + {}, view.document.defaultView); yield onBlur; - is(view.doc.querySelector(".styleinspector-propertyeditor"), null, + is(view.document.querySelector(".styleinspector-propertyeditor"), null, "Inplace editor has been removed."); } diff --git a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js similarity index 82% rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js index 5550a66423a4..197ef99d3b47 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js @@ -24,15 +24,15 @@ add_task(function* () { "Should have the right border"); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-border.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-border.boxmodel-top > span"); is(span.textContent, 0, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "0", "Should have the right value in the editor."); - EventUtils.synthesizeKey("1", {}, view.doc.defaultView); + EventUtils.synthesizeKey("1", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "1", "Should have the right value in the editor."); @@ -41,7 +41,7 @@ add_task(function* () { is((yield getStyle(testActor, "#div1", "border-top-style")), "solid", "Should have the right border"); - EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "border-top-width")), "", diff --git a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js similarity index 74% rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js index f3f41e4d04ed..28ba89898062 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js @@ -31,30 +31,30 @@ function* testUnits(inspector, view, testActor) { "Should have the right padding"); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-top > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-top > span"); is(span.textContent, 3, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "3px", "Should have the right value in the editor."); - EventUtils.synthesizeKey("1", {}, view.doc.defaultView); + EventUtils.synthesizeKey("1", {}, view.document.defaultView); yield waitForUpdate(inspector); - EventUtils.synthesizeKey("e", {}, view.doc.defaultView); + EventUtils.synthesizeKey("e", {}, view.document.defaultView); yield waitForUpdate(inspector); is((yield getStyle(testActor, "#div1", "padding-top")), "", "An invalid value is handled cleanly"); - EventUtils.synthesizeKey("m", {}, view.doc.defaultView); + EventUtils.synthesizeKey("m", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "1em", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div1", "padding-top")), "1em", "Should have updated the padding."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div1", "padding-top")), "1em", "Should be the right padding."); @@ -68,22 +68,22 @@ function* testValueComesFromStyleRule(inspector, view, testActor) { "Should have the right border-bottom-width"); yield selectNode("#div2", inspector); - let span = view.doc.querySelector(".old-boxmodel-border.old-boxmodel-bottom > span"); + let span = view.document.querySelector(".boxmodel-border.boxmodel-bottom > span"); is(span.textContent, 16, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "1em", "Should have the right value in the editor."); - EventUtils.synthesizeKey("0", {}, view.doc.defaultView); + EventUtils.synthesizeKey("0", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "0", "Should have the right value in the editor."); is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px", "Should have updated the border."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px", "Should be the right border-bottom-width."); @@ -97,15 +97,15 @@ function* testShorthandsAreParsed(inspector, view, testActor) { "Should have the right padding"); yield selectNode("#div3", inspector); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-right > span"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-right > span"); is(span.textContent, 32, "Should have the right value in the box model."); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); ok(editor, "Should have opened the editor."); is(editor.value, "2em", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); is((yield getStyle(testActor, "#div3", "padding-right")), "", "Should be the right padding."); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_guides.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js similarity index 89% rename from devtools/client/inspector/components/test/browser_boxmodel_guides.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js index 9dcaaa609cce..51457e25cd46 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_guides.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js @@ -28,16 +28,16 @@ add_task(function* () { highlighterOptions = options; }; - let elt = view.doc.getElementById("old-boxmodel-margins"); + let elt = view.document.querySelector(".boxmodel-margins"); yield testGuideOnLayoutHover(elt, "margin", inspector, view); - elt = view.doc.getElementById("old-boxmodel-borders"); + elt = view.document.querySelector(".boxmodel-borders"); yield testGuideOnLayoutHover(elt, "border", inspector, view); - elt = view.doc.getElementById("old-boxmodel-padding"); + elt = view.document.querySelector(".boxmodel-paddings"); yield testGuideOnLayoutHover(elt, "padding", inspector, view); - elt = view.doc.getElementById("old-boxmodel-content"); + elt = view.document.querySelector(".boxmodel-content"); yield testGuideOnLayoutHover(elt, "content", inspector, view); }); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_navigation.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js similarity index 75% rename from devtools/client/inspector/components/test/browser_boxmodel_navigation.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js index 6b8439710227..deee191fd2aa 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_navigation.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js @@ -29,50 +29,50 @@ add_task(function* () { function* testInitialFocus(inspector, view) { info("Test that the focus is on margin layout."); let viewdoc = view.doc; - let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper"); + let boxmodel = viewdoc.getElementById("boxmodel-wrapper"); boxmodel.focus(); EventUtils.synthesizeKey("VK_RETURN", {}); - is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-margins", + is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins", "Should be set to the margin layout."); } function* testChangingLevels(inspector, view) { info("Test that using arrow keys updates level."); let viewdoc = view.doc; - let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper"); + let boxmodel = viewdoc.getElementById("boxmodel-wrapper"); boxmodel.focus(); EventUtils.synthesizeKey("VK_RETURN", {}); EventUtils.synthesizeKey("VK_ESCAPE", {}); EventUtils.synthesizeKey("VK_DOWN", {}); - is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-borders", + is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders", "Should be set to the border layout."); EventUtils.synthesizeKey("VK_DOWN", {}); - is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-padding", + is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-padding", "Should be set to the padding layout."); EventUtils.synthesizeKey("VK_UP", {}); - is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-borders", + is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders", "Should be set to the border layout."); EventUtils.synthesizeKey("VK_UP", {}); - is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-margins", + is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins", "Should be set to the margin layout."); } function* testTabbingWrapAround(inspector, view) { info("Test that using arrow keys updates level."); let viewdoc = view.doc; - let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper"); + let boxmodel = viewdoc.getElementById("boxmodel-wrapper"); boxmodel.focus(); EventUtils.synthesizeKey("VK_RETURN", {}); let editLevel = boxmodel.getAttribute("aria-activedescendant"); let dataLevel = viewdoc.getElementById(editLevel).getAttribute("data-box"); let editBoxes = [...viewdoc.querySelectorAll( - `[data-box="${dataLevel}"].old-boxmodel-editable`)]; + `[data-box="${dataLevel}"].boxmodel-editable`)]; EventUtils.synthesizeKey("VK_ESCAPE", {}); editBoxes[3].focus(); @@ -87,12 +87,12 @@ function* testTabbingWrapAround(inspector, view) { function* testChangingLevelsByClicking(inspector, view) { info("Test that clicking on levels updates level."); let viewdoc = view.doc; - let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper"); + let boxmodel = viewdoc.getElementById("boxmodel-wrapper"); boxmodel.focus(); - let marginLayout = viewdoc.getElementById("old-boxmodel-margins"); - let borderLayout = viewdoc.getElementById("old-boxmodel-borders"); - let paddingLayout = viewdoc.getElementById("old-boxmodel-padding"); + let marginLayout = viewdoc.getElementById("boxmodel-margins"); + let borderLayout = viewdoc.getElementById("boxmodel-borders"); + let paddingLayout = viewdoc.getElementById("boxmodel-padding"); let layouts = [paddingLayout, borderLayout, marginLayout]; layouts.forEach(layout => { diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js new file mode 100644 index 000000000000..e931830b534f --- /dev/null +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js @@ -0,0 +1,49 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that longer values are rotated on the side + +const res1 = [ + {selector: ".boxmodel-margin.boxmodel-top > span", value: 30}, + {selector: ".boxmodel-margin.boxmodel-left > span", value: "auto"}, + {selector: ".boxmodel-margin.boxmodel-bottom > span", value: 30}, + {selector: ".boxmodel-margin.boxmodel-right > span", value: "auto"}, + {selector: ".boxmodel-padding.boxmodel-top > span", value: 20}, + {selector: ".boxmodel-padding.boxmodel-left > span", value: 2000000}, + {selector: ".boxmodel-padding.boxmodel-bottom > span", value: 20}, + {selector: ".boxmodel-padding.boxmodel-right > span", value: 20}, + {selector: ".boxmodel-border.boxmodel-top > span", value: 10}, + {selector: ".boxmodel-border.boxmodel-left > span", value: 10}, + {selector: ".boxmodel-border.boxmodel-bottom > span", value: 10}, + {selector: ".boxmodel-border.boxmodel-right > span", value: 10}, +]; + +const TEST_URI = encodeURIComponent([ + "", + "
" +].join("")); +const LONG_TEXT_ROTATE_LIMIT = 3; + +add_task(function* () { + yield addTab("data:text/html," + TEST_URI); + let {inspector, view} = yield openBoxModelView(); + yield selectNode("div", inspector); + + for (let i = 0; i < res1.length; i++) { + let elt = view.document.querySelector(res1[i].selector); + let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT; + let classList = elt.parentNode.classList; + let canBeRotated = classList.contains("boxmodel-left") || + classList.contains("boxmodel-right"); + let isRotated = classList.contains("boxmodel-rotate"); + + is(canBeRotated && isLong, + isRotated, res1[i].selector + " correctly rotated."); + } +}); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_sync.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js similarity index 77% rename from devtools/client/inspector/components/test/browser_boxmodel_sync.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js index c0fde5c80b86..382675ad2d2d 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_sync.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js @@ -17,14 +17,14 @@ add_task(function* () { yield selectNode("p", inspector); info("Modify padding-bottom in box model view"); - let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-bottom > span"); - EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); - let editor = view.doc.querySelector(".styleinspector-propertyeditor"); + let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span"); + EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView); + let editor = view.document.querySelector(".styleinspector-propertyeditor"); - EventUtils.synthesizeKey("7", {}, view.doc.defaultView); + EventUtils.synthesizeKey("7", {}, view.document.defaultView); yield waitForUpdate(inspector); is(editor.value, "7", "Should have the right value in the editor."); - EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView); + EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView); let onRuleViewRefreshed = once(inspector, "rule-view-refreshed"); let onRuleViewSelected = once(inspector.sidebar, "ruleview-selected"); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_tooltips.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js similarity index 83% rename from devtools/client/inspector/components/test/browser_boxmodel_tooltips.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js index cdbbd6885bb4..6f5997635fe8 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_tooltips.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js @@ -76,24 +76,24 @@ add_task(function* () { info("Checking the regions tooltips"); - ok(view.doc.querySelector("#old-boxmodel-margins").hasAttribute("title"), + ok(view.document.querySelector(".boxmodel-margins").hasAttribute("title"), "The margin region has a tooltip"); - is(view.doc.querySelector("#old-boxmodel-margins").getAttribute("title"), "margin", + is(view.document.querySelector(".boxmodel-margins").getAttribute("title"), "margin", "The margin region has the correct tooltip content"); - ok(view.doc.querySelector("#old-boxmodel-borders").hasAttribute("title"), + ok(view.document.querySelector(".boxmodel-borders").hasAttribute("title"), "The border region has a tooltip"); - is(view.doc.querySelector("#old-boxmodel-borders").getAttribute("title"), "border", + is(view.document.querySelector(".boxmodel-borders").getAttribute("title"), "border", "The border region has the correct tooltip content"); - ok(view.doc.querySelector("#old-boxmodel-padding").hasAttribute("title"), + ok(view.document.querySelector(".boxmodel-paddings").hasAttribute("title"), "The padding region has a tooltip"); - is(view.doc.querySelector("#old-boxmodel-padding").getAttribute("title"), "padding", + is(view.document.querySelector(".boxmodel-paddings").getAttribute("title"), "padding", "The padding region has the correct tooltip content"); - ok(view.doc.querySelector("#old-boxmodel-content").hasAttribute("title"), + ok(view.document.querySelector(".boxmodel-content").hasAttribute("title"), "The content region has a tooltip"); - is(view.doc.querySelector("#old-boxmodel-content").getAttribute("title"), "content", + is(view.document.querySelector(".boxmodel-content").getAttribute("title"), "content", "The content region has the correct tooltip content"); for (let {selector, values} of VALUES_TEST_DATA) { @@ -108,7 +108,7 @@ add_task(function* () { let name = view.map[key].property; let expectedTooltipData = values.find(o => o.name === name); - let el = view.doc.querySelector(view.map[key].selector); + let el = view.document.querySelector(view.map[key].selector); ok(el.hasAttribute("title"), "The " + name + " value has a tooltip"); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js similarity index 86% rename from devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js index 62918310ceea..dfcf8ceefe5a 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js @@ -17,14 +17,16 @@ add_task(function* () { yield testFirstPage(inspector, view, testActor); info("Navigate to the second page"); + let onMarkupLoaded = waitForMarkupLoaded(inspector); yield testActor.eval(`content.location.href="${IFRAME2}"`); - yield inspector.once("markuploaded"); + yield onMarkupLoaded; yield testSecondPage(inspector, view, testActor); info("Go back to the first page"); + onMarkupLoaded = waitForMarkupLoaded(inspector); yield testActor.eval("content.history.back();"); - yield inspector.once("markuploaded"); + yield onMarkupLoaded; yield testBackToFirstPage(inspector, view, testActor); }); @@ -32,12 +34,11 @@ add_task(function* () { function* testFirstPage(inspector, view, testActor) { info("Test that the box model view works on the first page"); - info("Selecting the test node"); yield selectNode("p", inspector); info("Checking that the box model view shows the right value"); - let paddingElt = view.doc.querySelector( - ".old-boxmodel-padding.old-boxmodel-top > span"); + let paddingElt = view.document.querySelector( + ".boxmodel-padding.boxmodel-top > span"); is(paddingElt.textContent, "50"); info("Listening for box model view changes and modifying the padding"); @@ -53,11 +54,10 @@ function* testFirstPage(inspector, view, testActor) { function* testSecondPage(inspector, view, testActor) { info("Test that the box model view works on the second page"); - info("Selecting the test node"); yield selectNode("p", inspector); info("Checking that the box model view shows the right value"); - let sizeElt = view.doc.querySelector(".old-boxmodel-size > span"); + let sizeElt = view.document.querySelector(".boxmodel-size > span"); is(sizeElt.textContent, "100" + "\u00D7" + "100"); info("Listening for box model view changes and modifying the size"); @@ -73,13 +73,12 @@ function* testSecondPage(inspector, view, testActor) { function* testBackToFirstPage(inspector, view, testActor) { info("Test that the box model view works on the first page after going back"); - info("Selecting the test node"); yield selectNode("p", inspector); info("Checking that the box model view shows the right value, which is the" + "modified value from step one because of the bfcache"); - let paddingElt = view.doc.querySelector( - ".old-boxmodel-padding.old-boxmodel-top > span"); + let paddingElt = view.document.querySelector( + ".boxmodel-padding.boxmodel-top > span"); is(paddingElt.textContent, "20"); info("Listening for box model view changes and modifying the padding"); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js similarity index 87% rename from devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js index 111031e115d2..f1ed222949a4 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js @@ -14,20 +14,20 @@ add_task(function* () { yield assertBoxModelView(inspector, view, testActor); info("Reload the page"); + let onMarkupLoaded = waitForMarkupLoaded(inspector); yield testActor.reload(); - yield inspector.once("markuploaded"); + yield onMarkupLoaded; info("Test that the box model view works on the reloaded page"); yield assertBoxModelView(inspector, view, testActor); }); function* assertBoxModelView(inspector, view, testActor) { - info("Selecting the test node"); yield selectNode("p", inspector); info("Checking that the box model view shows the right value"); - let paddingElt = view.doc.querySelector( - ".old-boxmodel-padding.old-boxmodel-top > span"); + let paddingElt = view.document.querySelector( + ".boxmodel-padding.boxmodel-top > span"); is(paddingElt.textContent, "50"); info("Listening for box model view changes and modifying the padding"); diff --git a/devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js similarity index 93% rename from devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js index 9aedb1b581ac..6aa10e441ae9 100644 --- a/devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js +++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js @@ -22,7 +22,7 @@ function* testResizingInIframe(inspector, view, testActor) { yield selectNodeInIframe2("div", inspector); info("Checking that the box model view shows the right value"); - let sizeElt = view.doc.querySelector(".old-boxmodel-size > span"); + let sizeElt = view.document.querySelector(".boxmodel-size > span"); is(sizeElt.textContent, "400\u00D7200"); info("Listening for box model view changes and modifying its size"); @@ -40,14 +40,15 @@ function* testReflowsAfterIframeDeletion(inspector, view, testActor) { "iframe"); info("Deleting the iframe2"); + let onInspectorUpdated = inspector.once("inspector-updated"); yield removeIframe2(testActor); - yield inspector.once("inspector-updated"); + yield onInspectorUpdated; info("Selecting the test node in iframe1"); yield selectNodeInIframe1("p", inspector); info("Checking that the box model view shows the right value"); - let sizeElt = view.doc.querySelector(".old-boxmodel-size > span"); + let sizeElt = view.document.querySelector(".boxmodel-size > span"); is(sizeElt.textContent, "100\u00D7100"); info("Listening for box model view changes and modifying its size"); diff --git a/devtools/client/inspector/components/test/doc_boxmodel_iframe1.html b/devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe1.html similarity index 100% rename from devtools/client/inspector/components/test/doc_boxmodel_iframe1.html rename to devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe1.html diff --git a/devtools/client/inspector/components/test/doc_boxmodel_iframe2.html b/devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe2.html similarity index 100% rename from devtools/client/inspector/components/test/doc_boxmodel_iframe2.html rename to devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe2.html diff --git a/devtools/client/inspector/components/test/head.js b/devtools/client/inspector/boxmodel/test/head.js similarity index 55% rename from devtools/client/inspector/components/test/head.js rename to devtools/client/inspector/boxmodel/test/head.js index fa86b5e9e179..b842968a5595 100644 --- a/devtools/client/inspector/components/test/head.js +++ b/devtools/client/inspector/boxmodel/test/head.js @@ -19,12 +19,13 @@ registerCleanupFunction(() => { /** * Highlight a node and set the inspector's current selection to the node or * the first match of the given css selector. - * @param {String|NodeFront} selectorOrNodeFront - * The selector for the node to be set, or the nodeFront - * @param {InspectorPanel} inspector - * The instance of InspectorPanel currently loaded in the toolbox - * @return a promise that resolves when the inspector is updated with the new - * node + * + * @param {String|NodeFront} selectorOrNodeFront + * The selector for the node to be set, or the nodeFront. + * @param {InspectorPanel} inspector + * The instance of InspectorPanel currently loaded in the toolbox. + * @return {Promise} a promise that resolves when the inspector is updated with the new + * node. */ function* selectAndHighlightNode(selectorOrNodeFront, inspector) { info("Highlighting and selecting the node " + selectorOrNodeFront); @@ -38,8 +39,9 @@ function* selectAndHighlightNode(selectorOrNodeFront, inspector) { /** * Open the toolbox, with the inspector tool visible, and the computed view * sidebar tab selected to display the box model view. - * @return a promise that resolves when the inspector is ready and the box model - * view is visible and ready + * + * @return {Promise} a promise that resolves when the inspector is ready and the box model + * view is visible and ready. */ function openBoxModelView() { return openInspectorSidebarTab("computedview").then(data => { @@ -58,7 +60,7 @@ function openBoxModelView() { return { toolbox: data.toolbox, inspector: data.inspector, - view: data.inspector.computedview.boxModelView, + view: data.inspector.computedview, testActor: data.testActor }; }); @@ -66,10 +68,37 @@ function openBoxModelView() { /** * Wait for the boxmodel-view-updated event. - * @return a promise + * + * @param {InspectorPanel} inspector + * The instance of InspectorPanel currently loaded in the toolbox. + * @param {Boolean} waitForSelectionUpdate + * Should the boxmodel-view-updated event come from a new selection. + * @return {Promise} a promise */ -function waitForUpdate(inspector) { - return inspector.once("boxmodel-view-updated"); +function waitForUpdate(inspector, waitForSelectionUpdate) { + return new Promise(resolve => { + inspector.on("boxmodel-view-updated", function onUpdate(e, reasons) { + // Wait for another update event if we are waiting for a selection related event. + if (waitForSelectionUpdate && !reasons.includes("new-selection")) { + return; + } + + inspector.off("boxmodel-view-updated", onUpdate); + resolve(); + }); + }); +} + +/** + * Wait for both boxmode-view-updated and markuploaded events. + * + * @return {Promise} a promise that resolves when both events have been received. + */ +function waitForMarkupLoaded(inspector) { + return Promise.all([ + waitForUpdate(inspector), + inspector.once("markuploaded"), + ]); } function getStyle(testActor, selector, propertyName) { @@ -85,3 +114,15 @@ function setStyle(testActor, selector, propertyName, value) { .style.${propertyName} = "${value}"; `); } + +/** + * The box model doesn't participate in the inspector's update mechanism, so simply + * calling the default selectNode isn't enough to guarantee that the box model view has + * finished updating. We also need to wait for the "boxmodel-view-updated" event. + */ +var _selectNode = selectNode; +selectNode = function* (node, inspector, reason) { + let onUpdate = waitForUpdate(inspector, true); + yield _selectNode(node, inspector, reason); + yield onUpdate; +}; diff --git a/devtools/client/inspector/boxmodel/types.js b/devtools/client/inspector/boxmodel/types.js new file mode 100644 index 000000000000..aba8f7893d7e --- /dev/null +++ b/devtools/client/inspector/boxmodel/types.js @@ -0,0 +1,17 @@ +/* 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 { PropTypes } = require("devtools/client/shared/vendor/react"); + +/** + * The box model data for the current selected node. + */ +exports.boxModel = { + + // The layout information of the current selected node + layout: PropTypes.object, + +}; diff --git a/devtools/client/inspector/layout/utils/editing-session.js b/devtools/client/inspector/boxmodel/utils/editing-session.js similarity index 100% rename from devtools/client/inspector/layout/utils/editing-session.js rename to devtools/client/inspector/boxmodel/utils/editing-session.js diff --git a/devtools/client/inspector/boxmodel/utils/moz.build b/devtools/client/inspector/boxmodel/utils/moz.build new file mode 100644 index 000000000000..4ad6c32d9ef5 --- /dev/null +++ b/devtools/client/inspector/boxmodel/utils/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'editing-session.js', +) diff --git a/devtools/client/inspector/components/deprecated-box-model.js b/devtools/client/inspector/components/deprecated-box-model.js deleted file mode 100644 index 874a79939c97..000000000000 --- a/devtools/client/inspector/components/deprecated-box-model.js +++ /dev/null @@ -1,912 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=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/. */ - -/** - * THIS MODULE IS DEPRECATED. - * - * To continue any work related to the box model view, see the new react/redux - * implementation in devtools/client/inspector/layout/. - */ - -"use strict"; - -const {Task} = require("devtools/shared/task"); -const {InplaceEditor, editableItem} = - require("devtools/client/shared/inplace-editor"); -const {ReflowFront} = require("devtools/shared/fronts/reflow"); -const {LocalizationHelper} = require("devtools/shared/l10n"); -const {getCssProperties} = require("devtools/shared/fronts/css-properties"); -const {KeyCodes} = require("devtools/client/shared/keycodes"); -const EditingSession = require("devtools/client/inspector/layout/utils/editing-session"); - -const STRINGS_URI = "devtools/client/locales/shared.properties"; -const STRINGS_INSPECTOR = "devtools/shared/locales/styleinspector.properties"; -const SHARED_L10N = new LocalizationHelper(STRINGS_URI); -const INSPECTOR_L10N = new LocalizationHelper(STRINGS_INSPECTOR); -const NUMERIC = /^-?[\d\.]+$/; -const LONG_TEXT_ROTATE_LIMIT = 3; - -/** - * The box model view - * @param {InspectorPanel} inspector - * An instance of the inspector-panel currently loaded in the toolbox - * @param {Document} document - * The document that will contain the box model view. - */ -function BoxModelView(inspector, document) { - this.inspector = inspector; - this.doc = document; - this.wrapper = this.doc.getElementById("old-boxmodel-wrapper"); - this.container = this.doc.getElementById("old-boxmodel-container"); - this.expander = this.doc.getElementById("old-boxmodel-expander"); - this.sizeLabel = this.doc.querySelector(".old-boxmodel-size > span"); - this.sizeHeadingLabel = this.doc.getElementById("old-boxmodel-element-size"); - this._geometryEditorHighlighter = null; - this._cssProperties = getCssProperties(inspector.toolbox); - - this.init(); -} - -BoxModelView.prototype = { - init: function () { - this.update = this.update.bind(this); - - this.onNewSelection = this.onNewSelection.bind(this); - this.inspector.selection.on("new-node-front", this.onNewSelection); - - this.onNewNode = this.onNewNode.bind(this); - this.inspector.sidebar.on("computedview-selected", this.onNewNode); - - this.onSidebarSelect = this.onSidebarSelect.bind(this); - this.inspector.sidebar.on("select", this.onSidebarSelect); - - this.onToggleExpander = this.onToggleExpander.bind(this); - this.expander.addEventListener("click", this.onToggleExpander); - let header = this.doc.getElementById("old-boxmodel-header"); - header.addEventListener("dblclick", this.onToggleExpander); - - this.onFilterComputedView = this.onFilterComputedView.bind(this); - this.inspector.on("computed-view-filtered", - this.onFilterComputedView); - - this.onPickerStarted = this.onPickerStarted.bind(this); - this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this); - this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this); - this.onWillNavigate = this.onWillNavigate.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - this.onLevelClick = this.onLevelClick.bind(this); - this.setAriaActive = this.setAriaActive.bind(this); - this.getEditBoxes = this.getEditBoxes.bind(this); - this.makeFocusable = this.makeFocusable.bind(this); - this.makeUnfocasable = this.makeUnfocasable.bind(this); - this.moveFocus = this.moveFocus.bind(this); - this.onFocus = this.onFocus.bind(this); - - this.borderLayout = this.doc.getElementById("old-boxmodel-borders"); - this.boxModel = this.doc.getElementById("old-boxmodel-wrapper"); - this.marginLayout = this.doc.getElementById("old-boxmodel-margins"); - this.paddingLayout = this.doc.getElementById("old-boxmodel-padding"); - - this.layouts = { - "margin": new Map([ - [KeyCodes.DOM_VK_ESCAPE, this.marginLayout], - [KeyCodes.DOM_VK_DOWN, this.borderLayout], - [KeyCodes.DOM_VK_UP, null], - ["click", this.marginLayout] - ]), - "border": new Map([ - [KeyCodes.DOM_VK_ESCAPE, this.borderLayout], - [KeyCodes.DOM_VK_DOWN, this.paddingLayout], - [KeyCodes.DOM_VK_UP, this.marginLayout], - ["click", this.borderLayout] - ]), - "padding": new Map([ - [KeyCodes.DOM_VK_ESCAPE, this.paddingLayout], - [KeyCodes.DOM_VK_DOWN, null], - [KeyCodes.DOM_VK_UP, this.borderLayout], - ["click", this.paddingLayout] - ]) - }; - - this.boxModel.addEventListener("click", this.onLevelClick, true); - this.boxModel.addEventListener("focus", this.onFocus, true); - this.boxModel.addEventListener("keydown", this.onKeyDown, true); - - this.initBoxModelHighlighter(); - - // Store for the different dimensions of the node. - // 'selector' refers to the element that holds the value; - // 'property' is what we are measuring; - // 'value' is the computed dimension, computed in update(). - this.map = { - position: { - selector: "#old-boxmodel-element-position", - property: "position", - value: undefined - }, - marginTop: { - selector: ".old-boxmodel-margin.old-boxmodel-top > span", - property: "margin-top", - value: undefined - }, - marginBottom: { - selector: ".old-boxmodel-margin.old-boxmodel-bottom > span", - property: "margin-bottom", - value: undefined - }, - marginLeft: { - selector: ".old-boxmodel-margin.old-boxmodel-left > span", - property: "margin-left", - value: undefined - }, - marginRight: { - selector: ".old-boxmodel-margin.old-boxmodel-right > span", - property: "margin-right", - value: undefined - }, - paddingTop: { - selector: ".old-boxmodel-padding.old-boxmodel-top > span", - property: "padding-top", - value: undefined - }, - paddingBottom: { - selector: ".old-boxmodel-padding.old-boxmodel-bottom > span", - property: "padding-bottom", - value: undefined - }, - paddingLeft: { - selector: ".old-boxmodel-padding.old-boxmodel-left > span", - property: "padding-left", - value: undefined - }, - paddingRight: { - selector: ".old-boxmodel-padding.old-boxmodel-right > span", - property: "padding-right", - value: undefined - }, - borderTop: { - selector: ".old-boxmodel-border.old-boxmodel-top > span", - property: "border-top-width", - value: undefined - }, - borderBottom: { - selector: ".old-boxmodel-border.old-boxmodel-bottom > span", - property: "border-bottom-width", - value: undefined - }, - borderLeft: { - selector: ".old-boxmodel-border.old-boxmodel-left > span", - property: "border-left-width", - value: undefined - }, - borderRight: { - selector: ".old-boxmodel-border.old-boxmodel-right > span", - property: "border-right-width", - value: undefined - } - }; - - // Make each element the dimensions editable - for (let i in this.map) { - if (i == "position") { - continue; - } - - let dimension = this.map[i]; - editableItem({ - element: this.doc.querySelector(dimension.selector) - }, (element, event) => { - this.initEditor(element, event, dimension); - }); - } - - this.onNewNode(); - - let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor"); - this.onGeometryButtonClick = this.onGeometryButtonClick.bind(this); - nodeGeometry.addEventListener("click", this.onGeometryButtonClick); - }, - - initBoxModelHighlighter: function () { - let highlightElts = this.doc.querySelectorAll("#old-boxmodel-container *[title]"); - this.onHighlightMouseOver = this.onHighlightMouseOver.bind(this); - this.onHighlightMouseOut = this.onHighlightMouseOut.bind(this); - - for (let element of highlightElts) { - element.addEventListener("mouseover", this.onHighlightMouseOver, true); - element.addEventListener("mouseout", this.onHighlightMouseOut, true); - } - }, - - /** - * Start listening to reflows in the current tab. - */ - trackReflows: function () { - if (!this.reflowFront) { - let { target } = this.inspector; - if (target.form.reflowActor) { - this.reflowFront = ReflowFront(target.client, - target.form); - } else { - return; - } - } - - this.reflowFront.on("reflows", this.update); - this.reflowFront.start(); - }, - - /** - * Stop listening to reflows in the current tab. - */ - untrackReflows: function () { - if (!this.reflowFront) { - return; - } - - this.reflowFront.off("reflows", this.update); - this.reflowFront.stop(); - }, - - /** - * Called when the user clicks on one of the editable values in the box model view - */ - initEditor: function (element, event, dimension) { - let { property } = dimension; - let session = new EditingSession(this); - let initialValue = session.getProperty(property); - - let editor = new InplaceEditor({ - element: element, - initial: initialValue, - contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, - property: { - name: dimension.property - }, - start: self => { - self.elt.parentNode.classList.add("old-boxmodel-editing"); - }, - change: value => { - if (NUMERIC.test(value)) { - value += "px"; - } - - let properties = [ - { name: property, value: value } - ]; - - if (property.substring(0, 7) == "border-") { - let bprop = property.substring(0, property.length - 5) + "style"; - let style = session.getProperty(bprop); - if (!style || style == "none" || style == "hidden") { - properties.push({ name: bprop, value: "solid" }); - } - } - - session.setProperties(properties).catch(e => console.error(e)); - }, - done: (value, commit) => { - editor.elt.parentNode.classList.remove("old-boxmodel-editing"); - if (!commit) { - session.revert().then(() => { - session.destroy(); - }, e => console.error(e)); - } - }, - contextMenu: this.inspector.onTextBoxContextMenu, - cssProperties: this._cssProperties - }, event); - }, - - /** - * Is the BoxModelView visible in the sidebar. - * @return {Boolean} - */ - isViewVisible: function () { - return this.inspector && - this.inspector.sidebar.getCurrentTabID() == "computedview"; - }, - - /** - * Is the BoxModelView visible in the sidebar and is the current node valid to - * be displayed in the view. - * @return {Boolean} - */ - isViewVisibleAndNodeValid: function () { - return this.isViewVisible() && - this.inspector.selection.isConnected() && - this.inspector.selection.isElementNode(); - }, - - /** - * Destroy the nodes. Remove listeners. - */ - destroy: function () { - let highlightElts = this.doc.querySelectorAll("#old-boxmodel-container *[title]"); - - for (let element of highlightElts) { - element.removeEventListener("mouseover", this.onHighlightMouseOver, true); - element.removeEventListener("mouseout", this.onHighlightMouseOut, true); - } - - this.expander.removeEventListener("click", this.onToggleExpander); - let header = this.doc.getElementById("old-boxmodel-header"); - header.removeEventListener("dblclick", this.onToggleExpander); - - let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor"); - nodeGeometry.removeEventListener("click", this.onGeometryButtonClick); - - this.boxModel.removeEventListener("click", this.onLevelClick, true); - this.boxModel.removeEventListener("focus", this.onFocus, true); - this.boxModel.removeEventListener("keydown", this.onKeyDown, true); - - this.inspector.off("picker-started", this.onPickerStarted); - - // Inspector Panel will destroy `markup` object on "will-navigate" event, - // therefore we have to check if it's still available in case BoxModelView - // is destroyed immediately after. - if (this.inspector.markup) { - this.inspector.markup.off("leave", this.onMarkupViewLeave); - this.inspector.markup.off("node-hover", this.onMarkupViewNodeHover); - } - - this.inspector.sidebar.off("computedview-selected", this.onNewNode); - this.inspector.selection.off("new-node-front", this.onNewSelection); - this.inspector.sidebar.off("select", this.onSidebarSelect); - this.inspector.target.off("will-navigate", this.onWillNavigate); - this.inspector.off("computed-view-filtered", this.onFilterComputedView); - - this.inspector = null; - this.doc = null; - this.wrapper = null; - this.container = null; - this.expander = null; - this.sizeLabel = null; - this.sizeHeadingLabel = null; - - this.marginLayout = null; - this.borderLayout = null; - this.paddingLayout = null; - this.boxModel = null; - this.layouts = null; - - if (this.reflowFront) { - this.untrackReflows(); - this.reflowFront.destroy(); - this.reflowFront = null; - } - }, - - /** - * Set initial box model focus to the margin layout. - */ - onFocus: function () { - let activeDescendant = this.boxModel.getAttribute("aria-activedescendant"); - - if (!activeDescendant) { - let nextLayout = this.marginLayout; - this.setAriaActive(nextLayout); - } - }, - - /** - * Active aria-level set to current layout. - * - * @param {Element} nextLayout - * Element of next layout that user has navigated to - * @param {Node} target - * Node to be observed - */ - setAriaActive: function (nextLayout, target) { - this.boxModel.setAttribute("aria-activedescendant", nextLayout.id); - if (target && target._editable) { - target.blur(); - } - - // Clear all - this.marginLayout.classList.remove("layout-active-elm"); - this.borderLayout.classList.remove("layout-active-elm"); - this.paddingLayout.classList.remove("layout-active-elm"); - - // Set the next level's border outline - nextLayout.classList.add("layout-active-elm"); - }, - - /** - * Update aria-active on mouse click. - * - * @param {Event} event - * The event triggered by a mouse click on the box model - */ - onLevelClick: function (event) { - let {target} = event; - let nextLayout = this.layouts[target.getAttribute("data-box")].get("click"); - - this.setAriaActive(nextLayout, target); - }, - - /** - * Handle keyboard navigation and focus for box model layouts. - * - * Updates active layout on arrow key navigation - * Focuses next layout's editboxes on enter key - * Unfocuses current layout's editboxes when active layout changes - * Controls tabbing between editBoxes - * - * @param {Event} event - * The event triggered by a keypress on the box model - */ - onKeyDown: function (event) { - let {target, keyCode} = event; - // If focused on editable value or in editing mode - let isEditable = target._editable || target.editor; - let level = this.boxModel.getAttribute("aria-activedescendant"); - let editingMode = target.tagName === "input"; - let nextLayout; - - switch (keyCode) { - case KeyCodes.DOM_VK_RETURN: - if (!isEditable) { - this.makeFocusable(level); - } - break; - case KeyCodes.DOM_VK_DOWN: - case KeyCodes.DOM_VK_UP: - if (!editingMode) { - event.preventDefault(); - this.makeUnfocasable(level); - let datalevel = this.doc.getElementById(level).getAttribute("data-box"); - nextLayout = this.layouts[datalevel].get(keyCode); - this.boxModel.focus(); - } - break; - case KeyCodes.DOM_VK_TAB: - if (isEditable) { - event.preventDefault(); - this.moveFocus(event, level); - } - break; - case KeyCodes.DOM_VK_ESCAPE: - if (isEditable && target._editable) { - event.preventDefault(); - event.stopPropagation(); - this.makeUnfocasable(level); - this.boxModel.focus(); - } - break; - default: - break; - } - - if (nextLayout) { - this.setAriaActive(nextLayout, target); - } - }, - - /** - * Make previous layout's elements unfocusable. - * - * @param {String} editLevel - * The previous layout - */ - makeUnfocasable: function (editLevel) { - let editBoxes = this.getEditBoxes(editLevel); - editBoxes.forEach(editBox => editBox.setAttribute("tabindex", "-1")); - }, - - /** - * Make current layout's elements focusable. - * - * @param {String} editLevel - * The current layout - */ - makeFocusable: function (editLevel) { - let editBoxes = this.getEditBoxes(editLevel); - editBoxes.forEach(editBox => editBox.setAttribute("tabindex", "0")); - editBoxes[0].focus(); - }, - - /** - * Keyboard navigation of edit boxes wraps around on edge - * elements ([layout]-top, [layout]-left). - * - * @param {Node} target - * Node to be observed - * @param {Boolean} shiftKey - * Determines if shiftKey was pressed - * @param {String} level - * Current active layout - */ - moveFocus: function ({target, shiftKey}, level) { - let editBoxes = this.getEditBoxes(level); - let editingMode = target.tagName === "input"; - // target.nextSibling is input field - let position = editingMode ? editBoxes.indexOf(target.nextSibling) - : editBoxes.indexOf(target); - - if (position === editBoxes.length - 1 && !shiftKey) { - position = 0; - } else if (position === 0 && shiftKey) { - position = editBoxes.length - 1; - } else { - shiftKey ? position-- : position++; - } - - let editBox = editBoxes[position]; - editBox.focus(); - - if (editingMode) { - editBox.click(); - } - }, - - /** - * Retrieve edit boxes for current layout. - * - * @param {String} editLevel - * Current active layout - * @return Layout's edit boxes - */ - getEditBoxes: function (editLevel) { - let dataLevel = this.doc.getElementById(editLevel).getAttribute("data-box"); - return [...this.doc.querySelectorAll( - `[data-box="${dataLevel}"].old-boxmodel-editable`)]; - }, - - onSidebarSelect: function (e, sidebar) { - this.setActive(sidebar === "computedview"); - }, - - /** - * Selection 'new-node-front' event handler. - */ - onNewSelection: function () { - let done = this.inspector.updating("computed-view"); - this.onNewNode() - .then(() => this.hideGeometryEditor()) - .then(done, (err) => { - console.error(err); - done(); - }).catch(console.error); - }, - - /** - * @return a promise that resolves when the view has been updated - */ - onNewNode: function () { - this.setActive(this.isViewVisibleAndNodeValid()); - return this.update(); - }, - - onHighlightMouseOver: function (e) { - let region = e.target.getAttribute("data-box"); - if (!region) { - return; - } - - this.showBoxModel({ - region, - showOnly: region, - onlyRegionArea: true - }); - }, - - onHighlightMouseOut: function () { - this.hideBoxModel(); - }, - - onGeometryButtonClick: function ({target}) { - if (target.hasAttribute("checked")) { - target.removeAttribute("checked"); - this.hideGeometryEditor(); - } else { - target.setAttribute("checked", "true"); - this.showGeometryEditor(); - } - }, - - onPickerStarted: function () { - this.hideGeometryEditor(); - }, - - onToggleExpander: function () { - let isOpen = this.expander.hasAttribute("open"); - - if (isOpen) { - this.container.hidden = true; - this.expander.removeAttribute("open"); - } else { - this.container.hidden = false; - this.expander.setAttribute("open", ""); - } - }, - - onMarkupViewLeave: function () { - this.showGeometryEditor(true); - }, - - onMarkupViewNodeHover: function () { - this.hideGeometryEditor(false); - }, - - onWillNavigate: function () { - this._geometryEditorHighlighter.release().catch(console.error); - this._geometryEditorHighlighter = null; - }, - - /** - * Event handler that responds to the computed view being filtered - * @param {String} reason - * @param {Boolean} hidden - * Whether or not to hide the box model wrapper - */ - onFilterComputedView: function (reason, hidden) { - this.wrapper.hidden = hidden; - }, - - /** - * Stop tracking reflows and hide all values when no node is selected or the - * box model view is hidden, otherwise track reflows and show values. - * @param {Boolean} isActive - */ - setActive: function (isActive) { - if (isActive === this.isActive) { - return; - } - this.isActive = isActive; - - if (isActive) { - this.trackReflows(); - } else { - this.untrackReflows(); - } - }, - - /** - * Compute the dimensions of the node and update the values in - * the inspector.xul document. - * @return a promise that will be resolved when complete. - */ - update: function () { - let lastRequest = Task.spawn((function* () { - if (!this.isViewVisibleAndNodeValid()) { - this.wrapper.hidden = true; - this.inspector.emit("boxmodel-view-updated"); - return null; - } - - let node = this.inspector.selection.nodeFront; - let layout = yield this.inspector.pageStyle.getLayout(node, { - autoMargins: this.isActive - }); - let styleEntries = yield this.inspector.pageStyle.getApplied(node, {}); - - yield this.updateGeometryButton(); - - // If a subsequent request has been made, wait for that one instead. - if (this._lastRequest != lastRequest) { - return this._lastRequest; - } - - this._lastRequest = null; - let width = layout.width; - let height = layout.height; - let newLabel = SHARED_L10N.getFormatStr("dimensions", width, height); - - if (this.sizeHeadingLabel.textContent != newLabel) { - this.sizeHeadingLabel.textContent = newLabel; - } - - for (let i in this.map) { - let property = this.map[i].property; - if (!(property in layout)) { - // Depending on the actor version, some properties - // might be missing. - continue; - } - let parsedValue = parseFloat(layout[property]); - if (Number.isNaN(parsedValue)) { - // Not a number. We use the raw string. - // Useful for "position" for example. - this.map[i].value = layout[property]; - } else { - this.map[i].value = parsedValue; - } - } - - let margins = layout.autoMargins; - if ("top" in margins) { - this.map.marginTop.value = "auto"; - } - if ("right" in margins) { - this.map.marginRight.value = "auto"; - } - if ("bottom" in margins) { - this.map.marginBottom.value = "auto"; - } - if ("left" in margins) { - this.map.marginLeft.value = "auto"; - } - - for (let i in this.map) { - let selector = this.map[i].selector; - let span = this.doc.querySelector(selector); - this.updateSourceRuleTooltip(span, this.map[i].property, styleEntries); - if (span.textContent.length > 0 && - span.textContent == this.map[i].value) { - continue; - } - span.textContent = this.map[i].value; - this.manageOverflowingText(span); - } - - width -= this.map.borderLeft.value + this.map.borderRight.value + - this.map.paddingLeft.value + this.map.paddingRight.value; - width = parseFloat(width.toPrecision(6)); - height -= this.map.borderTop.value + this.map.borderBottom.value + - this.map.paddingTop.value + this.map.paddingBottom.value; - height = parseFloat(height.toPrecision(6)); - - let newValue = width + "\u00D7" + height; - if (this.sizeLabel.textContent != newValue) { - this.sizeLabel.textContent = newValue; - } - - this.elementRules = styleEntries.map(e => e.rule); - - this.wrapper.hidden = false; - - this.inspector.emit("boxmodel-view-updated"); - return null; - }).bind(this)).catch(console.error); - - this._lastRequest = lastRequest; - return this._lastRequest; - }, - - /** - * Update the text in the tooltip shown when hovering over a value to provide - * information about the source CSS rule that sets this value. - * @param {DOMNode} el The element that will receive the tooltip. - * @param {String} property The name of the CSS property for the tooltip. - * @param {Array} rules An array of applied rules retrieved by - * styleActor.getApplied. - */ - updateSourceRuleTooltip: function (el, property, rules) { - // Dummy element used to parse the cssText of applied rules. - let dummyEl = this.doc.createElement("div"); - - // Rules are in order of priority so iterate until we find the first that - // defines a value for the property. - let sourceRule, value; - for (let {rule} of rules) { - dummyEl.style.cssText = rule.cssText; - value = dummyEl.style.getPropertyValue(property); - if (value !== "") { - sourceRule = rule; - break; - } - } - - let title = property; - if (sourceRule && sourceRule.selectors) { - title += "\n" + sourceRule.selectors.join(", "); - } - if (sourceRule && sourceRule.parentStyleSheet) { - if (sourceRule.parentStyleSheet.href) { - title += "\n" + sourceRule.parentStyleSheet.href + ":" + sourceRule.line; - } else { - title += "\n" + INSPECTOR_L10N.getStr("rule.sourceInline") + - ":" + sourceRule.line; - } - } - - el.setAttribute("title", title); - }, - - /** - * Show the box-model highlighter on the currently selected element - * @param {Object} options Options passed to the highlighter actor - */ - showBoxModel: function (options = {}) { - let toolbox = this.inspector.toolbox; - let nodeFront = this.inspector.selection.nodeFront; - - toolbox.highlighterUtils.highlightNodeFront(nodeFront, options); - }, - - /** - * Hide the box-model highlighter on the currently selected element - */ - hideBoxModel: function () { - let toolbox = this.inspector.toolbox; - - toolbox.highlighterUtils.unhighlight(); - }, - - /** - * Show the geometry editor highlighter on the currently selected element - * @param {Boolean} [showOnlyIfActive=false] - * Indicates if the Geometry Editor should be shown only if it's active but - * hidden. - */ - showGeometryEditor: function (showOnlyIfActive = false) { - let toolbox = this.inspector.toolbox; - let nodeFront = this.inspector.selection.nodeFront; - let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor"); - let isActive = nodeGeometry.hasAttribute("checked"); - - if (showOnlyIfActive && !isActive) { - return; - } - - if (this._geometryEditorHighlighter) { - this._geometryEditorHighlighter.show(nodeFront).catch(console.error); - return; - } - - // instantiate Geometry Editor highlighter - toolbox.highlighterUtils - .getHighlighterByType("GeometryEditorHighlighter").then(highlighter => { - highlighter.show(nodeFront).catch(console.error); - this._geometryEditorHighlighter = highlighter; - - // Hide completely the geometry editor if the picker is clicked - toolbox.on("picker-started", this.onPickerStarted); - - // Temporary hide the geometry editor - this.inspector.markup.on("leave", this.onMarkupViewLeave); - this.inspector.markup.on("node-hover", this.onMarkupViewNodeHover); - - // Release the actor on will-navigate event - this.inspector.target.once("will-navigate", this.onWillNavigate); - }); - }, - - /** - * Hide the geometry editor highlighter on the currently selected element - * @param {Boolean} [updateButton=true] - * Indicates if the Geometry Editor's button needs to be unchecked too - */ - hideGeometryEditor: function (updateButton = true) { - if (this._geometryEditorHighlighter) { - this._geometryEditorHighlighter.hide().catch(console.error); - } - - if (updateButton) { - let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor"); - nodeGeometry.removeAttribute("checked"); - } - }, - - /** - * Update the visibility and the state of the geometry editor button, - * based on the selected node. - */ - updateGeometryButton: Task.async(function* () { - let node = this.inspector.selection.nodeFront; - let isEditable = false; - - if (node) { - isEditable = yield this.inspector.pageStyle.isPositionEditable(node); - } - - let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor"); - nodeGeometry.style.visibility = isEditable ? "visible" : "hidden"; - }), - - manageOverflowingText: function (span) { - let classList = span.parentNode.classList; - - if (classList.contains("old-boxmodel-left") || - classList.contains("old-boxmodel-right")) { - let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT; - classList.toggle("old-boxmodel-rotate", force); - } - } -}; - -module.exports = BoxModelView; diff --git a/devtools/client/inspector/components/moz.build b/devtools/client/inspector/components/moz.build index 23d85edcdb23..50556cf26ecf 100644 --- a/devtools/client/inspector/components/moz.build +++ b/devtools/client/inspector/components/moz.build @@ -5,9 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( - 'deprecated-box-model.js', 'inspector-tab-panel.css', 'inspector-tab-panel.js', ) - -BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] diff --git a/devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js b/devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js deleted file mode 100644 index df667e6d1a55..000000000000 --- a/devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js +++ /dev/null @@ -1,49 +0,0 @@ -/* vim: set ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// Test that longer values are rotated on the side - -const res1 = [ - {selector: ".old-boxmodel-margin.old-boxmodel-top > span", value: 30}, - {selector: ".old-boxmodel-margin.old-boxmodel-left > span", value: "auto"}, - {selector: ".old-boxmodel-margin.old-boxmodel-bottom > span", value: 30}, - {selector: ".old-boxmodel-margin.old-boxmodel-right > span", value: "auto"}, - {selector: ".old-boxmodel-padding.old-boxmodel-top > span", value: 20}, - {selector: ".old-boxmodel-padding.old-boxmodel-left > span", value: 2000000}, - {selector: ".old-boxmodel-padding.old-boxmodel-bottom > span", value: 20}, - {selector: ".old-boxmodel-padding.old-boxmodel-right > span", value: 20}, - {selector: ".old-boxmodel-border.old-boxmodel-top > span", value: 10}, - {selector: ".old-boxmodel-border.old-boxmodel-left > span", value: 10}, - {selector: ".old-boxmodel-border.old-boxmodel-bottom > span", value: 10}, - {selector: ".old-boxmodel-border.old-boxmodel-right > span", value: 10}, -]; - -const TEST_URI = encodeURIComponent([ - "", - "
" -].join("")); -const LONG_TEXT_ROTATE_LIMIT = 3; - -add_task(function* () { - yield addTab("data:text/html," + TEST_URI); - let {inspector, view} = yield openBoxModelView(); - yield selectNode("div", inspector); - - for (let i = 0; i < res1.length; i++) { - let elt = view.doc.querySelector(res1[i].selector); - let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT; - let classList = elt.parentNode.classList; - let canBeRotated = classList.contains("old-boxmodel-left") || - classList.contains("old-boxmodel-right"); - let isRotated = classList.contains("old-boxmodel-rotate"); - - is(canBeRotated && isLong, - isRotated, res1[i].selector + " correctly rotated."); - } -}); diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js index 95c81f0cf14d..c721518adff8 100644 --- a/devtools/client/inspector/computed/computed.js +++ b/devtools/client/inspector/computed/computed.js @@ -26,9 +26,14 @@ const { const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu"); const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay"); const KeyShortcuts = require("devtools/client/shared/key-shortcuts"); -const BoxModelView = require("devtools/client/inspector/components/deprecated-box-model"); const clipboardHelper = require("devtools/shared/platform/clipboard"); +const { createElement, createFactory } = require("devtools/client/shared/vendor/react"); +const ReactDOM = require("devtools/client/shared/vendor/react-dom"); +const { Provider } = require("devtools/client/shared/vendor/react-redux"); + +const BoxModelApp = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModelApp")); + const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties"; const {LocalizationHelper} = require("devtools/shared/l10n"); const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES); @@ -154,6 +159,7 @@ UpdateProcess.prototype = { function CssComputedView(inspector, document, pageStyle) { this.inspector = inspector; this.highlighters = inspector.highlighters; + this.store = inspector.store; this.styleDocument = document; this.styleWindow = this.styleDocument.defaultView; this.pageStyle = pageStyle; @@ -174,6 +180,7 @@ function CssComputedView(inspector, document, pageStyle) { let doc = this.styleDocument; this.element = doc.getElementById("propertyContainer"); + this.boxModelWrapper = doc.getElementById("boxmodel-wrapper"); this.searchField = doc.getElementById("computedview-searchbox"); this.searchClearButton = doc.getElementById("computedview-searchinput-clear"); this.includeBrowserStylesCheckbox = @@ -209,6 +216,7 @@ function CssComputedView(inspector, document, pageStyle) { // The element that we're inspecting, and the document that it comes from. this._viewedElement = null; + this.createBoxModelView(); this.createStyleViews(); this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false }); @@ -552,10 +560,10 @@ CssComputedView.prototype = { this._filterChangedTimeout = setTimeout(() => { if (this.searchField.value.length > 0) { this.searchField.setAttribute("filled", true); - this.inspector.emit("computed-view-filtered", true); + this.boxModelWrapper.hidden = true; } else { this.searchField.removeAttribute("filled"); - this.inspector.emit("computed-view-filtered", false); + this.boxModelWrapper.hidden = false; } this.refreshPanel(); @@ -605,6 +613,29 @@ CssComputedView.prototype = { this.inspector.emit("computed-view-sourcelinks-updated"); }, + /** + * Render the box model view. + */ + createBoxModelView: function () { + let { + onHideBoxModelHighlighter, + onShowBoxModelEditor, + onShowBoxModelHighlighter, + } = this.inspector.boxmodel.getComponentProps(); + + let provider = createElement( + Provider, + { store: this.store }, + BoxModelApp({ + showBoxModelProperties: false, + onHideBoxModelHighlighter, + onShowBoxModelEditor, + onShowBoxModelHighlighter, + }) + ); + ReactDOM.render(provider, this.boxModelWrapper); + }, + /** * The CSS as displayed by the UI. */ @@ -783,7 +814,7 @@ CssComputedView.prototype = { // Nodes used in templating this.element = null; - this.panel = null; + this.boxModelWrapper = null; this.searchField = null; this.searchClearButton = null; this.includeBrowserStylesCheckbox = null; @@ -796,6 +827,7 @@ CssComputedView.prototype = { this.inspector = null; this.highlighters = null; + this.store = null; this.styleDocument = null; this.styleWindow = null; @@ -1416,7 +1448,6 @@ function ComputedViewTool(inspector, window) { this.computedView = new CssComputedView(this.inspector, this.document, this.inspector.pageStyle); - this.boxModelView = new BoxModelView(this.inspector, this.document); this.onSelected = this.onSelected.bind(this); this.refresh = this.refresh.bind(this); @@ -1525,9 +1556,8 @@ ComputedViewTool.prototype = { } this.computedView.destroy(); - this.boxModelView.destroy(); - this.computedView = this.boxModelView = this.document = this.inspector = null; + this.computedView = this.document = this.inspector = null; } }; diff --git a/devtools/client/inspector/computed/test/browser_computed_search-filter.js b/devtools/client/inspector/computed/test/browser_computed_search-filter.js index d5b2634f4884..f8f25f23a639 100644 --- a/devtools/client/inspector/computed/test/browser_computed_search-filter.js +++ b/devtools/client/inspector/computed/test/browser_computed_search-filter.js @@ -34,7 +34,7 @@ function* testToggleDefaultStyles(inspector, computedView) { function* testAddTextInFilter(inspector, computedView) { info("setting filter text to \"color\""); let doc = computedView.styleDocument; - let boxModelWrapper = doc.querySelector("#old-boxmodel-wrapper"); + let boxModelWrapper = doc.getElementById("boxmodel-wrapper"); let searchField = computedView.searchField; let onRefreshed = inspector.once("computed-view-refreshed"); let win = computedView.styleWindow; diff --git a/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js b/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js index 2b4a29618310..39dcefcb3838 100644 --- a/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js +++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js @@ -50,7 +50,7 @@ function* testClearSearchFilter(inspector, computedView) { let win = computedView.styleWindow; let doc = computedView.styleDocument; - let boxModelWrapper = doc.querySelector("#old-boxmodel-wrapper"); + let boxModelWrapper = doc.getElementById("boxmodel-wrapper"); let propertyViews = computedView.propertyViews; let searchField = computedView.searchField; let searchClearButton = computedView.searchClearButton; diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js index 1e9af044f029..2b6aecf95c80 100644 --- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -23,7 +23,7 @@ const Menu = require("devtools/client/framework/menu"); const MenuItem = require("devtools/client/framework/menu-item"); const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs"); -const {ComputedViewTool} = require("devtools/client/inspector/computed/computed"); +const BoxModel = require("devtools/client/inspector/boxmodel/box-model"); const {FontInspector} = require("devtools/client/inspector/fonts/fonts"); const {InspectorSearch} = require("devtools/client/inspector/inspector-search"); const {RuleViewTool} = require("devtools/client/inspector/rules/rules"); @@ -78,8 +78,6 @@ const PORTRAIT_MODE_WIDTH = 700; * - computed-view-sourcelinks-updated * Fired when the stylesheet source links have been updated (when switching * to source-mapped files) - * - computed-view-filtered - * Fired when the computed rules view is filtered * - rule-view-refreshed * Fired when the rule view updates to a new node * - rule-view-sourcelinks-updated @@ -572,6 +570,10 @@ Inspector.prototype = { defaultTab == "computedview"); this.ruleview = new RuleViewTool(this, this.panelWin); + this.boxmodel = new BoxModel(this, this.panelWin); + + const {ComputedViewTool} = + this.browserRequire("devtools/client/inspector/computed/computed"); this.computedview = new ComputedViewTool(this, this.panelWin); if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) { diff --git a/devtools/client/inspector/inspector.xhtml b/devtools/client/inspector/inspector.xhtml index d3e13fb7b781..529627161088 100644 --- a/devtools/client/inspector/inspector.xhtml +++ b/devtools/client/inspector/inspector.xhtml @@ -14,7 +14,6 @@ - @@ -132,60 +131,7 @@
-
-
- - -
- -
-
- -
- -
- -
-
-
-
-
-
- -

-

-

-

- -

-

-

-

- -

-

-

-

- -

- -

-
- -
- -
- - -
-
- -
-

-
-
+
diff --git a/devtools/client/inspector/layout/actions/index.js b/devtools/client/inspector/layout/actions/index.js index d694e37400a2..bf11969f238b 100644 --- a/devtools/client/inspector/layout/actions/index.js +++ b/devtools/client/inspector/layout/actions/index.js @@ -17,9 +17,6 @@ createEnum([ // Update the entire grids state with the new list of grids. "UPDATE_GRIDS", - // Update the layout state with the latest layout properties. - "UPDATE_LAYOUT", - // Update the grid highlighter's show grid line numbers state. "UPDATE_SHOW_GRID_LINE_NUMBERS", diff --git a/devtools/client/inspector/layout/actions/moz.build b/devtools/client/inspector/layout/actions/moz.build index 5e157a4bd728..09b7039d19de 100644 --- a/devtools/client/inspector/layout/actions/moz.build +++ b/devtools/client/inspector/layout/actions/moz.build @@ -5,7 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( - 'box-model.js', 'grids.js', 'highlighter-settings.js', 'index.js', diff --git a/devtools/client/inspector/layout/components/App.js b/devtools/client/inspector/layout/components/App.js index d8dd19582d0c..cbcb2bebc271 100644 --- a/devtools/client/inspector/layout/components/App.js +++ b/devtools/client/inspector/layout/components/App.js @@ -11,9 +11,10 @@ const { connect } = require("devtools/client/shared/vendor/react-redux"); const { LocalizationHelper } = require("devtools/shared/l10n"); const Accordion = createFactory(require("./Accordion")); -const BoxModel = createFactory(require("./BoxModel")); const Grid = createFactory(require("./Grid")); +const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel")); + const Types = require("../types"); const { getStr } = require("../utils/l10n"); diff --git a/devtools/client/inspector/layout/components/moz.build b/devtools/client/inspector/layout/components/moz.build index 7ad90ba4572b..551eaa5eda51 100644 --- a/devtools/client/inspector/layout/components/moz.build +++ b/devtools/client/inspector/layout/components/moz.build @@ -8,12 +8,6 @@ DevToolsModules( 'Accordion.css', 'Accordion.js', 'App.js', - 'BoxModel.js', - 'BoxModelEditable.js', - 'BoxModelInfo.js', - 'BoxModelMain.js', - 'BoxModelProperties.js', - 'ComputedProperty.js', 'Grid.js', 'GridDisplaySettings.js', 'GridItem.js', diff --git a/devtools/client/inspector/layout/layout.js b/devtools/client/inspector/layout/layout.js index d9c6ba201be7..2d6f1fe0cf60 100644 --- a/devtools/client/inspector/layout/layout.js +++ b/devtools/client/inspector/layout/layout.js @@ -6,18 +6,12 @@ const Services = require("Services"); const { Task } = require("devtools/shared/task"); -const { getCssProperties } = require("devtools/shared/fronts/css-properties"); -const { ReflowFront } = require("devtools/shared/fronts/reflow"); -const { InplaceEditor } = require("devtools/client/shared/inplace-editor"); const { createFactory, createElement } = require("devtools/client/shared/vendor/react"); const { Provider } = require("devtools/client/shared/vendor/react-redux"); const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip"); -const { - updateLayout, -} = require("./actions/box-model"); const { updateGridColor, updateGridHighlighted, @@ -30,13 +24,10 @@ const { const App = createFactory(require("./components/App")); -const EditingSession = require("./utils/editing-session"); - const { LocalizationHelper } = require("devtools/shared/l10n"); const INSPECTOR_L10N = new LocalizationHelper("devtools/client/locales/inspector.properties"); -const NUMERIC = /^-?[\d\.]+$/; const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers"; const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines"; @@ -59,11 +50,8 @@ function LayoutView(inspector, window) { this.store = inspector.store; this.walker = this.inspector.walker; - this.updateBoxModel = this.updateBoxModel.bind(this); - this.onGridLayoutChange = this.onGridLayoutChange.bind(this); this.onHighlighterChange = this.onHighlighterChange.bind(this); - this.onNewSelection = this.onNewSelection.bind(this); this.onSidebarSelect = this.onSidebarSelect.bind(this); this.init(); @@ -80,13 +68,18 @@ LayoutView.prototype = { return; } + let { + onHideBoxModelHighlighter, + onShowBoxModelEditor, + onShowBoxModelHighlighter, + } = this.inspector.boxmodel.getComponentProps(); + this.layoutInspector = yield this.inspector.walker.getLayoutInspector(); this.loadHighlighterSettings(); this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange); this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange); - this.inspector.selection.on("new-node-front", this.onNewSelection); this.inspector.sidebar.on("select", this.onSidebarSelect); // Create a shared SwatchColorPicker instance to be reused by all GridItem components. @@ -112,13 +105,9 @@ LayoutView.prototype = { */ showBoxModelProperties: true, - /** - * Hides the box-model highlighter on the currently selected element. - */ - onHideBoxModelHighlighter: () => { - let toolbox = this.inspector.toolbox; - toolbox.highlighterUtils.unhighlight(); - }, + onHideBoxModelHighlighter, + onShowBoxModelEditor, + onShowBoxModelHighlighter, /** * Handler for a change in the grid overlay color picker for a grid container. @@ -143,88 +132,6 @@ LayoutView.prototype = { } }, - /** - * Shows the inplace editor when a box model editable value is clicked on the - * box model panel. - * - * @param {DOMNode} element - * The element that was clicked. - * @param {Event} event - * The event object. - * @param {String} property - * The name of the property. - */ - onShowBoxModelEditor: (element, event, property) => { - let session = new EditingSession({ - inspector: this.inspector, - doc: this.document, - elementRules: this.elementRules, - }); - let initialValue = session.getProperty(property); - - let editor = new InplaceEditor({ - element: element, - initial: initialValue, - contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, - property: { - name: property - }, - start: self => { - self.elt.parentNode.classList.add("boxmodel-editing"); - }, - change: value => { - if (NUMERIC.test(value)) { - value += "px"; - } - - let properties = [ - { name: property, value: value } - ]; - - if (property.substring(0, 7) == "border-") { - let bprop = property.substring(0, property.length - 5) + "style"; - let style = session.getProperty(bprop); - if (!style || style == "none" || style == "hidden") { - properties.push({ name: bprop, value: "solid" }); - } - } - - session.setProperties(properties).catch(e => console.error(e)); - }, - done: (value, commit) => { - editor.elt.parentNode.classList.remove("boxmodel-editing"); - if (!commit) { - session.revert().then(() => { - session.destroy(); - }, e => console.error(e)); - return; - } - - let node = this.inspector.selection.nodeFront; - this.inspector.pageStyle.getLayout(node, { - autoMargins: true, - }).then(layout => { - this.store.dispatch(updateLayout(layout)); - }, e => console.error(e)); - }, - contextMenu: this.inspector.onTextBoxContextMenu, - cssProperties: getCssProperties(this.inspector.toolbox) - }, event); - }, - - /** - * Shows the box-model highlighter on the currently selected element. - * - * @param {Object} options - * Options passed to the highlighter actor. - */ - onShowBoxModelHighlighter: (options = {}) => { - let toolbox = this.inspector.toolbox; - let nodeFront = this.inspector.selection.nodeFront; - - toolbox.highlighterUtils.highlightNodeFront(nodeFront, options); - }, - /** * Handler for a change in the input checkboxes in the GridList component. * Toggles on/off the grid highlighter for the provided grid container element. @@ -309,16 +216,9 @@ LayoutView.prototype = { destroy() { this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange); this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange); - this.inspector.selection.off("new-node-front", this.onNewSelection); this.inspector.sidebar.off("select", this.onSidebarSelect); this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange); - if (this.reflowFront) { - this.untrackReflows(); - this.reflowFront.destroy(); - this.reflowFront = null; - } - this.document = null; this.inspector = null; this.layoutInspector = null; @@ -372,16 +272,6 @@ LayoutView.prototype = { this.inspector.sidebar.getCurrentTabID() === "layoutview"; }, - /** - * Returns true if the layout panel is visible and the current node is valid to - * be displayed in the view. - */ - isPanelVisibleAndNodeValid() { - return this.isPanelVisible() && - this.inspector.selection.isConnected() && - this.inspector.selection.isElementNode(); - }, - /** * Load the grid highligher display settings into the store from the stored preferences. */ @@ -395,72 +285,6 @@ LayoutView.prototype = { dispatch(updateShowInfiniteLines(showInfinteLines)); }, - /** - * Starts listening to reflows in the current tab. - */ - trackReflows() { - if (!this.reflowFront) { - let { target } = this.inspector; - if (target.form.reflowActor) { - this.reflowFront = ReflowFront(target.client, - target.form); - } else { - return; - } - } - - this.reflowFront.on("reflows", this.updateBoxModel); - this.reflowFront.start(); - }, - - /** - * Stops listening to reflows in the current tab. - */ - untrackReflows() { - if (!this.reflowFront) { - return; - } - - this.reflowFront.off("reflows", this.updateBoxModel); - this.reflowFront.stop(); - }, - - /** - * Updates the box model panel by dispatching the new layout data. - */ - updateBoxModel() { - let lastRequest = Task.spawn((function* () { - if (!(this.isPanelVisible() && - this.inspector.selection.isConnected() && - this.inspector.selection.isElementNode())) { - return null; - } - - let node = this.inspector.selection.nodeFront; - let layout = yield this.inspector.pageStyle.getLayout(node, { - autoMargins: true, - }); - let styleEntries = yield this.inspector.pageStyle.getApplied(node, {}); - this.elementRules = styleEntries.map(e => e.rule); - - // Update the redux store with the latest layout properties and update the box - // model view. - this.store.dispatch(updateLayout(layout)); - - // If a subsequent request has been made, wait for that one instead. - if (this._lastRequest != lastRequest) { - return this._lastRequest; - } - - this._lastRequest = null; - - this.inspector.emit("boxmodel-view-updated"); - return null; - }).bind(this)).catch(console.error); - - this._lastRequest = lastRequest; - }, - /** * Updates the grid panel by dispatching the new grid data. This is called when the * layout view becomes visible or the view needs to be updated with new grid data. @@ -526,17 +350,6 @@ LayoutView.prototype = { this.store.dispatch(updateGridHighlighted(nodeFront, highlighted)); }, - /** - * Selection 'new-node-front' event handler. - */ - onNewSelection: function () { - if (!this.isPanelVisibleAndNodeValid()) { - return; - } - - this.updateBoxModel(); - }, - /** * Handler for the inspector sidebar select event. Starts listening for * "grid-layout-changed" if the layout panel is visible. Otherwise, stop @@ -546,17 +359,10 @@ LayoutView.prototype = { onSidebarSelect() { if (!this.isPanelVisible()) { this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange); - this.untrackReflows(); return; } - if (this.inspector.selection.isConnected() && - this.inspector.selection.isElementNode()) { - this.trackReflows(); - } - this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange); - this.updateBoxModel(); this.updateGridPanel(); }, diff --git a/devtools/client/inspector/layout/reducers/moz.build b/devtools/client/inspector/layout/reducers/moz.build index 50e631af17b1..34867f4e837f 100644 --- a/devtools/client/inspector/layout/reducers/moz.build +++ b/devtools/client/inspector/layout/reducers/moz.build @@ -5,7 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( - 'box-model.js', 'grids.js', 'highlighter-settings.js', ) diff --git a/devtools/client/inspector/layout/types.js b/devtools/client/inspector/layout/types.js index b9c286200ca0..0c0cd3b77473 100644 --- a/devtools/client/inspector/layout/types.js +++ b/devtools/client/inspector/layout/types.js @@ -6,16 +6,6 @@ const { PropTypes } = require("devtools/client/shared/vendor/react"); -/** - * The box model data for the current selected node. - */ -exports.boxModel = { - - // The layout information of the current selected node - layout: PropTypes.object, - -}; - /** * A single grid container in the document. */ diff --git a/devtools/client/inspector/layout/utils/moz.build b/devtools/client/inspector/layout/utils/moz.build index 537f779cc3cb..e3053b63fab6 100644 --- a/devtools/client/inspector/layout/utils/moz.build +++ b/devtools/client/inspector/layout/utils/moz.build @@ -5,6 +5,5 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( - 'editing-session.js', 'l10n.js', ) diff --git a/devtools/client/inspector/moz.build b/devtools/client/inspector/moz.build index 1c5f3e58dd4c..8f1679fd2b39 100644 --- a/devtools/client/inspector/moz.build +++ b/devtools/client/inspector/moz.build @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += [ + 'boxmodel', 'components', 'computed', 'fonts', diff --git a/devtools/client/inspector/reducers.js b/devtools/client/inspector/reducers.js index 6c09e49c49e4..a6818664560e 100644 --- a/devtools/client/inspector/reducers.js +++ b/devtools/client/inspector/reducers.js @@ -7,6 +7,6 @@ // This file exposes the Redux reducers of the box model, grid and grid highlighter // settings. -exports.boxModel = require("devtools/client/inspector/layout/reducers/box-model"); +exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model"); exports.grids = require("devtools/client/inspector/layout/reducers/grids"); exports.highlighterSettings = require("devtools/client/inspector/layout/reducers/highlighter-settings"); diff --git a/devtools/client/inspector/test/browser_inspector_textbox-menu.js b/devtools/client/inspector/test/browser_inspector_textbox-menu.js index a5e0e75622d8..b3e9ca68515c 100644 --- a/devtools/client/inspector/test/browser_inspector_textbox-menu.js +++ b/devtools/client/inspector/test/browser_inspector_textbox-menu.js @@ -66,7 +66,7 @@ add_task(function* () { info("Testing the box-model region"); let margin = inspector.panelDoc.querySelector( - ".old-boxmodel-margin.old-boxmodel-top > span"); + ".boxmodel-margin.boxmodel-top > span"); EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin); yield checkTextBox(inspector.panelDoc.activeElement, toolbox); }); diff --git a/devtools/client/inspector/test/shared-head.js b/devtools/client/inspector/test/shared-head.js index a953d547e7f5..c42690a2813e 100644 --- a/devtools/client/inspector/test/shared-head.js +++ b/devtools/client/inspector/test/shared-head.js @@ -52,7 +52,15 @@ var openInspectorSidebarTab = Task.async(function* (id) { let {toolbox, inspector, testActor} = yield openInspector(); info("Selecting the " + id + " sidebar"); - inspector.sidebar.select(id); + + if (id === "computedview" || id === "layoutview") { + // The layout and computed views should wait until the box-model widget is ready. + let onBoxModelViewReady = inspector.once("boxmodel-view-updated"); + inspector.sidebar.select(id); + yield onBoxModelViewReady; + } else { + inspector.sidebar.select(id); + } return { toolbox, diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn index 628a336e45c0..b68d20740c14 100644 --- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -177,7 +177,6 @@ devtools.jar: skin/images/noise.png (themes/images/noise.png) skin/images/dropmarker.svg (themes/images/dropmarker.svg) skin/boxmodel.css (themes/boxmodel.css) - skin/deprecated-boxmodel.css (themes/deprecated-boxmodel.css) skin/images/geometry-editor.svg (themes/images/geometry-editor.svg) skin/images/pause.svg (themes/images/pause.svg) skin/images/play.svg (themes/images/play.svg) diff --git a/devtools/client/shared/browser-loader.js b/devtools/client/shared/browser-loader.js index ab7505beee9b..3aef74a99bf9 100644 --- a/devtools/client/shared/browser-loader.js +++ b/devtools/client/shared/browser-loader.js @@ -12,6 +12,8 @@ const Services = devtools.require("Services"); const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm"); const BROWSER_BASED_DIRS = [ + "resource://devtools/client/inspector/boxmodel", + "resource://devtools/client/inspector/computed", "resource://devtools/client/inspector/layout", "resource://devtools/client/jsonview", "resource://devtools/client/shared/vendor", diff --git a/devtools/client/themes/deprecated-boxmodel.css b/devtools/client/themes/deprecated-boxmodel.css deleted file mode 100644 index e1f6d1432e20..000000000000 --- a/devtools/client/themes/deprecated-boxmodel.css +++ /dev/null @@ -1,259 +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/ */ - -/** - * THIS STYLESHEET IS FOR THE DEPRECATED BOX MODEL MODULE (deprecated-box-model.js) AND - * SHOULD NO LONGER BE MODIFIED. - */ - -#old-boxmodel-wrapper { - border-bottom-style: solid; - border-bottom-width: 1px; - border-color: var(--theme-splitter-color); - -moz-user-select: none; -} - -#old-boxmodel-container { - /* The view will grow bigger as the window gets resized, until 400px */ - max-width: 400px; - margin: 0px auto; - padding: 0; -} - -/* Header */ - -#old-boxmodel-header, -#old-boxmodel-info { - display: flex; - align-items: center; - padding: 4px 17px; -} - -#old-layout-geometry-editor { - visibility: hidden; -} - -#old-layout-geometry-editor::before { - background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px; -} - -/* Main: contains the box-model regions */ - -#old-boxmodel-main { - position: relative; - box-sizing: border-box; - /* The regions are semi-transparent, so the white background is partly - visible */ - background-color: white; - color: var(--theme-selection-color); - /* Make sure there is some space between the window's edges and the regions */ - margin: 0 14px 4px 14px; - width: calc(100% - 2 * 14px); -} - -.old-boxmodel-margin, -.old-boxmodel-size { - color: var(--theme-highlight-blue); -} - -/* Regions are 3 nested elements with wide borders and outlines */ - -#old-boxmodel-content { - height: 18px; -} - -#old-boxmodel-margins, -#old-boxmodel-borders, -#old-boxmodel-padding { - border-color: hsla(210,100%,85%,0.2); - border-width: 18px; - border-style: solid; - outline: dotted 1px hsl(210,100%,85%); -} - -#old-boxmodel-margins { - /* This opacity applies to all of the regions, since they are nested */ - opacity: .8; -} - -/* Regions colors */ - -#old-boxmodel-margins { - border-color: #edff64; -} - -#old-boxmodel-borders { - border-color: #444444; -} - -#old-boxmodel-padding { - border-color: #6a5acd; -} - -#old-boxmodel-content { - background-color: #87ceeb; -} - -.theme-firebug #old-boxmodel-main, -.theme-firebug #old-boxmodel-borders, -.theme-firebug #old-boxmodel-content { - border-style: solid; -} - -.theme-firebug #old-boxmodel-main, -.theme-firebug #old-boxmodel-header { - font-family: var(--proportional-font-family); -} - -.theme-firebug #old-boxmodel-main { - color: var(--theme-body-color); - font-size: var(--theme-toolbar-font-size); -} - -.theme-firebug #old-boxmodel-header { - font-size: var(--theme-toolbar-font-size); -} - -/* Editable region sizes are contained in absolutely positioned

*/ - -#old-boxmodel-main > p { - position: absolute; - pointer-events: none; - margin: 0; - text-align: center; -} - -#old-boxmodel-main > p > span, -#old-boxmodel-main > p > input { - vertical-align: middle; - pointer-events: auto; -} - -/* Coordinates for the region sizes */ - -.old-boxmodel-top, -.old-boxmodel-bottom { - width: calc(100% - 2px); - text-align: center; -} - -.old-boxmodel-padding.old-boxmodel-top { - top: 37px; -} - -.old-boxmodel-padding.old-boxmodel-bottom { - bottom: 38px; -} - -.old-boxmodel-border.old-boxmodel-top { - top: 19px; -} - -.old-boxmodel-border.old-boxmodel-bottom { - bottom: 20px; -} - -.old-boxmodel-margin.old-boxmodel-top { - top: 1px; -} - -.old-boxmodel-margin.old-boxmodel-bottom { - bottom: 2px; -} - -.old-boxmodel-size, -.old-boxmodel-margin.old-boxmodel-left, -.old-boxmodel-margin.old-boxmodel-right, -.old-boxmodel-border.old-boxmodel-left, -.old-boxmodel-border.old-boxmodel-right, -.old-boxmodel-padding.old-boxmodel-left, -.old-boxmodel-padding.old-boxmodel-right { - top: 22px; - line-height: 80px; -} - -.old-boxmodel-size { - width: calc(100% - 2px); -} - -.old-boxmodel-margin.old-boxmodel-right, -.old-boxmodel-margin.old-boxmodel-left, -.old-boxmodel-border.old-boxmodel-left, -.old-boxmodel-border.old-boxmodel-right, -.old-boxmodel-padding.old-boxmodel-right, -.old-boxmodel-padding.old-boxmodel-left { - width: 21px; -} - -.old-boxmodel-padding.old-boxmodel-left { - left: 35px; -} - -.old-boxmodel-padding.old-boxmodel-right { - right: 35px; -} - -.old-boxmodel-border.old-boxmodel-left { - left: 16px; -} - -.old-boxmodel-border.old-boxmodel-right { - right: 17px; -} - -.old-boxmodel-margin.old-boxmodel-right { - right: 0; -} - -.old-boxmodel-margin.old-boxmodel-left { - left: 0; -} - -.old-boxmodel-rotate.old-boxmodel-left:not(.old-boxmodel-editing) { - transform: rotate(-90deg); -} - -.old-boxmodel-rotate.old-boxmodel-right:not(.old-boxmodel-editing) { - transform: rotate(90deg); -} - -/* Legend: displayed inside regions */ - -.old-boxmodel-legend { - position: absolute; - margin: 2px 6px; - z-index: 1; -} - -.old-boxmodel-legend[data-box="margin"] { - color: var(--theme-highlight-blue); -} - -/* Editable fields */ - -.old-boxmodel-editable { - border: 1px dashed transparent; - -moz-user-select: none; -} - -.old-boxmodel-editable:hover { - border-bottom-color: hsl(0, 0%, 50%); -} - -/* Make sure the content size doesn't appear as editable like the other sizes */ - -.old-boxmodel-size > span { - cursor: default; -} - -/* Box Model Info: contains the position and size of the element */ - -#old-boxmodel-element-size { - flex: 1; -} - -#old-boxmodel-position-group { - display: flex; - align-items: center; -} diff --git a/devtools/server/tests/unit/test_frameactor_wasm-01.js b/devtools/server/tests/unit/test_frameactor_wasm-01.js index 63376ae9efce..bc941b32283a 100644 --- a/devtools/server/tests/unit/test_frameactor_wasm-01.js +++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js @@ -8,13 +8,9 @@ var gDebuggee; var gClient; var gThreadClient; -var gOldPref; function run_test() { - gOldPref = Services.prefs.getBoolPref("javascript.options.wasm"); - Services.prefs.setBoolPref("javascript.options.wasm", true); - if (typeof WebAssembly == "undefined") { return; // wasm is not enabled for this platform } @@ -49,7 +45,6 @@ function test_pause_frame() do_check_eq(location.column > 0, true); do_check_eq(location.source.url.endsWith(" > wasm"), true); - Services.prefs.setBoolPref("javascript.options.wasm", gOldPref); finishClient(gClient); }); }); @@ -58,7 +53,7 @@ function test_pause_frame() // WebAssembly bytecode was generated by running: // js -e 'print(wasmTextToBinary("(module(import \"a\" \"b\")(func(export \"c\")call 0))"))' var m = new WebAssembly.Module(new Uint8Array([ - 0,97,115,109,13,0,0,0,1,132,128,128,128,0,1,96,0,0,2,135,128,128,128,0,1,1,97,1, + 0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,2,135,128,128,128,0,1,1,97,1, 98,0,0,3,130,128,128,128,0,1,0,6,129,128,128,128,0,0,7,133,128,128,128,0,1,1,99, 0,1,10,138,128,128,128,0,1,132,128,128,128,0,0,16,0,11 ])); diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 2fcd326e11bb..362c43ff8ca9 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -172,7 +172,7 @@ NS_INTERFACE_MAP_END CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject) { return Preferences::GetBool("dom.webcomponents.customelements.enabled") || - Preferences::GetBool("dom.webcomponents.enabled"); + nsContentUtils::IsWebComponentsEnabled(); } /* static */ already_AddRefed diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index c09827e9497b..27d92cef1109 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -285,6 +285,7 @@ bool nsContentUtils::sIsPerformanceTimingEnabled = false; bool nsContentUtils::sIsResourceTimingEnabled = false; bool nsContentUtils::sIsUserTimingLoggingEnabled = false; bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false; +bool nsContentUtils::sIsWebComponentsEnabled = false; bool nsContentUtils::sEncodeDecodeURLHash = false; bool nsContentUtils::sGettersDecodeURLHash = false; bool nsContentUtils::sPrivacyResistFingerprinting = false; @@ -512,10 +513,11 @@ nsContentUtils::Init() sSecurityManager->GetSystemPrincipal(&sSystemPrincipal); MOZ_ASSERT(sSystemPrincipal); - // We use the constructor here because we want infallible initialization; we - // apparently don't care whether sNullSubjectPrincipal has a sane URI or not. - RefPtr nullPrincipal = new nsNullPrincipal(); - nullPrincipal->Init(); + RefPtr nullPrincipal = nsNullPrincipal::Create(); + if (!nullPrincipal) { + return NS_ERROR_FAILURE; + } + nullPrincipal.forget(&sNullSubjectPrincipal); nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); @@ -582,6 +584,9 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled, "dom.forms.autocomplete.experimental", false); + Preferences::AddBoolVarCache(&sIsWebComponentsEnabled, + "dom.webcomponents.enabled", false); + Preferences::AddBoolVarCache(&sEncodeDecodeURLHash, "dom.url.encode_decode_hash", false); diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index af250c46c05b..e7889f9d1cbe 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2806,6 +2806,9 @@ public: const mozilla::dom::Sequence& aTransfer, JS::MutableHandle aValue); + static bool + IsWebComponentsEnabled() { return sIsWebComponentsEnabled; } + private: static bool InitializeEventTable(); @@ -2912,6 +2915,7 @@ private: static bool sIsUserTimingLoggingEnabled; static bool sIsFrameTimingPrefEnabled; static bool sIsExperimentalAutocompleteEnabled; + static bool sIsWebComponentsEnabled; static bool sEncodeDecodeURLHash; static bool sGettersDecodeURLHash; static bool sPrivacyResistFingerprinting; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index f926212d9d05..708ca29b3103 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -104,6 +104,7 @@ #include "nsIScriptSecurityManager.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" +#include "nsNullPrincipal.h" #include "nsIDOMWindow.h" #include "nsPIDOMWindow.h" @@ -2736,7 +2737,7 @@ nsDocument::InitCSP(nsIChannel* aChannel) if (cspSandboxFlags & SANDBOXED_ORIGIN) { // If the new CSP sandbox flags do not have the allow-same-origin flag // reset the document principal to a null principal - principal = do_CreateInstance("@mozilla.org/nullprincipal;1"); + principal = nsNullPrincipal::Create(); SetPrincipal(principal); } @@ -5941,7 +5942,7 @@ nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) { JS::Rooted obj(aCx, aObject); - if (Preferences::GetBool("dom.webcomponents.enabled")) { + if (nsContentUtils::IsWebComponentsEnabled()) { return true; } @@ -5957,7 +5958,7 @@ nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) bool nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo) { - if (Preferences::GetBool("dom.webcomponents.enabled")) { + if (nsContentUtils::IsWebComponentsEnabled()) { return true; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5f867545f177..3a9887d6bde9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -9406,21 +9406,16 @@ public: nsAutoString addonId; if (NS_SUCCEEDED(pc->GetAddonId(addonId)) && !addonId.IsEmpty()) { // We want to nuke all references to the add-on compartment. - js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(), - js::SingleCompartment(cpt), - win->IsInnerWindow() ? js::DontNukeWindowReferences - : js::NukeWindowReferences); - - // Now mark the compartment as nuked and non-scriptable. - auto compartmentPrivate = xpc::CompartmentPrivate::Get(cpt); - compartmentPrivate->wasNuked = true; - compartmentPrivate->scriptability.Block(); + xpc::NukeAllWrappersForCompartment(cx, cpt, + win->IsInnerWindow() ? js::DontNukeWindowReferences + : js::NukeWindowReferences); } else { // We only want to nuke wrappers for the chrome->content case js::NukeCrossCompartmentWrappers(cx, BrowserCompartmentMatcher(), js::SingleCompartment(cpt), win->IsInnerWindow() ? js::DontNukeWindowReferences - : js::NukeWindowReferences); + : js::NukeWindowReferences, + js::NukeIncomingReferences); } } } diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html index 13a284270083..f4cd87432fb9 100644 --- a/dom/base/test/test_postMessages.html +++ b/dom/base/test/test_postMessages.html @@ -11,8 +11,7 @@ diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index 80176a0fbe4f..9422ed2080f5 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -74,6 +74,7 @@ var ecmaGlobals = "URIError", "WeakMap", "WeakSet", + {name: "WebAssembly", disabled: !SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported()} ]; // IMPORTANT: Do not change the list above without review from // a JavaScript Engine peer! diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 6d6cd257ba0d..f2f92a7c175f 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -476,6 +476,7 @@ private: UniquePtr mPrincipalInfo; nsCString mCSPHeaderValue; nsCString mCSPReportOnlyHeaderValue; + nsCString mReferrerPolicyHeaderValue; }; NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver) @@ -622,6 +623,10 @@ private: MOZ_ASSERT(!loadInfo.mLoadingFinished); loadInfo.mLoadingFinished = true; + if (IsMainWorkerScript() && NS_SUCCEEDED(aRv)) { + MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->PrincipalURIMatchesScriptURL()); + } + MaybeExecuteFinishedScripts(aIndex); } @@ -1137,8 +1142,18 @@ private: // Store the channel info if needed. mWorkerPrivate->InitChannelInfo(channel); + // Our final channel principal should match the original principal + // in terms of the origin. MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->FinalChannelPrincipalIsValid(channel)); + // However, we must still override the principal since the nsIPrincipal + // URL may be different due to same-origin redirects. Unfortunately this + // URL must exactly match the final worker script URL in order to + // properly set the referrer header on fetch/xhr requests. If bug 1340694 + // is ever fixed this can be removed. + rv = mWorkerPrivate->SetPrincipalFromChannel(channel); + NS_ENSURE_SUCCESS(rv, rv); + // We did inherit CSP in bug 1223647. If we do not already have a CSP, we // should get it from the HTTP headers on the worker script. if (!mWorkerPrivate->GetCSP() && CSPService::sCSPEnabled) { @@ -1146,6 +1161,9 @@ private: tCspROHeaderValue); NS_ENSURE_SUCCESS(rv, rv); } + + mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue); + WorkerPrivate* parent = mWorkerPrivate->GetParent(); if (parent) { // XHR Params Allowed @@ -1153,16 +1171,6 @@ private: } } - NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue); - // If there's a Referrer-Policy header, apply it. - if (!tRPHeaderValue.IsEmpty()) { - net::ReferrerPolicy policy = - nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue); - if (policy != net::RP_Unset) { - mWorkerPrivate->SetReferrerPolicy(policy); - } - } - return NS_OK; } @@ -1172,7 +1180,8 @@ private: const mozilla::dom::ChannelInfo& aChannelInfo, UniquePtr aPrincipalInfo, const nsACString& aCSPHeaderValue, - const nsACString& aCSPReportOnlyHeaderValue) + const nsACString& aCSPReportOnlyHeaderValue, + const nsACString& aReferrerPolicyHeaderValue) { AssertIsOnMainThread(); MOZ_ASSERT(aIndex < mLoadInfos.Length()); @@ -1230,13 +1239,16 @@ private: // Override the principal on the WorkerPrivate. This is only necessary // in order to get a principal with exactly the correct URL. The fetch // referrer logic depends on the WorkerPrivate principal having a URL - // that matches the worker script URL. + // that matches the worker script URL. If bug 1340694 is ever fixed + // this can be removed. rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); rv = mWorkerPrivate->SetCSPFromHeaderValues(aCSPHeaderValue, aCSPReportOnlyHeaderValue); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + + mWorkerPrivate->SetReferrerPolicyFromHeaderValue(aReferrerPolicyHeaderValue); } if (NS_SUCCEEDED(rv)) { @@ -1652,6 +1664,8 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx, mCSPHeaderValue, ignored); headers->Get(NS_LITERAL_CSTRING("content-security-policy-report-only"), mCSPReportOnlyHeaderValue, ignored); + headers->Get(NS_LITERAL_CSTRING("referrer-policy"), + mReferrerPolicyHeaderValue, ignored); nsCOMPtr inputStream; response->GetBody(getter_AddRefs(inputStream)); @@ -1665,7 +1679,8 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx, mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached; mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo, Move(mPrincipalInfo), mCSPHeaderValue, - mCSPReportOnlyHeaderValue); + mCSPReportOnlyHeaderValue, + mReferrerPolicyHeaderValue); return; } @@ -1726,7 +1741,8 @@ CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCont MOZ_ASSERT(mPrincipalInfo); mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo, Move(mPrincipalInfo), mCSPHeaderValue, - mCSPReportOnlyHeaderValue); + mCSPReportOnlyHeaderValue, + mReferrerPolicyHeaderValue); return NS_OK; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index f79197835952..37ed1641d83f 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1985,6 +1985,57 @@ WorkerLoadInfo::PrincipalIsValid() const mPrincipalInfo->type() != PrincipalInfo::T__None && mPrincipalInfo->type() <= PrincipalInfo::T__Last; } + +bool +WorkerLoadInfo::PrincipalURIMatchesScriptURL() +{ + AssertIsOnMainThread(); + + nsAutoCString scheme; + nsresult rv = mBaseURI->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, false); + + // A system principal must either be a blob URL or a resource JSM. + if (mPrincipal->GetIsSystemPrincipal()) { + if (scheme == NS_LITERAL_CSTRING("blob")) { + return true; + } + + bool isResource = false; + nsresult rv = NS_URIChainHasFlags(mBaseURI, + nsIProtocolHandler::URI_IS_UI_RESOURCE, + &isResource); + NS_ENSURE_SUCCESS(rv, false); + + return isResource; + } + + // A null principal can occur for a data URL worker script or a blob URL + // worker script from a sandboxed iframe. + if (mPrincipal->GetIsNullPrincipal()) { + return scheme == NS_LITERAL_CSTRING("data") || + scheme == NS_LITERAL_CSTRING("blob"); + } + + // The principal for a blob: URL worker script does not have a matching URL. + // This is likely a bug in our referer setting logic, but exempt it for now. + // This is another reason we should fix bug 1340694 so that referer does not + // depend on the principal URI. + if (scheme == NS_LITERAL_CSTRING("blob")) { + return true; + } + + nsCOMPtr principalURI; + rv = mPrincipal->GetURI(getter_AddRefs(principalURI)); + NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_TRUE(principalURI, false); + + bool equal = false; + rv = principalURI->Equals(mBaseURI, &equal); + NS_ENSURE_SUCCESS(rv, false); + + return equal; +} #endif // defined(DEBUG) || !defined(RELEASE_OR_BETA) bool @@ -2640,6 +2691,26 @@ WorkerPrivateParent::SetCSPFromHeaderValues(const nsACString& aCSPHeade return NS_OK; } +template +void +WorkerPrivateParent::SetReferrerPolicyFromHeaderValue( + const nsACString& aReferrerPolicyHeaderValue) +{ + NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue); + + if (headerValue.IsEmpty()) { + return; + } + + net::ReferrerPolicy policy = + nsContentUtils::GetReferrerPolicyFromHeader(headerValue); + if (policy == net::RP_Unset) { + return; + } + + SetReferrerPolicy(policy); +} + // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the // templates. @@ -3869,6 +3940,13 @@ WorkerPrivateParent::FinalChannelPrincipalIsValid(nsIChannel* aChannel) { return mLoadInfo.FinalChannelPrincipalIsValid(aChannel); } + +template +bool +WorkerPrivateParent::PrincipalURIMatchesScriptURL() +{ + return mLoadInfo.PrincipalURIMatchesScriptURL(); +} #endif template diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 545f2c63a1d2..5e243ec1057c 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -655,6 +655,9 @@ public: #if defined(DEBUG) || !defined(RELEASE_OR_BETA) bool FinalChannelPrincipalIsValid(nsIChannel* aChannel); + + bool + PrincipalURIMatchesScriptURL(); #endif bool @@ -703,6 +706,9 @@ public: SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue, const nsACString& aCSPReportOnlyHeaderValue); + void + SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue); + net::ReferrerPolicy GetReferrerPolicy() const { diff --git a/dom/workers/Workers.h b/dom/workers/Workers.h index 66e5b9ecceab..f7b94c5394ba 100644 --- a/dom/workers/Workers.h +++ b/dom/workers/Workers.h @@ -296,6 +296,9 @@ struct WorkerLoadInfo bool PrincipalIsValid() const; + + bool + PrincipalURIMatchesScriptURL(); #endif bool diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js index af6d37a1069d..4f663e991f5c 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -69,6 +69,7 @@ var ecmaGlobals = "URIError", "WeakMap", "WeakSet", + {name: "WebAssembly", optional: true} ]; // IMPORTANT: Do not change the list above without review from // a JavaScript Engine peer! @@ -252,6 +253,8 @@ function createInterfaceMap(version, userAgent) { (entry.android === !isAndroid && !entry.nonReleaseAndroid && !entry.nightlyAndroid) || (entry.release === !isRelease)) { interfaceMap[entry.name] = false; + } else if (entry.optional) { + interfaceMap[entry.name] = "optional"; } else { interfaceMap[entry.name] = true; } @@ -272,17 +275,21 @@ function runTest(version, userAgent) { if (!/^[A-Z]/.test(name)) { continue; } - ok(interfaceMap[name], + ok(interfaceMap[name] === "optional" || interfaceMap[name], "If this is failing: DANGER, are you sure you want to expose the new interface " + name + " to all webpages as a property on the service worker? Do not make a change to this file without a " + " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"); delete interfaceMap[name]; } for (var name of Object.keys(interfaceMap)) { - ok(name in self === interfaceMap[name], - name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope"); - if (!interfaceMap[name]) { + if (interfaceMap[name] === "optional") { delete interfaceMap[name]; + } else { + ok(name in self === interfaceMap[name], + name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope"); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } } } is(Object.keys(interfaceMap).length, 0, diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index 694d99732929..e230f3ccf801 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -69,6 +69,7 @@ var ecmaGlobals = "URIError", "WeakMap", "WeakSet", + {name: "WebAssembly", optional: true} ]; // IMPORTANT: Do not change the list above without review from // a JavaScript Engine peer! @@ -266,6 +267,8 @@ function createInterfaceMap(version, userAgent) { (entry.release === !isRelease) || entry.disabled) { interfaceMap[entry.name] = false; + } else if (entry.optional) { + interfaceMap[entry.name] = "optional"; } else { interfaceMap[entry.name] = true; } @@ -286,17 +289,21 @@ function runTest(version, userAgent) { if (!/^[A-Z]/.test(name)) { continue; } - ok(interfaceMap[name], + ok(interfaceMap[name] === "optional" || interfaceMap[name], "If this is failing: DANGER, are you sure you want to expose the new interface " + name + " to all webpages as a property on the worker? Do not make a change to this file without a " + " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"); delete interfaceMap[name]; } for (var name of Object.keys(interfaceMap)) { - ok(name in self === interfaceMap[name], - name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope"); - if (!interfaceMap[name]) { + if (interfaceMap[name] === "optional") { delete interfaceMap[name]; + } else { + ok(name in self === interfaceMap[name], + name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope"); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } } } is(Object.keys(interfaceMap).length, 0, diff --git a/gfx/2d/SourceSurfaceSkia.cpp b/gfx/2d/SourceSurfaceSkia.cpp index 3f242eaf9356..186e3b8c1c48 100644 --- a/gfx/2d/SourceSurfaceSkia.cpp +++ b/gfx/2d/SourceSurfaceSkia.cpp @@ -41,10 +41,10 @@ SourceSurfaceSkia::GetFormat() const } static sk_sp -MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride) +MakeSkData(void* aData, int32_t aHeight, size_t aStride) { CheckedInt size = aStride; - size *= aSize.height; + size *= aHeight; if (size.isValid()) { void* mem = sk_malloc_flags(size.value(), 0); if (mem) { @@ -57,13 +57,24 @@ MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride) return nullptr; } +static sk_sp +ReadSkImage(const sk_sp& aImage, const SkImageInfo& aInfo, size_t aStride) +{ + if (sk_sp data = MakeSkData(nullptr, aInfo.height(), aStride)) { + if (aImage->readPixels(aInfo, data->writable_data(), aStride, 0, 0, SkImage::kDisallow_CachingHint)) { + return SkImage::MakeRasterData(aInfo, data, aStride); + } + } + return nullptr; +} + bool SourceSurfaceSkia::InitFromData(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) { - sk_sp data = MakeSkData(aData, aSize, aStride); + sk_sp data = MakeSkData(aData, aSize.height, aStride); if (!data) { return false; } @@ -125,16 +136,12 @@ SourceSurfaceSkia::InitFromImage(const sk_sp& aImage, uint8_t* SourceSurfaceSkia::GetData() { + if (!mImage) { + return nullptr; + } #ifdef USE_SKIA_GPU if (mImage->isTextureBacked()) { - sk_sp raster; - if (sk_sp data = MakeSkData(nullptr, mSize, mStride)) { - SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat); - if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) { - raster = SkImage::MakeRasterData(info, data, mStride); - } - } - if (raster) { + if (sk_sp raster = ReadSkImage(mImage, MakeSkiaImageInfo(mSize, mFormat), mStride)) { mImage = raster; } else { gfxCriticalError() << "Failed making Skia raster image for GPU surface"; @@ -159,7 +166,7 @@ SourceSurfaceSkia::DrawTargetWillChange() // don't need to do anything for them here. SkPixmap pixmap; if (mImage->peekPixels(&pixmap)) { - mImage = SkImage::MakeRasterCopy(pixmap); + mImage = ReadSkImage(mImage, pixmap.info(), pixmap.rowBytes()); if (!mImage) { gfxCriticalError() << "Failed copying Skia raster snapshot"; } diff --git a/gfx/layers/SourceSurfaceSharedData.h b/gfx/layers/SourceSurfaceSharedData.h index 9a26ed26f5cd..d8557eb466b8 100644 --- a/gfx/layers/SourceSurfaceSharedData.h +++ b/gfx/layers/SourceSurfaceSharedData.h @@ -104,7 +104,8 @@ public: /** * Indicates the buffer is not expected to be shared with any more processes. - * May release the handle if possible (see CloseHandleInternal). */ + * May release the handle if possible (see CloseHandleInternal). + */ void FinishedSharing() { MutexAutoLock lock(mMutex); @@ -112,6 +113,17 @@ public: CloseHandleInternal(); } + /** + * Indicates whether or not the buffer can be shared with another process + * without reallocating. Note that this is racy and should only be used for + * informational/reporting purposes. + */ + bool CanShare() const + { + MutexAutoLock lock(mMutex); + return !mClosed; + } + /** * Allocate a new shared memory buffer so that we can get a new handle for * sharing to new processes. ShareToProcess must have failed with @@ -150,7 +162,7 @@ private: */ void CloseHandleInternal(); - Mutex mMutex; + mutable Mutex mMutex; int32_t mStride; int32_t mMapCount; IntSize mSize; diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla index b96826735f49..54262bc46cf6 100644 --- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -2,7 +2,7 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/. Our reference repository is https://github.com/khaledhosny/ots/. -Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb +Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb (5.1.0) Upstream files included: LICENSE, src/, include/ diff --git a/gfx/ots/sync.sh b/gfx/ots/sync.sh index 5a7e54b0a9d8..392e8af8208e 100755 --- a/gfx/ots/sync.sh +++ b/gfx/ots/sync.sh @@ -22,7 +22,8 @@ cp -r $1/include . echo "Updating README.mozilla..." REVISION=`cd $1; git log | head -1 | sed "s/commit //"` -sed -e "s/\(Current revision: \).*/\1$REVISION/" README.mozilla > README.tmp +VERSION=`cd $1; git describe | cut -d '-' -f 1 | sed 's/v//'` +sed -e "s/\(Current revision: \).*/\1$REVISION \($VERSION\)/" README.mozilla > README.tmp mv README.tmp README.mozilla echo "Applying ots-visibility.patch..." diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index d1c58a5b6804..4d903c87e806 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -461,6 +461,7 @@ private: DECL_GFX_PREF(Live, "layers.advanced.bullet-layers", LayersAllowBulletLayers, bool, false); DECL_GFX_PREF(Live, "layers.advanced.caret-layers", LayersAllowCaretLayers, bool, false); DECL_GFX_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, bool, false); + DECL_GFX_PREF(Live, "layers.advanced.outline-layers", LayersAllowOutlineLayers, bool, false); DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback", LayersAllowD3D9Fallback, bool, false); DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled", LayersAMDSwitchableGfxEnabled, bool, false); DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled", AsyncPanZoomEnabledDoNotUseDirectly, bool, true); diff --git a/image/AnimationSurfaceProvider.cpp b/image/AnimationSurfaceProvider.cpp index 6487ff35e6e9..5c34f21ba231 100644 --- a/image/AnimationSurfaceProvider.cpp +++ b/image/AnimationSurfaceProvider.cpp @@ -118,7 +118,8 @@ AnimationSurfaceProvider::LogicalSizeInBytes() const void AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, - size_t& aNonHeapSizeOut) + size_t& aNonHeapSizeOut, + size_t& aSharedHandlesOut) { // Note that the surface cache lock is already held here, and then we acquire // mFramesMutex. For this method, this ordering is unavoidable, which means @@ -126,7 +127,8 @@ AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, MutexAutoLock lock(mFramesMutex); for (const RawAccessFrameRef& frame : mFrames) { - frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut); + frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, + aNonHeapSizeOut, aSharedHandlesOut); } } diff --git a/image/AnimationSurfaceProvider.h b/image/AnimationSurfaceProvider.h index bf87f37ac4c9..b0e2213195a1 100644 --- a/image/AnimationSurfaceProvider.h +++ b/image/AnimationSurfaceProvider.h @@ -47,7 +47,8 @@ public: size_t LogicalSizeInBytes() const override; void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, - size_t& aNonHeapSizeOut) override; + size_t& aNonHeapSizeOut, + size_t& aSharedHandlesOut) override; protected: DrawableFrameRef DrawableRef(size_t aFrame) override; diff --git a/image/Decoder.cpp b/image/Decoder.cpp index edfe3dfaf3a9..6f6a2f3a50d8 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -335,7 +335,8 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum, NotNull> frame = WrapNotNull(new imgFrame()); bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA); if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat, - aPaletteDepth, nonPremult))) { + aPaletteDepth, nonPremult, + aFrameNum > 0))) { NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef(); } diff --git a/image/FrameAnimator.cpp b/image/FrameAnimator.cpp index d1cc0ab085a0..cf1e782a9b52 100644 --- a/image/FrameAnimator.cpp +++ b/image/FrameAnimator.cpp @@ -353,10 +353,11 @@ DoCollectSizeOfCompositingSurfaces(const RawAccessFrameRef& aSurface, SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType); // Extract the surface's memory usage information. - size_t heap = 0, nonHeap = 0; - aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap); + size_t heap = 0, nonHeap = 0, handles = 0; + aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap, handles); counter.Values().SetDecodedHeap(heap); counter.Values().SetDecodedNonHeap(nonHeap); + counter.Values().SetSharedHandles(handles); // Record it. aCounters.AppendElement(counter); diff --git a/image/ISurfaceProvider.h b/image/ISurfaceProvider.h index 3b0aa3710588..e98afe229d94 100644 --- a/image/ISurfaceProvider.h +++ b/image/ISurfaceProvider.h @@ -64,14 +64,16 @@ public: /// implementation is appropriate for static ISurfaceProviders. virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, - size_t& aNonHeapSizeOut) + size_t& aNonHeapSizeOut, + size_t& aSharedHandlesOut) { DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0); if (!ref) { return; } - ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut); + ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, + aNonHeapSizeOut, aSharedHandlesOut); } /// @return the availability state of this ISurfaceProvider, which indicates diff --git a/image/Image.h b/image/Image.h index 8656b569f78c..5efa957ef4b3 100644 --- a/image/Image.h +++ b/image/Image.h @@ -33,6 +33,7 @@ struct MemoryCounter : mSource(0) , mDecodedHeap(0) , mDecodedNonHeap(0) + , mSharedHandles(0) { } void SetSource(size_t aCount) { mSource = aCount; } @@ -41,12 +42,15 @@ struct MemoryCounter size_t DecodedHeap() const { return mDecodedHeap; } void SetDecodedNonHeap(size_t aCount) { mDecodedNonHeap = aCount; } size_t DecodedNonHeap() const { return mDecodedNonHeap; } + void SetSharedHandles(size_t aCount) { mSharedHandles = aCount; } + size_t SharedHandles() const { return mSharedHandles; } MemoryCounter& operator+=(const MemoryCounter& aOther) { mSource += aOther.mSource; mDecodedHeap += aOther.mDecodedHeap; mDecodedNonHeap += aOther.mDecodedNonHeap; + mSharedHandles += aOther.mSharedHandles; return *this; } @@ -54,6 +58,7 @@ private: size_t mSource; size_t mDecodedHeap; size_t mDecodedNonHeap; + size_t mSharedHandles; }; enum class SurfaceMemoryCounterType diff --git a/image/SurfaceCache.cpp b/image/SurfaceCache.cpp index dd8982ce97f2..5e7bed4a5015 100644 --- a/image/SurfaceCache.cpp +++ b/image/SurfaceCache.cpp @@ -197,10 +197,12 @@ public: // for surfaces with PlaybackType::eAnimated.) size_t heap = 0; size_t nonHeap = 0; + size_t handles = 0; aCachedSurface->mProvider - ->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap); + ->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap, handles); counter.Values().SetDecodedHeap(heap); counter.Values().SetDecodedNonHeap(nonHeap); + counter.Values().SetSharedHandles(handles); mCounters.AppendElement(counter); } diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index db31c04989da..f5d6c675759c 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -932,7 +932,8 @@ imgFrame::SetCompositingFailed(bool val) void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, - size_t& aNonHeapSizeOut) const + size_t& aNonHeapSizeOut, + size_t& aSharedHandlesOut) const { MonitorAutoLock lock(mMonitor); @@ -949,6 +950,14 @@ imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, aHeapSizeOut += aMallocSizeOf(mRawSurface); mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut); + + if (mRawSurface->GetType() == SurfaceType::DATA_SHARED) { + auto sharedSurface = + static_cast(mRawSurface.get()); + if (sharedSurface->CanShare()) { + ++aSharedHandlesOut; + } + } } } diff --git a/image/imgFrame.h b/image/imgFrame.h index b940777fa81e..34d19447620a 100644 --- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -348,7 +348,8 @@ public: already_AddRefed GetSourceSurface(); void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut, - size_t& aNonHeapSizeOut) const; + size_t& aNonHeapSizeOut, + size_t& aSharedHandlesOut) const; private: // methods diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp index 68facd72c51c..8daee793674c 100644 --- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -280,6 +280,11 @@ private: surfacePathPrefix.Append("x"); surfacePathPrefix.AppendInt(counter.Key().Size().height); + if (counter.Values().SharedHandles() > 0) { + surfacePathPrefix.Append(", shared:"); + surfacePathPrefix.AppendInt(uint32_t(counter.Values().SharedHandles())); + } + if (counter.Type() == SurfaceMemoryCounterType::NORMAL) { PlaybackType playback = counter.Key().Playback(); surfacePathPrefix.Append(playback == PlaybackType::eAnimated diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index f710a95863be..298c273cbbbb 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -84,8 +84,8 @@ ReportBadArrayType(JSContext* cx) static bool ReportOutOfRange(JSContext* cx) { - // Use JSMSG_BAD_INDEX here even if it is generic, since that is - // the message used by NonStandardToIndex for its initial range checking. + // Use JSMSG_BAD_INDEX here, it is what ToIndex uses for some cases that it + // reports directly. JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); return false; } @@ -115,7 +115,7 @@ static bool GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle view, uint32_t* offset) { uint64_t index; - if (!NonStandardToIndex(cx, v, &index)) + if (!ToIndex(cx, v, &index)) return false; if (index >= view->length()) return ReportOutOfRange(cx); diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js index ced9630e9f1f..c9fafbcbd916 100644 --- a/js/src/builtin/Module.js +++ b/js/src/builtin/Module.js @@ -65,12 +65,12 @@ function ModuleGetExportedNames(exportStarSet = []) return exportedNames; } -// 15.2.1.16.3 ResolveExport(exportName, resolveSet, exportStarSet) -function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) +// 15.2.1.16.3 ResolveExport(exportName, resolveSet) +function ModuleResolveExport(exportName, resolveSet = []) { if (!IsObject(this) || !IsModule(this)) { return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet, - exportStarSet, "ModuleResolveExport"); + "ModuleResolveExport"); } // Step 1 @@ -100,38 +100,29 @@ function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) let e = indirectExportEntries[i]; if (exportName === e.exportName) { let importedModule = CallModuleResolveHook(module, e.moduleRequest, - MODULE_STATE_INSTANTIATED); - let indirectResolution = callFunction(importedModule.resolveExport, importedModule, - e.importName, resolveSet, exportStarSet); - if (indirectResolution !== null) - return indirectResolution; + MODULE_STATE_PARSED); + return callFunction(importedModule.resolveExport, importedModule, e.importName, + resolveSet); } } // Step 6 if (exportName === "default") { // A default export cannot be provided by an export *. - ThrowSyntaxError(JSMSG_BAD_DEFAULT_EXPORT); + return null; } // Step 7 - if (callFunction(ArrayIncludes, exportStarSet, module)) - return null; - - // Step 8 - _DefineDataProperty(exportStarSet, exportStarSet.length, module); - - // Step 9 let starResolution = null; - // Step 10 + // Step 8 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; let importedModule = CallModuleResolveHook(module, e.moduleRequest, - MODULE_STATE_INSTANTIATED); + MODULE_STATE_PARSED); let resolution = callFunction(importedModule.resolveExport, importedModule, - exportName, resolveSet, exportStarSet); + exportName, resolveSet); if (resolution === "ambiguous") return resolution; @@ -148,6 +139,7 @@ function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) } } + // Step 9 return starResolution; } @@ -213,8 +205,8 @@ function GetModuleEnvironment(module) function RecordInstantationFailure(module) { - // Set the module's environment slot to 'null' to indicate a failed module - // instantiation. + // Set the module's state to 'failed' to indicate a failed module + // instantiation and reset the environment slot to 'undefined'. assert(IsModule(module), "Non-module passed to RecordInstantationFailure"); SetModuleState(module, MODULE_STATE_FAILED); UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined); @@ -275,11 +267,13 @@ function ModuleDeclarationInstantiation() ThrowSyntaxError(JSMSG_MISSING_IMPORT, imp.importName); if (resolution === "ambiguous") ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT, imp.importName); + if (resolution.module.state < MODULE_STATE_INSTANTIATED) + ThrowInternalError(JSMSG_BAD_MODULE_STATE); CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName); } } - // Step 16.iv + // Step 17.a.iii InstantiateModuleFunctionDeclarations(module); } catch (e) { RecordInstantationFailure(module); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index dc9cf3bd884d..5f15014d30da 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -994,7 +994,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle global) static const JSFunctionSpec protoFunctions[] = { JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0), - JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 3, 0), + JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0), JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0), JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0), JS_FS_END diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 7e92b89b90de..1ca950e9e92b 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -160,6 +160,7 @@ function isSuppressedVirtualMethod(csu, method) var ignoreFunctions = { "ptio.c:pt_MapError" : true, "je_malloc_printf" : true, + "malloc_usable_size" : true, "vprintf_stderr" : true, "PR_ExplodeTime" : true, "PR_ErrorInstallTable" : true, diff --git a/js/src/ds/Bitmap.cpp b/js/src/ds/Bitmap.cpp new file mode 100644 index 000000000000..4cc698c3e375 --- /dev/null +++ b/js/src/ds/Bitmap.cpp @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ds/Bitmap.h" + +using namespace js; + +SparseBitmap::~SparseBitmap() +{ + if (data.initialized()) { + for (Data::Range r(data.all()); !r.empty(); r.popFront()) + js_delete(r.front().value()); + } +} + +size_t +SparseBitmap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) +{ + size_t size = data.sizeOfExcludingThis(mallocSizeOf); + for (Data::Range r(data.all()); !r.empty(); r.popFront()) + size += mallocSizeOf(r.front().value()); + return size; +} + +SparseBitmap::BitBlock& +SparseBitmap::getOrCreateBlock(size_t blockId) +{ + Data::AddPtr p = data.lookupForAdd(blockId); + if (!p) { + AutoEnterOOMUnsafeRegion oomUnsafe; + BitBlock* block = js_new(); + if (!block || !data.add(p, blockId, block)) + oomUnsafe.crash("Bitmap OOM"); + PodZero(block); + } + return *p->value(); +} + +void +SparseBitmap::setBit(size_t bit) +{ + size_t word = bit / JS_BITS_PER_WORD; + size_t blockWord = blockStartWord(word); + BitBlock& block = getOrCreateBlock(blockWord / WordsInBlock); + block[word - blockWord] |= uintptr_t(1) << (bit % JS_BITS_PER_WORD); +} + +SparseBitmap::BitBlock* +SparseBitmap::getBlock(size_t blockId) const +{ + Data::Ptr p = data.lookup(blockId); + return p ? p->value() : nullptr; +} + +bool +SparseBitmap::getBit(size_t bit) const +{ + size_t word = bit / JS_BITS_PER_WORD; + size_t blockWord = blockStartWord(word); + + BitBlock* block = getBlock(blockWord / WordsInBlock); + if (block) + return (*block)[word - blockWord] & (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); + return false; +} + +void +SparseBitmap::bitwiseAndWith(const DenseBitmap& other) +{ + for (Data::Enum e(data); !e.empty(); e.popFront()) { + BitBlock& block = *e.front().value(); + size_t blockWord = e.front().key() * WordsInBlock; + bool anySet = false; + size_t numWords = wordIntersectCount(blockWord, other); + for (size_t i = 0; i < numWords; i++) { + block[i] &= other.word(blockWord + i); + anySet |= !!block[i]; + } + if (!anySet) { + js_delete(&block); + e.removeFront(); + } + } +} + +void +SparseBitmap::bitwiseOrWith(const SparseBitmap& other) +{ + for (Data::Range r(other.data.all()); !r.empty(); r.popFront()) { + const BitBlock& otherBlock = *r.front().value(); + BitBlock& block = getOrCreateBlock(r.front().key()); + for (size_t i = 0; i < WordsInBlock; i++) + block[i] |= otherBlock[i]; + } +} + +void +SparseBitmap::bitwiseOrInto(DenseBitmap& other) const +{ + for (Data::Range r(data.all()); !r.empty(); r.popFront()) { + BitBlock& block = *r.front().value(); + size_t blockWord = r.front().key() * WordsInBlock; + size_t numWords = wordIntersectCount(blockWord, other); +#ifdef DEBUG + // Any words out of range in other should be zero in this bitmap. + for (size_t i = numWords; i < WordsInBlock; i++) + MOZ_ASSERT(!block[i]); +#endif + for (size_t i = 0; i < numWords; i++) + other.word(blockWord + i) |= block[i]; + } +} + +void +SparseBitmap::bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const +{ + size_t blockWord = blockStartWord(wordStart); + + // We only support using a single bit block in this API. + MOZ_ASSERT(numWords && (blockWord == blockStartWord(wordStart + numWords - 1))); + + BitBlock* block = getBlock(blockWord / WordsInBlock); + if (block) { + for (size_t i = 0; i < numWords; i++) + target[i] |= (*block)[wordStart - blockWord + i]; + } +} diff --git a/js/src/ds/Bitmap.h b/js/src/ds/Bitmap.h new file mode 100644 index 000000000000..2b0239e8b66a --- /dev/null +++ b/js/src/ds/Bitmap.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ds_Bitmap_h +#define ds_Bitmap_h + +#include + +#include "jsalloc.h" + +#include "js/HashTable.h" +#include "js/Vector.h" + +// This file provides two classes for representing bitmaps. +// +// DenseBitmap is an array of words of bits, with size linear in the maximum +// bit which has been set on it. +// +// SparseBitmap provides a reasonably simple, reasonably efficient (in time and +// space) implementation of a sparse bitmap. The basic representation is a hash +// table whose entries are fixed length malloc'ed blocks of bits. + +namespace js { + +class DenseBitmap +{ + typedef Vector Data; + Data data; + + public: + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { + return data.sizeOfExcludingThis(mallocSizeOf); + } + + bool ensureSpace(size_t numWords) { + MOZ_ASSERT(data.empty()); + return data.appendN(0, numWords); + } + + size_t numWords() const { return data.length(); } + uintptr_t word(size_t i) const { return data[i]; } + uintptr_t& word(size_t i) { return data[i]; } + + void copyBitsFrom(size_t wordStart, size_t numWords, uintptr_t* source) { + MOZ_ASSERT(wordStart + numWords <= data.length()); + mozilla::PodCopy(&data[wordStart], source, numWords); + } + + void bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const { + for (size_t i = 0; i < numWords; i++) + target[i] |= data[wordStart + i]; + } +}; + +class SparseBitmap +{ + // The number of words of bits to use for each block mainly affects the + // memory usage of the bitmap. To minimize overhead, bitmaps which are + // expected to be fairly dense should have a large block size, and bitmaps + // which are expected to be very sparse should have a small block size. + static const size_t WordsInBlock = 4096 / sizeof(uintptr_t); + + typedef mozilla::Array BitBlock; + typedef HashMap, SystemAllocPolicy> Data; + Data data; + + static size_t blockStartWord(size_t word) { + return word & ~(WordsInBlock - 1); + } + + // Return the number of words in a BitBlock starting at |blockWord| which + // are in |other|. + static size_t wordIntersectCount(size_t blockWord, const DenseBitmap& other) { + long count = other.numWords() - blockWord; + return std::min((size_t)WordsInBlock, std::max(count, 0)); + } + + BitBlock* getBlock(size_t blockId) const; + BitBlock& getOrCreateBlock(size_t blockId); + + public: + bool init() { return data.init(); } + ~SparseBitmap(); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); + + void setBit(size_t bit); + bool getBit(size_t bit) const; + + void bitwiseAndWith(const DenseBitmap& other); + void bitwiseOrWith(const SparseBitmap& other); + void bitwiseOrInto(DenseBitmap& other) const; + + // Currently, this API only supports a range of words that is in a single bit block. + void bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const; +}; + +} // namespace js + +#endif // ds_Bitmap_h diff --git a/js/src/ds/PageProtectingVector.h b/js/src/ds/PageProtectingVector.h index acd1440cb967..cf3bcd47b18a 100644 --- a/js/src/ds/PageProtectingVector.h +++ b/js/src/ds/PageProtectingVector.h @@ -8,13 +8,27 @@ #define ds_PageProtectingVector_h #include "mozilla/Atomics.h" +#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/PodOperations.h" +#include "mozilla/Types.h" #include "mozilla/Vector.h" +#ifdef MALLOC_H +# include MALLOC_H +#endif + #include "ds/MemoryProtectionExceptionHandler.h" #include "gc/Memory.h" #include "js/Utility.h" +#ifdef MOZ_MEMORY +# ifdef XP_DARWIN +# define malloc_usable_size malloc_size +# else +extern "C" MFBT_API size_t malloc_usable_size(MALLOC_USABLE_SIZE_CONST_PTR void* p); +# endif +#endif + namespace js { /* @@ -455,23 +469,82 @@ PageProtectingVector::appendSlow(const U* values, size_t class ProtectedReallocPolicy { + uintptr_t currAddr; + size_t currSize; + uintptr_t prevAddr; + size_t prevSize; + + template void update(T* newAddr, size_t newSize) { + prevAddr = currAddr; + prevSize = currSize; + currAddr = uintptr_t(newAddr); + currSize = newSize * sizeof(T); + } + + template void updateIfValid(T* newAddr, size_t newSize) { + if (newAddr) + update(newAddr, newSize); + } + + template T* reallocUpdate(T* oldAddr, size_t oldSize, size_t newSize) { + T* newAddr = js_pod_realloc(oldAddr, oldSize, newSize); + updateIfValid(newAddr, newSize); + return newAddr; + } + public: + ProtectedReallocPolicy() : currAddr(0), currSize(0), prevAddr(0), prevSize(0) {} + + ~ProtectedReallocPolicy() { + MOZ_RELEASE_ASSERT(!currSize && !currAddr); + } + template T* maybe_pod_malloc(size_t numElems) { - return js_pod_malloc(numElems); + MOZ_RELEASE_ASSERT(!currSize && !currAddr); + T* addr = js_pod_malloc(numElems); + updateIfValid(addr, numElems); + return addr; } + template T* maybe_pod_calloc(size_t numElems) { - return js_pod_calloc(numElems); + MOZ_RELEASE_ASSERT(!currSize && !currAddr); + T* addr = js_pod_calloc(numElems); + updateIfValid(addr, numElems); + return addr; } + template T* maybe_pod_realloc(T* oldAddr, size_t oldSize, size_t newSize) { + if (uintptr_t(oldAddr) != currAddr) { + MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: oldAddr and currAddr don't match " + "(0x%" PRIx64 " != 0x%" PRIx64 ", %" PRIu64 ")!", + uint64_t(oldAddr), uint64_t(currAddr), uint64_t(currSize)); + } + if (oldSize * sizeof(T) != currSize) { + MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: oldSize and currSize don't match " + "(%" PRIu64 " != %" PRIu64 ", 0x%" PRIx64 ")!", + uint64_t(oldSize * sizeof(T)), uint64_t(currSize), + uint64_t(currAddr)); + } + MOZ_ASSERT_IF(oldAddr, oldSize); if (MOZ_UNLIKELY(!newSize)) return nullptr; if (MOZ_UNLIKELY(!oldAddr)) - return js_pod_malloc(newSize); + return maybe_pod_malloc(newSize); + +#ifdef MOZ_MEMORY + size_t usableSize = malloc_usable_size(oldAddr); + if (usableSize < currSize) { + MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: usableSize < currSize " + "(%" PRIu64 " < %" PRIu64 ", %" PRIu64 ", %s)!", + uint64_t(usableSize), uint64_t(currSize), + uint64_t(prevSize), prevAddr == currAddr ? "true" : "false"); + } +#endif T* tmpAddr = js_pod_malloc(newSize); if (MOZ_UNLIKELY(!tmpAddr)) - return js_pod_realloc(oldAddr, oldSize, newSize); + return reallocUpdate(oldAddr, oldSize, newSize); size_t bytes = (newSize >= oldSize ? oldSize : newSize) * sizeof(T); memcpy(tmpAddr, oldAddr, bytes); @@ -479,19 +552,27 @@ class ProtectedReallocPolicy T* newAddr = js_pod_realloc(oldAddr, oldSize, newSize); if (MOZ_UNLIKELY(!newAddr)) { js_free(tmpAddr); - return js_pod_realloc(oldAddr, oldSize, newSize); + return reallocUpdate(oldAddr, oldSize, newSize); } const uint8_t* newAddrBytes = reinterpret_cast(newAddr); const uint8_t* tmpAddrBytes = reinterpret_cast(tmpAddr); if (!mozilla::PodEqual(tmpAddrBytes, newAddrBytes, bytes)) { - if (oldAddr == newAddr) - MOZ_CRASH("New buffer doesn't match the old buffer (newAddr == oldAddr)!"); - else - MOZ_CRASH("New buffer doesn't match the old buffer (newAddr != oldAddr)!"); +#ifdef MOZ_MEMORY + MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: buffers don't match " + "(%" PRIu64 " >= %" PRIu64 ", %" PRIu64 ", %s)!", + uint64_t(usableSize), uint64_t(currSize), + uint64_t(prevSize), prevAddr == currAddr ? "true" : "false"); +#else + MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: buffers don't match " + "(%" PRIu64 ", %" PRIu64 ", %s)!", + uint64_t(currSize), uint64_t(prevSize), + prevAddr == currAddr ? "true" : "false"); +#endif } js_free(tmpAddr); + update(newAddr, newSize); return newAddr; } @@ -500,7 +581,22 @@ class ProtectedReallocPolicy template T* pod_realloc(T* p, size_t oldSize, size_t newSize) { return maybe_pod_realloc(p, oldSize, newSize); } - void free_(void* p) { js_free(p); } + + void free_(void* p) { + MOZ_RELEASE_ASSERT(uintptr_t(p) == currAddr); +#ifdef MOZ_MEMORY + size_t usableSize = malloc_usable_size(p); + if (usableSize < currSize) { + MOZ_CRASH_UNSAFE_PRINTF("free_: usableSize < currSize " + "(%" PRIu64 " < %" PRIu64 ", %" PRIu64 ", %s)!", + uint64_t(usableSize), uint64_t(currSize), + uint64_t(prevSize), prevAddr == currAddr ? "true" : "false"); + } +#endif + js_free(p); + update(0, 0); + } + void reportAllocOverflow() const {} bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); diff --git a/js/src/gc/AtomMarking.cpp b/js/src/gc/AtomMarking.cpp index ae01e2a8bb8c..affb78d4c264 100644 --- a/js/src/gc/AtomMarking.cpp +++ b/js/src/gc/AtomMarking.cpp @@ -44,29 +44,6 @@ namespace gc { // bits for space between things being unused when things are larger than a // single Cell. -static inline void -SetBit(uintptr_t* bitmap, size_t bit) -{ - bitmap[bit / JS_BITS_PER_WORD] |= uintptr_t(1) << (bit % JS_BITS_PER_WORD); -} - -static inline bool -GetBit(uintptr_t* bitmap, size_t bit) -{ - return bitmap[bit / JS_BITS_PER_WORD] & (uintptr_t(1) << (bit % JS_BITS_PER_WORD)); -} - -static inline bool -EnsureBitmapLength(AtomMarkingRuntime::Bitmap& bitmap, size_t nwords) -{ - if (nwords > bitmap.length()) { - size_t needed = nwords - bitmap.length(); - if (needed) - return bitmap.appendN(0, needed); - } - return true; -} - void AtomMarkingRuntime::registerArena(Arena* arena) { @@ -98,12 +75,11 @@ AtomMarkingRuntime::unregisterArena(Arena* arena) } bool -AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap) +AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap) { MOZ_ASSERT(runtime->currentThreadHasExclusiveAccess()); - MOZ_ASSERT(bitmap.empty()); - if (!EnsureBitmapLength(bitmap, allocatedWords)) + if (!bitmap.ensureSpace(allocatedWords)) return false; Zone* atomsZone = runtime->unsafeAtomsCompartment()->zone(); @@ -111,8 +87,7 @@ AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& b for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) { Arena* arena = aiter.get(); uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena); - uintptr_t* bitmapWords = &bitmap[arena->atomBitmapStart()]; - mozilla::PodCopy(bitmapWords, chunkWords, ArenaBitmapWords); + bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords); } } @@ -120,25 +95,21 @@ AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& b } void -AtomMarkingRuntime::updateZoneBitmap(Zone* zone, const Bitmap& bitmap) +AtomMarkingRuntime::updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap) { if (zone->isAtomsZone()) return; - // |bitmap| was produced by computeBitmapFromChunkMarkBits, so it should - // have the maximum possible size. - MOZ_ASSERT(zone->markedAtoms().length() <= bitmap.length()); - // Take the bitwise and between the two mark bitmaps to get the best new // overapproximation we can. |bitmap| might include bits that are not in // the zone's mark bitmap, if additional zones were collected by the GC. - for (size_t i = 0; i < zone->markedAtoms().length(); i++) - zone->markedAtoms()[i] &= bitmap[i]; + zone->markedAtoms().bitwiseAndWith(bitmap); } // Set any bits in the chunk mark bitmaps for atoms which are marked in bitmap. +template static void -AddBitmapToChunkMarkBits(JSRuntime* runtime, AtomMarkingRuntime::Bitmap& bitmap) +AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap) { // Make sure that by copying the mark bits for one arena in word sizes we // do not affect the mark bits for other arenas. @@ -150,16 +121,7 @@ AddBitmapToChunkMarkBits(JSRuntime* runtime, AtomMarkingRuntime::Bitmap& bitmap) for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) { Arena* arena = aiter.get(); uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena); - - // The bitmap might not be long enough, in which case remaining - // bits are implicitly zero. - if (bitmap.length() <= arena->atomBitmapStart()) - continue; - MOZ_ASSERT(bitmap.length() >= arena->atomBitmapStart() + ArenaBitmapWords); - - uintptr_t* bitmapWords = &bitmap[arena->atomBitmapStart()]; - for (size_t i = 0; i < ArenaBitmapWords; i++) - chunkWords[i] |= bitmapWords[i]; + bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords); } } } @@ -172,17 +134,14 @@ AtomMarkingRuntime::updateChunkMarkBits(JSRuntime* runtime) // Try to compute a simple union of the zone atom bitmaps before updating // the chunk mark bitmaps. If this allocation fails then fall back to // updating the chunk mark bitmaps separately for each zone. - Bitmap markedUnion; - if (EnsureBitmapLength(markedUnion, allocatedWords)) { + DenseBitmap markedUnion; + if (markedUnion.ensureSpace(allocatedWords)) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { // We only need to update the chunk mark bits for zones which were // not collected in the current GC. Atoms which are referenced by // collected zones have already been marked. - if (!zone->isCollectingFromAnyThread()) { - MOZ_ASSERT(zone->markedAtoms().length() <= allocatedWords); - for (size_t i = 0; i < zone->markedAtoms().length(); i++) - markedUnion[i] |= zone->markedAtoms()[i]; - } + if (!zone->isCollectingFromAnyThread()) + zone->markedAtoms().bitwiseOrInto(markedUnion); } AddBitmapToChunkMarkBits(runtime, markedUnion); } else { @@ -225,14 +184,9 @@ AtomMarkingRuntime::markAtom(JSContext* cx, TenuredCell* thing) return; size_t bit = GetAtomBit(thing); + MOZ_ASSERT(bit / JS_BITS_PER_WORD < allocatedWords); - { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!EnsureBitmapLength(cx->zone()->markedAtoms(), allocatedWords)) - oomUnsafe.crash("Atom bitmap OOM"); - } - - SetBit(cx->zone()->markedAtoms().begin(), bit); + cx->zone()->markedAtoms().setBit(bit); if (!cx->helperThread()) { // Trigger a read barrier on the atom, in case there is an incremental @@ -272,18 +226,7 @@ void AtomMarkingRuntime::adoptMarkedAtoms(Zone* target, Zone* source) { MOZ_ASSERT(target->runtimeFromAnyThread()->currentThreadHasExclusiveAccess()); - - Bitmap* targetBitmap = &target->markedAtoms(); - Bitmap* sourceBitmap = &source->markedAtoms(); - if (targetBitmap->length() < sourceBitmap->length()) - std::swap(targetBitmap, sourceBitmap); - for (size_t i = 0; i < sourceBitmap->length(); i++) - (*targetBitmap)[i] |= (*sourceBitmap)[i]; - - if (targetBitmap != &target->markedAtoms()) - target->markedAtoms() = Move(source->markedAtoms()); - else - source->markedAtoms().clear(); + target->markedAtoms().bitwiseOrWith(source->markedAtoms()); } #ifdef DEBUG @@ -309,9 +252,7 @@ AtomMarkingRuntime::atomIsMarked(Zone* zone, Cell* thingArg) } size_t bit = GetAtomBit(thing); - if (bit >= zone->markedAtoms().length() * JS_BITS_PER_WORD) - return false; - return GetBit(zone->markedAtoms().begin(), bit); + return zone->markedAtoms().getBit(bit); } bool diff --git a/js/src/gc/AtomMarking.h b/js/src/gc/AtomMarking.h index c1cccd7f4067..f0d24b9b4547 100644 --- a/js/src/gc/AtomMarking.h +++ b/js/src/gc/AtomMarking.h @@ -8,6 +8,7 @@ #define gc_AtomMarking_h #include "NamespaceImports.h" +#include "ds/Bitmap.h" #include "gc/Heap.h" #include "threading/ProtectedData.h" @@ -21,13 +22,11 @@ class AtomMarkingRuntime // Unused arena atom bitmap indexes. Protected by the GC lock. js::ExclusiveAccessLockOrGCTaskData> freeArenaIndexes; + public: // The extent of all allocated and free words in atom mark bitmaps. // This monotonically increases and may be read from without locking. mozilla::Atomic allocatedWords; - public: - typedef Vector Bitmap; - AtomMarkingRuntime() : allocatedWords(0) {} @@ -41,11 +40,11 @@ class AtomMarkingRuntime // Fill |bitmap| with an atom marking bitmap based on the things that are // currently marked in the chunks used by atoms zone arenas. This returns // false on an allocation failure (but does not report an exception). - bool computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap); + bool computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap); // Update the atom marking bitmap in |zone| according to another // overapproximation of the reachable atoms in |bitmap|. - void updateZoneBitmap(Zone* zone, const Bitmap& bitmap); + void updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap); // Set any bits in the chunk mark bitmaps for atoms which are marked in any // zone in the runtime. diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index b82c4d1d244a..0aa34dcb4637 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -596,6 +596,56 @@ typedef HashMap, SystemAllocPolicy> R using AllocKinds = mozilla::EnumSet; +template +class MemoryCounter +{ + // Bytes counter to measure memory pressure for GC scheduling. It runs + // from maxBytes down to zero. + mozilla::Atomic bytes_; + + // GC trigger threshold for memory allocations. + js::ActiveThreadData maxBytes_; + + // Whether a GC has been triggered as a result of bytes falling below + // zero. + // + // This should be a bool, but Atomic only supports 32-bit and pointer-sized + // types. + mozilla::Atomic triggered_; + + public: + MemoryCounter() + : bytes_(0), + maxBytes_(0), + triggered_(false) + { } + + void reset() { + bytes_ = maxBytes_; + triggered_ = false; + } + + void setMax(size_t newMax) { + // For compatibility treat any value that exceeds PTRDIFF_T_MAX to + // mean that value. + maxBytes_ = (ptrdiff_t(newMax) >= 0) ? newMax : size_t(-1) >> 1; + reset(); + } + + bool update(T* owner, size_t bytes) { + bytes_ -= ptrdiff_t(bytes); + if (MOZ_UNLIKELY(isTooMuchMalloc())) { + if (!triggered_) + triggered_ = owner->triggerGCForTooMuchMalloc(); + } + return triggered_; + } + + ptrdiff_t bytes() const { return bytes_; } + size_t maxBytes() const { return maxBytes_; } + bool isTooMuchMalloc() const { return bytes_ <= 0; } +}; + class GCRuntime { public: @@ -667,8 +717,6 @@ class GCRuntime void setDeterministic(bool enable); #endif - size_t maxMallocBytesAllocated() { return maxMallocBytes; } - uint64_t nextCellUniqueId() { MOZ_ASSERT(nextCellUniqueId_ > 0); uint64_t uid = ++nextCellUniqueId_; @@ -725,12 +773,13 @@ class GCRuntime MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data); void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data); + bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); } + int32_t getMallocBytes() const { return mallocCounter.bytes(); } + size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); } + bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); } + void resetMallocBytes() { mallocCounter.reset(); } void setMaxMallocBytes(size_t value); - int32_t getMallocBytes() const { return mallocBytesUntilGC; } - void resetMallocBytes(); - bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; } void updateMallocCounter(JS::Zone* zone, size_t nbytes); - void onTooMuchMalloc(); void setGCCallback(JSGCCallback callback, void* data); void callGCCallback(JSGCStatus status) const; @@ -1031,8 +1080,6 @@ class GCRuntime ActiveThreadData rootsHash; - ActiveThreadData maxMallocBytes; - // An incrementing id used to assign unique ids to cells that require one. mozilla::Atomic nextCellUniqueId_; @@ -1248,17 +1295,7 @@ class GCRuntime CallbackVector updateWeakPointerZoneGroupCallbacks; CallbackVector updateWeakPointerCompartmentCallbacks; - /* - * Malloc counter to measure memory pressure for GC scheduling. It runs - * from maxMallocBytes down to zero. - */ - mozilla::Atomic mallocBytesUntilGC; - - /* - * Whether a GC has been triggered as a result of mallocBytesUntilGC - * falling below zero. - */ - mozilla::Atomic mallocGCTriggered; + MemoryCounter mallocCounter; /* * The trace operations to trace embedding-specific GC roots. One is for diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index 228380f4740b..b83950ece6cc 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -20,10 +20,10 @@ using namespace js; using namespace js::gc; static void -IterateCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback) +IterateCompartmentsArenasCellsUnbarriered(JSContext* cx, Zone* zone, void* data, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) (*compartmentCallback)(cx, data, comp); @@ -35,40 +35,40 @@ IterateCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data, for (ArenaIter aiter(zone, thingKind); !aiter.done(); aiter.next()) { Arena* arena = aiter.get(); (*arenaCallback)(cx->runtime(), data, arena, traceKind, thingSize); - for (ArenaCellIter iter(arena); !iter.done(); iter.next()) + for (ArenaCellIterUnbarriered iter(arena); !iter.done(); iter.next()) (*cellCallback)(cx->runtime(), data, iter.getCell(), traceKind, thingSize); } } } void -js::IterateZonesCompartmentsArenasCells(JSContext* cx, void* data, - IterateZoneCallback zoneCallback, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback) +js::IterateHeapUnbarriered(JSContext* cx, void* data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) { AutoPrepareForTracing prop(cx, WithAtoms); for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { (*zoneCallback)(cx->runtime(), data, zone); - IterateCompartmentsArenasCells(cx, zone, data, - compartmentCallback, arenaCallback, cellCallback); + IterateCompartmentsArenasCellsUnbarriered(cx, zone, data, + compartmentCallback, arenaCallback, cellCallback); } } void -js::IterateZoneCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data, - IterateZoneCallback zoneCallback, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback) +js::IterateHeapUnbarrieredForZone(JSContext* cx, Zone* zone, void* data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) { AutoPrepareForTracing prop(cx, WithAtoms); (*zoneCallback)(cx->runtime(), data, zone); - IterateCompartmentsArenasCells(cx, zone, data, - compartmentCallback, arenaCallback, cellCallback); + IterateCompartmentsArenasCellsUnbarriered(cx, zone, data, + compartmentCallback, arenaCallback, cellCallback); } void diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index ff303567dfee..575e83889339 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -8,6 +8,7 @@ # include #endif +#include "mozilla/DebugOnly.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Sprintf.h" @@ -302,7 +303,7 @@ CheckEdgeTracer::onChild(const JS::GCCellPtr& thing) void js::gc::AssertSafeToSkipBarrier(TenuredCell* thing) { - Zone* zone = thing->zoneFromAnyThread(); + mozilla::DebugOnly zone = thing->zoneFromAnyThread(); MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone()); } @@ -531,6 +532,10 @@ CheckHeapTracer::onChild(const JS::GCCellPtr& thing) return; } + // Don't trace into GC things owned by another runtime. + if (cell->runtimeFromAnyThread() != rt) + return; + WorkItem item(thing, contextName(), parentIndex); if (!stack.append(item)) oom = true; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index abbbe78f0381..f815442fd867 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -39,9 +39,6 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group) gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()), gcZoneGroupEdges_(group), typeDescrObjects_(group, this, SystemAllocPolicy()), - gcMallocBytes(0), - gcMaxMallocBytes(0), - gcMallocGCTriggered(false), markedAtoms_(group), usage(&rt->gc.usage), threshold(), @@ -69,6 +66,7 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group) AutoLockGC lock(rt); threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock); setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9); + jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8); } Zone::~Zone() @@ -93,7 +91,8 @@ bool Zone::init(bool isSystemArg) return uniqueIds().init() && gcZoneGroupEdges().init() && gcWeakKeys().init() && - typeDescrObjects().init(); + typeDescrObjects().init() && + markedAtoms().init(); } void @@ -110,33 +109,6 @@ Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit) needsIncrementalBarrier_ = needs; } -void -Zone::resetGCMallocBytes() -{ - gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); - gcMallocGCTriggered = false; -} - -void -Zone::setGCMaxMallocBytes(size_t value) -{ - /* - * For compatibility treat any value that exceeds PTRDIFF_T_MAX to - * mean that value. - */ - gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; - resetGCMallocBytes(); -} - -void -Zone::onTooMuchMalloc() -{ - if (!gcMallocGCTriggered) { - GCRuntime& gc = runtimeFromAnyThread()->gc; - gcMallocGCTriggered = gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC); - } -} - void Zone::beginSweepTypes(FreeOp* fop, bool releaseTypes) { diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 9bd09c1e0f57..15f234a00d5c 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -166,16 +166,6 @@ struct Zone : public JS::shadow::Zone, size_t* shapeTables, size_t* atomsMarkBitmaps); - void resetGCMallocBytes(); - void setGCMaxMallocBytes(size_t value); - void updateMallocCounter(size_t nbytes) { - // Note: this code may be run from worker threads. We tolerate any - // thread races when updating gcMallocBytes. - gcMallocBytes -= ptrdiff_t(nbytes); - if (MOZ_UNLIKELY(isTooMuchMalloc())) - onTooMuchMalloc(); - } - // Iterate over all cells in the zone. See the definition of ZoneCellIter // in jsgcinlines.h for the possible arguments and documentation. template @@ -183,11 +173,8 @@ struct Zone : public JS::shadow::Zone, return js::gc::ZoneCellIter(const_cast(this), mozilla::Forward(args)...); } - bool isTooMuchMalloc() const { return gcMallocBytes <= 0; } - void onTooMuchMalloc(); - MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, - void* reallocPtr = nullptr) { + void* reallocPtr = nullptr) { if (!js::CurrentThreadCanAccessRuntime(runtime_)) return nullptr; return runtimeFromActiveCooperatingThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr); @@ -399,31 +386,48 @@ struct Zone : public JS::shadow::Zone, js::SystemAllocPolicy>; private: js::ZoneGroupData> typeDescrObjects_; + + // Malloc counter to measure memory pressure for GC scheduling. This + // counter should be used only when it's not possible to know the size of + // a free. + js::gc::MemoryCounter gcMallocCounter; + + // Counter of JIT code executable memory for GC scheduling. Also imprecise, + // since wasm can generate code that outlives a zone. + js::gc::MemoryCounter jitCodeCounter; + public: JS::WeakCache& typeDescrObjects() { return typeDescrObjects_.ref(); } bool addTypeDescrObject(JSContext* cx, HandleObject obj); - // Malloc counter to measure memory pressure for GC scheduling. It runs from - // gcMaxMallocBytes down to zero. This counter should be used only when it's - // not possible to know the size of a free. - mozilla::Atomic gcMallocBytes; + bool triggerGCForTooMuchMalloc() { + return runtimeFromAnyThread()->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC); + } - // GC trigger threshold for allocations on the C heap. - js::ActiveThreadData gcMaxMallocBytes; + void resetGCMallocBytes() { gcMallocCounter.reset(); } + void setGCMaxMallocBytes(size_t value) { gcMallocCounter.setMax(value); } + void updateMallocCounter(size_t nbytes) { gcMallocCounter.update(this, nbytes); } + size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); } + size_t GCMallocBytes() const { return gcMallocCounter.bytes(); } - // Whether a GC has been triggered as a result of gcMallocBytes falling - // below zero. - // - // This should be a bool, but Atomic only supports 32-bit and pointer-sized - // types. - mozilla::Atomic gcMallocGCTriggered; + void updateJitCodeMallocBytes(size_t size) { jitCodeCounter.update(this, size); } + + // Resets all the memory counters. + void resetAllMallocBytes() { + resetGCMallocBytes(); + jitCodeCounter.reset(); + } + bool isTooMuchMalloc() const { + return gcMallocCounter.isTooMuchMalloc() || + jitCodeCounter.isTooMuchMalloc(); + } private: // Bitmap of atoms marked by this zone. - js::ZoneGroupOrGCTaskData markedAtoms_; + js::ZoneGroupOrGCTaskData markedAtoms_; public: - js::gc::AtomMarkingRuntime::Bitmap& markedAtoms() { return markedAtoms_.ref(); } + js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); } // Track heap usage under this Zone. js::gc::HeapUsage usage; diff --git a/js/src/gdb/tests/test-ExecutableAllocator.cpp b/js/src/gdb/tests/test-ExecutableAllocator.cpp index 7cb7ad364ca4..c9636312a47c 100644 --- a/js/src/gdb/tests/test-ExecutableAllocator.cpp +++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp @@ -18,7 +18,7 @@ FRAGMENT(ExecutableAllocator, onepool) { using namespace js::jit; ExecutablePool* pool = nullptr; ExecutableAllocator execAlloc(cx->runtime()); - execAlloc.alloc(16 * 1024, &pool, BASELINE_CODE); + execAlloc.alloc(cx, 16 * 1024, &pool, BASELINE_CODE); breakpoint(); @@ -32,10 +32,10 @@ FRAGMENT(ExecutableAllocator, twopools) { ExecutablePool* pool = nullptr; ExecutableAllocator execAlloc(cx->runtime()); - execAlloc.alloc(16 * 1024, &init, BASELINE_CODE); + execAlloc.alloc(cx, 16 * 1024, &init, BASELINE_CODE); do { // Keep allocating until we get a second pool. - execAlloc.alloc(32 * 1024, &pool, ION_CODE); + execAlloc.alloc(cx, 32 * 1024, &pool, ION_CODE); } while (pool == init); breakpoint(); diff --git a/js/src/jit-test/lib/wasm-binary.js b/js/src/jit-test/lib/wasm-binary.js index e57ac84914c3..df5116b35588 100644 --- a/js/src/jit-test/lib/wasm-binary.js +++ b/js/src/jit-test/lib/wasm-binary.js @@ -71,6 +71,7 @@ const I32ConstCode = 0x41; const I64ConstCode = 0x42; const F32ConstCode = 0x43; const F64ConstCode = 0x44; +const I32AddCode = 0x6a; const I32DivSCode = 0x6d; const I32DivUCode = 0x6e; const I32RemSCode = 0x6f; diff --git a/js/src/jit-test/modules/empty.js b/js/src/jit-test/modules/empty.js new file mode 100644 index 000000000000..bd9ec079d80d --- /dev/null +++ b/js/src/jit-test/modules/empty.js @@ -0,0 +1 @@ +// Intentionally empty. diff --git a/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js b/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js new file mode 100644 index 000000000000..2b91b6a28473 --- /dev/null +++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js @@ -0,0 +1,4 @@ +import "export-circular-nonexisting-binding-2.js"; + +export* from "empty.js"; +export {x} from "empty.js"; diff --git a/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js b/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js new file mode 100644 index 000000000000..ba7dcc1b4879 --- /dev/null +++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js @@ -0,0 +1 @@ +export {x} from "export-circular-nonexisting-binding-1.js"; diff --git a/js/src/jit-test/modules/export-star-circular-1.js b/js/src/jit-test/modules/export-star-circular-1.js new file mode 100644 index 000000000000..9a0771b02411 --- /dev/null +++ b/js/src/jit-test/modules/export-star-circular-1.js @@ -0,0 +1 @@ +export* from "export-star-circular-2.js"; diff --git a/js/src/jit-test/modules/export-star-circular-2.js b/js/src/jit-test/modules/export-star-circular-2.js new file mode 100644 index 000000000000..b273d9cefa77 --- /dev/null +++ b/js/src/jit-test/modules/export-star-circular-2.js @@ -0,0 +1,3 @@ +export {y as x} from "export-star-circular-1.js"; + +export var y = "pass"; diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js index 3869d5dd6a7d..59f7e5ac3008 100644 --- a/js/src/jit-test/tests/atomics/basic-tests.js +++ b/js/src/jit-test/tests/atomics/basic-tests.js @@ -212,8 +212,10 @@ function testRangeCAS(a) { assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg); assertEq(a[0], 0); - assertErrorMessage(() => Atomics.compareExchange(a, "hi", 0, 1), RangeError, msg); - assertEq(a[0], 0); + // Converted to 0 + assertEq(Atomics.compareExchange(a, "hi", 0, 33), 0); + assertEq(a[0], 33); + a[0] = 0; assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg); assertEq(a[0], 0); diff --git a/js/src/jit-test/tests/cacheir/nukedCCW.js b/js/src/jit-test/tests/cacheir/nukedCCW.js new file mode 100644 index 000000000000..303750ce482a --- /dev/null +++ b/js/src/jit-test/tests/cacheir/nukedCCW.js @@ -0,0 +1,23 @@ +var wrapper = evaluate("({a: 15, b: {c: 42}})", {global: newGlobal({sameZoneAs: this})}); + +function test() { + var i, error; + try { + for (i = 0; i < 150; i++) { + assertEq(wrapper.b.c, 42); + assertEq(wrapper.a, 15); + + if (i == 142) { + // Next access to wrapper.b should throw. + nukeCCW(wrapper); + } + } + } catch (e) { + error = e; + } + + assertEq(error.message.includes("dead object"), true); + assertEq(i, 143); +} + +test(); diff --git a/js/src/jit-test/tests/gc/bug-1337414.js b/js/src/jit-test/tests/gc/bug-1337414.js new file mode 100644 index 000000000000..9e647a810cc2 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1337414.js @@ -0,0 +1,46 @@ +var lfLogBuffer = ` +gczeal(15,10); +try { + a = [] + gczeal(2, 2)() +} catch (e) {} +a.every(function() {}) +//corefuzz-dcd-endofdata +//corefuzz-dcd-selectmode 5 +`; +lfLogBuffer = lfLogBuffer.split('\n'); +lfPreamble = ` +`; +var lfCodeBuffer = ""; +var lfRunTypeLimit = 7; +var lfOffThreadGlobal = newGlobal(); +try {} catch (lfVare5) {} +var lfAccumulatedCode = lfPreamble; +while (true) { + var line = lfLogBuffer.shift(); + if (line == null) { + break; + } else if (line == "//corefuzz-dcd-endofdata") { + loadFile(lfCodeBuffer); + } else if (line.indexOf("//corefuzz-dcd-selectmode ") === 0) { + loadFile(line); + } else { + lfCodeBuffer += line + "\n"; + } +} +if (lfCodeBuffer) loadFile(lfCodeBuffer); +function loadFile(lfVarx) { + try { + if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) { + lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % lfRunTypeLimit; + } else { + switch (lfRunTypeId) { + case 5: + evalInWorker(lfAccumulatedCode); + evaluate(lfVarx); + } + } + } catch (lfVare) { + lfAccumulatedCode += "try { evaluate(`\n" + lfVarx + "\n`); } catch(exc) {}\n"; + } +} diff --git a/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js new file mode 100644 index 000000000000..387c7c369a3d --- /dev/null +++ b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js @@ -0,0 +1,3 @@ +// |jit-test| module; error:SyntaxError + +import "export-circular-nonexisting-binding-1.js"; diff --git a/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js new file mode 100644 index 000000000000..f87829d89b8b --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js @@ -0,0 +1,4 @@ +// |jit-test| module; error:SyntaxError + +export { a } from "empty.js"; +export* from "module1.js"; diff --git a/js/src/jit-test/tests/modules/export-star-circular-dependencies.js b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js new file mode 100644 index 000000000000..9aa612f087db --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js @@ -0,0 +1,6 @@ +// |jit-test| module + +import { x, y } from "export-star-circular-1.js"; + +assertEq(x, "pass"); +assertEq(y, "pass"); diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index f903f1c9fd8c..3aa6d69bef5e 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -64,9 +64,6 @@ assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, 1])), Co assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0])), CompileError, versionError(0x6d736100)); assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), CompileError, versionError(0x6d736100)); -// This test should be removed shortly. -assertEq(WebAssembly.validate(toU8([magic0, magic1, magic2, magic3, 0xd, 0x0, 0x0, 0x0])), true); - function moduleHeaderThen(...rest) { return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest]; } diff --git a/js/src/jit-test/tests/wasm/errors.js b/js/src/jit-test/tests/wasm/errors.js index 65eb1cc20521..3efcf838bdb9 100644 --- a/js/src/jit-test/tests/wasm/errors.js +++ b/js/src/jit-test/tests/wasm/errors.js @@ -3,6 +3,7 @@ load(libdir + "wasm-binary.js"); const Module = WebAssembly.Module; const Instance = WebAssembly.Instance; +const CompileError = WebAssembly.CompileError; const RuntimeError = WebAssembly.RuntimeError; function isWasmFunction(name) { @@ -153,3 +154,21 @@ for (var i = 1; i < N; i++) { assertEq(binary[stack[i].line], i == 3 ? CallIndirectCode : CallCode); assertEq(stack[i].column, 1); } + +function testCompileError(opcode, text) { + var binary = new Uint8Array(wasmTextToBinary(text)); + var exn; + try { + new Instance(new Module(binary)); + } catch (e) { + exn = e; + } + + assertEq(exn instanceof CompileError, true); + var offset = Number(exn.message.match(/at offset (\d*)/)[1]); + assertEq(binary[offset], opcode); +} + +testCompileError(CallCode, '(module (func $f (param i32)) (func $g call $f))'); +testCompileError(I32AddCode, '(module (func (i32.add (i32.const 1) (f32.const 1))))'); +testCompileError(EndCode, '(module (func (block i32)))'); diff --git a/js/src/jit-test/tests/wasm/regress/current-memory-tls.js b/js/src/jit-test/tests/wasm/regress/current-memory-tls.js new file mode 100644 index 000000000000..b252d1ef3080 --- /dev/null +++ b/js/src/jit-test/tests/wasm/regress/current-memory-tls.js @@ -0,0 +1,97 @@ +load(libdir + "wasm.js"); + +// Bug 1341650: +// - when compiled with Ion, pass the TLS register to current_memory; +// - when compiled with Baseline, don't clobber the last stack slot when +// calling into current_memory/grow_memory; + +// This toy module starts with an empty memory, then tries to set values at different +// indexes, automatically growing memory when that would trigger an out of +// bounds access, for the exact right amount of pages. Note it's not made for +// efficiency, but optimizing it doesn't trigger the bugs mentioned above. + +let i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(` +(module + (memory $mem (export "mem") 0 65535) + + (func (export "cur_mem") (result i32) (current_memory)) + + (func $maybeGrow (param $i i32) (local $smem i32) + ;; get current_memory in number of bytes, not pages. + current_memory + i64.extend_u/i32 + i64.const 65536 + i64.mul + + ;; get the last byte index accessed by an int32 access. + get_local $i + i32.const 3 + i32.add + tee_local $i + i64.extend_u/i32 + + ;; if the memory is too small, grow it. + i64.le_u + if + ;; get the floor of the accessed *page* index. + get_local $i + i64.extend_u/i32 + i64.const 65536 + i64.div_u + + ;; subtract to that the size of the current memory in pages; + ;; that's the amount of pages we want to grow, minus one. + current_memory + i64.extend_u/i32 + + i64.sub + + ;; add 1 to that amount. + i64.const 1 + i64.add + + ;; get back to i32 and grow memory. + i32.wrap/i64 + grow_memory + drop + end + ) + + (func (export "set") (param $i i32) (param $v i32) + get_local $i + call $maybeGrow + get_local $i + get_local $v + i32.store + ) + + (func (export "get") (param $i i32) (result i32) + get_local $i + i32.load + ) +) +`))).exports; + +assertEq(i.cur_mem(), 0); + +i.set(0, 1); +assertEq(i.get(0), 1); + +assertEq(i.cur_mem(), 1); + +i.set(1234, 1); +assertEq(i.get(1234), 1); + +assertEq(i.cur_mem(), 1); + +i.set(65532, 1); +assertEq(i.get(65532), 1); + +assertErrorMessage(() => i.get(65533), WebAssembly.RuntimeError, /index out of bounds/); + +assertEq(i.cur_mem(), 1); + +i.set(65533, 1); +assertEq(i.get(65533), 1); + +assertEq(i.cur_mem(), 2); diff --git a/js/src/jit-test/tests/wasm/spec/binary.wast b/js/src/jit-test/tests/wasm/spec/binary.wast index b0ab9c4266fd..9bb55ef9ea45 100644 --- a/js/src/jit-test/tests/wasm/spec/binary.wast +++ b/js/src/jit-test/tests/wasm/spec/binary.wast @@ -1,18 +1,18 @@ -(module "\00asm\0d\00\00\00") -(module "\00asm" "\0d\00\00\00") -(module $M1 "\00asm\0d\00\00\00") -(module $M2 "\00asm" "\0d\00\00\00") +(module "\00asm\01\00\00\00") +(module "\00asm" "\01\00\00\00") +(module $M1 "\00asm\01\00\00\00") +(module $M2 "\00asm" "\01\00\00\00") (assert_malformed (module "") "unexpected end") (assert_malformed (module "\01") "unexpected end") (assert_malformed (module "\00as") "unexpected end") (assert_malformed (module "asm\00") "magic header not detected") (assert_malformed (module "msa\00") "magic header not detected") -(assert_malformed (module "msa\00\0d\00\00\00") "magic header not detected") +(assert_malformed (module "msa\00\01\00\00\00") "magic header not detected") (assert_malformed (module "msa\00\00\00\00\0d") "magic header not detected") (assert_malformed (module "\00asm") "unexpected end") -(assert_malformed (module "\00asm\0d") "unexpected end") -(assert_malformed (module "\00asm\0d\00\00") "unexpected end") +(assert_malformed (module "\00asm\01") "unexpected end") +(assert_malformed (module "\00asm\01\00\00") "unexpected end") (assert_malformed (module "\00asm\0e\00\00\00") "unknown binary version") (assert_malformed (module "\00asm\00\00\00\0d") "unknown binary version") diff --git a/js/src/jit-test/tests/wasm/spec/custom_section.wast b/js/src/jit-test/tests/wasm/spec/custom_section.wast index 66e707c8fc36..f087a9d2d9c3 100644 --- a/js/src/jit-test/tests/wasm/spec/custom_section.wast +++ b/js/src/jit-test/tests/wasm/spec/custom_section.wast @@ -1,5 +1,5 @@ (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\00\24\10" "a custom section" "this is the payload" "\00\20\10" "a custom section" "this is payload" "\00\11\10" "a custom section" "" @@ -9,7 +9,7 @@ ) (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\00\0e\06" "custom" "payload" "\00\0e\06" "custom" "payload" "\01\01\00" ;; type section @@ -45,7 +45,7 @@ ) (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\01\07\01\60\02\7f\7f\01\7f" ;; type section "\00\1a\06" "custom" "this is the payload" ;; custom section "\03\02\01\00" ;; function section @@ -56,7 +56,7 @@ (assert_malformed (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\00\00" ) "unexpected end" @@ -64,7 +64,7 @@ (assert_malformed (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\00\26\10" "a custom section" "this is the payload" ) "unexpected end" @@ -72,7 +72,7 @@ (assert_malformed (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\00\25\10" "a custom section" "this is the payload" "\00\24\10" "a custom section" "this is the payload" ) @@ -81,7 +81,7 @@ (assert_malformed (module - "\00asm" "\0d\00\00\00" + "\00asm" "\01\00\00\00" "\01\07\01\60\02\7f\7f\01\7f" ;; type section "\00\25\10" "a custom section" "this is the payload" ;; invalid length! "\03\02\01\00" ;; function section diff --git a/js/src/jit/ExecutableAllocator.cpp b/js/src/jit/ExecutableAllocator.cpp index 808c87a9bde0..e53c5fcfef29 100644 --- a/js/src/jit/ExecutableAllocator.cpp +++ b/js/src/jit/ExecutableAllocator.cpp @@ -238,7 +238,7 @@ ExecutableAllocator::createPool(size_t n) } void* -ExecutableAllocator::alloc(size_t n, ExecutablePool** poolp, CodeKind type) +ExecutableAllocator::alloc(JSContext* cx, size_t n, ExecutablePool** poolp, CodeKind type) { // Don't race with reprotectAll called from the signal handler. JitRuntime::AutoPreventBackedgePatching apbp(rt_); @@ -261,6 +261,9 @@ ExecutableAllocator::alloc(size_t n, ExecutablePool** poolp, CodeKind type) // (found, or created if necessary) a pool that had enough space. void* result = (*poolp)->alloc(n, type); MOZ_ASSERT(result); + + cx->zone()->updateJitCodeMallocBytes(n); + return result; } diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h index c6e90e8fb561..3a42918142ef 100644 --- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -177,7 +177,7 @@ class ExecutableAllocator // alloc() returns a pointer to some memory, and also (by reference) a // pointer to reference-counted pool. The caller owns a reference to the // pool; i.e. alloc() increments the count before returning the object. - void* alloc(size_t n, ExecutablePool** poolp, CodeKind type); + void* alloc(JSContext* cx, size_t n, ExecutablePool** poolp, CodeKind type); void releasePoolPages(ExecutablePool* pool); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 9260df3114a3..5410c7a4b657 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -3038,12 +3038,6 @@ jit::AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer, bool for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) { MPhi* phi = *iter; for (size_t i = 0, e = phi->numOperands(); i < e; ++i) { - // We sometimes see a phi with a magic-optimized-arguments - // operand defined in the normal entry block, while the phi is - // also reachable from the OSR entry (auto-regress/bug779818.js) - if (phi->getOperand(i)->type() == MIRType::MagicOptimizedArguments) - continue; - MOZ_ASSERT(phi->getOperand(i)->block()->dominates(block->getPredecessor(i)), "Phi input is not dominated by its operand"); } diff --git a/js/src/jit/Linker.cpp b/js/src/jit/Linker.cpp index 674f7a5b5fff..6038687f7d47 100644 --- a/js/src/jit/Linker.cpp +++ b/js/src/jit/Linker.cpp @@ -31,9 +31,10 @@ Linker::newCode(JSContext* cx, CodeKind kind, bool hasPatchableBackedges /* = fa bytesNeeded = AlignBytes(bytesNeeded, sizeof(void*)); ExecutableAllocator& execAlloc = hasPatchableBackedges - ? cx->runtime()->jitRuntime()->backedgeExecAlloc() - : cx->runtime()->jitRuntime()->execAlloc(); - uint8_t* result = (uint8_t*)execAlloc.alloc(bytesNeeded, &pool, kind); + ? cx->runtime()->jitRuntime()->backedgeExecAlloc() + : cx->runtime()->jitRuntime()->execAlloc(); + + uint8_t* result = (uint8_t*)execAlloc.alloc(cx, bytesNeeded, &pool, kind); if (!result) return fail(cx); diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index b0ad34e14e24..58c801af07a6 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -2807,7 +2807,7 @@ MacroAssembler::wasmCallBuiltinInstanceMethod(const ABIArg& instanceArg, if (instanceArg.kind() == ABIArg::GPR) { loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)), instanceArg.gpr()); } else if (instanceArg.kind() == ABIArg::Stack) { - // Safe to use ABINonArgReg0 since its the last thing before the call + // Safe to use ABINonArgReg0 since it's the last thing before the call. Register scratch = ABINonArgReg0; loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)), scratch); storePtr(scratch, Address(getStackPointer(), instanceArg.offsetFromArgBase())); diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 9a66b042c2f9..d43f2e5915a9 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -405,14 +405,6 @@ class PageBitSet #endif }; -// Limit on the number of bytes of executable memory to prevent JIT spraying -// attacks. -#if JS_BITS_PER_WORD == 32 -static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; -#else -static const size_t MaxCodeBytesPerProcess = 640 * 1024 * 1024; -#endif - // Per-process executable memory allocator. It reserves a block of memory of // MaxCodeBytesPerProcess bytes, then allocates/deallocates pages from that. // diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h index 078ce7cb75fc..3323c5cba4f5 100644 --- a/js/src/jit/ProcessExecutableMemory.h +++ b/js/src/jit/ProcessExecutableMemory.h @@ -12,6 +12,14 @@ namespace js { namespace jit { +// Limit on the number of bytes of executable memory to prevent JIT spraying +// attacks. +#if JS_BITS_PER_WORD == 32 +static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; +#else +static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; +#endif + // Executable code is allocated in 64K chunks. ExecutableAllocator uses pools // that are at least this big. Code we allocate does not necessarily have 64K // alignment though. diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a134febf6a39..0989b942fb39 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1184,11 +1184,11 @@ js::DumpHeap(JSContext* cx, FILE* fp, js::DumpHeapNurseryBehaviour nurseryBehavi fprintf(dtrc.output, "==========\n"); dtrc.prefix = "> "; - IterateZonesCompartmentsArenasCells(cx, &dtrc, - DumpHeapVisitZone, - DumpHeapVisitCompartment, - DumpHeapVisitArena, - DumpHeapVisitCell); + IterateHeapUnbarriered(cx, &dtrc, + DumpHeapVisitZone, + DumpHeapVisitCompartment, + DumpHeapVisitArena, + DumpHeapVisitCell); fflush(dtrc.output); } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index fdcc1400782f..2460e31f8349 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1130,12 +1130,17 @@ class RegExpGuard; extern JS_FRIEND_API(bool) RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared); -/* Implemented in jswrapper.cpp. */ +/* Implemented in CrossCompartmentWrapper.cpp. */ typedef enum NukeReferencesToWindow { NukeWindowReferences, DontNukeWindowReferences } NukeReferencesToWindow; +typedef enum NukeReferencesFromTarget { + NukeAllReferences, + NukeIncomingReferences, +} NukeReferencesFromTarget; + /* * These filters are designed to be ephemeral stack classes, and thus don't * do any rooting or holding of their members. @@ -1178,7 +1183,8 @@ extern JS_FRIEND_API(bool) NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, - NukeReferencesToWindow nukeReferencesToWindow); + NukeReferencesToWindow nukeReferencesToWindow, + NukeReferencesFromTarget nukeReferencesFromTarget); /* Specify information about DOMProxy proxies in the DOM, for use by ICs. */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 48fb02e2f72d..c1121371edd1 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -816,7 +816,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : marker(rt), usage(nullptr), mMemProfiler(rt), - maxMallocBytes(0), nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers. numArenasFreeCommitted(0), verifyPreData(nullptr), @@ -864,8 +863,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : incrementalLimit(0), #endif fullCompartmentChecks(false), - mallocBytesUntilGC(0), - mallocGCTriggered(false), alwaysPreserveCode(false), #ifdef DEBUG arenasEmptyAtShutdown(true), @@ -1259,7 +1256,7 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) case JSGC_MAX_BYTES: return uint32_t(tunables.gcMaxBytes()); case JSGC_MAX_MALLOC_BYTES: - return maxMallocBytes; + return mallocCounter.maxBytes(); case JSGC_BYTES: return uint32_t(usage.gcBytes()); case JSGC_MODE: @@ -1539,40 +1536,19 @@ js::RemoveRawValueRoot(JSContext* cx, Value* vp) void GCRuntime::setMaxMallocBytes(size_t value) { - /* - * For compatibility treat any value that exceeds PTRDIFF_T_MAX to - * mean that value. - */ - maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; - resetMallocBytes(); + mallocCounter.setMax(value); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) zone->setGCMaxMallocBytes(value); } -void -GCRuntime::resetMallocBytes() -{ - mallocBytesUntilGC = ptrdiff_t(maxMallocBytes); - mallocGCTriggered = false; -} - void GCRuntime::updateMallocCounter(JS::Zone* zone, size_t nbytes) { - mallocBytesUntilGC -= ptrdiff_t(nbytes); - if (MOZ_UNLIKELY(isTooMuchMalloc())) - onTooMuchMalloc(); - else if (zone) + bool triggered = mallocCounter.update(this, nbytes); + if (!triggered && zone) zone->updateMallocCounter(nbytes); } -void -GCRuntime::onTooMuchMalloc() -{ - if (!mallocGCTriggered) - mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC); -} - double ZoneHeapThreshold::allocTrigger(bool highFrequencyGC) const { @@ -4186,7 +4162,7 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock) /* Save existing mark bits. */ for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) { ChunkBitmap* bitmap = &chunk->bitmap; - ChunkBitmap* entry = js_new(); + ChunkBitmap* entry = js_new(); if (!entry) return; @@ -4921,7 +4897,7 @@ MAKE_GC_SWEEP_TASK(SweepMiscTask); /* virtual */ void SweepAtomsTask::run() { - AtomMarkingRuntime::Bitmap marked; + DenseBitmap marked; if (runtime()->gc.atomMarking.computeBitmapFromChunkMarkBits(runtime(), marked)) { for (GCZonesIter zone(runtime()); !zone.done(); zone.next()) runtime()->gc.atomMarking.updateZoneBitmap(zone, marked); @@ -6313,7 +6289,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason:: /* Clear gcMallocBytes for all zones. */ for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - zone->resetGCMallocBytes(); + zone->resetAllMallocBytes(); resetMallocBytes(); @@ -7621,7 +7597,8 @@ static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setNumber(double(cx->zone()->threshold.allocTrigger(cx->runtime()->gc.schedulingState.inHighFrequencyGCMode()))); + bool highFrequency = cx->runtime()->gc.schedulingState.inHighFrequencyGCMode(); + args.rval().setNumber(double(cx->zone()->threshold.allocTrigger(highFrequency))); return true; } @@ -7629,7 +7606,7 @@ static bool ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setNumber(double(cx->zone()->gcMallocBytes)); + args.rval().setNumber(double(cx->zone()->GCMallocBytes())); return true; } @@ -7637,7 +7614,7 @@ static bool ZoneMaxMallocGetter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setNumber(double(cx->zone()->gcMaxMallocBytes)); + args.rval().setNumber(double(cx->zone()->GCMaxMallocBytes())); return true; } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index e31d643960ec..b544d1b534f7 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1018,24 +1018,27 @@ typedef void (*IterateCellCallback)(JSRuntime* rt, void* data, void* thing, * This function calls |zoneCallback| on every zone, |compartmentCallback| on * every compartment, |arenaCallback| on every in-use arena, and |cellCallback| * on every in-use cell in the GC heap. + * + * Note that no read barrier is triggered on the cells passed to cellCallback, + * so no these pointers must not escape the callback. */ extern void -IterateZonesCompartmentsArenasCells(JSContext* cx, void* data, - IterateZoneCallback zoneCallback, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback); +IterateHeapUnbarriered(JSContext* cx, void* data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback); /* * This function is like IterateZonesCompartmentsArenasCells, but does it for a * single zone. */ extern void -IterateZoneCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data, - IterateZoneCallback zoneCallback, - JSIterateCompartmentCallback compartmentCallback, - IterateArenaCallback arenaCallback, - IterateCellCallback cellCallback); +IterateHeapUnbarrieredForZone(JSContext* cx, Zone* zone, void* data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback); /* * Invoke chunkCallback on every in-use chunk. diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 9ad1349da7eb..612c854b734d 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -154,8 +154,6 @@ class ArenaCellIterImpl void init(Arena* arena, CellIterNeedsBarrier mayNeedBarrier) { MOZ_ASSERT(!initialized); MOZ_ASSERT(arena); - MOZ_ASSERT_IF(!mayNeedBarrier, - CurrentThreadIsPerformingGC() || CurrentThreadIsGCSweeping()); initialized = true; AllocKind kind = arena->getAllocKind(); firstThingOffset = Arena::firstThingOffset(kind); @@ -243,6 +241,14 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl } }; +class ArenaCellIterUnbarriered : public ArenaCellIterImpl +{ + public: + explicit ArenaCellIterUnbarriered(Arena* arena) + : ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier) + {} +}; + template class ZoneCellIter; diff --git a/js/src/moz.build b/js/src/moz.build index 2cd3d6bfb7c8..8b11432f99bc 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -176,6 +176,7 @@ UNIFIED_SOURCES += [ 'builtin/WeakMapObject.cpp', 'builtin/WeakSetObject.cpp', 'devtools/sharkctl.cpp', + 'ds/Bitmap.cpp', 'ds/LifoAlloc.cpp', 'ds/MemoryProtectionExceptionHandler.cpp', 'frontend/BytecodeCompiler.cpp', diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index ba25c40bd0de..5e14980980e7 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -513,20 +513,23 @@ JS_FRIEND_API(bool) js::NukeCrossCompartmentWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter, - js::NukeReferencesToWindow nukeReferencesToWindow) + js::NukeReferencesToWindow nukeReferencesToWindow, + js::NukeReferencesFromTarget nukeReferencesFromTarget) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); EvictAllNurseries(rt); - // Iterate through scopes looking for system cross compartment wrappers - // that point to an object that shares a global with obj. - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; + // If the compartment matches both the source and target filter, we may + // want to cut both incoming and outgoing wrappers. + bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences && + targetFilter.match(c)); + // Iterate the wrappers looking for anything interesting. for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { // Some cross-compartment wrappers are for strings. We're not @@ -538,13 +541,15 @@ js::NukeCrossCompartmentWrappers(JSContext* cx, AutoWrapperRooter wobj(cx, WrapperValue(e)); JSObject* wrapped = UncheckedUnwrap(wobj); + // We only skip nuking window references that point to a target + // compartment, not the ones that belong to it. if (nukeReferencesToWindow == DontNukeWindowReferences && - IsWindowProxy(wrapped)) + MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) { continue; } - if (targetFilter.match(wrapped->compartment())) { + if (MOZ_UNLIKELY(nukeAll) || targetFilter.match(wrapped->compartment())) { // We found a wrapper to nuke. e.removeFront(); NukeCrossCompartmentWrapper(cx, wobj); diff --git a/js/src/shell/ModuleLoader.js b/js/src/shell/ModuleLoader.js index d8767f05f4b0..58b095f4db10 100644 --- a/js/src/shell/ModuleLoader.js +++ b/js/src/shell/ModuleLoader.js @@ -21,9 +21,7 @@ Reflect.Loader = new class { return os.file.readFile(path); } - loadAndParse(name) { - let path = this.resolve(name); - + loadAndParse(path) { if (this.registry.has(path)) return this.registry.get(path); @@ -33,11 +31,23 @@ Reflect.Loader = new class { return module; } - ["import"](name, referrer) { - let module = this.loadAndParse(name); + loadAndExecute(path) { + let module = this.loadAndParse(path); module.declarationInstantiation(); return module.evaluation(); } + + importRoot(path) { + return this.loadAndExecute(path); + } + + ["import"](name, referrer) { + let path = this.resolve(name); + return this.loadAndExecute(path); + } }; -setModuleResolveHook((module, requestName) => Reflect.Loader.loadAndParse(requestName)); +setModuleResolveHook((module, requestName) => { + let path = Reflect.Loader.resolve(requestName); + return Reflect.Loader.loadAndParse(path) +}); diff --git a/js/src/shell/OSObject.cpp b/js/src/shell/OSObject.cpp index d1019c77fd25..7d36295a3022 100644 --- a/js/src/shell/OSObject.cpp +++ b/js/src/shell/OSObject.cpp @@ -112,16 +112,18 @@ ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveM if (IsAbsolutePath(filename)) return filenameStr; - /* Get the currently executing script's name. */ JS::AutoFilename scriptFilename; - if (!DescribeScriptedCaller(cx, &scriptFilename)) - return nullptr; + if (resolveMode == ScriptRelative) { + // Get the currently executing script's name. + if (!DescribeScriptedCaller(cx, &scriptFilename)) + return nullptr; - if (!scriptFilename.get()) - return nullptr; + if (!scriptFilename.get()) + return nullptr; - if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0) - resolveMode = RootRelative; + if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0) + resolveMode = RootRelative; + } static char buffer[PATH_MAX+1]; if (resolveMode == ScriptRelative) { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 89e012c925e2..87e5112b9a7d 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -733,12 +733,12 @@ GetLoaderObject(JSContext* cx, MutableHandleObject resultOut) } static bool -GetImportMethod(JSContext* cx, HandleObject loader, MutableHandleFunction resultOut) +GetImportRootMethod(JSContext* cx, HandleObject loader, MutableHandleFunction resultOut) { - // Look up the module loader's |import| method. + // Look up the module loader's |importRoot| method. RootedValue value(cx); - if (!JS_GetProperty(cx, loader, "import", &value) || !value.isObject()) + if (!JS_GetProperty(cx, loader, "importRoot", &value) || !value.isObject()) return false; RootedObject object(cx, &value.toObject()); @@ -752,17 +752,31 @@ GetImportMethod(JSContext* cx, HandleObject loader, MutableHandleFunction result static MOZ_MUST_USE bool RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly) { - // Execute a module by calling |Reflect.Loader.import(filename)|. + // Execute a module by calling Reflect.Loader.importRoot on the resolved + // filename. RootedObject loaderObj(cx); - MOZ_ALWAYS_TRUE(GetLoaderObject(cx, &loaderObj)); + if (!GetLoaderObject(cx, &loaderObj)) { + JS_ReportErrorASCII(cx, "Failed to get Reflect.Loader"); + return false; + } RootedFunction importFun(cx); - MOZ_ALWAYS_TRUE(GetImportMethod(cx, loaderObj, &importFun)); + if (!GetImportRootMethod(cx, loaderObj, &importFun)) { + JS_ReportErrorASCII(cx, "Failed to get Reflect.Loader.importRoot method"); + return false; + } - JS::AutoValueArray<2> args(cx); - args[0].setString(JS_NewStringCopyZ(cx, filename)); - args[1].setUndefined(); + RootedString path(cx, JS_NewStringCopyZ(cx, filename)); + if (!path) + return false; + + path = ResolvePath(cx, path, RootRelative); + if (!path) + return false; + + JS::AutoValueArray<1> args(cx); + args[0].setString(path); RootedValue value(cx); return JS_CallFunction(cx, loaderObj, importFun, args, &value); @@ -5037,6 +5051,24 @@ NewGlobal(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +NukeCCW(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1 || !args[0].isObject() || + !IsCrossCompartmentWrapper(&args[0].toObject())) + { + JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, + "nukeCCW"); + return false; + } + + NukeCrossCompartmentWrapper(cx, &args[0].toObject()); + args.rval().setUndefined(); + return true; +} + static bool GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) { @@ -6366,6 +6398,10 @@ static const JSFunctionSpecWithHelp shell_functions[] = { " principal is treated as if its bits were 0xffff, for subsumption\n" " purposes. If this property is omitted, supply no principal."), + JS_FN_HELP("nukeCCW", NukeCCW, 1, 0, +"nukeCCW(wrapper)", +" Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."), + JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0, "createMappedArrayBuffer(filename, [offset, [size]])", " Create an array buffer that mmaps the given file."), diff --git a/js/src/tests/shell/futex-apis.js b/js/src/tests/shell/futex-apis.js index b3e0acd55488..6df6ca3e3739 100644 --- a/js/src/tests/shell/futex-apis.js +++ b/js/src/tests/shell/futex-apis.js @@ -99,9 +99,8 @@ let sab = new SharedArrayBuffer(16); let indices = [ (view) => -1, (view) => view.length, (view) => view.length*2, - (view) => undefined, - (view) => '3.5', - (view) => { password: "qumquat" } ]; + (view) => '-3.5', + (view) => ({ valueOf: () => -8 }) ]; for ( let iidx=0 ; iidx < indices.length ; iidx++ ) { let Idx = indices[iidx](view); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index cafc846668cc..e59842ef0461 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -488,7 +488,7 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam { RootedId importNameId(cx, AtomToId(importName)); RootedId localNameId(cx, AtomToId(localName)); - RootedModuleEnvironmentObject env(cx, module->environment()); + RootedModuleEnvironmentObject env(cx, &module->initialEnvironment()); if (!importBindings().putNew(cx, importNameId, env, localNameId)) { ReportOutOfMemory(cx); return false; diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index 4aa754eaeb72..fc9ab9149607 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -760,11 +760,11 @@ CollectRuntimeStatsHelper(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVis StatsClosure closure(rtStats, opv, anonymize); if (!closure.init()) return false; - IterateZonesCompartmentsArenasCells(cx, &closure, - StatsZoneCallback, - StatsCompartmentCallback, - StatsArenaCallback, - statsCellCallback); + IterateHeapUnbarriered(cx, &closure, + StatsZoneCallback, + StatsCompartmentCallback, + StatsArenaCallback, + statsCellCallback); // Take the "explicit/js/runtime/" measurements. rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime); @@ -906,11 +906,11 @@ AddSizeOfTab(JSContext* cx, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP StatsClosure closure(&rtStats, opv, /* anonymize = */ false); if (!closure.init()) return false; - IterateZoneCompartmentsArenasCells(cx, zone, &closure, - StatsZoneCallback, - StatsCompartmentCallback, - StatsArenaCallback, - StatsCellCallback); + IterateHeapUnbarrieredForZone(cx, zone, &closure, + StatsZoneCallback, + StatsCompartmentCallback, + StatsArenaCallback, + StatsCellCallback); MOZ_ASSERT(rtStats.zoneStatsVector.length() == 1); rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]); diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 8de489e1f87e..c66f02c95b2d 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -489,7 +489,6 @@ class BaseCompiler ValTypeVector Sig_; Label returnLabel_; Label stackOverflowLabel_; - TrapOffset prologueTrapOffset_; CodeOffset stackAddOffset_; LatentOp latentOp_; // Latent operation for branch (seen next) @@ -2046,31 +2045,31 @@ class BaseCompiler AllocatableGeneralRegisterSet knownGPR_ = availGPR_; AllocatableFloatRegisterSet knownFPU_ = availFPU_; for (size_t i = 0 ; i < stk_.length() ; i++) { - Stk& item = stk_[i]; - switch (item.kind_) { - case Stk::RegisterI32: - knownGPR_.add(item.i32reg()); - break; - case Stk::RegisterI64: + Stk& item = stk_[i]; + switch (item.kind_) { + case Stk::RegisterI32: + knownGPR_.add(item.i32reg()); + break; + case Stk::RegisterI64: #ifdef JS_PUNBOX64 - knownGPR_.add(item.i64reg().reg); + knownGPR_.add(item.i64reg().reg); #else - knownGPR_.add(item.i64reg().high); - knownGPR_.add(item.i64reg().low); + knownGPR_.add(item.i64reg().high); + knownGPR_.add(item.i64reg().low); #endif - break; - case Stk::RegisterF32: - knownFPU_.add(item.f32reg()); - break; - case Stk::RegisterF64: - knownFPU_.add(item.f64reg()); - break; - default: - break; - } - } - MOZ_ASSERT(knownGPR_.bits() == allGPR_.bits()); - MOZ_ASSERT(knownFPU_.bits() == allFPU_.bits()); + break; + case Stk::RegisterF32: + knownFPU_.add(item.f32reg()); + break; + case Stk::RegisterF64: + knownFPU_.add(item.f64reg()); + break; + default: + break; + } + } + MOZ_ASSERT(knownGPR_.bits() == allGPR_.bits()); + MOZ_ASSERT(knownFPU_.bits() == allFPU_.bits()); } #endif @@ -2106,8 +2105,7 @@ class BaseCompiler // Labels void insertBreakablePoint(CallSiteDesc::Kind kind) { - const uint32_t offset = trapOffset().bytecodeOffset; - masm.nopPatchableToCall(CallSiteDesc(offset, kind)); + masm.nopPatchableToCall(CallSiteDesc(iter_.errorOffset(), kind)); } ////////////////////////////////////////////////////////////////////// @@ -2271,7 +2269,8 @@ class BaseCompiler MOZ_ASSERT(localSize_ >= debugFrameReserved); if (localSize_ > debugFrameReserved) masm.addToStackPtr(Imm32(localSize_ - debugFrameReserved)); - masm.jump(TrapDesc(prologueTrapOffset_, Trap::StackOverflow, debugFrameReserved)); + TrapOffset prologueTrapOffset(func_.lineOrBytecode()); + masm.jump(TrapDesc(prologueTrapOffset, Trap::StackOverflow, debugFrameReserved)); masm.bind(&returnLabel_); @@ -2389,7 +2388,10 @@ class BaseCompiler void startCallArgs(FunctionCall& call, size_t stackArgAreaSize) { - call.stackArgAreaSize = stackArgAreaSize; + // It's possible the TLS pointer may have implicitly used stack before. + MOZ_ASSERT(call.stackArgAreaSize == 0 || call.stackArgAreaSize == sizeof(void*)); + + call.stackArgAreaSize += stackArgAreaSize; size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment; if (adjustment) @@ -2397,7 +2399,10 @@ class BaseCompiler } const ABIArg reservePointerArgument(FunctionCall& call) { - return call.abi.next(MIRType::Pointer); + ABIArg ret = call.abi.next(MIRType::Pointer); + if (ret.kind() == ABIArg::Stack) + call.stackArgAreaSize += sizeof(void*); + return ret; } // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place. @@ -2908,7 +2913,8 @@ class BaseCompiler // movl clears the high bits if the two registers are the same. masm.movl(src.reg, dest); #elif defined(JS_NUNBOX32) - masm.move32(src.low, dest); + if (src.low != dest) + masm.move32(src.low, dest); #else MOZ_CRASH("BaseCompiler platform hook: wrapI64ToI32"); #endif @@ -2947,7 +2953,8 @@ class BaseCompiler #if defined(JS_CODEGEN_X64) masm.movl(src, dest.reg); #elif defined(JS_NUNBOX32) - masm.move32(src, dest.low); + if (src != dest.low) + masm.move32(src, dest.low); masm.move32(Imm32(0), dest.high); #else MOZ_CRASH("BaseCompiler platform hook: extendU32ToI64"); @@ -3505,7 +3512,7 @@ class BaseCompiler uint32_t readCallSiteLineOrBytecode() { if (!func_.callSiteLineNums().empty()) return func_.callSiteLineNums()[lastReadCallSite_++]; - return trapOffset().bytecodeOffset; + return iter_.errorOffset(); } bool done() const { @@ -7326,7 +7333,7 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env, TempAllocator* alloc, MacroAssembler* masm) : env_(env), - iter_(env, decoder, func.lineOrBytecode()), + iter_(env, decoder), func_(func), lastReadCallSite_(0), alloc_(*alloc), @@ -7338,7 +7345,6 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env, deadCode_(false), debugEnabled_(debugEnabled), bceSafe_(0), - prologueTrapOffset_(trapOffset()), stackAddOffset_(0), latentOp_(LatentOp::None), latentType_(ValType::I32), @@ -7539,7 +7545,7 @@ js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, Uniq const FuncBytes& func = unit->func(); uint32_t bodySize = func.bytes().length(); - Decoder d(func.bytes(), error); + Decoder d(func.bytes().begin(), func.bytes().end(), func.lineOrBytecode(), error); if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d)) return false; diff --git a/js/src/wasm/WasmBinaryConstants.h b/js/src/wasm/WasmBinaryConstants.h index ca240f0a87b7..50253cfc18e8 100644 --- a/js/src/wasm/WasmBinaryConstants.h +++ b/js/src/wasm/WasmBinaryConstants.h @@ -27,10 +27,6 @@ namespace wasm { static const uint32_t MagicNumber = 0x6d736100; // "\0asm" static const uint32_t EncodingVersion = 0x01; -// 0xd is equivalent to 0x1 modulo unreachability validation rules, so to aid -// transition of toolchain, accept both for a short period of time. -static const uint32_t PrevEncodingVersion = 0x0d; - static const char NameSectionName[] = "name"; enum class SectionId diff --git a/js/src/wasm/WasmBinaryIterator.h b/js/src/wasm/WasmBinaryIterator.h index 5048eaf320ba..bae3edb8776f 100644 --- a/js/src/wasm/WasmBinaryIterator.h +++ b/js/src/wasm/WasmBinaryIterator.h @@ -326,13 +326,12 @@ class MOZ_STACK_CLASS OpIter : private Policy Decoder& d_; const ModuleEnvironment& env_; - const size_t offsetInModule_; Vector, 8, SystemAllocPolicy> valueStack_; Vector, 8, SystemAllocPolicy> controlStack_; DebugOnly op_; - size_t offsetOfExpr_; + size_t offsetOfLastReadOp_; MOZ_MUST_USE bool readFixedU8(uint8_t* out) { if (Validate) @@ -485,8 +484,8 @@ class MOZ_STACK_CLASS OpIter : private Policy public: typedef Vector ValueVector; - explicit OpIter(const ModuleEnvironment& env, Decoder& decoder, uint32_t offsetInModule = 0) - : d_(decoder), env_(env), offsetInModule_(offsetInModule), op_(Op::Limit), offsetOfExpr_(0) + explicit OpIter(const ModuleEnvironment& env, Decoder& decoder) + : d_(decoder), env_(env), op_(Op::Limit), offsetOfLastReadOp_(0) {} // Return the decoding byte offset. @@ -494,9 +493,14 @@ class MOZ_STACK_CLASS OpIter : private Policy return d_.currentOffset(); } - // Returning the offset within the entire module of the last-read Op. + // Return the offset within the entire module of the last-read op. + size_t errorOffset() const { + return offsetOfLastReadOp_ ? offsetOfLastReadOp_ : d_.currentOffset(); + } + + // Return a TrapOffset describing where the current op should be reported to trap. TrapOffset trapOffset() const { - return TrapOffset(offsetInModule_ + offsetOfExpr_); + return TrapOffset(errorOffset()); } // Test whether the iterator has reached the end of the buffer. @@ -661,7 +665,7 @@ template inline bool OpIter::fail(const char* msg) { - return d_.fail("%s", msg); + return d_.fail(errorOffset(), msg); } // This function pops exactly one value from the stack, yielding Any types in @@ -904,7 +908,7 @@ OpIter::readOp(uint16_t* op) { MOZ_ASSERT(!controlStack_.empty()); - offsetOfExpr_ = d_.currentOffset(); + offsetOfLastReadOp_ = d_.currentOffset(); if (Validate) { if (MOZ_UNLIKELY(!d_.readOp(op))) diff --git a/js/src/wasm/WasmBinaryToAST.cpp b/js/src/wasm/WasmBinaryToAST.cpp index 0296ed8a7d51..6c03543b54f5 100644 --- a/js/src/wasm/WasmBinaryToAST.cpp +++ b/js/src/wasm/WasmBinaryToAST.cpp @@ -1855,7 +1855,7 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAllo return false; UniqueChars error; - Decoder d(bytes, bytes + length, &error, /* resilient */ true); + Decoder d(bytes, bytes + length, 0, &error, /* resilient */ true); AstDecodeContext c(cx, lifo, d, *result, true); if (!AstDecodeEnvironment(c) || diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index bb65b45adad4..623d95095592 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -88,6 +88,8 @@ AllocateCodeSegment(JSContext* cx, uint32_t codeLength) return nullptr; } + cx->zone()->updateJitCodeMallocBytes(codeLength); + wasmCodeAllocations++; return (uint8_t*)p; } @@ -259,7 +261,6 @@ CodeSegment::~CodeSegment() if (!bytes_) return; - MOZ_ASSERT(wasmCodeAllocations > 0); wasmCodeAllocations--; @@ -271,7 +272,6 @@ CodeSegment::~CodeSegment() vtune::UnmarkBytes(bytes_, size); #endif DeallocateExecutableMemory(bytes_, size); - } void diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 5a5916f031b6..0dbf45ec80e0 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -110,7 +110,7 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha { MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers()); - Decoder d(bytecode.begin(), bytecode.end(), error); + Decoder d(bytecode.bytes, error); auto env = js::MakeUnique(); if (!env) diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index 3953dbd46e67..0d468408d60b 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -176,7 +176,7 @@ class FunctionCompiler const ValTypeVector& locals, MIRGenerator& mirGen) : env_(env), - iter_(env, decoder, func.lineOrBytecode()), + iter_(env, decoder), func_(func), locals_(locals), lastReadCallSite_(0), @@ -1509,7 +1509,7 @@ class FunctionCompiler uint32_t readCallSiteLineOrBytecode() { if (!func_.callSiteLineNums().empty()) return func_.callSiteLineNums()[lastReadCallSite_++]; - return iter_.trapOffset().bytecodeOffset; + return iter_.errorOffset(); } bool done() const { return iter_.done(); } @@ -2997,7 +2997,7 @@ EmitCurrentMemory(FunctionCompiler& f) if (!f.passInstance(&args)) return false; - f.finishCall(&args, TlsUsage::Unused); + f.finishCall(&args, TlsUsage::Need); MDefinition* ret; if (!f.builtinInstanceMethodCall(SymbolicAddress::CurrentMemory, args, ValType::I32, &ret)) @@ -3618,7 +3618,7 @@ wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* const ModuleEnvironment& env = task->env(); uint32_t bodySize = func.bytes().length(); - Decoder d(func.bytes(), error); + Decoder d(func.bytes().begin(), func.bytes().end(), func.lineOrBytecode(), error); if (!env.isAsmJS()) { if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d)) @@ -3645,18 +3645,12 @@ wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* IonOptimizations.get(OptimizationLevel::Wasm)); mir.initMinWasmHeapLength(env.minMemoryLength); - // Capture the prologue's trap site before decoding the function. - - TrapOffset prologueTrapOffset; - // Build MIR graph { FunctionCompiler f(env, d, func, locals, mir); if (!f.init()) return false; - prologueTrapOffset = f.iter().trapOffset(); - if (!f.startBlock()) return false; @@ -3695,6 +3689,7 @@ wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* CodeGenerator codegen(&mir, lir, &task->masm()); + TrapOffset prologueTrapOffset(func.lineOrBytecode()); FuncOffsets offsets; if (!codegen.generateWasm(sigId, prologueTrapOffset, &offsets)) return false; diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index 09a19f0a38c8..c68d152f17f3 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -34,7 +34,7 @@ using mozilla::CheckedInt; // Decoder implementation. bool -Decoder::fail(const char* msg, ...) +Decoder::failf(const char* msg, ...) { va_list ap; va_start(ap, msg); @@ -43,14 +43,14 @@ Decoder::fail(const char* msg, ...) if (!str) return false; - return fail(Move(str)); + return fail(str.get()); } bool -Decoder::fail(UniqueChars msg) +Decoder::fail(size_t errorOffset, const char* msg) { MOZ_ASSERT(error_); - UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get())); + UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", errorOffset, msg)); if (!strWithOffset) return false; @@ -110,7 +110,7 @@ Decoder::startSection(SectionId id, ModuleEnvironment* env, uint32_t* sectionSta return true; fail: - return fail("failed to start %s section", sectionName); + return failf("failed to start %s section", sectionName); } bool @@ -119,7 +119,7 @@ Decoder::finishSection(uint32_t sectionStart, uint32_t sectionSize, const char* if (resilientMode_) return true; if (sectionSize != (cur_ - beg_) - sectionStart) - return fail("byte size mismatch in %s section", sectionName); + return failf("byte size mismatch in %s section", sectionName); return true; } @@ -617,9 +617,9 @@ DecodePreamble(Decoder& d) if (!d.readFixedU32(&u32) || u32 != MagicNumber) return d.fail("failed to match magic number"); - if (!d.readFixedU32(&u32) || (u32 != EncodingVersion && u32 != PrevEncodingVersion)) { - return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32, - u32, EncodingVersion); + if (!d.readFixedU32(&u32) || u32 != EncodingVersion) { + return d.failf("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32, + u32, EncodingVersion); } return true; @@ -735,7 +735,7 @@ DecodeLimits(Decoder& d, Limits* limits) return d.fail("expected flags"); if (flags & ~uint32_t(0x1)) - return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1))); + return d.failf("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1))); if (!d.readVarU32(&limits->initial)) return d.fail("expected initial length"); @@ -746,9 +746,9 @@ DecodeLimits(Decoder& d, Limits* limits) return d.fail("expected maximum length"); if (limits->initial > maximum) { - return d.fail("memory size minimum must not be greater than maximum; " - "maximum length %" PRIu32 " is less than initial length %" PRIu32, - maximum, limits->initial); + return d.failf("memory size minimum must not be greater than maximum; " + "maximum length %" PRIu32 " is less than initial length %" PRIu32, + maximum, limits->initial); } limits->maximum.emplace(maximum); @@ -1580,7 +1580,7 @@ wasm::DecodeModuleTail(Decoder& d, ModuleEnvironment* env) bool wasm::Validate(const ShareableBytes& bytecode, UniqueChars* error) { - Decoder d(bytecode.begin(), bytecode.end(), error); + Decoder d(bytecode.bytes, error); ModuleEnvironment env; if (!DecodeModuleEnvironment(d, &env)) diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 49dfe2eb31fc..cae95917abb9 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -302,6 +302,7 @@ class Decoder const uint8_t* const beg_; const uint8_t* const end_; const uint8_t* cur_; + const size_t offsetInModule_; UniqueChars* error_; bool resilientMode_; @@ -384,11 +385,12 @@ class Decoder } public: - Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error, + Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule, UniqueChars* error, bool resilientMode = false) : beg_(begin), end_(end), cur_(begin), + offsetInModule_(offsetInModule), error_(error), resilientMode_(resilientMode) { @@ -398,12 +400,18 @@ class Decoder : beg_(bytes.begin()), end_(bytes.end()), cur_(bytes.begin()), + offsetInModule_(0), error_(error), resilientMode_(false) {} - bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3); - bool fail(UniqueChars msg); + // These convenience functions use currentOffset() as the errorOffset. + bool fail(const char* msg) { return fail(currentOffset(), msg); } + bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3); + + // Report an error at the given offset (relative to the whole module). + bool fail(size_t errorOffset, const char* msg); + void clearError() { if (error_) error_->reset(); @@ -429,7 +437,7 @@ class Decoder return cur_; } size_t currentOffset() const { - return cur_ - beg_; + return offsetInModule_ + (cur_ - beg_); } const uint8_t* begin() const { return beg_; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 7a4f71975ecd..8b1876ff3b9a 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3020,14 +3020,8 @@ nsXPCComponents_Utils::NukeSandbox(HandleValue obj, JSContext* cx) NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG); RootedObject sb(cx, UncheckedUnwrap(wrapper)); NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG); - NukeCrossCompartmentWrappers(cx, AllCompartments(), - SingleCompartment(GetObjectCompartment(sb)), - NukeWindowReferences); - // Now mark the compartment as nuked and non-scriptable. - auto compartmentPrivate = xpc::CompartmentPrivate::Get(sb); - compartmentPrivate->wasNuked = true; - compartmentPrivate->scriptability.Block(); + xpc::NukeAllWrappersForCompartment(cx, GetObjectCompartment(sb)); return NS_OK; } diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 7cc451990926..d52bd6f1b9ea 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -580,6 +580,37 @@ CurrentWindowOrNull(JSContext* cx) return glob ? WindowOrNull(glob) : nullptr; } +// Nukes all wrappers into or out of the given compartment, and prevents new +// wrappers from being created. Additionally marks the compartment as +// unscriptable after wrappers have been nuked. +// +// Note: This should *only* be called for browser or extension compartments. +// Wrappers between web compartments must never be cut in web-observable +// ways. +void +NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment, + js::NukeReferencesToWindow nukeReferencesToWindow) +{ + // First, nuke all wrappers into or out of the target compartment. Once + // the compartment is marked as nuked, WrapperFactory will refuse to + // create new live wrappers for it, in either direction. This means that + // we need to be sure that we don't have any existing cross-compartment + // wrappers which may be replaced with dead wrappers during unrelated + // wrapper recomputation *before* we set that bit. + js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(), + js::SingleCompartment(compartment), + nukeReferencesToWindow, + js::NukeAllReferences); + + // At this point, we should cross-compartment wrappers for the nuked + // compartment. Set the wasNuked bit so WrapperFactory will return a + // DeadObjectProxy when asked to create a new wrapper for it, and mark as + // unscriptable. + auto compartmentPrivate = xpc::CompartmentPrivate::Get(compartment); + compartmentPrivate->wasNuked = true; + compartmentPrivate->scriptability.Block(); +} + } // namespace xpc static void diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 38375d00a17e..5115b7ea198f 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -400,6 +400,9 @@ bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str, nsIPrincipal* GetCompartmentPrincipal(JSCompartment* compartment); +void NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment, + js::NukeReferencesToWindow nukeReferencesToWindow = js::NukeWindowReferences); + void SetLocationForGlobal(JSObject* global, const nsACString& location); void SetLocationForGlobal(JSObject* global, nsIURI* locationURI); diff --git a/js/xpconnect/tests/mochitest/mochitest.ini b/js/xpconnect/tests/mochitest/mochitest.ini index de6759e7ab7c..ffac4cc8f4a6 100644 --- a/js/xpconnect/tests/mochitest/mochitest.ini +++ b/js/xpconnect/tests/mochitest/mochitest.ini @@ -101,6 +101,7 @@ support-files = [test_getWebIDLCaller.html] skip-if = (debug == false || os == "android") [test_nac.xhtml] +[test_nukeContentWindow.html] [test_sameOriginPolicy.html] [test_sandbox_fetch.html] support-files = diff --git a/js/xpconnect/tests/mochitest/test_nukeContentWindow.html b/js/xpconnect/tests/mochitest/test_nukeContentWindow.html new file mode 100644 index 000000000000..ce2fd2a89356 --- /dev/null +++ b/js/xpconnect/tests/mochitest/test_nukeContentWindow.html @@ -0,0 +1,61 @@ + + + + + + Test for Bug 1322273 + + + + + +Mozilla Bug 1322273 + + + + + + + diff --git a/js/xpconnect/tests/unit/test_nuke_sandbox.js b/js/xpconnect/tests/unit/test_nuke_sandbox.js index a4dd25498af1..9659902610d8 100644 --- a/js/xpconnect/tests/unit/test_nuke_sandbox.js +++ b/js/xpconnect/tests/unit/test_nuke_sandbox.js @@ -4,20 +4,46 @@ /* See https://bugzilla.mozilla.org/show_bug.cgi?id=769273 */ +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +const global = this; + function run_test() { - var sb = Components.utils.Sandbox("http://www.blah.com"); + var ifacePointer = Cc["@mozilla.org/supports-interface-pointer;1"] + .createInstance(Ci.nsISupportsInterfacePointer); + + var sb = Cu.Sandbox(global); sb.prop = "prop" - var refToObjFromSb = Components.utils.evalInSandbox("var a = {prop2:'prop2'}; a", sb); - Components.utils.nukeSandbox(sb); - do_check_true(Components.utils.isDeadWrapper(sb), "sb should be dead"); + sb.ifacePointer = ifacePointer + + var refToObjFromSb = Cu.evalInSandbox(` + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + + ifacePointer.data = { + QueryInterface: XPCOMUtils.generateQI([]), + wrappedJSObject: {foo: "bar"}, + }; + + var a = {prop2:'prop2'}; + a + `, sb); + + equal(ifacePointer.data.wrappedJSObject.foo, "bar", + "Got expected wrapper into sandbox") + + Cu.nukeSandbox(sb); + ok(Cu.isDeadWrapper(sb), "sb should be dead"); + ok(Cu.isDeadWrapper(ifacePointer.data.wrappedJSObject), + "Wrapper retrieved via XPConnect should be dead"); + try{ sb.prop; do_check_true(false); } catch (e) { do_check_true(e.toString().indexOf("can't access dead object") > -1); } - + Components.utils.isDeadWrapper(refToObjFromSb, "ref to object from sb should be dead"); try{ refToObjFromSb.prop2; @@ -25,5 +51,4 @@ function run_test() } catch (e) { do_check_true(e.toString().indexOf("can't access dead object") > -1); } - } diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index bdb727841f08..c9ae96b7ff92 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -181,6 +181,24 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope, ExposeObjectToActiveJS(obj); } + // If we've somehow gotten to this point after either the source or target + // compartment has been nuked, return a DeadObjectProxy to prevent further + // access. + JSCompartment* origin = js::GetObjectCompartment(obj); + JSCompartment* target = js::GetObjectCompartment(scope); + if (CompartmentPrivate::Get(origin)->wasNuked || + CompartmentPrivate::Get(target)->wasNuked) { + NS_WARNING("Trying to create a wrapper into or out of a nuked compartment"); + + RootedObject ccw(cx, Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton)); + + NukeCrossCompartmentWrapper(cx, ccw); + + retObj.set(ccw); + return; + } + + // If we've got a WindowProxy, there's nothing special that needs to be // done here, and we can move on to the next phase of wrapping. We handle // this case first to allow us to assert against wrappers below. @@ -332,8 +350,10 @@ DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper* handler, JSCompartment* origin, JSCompartment* target) { if (CompartmentPrivate::Get(origin)->wasNuked || CompartmentPrivate::Get(target)->wasNuked) { - // If either compartment has already been nuked, we should have an opaque wrapper. - MOZ_ASSERT(handler->hasSecurityPolicy()); + // If either compartment has already been nuked, we should have returned + // a dead wrapper from our prewrap callback, and this function should + // not be called. + MOZ_ASSERT_UNREACHABLE("CheckUnwrapSafety called for a dead wrapper"); } else if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) { // If the caller is chrome (or effectively so), unwrap should always be allowed. MOZ_ASSERT(!handler->hasSecurityPolicy()); @@ -457,21 +477,9 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) // First, handle the special cases. // - // If we've somehow gotten to this point after either the source or target - // compartment has been nuked, return an opaque wrapper to prevent further - // access. - // Ideally, we should return a DeadProxyObject instead of a wrapper in this - // case (bug 1322273). - if (CompartmentPrivate::Get(origin)->wasNuked || - CompartmentPrivate::Get(target)->wasNuked) { - NS_WARNING("Trying to create a wrapper into or out of a nuked compartment"); - - wrapper = &FilteringWrapper::singleton; - } - // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. - else if (xpc::IsUniversalXPConnectEnabled(target)) { + if (xpc::IsUniversalXPConnectEnabled(target)) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } diff --git a/layout/painting/moz.build b/layout/painting/moz.build index 508e8d95edbf..932bc6d3256b 100644 --- a/layout/painting/moz.build +++ b/layout/painting/moz.build @@ -15,6 +15,7 @@ EXPORTS += [ 'DisplayListClipState.h', 'FrameLayerBuilder.h', 'LayerState.h', + 'nsCSSRenderingBorders.h', 'nsDisplayItemTypes.h', 'nsDisplayItemTypesList.h', 'nsDisplayList.h', diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index 6f24c0f7ff54..885eb4597127 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -948,13 +948,13 @@ GetOutlineInnerRect(nsIFrame* aFrame) return nsRect(nsPoint(0, 0), aFrame->GetSize()); } -void -nsCSSRendering::PaintOutline(nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, - nsIFrame* aForFrame, - const nsRect& aDirtyRect, - const nsRect& aBorderArea, - nsStyleContext* aStyleContext) +Maybe +nsCSSRendering::CreateBorderRendererForOutline(nsPresContext* aPresContext, + nsRenderingContext* aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aDirtyRect, + const nsRect& aBorderArea, + nsStyleContext* aStyleContext) { nscoord twipsRadii[8]; @@ -968,7 +968,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, if (width == 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) { // Empty outline - return; + return Nothing(); } nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame @@ -997,7 +997,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, // encroach into the content area. A safer calculation would be to // shorten insideRect by the radius one each side before performing this test. if (innerRect.Contains(aDirtyRect)) - return; + return Nothing(); nsRect outerRect = innerRect; outerRect.Inflate(width, width); @@ -1022,14 +1022,14 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, nsITheme* theme = aPresContext->GetTheme(); if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, NS_THEME_FOCUS_OUTLINE)) { - theme->DrawWidgetBackground(&aRenderingContext, aForFrame, + theme->DrawWidgetBackground(aRenderingContext, aForFrame, NS_THEME_FOCUS_OUTLINE, innerRect, aDirtyRect); - return; + return Nothing(); } } if (width == 0) { - return; // empty outline + return Nothing(); // empty outline } // http://dev.w3.org/csswg/css-ui/#outline // "User agents may treat 'auto' as 'solid'." @@ -1061,11 +1061,10 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, document = content->OwnerDoc(); } - // start drawing - + DrawTarget* dt = aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr; nsCSSBorderRenderer br(aPresContext, document, - aRenderingContext.GetDrawTarget(), + dt, dirtyRect, oRect, outlineStyles, @@ -1074,7 +1073,30 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, outlineColors, nullptr, bgColor); - br.DrawBorders(); + + return Some(br); +} + +void +nsCSSRendering::PaintOutline(nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aDirtyRect, + const nsRect& aBorderArea, + nsStyleContext* aStyleContext) +{ + Maybe br = CreateBorderRendererForOutline(aPresContext, + &aRenderingContext, + aForFrame, + aDirtyRect, + aBorderArea, + aStyleContext); + if (!br) { + return; + } + + // start drawing + br->DrawBorders(); PrintAsStringNewline(); } diff --git a/layout/painting/nsCSSRendering.h b/layout/painting/nsCSSRendering.h index 3c8007dae08f..334b57831573 100644 --- a/layout/painting/nsCSSRendering.h +++ b/layout/painting/nsCSSRendering.h @@ -450,6 +450,13 @@ struct nsCSSRendering { nsStyleContext* aStyleContext, Sides aSkipSides = Sides()); + static mozilla::Maybe + CreateBorderRendererForOutline(nsPresContext* aPresContext, + nsRenderingContext* aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aDirtyRect, + const nsRect& aBorderArea, + nsStyleContext* aStyleContext); /** * Render the outline for an element using css rendering rules diff --git a/layout/painting/nsCSSRenderingBorders.h b/layout/painting/nsCSSRenderingBorders.h index 65a25f8e72d8..26c96b35c1ad 100644 --- a/layout/painting/nsCSSRenderingBorders.h +++ b/layout/painting/nsCSSRenderingBorders.h @@ -78,6 +78,7 @@ class nsCSSBorderRenderer final typedef mozilla::gfx::StrokeOptions StrokeOptions; friend class nsDisplayBorder; + friend class nsDisplayOutline; public: @@ -121,7 +122,7 @@ private: // destination DrawTarget and dirty rect DrawTarget* mDrawTarget; - const Rect mDirtyRect; + Rect mDirtyRect; // the rectangle of the outside and the inside of the border Rect mOuterRect; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index ab473b5ebb72..8f3fcbf9346a 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -85,6 +85,7 @@ #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/layers/WebRenderDisplayItemLayer.h" #include "mozilla/layers/WebRenderMessages.h" +#include "mozilla/layers/WebRenderDisplayItemLayer.h" // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount(). @@ -4095,6 +4096,72 @@ nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, mFrame->StyleContext()); } +LayerState +nsDisplayOutline::GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) +{ + if (!gfxPrefs::LayersAllowOutlineLayers()) { + return LAYER_NONE; + } + + uint8_t outlineStyle = mFrame->StyleContext()->StyleOutline()->mOutlineStyle; + if (outlineStyle == NS_STYLE_BORDER_STYLE_AUTO && nsLayoutUtils::IsOutlineStyleAutoEnabled()) { + nsITheme* theme = mFrame->PresContext()->GetTheme(); + if (theme && theme->ThemeSupportsWidget(mFrame->PresContext(), mFrame, + NS_THEME_FOCUS_OUTLINE)) { + return LAYER_NONE; + } + } + + nsPoint offset = ToReferenceFrame(); + Maybe br = + nsCSSRendering::CreateBorderRendererForOutline(mFrame->PresContext(), + nullptr, mFrame, + mVisibleRect, + nsRect(offset, mFrame->GetSize()), + mFrame->StyleContext()); + + if (!br) { + return LAYER_NONE; + } + + mBorderRenderer = br; + + return LAYER_ACTIVE; +} + +already_AddRefed +nsDisplayOutline::BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) +{ + return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters); +} + +void +nsDisplayOutline::CreateWebRenderCommands(nsTArray& aCommands, + WebRenderDisplayItemLayer* aLayer) +{ + MOZ_ASSERT(mBorderRenderer.isSome()); + + Rect outlineTransformedRect = aLayer->RelativeToParent(mBorderRenderer->mOuterRect); + + nsCSSBorderRenderer* br = mBorderRenderer.ptr(); + WrBorderSide side[4]; + NS_FOR_CSS_SIDES(i) { + side[i] = wr::ToWrBorderSide(br->mBorderWidths[i], ToDeviceColor(br->mBorderColors[i]), br->mBorderStyles[i]); + } + WrBorderRadius borderRadius = wr::ToWrBorderRadius(LayerSize(br->mBorderRadii[0].width, br->mBorderRadii[0].height), + LayerSize(br->mBorderRadii[1].width, br->mBorderRadii[1].height), + LayerSize(br->mBorderRadii[3].width, br->mBorderRadii[3].height), + LayerSize(br->mBorderRadii[2].width, br->mBorderRadii[2].height)); + aCommands.AppendElement(OpDPPushBorder(wr::ToWrRect(outlineTransformedRect), + wr::ToWrRect(outlineTransformedRect), + side[0], side[1], side[2], side[3], + borderRadius)); +} + bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) { diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 4d7ecaffe48b..747ac90cb4a2 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -34,6 +34,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/gfx/UserData.h" +#include "nsCSSRenderingBorders.h" #include #include "nsTHashtable.h" @@ -3450,10 +3451,20 @@ public: } #endif + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) override; + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) override; + virtual void CreateWebRenderCommands(nsTArray& aCommands, + mozilla::layers::WebRenderDisplayItemLayer* aLayer) override; virtual bool IsInvisibleInRect(const nsRect& aRect) override; virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override; virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override; NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE) + + mozilla::Maybe mBorderRenderer; }; /** diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index 57599be44352..da8a559eeb46 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -264,7 +264,6 @@ nsFilterInstance::ComputeTargetBBoxInFilterSpace() bool nsFilterInstance::ComputeUserSpaceToFilterSpaceScale() { - gfxMatrix canvasTransform; if (mTargetFrame) { mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors(true); if (mUserSpaceToFilterSpaceScale.width <= 0.0f || @@ -279,6 +278,7 @@ nsFilterInstance::ComputeUserSpaceToFilterSpaceScale() mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width, 1.0f / mUserSpaceToFilterSpaceScale.height); + return true; } diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp index 49eb2797020b..4019119651a7 100644 --- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -1546,6 +1546,22 @@ NopStackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp, } #endif +static void +prefork() +{ + if (gStateLock) { + gStateLock->Lock(); + } +} + +static void +postfork() +{ + if (gStateLock) { + gStateLock->Unlock(); + } +} + // WARNING: this function runs *very* early -- before all static initializers // have run. For this reason, non-scalar globals such as gStateLock and // gStackTraceTable are allocated dynamically (so we can guarantee their @@ -1556,6 +1572,16 @@ Init(const malloc_table_t* aMallocTable) gMallocTable = aMallocTable; gDMDBridge = InfallibleAllocPolicy::new_(); +#ifndef XP_WIN + // Avoid deadlocks when forking by acquiring our state lock prior to forking + // and releasing it after forking. See |LogAlloc|'s |replace_init| for + // in-depth details. + // + // Note: This must run after attempting an allocation so as to give the + // system malloc a chance to insert its own atfork handler. + pthread_atfork(prefork, postfork, postfork); +#endif + // DMD is controlled by the |DMD| environment variable. const char* e = getenv("DMD"); diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index 4abe5fd9734c..c987cc67d723 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -2070,8 +2070,8 @@ public class BrowserApp extends GeckoApp request.setMimeType(mimeType); try { - request.setDestinationInExternalFilesDir( - this, Environment.DIRECTORY_DOWNLOADS, filename); + request.setDestinationInExternalPublicDir( + Environment.DIRECTORY_DOWNLOADS, filename); } catch (IllegalStateException e) { Log.e(LOGTAG, "Cannot create download directory"); break; diff --git a/mobile/android/components/HelperAppDialog.js b/mobile/android/components/HelperAppDialog.js index be83983c8178..9769bc1fa2d6 100644 --- a/mobile/android/components/HelperAppDialog.js +++ b/mobile/android/components/HelperAppDialog.js @@ -137,8 +137,16 @@ HelperAppLauncherDialog.prototype = { } if (this._shouldForwardToAndroidDownloadManager(aLauncher)) { - this._downloadWithAndroidDownloadManager(aLauncher); - aLauncher.cancel(Cr.NS_BINDING_ABORTED); + Task.spawn(function* () { + try { + let hasPermission = yield RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE); + if (hasPermission) { + this._downloadWithAndroidDownloadManager(aLauncher); + aLauncher.cancel(Cr.NS_BINDING_ABORTED); + } + } finally { + } + }.bind(this)).catch(Cu.reportError); return; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java index e5bf6551bd23..56f782915135 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java @@ -102,20 +102,19 @@ public class StringUtils { return url; } - int start = 0; - int end = url.length(); + String newURL = url; - if (url.startsWith("http://")) { - start = 7; - } else if (url.startsWith("https://") && flags == UrlFlags.STRIP_HTTPS) { - start = 8; + if (newURL.startsWith("http://")) { + newURL = newURL.replace("http://", ""); + } else if (newURL.startsWith("https://") && flags == UrlFlags.STRIP_HTTPS) { + newURL = newURL.replace("https://", ""); } - if (url.endsWith("/")) { - end--; + if (newURL.endsWith("/")) { + newURL = newURL.substring(0, newURL.length()-1); } - return url.substring(start, end); + return newURL; } public static boolean isHttpOrHttps(String url) { diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index b1f86b9e0f62..077289492076 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -60,11 +60,22 @@ return NS_ERROR_NOT_AVAILABLE; \ } \ } while (0); +class WatchinPrefRAII { +public: + WatchinPrefRAII() { + pref_SetWatchingPref(true); + } + ~WatchinPrefRAII() { + pref_SetWatchingPref(false); + } +}; +#define WATCHING_PREF_RAII() WatchinPrefRAII watchingPrefRAII #else #define ENSURE_MAIN_PROCESS(message, pref) \ if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \ return NS_ERROR_NOT_AVAILABLE; \ } +#define WATCHING_PREF_RAII() #endif class PrefCallback; @@ -1732,6 +1743,7 @@ Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback, void* aClosure, MatchKind aMatchKind) { + WATCHING_PREF_RAII(); nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind); if (NS_SUCCEEDED(rv)) { (*aCallback)(aPref, aClosure); @@ -1779,6 +1791,7 @@ Preferences::AddBoolVarCache(bool* aCache, const char* aPref, bool aDefault) { + WATCHING_PREF_RAII(); NS_ASSERTION(aCache, "aCache must not be NULL"); #ifdef DEBUG AssertNotAlreadyCached("bool", aPref, aCache); @@ -1804,6 +1817,7 @@ Preferences::AddIntVarCache(int32_t* aCache, const char* aPref, int32_t aDefault) { + WATCHING_PREF_RAII(); NS_ASSERTION(aCache, "aCache must not be NULL"); #ifdef DEBUG AssertNotAlreadyCached("int", aPref, aCache); @@ -1829,6 +1843,7 @@ Preferences::AddUintVarCache(uint32_t* aCache, const char* aPref, uint32_t aDefault) { + WATCHING_PREF_RAII(); NS_ASSERTION(aCache, "aCache must not be NULL"); #ifdef DEBUG AssertNotAlreadyCached("uint", aPref, aCache); @@ -1856,6 +1871,7 @@ Preferences::AddAtomicUintVarCache(Atomic* aCache, const char* aPref, uint32_t aDefault) { + WATCHING_PREF_RAII(); NS_ASSERTION(aCache, "aCache must not be NULL"); #ifdef DEBUG AssertNotAlreadyCached("uint", aPref, aCache); @@ -1888,6 +1904,7 @@ Preferences::AddFloatVarCache(float* aCache, const char* aPref, float aDefault) { + WATCHING_PREF_RAII(); NS_ASSERTION(aCache, "aCache must not be NULL"); #ifdef DEBUG AssertNotAlreadyCached("float", aPref, aCache); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 649c6b1a60d1..3c94c61460c9 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1273,7 +1273,7 @@ pref("javascript.options.strict.debug", false); pref("javascript.options.baselinejit", true); pref("javascript.options.ion", true); pref("javascript.options.asmjs", true); -pref("javascript.options.wasm", false); +pref("javascript.options.wasm", true); pref("javascript.options.wasm_baselinejit", false); pref("javascript.options.native_regexp", true); pref("javascript.options.parallel_parsing", true); diff --git a/modules/libpref/prefapi.cpp b/modules/libpref/prefapi.cpp index b9052c5d95dc..65d984d0f6bb 100644 --- a/modules/libpref/prefapi.cpp +++ b/modules/libpref/prefapi.cpp @@ -737,12 +737,21 @@ static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags #ifdef DEBUG static pref_initPhase gPhase = START; +static bool gWatchingPref = false; + void pref_SetInitPhase(pref_initPhase phase) { gPhase = phase; } +void +pref_SetWatchingPref(bool watching) +{ + gWatchingPref = watching; +} + + struct StringComparator { const char* mKey; @@ -774,8 +783,13 @@ PrefHashEntry* pref_HashTableLookup(const char *key) * Consider moving it later or add it to the whitelist in ContentPrefs.cpp * and get review from a DOM peer */ - MOZ_ASSERT((!XRE_IsContentProcess() || gPhase > END_INIT_PREFS || inInitArray(key)), - "accessing non-init pref before the rest of the prefs are sent"); +#ifdef DEBUG + if (XRE_IsContentProcess() && gPhase <= END_INIT_PREFS && + !gWatchingPref && !inInitArray(key)) { + MOZ_CRASH_UNSAFE_PRINTF("accessing non-init pref %s before the rest of the prefs are sent", + key); + } +#endif return static_cast(gHashTable->Search(key)); } diff --git a/modules/libpref/prefapi_private_data.h b/modules/libpref/prefapi_private_data.h index 4cf58f6a9f85..b7c49dafe804 100644 --- a/modules/libpref/prefapi_private_data.h +++ b/modules/libpref/prefapi_private_data.h @@ -29,6 +29,9 @@ pref_SetPref(const mozilla::dom::PrefSetting& aPref); #ifdef DEBUG void pref_SetInitPhase(pref_initPhase phase); + +void +pref_SetWatchingPref(bool watching); #endif int pref_CompareStrings(const void *v1, const void *v2, void* unused); diff --git a/netwerk/test/unit/head_channels.js b/netwerk/test/unit/head_channels.js index 6e37a18bc950..37c958dc811c 100644 --- a/netwerk/test/unit/head_channels.js +++ b/netwerk/test/unit/head_channels.js @@ -172,6 +172,7 @@ ChannelListener.prototype = { } try { this._closure(request, this._buffer, this._closurectx, this._isFromCache); + this._closurectx = null; } catch (ex) { do_throw("Error in closure function: " + ex); } diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 1e46d8d72e9f..b605fd7e745b 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -1077,6 +1077,7 @@ class RecursiveMakeBackend(CommonBackend): target_variable, target_cargo_variable): backend_file.write_once('CARGO_FILE := %s\n' % obj.cargo_file) + backend_file.write_once('CARGO_TARGET_DIR := .\n') backend_file.write('%s += %s\n' % (target_variable, obj.location)) backend_file.write('%s += %s\n' % (target_cargo_variable, obj.name)) @@ -1250,6 +1251,13 @@ class RecursiveMakeBackend(CommonBackend): feature_var = 'HOST_RUST_LIBRARY_FEATURES' backend_file.write_once('%s := %s\n' % (libdef.LIB_FILE_VAR, libdef.import_name)) backend_file.write('CARGO_FILE := $(srcdir)/Cargo.toml\n') + # Need to normalize the path so Cargo sees the same paths from all + # possible invocations of Cargo with this CARGO_TARGET_DIR. Otherwise, + # Cargo's dependency calculations don't work as we expect and we wind + # up recompiling lots of things. + target_dir = mozpath.join(backend_file.objdir, libdef.target_dir) + target_dir = mozpath.normpath(target_dir) + backend_file.write('CARGO_TARGET_DIR := %s\n' % target_dir) if libdef.features: backend_file.write('%s := %s\n' % (libdef.FEATURES_VAR, ' '.join(libdef.features))) diff --git a/python/mozbuild/mozbuild/backend/test_manifest.py b/python/mozbuild/mozbuild/backend/test_manifest.py index 4fb3b548abc9..0cfa59fb7772 100644 --- a/python/mozbuild/mozbuild/backend/test_manifest.py +++ b/python/mozbuild/mozbuild/backend/test_manifest.py @@ -32,6 +32,9 @@ class TestManifestBackend(PartialBackend): self.backend_input_files.add(obj.path) self.backend_input_files |= obj.context_all_paths + for source in obj.source_relpaths: + self.backend_input_files.add(mozpath.join(obj.topsrcdir, + source)) try: from reftest import ReftestManifest diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index 00560e85b606..0e784d835673 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -959,6 +959,15 @@ VARIABLES = { RustLibrary template instead. """), + 'RUST_LIBRARY_TARGET_DIR': (unicode, unicode, + """Where CARGO_TARGET_DIR should point when compiling this library. If + not set, it defaults to the current objdir. It should be a relative path + to the current objdir; absolute paths should not be used. + + This variable should not be used directly; you should be using the + RustLibrary template instead. + """), + 'HOST_RUST_LIBRARY_FEATURES': (List, list, """Cargo features to activate for this host library. diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index 5c5893c28d25..5b07dafdddcc 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -420,7 +420,7 @@ class HostSimpleProgram(HostMixin, BaseProgram): KIND = 'host' -def cargo_target_directory(context, target_var): +def cargo_output_directory(context, target_var): # cargo creates several directories and places its build artifacts # in those directories. The directory structure depends not only # on the target, but also what sort of build we are doing. @@ -446,7 +446,7 @@ class BaseRustProgram(ContextDerived): ContextDerived.__init__(self, context) self.name = name self.cargo_file = cargo_file - cargo_dir = cargo_target_directory(context, self.TARGET_SUBST_VAR) + cargo_dir = cargo_output_directory(context, self.TARGET_SUBST_VAR) exe_file = '%s%s' % (name, context.config.substs.get(self.SUFFIX_VAR, '')) self.location = mozpath.join(cargo_dir, exe_file) @@ -523,13 +523,14 @@ class RustLibrary(StaticLibrary): 'dependencies', 'deps_path', 'features', + 'target_dir', ) TARGET_SUBST_VAR = 'RUST_TARGET' FEATURES_VAR = 'RUST_LIBRARY_FEATURES' LIB_FILE_VAR = 'RUST_LIBRARY_FILE' def __init__(self, context, basename, cargo_file, crate_type, dependencies, - features, **args): + features, target_dir, **args): StaticLibrary.__init__(self, context, basename, **args) self.cargo_file = cargo_file self.crate_type = crate_type @@ -542,10 +543,12 @@ class RustLibrary(StaticLibrary): basename.replace('-', '_'), context.config.lib_suffix) self.dependencies = dependencies - build_dir = cargo_target_directory(context, self.TARGET_SUBST_VAR) + build_dir = mozpath.join(target_dir, + cargo_output_directory(context, self.TARGET_SUBST_VAR)) self.import_name = mozpath.join(build_dir, self.lib_name) self.deps_path = mozpath.join(build_dir, 'deps') self.features = features + self.target_dir = target_dir class SharedLibrary(Library): diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index d518bd20a32c..8de24fd8ee45 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -465,6 +465,36 @@ class TreeMetadataEmitter(LoggingMixin): ' in [profile.%s] section') % (libname, profile_name), context) + # gkrust and gkrust-gtest must have the exact same profile settings + # for our almost-workspaces configuration to work properly. + if libname in ('gkrust', 'gkrust-gtest'): + if profile_name == 'dev': + expected_profile = { + 'opt-level': 1, + 'debug': True, + 'rpath': False, + 'lto': False, + 'debug-assertions': True, + 'codegen-units': 1, + 'panic': 'abort', + } + else: + expected_profile = { + 'opt-level': 2, + 'debug': True, + 'rpath': False, + 'lto': True, + 'debug-assertions': False, + 'panic': 'abort', + } + + if profile != expected_profile: + raise SandboxValidationError( + 'Cargo profile.%s for %s is incorrect' % (profile_name, libname), + context) + + cargo_target_dir = context.get('RUST_LIBRARY_TARGET_DIR', '.') + dependencies = set(config.get('dependencies', {}).iterkeys()) features = context.get(cls.FEATURES_VAR, []) @@ -475,7 +505,7 @@ class TreeMetadataEmitter(LoggingMixin): context) return cls(context, libname, cargo_file, crate_type, dependencies, - features, **static_args) + features, cargo_target_dir, **static_args) def _handle_linkables(self, context, passthru, generated_files): diff --git a/python/mozbuild/mozbuild/test/backend/data/rust-library-features/Cargo.toml b/python/mozbuild/mozbuild/test/backend/data/rust-library-features/Cargo.toml index cbaeaecec8fb..756ab433420f 100644 --- a/python/mozbuild/mozbuild/test/backend/data/rust-library-features/Cargo.toml +++ b/python/mozbuild/mozbuild/test/backend/data/rust-library-features/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gkrust" +name = "feature-library" version = "0.1.0" authors = [ "Nobody ", diff --git a/python/mozbuild/mozbuild/test/backend/data/rust-library-features/moz.build b/python/mozbuild/mozbuild/test/backend/data/rust-library-features/moz.build index 5efbe4d3763d..3394fdd4e39a 100644 --- a/python/mozbuild/mozbuild/test/backend/data/rust-library-features/moz.build +++ b/python/mozbuild/mozbuild/test/backend/data/rust-library-features/moz.build @@ -16,4 +16,4 @@ def RustLibrary(name, features): RUST_LIBRARY_FEATURES = features -RustLibrary('gkrust', ['musthave', 'cantlivewithout']) +RustLibrary('feature-library', ['musthave', 'cantlivewithout']) diff --git a/python/mozbuild/mozbuild/test/backend/data/rust-library/Cargo.toml b/python/mozbuild/mozbuild/test/backend/data/rust-library/Cargo.toml index cbaeaecec8fb..bde9b929d997 100644 --- a/python/mozbuild/mozbuild/test/backend/data/rust-library/Cargo.toml +++ b/python/mozbuild/mozbuild/test/backend/data/rust-library/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gkrust" +name = "test-library" version = "0.1.0" authors = [ "Nobody ", diff --git a/python/mozbuild/mozbuild/test/backend/data/rust-library/moz.build b/python/mozbuild/mozbuild/test/backend/data/rust-library/moz.build index ec2c83d60393..970b6b4be5df 100644 --- a/python/mozbuild/mozbuild/test/backend/data/rust-library/moz.build +++ b/python/mozbuild/mozbuild/test/backend/data/rust-library/moz.build @@ -15,4 +15,4 @@ def RustLibrary(name): IS_RUST_LIBRARY = True -RustLibrary('gkrust') +RustLibrary('test-library') diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest-common.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest-common.ini new file mode 100644 index 000000000000..31d07b5af338 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest-common.ini @@ -0,0 +1 @@ +[test_bar.js] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest.ini b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest.ini new file mode 100644 index 000000000000..cf7a3c44bd42 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/mochitest.ini @@ -0,0 +1,2 @@ +[test_foo.js] +[include:mochitest-common.ini] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/moz.build b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/moz.build new file mode 100644 index 000000000000..d4003d0e0170 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/moz.build @@ -0,0 +1,6 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +MOCHITEST_MANIFESTS += [ + 'mochitest.ini', +] diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/test_bar.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/test_bar.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/test_foo.js b/python/mozbuild/mozbuild/test/backend/data/test-manifests-backend-sources/test_foo.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py index eacad57ef7d0..d4fc1b87763b 100644 --- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -762,8 +762,9 @@ class TestRecursiveMakeBackend(BackendTester): lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] expected = [ - 'RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libgkrust.a', + 'RUST_LIBRARY_FILE := ./x86_64-unknown-linux-gnu/release/libtest_library.a', 'CARGO_FILE := $(srcdir)/Cargo.toml', + 'CARGO_TARGET_DIR := %s' % env.topobjdir, ] self.assertEqual(lines, expected) @@ -776,8 +777,9 @@ class TestRecursiveMakeBackend(BackendTester): lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] expected = [ - 'HOST_RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libhostrusttool.a', + 'HOST_RUST_LIBRARY_FILE := ./x86_64-unknown-linux-gnu/release/libhostrusttool.a', 'CARGO_FILE := $(srcdir)/Cargo.toml', + 'CARGO_TARGET_DIR := %s' % env.topobjdir, ] self.assertEqual(lines, expected) @@ -790,8 +792,9 @@ class TestRecursiveMakeBackend(BackendTester): lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] expected = [ - 'HOST_RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libhostrusttool.a', + 'HOST_RUST_LIBRARY_FILE := ./x86_64-unknown-linux-gnu/release/libhostrusttool.a', 'CARGO_FILE := $(srcdir)/Cargo.toml', + 'CARGO_TARGET_DIR := %s' % env.topobjdir, 'HOST_RUST_LIBRARY_FEATURES := musthave cantlivewithout', ] @@ -805,8 +808,9 @@ class TestRecursiveMakeBackend(BackendTester): lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]] expected = [ - 'RUST_LIBRARY_FILE := x86_64-unknown-linux-gnu/release/libgkrust.a', + 'RUST_LIBRARY_FILE := ./x86_64-unknown-linux-gnu/release/libfeature_library.a', 'CARGO_FILE := $(srcdir)/Cargo.toml', + 'CARGO_TARGET_DIR := %s' % env.topobjdir, 'RUST_LIBRARY_FEATURES := musthave cantlivewithout', ] @@ -821,6 +825,7 @@ class TestRecursiveMakeBackend(BackendTester): expected = [ 'CARGO_FILE := %s/code/Cargo.toml' % env.topsrcdir, + 'CARGO_TARGET_DIR := .', 'RUST_PROGRAMS += i686-pc-windows-msvc/release/target.exe', 'RUST_CARGO_PROGRAMS += target', 'HOST_RUST_PROGRAMS += i686-pc-windows-msvc/release/host.exe', diff --git a/python/mozbuild/mozbuild/test/backend/test_test_manifest.py b/python/mozbuild/mozbuild/test/backend/test_test_manifest.py index 9bb93db855e3..250eb401d80a 100644 --- a/python/mozbuild/mozbuild/test/backend/test_test_manifest.py +++ b/python/mozbuild/mozbuild/test/backend/test_test_manifest.py @@ -68,6 +68,24 @@ class TestTestManifestBackend(BackendTester): self.assertIn('here', o[manifest_path]) self.assertIn('support-files', o[manifest_path]) + def test_test_manifest_sources(self): + """Ensure that backend sources are generated correctly.""" + env = self._consume('test-manifests-backend-sources', TestManifestBackend) + + backend_path = mozpath.join(env.topobjdir, 'backend.TestManifestBackend.in') + self.assertTrue(os.path.exists(backend_path)) + + status_path = mozpath.join(env.topobjdir, 'config.status') + + with open(backend_path, 'r') as fh: + sources = set(source.strip() for source in fh) + + self.assertEquals(sources, + set([mozpath.join(env.topsrcdir, 'mochitest.ini'), + mozpath.join(env.topsrcdir, 'mochitest-common.ini'), + mozpath.join(env.topsrcdir, 'moz.build'), + status_path])) + if __name__ == '__main__': main() diff --git a/security/apps/AppSignatureVerification.cpp b/security/apps/AppSignatureVerification.cpp index bd2d13dc6149..daef5a7ac499 100644 --- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -864,9 +864,13 @@ OpenSignedAppFile(AppTrustedRoot aTrustedRoot, nsIFile* aJarFile, // Return the signer's certificate to the reader if they want it. // XXX: We should return an nsIX509CertList with the whole validated chain. if (aSignerCert) { - MOZ_ASSERT(CERT_LIST_HEAD(builtChain)); + CERTCertListNode* signerCertNode = CERT_LIST_HEAD(builtChain); + if (!signerCertNode || CERT_LIST_END(signerCertNode, builtChain) || + !signerCertNode->cert) { + return NS_ERROR_FAILURE; + } nsCOMPtr signerCert = - nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert); + nsNSSCertificate::Create(signerCertNode->cert); NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY); signerCert.forget(aSignerCert); } @@ -938,9 +942,13 @@ VerifySignedManifest(AppTrustedRoot aTrustedRoot, // Return the signer's certificate to the reader if they want it. if (aSignerCert) { - MOZ_ASSERT(CERT_LIST_HEAD(builtChain)); + CERTCertListNode* signerCertNode = CERT_LIST_HEAD(builtChain); + if (!signerCertNode || CERT_LIST_END(signerCertNode, builtChain) || + !signerCertNode->cert) { + return NS_ERROR_FAILURE; + } nsCOMPtr signerCert = - nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert); + nsNSSCertificate::Create(signerCertNode->cert); if (NS_WARN_IF(!signerCert)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -1491,9 +1499,13 @@ VerifySignedDirectory(AppTrustedRoot aTrustedRoot, // Return the signer's certificate to the reader if they want it. // XXX: We should return an nsIX509CertList with the whole validated chain. if (aSignerCert) { - MOZ_ASSERT(CERT_LIST_HEAD(builtChain)); + CERTCertListNode* signerCertNode = CERT_LIST_HEAD(builtChain); + if (!signerCertNode || CERT_LIST_END(signerCertNode, builtChain) || + !signerCertNode->cert) { + return NS_ERROR_FAILURE; + } nsCOMPtr signerCert = - nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert); + nsNSSCertificate::Create(signerCertNode->cert); NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY); signerCert.forget(aSignerCert); } diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index 87bbefa5cafb..8d5cc05d3c7f 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -286,12 +286,13 @@ CertVerifier::VerifyCertificateTransparencyPolicy( } CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain); - if (!endEntityNode) { + if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) { return Result::FATAL_ERROR_INVALID_ARGS; } CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode); - if (!issuerNode) { + if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) { // Issuer certificate is required for SCT verification. + // TODO(bug 1294580): change this to Result::FATAL_ERROR_INVALID_ARGS return Success; } diff --git a/security/manager/ssl/ContentSignatureVerifier.cpp b/security/manager/ssl/ContentSignatureVerifier.cpp index 8ceaff5760e8..129bb153946b 100644 --- a/security/manager/ssl/ContentSignatureVerifier.cpp +++ b/security/manager/ssl/ContentSignatureVerifier.cpp @@ -158,7 +158,7 @@ ContentSignatureVerifier::CreateContextInternal(const nsACString& aData, } CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get()); - if (!node || !node->cert) { + if (!node || CERT_LIST_END(node, certCertList.get()) || !node->cert) { return NS_ERROR_FAILURE; } diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index 419cab4086fb..71c112472804 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -1190,12 +1190,12 @@ void GatherEndEntityTelemetry(const UniqueCERTCertList& certList) { CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); - MOZ_ASSERT(endEntityNode); - if (!endEntityNode) { + MOZ_ASSERT(endEntityNode && !CERT_LIST_END(endEntityNode, certList)); + if (!endEntityNode || CERT_LIST_END(endEntityNode, certList)) { return; } - CERTCertificate * endEntityCert = endEntityNode->cert; + CERTCertificate* endEntityCert = endEntityNode->cert; MOZ_ASSERT(endEntityCert); if (!endEntityCert) { return; diff --git a/security/manager/ssl/tests/unit/test_cert_trust.js b/security/manager/ssl/tests/unit/test_cert_trust.js index 21f93080a6c9..e33dae0234ce 100644 --- a/security/manager/ssl/tests/unit/test_cert_trust.js +++ b/security/manager/ssl/tests/unit/test_cert_trust.js @@ -204,4 +204,14 @@ function run_test() { setup_basic_trusts(ca_cert, int_cert); test_ca_distrust(ee_cert, int_cert, false); + + // Reset trust to default ("inherit trust") + setCertTrust(ca_cert, ",,"); + setCertTrust(int_cert, ",,"); + + // It turns out that if an end-entity certificate is manually trusted, it can + // be the root of its own verified chain. This will be removed in bug 1294580. + setCertTrust(ee_cert, "C,,"); + checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess, + certificateUsageSSLServer); } diff --git a/startupcache/moz.build b/startupcache/moz.build index 24068cb7e800..e3d88e417a54 100644 --- a/startupcache/moz.build +++ b/startupcache/moz.build @@ -4,6 +4,9 @@ # 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/. +with Files("**"): + BUG_COMPONENT = ("Core", "XPCOM") + if not CONFIG['MOZ_B2G']: TEST_DIRS += ['test'] diff --git a/storage/moz.build b/storage/moz.build index 8863105c9d5c..f42c3e47061b 100644 --- a/storage/moz.build +++ b/storage/moz.build @@ -4,6 +4,9 @@ # 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/. +with Files("**"): + BUG_COMPONENT = ("Toolkit", "Storage") + DIRS += ['build'] TEST_DIRS += ['test'] diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm index ab50db1c639b..48eae9c9c104 100644 --- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -183,6 +183,13 @@ this.BrowserTestUtils = { * @resolves When a load event is triggered for the browser. */ browserLoaded(browser, includeSubFrames=false, wantLoad=null) { + // If browser belongs to tabbrowser-tab, ensure it has been + // inserted into the document. + let tabbrowser = browser.ownerGlobal.gBrowser; + if (tabbrowser && tabbrowser.getTabForBrowser) { + tabbrowser._insertBrowser(tabbrowser.getTabForBrowser(browser)); + } + function isWanted(url) { if (!wantLoad) { return true; diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 8741ee781b29..e542ef445c40 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -43419,6 +43419,16 @@ {} ] ], + "fetch/api/policies/nested-policy.js": [ + [ + {} + ] + ], + "fetch/api/policies/nested-policy.js.headers": [ + [ + {} + ] + ], "fetch/api/policies/referrer-no-referrer.html.headers": [ [ {} @@ -80855,6 +80865,12 @@ {} ] ], + "XMLHttpRequest/open-url-redirected-worker-origin.htm": [ + [ + "/XMLHttpRequest/open-url-redirected-worker-origin.htm", + {} + ] + ], "XMLHttpRequest/open-url-worker-origin.htm": [ [ "/XMLHttpRequest/open-url-worker-origin.htm", @@ -87093,6 +87109,18 @@ {} ] ], + "fetch/api/basic/request-referrer-redirected-worker.html": [ + [ + "/fetch/api/basic/request-referrer-redirected-worker.html", + {} + ] + ], + "fetch/api/basic/request-referrer-worker.html": [ + [ + "/fetch/api/basic/request-referrer-worker.html", + {} + ] + ], "fetch/api/basic/request-referrer.html": [ [ "/fetch/api/basic/request-referrer.html", @@ -87445,6 +87473,12 @@ {} ] ], + "fetch/api/policies/referrer-no-referrer-service-worker.https.html": [ + [ + "/fetch/api/policies/referrer-no-referrer-service-worker.https.html", + {} + ] + ], "fetch/api/policies/referrer-no-referrer-worker.html": [ [ "/fetch/api/policies/referrer-no-referrer-worker.html", @@ -87457,6 +87491,18 @@ {} ] ], + "fetch/api/policies/referrer-origin-service-worker.https.html": [ + [ + "/fetch/api/policies/referrer-origin-service-worker.https.html", + {} + ] + ], + "fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html": [ + [ + "/fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html", + {} + ] + ], "fetch/api/policies/referrer-origin-when-cross-origin-worker.html": [ [ "/fetch/api/policies/referrer-origin-when-cross-origin-worker.html", @@ -87481,6 +87527,12 @@ {} ] ], + "fetch/api/policies/referrer-unsafe-url-service-worker.https.html": [ + [ + "/fetch/api/policies/referrer-unsafe-url-service-worker.https.html", + {} + ] + ], "fetch/api/policies/referrer-unsafe-url-worker.html": [ [ "/fetch/api/policies/referrer-unsafe-url-worker.html", @@ -136404,6 +136456,10 @@ "28603b8d225367ba648bb9271dec5cb3da73d733", "testharness" ], + "XMLHttpRequest/open-url-redirected-worker-origin.htm": [ + "98c20df1cd8c7e27a94ba55f7c94d19244d7ff59", + "testharness" + ], "XMLHttpRequest/open-url-worker-origin.htm": [ "e4db65c7c0a98d7f5aa84eac01705259f377f44b", "testharness" @@ -163308,12 +163364,20 @@ "6a15c64c072f4e57470ed3f2709e43fa2f806b79", "support" ], + "fetch/api/basic/request-referrer-redirected-worker.html": [ + "41013925a84ab080c9c9f3b16ef4d8fdd1d50588", + "testharness" + ], + "fetch/api/basic/request-referrer-worker.html": [ + "3dad9617d76eed94d8b759d0d27b20c431873dd4", + "testharness" + ], "fetch/api/basic/request-referrer.html": [ "acf1ca37cf3904eccf4c7a9248c49e68f6260866", "testharness" ], "fetch/api/basic/request-referrer.js": [ - "5267f72887c4f7b039d2a5aae3519dc54cceca97", + "03c8ccec8ee4b37994bf38f20ff9fd531a209974", "support" ], "fetch/api/basic/request-upload-worker.html": [ @@ -163644,6 +163708,18 @@ "94dc6f63015b0835eb2a8c1892636bafeb1dfee7", "support" ], + "fetch/api/policies/nested-policy.js": [ + "57cfc2060102695b601209e4927861aaee377c4e", + "support" + ], + "fetch/api/policies/nested-policy.js.headers": [ + "fab90d344cbb78bad6445288c418b87736a830ae", + "support" + ], + "fetch/api/policies/referrer-no-referrer-service-worker.https.html": [ + "274797274e05e467499b98179dd23a19a960a014", + "testharness" + ], "fetch/api/policies/referrer-no-referrer-worker.html": [ "cc9274c1b182a487dbe945e6d8754a671d78e3c4", "testharness" @@ -163664,6 +163740,14 @@ "fab90d344cbb78bad6445288c418b87736a830ae", "support" ], + "fetch/api/policies/referrer-origin-service-worker.https.html": [ + "cf400b001e381251b0eaca8290e975dc27f6ff0f", + "testharness" + ], + "fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html": [ + "c2d6e3abb2b4ec0342ac9c16c468adb8b03e0426", + "testharness" + ], "fetch/api/policies/referrer-origin-when-cross-origin-worker.html": [ "3242db3a9319923287f36987f6f14581c239ced2", "testharness" @@ -163677,7 +163761,7 @@ "support" ], "fetch/api/policies/referrer-origin-when-cross-origin.js": [ - "e750c643dda40176e230d39e38057d4d2bda4fcf", + "802ebd6d423099439a790c3db9b7ae37cbe5d9a0", "support" ], "fetch/api/policies/referrer-origin-when-cross-origin.js.headers": [ @@ -163689,7 +163773,7 @@ "testharness" ], "fetch/api/policies/referrer-origin.html": [ - "0ab191b8e4c349d47569a8d7cfeebec89711a0cb", + "ae8bfdfb5b9ae86522378ec798908b7db442797d", "testharness" ], "fetch/api/policies/referrer-origin.html.headers": [ @@ -163697,13 +163781,17 @@ "support" ], "fetch/api/policies/referrer-origin.js": [ - "368af4830da8fcd341ffb513b8c6c2c2bcfab3d9", + "e863bed9479b1b89cb0d8fae680e471ca66c757f", "support" ], "fetch/api/policies/referrer-origin.js.headers": [ "56b5f91097bb278ebc69345c0b56c65eb16cc3db", "support" ], + "fetch/api/policies/referrer-unsafe-url-service-worker.https.html": [ + "d2fa5c7813f57909228b51417df0267cd7ec0c20", + "testharness" + ], "fetch/api/policies/referrer-unsafe-url-worker.html": [ "70e7ec27a61ee68fb419213be149816ed618ce24", "testharness" @@ -163717,7 +163805,7 @@ "support" ], "fetch/api/policies/referrer-unsafe-url.js": [ - "24d042ef4e85cccf3d178f701af5664c954deaf3", + "e1881c5f3f0a7d1705a5d2eb5d3381d8788374d8", "support" ], "fetch/api/policies/referrer-unsafe-url.js.headers": [ diff --git a/testing/web-platform/meta/fetch/api/policies/referrer-origin-service-worker.https.html.ini b/testing/web-platform/meta/fetch/api/policies/referrer-origin-service-worker.https.html.ini new file mode 100644 index 000000000000..8df1d2a23318 --- /dev/null +++ b/testing/web-platform/meta/fetch/api/policies/referrer-origin-service-worker.https.html.ini @@ -0,0 +1,5 @@ +[referrer-origin-service-worker.https.html] + type: testharness + [Cross-origin referrer is overridden by client origin] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1341223 diff --git a/testing/web-platform/tests/XMLHttpRequest/open-url-redirected-worker-origin.htm b/testing/web-platform/tests/XMLHttpRequest/open-url-redirected-worker-origin.htm new file mode 100644 index 000000000000..86cba0ccb5fc --- /dev/null +++ b/testing/web-platform/tests/XMLHttpRequest/open-url-redirected-worker-origin.htm @@ -0,0 +1,44 @@ + + + + + XMLHttpRequest: redirected worker scripts, origin and referrer + + + + + +

+ + + diff --git a/testing/web-platform/tests/fetch/api/basic/request-referrer-redirected-worker.html b/testing/web-platform/tests/fetch/api/basic/request-referrer-redirected-worker.html new file mode 100644 index 000000000000..0306c8770c6d --- /dev/null +++ b/testing/web-platform/tests/fetch/api/basic/request-referrer-redirected-worker.html @@ -0,0 +1,17 @@ + + + + + Fetch in worker: referrer header + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/basic/request-referrer-worker.html b/testing/web-platform/tests/fetch/api/basic/request-referrer-worker.html new file mode 100644 index 000000000000..d598c8bad584 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/basic/request-referrer-worker.html @@ -0,0 +1,14 @@ + + + + + Fetch in worker: referrer header + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/basic/request-referrer.js b/testing/web-platform/tests/fetch/api/basic/request-referrer.js index cc5cc8e01d40..6a55c6179e1b 100644 --- a/testing/web-platform/tests/fetch/api/basic/request-referrer.js +++ b/testing/web-platform/tests/fetch/api/basic/request-referrer.js @@ -3,7 +3,7 @@ if (this.document === undefined) { importScripts("../resources/utils.js"); } -function testReferrer(referrer, expected) { +function testReferrer(referrer, expected, desc) { promise_test(function(test) { var url = RESOURCES_DIR + "inspect-headers.py?headers=referer" var req = new Request(url, { referrer: referrer }); @@ -17,12 +17,12 @@ function testReferrer(referrer, expected) { assert_equals(actual, "", "request's referer should be empty"); } }); - }); + }, desc); } -testReferrer("about:client", window.location.href); +testReferrer("about:client", self.location.href, 'about:client referrer'); -var fooURL = new URL("./foo", window.location).href; -testReferrer(fooURL, fooURL); +var fooURL = new URL("./foo", self.location).href; +testReferrer(fooURL, fooURL, 'url referrer'); done(); diff --git a/testing/web-platform/tests/fetch/api/policies/nested-policy.js b/testing/web-platform/tests/fetch/api/policies/nested-policy.js new file mode 100644 index 000000000000..b0d17696c337 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/nested-policy.js @@ -0,0 +1 @@ +// empty, but referrer-policy set on this file diff --git a/testing/web-platform/tests/fetch/api/policies/nested-policy.js.headers b/testing/web-platform/tests/fetch/api/policies/nested-policy.js.headers new file mode 100644 index 000000000000..7ffbf17d6be5 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/nested-policy.js.headers @@ -0,0 +1 @@ +Referrer-Policy: no-referrer diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-no-referrer-service-worker.https.html b/testing/web-platform/tests/fetch/api/policies/referrer-no-referrer-service-worker.https.html new file mode 100644 index 000000000000..0d30ef9e3850 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/referrer-no-referrer-service-worker.https.html @@ -0,0 +1,18 @@ + + + + + Fetch in service worker: referrer with no-referrer policy + + + + + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-origin-service-worker.https.html b/testing/web-platform/tests/fetch/api/policies/referrer-origin-service-worker.https.html new file mode 100644 index 000000000000..4018b837816e --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/referrer-origin-service-worker.https.html @@ -0,0 +1,18 @@ + + + + + Fetch in service worker: referrer with no-referrer policy + + + + + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html b/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html new file mode 100644 index 000000000000..d87192e22711 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin-service-worker.https.html @@ -0,0 +1,17 @@ + + + + + Fetch in service worker: referrer with origin-when-cross-origin policy + + + + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin.js b/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin.js index 2baf7d12593f..7cd4113f506f 100644 --- a/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin.js +++ b/testing/web-platform/tests/fetch/api/policies/referrer-origin-when-cross-origin.js @@ -1,10 +1,14 @@ if (this.document === undefined) { importScripts("/resources/testharness.js"); importScripts("../resources/utils.js"); + + // A nested importScripts() with a referrer-policy should have no effect + // on overall worker policy. + importScripts("nested-policy.js"); } -var referrerOrigin = "http://{{host}}:{{ports[http][0]}}/"; -var fetchedUrl = "http://{{host}}:{{ports[http][1]}}" + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?cors&headers=referer"; +var referrerOrigin = location.origin + '/'; +var fetchedUrl = "https://{{domains[www]}}:{{ports[https][0]}}" + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?cors&headers=referer"; promise_test(function(test) { return fetch(fetchedUrl).then(function(resp) { diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-origin.html b/testing/web-platform/tests/fetch/api/policies/referrer-origin.html index 92baed79be76..b164afe01de9 100644 --- a/testing/web-platform/tests/fetch/api/policies/referrer-origin.html +++ b/testing/web-platform/tests/fetch/api/policies/referrer-origin.html @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-origin.js b/testing/web-platform/tests/fetch/api/policies/referrer-origin.js index de187657486d..11541e94da59 100644 --- a/testing/web-platform/tests/fetch/api/policies/referrer-origin.js +++ b/testing/web-platform/tests/fetch/api/policies/referrer-origin.js @@ -1,9 +1,13 @@ if (this.document === undefined) { importScripts("/resources/testharness.js"); importScripts("../resources/utils.js"); + + // A nested importScripts() with a referrer-policy should have no effect + // on overall worker policy. + importScripts("nested-policy.js"); } -var referrerOrigin = "http://{{host}}:{{ports[http][0]}}/"; +var referrerOrigin = self.location.origin + '/'; var fetchedUrl = RESOURCES_DIR + "inspect-headers.py?headers=referer"; promise_test(function(test) { @@ -15,7 +19,7 @@ promise_test(function(test) { }, "Request's referrer is origin"); promise_test(function(test) { - var referrerUrl = "http://{{domains[www]}}:{{ports[http][0]}}/"; + var referrerUrl = "https://{{domains[www]}}:{{ports[https][0]}}/"; return fetch(fetchedUrl, { "referrer": referrerUrl }).then(function(resp) { assert_equals(resp.status, 200, "HTTP status is 200"); assert_equals(resp.type , "basic", "Response's type is basic"); diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url-service-worker.https.html b/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url-service-worker.https.html new file mode 100644 index 000000000000..39a65b0e333a --- /dev/null +++ b/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url-service-worker.https.html @@ -0,0 +1,18 @@ + + + + + Fetch in worker: referrer with unsafe-url policy + + + + + + + + + + + diff --git a/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url.js b/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url.js index b593fad5aa2b..4d61172613ee 100644 --- a/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url.js +++ b/testing/web-platform/tests/fetch/api/policies/referrer-unsafe-url.js @@ -1,6 +1,10 @@ if (this.document === undefined) { importScripts("/resources/testharness.js"); importScripts("../resources/utils.js"); + + // A nested importScripts() with a referrer-policy should have no effect + // on overall worker policy. + importScripts("nested-policy.js"); } var referrerUrl = location.href; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index c7b963b58ecc..8873252ee16d 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1990,6 +1990,7 @@ "expires_in_version": "never", "kind": "enumerated", "n_values": 672, + "releaseChannelCollection": "opt-out", "description": "SSL handshake result, 0=success, 1-255=NSS error offset, 256-511=SEC error offset + 256, 512-639=NSPR error offset + 512, 640-670=PKIX error, 671=unknown err" }, "SSL_TIME_UNTIL_READY": { diff --git a/toolkit/components/telemetry/TelemetryHistogram.cpp b/toolkit/components/telemetry/TelemetryHistogram.cpp index 9a5366b5f509..89e1da08b3d3 100644 --- a/toolkit/components/telemetry/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/TelemetryHistogram.cpp @@ -1316,9 +1316,6 @@ internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId, KeyedHistogram* keyed = internal_GetKeyedHistogramById(nsDependentCString(th.id())); MOZ_ASSERT(keyed); - // Assert on empty keys as well, we should be filtering them out in - // the outer API. - MOZ_ASSERT(!aKey.IsEmpty()); if (!keyed->IsRecordingEnabled()) { return false; } @@ -1746,12 +1743,6 @@ internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp) return true; } - if (key.IsEmpty()) { - LogToBrowserConsole(nsIScriptError::errorFlag, - NS_LITERAL_STRING("Empty histogram keys are not allowed.")); - return true; - } - const uint32_t type = keyed->GetHistogramType(); // If we don't have an argument for the count histogram, assume an increment of 1. @@ -2145,12 +2136,6 @@ TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID, return; } - if (aKey.IsEmpty()) { - LogToBrowserConsole(nsIScriptError::errorFlag, - NS_LITERAL_STRING("Empty histogram keys are not allowed.")); - return; - } - StaticMutexAutoLock locker(gTelemetryHistogramMutex); internal_Accumulate(aID, aKey, aSample); } @@ -2174,12 +2159,6 @@ void TelemetryHistogram::Accumulate(const char* name, const nsCString& key, uint32_t sample) { - if (key.IsEmpty()) { - LogToBrowserConsole(nsIScriptError::errorFlag, - NS_LITERAL_STRING("Empty histogram keys are not allowed.")); - return; - } - StaticMutexAutoLock locker(gTelemetryHistogramMutex); if (!internal_CanRecordBase()) { return; diff --git a/toolkit/components/telemetry/WebrtcTelemetry.cpp b/toolkit/components/telemetry/WebrtcTelemetry.cpp index a437bff22f31..8c03f3a99fb0 100644 --- a/toolkit/components/telemetry/WebrtcTelemetry.cpp +++ b/toolkit/components/telemetry/WebrtcTelemetry.cpp @@ -12,8 +12,6 @@ #include "nsPrintfCString.h" #include "nsTHashtable.h" -using mozilla::Telemetry::Common::AutoHashtable; - void WebrtcTelemetry::RecordIceCandidateMask(const uint32_t iceCandidateBitmask, const bool success) diff --git a/toolkit/components/telemetry/docs/collection/histograms.rst b/toolkit/components/telemetry/docs/collection/histograms.rst index 6a4d4eafa575..7a943a572da7 100644 --- a/toolkit/components/telemetry/docs/collection/histograms.rst +++ b/toolkit/components/telemetry/docs/collection/histograms.rst @@ -89,11 +89,7 @@ Keyed Histograms ---------------- Keyed histograms are collections of one of the histogram types above, indexed by a string key. This is for example useful when you want to break down certain counts by a name, like how often searches happen with which search engine. - -Note that: - -- keys can't be empty strings; -- when you need to record for a small set of known keys, using separate plain histograms is more efficient. +Note that when you need to record for a small set of known keys, using separate plain histograms is more efficient. .. warning:: @@ -207,11 +203,7 @@ A Telemetry probe is the code that measures and stores values in a histogram. Pr let keyed = Services.telemetry.getKeyedHistogramById("TAG_SEEN_COUNTS"); keyed.add("blink"); -Note that: - -- ``nsITelemetry.getHistogramById()`` will throw an ``NS_ERROR_ILLEGAL_VALUE`` JavaScript exception if it is called with an invalid histogram ID; -- the ``add()`` function will not throw on failure, but log an error to the browser console; -- for keyed histograms, calling ``add()`` with an empty key will be ignored and log an error. +Note that ``nsITelemetry.getHistogramById()`` will throw an ``NS_ERROR_ILLEGAL_VALUE`` JavaScript exception if it is called with an invalid histogram ID. The ``add()`` function will not throw if it fails, instead it prints an error in the browser console. For histograms measuring time, `TelemetryStopwatch `_ can be used to avoid working with Dates manually: diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js index 55b9355f22a1..16650773126b 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js @@ -487,15 +487,6 @@ add_task(function* test_keyed_histogram() { threw = true; } Assert.ok(threw, "getKeyedHistogramById should have thrown"); - - // Check that empty keys are not allowed. - const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT"; - let h = Telemetry.getKeyedHistogramById(KEYED_ID); - - // Try to add to an empty key and make sure nothing happens. - h.add(""); - Assert.ok(!("" in h.snapshot()), - "Keyed histogram should not allow empty keys."); }); add_task(function* test_keyed_boolean_histogram() { diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 7183458ad5ff..ba8d1ba2dd24 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -376,14 +376,16 @@ var snapshotFormatters = { apzInfo.length ? apzInfo.join("; ") : localizedMsg(["apzNone"])); + addRowFromKey("features", "webgl1WSIInfo"); addRowFromKey("features", "webgl1Renderer"); addRowFromKey("features", "webgl1Version"); + addRowFromKey("features", "webgl1DriverExtensions"); addRowFromKey("features", "webgl1Extensions"); - addRowFromKey("features", "webgl1WSIInfo"); + addRowFromKey("features", "webgl2WSIInfo"); addRowFromKey("features", "webgl2Renderer"); addRowFromKey("features", "webgl2Version"); + addRowFromKey("features", "webgl2DriverExtensions"); addRowFromKey("features", "webgl2Extensions"); - addRowFromKey("features", "webgl2WSIInfo"); addRowFromKey("features", "supportsHardwareH264", "hardwareH264"); addRowFromKey("features", "currentAudioBackend", "audioBackend"); addRowFromKey("features", "direct2DEnabled", "#Direct2D"); diff --git a/toolkit/library/gtest/rust/Cargo.toml b/toolkit/library/gtest/rust/Cargo.toml index 21627fc580aa..8d6e6b6ae6df 100644 --- a/toolkit/library/gtest/rust/Cargo.toml +++ b/toolkit/library/gtest/rust/Cargo.toml @@ -39,11 +39,6 @@ panic = "abort" opt-level = 2 debug = true rpath = false -# This would normally be 'true' for release configurations, but using LTO on -# rul-gtest causes link failures due to symbols also being found in libxul's -# librul.a. But LTO'ing things here is not crucial and not LTO'ing things -# enables us to link libxul-gtest, so we leave it turned off. -lto = false +lto = true debug-assertions = false -codegen-units = 1 panic = "abort" diff --git a/toolkit/library/gtest/rust/moz.build b/toolkit/library/gtest/rust/moz.build index 6fe3f8931bc2..26ba750e09f3 100644 --- a/toolkit/library/gtest/rust/moz.build +++ b/toolkit/library/gtest/rust/moz.build @@ -14,4 +14,4 @@ if CONFIG['MOZ_STYLO']: if CONFIG['MOZ_ENABLE_WEBRENDER']: features += ['quantum_render'] -RustLibrary('gkrust-gtest', features) +RustLibrary('gkrust-gtest', features, '../..') diff --git a/toolkit/library/rust/moz.build b/toolkit/library/rust/moz.build index a5cec1fbbf19..069a8d4ecde7 100644 --- a/toolkit/library/rust/moz.build +++ b/toolkit/library/rust/moz.build @@ -14,4 +14,4 @@ if CONFIG['MOZ_STYLO']: if CONFIG['MOZ_ENABLE_WEBRENDER']: features += ['quantum_render'] -RustLibrary('gkrust', features) +RustLibrary('gkrust', features, '..') diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.properties b/toolkit/locales/en-US/chrome/global/aboutSupport.properties index 09298359a1a5..d19dcd03cdae 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties @@ -71,14 +71,16 @@ gpuRAM = RAM gpuDriverVersion = Driver Version gpuDriverDate = Driver Date gpuActive = Active -webgl1Renderer = WebGL 1 Renderer -webgl1Version = WebGL 1 GL Version -webgl1Extensions = WebGL 1 GL Extensions -webgl1WSIInfo = WebGL 1 WSI Info -webgl2Renderer = WebGL 2 Renderer -webgl2Version = WebGL 2 GL Version -webgl2Extensions = WebGL 2 GL Extensions -webgl2WSIInfo = WebGL 2 WSI Info +webgl1WSIInfo = WebGL 1 Driver WSI Info +webgl1Renderer = WebGL 1 Driver Renderer +webgl1Version = WebGL 1 Driver Version +webgl1DriverExtensions = WebGL 1 Driver Extensions +webgl1Extensions = WebGL 1 Extensions +webgl2WSIInfo = WebGL 2 Driver WSI Info +webgl2Renderer = WebGL 2 Driver Renderer +webgl2Version = WebGL 2 Driver Version +webgl2DriverExtensions = WebGL 2 Driver Extensions +webgl2Extensions = WebGL 2 Extensions GPU1 = GPU #1 GPU2 = GPU #2 blocklistedBug = Blocklisted due to known issues diff --git a/toolkit/modules/Troubleshoot.jsm b/toolkit/modules/Troubleshoot.jsm index dbb114cb45b7..09991395f224 100644 --- a/toolkit/modules/Troubleshoot.jsm +++ b/toolkit/modules/Troubleshoot.jsm @@ -422,6 +422,7 @@ var dataProviders = { function GetWebGLInfo(data, keyPrefix, contextType) { data[keyPrefix + "Renderer"] = "-"; data[keyPrefix + "Version"] = "-"; + data[keyPrefix + "DriverExtensions"] = "-"; data[keyPrefix + "Extensions"] = "-"; data[keyPrefix + "WSIInfo"] = "-"; @@ -458,6 +459,10 @@ var dataProviders = { // // + data[keyPrefix + "Extensions"] = gl.getSupportedExtensions().join(" "); + + // // + let ext = gl.getExtension("MOZ_debug_get"); // This extension is unconditionally available to chrome. No need to check. let vendor = ext.getParameter(gl.VENDOR); @@ -465,7 +470,7 @@ var dataProviders = { data[keyPrefix + "Renderer"] = vendor + " -- " + renderer; data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION); - data[keyPrefix + "Extensions"] = ext.getParameter(ext.EXTENSIONS); + data[keyPrefix + "DriverExtensions"] = ext.getParameter(ext.EXTENSIONS); data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO); // // diff --git a/toolkit/modules/tests/browser/browser_Troubleshoot.js b/toolkit/modules/tests/browser/browser_Troubleshoot.js index d3c7711196ba..b1a9d71f90b2 100644 --- a/toolkit/modules/tests/browser/browser_Troubleshoot.js +++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js @@ -300,6 +300,9 @@ const SNAPSHOT_SCHEMA = { webgl1Version: { type: "string", }, + webgl1DriverExtensions: { + type: "string", + }, webgl1Extensions: { type: "string", }, @@ -312,6 +315,9 @@ const SNAPSHOT_SCHEMA = { webgl2Version: { type: "string", }, + webgl2DriverExtensions: { + type: "string", + }, webgl2Extensions: { type: "string", }, diff --git a/toolkit/themes/linux/global/button.css b/toolkit/themes/linux/global/button.css index cdcc66c53945..6ed09c94ca0b 100644 --- a/toolkit/themes/linux/global/button.css +++ b/toolkit/themes/linux/global/button.css @@ -20,7 +20,6 @@ button { .button-box { -moz-appearance: button-focus; - border: 1px solid transparent; padding-top: 1px; padding-bottom: 2px; padding-inline-start: 3px; @@ -36,12 +35,6 @@ button { text-align: center; } -/* .......... focused state .......... */ - -button:focus > .button-box { - border: 1px dotted ThreeDDarkShadow; -} - /* .......... hover state .......... */ button:hover { diff --git a/tools/profiler/core/ProfileBuffer.cpp b/tools/profiler/core/ProfileBuffer.cpp index 864f212b3bf4..3ae360420c1a 100644 --- a/tools/profiler/core/ProfileBuffer.cpp +++ b/tools/profiler/core/ProfileBuffer.cpp @@ -6,7 +6,7 @@ #include "ProfileBuffer.h" ProfileBuffer::ProfileBuffer(int aEntrySize) - : mEntries(mozilla::MakeUnique(aEntrySize)) + : mEntries(mozilla::MakeUnique(aEntrySize)) , mWritePos(0) , mReadPos(0) , mEntrySize(aEntrySize) @@ -22,7 +22,7 @@ ProfileBuffer::~ProfileBuffer() } // Called from signal, call only reentrant functions -void ProfileBuffer::addTag(const ProfileEntry& aTag) +void ProfileBuffer::addTag(const ProfileBufferEntry& aTag) { mEntries[mWritePos++] = aTag; if (mWritePos == mEntrySize) { @@ -34,7 +34,7 @@ void ProfileBuffer::addTag(const ProfileEntry& aTag) } if (mWritePos == mReadPos) { // Keep one slot open. - mEntries[mReadPos] = ProfileEntry(); + mEntries[mReadPos] = ProfileBufferEntry(); mReadPos = (mReadPos + 1) % mEntrySize; } } @@ -85,7 +85,7 @@ char* ProfileBuffer::processDynamicTag(int readPos, bool seenNullByte = false; while (readAheadPos != mWritePos && !seenNullByte) { (*tagsConsumed)++; - ProfileEntry readAheadEntry = mEntries[readAheadPos]; + ProfileBufferEntry readAheadEntry = mEntries[readAheadPos]; for (size_t pos = 0; pos < sizeof(void*); pos++) { tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos]; if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) { diff --git a/tools/profiler/core/ProfileBuffer.h b/tools/profiler/core/ProfileBuffer.h index 427afcd379f1..93d549d83b4e 100644 --- a/tools/profiler/core/ProfileBuffer.h +++ b/tools/profiler/core/ProfileBuffer.h @@ -6,7 +6,7 @@ #ifndef MOZ_PROFILE_BUFFER_H #define MOZ_PROFILE_BUFFER_H -#include "ProfileEntry.h" +#include "ProfileBufferEntry.h" #include "platform.h" #include "ProfileJSONWriter.h" #include "mozilla/RefPtr.h" @@ -21,7 +21,7 @@ public: virtual ~ProfileBuffer(); - void addTag(const ProfileEntry& aTag); + void addTag(const ProfileBufferEntry& aTag); void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime, JSContext* cx, UniqueStacks& aUniqueStacks); void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime, @@ -42,7 +42,7 @@ protected: public: // Circular buffer 'Keep One Slot Open' implementation for simplicity - mozilla::UniquePtr mEntries; + mozilla::UniquePtr mEntries; // Points to the next entry we will write to, which is also the one at which // we need to stop reading. diff --git a/tools/profiler/core/ProfileEntry.cpp b/tools/profiler/core/ProfileBufferEntry.cpp similarity index 93% rename from tools/profiler/core/ProfileEntry.cpp rename to tools/profiler/core/ProfileBufferEntry.cpp index 8a3cc88edf16..9a5acdb95488 100644 --- a/tools/profiler/core/ProfileEntry.cpp +++ b/tools/profiler/core/ProfileBufferEntry.cpp @@ -16,7 +16,7 @@ #include "js/TrackedOptimizationInfo.h" // Self -#include "ProfileEntry.h" +#include "ProfileBufferEntry.h" using mozilla::JSONWriter; using mozilla::MakeUnique; @@ -27,55 +27,55 @@ using mozilla::TimeStamp; using mozilla::UniquePtr; //////////////////////////////////////////////////////////////////////// -// BEGIN ProfileEntry +// BEGIN ProfileBufferEntry -ProfileEntry::ProfileEntry() +ProfileBufferEntry::ProfileBufferEntry() : mTagData(nullptr) , mKind(Kind::INVALID) { } // aTagData must not need release (i.e. be a string from the text segment) -ProfileEntry::ProfileEntry(Kind aKind, const char *aTagData) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, const char *aTagData) : mTagData(aTagData) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, ProfilerMarker *aTagMarker) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, ProfilerMarker *aTagMarker) : mTagMarker(aTagMarker) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, void *aTagPtr) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, void *aTagPtr) : mTagPtr(aTagPtr) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, double aTagDouble) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, double aTagDouble) : mTagDouble(aTagDouble) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, uintptr_t aTagOffset) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uintptr_t aTagOffset) : mTagOffset(aTagOffset) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, Address aTagAddress) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, Address aTagAddress) : mTagAddress(aTagAddress) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, int aTagInt) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, int aTagInt) : mTagInt(aTagInt) , mKind(aKind) { } -ProfileEntry::ProfileEntry(Kind aKind, char aTagChar) +ProfileBufferEntry::ProfileBufferEntry(Kind aKind, char aTagChar) : mTagChar(aTagChar) , mKind(aKind) { } -// END ProfileEntry +// END ProfileBufferEntry //////////////////////////////////////////////////////////////////////// class JSONSchemaWriter @@ -153,8 +153,8 @@ public: } }; -// As mentioned in ProfileEntry.h, the JSON format contains many arrays whose -// elements are laid out according to various schemas to help +// As mentioned in ProfileBufferEntry.h, the JSON format contains many +// arrays whose elements are laid out according to various schemas to help // de-duplication. This RAII class helps write these arrays by keeping track of // the last non-null element written and adding the appropriate number of null // elements when writing new non-null elements. It also automatically opens and @@ -589,13 +589,13 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre UniquePtr tagBuff = MakeUnique(DYNAMIC_MAX_STRING); while (readPos != mWritePos) { - ProfileEntry entry = mEntries[readPos]; + ProfileBufferEntry entry = mEntries[readPos]; if (entry.isThreadId()) { currentThreadID = entry.mTagInt; currentTime.reset(); int readAheadPos = (readPos + 1) % mEntrySize; if (readAheadPos != mWritePos) { - ProfileEntry readAheadEntry = mEntries[readAheadPos]; + ProfileBufferEntry readAheadEntry = mEntries[readAheadPos]; if (readAheadEntry.isTime()) { currentTime = Some(readAheadEntry.mTagDouble); } @@ -603,27 +603,27 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre } if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) { switch (entry.kind()) { - case ProfileEntry::Kind::Responsiveness: + case ProfileBufferEntry::Kind::Responsiveness: if (sample.isSome()) { sample->mResponsiveness = Some(entry.mTagDouble); } break; - case ProfileEntry::Kind::ResidentMemory: + case ProfileBufferEntry::Kind::ResidentMemory: if (sample.isSome()) { sample->mRSS = Some(entry.mTagDouble); } break; - case ProfileEntry::Kind::UnsharedMemory: + case ProfileBufferEntry::Kind::UnsharedMemory: if (sample.isSome()) { sample->mUSS = Some(entry.mTagDouble); } break; - case ProfileEntry::Kind::FrameNumber: + case ProfileBufferEntry::Kind::FrameNumber: if (sample.isSome()) { sample->mFrameNumber = Some(entry.mTagInt); } break; - case ProfileEntry::Kind::Sample: + case ProfileBufferEntry::Kind::Sample: { // end the previous sample if there was one if (sample.isSome()) { @@ -642,7 +642,7 @@ void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThre aUniqueStacks.BeginStack(UniqueStacks::OnStackFrameKey("(root)")); int framePos = (readPos + 1) % mEntrySize; - ProfileEntry frame = mEntries[framePos]; + ProfileBufferEntry frame = mEntries[framePos]; while (framePos != mWritePos && !frame.isSample() && !frame.isThreadId()) { int incBy = 1; frame = mEntries[framePos]; @@ -725,7 +725,7 @@ void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThre int readPos = mReadPos; int currentThreadID = -1; while (readPos != mWritePos) { - ProfileEntry entry = mEntries[readPos]; + ProfileBufferEntry entry = mEntries[readPos]; if (entry.isThreadId()) { currentThreadID = entry.mTagInt; } else if (currentThreadID == aThreadId && entry.isMarker()) { @@ -745,7 +745,7 @@ int ProfileBuffer::FindLastSampleOfThread(int aThreadId) for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize; readPos != (mReadPos + mEntrySize - 1) % mEntrySize; readPos = (readPos + mEntrySize - 1) % mEntrySize) { - ProfileEntry entry = mEntries[readPos]; + ProfileBufferEntry entry = mEntries[readPos]; if (entry.isThreadId() && entry.mTagInt == aThreadId) { return readPos; } @@ -771,14 +771,15 @@ ProfileBuffer::DuplicateLastSample(int aThreadId, const TimeStamp& aStartTime) readPos != mWritePos; readPos = (readPos + 1) % mEntrySize) { switch (mEntries[readPos].kind()) { - case ProfileEntry::Kind::ThreadId: + case ProfileBufferEntry::Kind::ThreadId: // We're done. return; - case ProfileEntry::Kind::Time: + case ProfileBufferEntry::Kind::Time: // Copy with new time - addTag(ProfileEntry::Time((TimeStamp::Now() - aStartTime).ToMilliseconds())); + addTag(ProfileBufferEntry::Time((TimeStamp::Now() - + aStartTime).ToMilliseconds())); break; - case ProfileEntry::Kind::Marker: + case ProfileBufferEntry::Kind::Marker: // Don't copy markers break; default: diff --git a/tools/profiler/core/ProfileEntry.h b/tools/profiler/core/ProfileBufferEntry.h similarity index 92% rename from tools/profiler/core/ProfileEntry.h rename to tools/profiler/core/ProfileBufferEntry.h index 6cec5b0babe1..25c328d8d6a2 100644 --- a/tools/profiler/core/ProfileEntry.h +++ b/tools/profiler/core/ProfileBufferEntry.h @@ -4,8 +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/. */ -#ifndef MOZ_PROFILE_ENTRY_H -#define MOZ_PROFILE_ENTRY_H +#ifndef ProfileBufferEntry_h +#define ProfileBufferEntry_h #include #include "GeckoProfiler.h" @@ -25,7 +25,7 @@ #include "mozilla/HashFunctions.h" #include "mozilla/UniquePtr.h" -#define PROFILE_ENTRY_KIND_LIST(_) \ +#define PROFILE_BUFFER_ENTRY_KIND_LIST(_) \ _(Category, int) \ _(CodeLocation, const char *) \ _(EmbeddedString, void *) \ @@ -46,34 +46,36 @@ #pragma pack(push, 1) #endif -class ProfileEntry +class ProfileBufferEntry { public: enum class Kind : uint8_t { INVALID = 0, # define DEF_ENUM_(k, t) k, - PROFILE_ENTRY_KIND_LIST(DEF_ENUM_) + PROFILE_BUFFER_ENTRY_KIND_LIST(DEF_ENUM_) # undef DEF_ENUM_ LIMIT }; - ProfileEntry(); + ProfileBufferEntry(); private: // aTagData must not need release (i.e. be a string from the text segment) - ProfileEntry(Kind aKind, const char *aTagData); - ProfileEntry(Kind aKind, void *aTagPtr); - ProfileEntry(Kind aKind, ProfilerMarker *aTagMarker); - ProfileEntry(Kind aKind, double aTagDouble); - ProfileEntry(Kind aKind, uintptr_t aTagOffset); - ProfileEntry(Kind aKind, Address aTagAddress); - ProfileEntry(Kind aKind, int aTagLine); - ProfileEntry(Kind aKind, char aTagChar); + ProfileBufferEntry(Kind aKind, const char *aTagData); + ProfileBufferEntry(Kind aKind, void *aTagPtr); + ProfileBufferEntry(Kind aKind, ProfilerMarker *aTagMarker); + ProfileBufferEntry(Kind aKind, double aTagDouble); + ProfileBufferEntry(Kind aKind, uintptr_t aTagOffset); + ProfileBufferEntry(Kind aKind, Address aTagAddress); + ProfileBufferEntry(Kind aKind, int aTagLine); + ProfileBufferEntry(Kind aKind, char aTagChar); public: # define DEF_MAKE_(k, t) \ - static ProfileEntry k(t val) { return ProfileEntry(Kind::k, val); } - PROFILE_ENTRY_KIND_LIST(DEF_MAKE_) + static ProfileBufferEntry k(t val) { \ + return ProfileBufferEntry(Kind::k, val); \ + } + PROFILE_BUFFER_ENTRY_KIND_LIST(DEF_MAKE_) # undef DEF_MAKE_ Kind kind() const { return mKind; } @@ -81,7 +83,7 @@ public: # define DEF_METHODS_(k, t) \ bool is##k() const { return hasKind(Kind::k); } - PROFILE_ENTRY_KIND_LIST(DEF_METHODS_) + PROFILE_BUFFER_ENTRY_KIND_LIST(DEF_METHODS_) # undef DEF_METHODS_ const ProfilerMarker* getMarker() { @@ -408,4 +410,4 @@ private: // } // -#endif /* ndef MOZ_PROFILE_ENTRY_H */ +#endif /* ndef ProfileBufferEntry_h */ diff --git a/tools/profiler/core/SyncProfile.cpp b/tools/profiler/core/SyncProfile.cpp index c164f0e51798..4b092bd5b138 100644 --- a/tools/profiler/core/SyncProfile.cpp +++ b/tools/profiler/core/SyncProfile.cpp @@ -12,7 +12,7 @@ SyncProfile::SyncProfile(int aThreadId, PseudoStack* aStack) , mOwnerState(REFERENCED) { MOZ_COUNT_CTOR(SyncProfile); - SetProfile(new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRY)); + SetProfile(new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES)); } SyncProfile::~SyncProfile() diff --git a/tools/profiler/core/SyncProfile.h b/tools/profiler/core/SyncProfile.h index af7689002a40..96ebaafcfd62 100644 --- a/tools/profiler/core/SyncProfile.h +++ b/tools/profiler/core/SyncProfile.h @@ -7,7 +7,6 @@ #ifndef __SYNCPROFILE_H #define __SYNCPROFILE_H -#include "ProfileEntry.h" #include "ThreadInfo.h" class SyncProfile : public ThreadInfo diff --git a/tools/profiler/core/ThreadInfo.cpp b/tools/profiler/core/ThreadInfo.cpp index 808d7834f79b..35f1b7d778f2 100644 --- a/tools/profiler/core/ThreadInfo.cpp +++ b/tools/profiler/core/ThreadInfo.cpp @@ -64,7 +64,7 @@ ThreadInfo::CanInvokeJS() const } void -ThreadInfo::addTag(const ProfileEntry& aTag) +ThreadInfo::addTag(const ProfileBufferEntry& aTag) { mBuffer->addTag(aTag); } diff --git a/tools/profiler/core/ThreadInfo.h b/tools/profiler/core/ThreadInfo.h index addfb6d7b402..8da8e783df10 100644 --- a/tools/profiler/core/ThreadInfo.h +++ b/tools/profiler/core/ThreadInfo.h @@ -57,7 +57,7 @@ class ThreadInfo { public: bool hasProfile() { return !!mBuffer; } - void addTag(const ProfileEntry& aTag); + void addTag(const ProfileBufferEntry& aTag); // Track a marker which has been inserted into the thread profile. // This marker can safely be deleted once the generation has diff --git a/tools/profiler/core/platform-linux-android.cpp b/tools/profiler/core/platform-linux-android.cpp index 7713b837ba73..bdb481dbe407 100644 --- a/tools/profiler/core/platform-linux-android.cpp +++ b/tools/profiler/core/platform-linux-android.cpp @@ -63,7 +63,6 @@ #include "prenv.h" #include "mozilla/LinuxSignal.h" #include "mozilla/DebugOnly.h" -#include "ProfileEntry.h" // Memory profile #include "nsMemoryReporterManager.h" @@ -458,10 +457,12 @@ static uint32_t readCSVArray(char* csvList, const char** buffer) { return count; } -// Currently support only the env variables -// reported in read_profiler_env -static void ReadProfilerVars(const char* fileName, const char** features, - uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) { +// Support some of the env variables reported in ReadProfilerEnvVars, plus some +// extra stuff. +static void +ReadProfilerVars(const char* fileName, + const char** features, uint32_t* featureCount, + const char** threadNames, uint32_t* threadCount) { FILE* file = fopen(fileName, "r"); const int bufferSize = 1024; char line[bufferSize]; @@ -474,11 +475,11 @@ static void ReadProfilerVars(const char* fileName, const char** features, feature = strtok_r(line, "=", &savePtr); value = strtok_r(NULL, "", &savePtr); - if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) { + if (strncmp(feature, "MOZ_PROFILER_INTERVAL", bufferSize) == 0) { set_profiler_interval(value); - } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) { + } else if (strncmp(feature, "MOZ_PROFILER_ENTRIES", bufferSize) == 0) { set_profiler_entries(value); - } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) { + } else if (strncmp(feature, "MOZ_PROFILER_FEATURES", bufferSize) == 0) { *featureCount = readCSVArray(value, features); } else if (strncmp(feature, "threads", bufferSize) == 0) { *threadCount = readCSVArray(value, threadNames); @@ -505,9 +506,8 @@ static void DoStartTask() { MOZ_ASSERT(featureCount < 10); MOZ_ASSERT(threadCount < 10); - profiler_start(PROFILE_DEFAULT_ENTRY, 1, - features, featureCount, - threadNames, threadCount); + profiler_start(PROFILE_DEFAULT_ENTRIES, /* interval */ 1, + features, featureCount, threadNames, threadCount); freeArray(threadNames, threadCount); freeArray(features, featureCount); diff --git a/tools/profiler/core/platform-win32.cpp b/tools/profiler/core/platform-win32.cpp index e66438025a37..26ed3bc50df6 100644 --- a/tools/profiler/core/platform-win32.cpp +++ b/tools/profiler/core/platform-win32.cpp @@ -31,7 +31,6 @@ #include #include #include -#include "ProfileEntry.h" // Memory profile #include "nsMemoryReporterManager.h" diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index b268e9237121..89c63bdf634d 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -119,8 +119,8 @@ static mozilla::StaticMutex gThreadNameFiltersMutex; // All accesses to gFeatures are on the main thread, so no locking is needed. static Vector gFeatures; -// All accesses to gEntrySize are on the main thread, so no locking is needed. -static int gEntrySize = 0; +// All accesses to gEntries are on the main thread, so no locking is needed. +static int gEntries = 0; // All accesses to gInterval are on the main thread, so no locking is needed. static double gInterval = 0; @@ -173,8 +173,8 @@ static Atomic gUseStackWalk(false); * to know are associated with different events */ // Values harvested from env vars, that control the profiler. -static int gUnwindInterval; /* in milliseconds */ -static int gProfileEntries; /* how many entries do we store? */ +static int gEnvVarEntries; /* how many entries do we store? */ +static int gEnvVarInterval; /* in milliseconds */ static mozilla::StaticAutoPtr gInterposeObserver; @@ -232,7 +232,7 @@ public: static void AddDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr) { - aInfo.addTag(ProfileEntry::CodeLocation("")); + aInfo.addTag(ProfileBufferEntry::CodeLocation("")); size_t strLen = strlen(aStr) + 1; // +1 for the null terminator for (size_t j = 0; j < strLen; ) { @@ -246,17 +246,17 @@ AddDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr) j += sizeof(void*) / sizeof(char); // Cast to *((void**) to pass the text data to a void*. - aInfo.addTag(ProfileEntry::EmbeddedString(*((void**)(&text[0])))); + aInfo.addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0])))); } } static void -AddPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo, +AddPseudoEntry(volatile js::ProfileEntry& entry, ThreadInfo& aInfo, PseudoStack* stack, void* lastpc) { // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and // should not be recorded in the profile. - if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS)) { + if (entry.hasFlag(js::ProfileEntry::BEGIN_PSEUDO_JS)) { return; } @@ -293,7 +293,7 @@ AddPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo, lineno = entry.line(); } } else { - aInfo.addTag(ProfileEntry::CodeLocation(sampleLabel)); + aInfo.addTag(ProfileBufferEntry::CodeLocation(sampleLabel)); // XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for // js entries as well. @@ -303,15 +303,15 @@ AddPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo, } if (lineno != -1) { - aInfo.addTag(ProfileEntry::LineNumber(lineno)); + aInfo.addTag(ProfileBufferEntry::LineNumber(lineno)); } uint32_t category = entry.category(); - MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY)); - MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY)); + MOZ_ASSERT(!(category & js::ProfileEntry::IS_CPP_ENTRY)); + MOZ_ASSERT(!(category & js::ProfileEntry::FRAME_LABEL_COPY)); if (category) { - aInfo.addTag(ProfileEntry::Category((int)category)); + aInfo.addTag(ProfileBufferEntry::Category((int)category)); } } @@ -345,7 +345,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, NativeStack& aNativeStack) { PseudoStack* pseudoStack = aInfo.Stack(); - volatile StackEntry* pseudoFrames = pseudoStack->mStack; + volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack; uint32_t pseudoCount = pseudoStack->stackSize(); // Make a copy of the JS stack into a JSFrame array. This is necessary since, @@ -399,7 +399,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, } // Start the sample with a root entry. - aInfo.addTag(ProfileEntry::Sample("(root)")); + aInfo.addTag(ProfileBufferEntry::Sample("(root)")); // While the pseudo-stack array is ordered oldest-to-youngest, the JS and // native arrays are ordered youngest-to-oldest. We must add frames to aInfo @@ -420,7 +420,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, uint8_t* nativeStackAddr = nullptr; if (pseudoIndex != pseudoCount) { - volatile StackEntry& pseudoFrame = pseudoFrames[pseudoIndex]; + volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex]; if (pseudoFrame.isCpp()) { lastPseudoCppStackAddr = (uint8_t*) pseudoFrame.stackAddress(); @@ -471,7 +471,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, // Check to see if pseudoStack frame is top-most. if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) { MOZ_ASSERT(pseudoIndex < pseudoCount); - volatile StackEntry& pseudoFrame = pseudoFrames[pseudoIndex]; + volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex]; AddPseudoEntry(pseudoFrame, aInfo, pseudoStack, nullptr); pseudoIndex++; continue; @@ -502,7 +502,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion || jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline); aInfo.addTag( - ProfileEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress)); + ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress)); } jsIndex--; @@ -514,7 +514,7 @@ MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample, if (nativeStackAddr) { MOZ_ASSERT(nativeIndex >= 0); void* addr = (void*)aNativeStack.pc_array[nativeIndex]; - aInfo.addTag(ProfileEntry::NativeLeafAddr(addr)); + aInfo.addTag(ProfileBufferEntry::NativeLeafAddr(addr)); } if (nativeIndex >= 0) { nativeIndex--; @@ -616,7 +616,7 @@ DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample) for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) { // The pseudostack grows towards higher indices, so we iterate // backwards (from callee to caller). - volatile StackEntry& entry = pseudoStack->mStack[i - 1]; + volatile js::ProfileEntry& entry = pseudoStack->mStack[i - 1]; if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) { // Found JIT entry frame. Unwind up to that point (i.e., force // the stack walk to stop before the block of saved registers; @@ -772,7 +772,7 @@ DoSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample, MergeStacksIntoProfile(aInfo, aSample, nativeStack); if (aSample && aAddLeafAddresses) { - aInfo.addTag(ProfileEntry::NativeLeafAddr((void*)aSample->pc)); + aInfo.addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc)); } } @@ -783,10 +783,11 @@ Tick(TickSample* aSample) { ThreadInfo& currThreadInfo = *aSample->threadInfo; - currThreadInfo.addTag(ProfileEntry::ThreadId(currThreadInfo.ThreadId())); + currThreadInfo.addTag( + ProfileBufferEntry::ThreadId(currThreadInfo.ThreadId())); mozilla::TimeDuration delta = aSample->timestamp - gStartTime; - currThreadInfo.addTag(ProfileEntry::Time(delta.ToMilliseconds())); + currThreadInfo.addTag(ProfileBufferEntry::Time(delta.ToMilliseconds())); PseudoStack* stack = currThreadInfo.Stack(); @@ -808,7 +809,7 @@ Tick(TickSample* aSample) while (pendingMarkersList && pendingMarkersList->peek()) { ProfilerMarker* marker = pendingMarkersList->popHead(); currThreadInfo.addStoredMarker(marker); - currThreadInfo.addTag(ProfileEntry::Marker(marker)); + currThreadInfo.addTag(ProfileBufferEntry::Marker(marker)); } } @@ -816,23 +817,24 @@ Tick(TickSample* aSample) mozilla::TimeDuration delta = currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration( aSample->timestamp); - currThreadInfo.addTag(ProfileEntry::Responsiveness(delta.ToMilliseconds())); + currThreadInfo.addTag( + ProfileBufferEntry::Responsiveness(delta.ToMilliseconds())); } // rssMemory is equal to 0 when we are not recording. if (aSample->rssMemory != 0) { - currThreadInfo.addTag(ProfileEntry::ResidentMemory( + currThreadInfo.addTag(ProfileBufferEntry::ResidentMemory( static_cast(aSample->rssMemory))); } // ussMemory is equal to 0 when we are not recording. if (aSample->ussMemory != 0) { - currThreadInfo.addTag(ProfileEntry::UnsharedMemory( + currThreadInfo.addTag(ProfileBufferEntry::UnsharedMemory( static_cast(aSample->ussMemory))); } if (gLastFrameNumber != gFrameNumber) { - currThreadInfo.addTag(ProfileEntry::FrameNumber(gFrameNumber)); + currThreadInfo.addTag(ProfileBufferEntry::FrameNumber(gFrameNumber)); gLastFrameNumber = gFrameNumber; } } @@ -1233,9 +1235,7 @@ void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter, } // Verbosity control for the profiler. The aim is to check env var -// MOZ_PROFILER_VERBOSE only once. However, we may need to temporarily -// override that so as to print the profiler's help message. That's -// what profiler_set_verbosity is for. +// MOZ_PROFILER_VERBOSE only once. enum class Verbosity : int8_t { UNCHECKED, NOTVERBOSE, VERBOSE }; @@ -1247,21 +1247,13 @@ profiler_verbose() { if (gVerbosity == Verbosity::UNCHECKED) { gVerbosity = getenv("MOZ_PROFILER_VERBOSE") - ? Verbosity::VERBOSE - : Verbosity::NOTVERBOSE; + ? Verbosity::VERBOSE + : Verbosity::NOTVERBOSE; } return gVerbosity == Verbosity::VERBOSE; } -static void -profiler_set_verbosity(Verbosity aPv) -{ - MOZ_ASSERT(aPv == Verbosity::UNCHECKED || - aPv == Verbosity::VERBOSE); - gVerbosity = aPv; -} - static bool set_profiler_interval(const char* aInterval) { @@ -1269,7 +1261,7 @@ set_profiler_interval(const char* aInterval) errno = 0; long int n = strtol(aInterval, nullptr, 10); if (errno == 0 && 1 <= n && n <= 1000) { - gUnwindInterval = n; + gEnvVarInterval = n; return true; } return false; @@ -1285,7 +1277,7 @@ set_profiler_entries(const char* aEntries) errno = 0; long int n = strtol(aEntries, nullptr, 10); if (errno == 0 && n > 0) { - gProfileEntries = n; + gEnvVarEntries = n; return true; } return false; @@ -1304,84 +1296,63 @@ is_native_unwinding_avail() #endif } -// Environment variables to control the profiler -static const char* PROFILER_HELP = "MOZ_PROFILER_HELP"; -static const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL"; -static const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES"; -#if defined(GP_OS_android) -static const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES"; -#endif - static void -profiler_usage() +profiler_usage(int aExitCode) { - LOG( "Profiler: "); - LOG( "Profiler: Environment variable usage:"); - LOG( "Profiler: "); - LOG( "Profiler: MOZ_PROFILER_HELP"); - LOG( "Profiler: If set to any value, prints this message."); - LOG( "Profiler: "); - LOG( "Profiler: MOZ_PROFILER_INTERVAL= (milliseconds, 1 to 1000)"); - LOG( "Profiler: If unset, platform default is used."); - LOG( "Profiler: "); - LOG( "Profiler: MOZ_PROFILER_ENTRIES= (count, minimum of 1)"); - LOG( "Profiler: If unset, platform default is used."); - LOG( "Profiler: "); - LOG( "Profiler: MOZ_PROFILER_VERBOSE"); - LOG( "Profiler: If set to any value, increases verbosity (recommended)."); - LOG( "Profiler: "); - LOG( "Profiler: MOZ_PROFILER_LUL_TEST"); - LOG( "Profiler: If set to any value, runs LUL unit tests at startup of"); - LOG( "Profiler: the unwinder thread, and prints a short summary of results."); - LOG( "Profiler: "); - LOGF("Profiler: This platform %s native unwinding.", + // Force-enable verbosity so that LOG prints something. + gVerbosity = Verbosity::VERBOSE; + + LOG (""); + LOG ("Environment variable usage:"); + LOG (""); + LOG (" MOZ_PROFILER_HELP"); + LOG (" If set to any value, prints this message."); + LOG (""); + LOG (" MOZ_PROFILER_ENTRIES=<1..> (count)"); + LOG (" If unset, platform default is used."); + LOG (""); + LOG (" MOZ_PROFILER_INTERVAL=<1..1000> (milliseconds)"); + LOG (" If unset, platform default is used."); + LOG (""); + LOG (" MOZ_PROFILER_VERBOSE"); + LOG (" If set to any value, increases verbosity (recommended)."); + LOG (""); + LOG (" MOZ_PROFILER_LUL_TEST"); + LOG (" If set to any value, runs LUL unit tests at startup of"); + LOG (" the unwinder thread, and prints a short summary of "); + LOG (" results."); + LOG (""); + LOGF(" This platform %s native unwinding.", is_native_unwinding_avail() ? "supports" : "does not support"); - LOG( "Profiler: "); + LOG (""); - /* Re-set defaults */ - gUnwindInterval = 0; /* We'll have to look elsewhere */ - gProfileEntries = 0; - - LOG( "Profiler:"); - LOGF("Profiler: Sampling interval = %d ms (zero means \"platform default\")", - (int)gUnwindInterval); - LOGF("Profiler: Entry store size = %d (zero means \"platform default\")", - (int)gProfileEntries); - LOG( "Profiler:"); + exit(aExitCode); } // Read env vars at startup, so as to set: -// gUnwindInterval, gProfileEntries +// gEnvVarEntries, gEnvVarInterval static void -read_profiler_env_vars() +ReadProfilerEnvVars() { - /* Set defaults */ - gUnwindInterval = 0; /* We'll have to look elsewhere */ - gProfileEntries = 0; + const char* help = getenv("MOZ_PROFILER_HELP"); + const char* entries = getenv("MOZ_PROFILER_ENTRIES"); + const char* interval = getenv("MOZ_PROFILER_INTERVAL"); - const char* interval = getenv(PROFILER_INTERVAL); - const char* entries = getenv(PROFILER_ENTRIES); - - if (getenv(PROFILER_HELP)) { - // Enable verbose output - profiler_set_verbosity(Verbosity::VERBOSE); - profiler_usage(); - // Now force the next enquiry of profiler_verbose to re-query - // env var MOZ_PROFILER_VERBOSE. - profiler_set_verbosity(Verbosity::UNCHECKED); + if (help) { + profiler_usage(0); // terminates execution } - if (!set_profiler_interval(interval) || - !set_profiler_entries(entries)) { - profiler_usage(); - } else { - LOG( "Profiler:"); - LOGF("Profiler: Sampling interval = %d ms (zero means \"platform default\")", - (int)gUnwindInterval); - LOGF("Profiler: Entry store size = %d (zero means \"platform default\")", - (int)gProfileEntries); - LOG( "Profiler:"); + if (!set_profiler_entries(entries) || + !set_profiler_interval(interval)) { + profiler_usage(1); // terminates execution } + + LOG (""); + LOGF("entries = %d (zero means \"platform default\")", + gEnvVarEntries); + LOGF("interval = %d ms (zero means \"platform default\")", + gEnvVarInterval); + LOG (""); } static bool @@ -1600,7 +1571,7 @@ profiler_init(void* stackTop) RegisterCurrentThread(gGeckoThreadName, stack, isMainThread, stackTop); // Read settings from environment variables. - read_profiler_env_vars(); + ReadProfilerEnvVars(); // Platform-specific initialization. PlatformInit(); @@ -1632,7 +1603,7 @@ profiler_init(void* stackTop) const char* threadFilters[] = { "GeckoMain", "Compositor" }; - profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL, + profiler_start(PROFILE_DEFAULT_ENTRIES, PROFILE_DEFAULT_INTERVAL, features, MOZ_ARRAY_LENGTH(features), threadFilters, MOZ_ARRAY_LENGTH(threadFilters)); LOG("END profiler_init"); @@ -1752,19 +1723,18 @@ profiler_save_profile_to_file_async(double aSinceTime, const char* aFileName) } void -profiler_get_start_params(int* aEntrySize, - double* aInterval, +profiler_get_start_params(int* aEntries, double* aInterval, mozilla::Vector* aFilters, mozilla::Vector* aFeatures) { MOZ_RELEASE_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) || + if (NS_WARN_IF(!aEntries) || NS_WARN_IF(!aInterval) || NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) { return; } - *aEntrySize = gEntrySize; + *aEntries = gEntries; *aInterval = gInterval; { @@ -1886,9 +1856,9 @@ profiler_get_features() } void -profiler_get_buffer_info_helper(uint32_t *aCurrentPosition, - uint32_t *aTotalSize, - uint32_t *aGeneration) +profiler_get_buffer_info_helper(uint32_t* aCurrentPosition, + uint32_t* aEntries, + uint32_t* aGeneration) { // This function is called by profiler_get_buffer_info(), which has already // zeroed the outparams. @@ -1903,7 +1873,7 @@ profiler_get_buffer_info_helper(uint32_t *aCurrentPosition, } *aCurrentPosition = gBuffer->mWritePos; - *aTotalSize = gEntrySize; + *aEntries = gEntries; *aGeneration = gBuffer->mGeneration; } @@ -1923,7 +1893,7 @@ class Sampler {}; // Values are only honored on the first start void -profiler_start(int aProfileEntries, double aInterval, +profiler_start(int aEntries, double aInterval, const char** aFeatures, uint32_t aFeatureCount, const char** aThreadNameFilters, uint32_t aFilterCount) @@ -1937,12 +1907,12 @@ profiler_start(int aProfileEntries, double aInterval, /* If the sampling interval was set using env vars, use that in preference to anything else. */ - if (gUnwindInterval > 0) - aInterval = gUnwindInterval; + if (gEnvVarInterval > 0) + aInterval = gEnvVarInterval; /* If the entry count was set using env vars, use that, too: */ - if (gProfileEntries > 0) - aProfileEntries = gProfileEntries; + if (gEnvVarEntries > 0) + aEntries = gEnvVarEntries; // Reset the current state if the profiler is running profiler_stop(); @@ -1987,9 +1957,9 @@ profiler_start(int aProfileEntries, double aInterval, aFilterCount > 0; gUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk"); - gEntrySize = aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY; + gEntries = aEntries ? aEntries : PROFILE_DEFAULT_ENTRIES; gInterval = aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL; - gBuffer = new ProfileBuffer(gEntrySize); + gBuffer = new ProfileBuffer(gEntries); gSampler = new Sampler(); bool ignore; @@ -2073,7 +2043,7 @@ profiler_start(int aProfileEntries, double aInterval, } nsCOMPtr params = - new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray, + new nsProfilerStartParams(aEntries, aInterval, featuresArray, threadNameFiltersArray); os->NotifyObservers(params, "profiler-started", nullptr); @@ -2150,7 +2120,7 @@ profiler_stop() delete gSampler; gSampler = nullptr; gBuffer = nullptr; - gEntrySize = 0; + gEntries = 0; gInterval = 0; // Cancel any in-flight async profile gatherering requests. @@ -2518,7 +2488,7 @@ profiler_get_backtrace_noalloc(char *output, size_t outputSize) return; } - volatile StackEntry *pseudoFrames = pseudoStack->mStack; + volatile js::ProfileEntry *pseudoFrames = pseudoStack->mStack; uint32_t pseudoCount = pseudoStack->stackSize(); for (uint32_t i = 0; i < pseudoCount; i++) { diff --git a/tools/profiler/gecko/ThreadResponsiveness.cpp b/tools/profiler/gecko/ThreadResponsiveness.cpp index 6d2d7e0025de..b7e317e6abc8 100644 --- a/tools/profiler/gecko/ThreadResponsiveness.cpp +++ b/tools/profiler/gecko/ThreadResponsiveness.cpp @@ -9,7 +9,6 @@ #include "nsThreadUtils.h" #include "nsITimer.h" #include "mozilla/Monitor.h" -#include "ProfileEntry.h" using mozilla::Monitor; using mozilla::MonitorAutoLock; diff --git a/tools/profiler/gecko/nsProfiler.cpp b/tools/profiler/gecko/nsProfiler.cpp index 582a26e12aa7..fb6c0d306fb9 100644 --- a/tools/profiler/gecko/nsProfiler.cpp +++ b/tools/profiler/gecko/nsProfiler.cpp @@ -280,7 +280,8 @@ nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal) } NS_IMETHODIMP -nsProfiler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration) +nsProfiler::GetBufferInfo(uint32_t* aCurrentPosition, uint32_t* aTotalSize, + uint32_t* aGeneration) { MOZ_ASSERT(aCurrentPosition); MOZ_ASSERT(aTotalSize); diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build index 5b83893a4ab6..cc85bb43706e 100644 --- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -22,7 +22,7 @@ if CONFIG['MOZ_GECKO_PROFILER']: UNIFIED_SOURCES += [ 'core/platform.cpp', 'core/ProfileBuffer.cpp', - 'core/ProfileEntry.cpp', + 'core/ProfileBufferEntry.cpp', 'core/ProfileJSONWriter.cpp', 'core/ProfilerBacktrace.cpp', 'core/ProfilerMarkers.cpp', diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index 49effd3fe5fc..14c3247d7bd3 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -161,15 +161,17 @@ PROFILER_FUNC_VOID(profiler_shutdown()) // Start the profiler with the selected options. The samples will be // recorded in a circular buffer. -// "aProfileEntries" is an abstract size indication of how big +// "aEntries" is an abstract size indication of how big // the profile's circular buffer should be. Multiply by 4 // words to get the cost. // "aInterval" the sampling interval. The profiler will do its // best to sample at this interval. The profiler visualization // should represent the actual sampling accuracy. -PROFILER_FUNC_VOID(profiler_start(int aProfileEntries, double aInterval, - const char** aFeatures, uint32_t aFeatureCount, - const char** aThreadNameFilters, uint32_t aFilterCount)) +PROFILER_FUNC_VOID(profiler_start(int aEntries, double aInterval, + const char** aFeatures, + uint32_t aFeatureCount, + const char** aThreadNameFilters, + uint32_t aFilterCount)) // Stop the profiler and discard the profile. Call 'profiler_save' before this // to retrieve the profile. @@ -234,7 +236,7 @@ PROFILER_FUNC_VOID(profiler_save_profile_to_file(const char* aFilename)) PROFILER_FUNC(const char** profiler_get_features(), nullptr) PROFILER_FUNC_VOID(profiler_get_buffer_info_helper(uint32_t* aCurrentPosition, - uint32_t* aTotalSize, + uint32_t* aEntries, uint32_t* aGeneration)) // Get information about the current buffer status. @@ -245,14 +247,14 @@ PROFILER_FUNC_VOID(profiler_get_buffer_info_helper(uint32_t* aCurrentPosition, // for how fast the buffer is being written to, and how much // data is visible. static inline void profiler_get_buffer_info(uint32_t* aCurrentPosition, - uint32_t* aTotalSize, + uint32_t* aEntries, uint32_t* aGeneration) { *aCurrentPosition = 0; - *aTotalSize = 0; + *aEntries = 0; *aGeneration = 0; - profiler_get_buffer_info_helper(aCurrentPosition, aTotalSize, aGeneration); + profiler_get_buffer_info_helper(aCurrentPosition, aEntries, aGeneration); } // Lock the profiler. When locked the profiler is (1) stopped, @@ -433,14 +435,14 @@ void profiler_OOP_exit_profile(const nsCString& aProfile); #endif #if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6) -# define PROFILE_DEFAULT_ENTRY 1000000 +# define PROFILE_DEFAULT_ENTRIES 1000000 #else -# define PROFILE_DEFAULT_ENTRY 100000 +# define PROFILE_DEFAULT_ENTRIES 100000 #endif // In the case of profiler_get_backtrace we know that we only need enough space // for a single backtrace. -#define GET_BACKTRACE_DEFAULT_ENTRY 1000 +#define GET_BACKTRACE_DEFAULT_ENTRIES 1000 #if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) /* A 1ms sampling interval has been shown to be a large perf hit diff --git a/tools/profiler/public/PseudoStack.h b/tools/profiler/public/PseudoStack.h index cabeabe17f9d..c5e8765401cf 100644 --- a/tools/profiler/public/PseudoStack.h +++ b/tools/profiler/public/PseudoStack.h @@ -49,18 +49,6 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = # error "Memory clobber not supported for your platform." #endif -// A stack entry exists to allow the JS engine to inform the Gecko Profiler of -// the current backtrace, but also to instrument particular points in C++ in -// case stack walking is not available on the platform we are running on. -// -// Each entry has a descriptive string, a relevant stack address, and some extra -// information the JS engine might want to inform the Gecko Profiler of. This -// class inherits from the JS engine's version of the entry to ensure that the -// size and layout of the two representations are consistent. -class StackEntry : public js::ProfileEntry -{ -}; - class ProfilerMarkerPayload; template class ProfilerLinkedList; @@ -273,7 +261,7 @@ public: return; } - volatile StackEntry& entry = mStack[mStackPointer]; + volatile js::ProfileEntry& entry = mStack[mStackPointer]; // Make sure we increment the pointer after the name has been written such // that mStack is always consistent. @@ -412,7 +400,7 @@ private: public: // The list of active checkpoints. - StackEntry volatile mStack[1024]; + js::ProfileEntry volatile mStack[1024]; private: // A list of pending markers that must be moved to the circular buffer. diff --git a/tools/profiler/tasktracer/GeckoTaskTracer.cpp b/tools/profiler/tasktracer/GeckoTaskTracer.cpp index ed320e7b635f..5a0acd916c3a 100644 --- a/tools/profiler/tasktracer/GeckoTaskTracer.cpp +++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp @@ -57,7 +57,7 @@ static mozilla::StaticMutex sMutex; // The generation of TraceInfo. It will be > 0 if the Task Tracer is started and // <= 0 if stopped. -static mozilla::Atomic sStarted; +static mozilla::Atomic sStarted(false); static nsTArray>* sTraceInfos = nullptr; static PRTime sStartTime; @@ -74,8 +74,8 @@ GetTimestamp() static TraceInfo* AllocTraceInfo(int aTid) { - StaticMutexAutoLock lock(sMutex); - + sMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(sTraceInfos); auto* info = sTraceInfos->AppendElement(MakeUnique(aTid)); return info->get(); @@ -86,12 +86,14 @@ CreateSourceEvent(SourceEventType aType) { // Create a new unique task id. uint64_t newId = GenNewUniqueTaskId(); - TraceInfo* info = GetOrCreateTraceInfo(); - ENSURE_TRUE_VOID(info); + { + TraceInfoHolder info = GetOrCreateTraceInfo(); + ENSURE_TRUE_VOID(info); - info->mCurTraceSourceId = newId; - info->mCurTraceSourceType = aType; - info->mCurTaskId = newId; + info->mCurTraceSourceId = newId; + info->mCurTraceSourceType = aType; + info->mCurTaskId = newId; + } uintptr_t* namePtr; #define SOURCE_EVENT_NAME(type) \ @@ -118,10 +120,23 @@ static void DestroySourceEvent() { // Log a fake end for this source event. - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); - LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId); + uint64_t curTraceSourceId; + curTraceSourceId = info->mCurTraceSourceId; + info.Reset(); + + LogEnd(curTraceSourceId, curTraceSourceId); +} + +inline static void +ObsoleteCurrentTraceInfos() +{ + MOZ_ASSERT(sTraceInfos); + for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { + (*sTraceInfos)[i]->mObsolete = true; + } } inline static bool @@ -134,24 +149,16 @@ static void SetLogStarted(bool aIsStartLogging) { MOZ_ASSERT(aIsStartLogging != sStarted); - MOZ_ASSERT(sTraceInfos != nullptr); + StaticMutexAutoLock lock(sMutex); + sStarted = aIsStartLogging; - StaticMutexAutoLock lock(sMutex); - if (!aIsStartLogging && sTraceInfos) { - for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { - (*sTraceInfos)[i]->mObsolete = true; - } + if (aIsStartLogging && sTraceInfos == nullptr) { + sTraceInfos = new nsTArray>(); } -} -inline static void -ObsoleteCurrentTraceInfos() -{ - // Note that we can't and don't need to acquire sMutex here because this - // function is called before the other threads are recreated. - for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { - (*sTraceInfos)[i]->mObsolete = true; + if (!aIsStartLogging && sTraceInfos) { + ObsoleteCurrentTraceInfos(); } } @@ -191,9 +198,6 @@ InitTaskTracer(uint32_t aFlags) if (!success) { MOZ_CRASH(); } - - // A memory barrier is necessary here. - sTraceInfos = new nsTArray>(); } void @@ -201,13 +205,22 @@ ShutdownTaskTracer() { if (IsStartLogging()) { SetLogStarted(false); + + StaticMutexAutoLock lock(sMutex); + // Make sure all threads are out of holding mutics. + // See |GetOrCreateTraceInfo()| + for (auto& traceinfo: *sTraceInfos) { + MutexAutoLock lock(traceinfo->mLogsMutex); + } + delete sTraceInfos; + sTraceInfos = nullptr; } } static void FreeTraceInfo(TraceInfo* aTraceInfo) { - StaticMutexAutoLock lock(sMutex); + sMutex.AssertCurrentThreadOwns(); if (aTraceInfo) { UniquePtr traceinfo(aTraceInfo); mozilla::DebugOnly removed = @@ -219,15 +232,19 @@ FreeTraceInfo(TraceInfo* aTraceInfo) void FreeTraceInfo() { - FreeTraceInfo(sTraceInfoTLS.get()); + StaticMutexAutoLock lock(sMutex); + if (sTraceInfos) { + FreeTraceInfo(sTraceInfoTLS.get()); + } } -TraceInfo* +TraceInfoHolder GetOrCreateTraceInfo() { - ENSURE_TRUE(IsStartLogging(), nullptr); - TraceInfo* info = sTraceInfoTLS.get(); + StaticMutexAutoLock lock(sMutex); + ENSURE_TRUE(IsStartLogging(), TraceInfoHolder{}); + if (info && info->mObsolete) { // TraceInfo is obsolete: remove it. FreeTraceInfo(info); @@ -239,13 +256,15 @@ GetOrCreateTraceInfo() sTraceInfoTLS.set(info); } - return info; + return TraceInfoHolder{info}; // |mLogsMutex| will be held, then + // ||sMutex| will be released for + // efficiency reason. } uint64_t GenNewUniqueTaskId() { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE(info, 0); Thread::tid_t tid = Thread::GetCurrentId(); @@ -267,7 +286,7 @@ void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId, SourceEventType aSourceEventType) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = aSourceEventId; @@ -279,7 +298,7 @@ void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId, SourceEventType* aOutSourceEventType) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); *aOutSourceEventId = info->mCurTraceSourceId; @@ -298,7 +317,7 @@ void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, SourceEventType aSourceEventType, int aDelayTimeMs) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); // aDelayTimeMs is the expected delay time in milliseconds, thus the dispatch @@ -306,7 +325,6 @@ LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, uint64_t time = (aDelayTimeMs <= 0) ? GetTimestamp() : GetTimestamp() + aDelayTimeMs; - MutexAutoLock lock(info->mLogsMutex); // Log format: // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId] TraceInfoLogType* log = info->AppendLog(); @@ -323,10 +341,9 @@ LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); - MutexAutoLock lock(info->mLogsMutex); // Log format: // [1 taskId beginTime processId threadId] TraceInfoLogType* log = info->AppendLog(); @@ -342,10 +359,9 @@ LogBegin(uint64_t aTaskId, uint64_t aSourceEventId) void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); - MutexAutoLock lock(info->mLogsMutex); // Log format: // [2 taskId endTime] TraceInfoLogType* log = info->AppendLog(); @@ -359,10 +375,9 @@ LogEnd(uint64_t aTaskId, uint64_t aSourceEventId) void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, uintptr_t* aVptr) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); - MutexAutoLock lock(info->mLogsMutex); // Log format: // [4 taskId address] TraceInfoLogType* log = info->AppendLog(); @@ -412,10 +427,9 @@ AutoScopedLabel::~AutoScopedLabel() void AddLabel(const char* aFormat, ...) { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); - MutexAutoLock lock(info->mLogsMutex); // Log format: // [3 taskId "label"] TraceInfoLogType* log = info->AppendLog(); @@ -456,6 +470,10 @@ GetLoggedData(TimeStamp aTimeStamp) // TODO: This is called from a signal handler. Use semaphore instead. StaticMutexAutoLock lock(sMutex); + if (sTraceInfos == nullptr) { + return result; + } + for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { TraceInfo* info = (*sTraceInfos)[i].get(); MutexAutoLock lockLogs(info->mLogsMutex); diff --git a/tools/profiler/tasktracer/GeckoTaskTracerImpl.h b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h index 428d020edbd0..48a9da1287fc 100644 --- a/tools/profiler/tasktracer/GeckoTaskTracerImpl.h +++ b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h @@ -107,8 +107,43 @@ struct TraceInfo nsTArray mStrs; }; +class TraceInfoHolder { +public: + TraceInfoHolder() : mInfo(nullptr) {} + explicit TraceInfoHolder(TraceInfo* aInfo) : mInfo(aInfo) { + aInfo->mLogsMutex.AssertNotCurrentThreadOwns(); // in case of recursive + aInfo->mLogsMutex.Lock(); + MOZ_ASSERT(aInfo); + } + TraceInfoHolder(const TraceInfoHolder& aOther) = delete; + TraceInfoHolder(TraceInfoHolder&& aOther) : mInfo(aOther.mInfo) { + if (!!aOther) { + aOther->mLogsMutex.AssertCurrentThreadOwns(); + } + aOther.mInfo = nullptr; + } + ~TraceInfoHolder() { if (mInfo) mInfo->mLogsMutex.Unlock(); } + explicit operator bool() const { return !!mInfo; } + TraceInfo* operator ->() { return mInfo; } + bool operator ==(TraceInfo* aOther) const { + return mInfo == aOther; + } + bool operator ==(const TraceInfoHolder& aOther) const { + return mInfo == aOther.mInfo; + } + void Reset() { + if (mInfo) { + mInfo->mLogsMutex.Unlock(); + mInfo = nullptr; + } + } + +private: + TraceInfo* mInfo; +}; + // Return the TraceInfo of current thread, allocate a new one if not exit. -TraceInfo* GetOrCreateTraceInfo(); +TraceInfoHolder GetOrCreateTraceInfo(); uint64_t GenNewUniqueTaskId(); diff --git a/tools/profiler/tasktracer/TracedTaskCommon.cpp b/tools/profiler/tasktracer/TracedTaskCommon.cpp index bc4b4bd4030f..4e8796f8a3c9 100644 --- a/tools/profiler/tasktracer/TracedTaskCommon.cpp +++ b/tools/profiler/tasktracer/TracedTaskCommon.cpp @@ -34,7 +34,7 @@ TracedTaskCommon::~TracedTaskCommon() void TracedTaskCommon::Init() { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); mTaskId = GenNewUniqueTaskId(); @@ -54,7 +54,7 @@ TracedTaskCommon::DispatchTask(int aDelayTimeMs) void TracedTaskCommon::GetTLSTraceInfo() { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); mSourceEventType = info->mCurTraceSourceType; @@ -66,7 +66,7 @@ TracedTaskCommon::GetTLSTraceInfo() void TracedTaskCommon::SetTLSTraceInfo() { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); if (mIsTraceInfoInit) { @@ -79,7 +79,7 @@ TracedTaskCommon::SetTLSTraceInfo() void TracedTaskCommon::ClearTLSTraceInfo() { - TraceInfo* info = GetOrCreateTraceInfo(); + TraceInfoHolder info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = 0; diff --git a/tools/profiler/tests/gtest/ThreadProfileTest.cpp b/tools/profiler/tests/gtest/ThreadProfileTest.cpp index a7f04bca329e..3c05b813c6ed 100644 --- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp +++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp @@ -5,7 +5,7 @@ #include "gtest/gtest.h" -#include "ProfileEntry.h" +#include "ProfileBufferEntry.h" #include "ThreadInfo.h" // Make sure we can initialize our thread profile @@ -23,9 +23,10 @@ TEST(ThreadProfile, InsertOneTag) { Thread::tid_t tid = 1000; ThreadInfo info("testThread", tid, true, stack, nullptr); RefPtr pb = new ProfileBuffer(10); - pb->addTag(ProfileEntry::Time(123.1)); + pb->addTag(ProfileBufferEntry::Time(123.1)); ASSERT_TRUE(pb->mEntries != nullptr); - ASSERT_TRUE(pb->mEntries[pb->mReadPos].kind() == ProfileEntry::Kind::Time); + ASSERT_TRUE(pb->mEntries[pb->mReadPos].kind() == + ProfileBufferEntry::Kind::Time); ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1); } @@ -37,12 +38,13 @@ TEST(ThreadProfile, InsertTagsNoWrap) { RefPtr pb = new ProfileBuffer(100); int test_size = 50; for (int i = 0; i < test_size; i++) { - pb->addTag(ProfileEntry::Time(i)); + pb->addTag(ProfileBufferEntry::Time(i)); } ASSERT_TRUE(pb->mEntries != nullptr); int readPos = pb->mReadPos; while (readPos != pb->mWritePos) { - ASSERT_TRUE(pb->mEntries[readPos].kind() == ProfileEntry::Kind::Time); + ASSERT_TRUE(pb->mEntries[readPos].kind() == + ProfileBufferEntry::Kind::Time); ASSERT_TRUE(pb->mEntries[readPos].mTagDouble == readPos); readPos = (readPos + 1) % pb->mEntrySize; } @@ -59,13 +61,14 @@ TEST(ThreadProfile, InsertTagsWrap) { RefPtr pb = new ProfileBuffer(buffer_size); int test_size = 43; for (int i = 0; i < test_size; i++) { - pb->addTag(ProfileEntry::Time(i)); + pb->addTag(ProfileBufferEntry::Time(i)); } ASSERT_TRUE(pb->mEntries != nullptr); int readPos = pb->mReadPos; int ctr = 0; while (readPos != pb->mWritePos) { - ASSERT_TRUE(pb->mEntries[readPos].kind() == ProfileEntry::Kind::Time); + ASSERT_TRUE(pb->mEntries[readPos].kind() == + ProfileBufferEntry::Kind::Time); // the first few tags were discarded when we wrapped ASSERT_TRUE(pb->mEntries[readPos].mTagDouble == ctr + (test_size - tags)); ctr++; diff --git a/widget/android/ANRReporter.cpp b/widget/android/ANRReporter.cpp index 30d9b3d7624d..052299c41366 100644 --- a/widget/android/ANRReporter.cpp +++ b/widget/android/ANRReporter.cpp @@ -38,8 +38,10 @@ ANRReporter::RequestNativeStack(bool aUnwind) const char *NATIVE_STACK_THREADS[] = {"GeckoMain", "Compositor"}; // Buffer one sample and let the profiler wait a long time - profiler_start(100, 10000, features, features_size / sizeof(char*), - NATIVE_STACK_THREADS, sizeof(NATIVE_STACK_THREADS) / sizeof(char*)); + profiler_start(/* entries */ 100, /* interval */ 10000, + features, features_size / sizeof(char*), + NATIVE_STACK_THREADS, + sizeof(NATIVE_STACK_THREADS) / sizeof(char*)); return true; } diff --git a/xpcom/threads/ThreadStackHelper.cpp b/xpcom/threads/ThreadStackHelper.cpp index 66d1d550adcd..0e2437e8cc6e 100644 --- a/xpcom/threads/ThreadStackHelper.cpp +++ b/xpcom/threads/ThreadStackHelper.cpp @@ -426,7 +426,7 @@ GetPathAfterComponent(const char* filename, const char (&component)[LEN]) { } // namespace const char* -ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry, +ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry, intptr_t& aAvailableBufferSize, const char* aPrevLabel) { @@ -513,8 +513,8 @@ ThreadStackHelper::FillStackBuffer() intptr_t availableBufferSize = intptr_t(reservedBufferSize); // Go from front to back - const volatile StackEntry* entry = mPseudoStack->mStack; - const volatile StackEntry* end = entry + mPseudoStack->stackSize(); + const volatile js::ProfileEntry* entry = mPseudoStack->mStack; + const volatile js::ProfileEntry* end = entry + mPseudoStack->stackSize(); // Deduplicate identical, consecutive frames const char* prevLabel = nullptr; for (; reservedSize-- && entry != end; entry++) { diff --git a/xpcom/threads/ThreadStackHelper.h b/xpcom/threads/ThreadStackHelper.h index 12e9e6487b9d..347f7c0d4c97 100644 --- a/xpcom/threads/ThreadStackHelper.h +++ b/xpcom/threads/ThreadStackHelper.h @@ -8,8 +8,7 @@ #define mozilla_ThreadStackHelper_h #include "mozilla/ThreadHangStats.h" - -#include "GeckoProfiler.h" +#include "js/ProfilingStack.h" #include @@ -79,7 +78,7 @@ private: void FillStackBuffer(); void FillThreadContext(void* aContext = nullptr); #ifdef MOZ_THREADSTACKHELPER_PSEUDO - const char* AppendJSEntry(const volatile StackEntry* aEntry, + const char* AppendJSEntry(const volatile js::ProfileEntry* aEntry, intptr_t& aAvailableBufferSize, const char* aPrevLabel); #endif