diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 6f253418ef88..c64c375df561 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1248,7 +1248,7 @@ void Element::NotifyUAWidgetSetupOrChange() { MOZ_ASSERT(IsInComposedDoc()); // Schedule a runnable, ensure the event dispatches before // returning to content script. - // This event cause UA Widget to construct or cause onattributechange callback + // This event cause UA Widget to construct or cause onchange callback // of existing UA Widget to run; dispatching this event twice should not cause // UA Widget to re-init. nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( @@ -1278,6 +1278,14 @@ void Element::NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot) { } MOZ_ASSERT(self->GetShadowRoot()->IsUAWidget()); + // Bail out if the element is being collected by CC + bool hasHadScriptObject = true; + nsIScriptGlobalObject* scriptObject = + ownerDoc->GetScriptHandlingObject(hasHadScriptObject); + if (!scriptObject && hasHadScriptObject) { + return; + } + nsresult rv = nsContentUtils::DispatchChromeEvent( ownerDoc, self, NS_LITERAL_STRING("UAWidgetTeardown"), CanBubble::eYes, Cancelable::eNo); diff --git a/dom/base/Element.h b/dom/base/Element.h index 2606b3b66892..37e4a029da4f 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1218,7 +1218,7 @@ class Element : public FragmentOrElement { void AttachAndSetUAShadowRoot(); // Dispatch an event to UAWidgetsChild, triggering construction - // or onattributechange callback on the existing widget. + // or onchange callback on the existing widget. void NotifyUAWidgetSetupOrChange(); enum class UnattachShadowRoot { diff --git a/toolkit/actors/UAWidgetsChild.jsm b/toolkit/actors/UAWidgetsChild.jsm index 5ce5f1a9405d..8b88bbb35b0b 100644 --- a/toolkit/actors/UAWidgetsChild.jsm +++ b/toolkit/actors/UAWidgetsChild.jsm @@ -37,9 +37,9 @@ class UAWidgetsChild extends ActorChild { this.setupWidget(aElement); return; } - if (typeof widget.wrappedJSObject.onchange == "function") { + if (typeof widget.onchange == "function") { try { - widget.wrappedJSObject.onchange(); + widget.onchange(); } catch (ex) { Cu.reportError(ex); } @@ -90,13 +90,12 @@ class UAWidgetsChild extends ActorChild { } let widget = new sandbox[widgetName](shadowRoot); + if (!isSystemPrincipal) { + widget = widget.wrappedJSObject; + } this.widgets.set(aElement, widget); try { - if (!isSystemPrincipal) { - widget.wrappedJSObject.onsetup(); - } else { - widget.onsetup(); - } + widget.onsetup(); } catch (ex) { Cu.reportError(ex); } @@ -107,9 +106,9 @@ class UAWidgetsChild extends ActorChild { if (!widget) { return; } - if (typeof widget.wrappedJSObject.destructor == "function") { + if (typeof widget.destructor == "function") { try { - widget.wrappedJSObject.destructor(); + widget.destructor(); } catch (ex) { Cu.reportError(ex); } diff --git a/toolkit/content/widgets/docs/ua_widget.rst b/toolkit/content/widgets/docs/ua_widget.rst index ccfaf939241b..847e6714f742 100644 --- a/toolkit/content/widgets/docs/ua_widget.rst +++ b/toolkit/content/widgets/docs/ua_widget.rst @@ -19,6 +19,8 @@ The ``onsetup`` method is called right after the instance is constructed. The ca When the element is removed from the tree, ``UAWidgetTeardown`` is dispatched so UAWidgetsChild can destroy the widget, if it exists. If so, the UAWidgetsChild calls the ``destructor()`` method on the widget, causing the widget to destruct itself. +Counter-intuitively, elements are not considered "removed from the tree" when the document is unloaded. This is considered safe as anything the widget touches should be reset or cleaned up when the document unloads. Please do not violate the assumption by having any browser state toggled by the destructor. + When a UA Widget initializes, it should create its own DOM inside the passed UA Widget Shadow Root, including the ```` element necessary to load the stylesheet, add event listeners, etc. When destroyed (i.e. the destructor method is called), it should do the opposite. **Specialization**: for video controls, we do not want to do the work if the control is not needed (i.e. when the ``