Open Bug 1894718 Opened 5 months ago Updated 1 month ago

Try to use the asynchronous clipboard API for the clipboard suggest results

Categories

(Firefox :: Address Bar, task, P2)

task

Tracking

()

People

(Reporter: mak, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: perf, Whiteboard: [sng])

Attachments

(1 file, 1 obsolete file)

In Bug 1894614 we are disabling the clipboard feature due to performance issues, see the profile at https://share.firefox.dev/4dmjmZV

It may be due to a third party application blocking access to the clipboard, see https://searchfox.org/mozilla-central/rev/f1532761de0b60337e42c6c3f525288a523dabef/widget/windows/nsClipboard.cpp#615. Though I suspect that comment refers to getClipboardData returning an error, rather than hanging. Anyway I can reproduce the blocked clipboard through a third party software that runs a nested loop after accessing the ole clipboard, when returning to Firefox it completely hangs while trying to access the clipboard.

We should check if we can use the new asynchronous clipboard API, see https://searchfox.org/mozilla-central/rev/f1532761de0b60337e42c6c3f525288a523dabef/widget/nsIClipboard.idl#168-188

We could start the asynchronous lookup in the isActive() and then in startQuery race with a timeout of 100ms before returning the result.

Blocks: 1866710

Emilio correctly points out that it's now also available navigator.clipboard.readText()

Assignee: nobody → klubana
Status: NEW → ASSIGNED

The async clipboard API is still locking up for me, when we actually try to get the data. I didn't debug the code yet to see exactly where we're blocked, but I suppose we still do some request to ole on the main thread?
This is my hack compiled in VS https://gist.github.com/mak77/36d67961c2f5fb7c219b0fbe42079186
It's likely there's more here that escapes my understanding.
Edgar, can you help me understand if I'm doing wrong assumptions here, or effectively there's still clipboard code on the main thread even with async clipboard?
The problem at hand is that apparently a third party app can lock up Firefox when it's accessing the clipboard.

Flags: needinfo?(echen)

(In reply to Marco Bonardo [:mak] from comment #3)

Edgar, can you help me understand if I'm doing wrong assumptions here, or effectively there's still clipboard code on the main thread even with async clipboard?

Yes, the ole request is still on main thread.
In parent process, only Linux supports accessing clipboard asynchronously, not sure if other platform APIs support that.

Flags: needinfo?(echen)

(In reply to Edgar Chen [:edgar] from comment #4)

Yes, the ole request is still on main thread.
In parent process, only Linux supports accessing clipboard asynchronously, not sure if other platform APIs support that.

From a quick glance, OLE clipboard access requires single-Threaded Apartment to be set on the thread when spawning it. But it should work then.
Is there any alternative for us to stop having these main-thread lock ups? Is your team investigating ways to make it effectively async?
Sorry for nagging, but we're looking for a path forward.

Flags: needinfo?(echen)

Currently, when the OLE clipboard is locked by another application, we retry accessing it synchronously. I think one thing we could try is to retry asynchronously for async API, I filed bug 1899273.

Oh, Wait, no, the OLEGetClipboard doesn't throw an error, but stuck in trying to get the actual data (from the IDataObject provided by source application?), then I have no idea at the moment.

Flags: needinfo?(echen)

:cmartin, do you know if there is any alternative to avoid getting stuck in OLEGetClipboard() and how big is the effort to implement accessing OLE clipboard off the main thread?

Flags: needinfo?(cmartin)

Hi Edgar,

I am away on PTO for the next couple of weeks, but perhaps Yannis will be able to help? He is generally very knowledgeable about COM.

Flags: needinfo?(cmartin) → needinfo?(yjuglaret)
Attached file Hung thread call stack (obsolete) —
[deleted]

I'm not the COM expert Yannis is so I would defer to him but I did take a look at this and I don't think this is a COM issue per se -- it would seem to be OLE behaving as expected and that this is just what happens in OLE when your data source isn't well behaved. Unless there is an easier solution, one moderate difficult way would be to put this in a utility process and communicate the clipboard contents back via async IPDL. Then it would be easy to implement a timeout. This kind of system API issue is what utility processes are for.

The gist in comment 3 makes reproduction easy (thanks, :mak!) -- I put the binary up here. Just run it alongside Fx with the config settings that were unset in bug 1894614: browser.urlbar.clipboard.featureGate = true and clipboard.featureGate = true, then type in the URL bar and it will eventually trigger.

The stack of the hung main thread is below. I could be missing it but I don't see any threads that look like they are handing the (sync) STA COM RPC that this thread is waiting for. So the target is probably in a remote process. This isn't giving us much to work with.

The call is happening during the keyDown event handler. There is a tiny chance this is related.

(Longer term, I wonder if we still need OLE formats or if we could switch to the simpler non-OLE clipboard API, which may not have these issues.)

[1] Eclipse running into this problem in SWT.


Stack:

 	win32u.dll!NtUserMsgWaitForMultipleObjectsEx()	Unknown
 	combase.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 2105	C++
 	combase.dll!ModalLoop(CSyncClientCall * pClientCall) Line 169	C++
 	[Inline Frame] combase.dll!ThreadSendReceive(tagRPCOLEMESSAGE *) Line 7272	C++
 	[Inline Frame] combase.dll!CSyncClientCall::SwitchAptAndDispatchCall(tagRPCOLEMESSAGE * pMessage) Line 5748	C++
 	combase.dll!CSyncClientCall::SendReceive2(tagRPCOLEMESSAGE * pMessage, unsigned long * pstatus) Line 5310	C++
 	[Inline Frame] combase.dll!SyncClientCallRetryContext::SendReceiveWithRetry(tagRPCOLEMESSAGE *) Line 1493	C++
 	[Inline Frame] combase.dll!CSyncClientCall::SendReceiveInRetryContext(SyncClientCallRetryContext *) Line 581	C++
 	combase.dll!ClassicSTAThreadSendReceive(CSyncClientCall * pClientCall, tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus) Line 563	C++
 	combase.dll!CSyncClientCall::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 796	C++
 	[Inline Frame] combase.dll!CClientChannel::SendReceive(tagRPCOLEMESSAGE *) Line 673	C++
 	combase.dll!NdrExtpProxySendReceive(void * pThis, _MIDL_STUB_MESSAGE * pStubMsg) Line 1899	C++
 	rpcrt4.dll!NdrpClientCall3()	Unknown
 	combase.dll!ObjectStublessClient(void * ParamAddress, __int64 * FloatRegisters, long Method) Line 366	C++
 	combase.dll!ObjectStubless() Line 176	Unknown
 	combase.dll!CStdMarshal::RemoteAddRef(tagIPIDEntry * pIPIDEntry, OXIDEntry * pOXIDEntry, unsigned long cStrongNeed, unsigned long cSecureNeed, int fGiveToCaller) Line 8445	C++
 	[Inline Frame] combase.dll!CStdMarshal::GetNeededRefs(tagSTDOBJREF *) Line 3653	C++
 	combase.dll!CStdMarshal::ConnectCliIPIDEntry(tagSTDOBJREF * pStd, OXIDEntry * pOXIDEntry, tagIPIDEntry * pEntry) Line 3337	C++
 	combase.dll!CStdMarshal::MakeCliIPIDEntry(const _GUID & riid, tagSTDOBJREF * pStd, OXIDEntry * pOXIDEntry, tagIPIDEntry * * ppEntry) Line 3107	C++
 	combase.dll!CStdMarshal::UnmarshalIPID(const _GUID & riid, tagSTDOBJREF * pStd, OXIDEntry * pOXIDEntry, void * * ppv) Line 2619	C++
 	combase.dll!CStdMarshal::UnmarshalObjRef(tagOBJREF & objref, void * * ppv, const void *) Line 2478	C++
 	combase.dll!UnmarshalSwitch(void * pv) Line 2114	C++
 	combase.dll!UnmarshalObjRef(tagOBJREF & objref, EffectiveUnmarshalingPolicy policy, void * * ppv, int fBypassActLock, CBaseCall * callMarshalingContext, CStdMarshal * * ppStdMarshal, const void * callerAddressForDiagnostics) Line 2259	C++
 	combase.dll!InternalGetWindowPropInterface2(HWND__ * hWnd, unsigned __int64 dwCookie, int fCallStrongNamedProcesses, const _GUID & iid, void * * ppv, int * pfLocal) Line 285	C++
 	ole32.dll!UnmarshalFromEndpointProperty(HWND__ * hWnd, int fDragDrop, int fCallStrongNamedProcesses, IUnknown * * ppUnk, int * pfLocal) Line 432	C++
 	ole32.dll!GetInterfaceFromWindowProp(HWND__ * hWnd, const _GUID & fCallStrongNamedProcesses, int ppunk, IUnknown * *) Line 497	C++
 	ole32.dll!CClipDataObject::CacheDataPointer() Line 223	C++
 	ole32.dll!CClipDataObject::GetData(tagFORMATETC * pformatetc, tagSTGMEDIUM * pmedium) Line 1058	C++
>	xul.dll!RepeatedlyTryGetData::<lambda>() Line 626	C++
 	xul.dll!RepeatedlyTry<`lambda at C:\mozilla-source\mozilla-unified\widget\windows\nsClipboard.cpp:626:7',std::_Binder<std::_Unforced,void (&)(long, const nsTString<char> &),const std::_Ph<1> &,nsTLiteralString<char>>>(RepeatedlyTryGetData::mozilla::a11y::LocalAccessible * <lambda>(mozilla::dom::Element *, mozilla::a11y::LocalAccessible *) aFunction, std::_Binder<std::_Unforced,void (&)(long, const nsTString<char> &),const std::_Ph<1> &,nsTLiteralString<char>> aLogFunction) Line 456	C++
 	xul.dll!RepeatedlyTryGetData(IDataObject & aDataObject, tagFORMATETC * pFE, tagSTGMEDIUM * pSTM) Line 625	C++
 	xul.dll!nsClipboard::FillSTGMedium(IDataObject * aDataObject, unsigned int aFormat, tagFORMATETC * pFE, tagSTGMEDIUM * pSTM, unsigned long aTymed) Line 644	C++
 	xul.dll!nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, unsigned int aIndex, unsigned int aFormat, const char * aMIMEImageFormat, void * * aData, unsigned int * aLen) Line 675	C++
 	xul.dll!nsClipboard::GetDataFromDataObject(IDataObject * aDataObject, unsigned int anIndex, nsIWidget * aWindow, nsITransferable * aTransferable) Line 927	C++
 	xul.dll!nsClipboard::GetNativeClipboardData(nsITransferable * aTransferable, int aWhichClipboard) Line 1341	C++
 	xul.dll!nsBaseClipboard::GetData(nsITransferable * aTransferable, int aWhichClipboard, mozilla::dom::WindowContext * aWindowContext) Line 463	C++
 	xul.dll!XPTC__InvokebyIndex()	Unknown
 	xul.dll!NS_InvokeByIndex(nsISupports * that, unsigned int methodIndex, unsigned int paramCount, nsXPTCVariant * params) Line 57	C++
 	xul.dll!CallMethodHelper::Invoke() Line 1620	C++
 	xul.dll!CallMethodHelper::Call() Line 1174	C++
 	xul.dll!XPCWrappedNative::CallMethod(XPCCallContext & ccx, XPCWrappedNative::CallMode mode) Line 1120	C++
 	xul.dll!XPC_WN_CallMethod(JSContext * cx, unsigned int argc, JS::Value * vp) Line 966	C++
 	xul.dll!CallJSNative(JSContext * cx, bool(*)(JSContext *, unsigned int, JS::Value *) native, js::CallReason reason, const JS::CallArgs & args) Line 480	C++
 	xul.dll!js::InternalCallOrConstruct(JSContext * cx, const JS::CallArgs & args, js::MaybeConstruct construct, js::CallReason reason) Line 574	C++
 	xul.dll!InternalCall(JSContext * cx, const js::AnyInvokeArgs & args, js::CallReason reason) Line 641	C++
 	xul.dll!js::CallFromStack(JSContext * cx, const JS::CallArgs & args, js::CallReason reason) Line 646	C++
 	xul.dll!js::Interpret(JSContext * cx, js::RunState & state) Line 3071	C++
 	xul.dll!MaybeEnterInterpreterTrampoline(JSContext * cx, js::RunState & state) Line 394	C++
 	xul.dll!js::RunScript(JSContext * cx, js::RunState & state) Line 452	C++
 	xul.dll!js::InternalCallOrConstruct(JSContext * cx, const JS::CallArgs & args, js::MaybeConstruct construct, js::CallReason reason) Line 606	C++
 	xul.dll!InternalCall(JSContext * cx, const js::AnyInvokeArgs & args, js::CallReason reason) Line 641	C++
 	xul.dll!js::CallFromStack(JSContext * cx, const JS::CallArgs & args, js::CallReason reason) Line 646	C++
 	xul.dll!js::jit::DoCallFallback(JSContext * cx, js::jit::BaselineFrame * frame, js::jit::ICFallbackStub * stub, unsigned int argc, JS::Value * vp, JS::MutableHandle<JS::Value> res) Line 1663	C++
 	000003e7e20e7f0d()	Unknown
 	000003e7e21113c4()	Unknown
 	000003e7e2115b16()	Unknown
 	000003e7e23d5622()	Unknown
 	000003e7e2489595()	Unknown
 	000003e7e22a4bc6()	Unknown
 	000003e7e248bf0a()	Unknown
 	000003e7e2115b16()	Unknown
 	000003e7e21bb226()	Unknown
 	000003e7e20e1973()	Unknown
 	xul.dll!EnterJit(JSContext * cx, js::RunState & state, unsigned char * code) Line 132	C++
 	xul.dll!js::jit::MaybeEnterJit(JSContext * cx, js::RunState & state) Line 261	C++
 	xul.dll!js::RunScript(JSContext * cx, js::RunState & state) Line 442	C++
 	xul.dll!js::InternalCallOrConstruct(JSContext * cx, const JS::CallArgs & args, js::MaybeConstruct construct, js::CallReason reason) Line 606	C++
 	xul.dll!InternalCall(JSContext * cx, const js::AnyInvokeArgs & args, js::CallReason reason) Line 641	C++
 	xul.dll!js::Call(JSContext * cx, JS::Handle<JS::Value> fval, JS::Handle<JS::Value> thisv, const js::AnyInvokeArgs & args, JS::MutableHandle<JS::Value> rval, js::CallReason reason) Line 673	C++
 	xul.dll!js::CallSelfHostedFunction(JSContext * cx, JS::Handle<js::PropertyName *> name, JS::Handle<JS::Value> thisv, const js::AnyInvokeArgs & args, JS::MutableHandle<JS::Value> rval) Line 1592	C++
 	xul.dll!AsyncFunctionResume(JSContext * cx, JS::Handle<js::AsyncFunctionGeneratorObject *> generator, ResumeKind kind, JS::Handle<JS::Value> valueOrReason) Line 151	C++
 	xul.dll!js::AsyncFunctionAwaitedFulfilled(JSContext * cx, JS::Handle<js::AsyncFunctionGeneratorObject *> generator, JS::Handle<JS::Value> value) Line 192	C++
 	xul.dll!AsyncFunctionPromiseReactionJob(JSContext * cx, JS::Handle<PromiseReactionRecord *> reaction) Line 2113	C++
 	xul.dll!PromiseReactionJob(JSContext * cx, unsigned int argc, JS::Value * vp) Line 2176	C++
 	xul.dll!CallJSNative(JSContext * cx, bool(*)(JSContext *, unsigned int, JS::Value *) native, js::CallReason reason, const JS::CallArgs & args) Line 480	C++
 	xul.dll!js::InternalCallOrConstruct(JSContext * cx, const JS::CallArgs & args, js::MaybeConstruct construct, js::CallReason reason) Line 574	C++
 	xul.dll!InternalCall(JSContext * cx, const js::AnyInvokeArgs & args, js::CallReason reason) Line 641	C++
 	xul.dll!js::Call(JSContext * cx, JS::Handle<JS::Value> fval, JS::Handle<JS::Value> thisv, const js::AnyInvokeArgs & args, JS::MutableHandle<JS::Value> rval, js::CallReason reason) Line 673	C++
 	xul.dll!JS::Call(JSContext * cx, JS::Handle<JS::Value> thisv, JS::Handle<JS::Value> fval, const JS::HandleValueArray & args, JS::MutableHandle<JS::Value> rval) Line 119	C++
 	xul.dll!mozilla::dom::PromiseJobCallback::Call(mozilla::dom::BindingCallContext & cx, JS::Handle<JS::Value> aThisVal, mozilla::ErrorResult & aRv) Line 83	C++
 	xul.dll!mozilla::dom::PromiseJobCallback::Call(mozilla::ErrorResult & aRv, const char * aExecutionReason, mozilla::dom::CallbackObject::ExceptionHandling aExceptionHandling, JS::Realm * aRealm) Line 198	C++
 	xul.dll!mozilla::dom::PromiseJobCallback::Call(const char * aExecutionReason) Line 211	C++
 	xul.dll!mozilla::PromiseJobRunnable::Run(mozilla::AutoSlowOperation & aAso) Line 210	C++
 	xul.dll!mozilla::CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) Line 712	C++
 	xul.dll!mozilla::CycleCollectedJSContext::LeaveMicroTask() Line 243	C++
 	xul.dll!mozilla::nsAutoMicroTask::~nsAutoMicroTask() Line 392	C++
 	xul.dll!mozilla::EventListenerManager::HandleEventSingleListener(mozilla::EventListenerManager::Listener * aListener, nsAtom * aTypeAtom, mozilla::WidgetEvent * aEvent, mozilla::dom::Event * aDOMEvent, mozilla::dom::EventTarget * aCurrentTarget, bool aItemInShadowTree) Line 1315	C++
 	xul.dll!mozilla::EventListenerManager::HandleEventWithListenerArray(mozilla::EventListenerManager::ListenerArray * aListeners, nsAtom * aTypeAtom, mozilla::EventMessage aEventMessage, nsPresContext * aPresContext, mozilla::WidgetEvent * aEvent, mozilla::dom::Event * * aDOMEvent, mozilla::dom::EventTarget * aCurrentTarget, bool aItemInShadowTree) Line 1630	C++
 	xul.dll!mozilla::EventListenerManager::HandleEventInternal(nsPresContext * aPresContext, mozilla::WidgetEvent * aEvent, mozilla::dom::Event * * aDOMEvent, mozilla::dom::EventTarget * aCurrentTarget, nsEventStatus * aEventStatus, bool aItemInShadowTree) Line 1527	C++
 	xul.dll!mozilla::EventListenerManager::HandleEvent(nsPresContext * aPresContext, mozilla::WidgetEvent * aEvent, mozilla::dom::Event * * aDOMEvent, mozilla::dom::EventTarget * aCurrentTarget, nsEventStatus * aEventStatus, bool aItemInShadowTree) Line 467	C++
 	xul.dll!mozilla::EventTargetChainItem::HandleEvent(mozilla::EventChainPostVisitor & aVisitor, mozilla::ELMCreationDetector & aCd) Line 368	C++
 	xul.dll!mozilla::EventTargetChainItem::HandleEventTargetChain(nsTArray<mozilla::EventTargetChainItem> & aChain, mozilla::EventChainPostVisitor & aVisitor, mozilla::EventDispatchingCallback * aCallback, mozilla::ELMCreationDetector & aCd) Line 609	C++
 	xul.dll!mozilla::EventTargetChainItem::HandleEventTargetChain(nsTArray<mozilla::EventTargetChainItem> & aChain, mozilla::EventChainPostVisitor & aVisitor, mozilla::EventDispatchingCallback * aCallback, mozilla::ELMCreationDetector & aCd) Line 688	C++
 	xul.dll!mozilla::EventDispatcher::Dispatch(mozilla::dom::EventTarget * aTarget, nsPresContext * aPresContext, mozilla::WidgetEvent * aEvent, mozilla::dom::Event * aDOMEvent, nsEventStatus * aEventStatus, mozilla::EventDispatchingCallback * aCallback, nsTArray<mozilla::dom::EventTarget *> * aTargets) Line 1224	C++
 	xul.dll!mozilla::PresShell::EventHandler::DispatchEventToDOM(mozilla::WidgetEvent * aEvent, nsEventStatus * aEventStatus, nsPresShellEventCB * aEventCB) Line 8909	C++
 	xul.dll!mozilla::PresShell::EventHandler::DispatchEvent(mozilla::EventStateManager * aEventStateManager, mozilla::WidgetEvent * aEvent, bool aTouchIsNew, nsEventStatus * aEventStatus, nsIContent * aOverrideClickTarget) Line 8487	C++
 	xul.dll!mozilla::PresShell::EventHandler::HandleEventWithCurrentEventInfo(mozilla::WidgetEvent * aEvent, nsEventStatus * aEventStatus, bool aIsHandlingNativeEvent, nsIContent * aOverrideClickTarget) Line 8416	C++
 	xul.dll!mozilla::PresShell::EventHandler::HandleEventAtFocusedContent(mozilla::WidgetGUIEvent * aGUIEvent, nsEventStatus * aEventStatus) Line 8161	C++
 	xul.dll!mozilla::PresShell::EventHandler::HandleEvent(nsIFrame * aFrameForPresShell, mozilla::WidgetGUIEvent * aGUIEvent, bool aDontRetargetEvents, nsEventStatus * aEventStatus) Line 7075	C++
 	xul.dll!mozilla::PresShell::HandleEvent(nsIFrame * aFrameForPresShell, mozilla::WidgetGUIEvent * aGUIEvent, bool aDontRetargetEvents, nsEventStatus * aEventStatus) Line 6993	C++
 	xul.dll!nsViewManager::DispatchEvent(mozilla::WidgetGUIEvent * aEvent, nsView * aView, nsEventStatus * aStatus) Line 654	C++
 	xul.dll!nsView::HandleEvent(mozilla::WidgetGUIEvent * aEvent, bool aUseAttachedEvents) Line 1114	C++
 	xul.dll!nsWindow::DispatchEvent(mozilla::WidgetGUIEvent * event, nsEventStatus & aStatus) Line 4026	C++
 	xul.dll!nsBaseWidget::ProcessUntransformedAPZEvent(mozilla::WidgetInputEvent * aEvent, const mozilla::layers::APZEventResult & aApzResult) Line 1101	C++
 	xul.dll!nsBaseWidget::DispatchInputEvent(mozilla::WidgetInputEvent * aEvent) Line 1288	C++
 	xul.dll!mozilla::widget::TextEventDispatcher::DispatchInputEvent(nsIWidget * aWidget, mozilla::WidgetInputEvent & aEvent, nsEventStatus & aStatus) Line 291	C++
 	xul.dll!mozilla::widget::TextEventDispatcher::DispatchKeyboardEventInternal(mozilla::EventMessage aMessage, const mozilla::WidgetKeyboardEvent & aKeyboardEvent, nsEventStatus & aStatus, void * aData, unsigned int aIndexOfKeypress, bool aNeedsCallback) Line 770	C++
 	xul.dll!mozilla::widget::TextEventDispatcher::MaybeDispatchKeypressEvents(const mozilla::WidgetKeyboardEvent & aKeyboardEvent, nsEventStatus & aStatus, void * aData, bool aNeedsCallback) Line 799	C++
 	xul.dll!mozilla::widget::NativeKey::DispatchKeyPressEventsWithoutCharMessage() Line 3618	C++
 	xul.dll!mozilla::widget::NativeKey::HandleKeyDownMessage(bool * aEventDispatched) Line 2653	C++
 	xul.dll!nsWindow::ProcessKeyDownMessage(const tagMSG & aMsg, bool * aEventDispatched) Line 6293	C++
 	xul.dll!nsWindow::ProcessMessageInternal(unsigned int msg, unsigned __int64 & wParam, __int64 & lParam, __int64 * aRetValue) Line 5236	C++
 	xul.dll!nsWindow::ProcessMessage(unsigned int msg, unsigned __int64 & wParam, __int64 & lParam, __int64 * aRetValue) Line 4738	C++
 	xul.dll!nsWindow::WindowProcInternal(HWND__ * hWnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 4690	C++
 	xul.dll!CallWindowProcCrashProtected(__int64(*)(HWND__ *, unsigned int, unsigned __int64, __int64) aWndProc, HWND__ * aHWnd, unsigned int aMsg, unsigned __int64 aWParam, __int64 aLParam) Line 27	C++
 	xul.dll!nsWindow::WindowProc(HWND__ * hWnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 4643	C++
 	user32.dll!00007ff9dd785581()	Unknown
 	user32.dll!00007ff9dd7830cd()	Unknown
 	xul.dll!nsAppShell::ProcessNextNativeEvent(bool mayWait) Line 958	C++
 	xul.dll!nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait) Line 131	C++
 	xul.dll!nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal * thr, bool mayWait) Line 250	C++
 	xul.dll!nsThread::ProcessNextEvent(bool aMayWait, bool * aResult) Line 1117	C++
 	xul.dll!NS_ProcessNextEvent(nsIThread * aThread, bool aMayWait) Line 480	C++
 	xul.dll!mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate * aDelegate) Line 85	C++
 	xul.dll!MessageLoop::RunInternal() Line 370	C++
 	xul.dll!MessageLoop::RunHandler() Line 364	C++
 	xul.dll!MessageLoop::Run() Line 346	C++
 	xul.dll!nsBaseAppShell::Run() Line 150	C++
 	xul.dll!nsAppShell::Run() Line 822	C++
 	xul.dll!nsAppStartup::Run() Line 296	C++
 	xul.dll!XREMain::XRE_mainRun() Line 5770	C++
 	xul.dll!XREMain::XRE_main(int argc, char * * argv, const mozilla::BootstrapConfig & aConfig) Line 5982	C++
 	xul.dll!XRE_main(int argc, char * * argv, const mozilla::BootstrapConfig & aConfig) Line 6039	C++
 	xul.dll!mozilla::BootstrapImpl::XRE_main(int argc, char * * argv, const mozilla::BootstrapConfig & aConfig) Line 45	C++
 	firefox.exe!do_main(int argc, char * * argv, char * * envp) Line 230	C++
 	firefox.exe!NS_internal_main(int argc, char * * argv, char * * envp) Line 448	C++
 	firefox.exe!wmain(int argc, wchar_t * * argv) Line 151	C++
 	[Inline Frame] firefox.exe!invoke_main() Line 90	C++
 	firefox.exe!__scrt_common_main_seh() Line 288	C++
 	kernel32.dll!BaseThreadInitThunk()	Unknown
 	ntdll.dll!RtlUserThreadStart()	Unknown
Attachment #9405602 - Attachment is obsolete: true

I reproduced the issue thanks to :mak and :handyman's comments and analyzed what is going on here. What is causing the hang with the stack from comment 10 is that :mak's example program registers as the clipboard owner by using OleSetClipboard and then remains the clipboard owner, but this program does not have a message loop. Clipboard functions rely on window messages as an IPC mechanism to interact with the clipboard owner, and in this stack Firefox has sent a window message and is waiting for :mak's example program to handle it. Since there is no message loop, we are waiting forever.

Doing this instead of just OleSetClipboard(&data); removes the hang:

OleSetClipboard(&data);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
	if (bRet == -1) {
		break;
	}
	else {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

This makes calls to DataObject::GetData occur in :mak's example program as expected when Firefox asks for the data. And we can now force a hang again if we want that, e.g. by putting an infinite loop there. As :handyman suspected, the implementation by Microsoft does not mitigate against unresponsive source programs. The source can take any amount of time to answer and that will block the thread that is waiting for the data. In particular if there is no message loop, no answer will ever come.

Are you aware of real-world software that takes ownership of the clipboard like that without having a message loop? Or, do we believe that this is just programs taking a long time to respond?

Flags: needinfo?(yjuglaret)

Answering my own comment: based on the jank stack in the profile from comment 0, the issue would be a source program taking a long time to respond (otherwise the stack would be the one from comment 10). Here is a profile where I made :mak's example program have a message loop and sleep for 10 seconds at the beginning of DataObject::GetData, as you can see it seems very similar to the profile from comment 0.

(In reply to Yannis Juglaret [:yannis] from comment #11)

Are you aware of real-world software that takes ownership of the clipboard like that without having a message loop? Or, do we believe that this is just programs taking a long time to respond?

Unfortunately we don't know what is causing our hangs here, we just know users are hitting multi-seconds hangs. I think it's just some software slow to respond. That's a problem though, as that external program will affect our responsiveness. Fwiw, Chromium has the same problem afaict, as my dumb program also hangs it.

Our specific problem here, is that we are polling the clipboard as consequence of a non-clipboard related operation (opening the urlbar). So the user won't associate the slowdown to the clipboard, as they would normally do for a copy or paste. That makes the problem worse for our use case. As things stand, I doubt we'll be able to ever ship this, unless the clipboard data getter moves to its own thread.

The bug assignee is inactive on Bugzilla, so the assignee is being reset.

Assignee: klubana → nobody
Status: ASSIGNED → NEW
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: