Страницы: 1
Известно, что при симуляции события из под расширения, например щелчка по какому либо элементу:
evt = target_document.createEvent("MouseEvents"); evt.initMouseEvent("click", true, true, target_document.defaultView, 1, x+ox, y+oy, x, y, false, false, false, false, v, null); element.dispatchEvent(evt);
оно посылается с параметром trusted=true , тоесть ведёт себя в принципе как настоящее, и target_document его видит как настоящее. Тоесть по логике при симуляции щелчка на sfw объекте, фаерфокс должен передать события в Flash интерпретатор, но он этого не делает. Собственно необходимо симулировать щелчок на flash-кнопке. Пробовал сделать: DOM видит событие, а во flash не выполняется действие по щелчку в заданных координатах - тоесть во флеше событие не пошло.
Вопрос - как средствами фаерфокс-расширения кормить флешу события, чтобы он их обрабатывал ровно также, как настоящие щелчки/перемещения мышки, нажатия клавиш?
Буду очень благодарен за любую помощь и ссылки!
Отсутствует
Ну по сути флешки на сайте являются независимыми элементами и все события они обрабатывают напрямую и сами. И если в флешке не предусмотрено пробрасывание события из скрипта, то наверное никак.
Но я могу ошибаться.
Отсутствует
-и все события они обрабатывают напрямую и сами
вот это печально.. Тоесть флеш спрашивает у ОС координаты мышки/какие клавиши сейчас нажаты, а не у фоерфокса?
-о наверное никак
один то способ есть всегда, но он уж черезчур костыльный, и будет работать только на флеш-плеере debug версии:
1. Ставим флеш-дебаг версию.
2. Указываем профайлер для флеша, наподобие как это делается в расширении Flash Firebug - можно и взять FFBug-профайлер.
3. Обмениваемся данными с флешом нативными методами swf.flashfirebug_send(some_data) в том числе можно посылать скрипты, в том числе и создавать события.
Отсутствует
SunnyDay
js-ctypes + mouse_event/SendInput. Других способов нет.
Отсутствует
SunnyDay
js-ctypes + mouse_event/SendInput. Других способов нет.
Большое спасибо за инфу, отличное решение!
Единственное, в первый раз слышу о js-ctypes, тоесть надо примерно так сделать:
1. Импортим ctypes.
2. Грузим user32.dll для винды
3. Далее выполняем из user32.dll цепочку mouse_event - с зажатой левой кнопкой мыши, затем с отжатой (эмулируя тем самым щелчок) в нужных координатах.
Всё верно? Если реализовывал сам, поделись плиз кусочком кода
Отсутствует
SunnyDay
Да, примерно так.
Если реализовывал сам
Именно это - не реализовывал, я через js-ctypes работал с системными раскладками (GetKeyboardLayout/GetKeyboardLayoutList/ActivateKeyboardLayout etc).
Да, если клик нужно послать в определенное окно (вот не знаю, являются ли у флэша кнопки отдельными окнами, но для win32-приложений это обычное дело), то тут будет нужно смотреть в сторону FindWindow/FindWindowEx/EnumWindows- чтобы в найденное окно посредством PostMessage слать WM_LBUTTONDOWN и WM_LBUTTONUP.
Вообще всё это намного удобней делать, если по возможности большую часть кода написать на C, и завернуть в статические методы с пометкой extern "C" __declspec(dllexport) - тогда эти свои методы можно будет вызывать из своей библиотеки с помощью всё того же js-ctypes точно так же, как методы системных библиотек.
Отсутствует
Ясно, спасибо. Что-то придумаю, решение кину в тему. Кнопки в флеше не являются окнами, покрайней мере в AS3.
- но для win32-приложений это обычное дело
В идеале - найти нужную вкладку ФФ, и послать туда событие, после этого по логике флеш-плеер должен задиспечить событие себе тоже. Вкладка ФФ это ведь отдельное окно в win32?
Отсутствует
Проблемма решена! Как и обещал, выкладываю решение:
небольшая обёрточка на js-ctypes для работы с windows-окнами, и отправки событий мышки:
var YBWindows = { loaded: false, //объявление windows-типов CallBackABI: false, WinABI: false, EnumWindowsProc: false, //windows-функции EnumWindows: false, EnumChildWindows: false, PostMessage: false, load: function () { try{ eval('Components.utils.import("resource://gre/modules/ctypes.jsm")'); this.user32dll = ctypes.open("user32.dll"); /* Linux ctypes.open("libc.so.6"); Most other Unixes libc = ctypes.open("libc.so"); */ if (ctypes.size_t.size == 8) { this.CallBackABI = ctypes.default_abi; this.WinABI = ctypes.default_abi; } else { this.CallBackABI = ctypes.stdcall_abi; this.WinABI = ctypes.winapi_abi; } //объявляем тип коллбека this.EnumWindowsProc = ctypes.FunctionType(this.CallBackABI, ctypes.bool, [ctypes.size_t, ctypes.size_t]); //объявляем методы user32.dll для работы с окнами и для отправки событий this.EnumWindows = this.user32dll.declare('EnumWindows', this.WinABI, ctypes.bool, this.EnumWindowsProc.ptr, ctypes.size_t); this.EnumChildWindows = this.user32dll.declare('EnumChildWindows', this.WinABI, ctypes.bool, ctypes.size_t, this.EnumWindowsProc.ptr, ctypes.size_t); eval("this.GetClassName = this.user32dll.declare('GetClassNameW', this.WinABI, ctypes.int, ctypes.size_t, ctypes.jschar.ptr, ctypes.int);"); eval("this.GetWindowText = this.user32dll.declare('GetWindowTextW', this.WinABI, ctypes.int, ctypes.size_t, ctypes.jschar.ptr, ctypes.int);"); this.PostMessage = this.user32dll.declare('PostMessageW', this.WinABI, ctypes.bool, ctypes.size_t, ctypes.unsigned_int, ctypes.size_t, ctypes.size_t); this.loaded = true; }catch(e){ alert(e.message); } }, //возвращает список окон корневого уровня - без параметра parent_hWnd, иначе - окон, дочерних от данного WindowsList: function(parent_hWnd, selector){ if (parent_hWnd==undefined) parent_hWnd = false; if (selector==undefined) selector = false; //список названий оконных дескрипторов - заполняемый коллбеком EnumWindowsCallback var wnames = []; var self = this; var EnumWindowsCallback = function(hwnd, lParam) { try{ var result = true; //wnames.push(self.GetWindowTextLength(hwnd)); var cl_buf = new new ctypes.ArrayType(ctypes.jschar, 255); self.GetClassName(hwnd, cl_buf, 255); var tit_buf = new new ctypes.ArrayType(ctypes.jschar, 255); self.GetWindowText(hwnd, tit_buf, 255); //if (clss.indexOf('mozilla')!=-1||clss.indexOf('firefox')!=-1||clss.indexOf('gecko')!=-1){ var ni = wnames.length; var is_selector = true; if (typeof selector.classname != 'undefined'){ if (cl_buf.readString().toLocaleLowerCase().indexOf(selector.classname)==-1){ is_selector = false; } } if (typeof selector.title != 'undefined'){ if (tit_buf.readString().toLocaleLowerCase().indexOf(selector.title)==-1){ is_selector = false; } } if (is_selector){ wnames.push({classname: cl_buf.readString(),title: tit_buf.readString(), wnd: hwnd, i: ni}); } //в lParam можно вернуть окно }catch(e){ alert(e.message); return false; } return true; }; //создание указателя на коллбэк ф-ю var callback_ptr = this.EnumWindowsProc.ptr(EnumWindowsCallback); //создаем переменную, адрес которой будет передан в калбэк-функцию var wnd = ctypes.size_t(0); //типизация оказалась строгая, приходится приводить типы в явном виде с ctypes.cast if (parent_hWnd){ this.EnumChildWindows(parent_hWnd, callback_ptr, ctypes.cast(wnd.address(), ctypes.size_t)); }else{ this.EnumWindows(callback_ptr, ctypes.cast(wnd.address(), ctypes.size_t)); } return wnames; }, //возвращает дерево окон - от родителя parent_hWnd, либо окон топ-уровня WindowsTree: function(base_selector, parent_hWnd){ if (base_selector==undefined) base_selector = false; if (parent_hWnd==undefined) parent_hWnd = false; var list = this.WindowsList(parent_hWnd, base_selector); var mywin_arr = Array(); for(var i=0;i<list.length;i++){ list[i].childs = this.WindowsTree(false,list[i].wnd); mywin_arr.push(list[i]); } return mywin_arr; }, //эмулирует щелчок мыши через PostMessage SendMouseEvent: function(wnd, event, x, y, params){ var params_code = 0; try{ if (params == undefined) params = false; else { for(var i=0;i<params.length;i++){ if (params[i]=='MK_CONTROL'){ params_code = params_code | 0x0008; }else if (params[i]=='MK_LBUTTON'){ params_code = params_code | 0x0001; }else if (params[i]=='MK_MBUTTON'){ params_code = params_code | 0x0010; }else if (params[i]=='MK_RBUTTON'){ params_code = params_code | 0x0002; }else if (params[i]=='MK_SHIFT'){ params_code = params_code | 0x0004; } } } var evt_code = 0; if (event == 'WM_MOUSEMOVE'){ evt_code = 0x0200; }else if (event == 'WM_LBUTTONDOWN'){ evt_code = 0x0201; }else if (event == 'WM_LBUTTONUP'){ evt_code = 0x0202; }else if (event == 'WM_RBUTTONDOWN'){ evt_code = 0x0204; }else if (event == 'WM_RBUTTONUP'){ evt_code = 0x0205; }else if (event == 'WM_MBUTTONDOWN'){ evt_code = 0x0207; }else if (event == 'WM_MBUTTONUP'){ evt_code = 0x0208; } var coords = (y << 16) | (x & 0xFFFF); this.PostMessage(wnd, evt_code,params_code,coords); }catch(e){ alert(e.message); } } } YBWindows.load();
мы можем посмотреть все окна вот так(вывод в фаербаг):
content.document.defaultView.wrappedJSObject.console.log(YBWindows.WindowsList());//список окон content.document.defaultView.wrappedJSObject.console.log(YBWindows.WindowsTree());//дерево окон
найти главные окна фаерфокса вот так:
content.document.defaultView.wrappedJSObject.console.log(YBWindows.WindowsTree({classname: 'mozillawindow', title: 'mozilla'}));
отправить в первое главное окно щелчок вот так:
var hwnd = YBWindows.WindowsList({classname: 'mozillawindow', title: 'mozilla'})[0].wnd; YBWindows.SendMouseEvent(hwnd, 'WM_LBUTTONDOWN',350,400); YBWindows.SendMouseEvent(hwnd, 'WM_LBUTTONUP',350,400);
щелчок обрабатывается флешом, мало того такая отправка сообщений работает на свёрнутых окнах.
Единственное что не получилось - это отправлять щелчок на определённую вкладку, ибо Starting in Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1), only the top level browser window has an HWND. https://developer.mozilla.org/en-US/docs/Code_snippets/Finding_Window_Handles но это не критично.
Помогла статья с хабра http://habrahabr.ru/post/111044/ .
Ещё впилю потом в обёртку работу на линуксах дабы не рушить кроссплатформенность, и симуляция клавиатурных событий аналогично мышовых. Гидролизер, благодарю
Отредактировано SunnyDay (09-11-2012 18:18:30)
Отсутствует
По просьбе орец выкладываю рабочий код кормёжки флеша событиями:
обновлённый класс работы с windows (linux пока не заморачивал..)
var YBOSWrapper = { loaded: false, //объявление windows-типов CallBackABI: false, WinABI: false, EnumWindowsProc: false, //windows-функции EnumWindows: false, EnumChildWindows: false, PostMessage: false, load: function () { try{ eval('Components.utils.import("resource://gre/modules/ctypes.jsm")'); this.user32dll = ctypes.open("user32.dll"); /* Linux ctypes.open("libc.so.6"); Most other Unixes libc = ctypes.open("libc.so"); */ if (ctypes.size_t.size == 8) { this.CallBackABI = ctypes.default_abi; this.WinABI = ctypes.default_abi; } else { this.CallBackABI = ctypes.stdcall_abi; this.WinABI = ctypes.winapi_abi; } //объявляем тип коллбека this.EnumWindowsProc = ctypes.FunctionType(this.CallBackABI, ctypes.bool, [ctypes.size_t, ctypes.size_t]); //объявляем методы user32.dll для работы с окнами и для отправки событий this.EnumWindows = this.user32dll.declare('EnumWindows', this.WinABI, ctypes.bool, this.EnumWindowsProc.ptr, ctypes.size_t); this.EnumChildWindows = this.user32dll.declare('EnumChildWindows', this.WinABI, ctypes.bool, ctypes.size_t, this.EnumWindowsProc.ptr, ctypes.size_t); eval("this.GetClassName = this.user32dll.declare('GetClassNameW', this.WinABI, ctypes.int, ctypes.size_t, ctypes.jschar.ptr, ctypes.int);"); eval("this.GetWindowText = this.user32dll.declare('GetWindowTextW', this.WinABI, ctypes.int, ctypes.size_t, ctypes.jschar.ptr, ctypes.int);"); this.PostMessage = this.user32dll.declare('PostMessageW', this.WinABI, ctypes.bool, ctypes.size_t, ctypes.unsigned_int, ctypes.size_t, ctypes.size_t); }catch(e){ alert(e.message); } }, //возвращает список окон корневого уровня - без параметра parent_hWnd, иначе - окон, дочерних от данного WindowsList: function(parent_hWnd, selector){ if (parent_hWnd==undefined) parent_hWnd = false; if (selector==undefined) selector = false; //список названий оконных дескрипторов - заполняемый коллбеком EnumWindowsCallback var wnames = []; var self = this; var EnumWindowsCallback = function(hwnd, lParam) { try{ var result = true; //wnames.push(self.GetWindowTextLength(hwnd)); var cl_buf = new new ctypes.ArrayType(ctypes.jschar, 255); self.GetClassName(hwnd, cl_buf, 255); var tit_buf = new new ctypes.ArrayType(ctypes.jschar, 255); self.GetWindowText(hwnd, tit_buf, 255); //if (clss.indexOf('mozilla')!=-1||clss.indexOf('firefox')!=-1||clss.indexOf('gecko')!=-1){ var ni = wnames.length; var is_selector = true; if (typeof selector.classname != 'undefined'){ if (cl_buf.readString().toLocaleLowerCase().indexOf(selector.classname)==-1){ is_selector = false; } } if (typeof selector.title != 'undefined'){ if (tit_buf.readString().toLocaleLowerCase().indexOf(selector.title)==-1){ is_selector = false; } } if (is_selector){ wnames.push({classname: cl_buf.readString(),title: tit_buf.readString(), wnd: hwnd, i: ni}); } //в lParam можно вернуть окно }catch(e){ alert(e.message); return false; } return true; }; //создание указателя на коллбэк ф-ю var callback_ptr = this.EnumWindowsProc.ptr(EnumWindowsCallback); //создаем переменную, адрес которой будет передан в калбэк-функцию var wnd = ctypes.size_t(0); //типизация оказалась строгая, приходится приводить типы в явном виде с ctypes.cast if (parent_hWnd){ this.EnumChildWindows(parent_hWnd, callback_ptr, ctypes.cast(wnd.address(), ctypes.size_t)); }else{ this.EnumWindows(callback_ptr, ctypes.cast(wnd.address(), ctypes.size_t)); } return wnames; }, //возвращает дерево окон - от родителя parent_hWnd, либо окон топ-уровня WindowsTree: function(base_selector, parent_hWnd){ if (base_selector==undefined) base_selector = false; if (parent_hWnd==undefined) parent_hWnd = false; var list = this.WindowsList(parent_hWnd, base_selector); var mywin_arr = Array(); for(var i=0;i<list.length;i++){ list[i].childs = this.WindowsTree(false,list[i].wnd); mywin_arr.push(list[i]); } return mywin_arr; }, //эмулирует щелчок мыши через PostMessage SendMouseEvent: function(wnd, event, x, y, params){ var params_code = 0; try{ if (params == undefined) params = false; else { for(var i=0;i<params.length;i++){ if (params[i]=='MK_CONTROL'){ params_code = params_code | 0x0008; }else if (params[i]=='MK_LBUTTON'){ params_code = params_code | 0x0001; }else if (params[i]=='MK_MBUTTON'){ params_code = params_code | 0x0010; }else if (params[i]=='MK_RBUTTON'){ params_code = params_code | 0x0002; }else if (params[i]=='MK_SHIFT'){ params_code = params_code | 0x0004; } } } var evt_code = 0; if (event == 'WM_MOUSEMOVE'){ evt_code = 0x0200; }else if (event == 'WM_LBUTTONDOWN'){ evt_code = 0x0201; }else if (event == 'WM_LBUTTONUP'){ evt_code = 0x0202; }else if (event == 'WM_RBUTTONDOWN'){ evt_code = 0x0204; }else if (event == 'WM_RBUTTONUP'){ evt_code = 0x0205; }else if (event == 'WM_MBUTTONDOWN'){ evt_code = 0x0207; }else if (event == 'WM_MBUTTONUP'){ evt_code = 0x0208; } var coords = (y << 16) | (x & 0xFFFF); this.PostMessage(wnd, evt_code,params_code,coords); }catch(e){ alert(e.message); } } }
рабочий код для отправки щелчка мышки:
x,y - локальные координаты во флехе, по которым надо кликнуть
document_offset - смещение координат документа относительно глобальных
hwnd_in_cache() - храню дескрипторы hwnd в кеше чтобы лишний раз не пробегаться по 1000 окон)
this.oswrapper - это структурка которую я выкладывал
... //находим окно // !если ифрейм - берём документ родителя var hwnd = this.hwnd_in_cache(owdoc); if (!hwnd){ var ffwindows = this.oswrapper.WindowsList(false,{classname: 'mozillawindow', title: 'mozilla'}); for(var i=0;i<ffwindows.length;i++){ var tit = ffwindows[i].title; if (tit.indexOf(owdoc.title.substring(0, 10))!=-1){ hwnd = ffwindows[i].wnd; this.hwnd_cache[i] = {document: owdoc, hwnd: hwnd}; break; } } } //переброс на активную вкладку if (!hwnd){ return false; } // ! logger если не нашли окно this.oswrapper.SendMouseEvent(hwnd, 'WM_LBUTTONDOWN',x+document_offset.x,y+document_offset.y); var self = this; setTimeout(function(){ self.oswrapper.SendMouseEvent(hwnd, 'WM_LBUTTONUP',x+document_offset.x,y+document_offset.y); // this.dispatch oncomplete event },100+Math.random()*200);
клавишные события я только тестовый код пробовал, уже потерял..
Потом второй способ событий во флеш - через PreloadSwf, он оказался для меня лучше:
as3:
//конструктор public function PreloadSwf():void { //trace(root.name); addEventListener("allComplete", allComplete); .... ExternalInterface.addCallback("Simulate", this.Simulate); } private function allComplete(e:Event):void { removeEventListener("allComplete", allComplete); var info:LoaderInfo = e.target as LoaderInfo; trace(info.url, "is being monitored"); var domain:String = getDomainFromURL(info.url) //info.url. if (domain!=""){ flash.system.Security.allowDomain(domain); } MonitoredSWFs.push(info); } private function findSWF():LoaderInfo{ for each (var li:LoaderInfo in MonitoredSWFs){ if (li.url.indexOf(monitoredDfl)==li.url.length-monitoredDfl.length){ return li; } } return null; } .... public function Simulate(x:Number,y:Number):String{ // var stg:Stage = findSWF().content.stage; var under_pt:Array = stg.getObjectsUnderPoint(new Point(x,y)); var rets:String = ""; var bubbles:Boolean = true; //under_pt[5] if (!under_pt.length){return "not found";} var uo:DisplayObject = under_pt[under_pt.length - 1]; //for each (var uo:DisplayObject in monitored){ rets+=RemovePackageFromClassname(getQualifiedClassName(uo)) + "-"+uo.name+", v="+(uo.visible?"1":"0")+"\n"; uo.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE, bubbles, false, x-5, y-5)); uo.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OVER, bubbles, false, x, y)); setTimeout(function(tmo:DisplayObject):void{ tmo.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN, bubbles, false, x, y)); },50,uo); setTimeout(function(tmo:DisplayObject):void{ tmo.dispatchEvent(new MouseEvent(MouseEvent.CLICK, bubbles, false, x, y)); },100,uo); setTimeout(function(tmo:DisplayObject):void{ tmo.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP, bubbles, false, x, y)); },150,uo); setTimeout(function(tmo:DisplayObject):void{ tmo.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT, bubbles, false, x+5, y+5)); },250,uo); //} return rets; }
вмонтировать PreloadSwf надо через правку [винда-папка-пользователя]/файла mm.cfg добавить строчку например PreloadSwf=C:\Users\goncharov\Adobe Flash Builder 4.6\fltest\bin-debug\PreloadSwf.swf
далее всё просто - в JS коде плагина
document_with_sfw.getElementsByTagName('EMBED')[0].Simulate(20, 30);
кликнет по локальным координатам флешки x:20 y:30 - работает даже если браузер свёрнут и вкладка не активна, поэтому я оставил этот способ, хоть он и требует Flash дебаг версии для инъекции PreloadSwf .
Отсутствует
Страницы: 1