timeline events starting to work
diff --git a/weinre.web/demo/weinre-demo-pieces.html b/weinre.web/demo/weinre-demo-pieces.html
index c3ab90d..3d764a5 100644
--- a/weinre.web/demo/weinre-demo-pieces.html
+++ b/weinre.web/demo/weinre-demo-pieces.html
@@ -20,6 +20,8 @@
 <script src="/weinre/common/Binding.transportd.js"></script>
 <script src="/weinre/common/Callback.transportd.js"></script>
 <script src="/weinre/common/EventListeners.transportd.js"></script>
+<script src="/weinre/common/Native.transportd.js"></script>
+<script src="/weinre/common/IDGenerator.transportd.js"></script>
 <script src="/weinre/target/Console.transportd.js"></script>
 <script src="/add-css-properties.js"></script>
 <script src="/weinre/target/WiConsoleImpl.transportd.js"></script>
@@ -36,6 +38,7 @@
 <script src="/weinre/target/Target.transportd.js"></script>
 <script src="/weinre/target/ElementHighlighter.transportd.js"></script>
 <script src="/weinre/target/InjectedScript.js"></script>
+<script src="/weinre/target/Timeline.transportd.js"></script>
 <script src="/interfaces/all-json-idls.js"></script>
 
 <script type="text/javascript">
@@ -43,6 +46,46 @@
     require("weinre/target/Target").main()
 </script>
 
+<script>
+var started = false
+var button
+
+function onLoad() {
+    if (!button) button = document.getElementById("button")
+    
+    button.addEventListener("click", function() {
+        if (!started) {
+            button.value = "stop stuff"
+            startStuff()
+        }
+        else {
+            button.value = "start stuff"
+            stopStuff()      
+        }
+        started = !started
+    })
+}
+
+var interval
+
+function startStuff() {
+    interval = setInterval(intervalStuff, 1000)
+}
+
+function stopStuff() {
+    clearInterval(interval)
+}
+
+function intervalStuff() {
+    setTimeout(function() { console.log("doing interval stuff")}, 333)
+    
+    var xhr = new XMLHttpRequest()
+    xhr.open("GET", "../target/target-script.js", true)
+    xhr.send()
+}
+
+</script>
+
 <style>
 h1 {
     color: green;
@@ -57,10 +100,16 @@
     border:   0.2em solid;
     padding:  3em;
 }
+
+#button {
+    font-size: 150%;
+}
 </style>
 </head>
 
-<body>
+<body onload="onLoad()">
+<input id="button" type="button" value="start stuff">
+
 <h1>this is a green h1 element</h1>
 <h1 class="blue">this is a blue h1 element</h1>
 <h1 style="color:red">this is a red h1 element</h1>
diff --git a/weinre.web/modules/weinre/client/Client.scoop b/weinre.web/modules/weinre/client/Client.scoop
index 9bebe40..8123edf 100644
--- a/weinre.web/modules/weinre/client/Client.scoop
+++ b/weinre.web/modules/weinre/client/Client.scoop
@@ -6,6 +6,8 @@
  * Copyright (c) 2010, 2011 IBM Corporation
  */
 
+requireClass ../common/Native
+
 requireClass ../common/IDLTools
 requireClass ../common/Callback
 requireClass ../common/Weinre
@@ -74,8 +76,8 @@
     
     var toolButtonsToHide = ["scripts"]
     toolButtonsToHide.forEach(function(toolButtonToHide){
-        if (!WebInspector.panels[toolButtonToHide]) continue
-        if (!WebInspector.panels[toolButtonToHide].toolbarItem) continue
+        if (!WebInspector.panels[toolButtonToHide]) return
+        if (!WebInspector.panels[toolButtonToHide].toolbarItem) return
         WebInspector.panels[toolButtonToHide].toolbarItem.style.display = "none"
     })
     
@@ -114,7 +116,7 @@
     
     // use a delay, otherwise reloading will cause this stuff to flash
     // before page is actually reloaded
-    setTimeout(function() {
+    Native.setTimeout(function() {
         WebInspector.panels.remote.connectionClosed()
         WebInspector.currentPanel = WebInspector.panels.remote 
     }, 1000)
diff --git a/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop b/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
index f4aeb73..a39ee09 100644
--- a/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
+++ b/weinre.web/modules/weinre/client/InspectorBackendImpl.scoop
@@ -65,7 +65,9 @@
 //-----------------------------------------------------------------------------
 method registerDomainDispatcher(name, intf)
     this.registeredDomainDispatchers[name] = intf
-
+    
 //-----------------------------------------------------------------------------
 method getRegisteredDomainDispatcher(name)
+    if (!this.registeredDomainDispatchers.hasOwnProperty(name)) return null
+    
     return this.registeredDomainDispatchers[name]
diff --git a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
index f0d0e17..b4514b6 100644
--- a/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
+++ b/weinre.web/modules/weinre/client/InspectorFrontendHostImpl.scoop
@@ -23,7 +23,7 @@
 
 //-----------------------------------------------------------------------------
 method hiddenPanels
-    return "audits,profiles,resources,timeline,network"
+    return "audits,profiles,resources,network"
 //    return "audits,profiles,resources,scripts,timeline,network"
 
 //-----------------------------------------------------------------------------
diff --git a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
index 1dc8b3c..f84f61f 100644
--- a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
+++ b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
@@ -51,6 +51,9 @@
     if (clientId != Weinre.clientId) return
 
     Weinre.targetId = targetId
+    
+    WebInspector.panels.elements.reset()
+    WebInspector.panels.timeline._clearPanel()
 
 //-----------------------------------------------------------------------------
 method connectionDestroyed(/*int*/ clientId, /*int*/ targetId)
diff --git a/weinre.web/modules/weinre/common/IDGenerator.scoop b/weinre.web/modules/weinre/common/IDGenerator.scoop
new file mode 100644
index 0000000..0b91770
--- /dev/null
+++ b/weinre.web/modules/weinre/common/IDGenerator.scoop
@@ -0,0 +1,20 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+//-----------------------------------------------------------------------------
+class IDGenerator
+
+//-----------------------------------------------------------------------------
+init
+    var nextId = 1
+
+//-----------------------------------------------------------------------------
+static method next
+    var result = nextId
+    nextId += 1
+    return result
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/common/MessageDispatcher.scoop b/weinre.web/modules/weinre/common/MessageDispatcher.scoop
index 038c43a..71b7b36 100644
--- a/weinre.web/modules/weinre/common/MessageDispatcher.scoop
+++ b/weinre.web/modules/weinre/common/MessageDispatcher.scoop
@@ -156,7 +156,8 @@
 
     var methodSignature = intfName + "." + methodName + "()"
     
-    var intf = this._interfaces[intfName]
+    var intf = this._interfaces.hasOwnProperty(intfName) && this._interfaces[intfName]
+    
     if (!intf && InspectorBackend && intfName.match(/.*Notify/)) {
         intf = InspectorBackend.getRegisteredDomainDispatcher(intfName.substr(0,intfName.length-6))
     }
diff --git a/weinre.web/modules/weinre/common/Native.scoop b/weinre.web/modules/weinre/common/Native.scoop
new file mode 100644
index 0000000..cc8ce08
--- /dev/null
+++ b/weinre.web/modules/weinre/common/Native.scoop
@@ -0,0 +1,29 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+//-----------------------------------------------------------------------------
+class Native
+
+//-----------------------------------------------------------------------------
+init
+    Native.original = {}
+
+    Native.original.clearInterval       = window.clearInterval
+    Native.original.clearTimeout        = window.clearTimeout
+    Native.original.setTimeout          = window.setTimeout
+    Native.original.setInterval         = window.setInterval
+    Native.original.XMLHttpRequest      = window.XMLHttpRequest
+    Native.original.XMLHttpRequest_open = window.XMLHttpRequest.prototype.open
+
+    Native.clearInterval       = function() { return Native.original.clearInterval.apply( window, [].slice.call(arguments))}
+    Native.clearTimeout        = function() { return Native.original.clearTimeout.apply(  window, [].slice.call(arguments))}
+    Native.setInterval         = function() { return Native.original.setInterval.apply(   window, [].slice.call(arguments))}
+    Native.setTimeout          = function() { return Native.original.setTimeout.apply(    window, [].slice.call(arguments))}
+    Native.XMLHttpRequest      = function() { return new Native.original.XMLHttpRequest()}
+    Native.XMLHttpRequest_open = function() { return Native.original.XMLHttpRequest_open.apply(this, [].slice.call(arguments))}
+    
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/common/WebSocketXhr.scoop b/weinre.web/modules/weinre/common/WebSocketXhr.scoop
index b270ffd..ce6b6c3 100644
--- a/weinre.web/modules/weinre/common/WebSocketXhr.scoop
+++ b/weinre.web/modules/weinre/common/WebSocketXhr.scoop
@@ -9,6 +9,7 @@
 requireClass ./Ex
 requireClass ./Weinre
 requireClass ./EventListeners
+requireClass ./Native
 
 //-----------------------------------------------------------------------------
 class WebSocketXhr(url)
@@ -16,6 +17,8 @@
     
 //-----------------------------------------------------------------------------
 init
+    var XMLHttpRequest = Native.XMLHttpRequest
+    
     WebSocketXhr.CONNECTING = 0
     WebSocketXhr.OPEN       = 1
     WebSocketXhr.CLOSING    = 2
@@ -98,7 +101,7 @@
         return
     }
     
-    window.setTimeout(function() {self._readLoop()}, 0)
+    Native.setTimeout(function() {self._readLoop()}, 0)
 
     datum.forEach(function(data) {
         self._fireEventListeners("message", {data: data})
@@ -133,7 +136,7 @@
     if (xhr.status != 200) return this._handleXhrResponseError(xhr)
     
     this._sendInProgress = false
-    window.setTimeout(function() {httpSocket._sendQueued()}, 0)
+    Native.setTimeout(function() {httpSocket._sendQueued()}, 0)
 
 //-----------------------------------------------------------------------------
 method close
diff --git a/weinre.web/modules/weinre/target/Console.scoop b/weinre.web/modules/weinre/target/Console.scoop
index 54e85ae..ca94ad9 100644
--- a/weinre.web/modules/weinre/target/Console.scoop
+++ b/weinre.web/modules/weinre/target/Console.scoop
@@ -7,6 +7,7 @@
  */
 
 requireClass ../common/Weinre
+requireClass ../target/Timeline
 
 //-----------------------------------------------------------------------------
 class Console
@@ -121,8 +122,8 @@
     Weinre.notImplemented(arguments.callee.signature)
     
 //-----------------------------------------------------------------------------
-method markTimeline
-    Weinre.notImplemented(arguments.callee.signature)
+method markTimeline(message)
+    Timeline.addRecord_Mark(message)
     
 //-----------------------------------------------------------------------------
 method lastWMLErrorMessage
diff --git a/weinre.web/modules/weinre/target/Target.scoop b/weinre.web/modules/weinre/target/Target.scoop
index 9109f1f..8aede78 100644
--- a/weinre.web/modules/weinre/target/Target.scoop
+++ b/weinre.web/modules/weinre/target/Target.scoop
@@ -6,6 +6,8 @@
  * Copyright (c) 2010, 2011 IBM Corporation
  */
 
+requireClass ../common/Native
+
 requireClass ../common/Ex
 requireClass ../common/Binding
 requireClass ../common/Callback
diff --git a/weinre.web/modules/weinre/target/Timeline.scoop b/weinre.web/modules/weinre/target/Timeline.scoop
new file mode 100644
index 0000000..0d59b7a
--- /dev/null
+++ b/weinre.web/modules/weinre/target/Timeline.scoop
@@ -0,0 +1,257 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+requireClass ../common/Ex
+requireClass ../common/Weinre
+
+requireClass ../common/Native
+
+//-----------------------------------------------------------------------------
+class Timeline
+
+//-----------------------------------------------------------------------------
+static method start
+    Running = true
+
+//-----------------------------------------------------------------------------
+static method stop
+    Running = false
+
+//-----------------------------------------------------------------------------
+static method isRunning
+    return Running
+
+//-----------------------------------------------------------------------------
+static method addRecord_Mark(message)
+    if (!Timeline.isRunning()) return
+    
+    var record = {}
+
+    record.type      = TimelineRecordType.Mark
+    record.category  = { name: "scripting" }
+    record.startTime = Date.now()
+    record.data      = { message: message }
+    
+    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
+
+//-----------------------------------------------------------------------------
+static method addRecord_EventDispatch(event, name, category)
+    if (!Timeline.isRunning()) return
+    
+    if (!category) category = "scripting"
+    var record = {}
+
+    record.type      = TimelineRecordType.EventDispatch
+    record.category  = { name: category }
+    record.startTime = Date.now()
+    record.data      = { type: event.type }
+    
+    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
+
+//-----------------------------------------------------------------------------
+static method addRecord_TimerInstall(id, timeout, singleShot)
+    if (!Timeline.isRunning()) return
+    
+    var record = {}
+
+    record.type      = TimelineRecordType.TimerInstall
+    record.category  = { name: "scripting" }
+    record.startTime = Date.now()
+    record.data      = { timerId: id, timeout: timeout, singleShot: singleShot }
+    
+    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
+
+//-----------------------------------------------------------------------------
+static method addRecord_TimerRemove(id, timeout, singleShot)
+    if (!Timeline.isRunning()) return
+
+    var record = {}
+
+    record.type      = TimelineRecordType.TimerRemove
+    record.category  = { name: "scripting" }
+    record.startTime = Date.now()
+    record.data      = { timerId: id, timeout: timeout, singleShot: singleShot }
+    
+    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
+
+//-----------------------------------------------------------------------------
+static method addRecord_XHRReadyStateChange(url, readyState)
+    if (!Timeline.isRunning()) return
+
+    var record = {}
+
+    record.type      = TimelineRecordType.XHRReadyStateChange
+    record.category  = { name: "scripting" }
+    record.startTime = Date.now()
+    record.data      = { url: url, readyState: readyState }
+    
+    Weinre.wi.TimelineNotify.addRecordToTimeline(record)
+
+//-----------------------------------------------------------------------------
+static method addRecord_TimerFire(id, timeout, singleShot)
+    
+//-----------------------------------------------------------------------------
+static method installGlobalListeners
+    if (applicationCache) {
+        applicationCache.addEventListener("checking",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.checking",    "loading")}, false)
+        applicationCache.addEventListener("error",       function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.error",       "loading")}, false)
+        applicationCache.addEventListener("noupdate",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.noupdate",    "loading")}, false)
+        applicationCache.addEventListener("downloading", function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.downloading", "loading")}, false)
+        applicationCache.addEventListener("progress",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.progress",    "loading")}, false)
+        applicationCache.addEventListener("updateready", function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.updateready", "loading")}, false)
+        applicationCache.addEventListener("cached",      function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.cached",      "loading")}, false)
+        applicationCache.addEventListener("obsolete",    function(e) {Timeline.addRecord_EventDispatch(e, "applicationCache.obsolete",    "loading")}, false)
+    }
+    
+    // window.addEventListener("deviceorientation", function(e) {Timeline.addRecord_EventDispatch("window.deviceorientation")}, false)
+    window.addEventListener("error",             function(e) {Timeline.addRecord_EventDispatch(e, "window.error")},             false)
+    window.addEventListener("hashchange",        function(e) {Timeline.addRecord_EventDispatch(e, "window.hashchange")},        false)
+    window.addEventListener("message",           function(e) {Timeline.addRecord_EventDispatch(e, "window.message")},           false)
+    window.addEventListener("offline",           function(e) {Timeline.addRecord_EventDispatch(e, "window.offline")},           false)
+    window.addEventListener("online",            function(e) {Timeline.addRecord_EventDispatch(e, "window.online")},            false)
+    window.addEventListener("scroll",            function(e) {Timeline.addRecord_EventDispatch(e, "window.scroll")},            false)
+    
+//-----------------------------------------------------------------------------
+static method installFunctionWrappers
+    window.clearInterval  = wrapped_clearInterval
+    window.clearTimeout   = wrapped_clearTimeout
+    window.setTimeout     = wrapped_setTimeout
+    window.setInterval    = wrapped_setInterval
+    
+    window.XMLHttpRequest.prototype.open = wrapped_XMLHttpRequest_open
+    window.XMLHttpRequest                = wrapped_XMLHttpRequest
+        
+
+//-----------------------------------------------------------------------------
+function wrapped_setInterval(code, interval)
+    var code = instrumentedTimerCode(code, interval, false)
+    var id = Native.setInterval(code, interval)
+    
+    code.__timerId = id
+    
+    addTimer(id, interval, false)
+
+    return id
+
+//-----------------------------------------------------------------------------
+function wrapped_setTimeout(code, delay)
+    var code = instrumentedTimerCode(code, delay, true)
+    var id   = Native.setTimeout(code, delay)
+
+    code.__timerId = id
+    
+    addTimer(id, delay, true)
+
+    return id
+
+//-----------------------------------------------------------------------------
+function wrapped_clearInterval(id)
+    var result = Native.clearInterval(id)
+    
+    removeTimer(id, false)
+    return result
+
+//-----------------------------------------------------------------------------
+function wrapped_clearTimeout(id)
+    var result = Native.clearTimeout(id)
+    
+    removeTimer(id, true)
+    return result
+
+//-----------------------------------------------------------------------------
+function addTimer(id, timeout, singleShot)
+    var timerSet = singleShot ? TimerTimeouts : TimerIntervals
+    
+    timerSet[id] = {
+         id:          id,
+         timeout:     timeout,
+         singleShot: singleShot
+    }
+    
+    Timeline.addRecord_TimerInstall(id, timeout, singleShot)
+    
+//-----------------------------------------------------------------------------
+function removeTimer(id, singleShot)
+    var timerSet = singleShot ? TimerTimeouts : TimerIntervals
+    var timer = timerSet[id]
+    
+    if (!timer) return
+    
+    Timeline.addRecord_TimerRemove(id, timer.timeout, singleShot)
+    
+    delete timerSet[id]
+    
+//-----------------------------------------------------------------------------
+function instrumentedTimerCode(code, timeout, singleShot)
+    if (typeof(code) != "function") return code
+    
+    var instrumentedCode = function() {
+        var result = code()
+        var id     = arguments.callee.__timerId
+
+        Timeline.addRecord_TimerFire(id, timeout, singleShot)
+
+        return result
+    }
+    
+    return instrumentedCode 
+
+//-----------------------------------------------------------------------------
+function wrapped_XMLHttpRequest
+    var xhr = new Native.XMLHttpRequest()
+    xhr.addEventListener("readystatechange", getXhrEventHandler(xhr), false)
+    return xhr
+
+//-----------------------------------------------------------------------------
+function wrapped_XMLHttpRequest_open()
+    var xhr    = this
+    var result = Native.XMLHttpRequest_open.apply(xhr, [].slice.call(arguments))
+    xhr.__weinre_url  = arguments[1]
+    return result
+
+    
+//-----------------------------------------------------------------------------
+function getXhrEventHandler(xhr)
+    return function(event) {
+        Timeline.addRecord_XHRReadyStateChange(xhr.__weinre_url, xhr.readyState)
+    }
+    
+//-----------------------------------------------------------------------------
+init 
+    var Running = false
+    
+    var TimerTimeouts  = {}
+    var TimerIntervals = {}
+
+    var TimelineRecordType = {
+            EventDispatch:            0,
+            Layout:                   1,
+            RecalculateStyles:        2,
+            Paint:                    3,
+            ParseHTML:                4,
+            TimerInstall:             5,
+            TimerRemove:              6,
+            TimerFire:                7,
+            XHRReadyStateChange:      8,
+            XHRLoad:                  9,
+            EvaluateScript:          10,
+            Mark:                    11,
+            ResourceSendRequest:     12,
+            ResourceReceiveResponse: 13,
+            ResourceFinish:          14,
+            FunctionCall:            15,
+            ReceiveResourceData:     16,
+            GCEvent:                 17,
+            MarkDOMContent:          18,
+            MarkLoad:                19,
+            ScheduleResourceRequest: 20
+    }
+
+    Timeline.installGlobalListeners()
+    Timeline.installFunctionWrappers()
+    
\ No newline at end of file
diff --git a/weinre.web/modules/weinre/target/WiInspectorImpl.scoop b/weinre.web/modules/weinre/target/WiInspectorImpl.scoop
index 26bdab9..098e58b 100644
--- a/weinre.web/modules/weinre/target/WiInspectorImpl.scoop
+++ b/weinre.web/modules/weinre/target/WiInspectorImpl.scoop
@@ -7,6 +7,7 @@
  */
 
 requireClass ../common/Weinre
+requireClass ../target/Timeline
 
 //-----------------------------------------------------------------------------
 class WiInspectorImpl
@@ -41,3 +42,24 @@
         Weinre.WeinreTargetCommands.sendClientCallback(callback)
     }
 
+//-----------------------------------------------------------------------------
+method startTimelineProfiler(callback)
+    Timeline.start()
+    
+    Weinre.wi.TimelineNotify.timelineProfilerWasStarted()
+
+    if (callback) {
+        Weinre.WeinreTargetCommands.sendClientCallback(callback)
+    }
+
+
+//-----------------------------------------------------------------------------
+method stopTimelineProfiler(callback)
+    Timeline.stop()
+    
+    Weinre.wi.TimelineNotify.timelineProfilerWasStopped()
+
+    if (callback) {
+        Weinre.WeinreTargetCommands.sendClientCallback(callback)
+    }
+