ma1 pushed to branch mullvad-browser-140.17.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 02322e29 by James Teh at 2026-09-28T10:43:54+02:00 Bug 2056767: Shut down DocAccessibleParents before we shut down platform accessibility. a=pascalc Original Revision: https://phabricator.services.mozilla.com/D322228 Differential Revision: https://phabricator.services.mozilla.com/D323108 - - - - - 76943803 by Rob Wu at 2026-09-28T10:43:58+02:00 Bug 2061470 - Don't drop __proto__ from webRequest bodies r=rpl Differential Revision: https://phabricator.services.mozilla.com/D323631 - - - - - 357553c1 by Marco Bonardo at 2026-09-28T10:44:03+02:00 Bug 2066019. a=pascalc Original Revision: https://phabricator.services.mozilla.com/D321246 Differential Revision: https://phabricator.services.mozilla.com/D323563 - - - - - 0060fe36 by Tom Schuster at 2026-09-28T10:44:07+02:00 Bug 2066321 - Update nonceable attribute checks for link elements. r=freddyb Pending PR: https://github.com/w3c/webappsec-csp/pull/810 Differential Revision: https://phabricator.services.mozilla.com/D322966 - - - - - 855edaae by Leo Tenenbaum at 2026-09-28T10:44:12+02:00 Bug 2067172 - a=pascalc Original Revision: https://phabricator.services.mozilla.com/D321879 Differential Revision: https://phabricator.services.mozilla.com/D322306 - - - - - ba101997 by Iain Ireland at 2026-09-28T10:44:16+02:00 Bug 2068385: Fix PBL a=pascalc Original Revision: https://phabricator.services.mozilla.com/D323744 Differential Revision: https://phabricator.services.mozilla.com/D324712 - - - - - 59434ea5 by Iain Ireland at 2026-09-28T10:44:19+02:00 Bug 2068385: Don't support sparse arrays in SpreadMathMinMax a=pascalc Marking the testcase --slow because it takes 7+ minutes on my laptop in an opt-debug build. Original Revision: https://phabricator.services.mozilla.com/D323120 Differential Revision: https://phabricator.services.mozilla.com/D324692 - - - - - 14 changed files: - accessible/base/DocManager.cpp - accessible/ipc/DocAccessibleParent.cpp - browser/components/places/content/controller.js - browser/components/places/tests/browser/browser_toolbar_drop_bookmarklet.js - browser/components/places/tests/browser/browser_toolbar_drop_multiple_with_bookmarklet.js - dom/base/nsContentUtils.cpp - dom/script/ScriptLoader.cpp - dom/security/nsContentSecurityUtils.cpp - js/src/jit/BaselineIC.cpp - js/src/jit/CacheIR.cpp - js/src/jit/CacheIRWriter.h - js/src/vm/PortableBaselineInterpret.cpp - toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html - toolkit/components/extensions/webrequest/WebRequestUpload.sys.mjs Changes: ===================================== accessible/base/DocManager.cpp ===================================== @@ -31,6 +31,10 @@ #include "nsCoreUtils.h" #include "xpcAccessibleDocument.h" +#if defined(ANDROID) +# include "mozilla/Monitor.h" +#endif + using namespace mozilla; using namespace mozilla::a11y; using namespace mozilla::dom; @@ -195,6 +199,18 @@ void DocManager::Shutdown() { } ClearDocCache(); + // Even though remote documents aren't strictly managed by this DocManager + // instance, destroy them now because they might depend on platform specific + // state which is about to be torn down by PlatformShutdown. Iterate the array + // backwards because destroying the document removes it from this array. + if (sRemoteDocuments) { +#if defined(ANDROID) + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); +#endif + for (size_t i = sRemoteDocuments->Length(); i-- > 0;) { + (*sRemoteDocuments)[i]->Destroy(); + } + } } //////////////////////////////////////////////////////////////////////////////// ===================================== accessible/ipc/DocAccessibleParent.cpp ===================================== @@ -1241,15 +1241,14 @@ void DocAccessibleParent::MaybeInitWindowEmulation() { isActive = browserParent->GetDocShellIsActive(); } - // onCreate is guaranteed to be called synchronously by - // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary. - // However, static analysis complains without it. RefPtr<DocAccessibleParent> thisRef = this; - nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void { - ::SetPropW(aHwnd, kPropNameDocAccParent, - reinterpret_cast<HANDLE>(thisRef.get())); - thisRef->SetEmulatedWindowHandle(aHwnd); - }); + nsWinUtils::NativeWindowCreateProc onCreate( + [thisRef](HWND aHwnd) mutable -> void { + thisRef->SetEmulatedWindowHandle(aHwnd); + HANDLE val; + thisRef.forget(&val); // Release in SetEmulatedWindowHandle. + ::SetPropW(aHwnd, kPropNameDocAccParent, val); + }); HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow()); DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow( @@ -1261,6 +1260,7 @@ void DocAccessibleParent::MaybeInitWindowEmulation() { void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) { if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) { ::DestroyWindow(mEmulatedWindowHandle); + Release(); // AddRef in MaybeInitWindowEmulation. } mEmulatedWindowHandle = aWindowHandle; } ===================================== browser/components/places/content/controller.js ===================================== @@ -1609,7 +1609,7 @@ var PlacesControllerDragHelper = { if ( !flavor.startsWith("text/x-moz-place") && (validNodes.length > 1 || dropCount > 1) && - validNodes.some(n => n.uri?.startsWith("javascript:")) + validNodes.some(n => URL.parse(n.uri)?.protocol === "javascript:") ) { return false; } @@ -1686,18 +1686,14 @@ var PlacesControllerDragHelper = { if ( externalDrag && (nodes.length > 1 || dropCount > 1) && - nodes.some(n => n.uri?.startsWith("javascript:")) + nodes.some(n => URL.parse(n.uri)?.protocol === "javascript:") ) { throw new Error("Javascript bookmarklet passed with uris"); } // If a single javascript url is being dropped from the urlbar or an external source, // show the bookmark dialog as a speedbump protection against malicious cases. - if ( - nodes.length == 1 && - externalDrag && - nodes[0].uri?.startsWith("javascript") - ) { + if (nodes.length == 1 && externalDrag) { let uri; try { uri = Services.io.newURI(nodes[0].uri); @@ -1705,7 +1701,7 @@ var PlacesControllerDragHelper = { // Invalid uri, we skip this code and the entry will be discarded later. } - if (uri) { + if (uri?.scheme === "javascript") { let bookmarkGuid = await PlacesUIUtils.showBookmarkDialog( { action: "add", ===================================== browser/components/places/tests/browser/browser_toolbar_drop_bookmarklet.js ===================================== @@ -9,6 +9,7 @@ const sandbox = sinon.createSandbox(); const URL1 = "https://example.com/1/"; const URL2 = "https://example.com/2/"; const BOOKMARKLET_URL = `javascript: (() => {alert('Hello, World!');})();`; +const BOOKMARKLET_URL_MIXED_CASE = `JavaScript: (() => {})();`; let bookmarks; registerCleanupFunction(async function () { @@ -34,12 +35,18 @@ add_task(async function test() { Assert.ok(placesItems, "PlacesToolbarItems should not be null"); /** - * Simulates a drop of a bookmarklet URI onto the bookmarks bar. + * Simulates a drop of a bookmarklet URI onto the bookmarks bar and verifies + * the speedbump dialog appears. * * @param {string} aEffect * The effect to use for the drop operation: move, copy, or link. + * @param {string} aBookmarkletUrl + * The bookmarklet URL to be dropped onto the bookmarks bar. */ - let simulateDragDrop = async function (aEffect) { + let simulateBookmarkletDragDrop = async function ( + aEffect, + aBookmarkletUrl = BOOKMARKLET_URL + ) { info("Simulates drag/drop of a new javascript:URL to the bookmarks"); await withBookmarksDialog( true, @@ -47,7 +54,7 @@ add_task(async function test() { EventUtils.synthesizeDrop( toolbar, placesItems, - [[{ type: "text/x-moz-url", data: BOOKMARKLET_URL }]], + [[{ type: "text/x-moz-url", data: aBookmarkletUrl }]], aEffect, window ); @@ -61,11 +68,21 @@ add_task(async function test() { Assert.equal( location, - BOOKMARKLET_URL, + aBookmarkletUrl.trim().replace(/^javascript/i, "javascript"), "Should have opened the ShowBookmarksDialog with the correct bookmarklet url to be bookmarked" ); } ); + }; + + for (let effect of ["copy", "link"]) { + for (let bookmarkletUrl of [ + BOOKMARKLET_URL, + BOOKMARKLET_URL_MIXED_CASE, + ` javascript: (() => {})();`, + ]) { + await simulateBookmarkletDragDrop(effect, bookmarkletUrl); + } info("Simulates drag/drop of a new URL to the bookmarks"); let spy = sandbox @@ -81,18 +98,13 @@ add_task(async function test() { toolbar, placesItems, [[{ type: "text/x-moz-url", data: URL1 }]], - aEffect, + effect, window ); await promise; Assert.ok(spy.notCalled, "ShowBookmarksDialog on drop not called for url"); sandbox.restore(); - }; - - let effects = ["copy", "link"]; - for (let effect of effects) { - await simulateDragDrop(effect); } info("Move of existing bookmark / bookmarklet on toolbar"); ===================================== browser/components/places/tests/browser/browser_toolbar_drop_multiple_with_bookmarklet.js ===================================== @@ -18,12 +18,12 @@ add_task(async function test() { // matter because we will set its data, effect, and mimeType manually. let placesItems = document.getElementById("PlacesToolbarItems"); Assert.ok(placesItems, "PlacesToolbarItems should not be null"); - let simulateDragDrop = async function (aEffect, aMimeType) { - let urls = [ - "https://example.com/1/", - `javascript: (() => {alert('Hello, World!');})();`, - "https://example.com/2/", - ]; + let simulateDragDrop = async function ( + aEffect, + aMimeType, + aJsUrl = `javascript: (() => {alert('Hello, World!');})();` + ) { + let urls = ["https://example.com/1/", aJsUrl, "https://example.com/2/"]; let data = urls.map(spec => spec + "\n" + spec).join("\n"); @@ -43,8 +43,13 @@ add_task(async function test() { // Simulate a bookmark drop for all of the mime types and effects. let mimeType = ["text/x-moz-url"]; - let effects = ["copy", "link"]; - for (let effect of effects) { - await simulateDragDrop(effect, mimeType); + for (let effect of ["copy", "link"]) { + for (let jsUrl of [ + `javascript: (() => {alert('Hello, World!');})();`, + `JavaScript: (() => {})();`, + ` javascript: (() => {})();`, + ]) { + await simulateDragDrop(effect, mimeType, jsUrl); + } } }); ===================================== dom/base/nsContentUtils.cpp ===================================== @@ -5635,9 +5635,10 @@ void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise, RefPtr<Element> target = &aFrameElement; bool defaultAction = true; if (aCanRaise) { - DispatchEventOnlyToChrome(target->OwnerDoc(), target, - u"framefocusrequested"_ns, CanBubble::eYes, - Cancelable::eYes, &defaultAction); + RefPtr<Document> doc = target->OwnerDoc(); + DispatchEventOnlyToChrome(doc, target, u"framefocusrequested"_ns, + CanBubble::eYes, Cancelable::eYes, + &defaultAction); } if (!defaultAction) { return; ===================================== dom/script/ScriptLoader.cpp ===================================== @@ -240,27 +240,27 @@ ScriptLoader::~ScriptLoader() { FireScriptAvailable(NS_ERROR_ABORT, mParserBlockingRequest); } - for (ScriptLoadRequest* req = mXSLTRequests.getFirst(); req; + for (RefPtr<ScriptLoadRequest> req = mXSLTRequests.getFirst(); req; req = req->getNext()) { FireScriptAvailable(NS_ERROR_ABORT, req); } - for (ScriptLoadRequest* req = mDeferRequests.getFirst(); req; + for (RefPtr<ScriptLoadRequest> req = mDeferRequests.getFirst(); req; req = req->getNext()) { FireScriptAvailable(NS_ERROR_ABORT, req); } - for (ScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req; + for (RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.getFirst(); req; req = req->getNext()) { FireScriptAvailable(NS_ERROR_ABORT, req); } - for (ScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req; + for (RefPtr<ScriptLoadRequest> req = mLoadedAsyncRequests.getFirst(); req; req = req->getNext()) { FireScriptAvailable(NS_ERROR_ABORT, req); } - for (ScriptLoadRequest* req = + for (RefPtr<ScriptLoadRequest> req = mNonAsyncExternalScriptInsertedRequests.getFirst(); req; req = req->getNext()) { FireScriptAvailable(NS_ERROR_ABORT, req); ===================================== dom/security/nsContentSecurityUtils.cpp ===================================== @@ -1176,8 +1176,9 @@ nsString nsContentSecurityUtils::GetIsElementNonceableNonce( // element’s attribute list: if (nsCOMPtr<nsIScriptElement> script = do_QueryInterface(const_cast<Element*>(&aElement))) { - auto containsScriptOrStyle = [](const nsAString& aStr) { - return aStr.LowerCaseFindASCII("<script") != kNotFound || + auto containsLinkScriptOrStyle = [](const nsAString& aStr) { + return aStr.LowerCaseFindASCII("<link") != kNotFound || + aStr.LowerCaseFindASCII("<script") != kNotFound || aStr.LowerCaseFindASCII("<style") != kNotFound; }; @@ -1185,21 +1186,21 @@ nsString nsContentSecurityUtils::GetIsElementNonceableNonce( uint32_t i = 0; while (BorrowedAttrInfo info = aElement.GetAttrInfoAt(i++)) { // Step 2.1. If attribute’s name contains an ASCII case-insensitive match - // for "<script" or "<style", return "Not Nonceable". + // for "<link", <script" or "<style", return "Not Nonceable". const nsAttrName* name = info.mName; if (nsAtom* prefix = name->GetPrefix()) { - if (containsScriptOrStyle(nsDependentAtomString(prefix))) { + if (containsLinkScriptOrStyle(nsDependentAtomString(prefix))) { return EmptyString(); } } - if (containsScriptOrStyle(nsDependentAtomString(name->LocalName()))) { + if (containsLinkScriptOrStyle(nsDependentAtomString(name->LocalName()))) { return EmptyString(); } // Step 2.2. If attribute’s value contains an ASCII case-insensitive match - // for "<script" or "<style", return "Not Nonceable". + // for "<link", "<script" or "<style", return "Not Nonceable". info.mValue->ToString(value); - if (containsScriptOrStyle(value)) { + if (containsLinkScriptOrStyle(value)) { return EmptyString(); } } ===================================== js/src/jit/BaselineIC.cpp ===================================== @@ -1735,10 +1735,15 @@ bool DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, // Transition stub state to megamorphic or generic if warranted. MaybeTransition(cx, frame, stub); + // The array is required to be packed, but may have indexed properties + // if its length exceeds MAX_DENSE_ELEMENTS_COUNT. Don't optimize in + // that case. + bool isIndexed = arr.toObject().as<NativeObject>().isIndexed(); + // Try attaching a call stub. bool handled = false; if (op != JSOp::SpreadEval && op != JSOp::StrictSpreadEval && - stub->state().canAttachStub()) { + stub->state().canAttachStub() && !isIndexed) { // Try CacheIR first: Rooted<ArrayObject*> aobj(cx, &arr.toObject().as<ArrayObject>()); MOZ_ASSERT(IsPackedArray(aobj)); ===================================== js/src/jit/CacheIR.cpp ===================================== @@ -6431,7 +6431,9 @@ ObjOperandId InlinableNativeIRGenerator::emitLoadArgsArray() { MOZ_ASSERT(!hasBoundArguments()); if (flags_.getArgFormat() == CallFlags::Spread) { - return writer.loadSpreadArgs(); + ObjOperandId result = writer.loadSpreadArgs(); + writer.guardArrayIsPacked(result); + return result; } MOZ_ASSERT(flags_.getArgFormat() == CallFlags::FunApplyArray); ===================================== js/src/jit/CacheIRWriter.h ===================================== @@ -523,7 +523,13 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { ArgumentKind kind = ArgumentKind::Arg0; uint32_t argc = 1; CallFlags flags(CallFlags::Spread); - return ObjOperandId(loadArgumentFixedSlot(kind, argc, flags).id()); + ValOperandId argId = loadArgumentFixedSlot(kind, argc, flags); +#ifdef ENABLE_PORTABLE_BASELINE_INTERP + // PBL doesn't support implicit unboxing of objects. + return guardToObject(argId); +#else + return ObjOperandId(argId.id()); +#endif } void callScriptedFunction(ObjOperandId callee, Int32OperandId argc, ===================================== js/src/vm/PortableBaselineInterpret.cpp ===================================== @@ -3915,11 +3915,8 @@ uint64_t ICInterpretOps(uint64_t arg0, uint64_t arg1, ICStub* stub, CACHEOP_CASE(Int32MinMaxArrayResult) { ObjOperandId arrayId = cacheIRReader.objOperandId(); bool isMax = cacheIRReader.readBool(); - // ICs that use this opcode depend on implicit unboxing due to - // type-overload on ObjOperandId when a value is loaded - // directly from an argument slot. We explicitly unbox here. NativeObject* nobj = reinterpret_cast<NativeObject*>( - &READ_VALUE_REG(arrayId.id()).toObject()); + READ_REG(arrayId.id())); uint32_t len = nobj->getDenseInitializedLength(); if (len == 0) { FAIL_IC(); @@ -3948,11 +3945,8 @@ uint64_t ICInterpretOps(uint64_t arg0, uint64_t arg1, ICStub* stub, CACHEOP_CASE(NumberMinMaxArrayResult) { ObjOperandId arrayId = cacheIRReader.objOperandId(); bool isMax = cacheIRReader.readBool(); - // ICs that use this opcode depend on implicit unboxing due to - // type-overload on ObjOperandId when a value is loaded - // directly from an argument slot. We explicitly unbox here. NativeObject* nobj = reinterpret_cast<NativeObject*>( - &READ_VALUE_REG(arrayId.id()).toObject()); + READ_REG(arrayId.id())); uint32_t len = nobj->getDenseInitializedLength(); if (len == 0) { FAIL_IC(); ===================================== toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html ===================================== @@ -27,6 +27,7 @@ enctype="multipart/form-data"
<input type="text" name="textInput2" value="value2"> +<input type="text" name="__proto__" value="regression test for bug 2061470"> <input type="file" name="testFile"> <input type="file" name="emptyFile"> </form> @@ -161,11 +162,19 @@ add_task(async function test_xhr_forms() { } let action = new URL(form.action); let formData = new FormData(form); - let webRequestFD = {}; let updateActionURL = () => { + let webRequestFD = {}; for (let name of formData.keys()) { - webRequestFD[name] = name in uploads ? [uploads[name].fileName] : formData.getAll(name); + if (Object.hasOwn(webRequestFD, name)) { + // Ignore duplicate keys; formData.getAll already read all values. + continue; + } + const value = Object.hasOwn(uploads, name) ? [uploads[name].fileName] : formData.getAll(name); + // Cannot use webRequestFD[name] = value, because for "__proto__" as + // name, that would trigger the Object.prototype.__proto__ setter + // instead of defining a data property. + Object.defineProperty(webRequestFD, name, { value, enumerable: true }); } action.searchParams.set("upload", JSON.stringify(webRequestFD)); action.searchParams.set("enctype", form.enctype); ===================================== toolkit/components/extensions/webrequest/WebRequestUpload.sys.mjs ===================================== @@ -117,22 +117,6 @@ class Headers extends Map { } } -/** - * Creates a new Object with a corresponding property for every - * key-value pair in the given Map. - * - * @param {Map} map - * The map to convert. - * @returns {object} - */ -function mapToObject(map) { - let result = {}; - for (let [key, value] of map) { - result[key] = value; - } - return result; -} - /** * Rewinds the given seekable input stream to its beginning, and catches * any resulting errors. @@ -446,7 +430,7 @@ function createFormData(stream, channel, lenient) { try { let formData = parseFormData(stream, channel, lenient); if (formData) { - return mapToObject(formData); + return Object.fromEntries(formData); } } catch (e) { Cu.reportError(e); View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/614... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/614... You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
participants (1)
-
ma1 (@ma1)