Last active
August 29, 2015 13:57
-
-
Save Sunitha/9425626 to your computer and use it in GitHub Desktop.
Userscript server for IE in stead Trixie or IE7pro.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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