2014-02-14 15:18:10 -08:00
|
|
|
# vim: set sw=4 et:
|
|
|
|
|
2014-05-05 11:58:55 -07:00
|
|
|
import json
|
2014-05-05 12:26:39 -07:00
|
|
|
import itertools
|
2014-05-05 11:58:55 -07:00
|
|
|
import os
|
|
|
|
import re
|
2014-02-14 15:18:10 -08:00
|
|
|
import logging
|
2014-05-04 21:33:13 -07:00
|
|
|
import time
|
2014-05-05 11:58:55 -07:00
|
|
|
import sys
|
2015-01-26 16:01:53 -08:00
|
|
|
import yaml
|
2015-01-26 16:58:12 -08:00
|
|
|
import string
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
class Behavior:
|
2014-05-29 20:43:00 -07:00
|
|
|
logger = logging.getLogger(__module__ + "." + __qualname__)
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
_behaviors = None
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def behaviors():
|
|
|
|
if Behavior._behaviors is None:
|
2015-01-26 16:01:53 -08:00
|
|
|
behaviors_yaml = os.path.sep.join(__file__.split(os.path.sep)[:-1] + ['behaviors.yaml'])
|
|
|
|
with open(behaviors_yaml) as fin:
|
|
|
|
conf = yaml.load(fin)
|
|
|
|
Behavior._behaviors = conf['behaviors']
|
2014-05-04 21:33:13 -07:00
|
|
|
|
2015-01-26 16:58:12 -08:00
|
|
|
simpleclicks_js_in = os.path.sep.join(__file__.split(os.path.sep)[:-1] + ["behaviors.d"] + ["simpleclicks.js.in"])
|
|
|
|
with open(simpleclicks_js_in) as fin:
|
|
|
|
simpleclicks_js_template = string.Template(fin.read())
|
|
|
|
|
2015-01-26 16:01:53 -08:00
|
|
|
for behavior in Behavior._behaviors:
|
|
|
|
if "behavior_js" in behavior:
|
|
|
|
behavior_js = os.path.sep.join(__file__.split(os.path.sep)[:-1] + ["behaviors.d"] + [behavior["behavior_js"]])
|
|
|
|
behavior["script"] = open(behavior_js, encoding="utf-8").read()
|
2015-01-26 16:58:12 -08:00
|
|
|
elif "click_css_selector" in behavior:
|
2015-09-15 18:03:08 -07:00
|
|
|
if "click_css_selector_end_condition" not in behavior:
|
|
|
|
behavior["click_css_selector_end_condition"] = "";
|
|
|
|
|
|
|
|
if "click_css_selector_computed_style_end_condition" not in behavior:
|
|
|
|
behavior["click_css_selector_computed_style_end_condition"] = "";
|
|
|
|
|
|
|
|
behavior["script"] = simpleclicks_js_template.substitute(click_css_selector=behavior["click_css_selector"], click_css_selector_end_condition=behavior["click_css_selector_end_condition"], click_css_selector_computed_style_end_condition=behavior["click_css_selector_computed_style_end_condition"])
|
2014-05-04 21:33:13 -07:00
|
|
|
|
2015-01-26 16:01:53 -08:00
|
|
|
return Behavior._behaviors
|
2014-05-04 21:33:13 -07:00
|
|
|
|
2014-05-05 12:26:39 -07:00
|
|
|
def __init__(self, url, umbra_worker):
|
2014-05-04 21:33:13 -07:00
|
|
|
self.url = url
|
2014-05-05 12:26:39 -07:00
|
|
|
self.umbra_worker = umbra_worker
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
self.script_finished = False
|
|
|
|
self.waiting_result_msg_ids = []
|
2014-05-05 11:58:55 -07:00
|
|
|
self.active_behavior = None
|
|
|
|
self.last_activity = time.time()
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
def start(self):
|
|
|
|
for behavior in Behavior.behaviors():
|
|
|
|
if re.match(behavior['url_regex'], self.url):
|
2015-01-26 16:01:53 -08:00
|
|
|
if "behavior_js" in behavior:
|
|
|
|
self.logger.info("using {} behavior for {}".format(behavior["behavior_js"], self.url))
|
2015-01-26 16:58:12 -08:00
|
|
|
elif "click_css_selector" in behavior:
|
|
|
|
self.logger.info("using simple click behavior with css selector {} for {}".format(behavior["click_css_selector"], self.url))
|
|
|
|
|
2014-05-05 11:58:55 -07:00
|
|
|
self.active_behavior = behavior
|
2015-01-26 16:01:53 -08:00
|
|
|
self.umbra_worker.send_to_chrome(method="Runtime.evaluate",
|
|
|
|
suppress_logging=True, params={"expression": behavior["script"]})
|
|
|
|
self.notify_of_activity()
|
|
|
|
return
|
2015-01-26 16:58:12 -08:00
|
|
|
|
2015-01-26 16:01:53 -08:00
|
|
|
self.logger.warn("no behavior to run on {}".format(self.url))
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
def is_finished(self):
|
2014-08-01 16:22:45 -07:00
|
|
|
msg_id = self.umbra_worker.send_to_chrome(method="Runtime.evaluate",
|
|
|
|
suppress_logging=True, params={"expression":"umbraBehaviorFinished()"})
|
2014-05-04 21:33:13 -07:00
|
|
|
self.waiting_result_msg_ids.append(msg_id)
|
|
|
|
|
2014-05-05 11:58:55 -07:00
|
|
|
request_idle_timeout_sec = 30
|
|
|
|
if self.active_behavior and 'request_idle_timeout_sec' in self.active_behavior:
|
|
|
|
request_idle_timeout_sec = self.active_behavior['request_idle_timeout_sec']
|
|
|
|
idle_time = time.time() - self.last_activity
|
|
|
|
|
|
|
|
return self.script_finished and idle_time > request_idle_timeout_sec
|
2014-05-04 21:33:13 -07:00
|
|
|
|
|
|
|
def is_waiting_on_result(self, msg_id):
|
|
|
|
return msg_id in self.waiting_result_msg_ids
|
|
|
|
|
|
|
|
def notify_of_result(self, chrome_message):
|
|
|
|
# {'id': 59, 'result': {'result': {'type': 'boolean', 'value': True}, 'wasThrown': False}}
|
2014-05-05 19:58:41 -07:00
|
|
|
# {'id': 59, 'result': {'result': {'type': 'boolean', 'value': False}}
|
2014-05-04 21:33:13 -07:00
|
|
|
self.waiting_result_msg_ids.remove(chrome_message['id'])
|
|
|
|
if ('result' in chrome_message
|
2014-05-05 19:58:41 -07:00
|
|
|
and not ('wasThrown' in chrome_message['result'] and chrome_message['result']['wasThrown'])
|
2014-05-04 21:33:13 -07:00
|
|
|
and 'result' in chrome_message['result']
|
|
|
|
and type(chrome_message['result']['result']['value']) == bool):
|
|
|
|
self.script_finished = chrome_message['result']['result']['value']
|
|
|
|
else:
|
|
|
|
self.logger.error("chrome message doesn't look like a boolean result! {}".format(chrome_message))
|
|
|
|
|
|
|
|
def notify_of_activity(self):
|
|
|
|
self.last_activity = time.time()
|
|
|
|
|
2014-05-05 11:58:55 -07:00
|
|
|
if __name__ == "__main__":
|
|
|
|
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
|
|
|
|
format='%(asctime)s %(process)d %(levelname)s %(threadName)s %(name)s.%(funcName)s(%(filename)s:%(lineno)d) %(message)s')
|
|
|
|
logger = logging.getLogger('umbra.behaviors')
|
|
|
|
logger.info("custom behaviors: {}".format(Behavior.behaviors()))
|
|
|
|
|
2014-02-13 01:00:39 -05:00
|
|
|
|