Skip to content

Instantly share code, notes, and snippets.

@Sunitha
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sunitha/9425626 to your computer and use it in GitHub Desktop.
Save Sunitha/9425626 to your computer and use it in GitHub Desktop.
Userscript server for IE in stead Trixie or IE7pro.
# -*- coding: utf-8 -*-
require "win32ole"
require "rkelly"
require "kconv"
# put the directory that you please.
# if not, the directory where the script is will be searched for as it looks.
USER_SCRIPT_DIR = ""
class UserScriptRunner
def initialize
@err_hwnd = {}
@scripts = load_user_scripts
STDERR.puts "begin serving"
serve
end
def serve
while 0
execute_scripts(get_all_ie)
sleep 1
end
end
def load_user_scripts
#TODO:
# what hitted on me about what needs be.
# covert of character code (probably UTF-8 to SJIS)
# elimination of comment
# the eval function will vomit script includes those.
# substitution of both carriage return, line feed
Dir.glob("*.user.js").map{|js|
scr = File.new(js, "r").read
begin
mod = modify(scr)
res = {
:script => "setTimeout(#{mod}, 1);",
:script_ie11 => "(#{mod})()",
:options => get_options(scr),
:name => js,
}
STDERR.puts "loaded #{js}"
res
rescue => err
STDERR.puts "#{js}, failed : #{err}"
end
}
end
def get_options(scr)
found_declaration_field = false
options = {}
scr.split("\n").each do |line|
if (/^\/\/ +==UserScript==/ =~ line)
if (found_declaration_field)
break
else
found_declaration_field = true
next
end
else
if (/^\/\/ +@([^ ]+) +([^ ]+)/ =~ line)
options[$1.to_sym] = $2
end
end
end
options
end
#IE's eval won't eat userscript as it is, it convert the scripts so that IE may be able to eat them.
def modify(scr)
nodes_stacks = get_nodes_stacks(scr)
last_ix = nodes_stacks.size - 1
(0..last_ix).to_a.reverse.each do |frg_ix|
nodes_stacks = convert_iter(nodes_stacks, {
:start_ix => (frg_ix-1),
:chain => [frg_ix],
})
end
#"setTimeout(function(){#{nodes_stacks.first[:new].gsub(/[\n\r]/,"")}}, 1);"
r = "function(){#{nodes_stacks.first[:new]}}".tosjis
#puts r
r
end
def convert_iter(nodes_stacks, args)
start_ix = args[:start_ix]
chain = args[:chain]
return nodes_stacks if chain.size.zero?
p = nodes_stacks[chain.first]
#puts "p:#{p}"
new = p[:new]
old = p[:old]
last_ix = nodes_stacks.size - 1
if (p[:class] == RKelly::Nodes::StringNode)
#TODO:do something if needs be
end
#TODO:再帰が動きませんwww
chnk_ix = (0..(start_ix)).to_a.reverse.find_index{|cand_ix|
nodes_stacks[cand_ix][:old].include?(old)
}
if (chnk_ix.nil?)
return nodes_stacks if chain.size.zero?
convert_iter(nodes_stacks, {
:start_ix => start_ix,
:chain => chain.drop(1),
})
else
nodes_stacks[chnk_ix][:new] = nodes_stacks[chnk_ix][:new].sub(old, new)
chain.unshift(chnk_ix)
# nodes_stacks = convert_iter(nodes_stacks, {
# :start_ix => (chnk_ix-1),
# :chain => chain,
# })
nodes_stacks
end
end
def get_nodes_stacks(scr)
parser = RKelly::Parser.new
ast = parser.parse(scr)
nodes_stacks = []
ix = 0
ast.each do |node|
#since troublesome, substituted backwards
nodes_stacks.push({
:old => node.to_ecma,
:new => node.to_ecma,
:class => node.class,
:index => ix,
})
ix+=1
end
nodes_stacks
end
def get_all_ie
get_ie_tabs
end
#I took a write, it must not be needed.
def get_ie_by_window
ies = get_ie_tabs
ie_by_window = {}
ies.each do |ie|
ie_by_window[ie[:hwnd]] ||= []
end
ie_by_window
end
def eval(window, scr)
begin
return window.eval(scr[:script])
rescue => err
#puts err
begin
#I am not sure, but there is case that window object has no eval function under itself.
# in lieu of that, since IE has execScript,
# it execute it.
# proviaded that, it is likely to be obsoleted on the coming IE11,
# that is concerned.
# IF so, I will examine to use setTimeout or something like that.
#window.execScript(scr[:script], "JavaScript")
#puts scr[:script_ie11]
window.setTimeout(scr[:script_ie11], 1)
return nil
rescue => e
STDERR.puts e
end
end
STDERR.puts "failed"
raise
end
def get_executed(window)
#window.localStorage.getItem("__RETURN__")
begin
window.document.getElementById("__RETURN__").getAttribute("name")
rescue
nil
end
end
#
def set_executed(window)
#window.localStorage.setItem("__RETURN__", "OK")
scr = <<EOS
if (document && document.body && window.document.getElementById) {
var elem = document.getElementById('__RETURN__');
if (elem){
elem.setAttribute('name', 'OK');
} else {
elem = document.createElement("style");
elem.setAttribute('id', '__RETURN__');
elem.setAttribute('name', 'OK');
document.body.appendChild(elem, document.body);
}
}
EOS
begin
mod = modify(scr)
eval(window, {
:script => "setTimeout(#{mod}, 1);",
:script_ie11 => "(#{mod})()"
})
rescue
nil
end
end
def get_mod_ie(window)
win = window.document.parentWindow
#STDERR.puts window.document.eval("1;");
{
#:body => window,
:document => window.document,
:hwnd => window.hwnd.to_s,
:location => win.location.href.to_s,
#it taints a global variable to judge wether the scripts has been executed on the tab or not.
#attention! if the domain is IP address itsef, or not html document, eval cannot be executed.
# if that is the case, have a register a domain for the IP to avoid.
:executed => get_executed(win)
}
end
def set_connector
=begin
document.addEventListener("keypress", function(event){
var code = (event.charCode||event.keyCode);
var elem = document.geteElementById("__CONNECTOR__");
if (elem) {
} {
}
elem.setAttribute()
});
=end
end
def get_connect_element(document)
elem = document.getElementById("__CONNECTOR__")
begin
name = elem.getAttribute("class")
if (name)
value = elem.getAttribute("value")
return {:name => name, :value => value}
end
end
{}
end
def erase_connect
elem = document.getElementById("__CONNECTOR__")
begin
elem.setAttribute("class", "")
elem.setAttribute("value", "")
end
end
def get_mod_frame(frame)
document = frame.contentDocument
window = document.parentWindow
puts window.location.href
{
#:body => window,
:document => document,
#:hwnd => window.hwnd.to_s,
:location => window.location.href.to_s,
#it taints a global variable to judge wether the scripts has been executed on the tab or not.
#attention! if the domain is IP address itsef, or not html document, eval cannot be executed.
# if that is the case, have a register a domain for the IP to avoid.
:executed => get_executed(window)
}
end
def get_ie_tabs
ies = []
shell = WIN32OLE.new('Shell.Application')
shell.Windows.each do |window|
next unless (window.path =~ /Internet Explorer/ rescue false)
next unless (hwnd = window.hwnd rescue false)
begin
res = false
begin
ies.push(get_mod_ie(window))
ifrs = window.document.getElementsByTagName("iframe")
# ifrs.each do |fr|
#puts "an iframe exists~"
# name = fr.getAttribute("name")
# puts name
# #window.document.iframes[name].document.paretWindow
# ies.push(ifrs.contentWindow)
# end
rescue => e
#puts e
if (@err_hwnd[window.hwnd.to_s].nil?)
#STDERR.puts window.hwnd.to_s
#STDERR.puts window.document.parentWindow.location.href.to_s
#STDERR.puts window.document.parentWindow.ole_methods.map{|e| e.to_s}.sort.join(",")
STDERR.puts e
@err_hwnd[window.hwnd.to_s] = true
end
end
rescue => err
#STDERR.puts "fuga"
#STDERR.puts window.document.parentWindow.location.to_s
#STDERR.puts err
#TODO do nothing, but i ought to do something.
begin
if (@err_hwnd[window.hwnd.to_s].nil?)
@err_hwnd[window.hwnd.to_s] = true
STDERR.puts window.hwnd.to_s
STDERR.puts window.document.parentWindow.location.href.to_s
STDERR.puts e
end
rescue
end
end
end
ies
end
def execute_scripts(ies)
# ies.map{|ie|
# (ie[:executed] ? nil : ie) }.compact.each do |ie|
ies.each do |ie|
scrs = get_script_to_be_executed(ie)
if (scrs.size > 0 && !has_executed?(ie))
win = ie[:document].parentWindow
scrs.each do |scr|
#STDERR.puts "scr:#{scr[:name]}"
execute_script(ie, scr)
end
set_executed(win)
#STDERR.puts "executed"
#puts win.document.body.innerHTML
end
end
end
def execute_script(ie, scr)
# to avoid any freeze, any defer,
# it must require lazy evaluation.
# it would better if using setTime or something.
begin
eval(ie[:document].parentWindow, scr)
rescue => err
if (@err_hwnd[ie[:hwnd]])
STDERR.puts "tres"
STDERR.puts ie[:hwnd]
begin
STDERR.puts ie[:document].parentWindow.location.href.to_s
rescue
end
STDERR.puts err
@err_hwnd[ie[:hwnd]] = true
end
#STDERR.puts err
end
end
def get_script_to_be_executed(ie)
target = []
@scripts.each do |scr|
next unless scr
if (url_matched_with(ie[:location], scr[:options][:include]))
target.push(scr)
end
end
target
end
def url_matched_with(location, exp)
rgx = Regexp.new(exp.gsub("*", ".*"))
# puts ""
# puts "rgx:#{rgx}"
# puts "location:#{location}"
# puts "rgx =~ location:#{rgx =~ location}"
rgx =~ location
end
# For there is no need to execute in the same tab twice.
def has_executed?(ie)
get_executed(ie[:document].parentWindow) == "OK"
end
end
u = UserScriptRunner.new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment