Created attachment 440054 [details] iframe unload notification test case In the attached test case an iframe sends a notification to its parent frame via a) a MessagePort and b) `window.parent.postMessage` when it is unloaded. In Firefox and Chrome the messages sent via both methods are received in the parent frame. In Safari only the `window.parent.postMessage` events are received. The message sent via the MessagePort is not. Steps to reproduce: 1. Load attached test case and click "Remove iframe" button Console output in Chrome: ``` iframe received "pagehide" event iframe received "visibilitychange" event iframe received "unload" event MessagePort in parent frame received message {trigger: 'pagehide'} Parent frame received "message" event {trigger: 'pagehide'} Parent frame received "message" event {trigger: 'visibilitychange'} Parent frame received "message" event {trigger: 'unload'} MessagePort in parent frame received message {trigger: 'visibilitychange'} MessagePort in parent frame received message {trigger: 'unload'} ``` Firefox has the same output but with a slightly different order. Console output in Safari 15: ``` iframe received "pagehide" event iframe received "visibilitychange" event iframe received "unload" event Parent frame received "message" event – {trigger: "pagehide"} Parent frame received "message" event – {trigger: "visibilitychange"} Parent frame received "message" event – {trigger: "unload"} ```
In case it is relevant, the results seem to be the same when the parent and child frames are cross-origin.
<rdar://problem/84095357>
MessagePort::close() gets called *before* the calls to MessagePort::postMessage(), likely as a result of MessagePort::stop() since MessagePort is an ActiveDOMObject. As a result, the calls to postMessage() are ignored.
(In reply to Chris Dumez from comment #3) > MessagePort::close() gets called *before* the calls to > MessagePort::postMessage(), likely as a result of MessagePort::stop() since > MessagePort is an ActiveDOMObject. As a result, the calls to postMessage() > are ignored. 1 0x62998b0c4 WebCore::MessagePort::close() 2 0x629990165 WebCore::MessagePort::stop() 3 0x629a3a13a auto WebCore::ScriptExecutionContext::stopActiveDOMObjects()::$_4::operator()<WebCore::ActiveDOMObject>(WebCore::ActiveDOMObject&) const 4 0x629a3a0d1 WTF::Detail::CallableWrapper<WebCore::ScriptExecutionContext::stopActiveDOMObjects()::$_4, WebCore::ScriptExecutionContext::ShouldContinue, WebCore::ActiveDOMObject&>::call(WebCore::ActiveDOMObject&) 5 0x629a23c37 WTF::Function<WebCore::ScriptExecutionContext::ShouldContinue (WebCore::ActiveDOMObject&)>::operator()(WebCore::ActiveDOMObject&) const 6 0x629a23aaa WebCore::ScriptExecutionContext::forEachActiveDOMObject(WTF::Function<WebCore::ScriptExecutionContext::ShouldContinue (WebCore::ActiveDOMObject&)> const&) const 7 0x629a23fa4 WebCore::ScriptExecutionContext::stopActiveDOMObjects() 8 0x629833765 WebCore::Document::stopActiveDOMObjects() 9 0x62a3f64a0 WebCore::FrameLoader::frameDetached() 10 0x629d1d337 WebCore::HTMLFrameOwnerElement::disconnectContentFrame() 11 0x6297ebce8 WebCore::disconnectSubframes(WebCore::ContainerNode&, WebCore::SubframeDisconnectPolicy) 12 0x6297e7112 WebCore::disconnectSubframesIfNeeded(WebCore::ContainerNode&, WebCore::SubframeDisconnectPolicy) 13 0x6297e72a5 WebCore::ContainerNode::removeNodeWithScriptAssertion(WebCore::Node&, WebCore::ContainerNode::ChildChange::Source) 14 0x6297e3862 WebCore::ContainerNode::removeChild(WebCore::Node&) 15 0x6299bf8b6 WebCore::Node::remove() 16 0x62734baf6 WebCore::jsElementPrototypeFunction_removeBody(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSElement*)::'lambda'()::operator()() const
(In reply to Chris Dumez from comment #4) > (In reply to Chris Dumez from comment #3) > > MessagePort::close() gets called *before* the calls to > > MessagePort::postMessage(), likely as a result of MessagePort::stop() since > > MessagePort is an ActiveDOMObject. As a result, the calls to postMessage() > > are ignored. > > 1 0x62998b0c4 WebCore::MessagePort::close() > 2 0x629990165 WebCore::MessagePort::stop() > 3 0x629a3a13a auto > WebCore::ScriptExecutionContext::stopActiveDOMObjects()::$_4:: > operator()<WebCore::ActiveDOMObject>(WebCore::ActiveDOMObject&) const > 4 0x629a3a0d1 > WTF::Detail::CallableWrapper<WebCore::ScriptExecutionContext:: > stopActiveDOMObjects()::$_4, > WebCore::ScriptExecutionContext::ShouldContinue, > WebCore::ActiveDOMObject&>::call(WebCore::ActiveDOMObject&) > 5 0x629a23c37 > WTF::Function<WebCore::ScriptExecutionContext::ShouldContinue > (WebCore::ActiveDOMObject&)>::operator()(WebCore::ActiveDOMObject&) const > 6 0x629a23aaa > WebCore::ScriptExecutionContext::forEachActiveDOMObject(WTF:: > Function<WebCore::ScriptExecutionContext::ShouldContinue > (WebCore::ActiveDOMObject&)> const&) const > 7 0x629a23fa4 WebCore::ScriptExecutionContext::stopActiveDOMObjects() > 8 0x629833765 WebCore::Document::stopActiveDOMObjects() > 9 0x62a3f64a0 WebCore::FrameLoader::frameDetached() > 10 0x629d1d337 WebCore::HTMLFrameOwnerElement::disconnectContentFrame() > 11 0x6297ebce8 WebCore::disconnectSubframes(WebCore::ContainerNode&, > WebCore::SubframeDisconnectPolicy) > 12 0x6297e7112 > WebCore::disconnectSubframesIfNeeded(WebCore::ContainerNode&, > WebCore::SubframeDisconnectPolicy) > 13 0x6297e72a5 > WebCore::ContainerNode::removeNodeWithScriptAssertion(WebCore::Node&, > WebCore::ContainerNode::ChildChange::Source) > 14 0x6297e3862 WebCore::ContainerNode::removeChild(WebCore::Node&) > 15 0x6299bf8b6 WebCore::Node::remove() > 16 0x62734baf6 > WebCore::jsElementPrototypeFunction_removeBody(JSC::JSGlobalObject*, > JSC::CallFrame*, WebCore::JSElement*)::'lambda'()::operator()() const And the call trace from postMessage() is: 1 0x58198b2d2 WebCore::MessagePort::postMessage(JSC::JSGlobalObject&, JSC::JSValue, WebCore::StructuredSerializeOptions&&) 2 0x57f9264c9 WebCore::jsMessagePortPrototypeFunction_postMessage2Body(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*)::'lambda'()::operator()() const 3 0x57f9263c1 JSC::JSValue WebCore::toJS<WebCore::IDLUndefined, WebCore::jsMessagePortPrototypeFunction_postMessage2Body(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*)::'lambda'()>(JSC::JSGlobalObject&, JSC::ThrowScope&, WebCore::jsMessagePortPrototypeFunction_postMessage2Body(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*)::'lambda'()&&) 4 0x57f925efc WebCore::jsMessagePortPrototypeFunction_postMessage2Body(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*) 5 0x57f9257ab WebCore::jsMessagePortPrototypeFunction_postMessageOverloadDispatcher(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*) 6 0x57f92568d long long WebCore::IDLOperation<WebCore::JSMessagePort>::call<&(WebCore::jsMessagePortPrototypeFunction_postMessageOverloadDispatcher(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSMessagePort*)), (WebCore::CastedThisErrorBehavior)0>(JSC::JSGlobalObject&, JSC::CallFrame&, char const*) 7 0x57f925374 WebCore::jsMessagePortPrototypeFunction_postMessage(JSC::JSGlobalObject*, JSC::CallFrame*) 8 0x3df9658011d8 9 0x56d11f990 llint_entry 10 0x56d0fbda0 vmEntryToJavaScript 11 0x56dfc81b5 JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) 12 0x56dfc8973 JSC::Interpreter::executeCall(JSC::JSGlobalObject*, JSC::JSObject*, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) 13 0x56e38079a JSC::call(JSC::JSGlobalObject*, JSC::JSValue, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) 14 0x56e38087d JSC::call(JSC::JSGlobalObject*, JSC::JSValue, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&, WTF::NakedPtr<JSC::Exception>&) 15 0x56e380b3d JSC::profiledCall(JSC::JSGlobalObject*, JSC::ProfilingReason, JSC::JSValue, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&, WTF::NakedPtr<JSC::Exception>&) 16 0x5811a464c WebCore::JSExecState::profiledCall(JSC::JSGlobalObject*, JSC::ProfilingReason, JSC::JSValue, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&, WTF::NakedPtr<JSC::Exception>&) 17 0x5811c3cb9 WebCore::JSEventListener::handleEvent(WebCore::ScriptExecutionContext&, WebCore::Event&) 18 0x58194e2fe WebCore::EventTarget::innerInvokeEventListeners(WebCore::Event&, WTF::Vector<WTF::RefPtr<WebCore::RegisteredEventListener, WTF::RawPtrTraits<WebCore::RegisteredEventListener>, WTF::DefaultRefDerefTraits<WebCore::RegisteredEventListener> >, 1ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>, WebCore::EventTarget::EventInvokePhase) 19 0x58194dd86 WebCore::EventTarget::fireEventListeners(WebCore::Event&, WebCore::EventTarget::EventInvokePhase) 20 0x58257d28f WebCore::DOMWindow::dispatchEvent(WebCore::Event&, WebCore::EventTarget*) 21 0x5823e297b WebCore::FrameLoader::dispatchUnloadEvents(WebCore::UnloadEventPolicy) 22 0x5823e270a WebCore::FrameLoader::stopLoading(WebCore::UnloadEventPolicy) 23 0x5823e2f59 WebCore::FrameLoader::closeURL() 24 0x5823f5c79 WebCore::FrameLoader::detachFromParent() 25 0x5823f64a9 WebCore::FrameLoader::frameDetached() 26 0x581d1d337 WebCore::HTMLFrameOwnerElement::disconnectContentFrame()
Created attachment 440964 [details] WIP Patch
Created attachment 441083 [details] Patch
Committed r284101 (242929@main): <https://commits.webkit.org/242929@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 441083 [details].