tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit fc50bf838085b780fcef5bb39e13d0ac7475d8a0
parent 741ef7eefc899534751b6ace043c19d3371af204
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Mon,  1 Dec 2025 15:14:54 +0000

Bug 2002481 - Fire async load event for CKEditor 4 wysiwyg editor iframes. r=hsivonen,smaug

Differential Revision: https://phabricator.services.mozilla.com/D274583

Diffstat:
Mdom/base/Document.cpp | 2++
Mdom/base/nsDeprecatedOperationList.h | 1+
Mdom/base/nsGlobalWindowInner.cpp | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdom/base/use_counter_metrics.yaml | 176+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Adom/chrome-webidl/WebCompat.webidl | 17+++++++++++++++++
Mdom/chrome-webidl/moz.build | 1+
Mdom/locales/en-US/chrome/dom/dom.properties | 3+++
Mmodules/libpref/init/StaticPrefList.yaml | 12++++++++++++
Mxpcom/ds/StaticAtoms.py | 1+
9 files changed, 211 insertions(+), 73 deletions(-)

diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp @@ -14423,6 +14423,8 @@ void Document::WarnOnceAbout( // Don't count deprecated operations for about pages since those pages // are almost in our control, and we always need to remove uses there // before we remove the operation itself anyway. + // FIXME: This seems wrong for about:blank. we don't report telemetry for + // internal about: pages anyways (see ShouldIncludeInTelemetry()). if (!IsAboutPage()) { const_cast<Document*>(this)->SetUseCounter( OperationToUseCounter(aOperation)); diff --git a/dom/base/nsDeprecatedOperationList.h b/dom/base/nsDeprecatedOperationList.h @@ -59,3 +59,4 @@ DEPRECATED_OPERATION(SVGDeselectAll) DEPRECATED_OPERATION(IDBObjectStoreCreateIndexLocale) DEPRECATED_OPERATION(FullscreenAttribute) DEPRECATED_OPERATION(XSLTDeprecated) +DEPRECATED_OPERATION(CKEditor4CompatHack) diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp @@ -179,6 +179,7 @@ #include "mozilla/dom/VRDisplayEventBinding.h" #include "mozilla/dom/VREventObserver.h" #include "mozilla/dom/VisualViewport.h" +#include "mozilla/dom/WebCompatBinding.h" #include "mozilla/dom/WebIDLGlobalNameHash.h" #include "mozilla/dom/WebIdentityHandler.h" #include "mozilla/dom/WebTaskSchedulerMainThread.h" @@ -2142,6 +2143,67 @@ void nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.SetParentTarget(GetParentTarget(), true); } +// ckeditor 4 uses UA sniffing to wait for an async load event for its editor +// iframe. This patch makes it work by delaying the sync-about:blank's load +// event on such frames. See bug 2002481 and: +// https://github.com/ckeditor/ckeditor4/blob/c7e59ec199298b6b23f4aa7a7668f18572385bac/plugins/wysiwygarea/plugin.js#L43 +MOZ_CAN_RUN_SCRIPT static bool IsCkEditor4EmptyFrame(Element& aEmbedder) { + if (!StaticPrefs::dom_about_blank_ckeditor_hack_enabled()) { + return false; + } + const nsAttrValue* classes = aEmbedder.GetClasses(); + // We're looking for an <iframe> with a cke_wysiwyg_frame class. That's the + // most likely check to fail so do it first. + if (!classes || + !classes->Contains(nsGkAtoms::cke_wysiwyg_frame, eCaseMatters)) { + return false; + } + if (!aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) { + return false; + } + // Additionally, we expect it to have an empty src attribute. + if (const auto* src = aEmbedder.GetParsedAttr(nsGkAtoms::src); + !src || !src->IsEmptyString()) { + return false; + } + // Deal with the blocklist here before checking for the ckeditor version + // (which is potentially observable via JS getters). + if (aEmbedder.NodePrincipal()->IsURIInPrefList( + "dom.about-blank-ckeditor-hack.disabled-domains")) { + return false; + } + // Finally, we also get the version string off the embedder's global to be + // extra sure. + RefPtr global = aEmbedder.GetOwnerGlobal(); + if (!global || !global->GetGlobalJSObject()) { + return false; + } + AutoJSAPI jsapi; + if (!jsapi.Init(global)) { + return false; + } + CkEditorProperty property; + JS::RootedValue v(jsapi.cx(), JS::ObjectValue(*global->GetGlobalJSObject())); + if (!property.Init(jsapi.cx(), v)) { + JS_ClearPendingException(jsapi.cx()); + return false; + } + if (!StringBeginsWith(property.mCKEDITOR.mVersion, u"4."_ns)) { + return false; + } + aEmbedder.OwnerDoc()->WarnOnceAbout( + DeprecatedOperations::eCKEditor4CompatHack); + return true; +} + +MOZ_CAN_RUN_SCRIPT static bool NeedsAsyncLoadEventForInitialDocument( + nsGlobalWindowInner& aInner, Element& aEmbedder) { + if (auto* doc = aInner.GetExtantDoc(); !doc || !doc->IsInitialDocument()) { + return false; + } + return IsCkEditor4EmptyFrame(aEmbedder); +} + void nsGlobalWindowInner::FireFrameLoadEvent() { // If we're not in a content frame, or are at a BrowsingContext tree boundary, // such as the content-chrome boundary, don't fire the "load" event. @@ -2154,8 +2216,13 @@ void nsGlobalWindowInner::FireFrameLoadEvent() { // // XXX: Bug 1440212 is looking into potentially changing this behaviour to act // more like the remote case when in-process. - RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement(); - if (element) { + if (RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement()) { + if (NeedsAsyncLoadEventForInitialDocument(*this, *element)) { + (new AsyncEventDispatcher(element, eLoad, CanBubble::eNo)) + ->PostDOMEvent(); + return; + } + nsEventStatus status = nsEventStatus_eIgnore; WidgetEvent event(/* aIsTrusted = */ true, eLoad); event.mFlags.mBubbles = false; diff --git a/dom/base/use_counter_metrics.yaml b/dom/base/use_counter_metrics.yaml @@ -108,7 +108,7 @@ use.counter: send_in_pings: - use-counters -# Total of 2420 use counter metrics (excludes denominators). +# Total of 2422 use counter metrics (excludes denominators). # Total of 364 'page' use counters. use.counter.page: svgsvgelement_getelementbyid: @@ -15657,7 +15657,7 @@ use.counter.worker.service: send_in_pings: - use-counters -# Total of 49 'deprecated operations (page)' use counters. +# Total of 50 'deprecated operations (page)' use counters. use.counter.deprecated_ops.page: components: type: counter @@ -16492,7 +16492,24 @@ use.counter.deprecated_ops.page: send_in_pings: - use-counters -# Total of 49 'deprecated operations (document)' use counters. + ckeditor4_compat_hack: + type: counter + description: > + Whether a page used CKEditor4CompatHack. + Compare against `use.counter.top_level_content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + +# Total of 50 'deprecated operations (document)' use counters. use.counter.deprecated_ops.doc: components: type: counter @@ -17327,6 +17344,23 @@ use.counter.deprecated_ops.doc: send_in_pings: - use-counters + ckeditor4_compat_hack: + type: counter + description: > + Whether a document used CKEditor4CompatHack. + Compare against `use.counter.content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + # Total of 704 'CSS (page)' use counters. use.counter.css.page: css_align_items: @@ -24860,6 +24894,40 @@ use.counter.css.page: send_in_pings: - use-counters + css_webkit_perspective: + type: counter + description: > + Whether a page used the CSS property -webkit-perspective. + Compare against `use.counter.top_level_content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + + css_webkit_transform: + type: counter + description: > + Whether a page used the CSS property -webkit-transform. + Compare against `use.counter.top_level_content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + css_columns: type: counter description: > @@ -25914,23 +25982,6 @@ use.counter.css.page: send_in_pings: - use-counters - css_webkit_transform: - type: counter - description: > - Whether a page used the CSS property -webkit-transform. - Compare against `use.counter.top_level_content_documents_destroyed` - to calculate the rate. - bugs: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - notification_emails: - - dom-core@mozilla.com - - emilio@mozilla.com - expires: never - send_in_pings: - - use-counters - css_moz_perspective: type: counter description: > @@ -25948,23 +25999,6 @@ use.counter.css.page: send_in_pings: - use-counters - css_webkit_perspective: - type: counter - description: > - Whether a page used the CSS property -webkit-perspective. - Compare against `use.counter.top_level_content_documents_destroyed` - to calculate the rate. - bugs: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - notification_emails: - - dom-core@mozilla.com - - emilio@mozilla.com - expires: never - send_in_pings: - - use-counters - css_moz_perspective_origin: type: counter description: > @@ -36830,6 +36864,40 @@ use.counter.css.doc: send_in_pings: - use-counters + css_webkit_perspective: + type: counter + description: > + Whether a document used the CSS property -webkit-perspective. + Compare against `use.counter.content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + + css_webkit_transform: + type: counter + description: > + Whether a document used the CSS property -webkit-transform. + Compare against `use.counter.content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + css_columns: type: counter description: > @@ -37884,23 +37952,6 @@ use.counter.css.doc: send_in_pings: - use-counters - css_webkit_transform: - type: counter - description: > - Whether a document used the CSS property -webkit-transform. - Compare against `use.counter.content_documents_destroyed` - to calculate the rate. - bugs: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - notification_emails: - - dom-core@mozilla.com - - emilio@mozilla.com - expires: never - send_in_pings: - - use-counters - css_moz_perspective: type: counter description: > @@ -37918,23 +37969,6 @@ use.counter.css.doc: send_in_pings: - use-counters - css_webkit_perspective: - type: counter - description: > - Whether a document used the CSS property -webkit-perspective. - Compare against `use.counter.content_documents_destroyed` - to calculate the rate. - bugs: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 - notification_emails: - - dom-core@mozilla.com - - emilio@mozilla.com - expires: never - send_in_pings: - - use-counters - css_moz_perspective_origin: type: counter description: > diff --git a/dom/chrome-webidl/WebCompat.webidl b/dom/chrome-webidl/WebCompat.webidl @@ -0,0 +1,17 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// Various non-exposed dictionaries that we need for web compatibility +// interventions. + +dictionary CkEditorVersion { + required DOMString version; +}; + +[GenerateInit] +dictionary CkEditorProperty { + required CkEditorVersion CKEDITOR; +}; diff --git a/dom/chrome-webidl/moz.build b/dom/chrome-webidl/moz.build @@ -96,6 +96,7 @@ WEBIDL_FILES = [ "TreeContentView.webidl", "TreeView.webidl", "UserInteraction.webidl", + "WebCompat.webidl", "WebExtensionContentScript.webidl", "WebExtensionPolicy.webidl", "WidevineCDMManifest.webidl", diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties @@ -483,3 +483,6 @@ WakeLockVideoPlaying=Playing video WakeLockAudioPlaying=Playing audio XSLTDeprecatedWarning=XSLT will be removed from this web browser soon. See https://github.com/mozilla/standards-positions/issues/1287 + +# LOCALIZATION NOTE: Do not translate CKEditor +CKEditor4CompatHackWarning=Altered iframe load event timing for compatibility with CKEditor 4's browser detection diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -5935,6 +5935,18 @@ value: @IS_NIGHTLY_BUILD@ mirror: always +# Whether to enable about:blank async-load workaround for ckeditor. +- name: dom.about-blank-ckeditor-hack.enabled + type: bool + value: true + mirror: always + +# Per-domain blocklist for the previous hack. +- name: dom.about-blank-ckeditor-hack.disabled-domains + type: String + value: "" + mirror: never + #--------------------------------------------------------------------------- # Prefs starting with "editor" #--------------------------------------------------------------------------- diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py @@ -252,6 +252,7 @@ STATIC_ATOMS = [ Atom("circ", "circ"), Atom("circle", "circle"), Atom("cite", "cite"), + Atom("cke_wysiwyg_frame", "cke_wysiwyg_frame"), Atom("_class", "class"), Atom("classid", "classid"), Atom("clear", "clear"),