Как безопасно открыть диалог в плагине браузера NPAPI на OSX? - PullRequest
2 голосов
/ 10 декабря 2011

Моя проблема: JavaScript вызывает плагин, который разветвляет поток, открывающий диалоговое окно NSOpenPanel (или NSSavePanel). Часто это работает, но иногда происходит сбой в «runModal», как в Firefox, так и в Chrome. Похоже, что сбой происходит случайно и чаще на некоторых компьютерах (возможно, 10,7), чем на других. Расположение сбоя в стеке варьируется, но обычно происходит в потоке, открывающем диалог.

Я использую поток POSIX, потому что это кроссплатформенный код, но я создаю NSThread для , чтобы сообщить Какао :

NSThread* t = [[NSThread alloc] init];
[t start];
[t release];

Код, открывающий диалоговое окно:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // necessary?
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
[openPanel setAllowsMultipleSelection:_allowMultipleFiles];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:YES];
NSInteger result = [openPanel runModal]; // crash
... handle result ...
[pool release];

Трассировка стека:

Process:         plugin-container [25536]
Path:            /Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
Identifier:      org.mozilla.plugincontainer
Version:         ??? (1.0)
Code Type:       X86-64 (Native)
Parent Process:  firefox-bin [25531]

Date/Time:       2011-12-09 17:12:04.271 -0800
OS Version:      Mac OS X 10.6.8 (10K549)
Report Version:  6

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000000000000d
Crashed Thread:  14

Thread 14 Crashed:
0   XUL                             0x0000000100d84ff4 mac_plugin_interposing_child_OnSetCursor + 38068
1   XUL                             0x0000000100dd38bd mac_plugin_interposing_child_OnSetCursor + 359805
2   XUL                             0x0000000100d7b247 mac_plugin_interposing_child_OnHideCursor + 1175
3   com.apple.AppKit                0x00007fff801cbfdb _NSCreateWindowWithOpaqueShape2 + 610
4   com.apple.AppKit                0x00007fff80160691 -[NSWindow _commonAwake] + 1214
5   com.apple.AppKit                0x00007fff803c282f -[NSWindow _setModal:] + 138
6   com.apple.AppKit                0x00007fff803c2183 -[NSApplication _orderFrontModalWindow:relativeToWindow:] + 482
7   com.apple.AppKit                0x00007fff803c1ce7 -[NSApplication _commonBeginModalSessionForWindow:relativeToWindow:modalDelegate:didEndSelector:contextInfo:] + 714
8   com.apple.AppKit                0x00007fff803c1a18 -[NSApplication beginModalSessionForWindow:] + 36
9   com.apple.AppKit                0x00007fff803c193a -[NSApplication runModalForWindow:] + 106
10  XUL                             0x0000000100d7af01 mac_plugin_interposing_child_OnHideCursor + 337
11  com.apple.AppKit                0x00007fff80627112 -[NSSavePanel runModal] + 318
12  com.aspera.AsperaWeb            0x000000010509f9d4 Connect::Web::MacOpenFolderDialogImpl::doShow() + 596
13  com.aspera.AsperaWeb            0x00000001050a0fa3 Connect::Web::IDialogImpl::show() + 45
14  com.aspera.AsperaWeb            0x00000001050a1e07 Connect::Web::OpenFolderDialog::showHelper(bool) + 141
15  com.aspera.AsperaWeb            0x00000001050a1ef0 Connect::Web::OpenFolderDialog::run() + 42
16  com.aspera.AsperaWeb            0x000000010509eb91 Connect::Web::SingleJobWorker::run() + 63
17  com.aspera.AsperaWeb            0x0000000105098160 start_helper + 38
18  libSystem.B.dylib               0x00007fff8593afd6 _pthread_start + 331
19  libSystem.B.dylib               0x00007fff8593ae89 thread_start + 13

Thread 14 crashed with X86 Thread State (64-bit):
  rax: 0x0000000000000000  rbx: 0x0000000102063840  rcx: 0x0000000000000000  rdx: 0x0000000101e27ee0
  rdi: 0x0000000101e0bcc8  rsi: 0x0000000000000000  rbp: 0x0000000101e27ef0  rsp: 0x000000011d5805e0
   r8: 0x0000000101e0bcc8   r9: 0x00000001040fc0a4  r10: 0x00007fffffe001a0  r11: 0x0000000000000202
  r12: 0x0000000101e27ee0  r13: 0x0000000000000001  r14: 0x000000011d5805e0  r15: 0x000000011d580754
  rip: 0x0000000100d84ff4  rfl: 0x0000000000010246  cr2: 0x000000000000000d

Я предполагаю, что есть проблема с открытием диалога во вторичном потоке. Может быть, мне нужно открыть его в главном потоке? Тем не менее, я хочу сохранить асинхронный вызов JavaScript. Я смотрю на NSNotificationQueue или executeSelectorOnMainThread .

Ответы [ 3 ]

4 голосов
/ 10 декабря 2011

У меня есть диалоговая абстракция, которую я использую в FireBreath здесь: https://gist.github.com/1368648

Оказывается, вы не можете использовать runModal во вторичном потоке; это должно быть в основном потоке приложения. Это проблема в плагине NPAPI, потому что вы также не можете заблокировать основной поток во время вызова NPAPI или браузер (по крайней мере, Chrome и Firefox 4+) может убить вас.

Решение состоит в том, чтобы запустить его в основном потоке, но не как часть вызова NPAPI; Вы можете сделать это, используя executeSelectorOnMainThread и не дожидаясь его завершения. Если вы предоставляете какой-то обратный вызов (функция javascript NPObject прекрасно работает), вы можете вызвать его, когда ваш диалог завершится.

FireBreath использует этот метод для своей функции BrowserHost :: ScheduleOnMainThread, которую я сейчас использую в той сущности, на которую я ссылался. Кто-то (вы?) Опубликовал комментарий к сути, спрашивая, работает ли он для меня; Суть в момент комментария не сделал, но я обновил его с исправлением (которое я только что выяснил вчера). Так как мы обновили его, чтобы использовать executeSelectorOnMainThread для запуска вызова позже, я не смог воспроизвести сбой.

Оглядываясь назад, вы уже знакомы с тем, чтобы перезвонить в главном потоке следующим образом: Как перезаписать поток плагинов в Safari 5.1 для OSX?

Вы используете ту же технику; можно было бы подумать, что блокирование потока также будет опасным, но, поскольку во время модального диалога цикл системных событий продолжает выполняться, все в порядке; Ваш код не возвращается, но другие события по-прежнему отправляются. Браузер не ожидает возврата вызова NPAPI, поэтому у него, похоже, нет проблем.

2 голосов
/ 10 декабря 2011

Весь Appkit не является потокобезопасным, и все методы Appkit должны выполняться в основном потоке.Вы можете использовать executeselectoronmainthread ... Методы, чтобы заставить логику находиться в главном потоке.

0 голосов
/ 10 декабря 2011

Открытые панели в любом случае должны быть асинхронными, поэтому нет причин, по которым вы не можете отобразить открытую панель в главном потоке. Просто не используйте runModal. В идеале показывать это в виде листа в окне браузера.

Когда он вернется к вам, вы можете вернуть результат на сторону JavaScript любым способом, который вы уже планировали использовать для этого.

...