Browser_Dialog

Browser_Dialog is a tool for debugging dynamic content in WebDialogs.

The Problem

It is possible to point a web browser at the static files used for a WebDialog, then use the browser's built-in and add-on facilities to look for syntax errors, etc. However, this does not provide some key debugging capabilities:

  • debugging of Ruby callback interactions

  • inspection of dynamically loaded content

  • JavaScript logging, ala console.log()

Let's assume that a plugin loads several hundred lines of HTML into a tab, then tells jQuery to set up event handlers, etc. Debugging this could be quite difficult without the support tools found in modern web browsers (eg, Chrome, Firefox, Safari).

Workarounds

Until such time as SketchUp provides browser-based support for dialogs, there will be a need for a user-installable workaround. At least three possibilities exist:

  • JavaScript-based debugging in a WebDialog

    Firebug Lite is a portable, JavaScript-based debugger that claims to work with "all major browsers". This could be a very attractive, lightweight solution for debugging, if there aren't any critical bugs or limitations. I am told that Firebug Lite can be used with WebDialogs, but I don't know the exact procedure; please let me know if you have any information!

  • Running plugins in a SketchUp emulator

    A Ruby-based web application framework such as Ruby on Rails or Sinatra could emulate portions of the SketchUp API. Using some sort of "push" technology, browser-based debugging and/or testing could then be supported. Emulating even a critical subset of SketchUp's API is not trivial, but I have heard of some early efforts in this direction.

  • Emulating WebDialogs using a proxy server

    This is the approach that Browser_Dialog takes. A proxy server, acting as a protocol converter of sorts, receives message files from the plugin and forwards them to the client code using WebSockets. Returning messages use corresponding communication paths. Again, this approach supports browser-based debugging and/or testing.

Usage Overview

At run time (based on the :bd_proxy setting in the sa_config.rb file), the Plugin Framework creates an instance of the Browser_Dialog class (rather than SketchUp's usual UI::WebDialog class) for the plugin.

Once the dialog instance has been created, the plugin uses it in (approximately) the normal manner. The main restriction, at this point, is that Browser_Dialog only supports a few UI::WebDialog methods (add_action_callback, execute_script, set_file, and show_modal).

These are the only methods my current set of plugins have needed, but I expect to add support for others (eg, get_element_value, show) over time. Feel free to implement and contribute your own!

Like the plugin code, the client-side code must also be modified slightly. Specifically, it must use a set of wrapper methods for communication. For example, jQuery event handlers use callback(), rather than setting the location variable directly.

After the needed modifications are in place, the developer:

  • starts the proxy server (bd_proxy)

  • starts up SketchUp (Pro)

  • invokes the plugin, using the normal menu item

  • browses to file:///...html?8080

Note: Because we are not redefining UI::WebDialog, we can use either class to create a given dialog. Thus, we can reserve Browser_Dialog instances for dialogs which are currently being debugged. We may also be able to use some UI::WebDialog methods on Browser_Dialog objects.

Implementation

WebDialogs provide (and plugins expect) asynchronous, bi-directional, message-based communication. To support this, we need to address (or avoid) a few problems:

  • To handle message-based communication, stream-based protocols
    (eg, named pipes, sockets) require encapsulation support.

  • SketchUp doesn't provide plugins with a general way to use sockets,
    so we need an external proxy server of some sort.

  • Web clients normally expect to "pull" data from a server,
    so our proxy will need to implement server push technology.

I looked into several ways of handling these problems, eg:

  • UI::WebDialog#post_url can POST a message to an arbitrary URL.
    A proxy could then forward the message to the client.

  • If JavaScript's security provisions can be worked around,
    WebDialog-based client code could proxy messages for a plugin.

  • Named pipes enable buffered, uni-directional data streams,
    but plugins and proxy servers can access them using file I/O.

  • A normal file can be used to store each message.

Ultimately, I opted to go with the last of these approaches. Normal files are well supported and easy to use on all operating systems of interest. Given that we're only supporting one user and running on a desktop, latency and bandwidth do not present any problems.

Also, files are well suited to handling discrete messages. They can be "published" atomically (eg, by File#rename) and I don't need to worry about message encapsulation.

Data Flow

The top part of the diagram below should be quite familiar. The plugin creates some base files (eg, CSS, HTML, JavaScript) which are loaded by a dialog and become the "client code". The middle and bottom parts of the diagram vary, however, between WebDialog and Browser_Dialog.

SketchUp's WebDialogs provide asynchronous data paths between the plugin and client code. Browser_Dialog emulates these paths, using message files, a proxy server, and a WebSocket. Browser_Dialog's library code handles the details of dealing with message files (in the plugin) and WebSockets (in the client).

Plugin Support

Outgoing messages are easy to handle: we simply create a file for each message. Incoming messages are harder, because they can arrive at any time. However, a sub-second timer (supported in SketchUp 8), allows Browser_Dialog to check frequently for incoming messages:

@timer_id = UI.start_timer(0.05, true) do
  paths = Dir.glob(patt)
  paths.each {|path| process_input(path) } unless paths.empty?
end

Proxy Server

Using EventMachine, I wrote a simple proxy server which watches an input directory for activity. The server forwards each outgoing message to the client via WebSockets.

Similarly, incoming WebSocket messages are turned into message files. To support follow-on analysis, while minimizing disk overhead, the proxy server collects the incoming and outgoing messages for each (dialog-specific) session into a pair of log files.

Status

I have a set of code that runs in the Plugin Framework. I have run three substantial plugins with it, using Safari. Chrome should also work. Firefox will not work, by default, because it disables WebSockets for security reasons (see "WebSockets and Firefox", below).

At this point, I'm looking for some other plugin developers who would like to try out the code, look it over for problems, etc. I'm particularly interested in finding testers for Linux and M$Windows.

Details

Resources

EventMachine

I strongly recommend watching the video of Aman Gupta's EventMachine talk, given at Mountain West Ruby Conference in 2010.

WebSockets

WebSockets and Firefox

A recent paper, Transparent Proxies: Threat or Menace?, details security problems with socket-based communication schemes (as used by Flash Player, Java, and WebSockets). In response, some browser providers (eg, Mozilla) have suspended WebSocket support.

It is possible to re-enable WebSockets by setting preferences, but this leaves Firefox in a potentially vulnerable state. YMMV!


This wiki page is maintained by Rich Morin, an independent consultant specializing in software design, development, and documentation. Please feel free to email comments, inquiries, suggestions, etc!

Topic revision: r25 - 26 May 2012, RichMorin
This site is powered by Foswiki Copyright © by the contributing authors. All material on this wiki is the property of the contributing authors.
Foswiki version v2.1.6, Release Foswiki-2.1.6, Plugin API version 2.4
Ideas, requests, problems regarding CFCL Wiki? Send us email