<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>An Experiment in Bloggery &#187; osax</title>
	<atom:link href="http://kevin.sb.org/tag/osax/feed/" rel="self" type="application/rss+xml" />
	<link>http://kevin.sb.org</link>
	<description>The occasional view into my life</description>
	<lastBuildDate>Fri, 09 Sep 2011 00:19:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>1Password extension loading in Snow Leopard</title>
		<link>http://kevin.sb.org/2009/09/02/1password-extension-loading-in-snow-leopard/</link>
		<comments>http://kevin.sb.org/2009/09/02/1password-extension-loading-in-snow-leopard/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 22:11:11 +0000</pubDate>
		<dc:creator>Kevin Ballard</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[1Password]]></category>
		<category><![CDATA[64bit]]></category>
		<category><![CDATA[osax]]></category>
		<category><![CDATA[safari]]></category>
		<category><![CDATA[Snow Leopard]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://kevin.sb.org/?p=201</guid>
		<description><![CDATA[One of the big changes in Snow Leopard is the move to 64-bit applications system-wide. This includes Safari. Unfortunately, this change breaks all of the Safari plugins out there, including mine. There&#8217;s two reasons for this. The first is simply that these plugins are all 32-bit binaries, and a 64-bit app cannot load a 32-bit [...]]]></description>
			<content:encoded><![CDATA[<p>One of the big changes in Snow Leopard is the move to 64-bit applications system-wide. This includes Safari. Unfortunately, this change breaks all of the Safari plugins out there, including mine. There&#8217;s two reasons for this. The first is simply that these plugins are all 32-bit binaries, and a 64-bit app cannot load a 32-bit binary. The second, and significantly harder obstacle, is that the entire Input Manager mechanism has been eliminated in 64-bit apps.</p>

<p>Read more to find out how 1Password gets around these limitations.</p>

<p><span id="more-201"></span></p>

<h4>A bit of history</h4>

<p>When Cocoa was introduced, one of the behaviors that every Cocoa application automatically acquired was the loading of Input Managers. These Input Managers were intended to allow developers to extend the text input system of OS X in ways that the system did not provide by default. However, these Input Managers were really nothing more than Cocoa bundles that got loaded by every single Cocoa app at launch. This means that it was very quickly abused to become a general plugin mechanism for applications that do not natively support plugins (such as Safari). In recent OS updates, Apple has been deprecating this mechanism, and now in Snow Leopard it&#8217;s completely gone for 64-bit apps.</p>

<h4>How can I use Safari plugins?</h4>

<p>The simplest way is to simply right-click Safari in the Finder, select Get Info, and check the Open in 32-bit mode checkbox. However, this has the downside of removing all of the performance benefits of 64-bit apps. Besides the normal benefits of an improved instruction set and a more modern Objective-C runtime, Safari&#8217;s JavaScript engine particularly benefits from running in 64-bit mode. So while this is a stopgap measure, it isn&#8217;t suitable for long-term use.</p>

<p>Luckily, the smart folks who make <a href="http://agilewebsolutions.com/products/1Password" title="Agile Web Solutions">1Password</a> came up with a solution for their upcoming 1Password 3.0 (which is in public beta right now). I have only done some minimal poking around, but I believe I&#8217;ve figured out the mechanism they use to load their plugin into Safari in the absence of Input Managers.</p>

<h4>Sow how does it work already?</h4>

<p>First, an introduction to <a href="http://en.wikipedia.org/wiki/AppleScript">AppleScript</a> and <a href="http://developer.apple.com/mac/library/technotes/tn/tn1164.html">Scripting Additions</a>. AppleScript is a rather old technology, first introduced in <a href="http://en.wikipedia.org/wiki/System_7" title="Mac OS 7">System 7</a>. It is a human-readable scripting language that can control any application that implements support for it, along with a slew of system functions and, more recently, generic control of UI elements[^1]. Under the hood, it sends <a href="http://en.wikipedia.org/wiki/Apple_events">Apple events</a> to actually talk to each process. Scripting additions are bundles that provide additional functionality to AppleScript, generally by installing Apple event handlers or doing Apple event data coercion. Several scripting additions are bundled with the system, such as the Keychain Scripting addition which provides access to the Keychain from within AppleScripts.</p>

<h4>What does this have to do with 1Password?</h4>

<p>I&#8217;m getting there. The thing about scripting additions is that they will be potentially loaded by any process on the system. Generally, they get loaded into a process that attempts to use an AppleEvent that the scripting addition handles. Here it gets a little fuzzy, as the documentation does not specify whether the sending process or the receiving process loads the scripting addition. In general, it doesn&#8217;t matter, and so I suspect the sending process is usually the one that handles the event[^2]. In fact, up through Mac OS X 10.5, the &#8220;context&#8221; for a scripting addition was always assumed to be &#8220;Machine&#8221;, which means the AppleEvent can be handled by any process on the machine it was sent to[^3]. New in 10.6 is the ability to specify exactly which context the scripting addition handler must execute in, including &#8220;Any&#8221;, &#8220;Machine&#8221;, &#8220;User&#8221;, or &#8220;Process&#8221;. That last context is the important one, as it means the scripting addition handler must be executed in the Apple event&#8217;s target process.</p>

<p>There is one caveat here which is that if a process has not initialized AppleScript, it will not have loaded any scripting additions. You can get around this by sending the kASAppleScriptSuite/kGetAEUT (ascr/gdut) Apple event to a process (according to <a href="http://developer.apple.com/mac/library/qa/qa2001/qa1070.html" title="Technical Q&#038;A QA1070">QA1070</a> the system automatically installs the handler for kASAppleScriptSuite/kGetAEUT on Mac OS X 10.2 or later). This event causes the &#8220;system handler table&#8221; for that process to be initialized, which loads the scripting additions.</p>

<h4>So we can load Scripting Additions. What now?</h4>

<p>The ability to load a scripting addition into a target process simply by sending it an Apple event is the key mechanism that allows us to restore the old Input Manager functionality. And this is exactly what 1Password does. 1Password includes a scripting addition that handles the ONEP/Load Apple event with a context of &#8220;Process&#8221;. This handler takes a single argument, the path to a given bundle, and it loads that specified bundle into the target process. The last component is a background daemon called 1PasswordAgent. This daemon sends the ONEP/Load Apple event to Safari immediately after Safari is launched[^4], causing Safari to load the 1Password WebKit plugin. You can see this in the following AppleEvent log:</p>

<p>~~~
{ 1 } &#8216;aevt&#8217;:  ONEP/Load (i386){
          return id: 284 (0x11c)
     transaction id: 0 (0x0)
  interaction level: 64 (0x40)
     reply required: 0 (0x0)
             remote: 0 (0x0)
      for recording: 0 (0x0)
         reply port: 37635 (0x9303)
  target:
    { 1 } &#8216;psn &#8216;:  8 bytes {
      { 0x0, 0x1a01a } (1PasswordAgent)
    }
  fEventSourcePSN: { 0x0,0x1a01a } (1PasswordAgent)
  optional attributes:
    &lt; empty record >
  event data:
    { 1 } &#8216;aevt&#8217;:  - 1 items {
      key &#8216;&#8212;&#8212;&#8217; - 
        { 1 } &#8216;TEXT&#8217;:  70 bytes {
          &#8220;/Applications/1Password.app/Contents/Extensions/WebKitExtension.bundle&#8221;
        }
    }
}
~~~</p>

<h4>Is that it?</h4>

<p>Yep, that&#8217;s it. Through the use of the Scripting Additions mechanism and a daemon, 1Password is able to load its bundle into 64-bit processes. This is actually fairly similar to how applications that use <a href="http://rentzsch.com/mach_inject/">mach_inject</a> work, except instead of injecting code at the mach level, it leverages existing higher-level interprocess communication mechanisms, which is likely to be far safer.</p>

<p>There is one more step. Safari sends an AppleEvent back to 1PasswordAgent (1Pwd/UNLK) after the bundle has been loaded. I am not sure what the purpose of this is, but I suspect UNLK stands for &#8220;Unlock&#8221; and is used for some sort of synchronization between the 1Password extension and 1PasswordAgent. It should not be necessary for most plugins.</p>

<h4>How do I use this in my own Safari plugin?</h4>

<p>At the moment, the only way is to do all the work that the 1Password guys did, and run your own daemon in the background to load your plugin into Safari. Perhaps if enough people ask, the 1Password guys will generalize their solution into something like SIMBL, e.g. a generic application plugin loader. Or maybe someone who reads this will be inspired to do it themselves.</p>

<p>One last thing: Apple clearly does not like bundles loading themselves into arbitrary processes on the system. And for a good reason. A lot of crashes and instability in the system are caused by poorly-written Input Managers or APE Haxies. This is why they removed the Input Manager system entirely. Unfortunately, as long as Safari continues to have no plugin API, such hacks are necessary to be able to use products like 1Password. If you choose to use this mechanism yourself, just be aware that Apple will most certainly frown upon it, and may try and take steps to eliminate this hole in future versions of the OS. And please, make sure your plugin is bug-free and future-compatible[^5]. The absolute worst thing you can do is cause users to experience crashes in applications.</p>

<p>[^1]: Controlling UI elements requires turning on the &#8220;Enable access for assistive devices&#8221; checkbox in the Universal Access preference pane.
[^2]: At least, on Snow Leopard. It seems Leopard always loaded the scripting addition into the target process, as it did not have the concept of a context.
[^3]: AppleEvents can be sent between machines if Remote Apple Events is enabled in the Sharing preference pane.
[^4]: As I suggested earlier, it actually sends the kASAppleScriptSuite/kGetAEUT (ascr/gdut) Apple event to Safari first, to ensure the scripting addition has been loaded.
[^5]: One of the big wins of SIMBL was that it made it so easy to specify a maximum supported bundle version for your plugin, and it strongly encouraged that you always set this to the current bundle version. In other words, whenever Safari got updated, you would be forced to test your plugin against the latest version of Safari and re-certify it as correct, or it would simply disable itself.</p>
]]></content:encoded>
			<wfw:commentRss>http://kevin.sb.org/2009/09/02/1password-extension-loading-in-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
