Table of contents

Created

26 November 2013

Requirements

Prerequisite knowledge

This article assumes basic familiarity with Creative Suite and Creative Cloud product extensibility.

User level

All

This article will walk you through the entire process of creating an Adobe CC HTML extension from scratch. The resulting extension will allow you to augment the functionality of an Adobe application using HTML, JavaScript, ExtendScript, and CSS.

Setup

Here's what you'll need to follow along:

  • Your favorite text editor
  • An Adobe CC application that supports HTML extensions1

You can also use Extension Builder 3, an Eclipse-based IDE, to help automate some of these steps, however it's not required.

The code

To start, we're going to create two files for our extension, index.html and manifest.xml. The relative location of these files is important and should be arranged:

HelloWorld/

    CSXS/

        manifest.xml

    index.html

HTML

Extensions are hosted in an embedded version of Chromium; that means that nearly everything you can do in Google Chrome, you can also do in an extension. In index.html, let's create a simple UI:

<!doctype html> <html> <body> <button id="btn">Click me</button> </body> </html>

Manifest

For our manifest.xml file, we need to provide some metadata for our extension:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <ExtensionManifest ExtensionBundleId="com.example.helloworld" ExtensionBundleName="Hello world" ExtensionBundleVersion="1.0" Version="4.0"> <ExtensionList> <Extension Id="com.example.helloworld.extension" Version="1.0"/> </ExtensionList> <ExecutionEnvironment> <HostList> <Host Name="PHXS" Version="[14.0,14.9]"/> </HostList> <LocaleList> <Locale Code="All"/> </LocaleList> <RequiredRuntimeList> <RequiredRuntime Name="CSXS" Version="4.0"/> </RequiredRuntimeList> </ExecutionEnvironment> <DispatchInfoList> <Extension Id="com.example.helloworld.extension"> <DispatchInfo> <Resources> <MainPath>./index.html</MainPath> </Resources> <UI> <Type>Panel</Type> <Menu>Hello world</Menu> <Geometry> <Size> <Height>400</Height> <Width>400</Width> </Size> </Geometry> </UI> </DispatchInfo> </Extension> </DispatchInfoList> </ExtensionManifest>

At first glance, the manifest can be a bit overwhelming, so let's go through each section to understand what it's doing.

<ExtensionManifest>

<ExtensionManifest ExtensionBundleId="com.example.helloworld" ExtensionBundleName="Hello world" ExtensionBundleVersion="1.0" Version="4.0">

At the root of the manifest, we provide the id, human readable name, and version of the extension bundle. A bundle is just a collection of extensions; in this bundle, we'll only have one extension to keep things simple. The attribute Version="4.0" should be hardcoded; it defines the manifest version, everything else is up to you to define.

<ExtensionList>

<ExtensionList> <Extension Id="com.example.helloworld.extension" Version="1.0"/> </ExtensionList>

Here we specify the list of extensions along with their ids and versions. Again, for this example, we only have one extension.

<HostList>

<HostList> <Host Name="PHXS" Version="[14.0,14.9]"/> </HostList>

In the <HostList> section, we define a list of applications our extension will work with. Each application is identified by a 4 character code along with a version range. In this case we're targeting Photoshop CC.

<LocaleList>

<LocaleList> <Locale Code="All"/> </LocaleList>

The <LocaleList> section defines the locales your extension will work in. Here we've set up the extension to work for all locales.

<RequiredRuntimeList>

<RequiredRuntimeList> <RequiredRuntime Name="CSXS" Version="4.0"/> </RequiredRuntimeList>

The <RequiredRuntimeList> section will be hardcoded for all extensions you create. All it says is that we require version 4.0 of the CSXS runtime (now called CEP).

<Extension>

<Extension Id="com.example.helloworld.extension">

The <Extension> section begins the definition of our extension. The id should match the one defined in the <ExtensionList> section.

<Resources>

<Resources> <MainPath>./index.html</MainPath> </Resources>

In the <Resources> section, we point to our index.html file we created earlier, telling the application that this is the HTML we want loaded when the extension is opened.

<UI>

<UI> <Type>Panel</Type> <Menu>Hello world</Menu> <Geometry> <Size> <Height>400</Height> <Width>400</Width> </Size> </Geometry> </UI>

Finally, we have the <UI> section. Here we define the window type of the extension, the menu name (which will show up under Window > Extensions), and the initial dimensions.

Running the extension

Now that we have our code ready to go, the next step is to prepare our environment for development and copy the extension into place. Then we can run the extension.

PlayerDebugMode

Applications will normally not load an extension unless it is cryptographically signed. However, during development we want to be able to quickly test an extension without having to sign it. To turn on debug mode:

  • On Mac, open the file ~/Library/Preferences/com.adobe.CSXS.4.plist and add a row with key PlayerDebugMode, of type String, and value 1.
  • On Windows, open the registry key HKEY_CURRENT_USER/Software/Adobe/CSXS.4 and add a key named PlayerDebugMode, of type String, and value 1.

You should only need to do this once.

Copying the extension into place

Now that the system is ready to load our unsigned extension, the last thing we have to do is copy our extension into the shared extensions folder on disk:

  • On Mac, copy the extension into ~/Library/Application Support/Adobe/CEPServiceManager4/extensions
  • On Windows, copy the extension into %APPDATA%\Adobe\CEPServiceManager4\extensions

When you're done you should have a folder structure like this:

extensions/
    HelloWorld/
        CSXS/
            manifest.xml
        index.html

Opening the extension

Now we can open the extension in Photoshop CC. Launch the application and choose the menuWindow > Extensions > Hello World. You should see something like this:

 

Debugging

Our extension is up and running, now let's debug it.

There are two ways to debug an HTML5 extension.

Method 1 (Deprecated): Debugging can be enabled by calling a special JavaScript function that only works for extensions:

window.__adobe_cep__.showDevTools();

Add this line to a file named main.js and reference the new file from index.html:

<!doctype html> <html> <body> <button id="btn">Click me</button> </body> <script src="js/main.js"></script> </html>

Create a file called .debug in the root folder of your extension.

Note: The above step is necessary with more recent versions of Creative Cloud products. At the time of writing it is only required with Premiere Pro 7.1 and Illustrator 17.0.2. For other products it is not required.

Your current folder structure should look like this:

extensions/ HelloWorld/ CSXS/ manifest.xml js/ main.js index.html .debug

When you open the extension again, you should see the Chromium debugger pop up (note that the debugger UI may differ slightly between versions of Adobe applications):

This window works just like you'd expect if you've debugged in Google Chrome before - you can inspect elements, console output, network performance, etc.

Method 2: Remote Debugging.

Note: Not all Creative Cloud applications support this method at the time of writing.

CEP/CSXS 4.2 supports remote debugging for HTML/JavaScript extensions using the Chrome debugger.

To use this method, you must specify debug ports in a mapping file in your extension's root folder. You can then open the debug port for the host application from a Google  Chrome browser and use the Chrome debugging tools. For example, if you have specified the debug port 8088 for Photoshop, and you open your extension in Photoshop, open the Chrome browser and go to http://localhost:8088. Mozilla Firefox and Safari should also work. The browser will display information about your extension and a clickable link to debug the extension. Once you click on that you will see a similar view as when using Method 1.

To specify the debug ports, create a special file named .debug and place it in your extension's root folder; for example, MyExtension\.debug

Edit this file to include valid remote debug ports for all applications you wish to debug, in the Extension Manifest XML format for <Host> specifications. Valid port values are in the range 1024 to 65534.

With our HelloWorld extension, the file would look like this:

<?xml version="1.0" encoding="UTF-8"?> <ExtensionList> <Extension Id="com.example.helloworld.extension"> <HostList> <Host Name="PHXS" Port="8088"/> </HostList> </Extension> </ExtensionList>

Interacting with the host application

In most cases, your extension won't just be isolated HTML, you'll probably want to interact with the application your extension is running in. To make such calls, we use a library called CSInterface.js (this API is documented in Extension Builder 3).

This library provides a number of useful functions, everything from retrieving the id of the current host application, to programmatically closing your extension. For this demo, we're going to use it to make calls against the host application's scripting DOM.

First, we need to add the library to index.html and copy it into the js folder:

<!doctype html> <html> <body> <button id="btn">Click me</button> </body> <script src="js/CSInterface-4.0.0.js"></script> <script src="js/main.js"></script> </html>

Next, we're going to add some ExtendScript which will create a new document using Photoshop's scripting DOM. Create a new file called ps.jsx with the code:

function addDocument() { app.documents.add(); }

Add this new file to a folder called host inside of the extension, so our folder structure now has:

HelloWorld/ CSXS/ manifest.xml js/ CSInterface-4.0.0.js main.js host/ ps.jsx index.html

At the moment, our script won't be loaded by the extension. In order to wire it up, we have to add a reference to it in our manifest.xml:

<Resources> <MainPath>./index.html</MainPath> <ScriptPath>./host/ps.jsx</ScriptPath> </Resources>

And with that, our ExtendScript function will be loaded when the extension is loaded. The last step is to modify main.js to call this function using CSInterface when the extension's button is clicked:

// Get a reference to a CSInterface object var csInterface = new CSInterface(); var button = window.document.getElementById("btn"); button.onclick = function() { // Call function defined in host/ps.jsx csInterface.evalScript("addDocument()"); };

Finally, when we reload the extension and click on the button, Photoshop should create a new document.

Note: You may also want to check out CEPEngine_extensions which provides APIs to manipulate local files and external processes. 

Packaging for deployment

Once our extension has been developed, we'll eventually want to sign and package it for deployment. For that, we're going to use a tool called ZXPSignCmd which is available at the bottom of the Extension Builder 3 download page.

Certificates

Before we can sign and package our extension, we need a certificate. In the real world, we'd buy a certificate from a trusted certificate authority like VeriSign, but for simplicity let's create a self-signed certificate. We can use ZXPSignCmd to self-sign a certificate using the syntax:

ZXPSignCmd -selfSignedCert <countryCode> <stateOrProvince> <organization> <commonName> <password> <outputPath.p12>

For example, the following command will generate the certificate myCertificate.p12:

./ZXPSignCmd -selfSignedCert US Washington myOrganization "John Smith" myPassword myCertificate.p12

Signing and packaging

Now that we have a certificate, we can now sign and package the extension using ZXPSignCmd:

ZXPSignCmd -sign <inputDirectory> <outputZxp> <p12> <p12Password>

For example, using our existing extension and certificate:

./ZXPSignCmd -sign HelloWorld/ HelloWorld.zxp myCertificate.p12 myPassword

Congrats! Now we can distribute the signed HelloWorld.zxp to our users.

Conclusion

At this point, we've touched on all the major components of building an HTML extension. We have:

  • Created a simple UI using HTML
  • Defined metadata in the manifest
  • Setup our environment for running the extension
  • Debugged a running extension
  • Interacted with the host application using CSInterface and ExtendScript
  • Packaged and prepared the extension for deployment

However, even with all that, there's still plenty more to learn. Below is a list of additional resources that will help take you beyond the basics.

Resources