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:
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"),