                                (marker-position (process-mark process)))
           with end-time = (+ (float-time) timeout)
           for time-left = (- end-time (float-time))
+          do (goto-char (point-max))
           if (looking-back regexp start-pos) return t
           while (> time-left 0)
           do (accept-process-output process time-left nil t)

                           espresso--available-frameworks))
  :group 'espresso)

;;; KeyMap

(defvar espresso-mode-map


(defun espresso-js-eval (js)
  "Evaluate the Javascript contained in JS and return the
JSON-decoded result. eval must be called using this function
because it is special to the Javascript interpreter."
  (interactive "MJavascript to evaluate: ")
  (with-espresso-js
   (let* ((context (espresso--get-js-context))
          (content-window (ecase (car context)
                            (window (cdr context))
                            (browser (js< (cdr context)
                                          "contentWindow" "wrappedJSObject"))))
          (result (js-eval content-window js)))

     (when (interactive-p)
       (message "%s" (js! "String" result)))
     result)))

(defun espresso--get-tabs ()
  "Enumerate all the contexts available. Each one is a list:

   The list is (TITLE URL BROWSER TAB TABBROWSER) for content documents
   The list is (TITLE URL WINDOW) for windows

   All tabs of a given window are grouped together. The most
   recent window is first. Within each window, the tabs are
   returned left-to-right.
"
  (with-espresso-js
   (let (windows)

     (loop with window-mediator = (js! ("Components" "classes"
                                        "@mozilla.org/appshell/window-mediator;1"
                                        "getService")
                                       (js< "Components" "interfaces"
                                            "nsIWindowMediator"))
           with enumerator = (js! (window-mediator "getEnumerator") nil)

           while (js? (js! (enumerator "hasMoreElements")))
           for window = (js! (enumerator "getNext"))
           for window-info = (js-list window
                                      (js< window "document" "title")
                                      (js! (window "location" "toString"))
                                      (js< window "closed")
                                      (js< window "windowState"))

           unless (or (js? (fourth window-info))
                      (eq (fifth window-info) 2))
           do (push window-info windows))

     (loop for window-info in windows
           for window = (first window-info)
           collect (list (second window-info)
                         (third window-info)
                         window)

           for gbrowser = (js< window "gBrowser")
           if (js-handle? gbrowser)
           nconc (loop
                  for x below (js< gbrowser "browsers" "length")
                  collect (js-list (js< gbrowser
                                        "browsers"
                                        x
                                        "contentDocument"
                                        "title")

                                   (js! (gbrowser
                                         "browsers"
                                         x
                                         "contentWindow"
                                         "location"
                                         "toString"))
                                   (js< gbrowser
                                        "browsers"
                                        x)

                                   (js! (gbrowser
                                         "tabContainer"
                                         "childNodes"
                                         "item")
                                        x)

                                   gbrowser))))))

(defvar espresso-read-tab-history nil)

(defun espresso--read-tab (prompt)
  "Read a Mozilla tab with prompt PROMPT. Return a cons of (TYPE . OBJECT).
TYPE is either 'window or 'tab, and OBJECT is a Javascript handle
to a ChromeWindow or a browser, respectively."

  ;; Prime IDO
  (unless ido-mode
    (ido-mode t)
    (ido-mode nil))

  (with-espresso-js
   (lexical-let ((tabs (espresso--get-tabs)) selected-tab-name selected-tab
                 prev-hitab)

     ;; Disambiguate names
     (setq tabs (loop with tab-names = (make-hash-table :test 'equal)
                      for tab in tabs
                      for cname = (format "%s (%s)" (second tab) (first tab))
                      for num = (incf (gethash cname tab-names -1))
                      if (> num 0)
                      do (setq cname (format "%s <%d>" cname num))
                      collect (cons cname tab)))

     (labels ((find-tab-by-cname
               (cname)
               (loop for tab in tabs
                     if (equal (car tab) cname)
                     return (cdr tab)))

              (mogrify-highlighting
               (hitab unhitab)

               ;; Hack to reduce the number of
               ;; round-trips to mozilla
               (let (cmds)
                 (cond
                  ;; Highlighting tab
                  ((fourth hitab)
                   (push '(js! ((fourth hitab) "setAttribute")
                               "style"
                               "color: red; font-weight: bold")
                         cmds)

                   ;; Highlight window proper
                   (push '(js! ((third hitab)
                                "setAttribute")
                               "style"
                               "border: 8px solid red")
                         cmds)

                   ;; Select tab, when appropriate
                   (when espresso-js-switch-tabs
                     (push
                      '(js> ((fifth hitab) "selectedTab") (fourth hitab))
                      cmds)))

                  ;; Hilighting whole window
                  ((third hitab)
                   (push '(js! ((third hitab) "document"
                                "documentElement" "setAttribute")
                               "style"
                               (concat "-moz-appearance: none;"
                                       "border: 8px solid red;"))
                         cmds)))

                 (cond
                  ;; Unhighlighting tab
                  ((fourth unhitab)
                   (push '(js! ((fourth unhitab) "setAttribute") "style" "")
                         cmds)
                   (push '(js! ((third unhitab) "setAttribute") "style" "")
                         cmds))

                  ;; Unhighlighting window
                  ((third unhitab)
                   (push '(js! ((third unhitab) "document"
                                "documentElement" "setAttribute")
                               "style" "")
                         cmds)))

                 (eval (list 'with-espresso-js
                             (cons 'js-list (nreverse cmds))))))

              (command-hook
               ()
               (let* ((tab (find-tab-by-cname (car ido-matches))))
                 (mogrify-highlighting tab prev-hitab)
                 (setq prev-hitab tab)))

              (setup-hook
               ()
               ;; Fiddle with the match list a bit: if our first match
               ;; is a tabbrowser window, rotate the match list until
               ;; the active tab comes up
               (let ((matched-tab (find-tab-by-cname (car ido-matches))))
                 (when (and matched-tab
                            (null (fourth matched-tab))
                            (equal "navigator:browser"
                                   (js! ((third matched-tab)
                                         "document"
                                         "documentElement"
                                         "getAttribute")
                                        "windowtype")))

                   (loop with tab-to-match = (js< (third matched-tab)
                                                  "gBrowser"
                                                  "selectedTab")

                         with index = 0
                         for match in ido-matches
                         for candidate-tab = (find-tab-by-cname match)
                         if (eq (fourth candidate-tab) tab-to-match)
                         do (setq ido-cur-list (ido-chop ido-cur-list match))
                         and return t)))

               (add-hook 'post-command-hook #'command-hook t t)))


       (unwind-protect
           (setq selected-tab-cname
                 (let ((ido-minibuffer-setup-hook
                        (cons #'setup-hook ido-minibuffer-setup-hook)))
                   (ido-completing-read
                    prompt
                    (mapcar #'car tabs)
                    nil t nil
                    'espresso-read-tab-history)))

         (when prev-hitab
           (mogrify-highlighting nil prev-hitab)
           (setq prev-hitab nil)))

       (add-to-history 'espresso-read-tab-history selected-tab-cname)

       (setq selected-tab (loop for tab in tabs
                                if (equal (car tab) selected-tab-cname)
                                return (cdr tab)))

       (if (fourth selected-tab)
           (cons 'browser (third selected-tab))
         (cons 'window (third selected-tab)))))))

(defun espresso--guess-eval-defun-info (pstate)
  "Internal helper for `espresso-eval-defun'. Returns a list (NAME . CLASSPARTS),
