WIN32OLE - 将 Ruby 函数注入 Internet Explorer 的 JavaScript

WIN32OLE - Inject Ruby function into Internet Explorer's JavaScript

在 AHK 中,以下代码可用于将 AHK 函数注入 Internet Explorer 的 JavaScript 引擎:

#Persistent

html =
(
<html>
  <head>
    <script>
      document.setVar = function`(name,val`){
        document[name]=val;
      }
    </script>
  </head>
  <body>
    <h1>Hello!</h1>
  </body>
</html>
)

ie := ComObjCreate("InternetExplorer.Application")
ie.navigate("about:blank")
sleep, 2000


msgbox, %html%
ie.document.writeln(html)
ie.visible := true
ie.document.setVar("someFunc",Func("someFunc"))


someFunc(){
  msgbox, "hello"
}

注入函数后,JavaScript可以调用document.someFunc(),从而JavaScript调用AHK函数,最终运行一个消息框。

我想将此代码移植到 Ruby。到目前为止我有这个:

require 'win32ole'
ie = WIN32OLE.new('InternetExplorer.Application')
ie.navigate("about:blank")
sleep(0.1) until !ie.busy
html = <<Heredoc
<html>
  <head>
    <script>
      document.setVar = function(name,val){
        document[name]=val;
      }
    </script>
  </head>
  <body>
    <h1>Hello!</h1>
  </body>
</html>
Heredoc

ie.document.writeln(html)
ie.visible = true

既然我们到了这里,我们应该能够注入 Ruby 方法,但是目前我不知道如何实现它。每次我尝试某事时,JavaScript 引擎都会冻结。我尝试过的一些事情:

ie.document.setVar("someFunc",method(:someFunc))
#----------------------------------
ie.document.setVar("someFunc",->{puts "hello"})
#----------------------------------
class someClass
  def someFunc
    puts "hello"
  end
end
ie.document.setVar("someClass",someClass})
#----------------------------------
closure = Class.new(Fiddle::Closure) {
  def call
    puts "hello world"
  end
}.new(Fiddle::TYPE_INT,[])
someFunc = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
#Both:
doc.setVar("someFunc",closure)
#and
doc.setVar("someFunc",someFunc)
#----------------------------------

None 以上方法都有效。最后他们都冻结了 JavaScript 引擎...有谁知道我如何将对 Ruby 函数的实际引用传递给 JavaScript?

在互联网上搜索了一个星期后,一无所获,我想 "What if we are naive and pass an object with method_missing defined?"

30 分钟后,我可以从 JavaScript 呼叫 Ruby。

class MyFunc
  # Called when no arguments are passed to JavaScript function
  def call
    #Execute any ruby code here!!

    #You can also return values back to JavaScript!!
    return 1
  end

  # Called when arguments are passed to JavaScript function
  def value(*args)
    if args.length == 0
      # This will be called if the function is called without parenthesis in JS
      # e.g. console.log(document.someFunc)
      return nil
    else
      #This is called with the parsed arguments. Note: Functions passed in from JS are of type WIN32OLE. Theoretically this should be callable, but better would be to make a JS function which can call other JS functions
      #Execute any ruby code here!!
      puts "#{args.inspect}"

      #Can also return values here as well
      return 1
    end
  end
end
ie.document.setVar("myFunc",MyFunc.new})

您还可以初始化和访问实例变量:

class MyClass
  def initialize
    @hello = "world"
  end
end
ie.document.setVar("myClass",MyClass.new})

#IN IE: document.myClass["hello"] //=> "world"

注:

有些事情可能会出现严重错误,甚至会导致 ruby 崩溃。一些不起作用的例子:

  • 直接评估对象:document.myObj。 JavaScript 会将函数解释为 Object ,也许正如人们所期望的那样。
  • 获取不存在的值没有任何作用:document.myObj["hello"]
  • 设置不存在的值会导致 ruby 崩溃:document.myObj["hello"]=1

有些事情也毫无意义,例如我做了以下循环:

给定一个 class:

class MyClass
  def call
    puts "Call"
  end
  def method_missing(m,*args,&block)
    puts "#{m}(#{args.inspect})"
  end
end
ie.document.setVar("obj",MyClass.new)

和JavaScript:

for(var i=0;i<24;i++){
  document.obj[chars[i]]()
}

这将执行以字母字符命名的obj 的每个函数。事实上,它在大多数情况下确实这样做了,但有时却没有。有时它会调用主要的call方法,而在document.obj.j()的情况下它什么都不做...

完整日志:

a([])
b([])
c([])
d([])
e([])
f([])
Hello world
h([])
i([])
k([])
Hello world
m([])
n([])
o([])
q([])
s([])
t([])
Hello world
v([])
w([])
x([])
y([])
Hello world

编辑

I've written a GIST 这通常更容易实现。例如。要将 File 对象传递给 IE,您可以执行以下操作:

ie.document.setVar("RubyFile",WIN32OLE::getDispatch(File))