CEP 6 HTML Extension Cookbook for CC 2015

Qwertyfly edited this page Feb 23, 2016 · 20 revisions

This cookbook is a guide of tips and samples for creating CEP HTML/JavaScript Extensions for Creative Cloud 2015 applications.

CEP Extensions

CEP (formerly CSXS) Extensions extend the functionality of the host application that they run in. Extensions are loaded into applications through the PlugPlug Library architecture. Starting from CEP 4.0, HTML/JavaScript technology can be used to develop extensions.

Extension Types

Currently there are four types of CEP extensions that can be created - Panel, Modal Dialog, Modeless Dialog and invisible extension.

  • The Panel type of extension behaves like any other application panel. It can be docked, participates in workspaces, has fly-out menus, and is re-opened at start-up if open at shutdown. The corresponding type identifier in the extension manifest is Panel.
  • The Modal Dialog type opens a new window and forces the user to interact with the window before returning control to the host application. The corresponding type identifier in the extension manifest is ModalDialog.
  • The Modeless Dialog type opens a new window but doesn't force the user to interact with it. The corresponding type identifier in the extension manifest is Modeless.
  • The invisible extension is the one that remains hidden and never becomes visible during its whole life cycle. Read "Invisible HTML Extensions" for more details.

Extensions can be installed by clients using Adobe Exchange.

Adobe Applications

Application Host ID CC2015 Version
Photoshop PHXS/PHSP 16
InDesign IDSN 11
InCopy AICY 11
Illustrator ILST 19
Premiere Pro PPRO 9
After Effects AEFT 14
Prelude PRLD 4
Flash Pro FLPR 15
Dreamweaver DRWV 15

Chromium Embedded Framework (CEF)

CEP is based on the Chromium Embedded Framework version 3 (CEF3). You can find more information about CEF on http://code.google.com/p/chromiumembedded/. We currently use CEF version: CEF 3, branch 1453, revision 1339

Browser Features supported by CEP


CEP currently supports the following HTML5 features:

  • Video
  • Audio
  • Drag and Drop
  • Canvas
  • SVG
  • Web Workers
  • Web Storage
  • Web SQL Database
  • Application Cache
  • Server sent events
  • Form input types
  • Form elements
  • Form attributes
  • File API

HTTP Cookies

CEP supports two kinds of cookies:

  • Session Cookies - Temporary in-memory cookie which will expire and disappear when user closes extension
  • Persistent Cookies - No expiry date or validity interval, stored in the user's file system

Persistent Cookies location:

CEP Windows Mac
4.x C:\Users\USERNAME\AppData\Local\Temp\cep_cookies\ /Users/USERNAME/Library/Logs/CSXS/cep_cookies/
5.x C:\Users\USERNAME\AppData\Local\Temp\cep_cache\ /Users/USERNAME/Library/Logs/CSXS/cep_cache/
6.x C:\Users\USERNAME\AppData\Local\Temp\cep_cache\ /Users/USERNAME/Library/Caches/CSXS/cep_cache/

Each persistent cookie is a file. File name is HostID_HostVersion_ExtensionName, such as PHXS_15.0.0_com.adobe.extension1.

Development and Debugging

Development Machine Setup

CEP HTML Extensions can be developed on both Windows and Mac platforms. The development machine needs to have the following applications in order to successfully develop CSXS extensions:

  • Adobe Creative Suite applications supporting CEP HTML extensions.
  • HTML Extension Builder.(Nice to have, but not mandatory). HTML Extension Builder is a tool set built on top of Eclipse and can be used for developing and debugging HTML extensions. Please download the Extension Builder 3 from http://labs.adobe.com/technologies/extensionbuilder3/
  • Adobe ExtendScript Tool Kit (This tool kit is installed with all Creative Suite applications).
  • Adobe Extension Manager

Signing Extensions

Before you sign the extensions, you need to get or create the certificate file. Configurator and Adobe Exchange Packer can create certificates. The developer can get all the required information from https://www.adobeexchange.com/resources/7#overview (please sign in so that you can see the content).

Three tools can be used to sign a HTML5 extension.

ccextensionswin64.exe -sign "d:\Adobe Layer Namer\Adobe Layer Namer\"(input extension path) d:\AdobeLayerNamer.zxp(output zxp path) d:\sign.p12(certificate path) 1(certificate password)

Debugging Unsigned Extensions

If you are in the midst of development and are not using HTML Extension Builder for debug workflows and want to bypass the need to sign your extensions, you can bypass the check for extension signature by editing the CSXS preference properties file, located at:
Win: regedit > HKEY_CURRENT_USER/Software/Adobe/CSXS.6, then add a new entry PlayerDebugMode of type "string" with the value of "1".
Mac: In the terminal, type: defaults write com.adobe.CSXS.6 PlayerDebugMode 1 (The plist is also located at /Users/USERNAME/Library/Preferences/com.adobe.CSXS.6.plist)

These entries will enable debug extensions to be displayed in the host applications.

Remote Debugging

CEP supports remote debugging for html extensions. Create and put a “.debug” file to the extension root directory such as Test_Extension\.debug. The .debug file contains remote debug ports. Developers must create this file and use valid debug ports. ".debug" is a special file name for both Windows and Mac platform, it has to be created by command line.
On Windows, use "copy con .debug" and "Ctrl+Z" to create an empty file.
On Mac, use "touch .debug" to create an empty file.

The value of Port should be between 1024 and 65535 (not include 65535), otherwise remote debugging will not work. One extension bundle may have multiple extensions so the .debug file can specify debug ports for each extension. Here is an example file.

<?xml version="1.0" encoding="UTF-8"?>
    <Extension Id="com.adobe.CEPHTMLTEST.Panel1">
            <Host Name="PHXS" Port="8000"/>
            <Host Name="IDSN" Port="8001"/>
            <Host Name="AICY" Port="8002"/>
            <Host Name="ILST" Port="8003"/>
            <Host Name="PPRO" Port="8004"/>
            <Host Name="PRLD" Port="8005"/>
            <Host Name="FLPR" Port="8006"/>
    <Extension Id="com.adobe.CEPHTMLTEST.Panel2">
            <Host Name="PHXS" Port="8100"/>
            <Host Name="IDSN" Port="8101"/>
            <Host Name="AICY" Port="8102"/>
            <Host Name="ILST" Port="8103"/>
            <Host Name="PPRO" Port="8104"/>
            <Host Name="PRLD" Port="8105"/>
            <Host Name="FLPR" Port="8106"/>
    <Extension Id="com.adobe.CEPHTMLTEST.ModalDialog">
            <Host Name="PHXS" Port="8200"/>
            <Host Name="IDSN" Port="8201"/>
            <Host Name="AICY" Port="8202"/>
            <Host Name="ILST" Port="8203"/>
            <Host Name="PPRO" Port="8204"/>
            <Host Name="PRLD" Port="8205"/>
            <Host Name="FLPR" Port="8206"/>
    <Extension Id="com.adobe.CEPHTMLTEST.Modeless">
            <Host Name="PHXS" Port="8300"/>
            <Host Name="IDSN" Port="8301"/>
            <Host Name="AICY" Port="8302"/>
            <Host Name="ILST" Port="8303"/>
            <Host Name="PPRO" Port="8304"/>
            <Host Name="PRLD" Port="8305"/>
            <Host Name="FLPR" Port="8306"/>

If you load an extension whose debug port is 8088, open the port from the Chrome browser by using URL http://localhost:8088/.

Known Issues

  • If you attempt to launch an extension using the requestOpenExtension API and the extension invoking the API has the same debug port as the target extension, the target extension will not load.
  • Due to a bug (which should be fixed in Webkit inspector), in the dev tools, you cannot inspect the variables or open watches. please do the following steps to workaround this issue when it happens: Open the Developer tools from Chrome browser main menu by choosing View->Developer->Developer Tools Execute the following JS snippet in the Console:
TreeElement.prototype.isEventWithinDisclosureTriangle = function(event) {
  var computedLeftPadding = 10;
  if(window.getComputedStyle(this._listItemNode).getPropertyCSSValue) {
    computedLeftPadding = window.getComputedStyle(this._listItemNode).getPropertyCSSValue("padding-left").getFloatValue(CSSPrimitiveValue.CSS_PX);
  var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
  return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;

Where are the log files

PlugPlug Logs

Log files with useful debug information are created for each of the applications supporting CEP extensions. These files are generated with the naming convention as csxs{versionNumber}-{HostID}.log. For Illustrator, the log file is csxs6-ILST.log. The platform-specific locations for the log files are as follows:

Win: C:\Users\USERNAME\AppData\Local\Temp
Mac: /Users/USERNAME/Library/Logs/CSXS

Logging levels can be modified as per the following levels:

  • 0 - Off (No logs are generated)
  • 1 - Error (the default logging value)
  • 2 - Warn
  • 3 - Info
  • 4 - Debug
  • 5 - Trace
  • 6 - All The LogLevel key can be updated at the following location (The application should be restarted for the log level changes to take effect):
Win: regedit > HKEY_CURRENT_USER/Software/Adobe/CSXS.6
Mac: /Users/USERNAME/Library/Preferences/com.adobe.CSXS.6.plist

For example on Mac, in the terminal do:

defaults write com.adobe.CSXS.6 LogLevel <loglevel>


In CEP the Chromium Embedded Framework (CEF) in CEPHtmlEngine generates a log:

Win: C:\Users\USERNAME\AppData\Local\Temp\cef_debug.log
Mac: /Users/USERNAME/Library/Logs/CSXS/cef_debug.log

Where are the Extensions

To run an extension, copy it into the shared extensions folder on disk. System extension folder

Win: C:\Program Files (x86)\Common Files\Adobe\CEP\extensions
Mac: /Library/Application Support/Adobe/CEP/extensions

Per-user extension folder

Win: C:\<username>\AppData\Roaming\Adobe\CEP\extensions
Mac: ~/Library/Application Support/Adobe/CEP/extensions

Please note that the character '#' is not allowed in extension folder path on both Windows and Mac OSX, since CEF treats '#' as a delimiter.

Extension Manifest

The manifest.xml file is required for every extension and provides the necessary information to configure a CEP extension. ExtensionManifest is the root element of the manifest.xml file. Extensions, ExtensionList, and DispatchList are the three child elements of the ExtensionManifest root element.

Extension Manifest XSD

To check if the extension's manifest is in sync with the latest schema, perform the following steps:

Important Manifest Change for CEP 6.0 Extensions

Make sure correct point product versions are used. Here is an example.

    <Host Name="PHXS" Version="[16.0,16.9]"/>
    <Host Name="PHSP" Version="[16.0,16.9]"/>

This will support Photoshop version 16.0 up to, and including, 16.9. If you use the following syntax then you are supporting releases up to 16.9 but not including 16.9

    <Host Name="PHXS" Version="[16.0,16.9)"/>
    <Host Name="PHSP" Version="[16.0,16.9)"/>

Make sure correct CEP version is used.

    <RequiredRuntime Name="CSXS" Version="6.0"/>

Extension Size

You can specify extension size, max size and min size in extension manifest. Size is mandatory; max size and min size are optional. A modal or modeless dialog is resizable if there are max size and min size, otherwise it is un-resizable. When you move mouse pointer over dialog border, CEP shows different cursor for resizable and un-resizable dialogs.


High DPI Panel Icons

In high DPI display mode, panel extensions may want to use high DPI icons. You set these icons in extensions manifest.

 <Icon Type="Normal">./images/IconLight.png</Icon>
 <Icon Type="RollOver">./images/IconLight.png</Icon>
 <Icon Type="DarkNormal">./images/IconDark.png</Icon>
 <Icon Type="DarkRollOver">./images/IconDark.png</Icon>

You pack both normal icon files (IconLight.png and IconDark.png) and high DPI icon files (IconLight@2X.png and IconDark@2X.png) in your extension. Host applications will be able to find and use

@2X.ext is the industry standard. Please see more details on https://developer.apple.com/library/ios/qa/qa1686/_index.html.

Please note that Photoshop supports _x2.ext format.

Shortcut Keys for HTML Extensions

When focus is on HTML extensions, these shortcut keys are handled by extension.

Windows Keys Mac Keys Function
Ctrl + A Command + A Select All
Ctrl + C Command + C Copy
Ctrl + V Command + V Paste
Ctrl + X Command + X Cut

Other shortcut keys are handled by point products, such as pressing Ctrl + N to create a new document.

CEP JavaScript Programming

CEP JavaScript Libraries

CEP JavaScript Libraries provide JavaScript APIs to access application and CEP information.

  • CSInterface.js
  • Vulcan.js
  • AgoraLib.js

Get them from https://github.com/Adobe-CEP/CEP-Resources. To use them, please include these JavaScript files in your HTML extension.

API version

The CEP JavaScript APIs keep changing with each new CEP release. The changes are guaranteed to be backward compatible. For newly added APIs, a version tag like "Since x.x.x" is added to its API comments indicating since which CEP version the APIs is available.

You will need to check the version tag against the version of CEP integrated by the Adobe Product you are using to make sure the API you want to use is available. To do so, use CSInterface.getCurrentApiVersion() to retrieve the version of CEP integrated by the Adobe Product. Please note this API itself is available only since 4.2.0. If you get an error saying getCurrentApiVersion is undefined, then you are running in CEP 4.0 or 4.1. Otherwise, the value returned will tell you the version of CEP integrated by the Adobe product.

CEP Events

CEP supports sending and receiving events within an extension, among extensions in application, and among extensions in different applications. Since both Flash and HTML extensions are based on the common communication layer with the same event data format, they could communicate with each other based on CEP event, and even they could communicate with native side as long as point products invoke PlugPlugAddEventListener/PlugPlugDispatchEvent accordingly to achieve this.

Like what we have for Flash extensions, there are dispatchEvent/addEventListener/removeEventListener APIs available in JavaScript world to dispatch and listen for events. Let's go through CEP event data format/structure, APIs to dispatch and listen event, and sample code snippet accordingly in JavaScript world.


In terms of CSEvent, it just means CEP Event here. The data structure of CSEvent (CEP Event) in JavaScript is just the same as the one defined in Flash extension, as below.

 * Class CSEvent.
 * You can use it to dispatch a standard CEP event.
 * @param type        Event type.
 * @param scope       The scope of event, can be "GLOBAL" or "APPLICATION".
 * @param appId       The unique identifier of the application that generated the event. Optional.
 * @param extensionId The unique identifier of the extension that generated the event. Optional.
 * @return CSEvent object
function CSEvent(type, scope, appId, extensionId)
    this.type = type;
    this.scope = scope;
    this.appId = appId;
    this.extensionId = extensionId;

You could create a CSEvent object and dispatch it by using CSInterface.dispatchEvent. Also you could access its property in your callback of CSInterface.addEventListener. Refer to the section addEventListener/dispatchEvent below for more details.

Listen for and Dispatch CSEvent

dispatchEvent/addEventListener/removeEventListener APIs are available in JavaScript world to dispatch and listen for CSEvent.


Here is the definition for addEventListener. Refer to CSInterface.js for more information.

 * Registers an interest in a CEP event of a particular type, and
 * assigns an event handler.
 * The event infrastructure notifies your extension when events of this type occur,
 * passing the event object to the registered handler function.
 * @param type     The name of the event type of interest.
 * @param listener The JavaScript handler function or method.
 * @param obj      Optional, the object containing the handler method, if any.
 *         Default is null.
CSInterface.prototype.addEventListener = function(type, listener, obj)

One thing needs to be mentioned here is both named and anonymous callback functions are supported in CSInterface.addEventListener. Here is an example of how to use named callback function in CSInterface.addEventListener:

function callback(event)
  console.log(“type=” + event.type + “, data=” + event.data);

var csInterface = new CSInterface();
csInterface.addEventListener(“com.adobe.cep.test”, callback); //invoke the function

Here is an example of how to use anonymous callback function in CSInterface.addEventListener.

    var csInterface = new CSInterface();
    csInterface.addEventListener(“com.adobe.cep.test”, function (event)
           console.log(“type=” + event.type + “, data=” + event.data);
    ); // Anonymous function is the second parameter

Besides, like flash world, event.data could even be an object, i.e. you could use an object as event.data. If you use a JSON object as event data, there is a known issue that the data order will be messed up sometimes. A work around for this issue is using JSON.stringify() to convert this JSON object to a string first, and then add any prefix to this string like “Your JSON string”. Here is an example on how to use it.

    var csInterface = new CSInterface();
    csInterface.addEventListener(“com.adobe.cep.test”, function (event)
           var obj = event.data;
           console.log(“type=” + event.type + “, data.property1=” + obj.property1 + “, data.property2=” + obj.property2);
    ); // Anonymous function is the second parameter


Here is the definition for CSInterface.dispatchEvent. Refer to CSInterface.js for more details.

 * Triggers a CEP event programmatically. You can use it to dispatch
 * an event of a predefined type, or of a type you have defined.
 * @param event A \c CSEvent object.
CSInterface.prototype.dispatchEvent = function(event)

Here are three samples to demonstrate how to dispatch event in JavaScript. Firstly an example of how to dispatch event in JavaScript.

    var csInterface = new CSInterface();
    var event = new CSEvent("com.adobe.cep.test", "APPLICATION");
    event.data = "This is a test!";

Another example of creating event object and setting property, then dispatch it.

    var csInterface = new CSInterface();
    var event = new CSEvent();
    event.type = "com.adobe.cep.test";
    event.scope = "APPLICATION";
    event.data = "This is a test!";

An example of dispatching an event whose data is an object.

    var event = new CSEvent("com.adobe.cep.test", "APPLICATION");
    var obj = new Object();
    obj.a = "a";
    obj.b = "b";
    event.data = obj;

Communication between Flash and Html extensions

CEP event based communication between Flash and Html extensions is simple, as long as the event data is string. You could simply use API of dispatching and listening event accordingly.

If you dispatch an event whose data is an object in Javascript and handle it in Flash side, then you need a little bit conversion work to do in Flash extension. Since in JavaScript the object is serialized as JSON string, you need to deserialize it to be an XML-based object in Flash side. Vice versa, if you dispatch an event whose data is an object in Flash and handle it in JavaScript side, you need to deserialize it from XML to be JSON-based object in JavaScript side. Anyway, considering that usage of Flash extension is down, therefore the communication between Flash and Html extension is limited, and object-based event data is very limited.

Handling Window State Change Events - Extensions

Unlike Flash extension, Html extension does not support Window State Change Events.

Standard Events in Point Products

The following table lists the standard events currently supported by Point Products:

Event Type Description Event Parameter Support
documentAfterActivate Event fired when a document has been activated (after new/open document;after document has retrieved focus). URL to the active document. If the doc was not save, the NAME will be set instead of the URL. InDesign, Illustrator
documentAfterDeactivate Event fired when the active document has been de-activated. (after document loses focus) URL to the active document. If the doc was not save, the name will be set instead of the URL. Photoshop, InDesign, Illustrator
applicationBeforeQuit Event fired when the application got the signal to start to terminate. none InDesign
applicationActivate Event fired when the Application got an "activation" event from the OS. none Photoshop, InDesign, Illustrator, Premiere (on Mac), Prelude (on Mac)
documentAfterSave Event fired after the document has been saved URL to the saved document. Photoshop, InDesign

Specific Events in Products


In Photoshop, the following specific events are defined:

  • com.adobe.PhotoshopPersistent
  • com.adobe.PhotoshopUnPersistent
  • com.adobe.PhotoshopCallback => This event will be removed in Photoshop 17.0 (see below)
  • com.adobe.PhotoshopWorkspaceSet
  • com.adobe.PhotoshopWorkspaceGet
  • com.adobe.PhotoshopWorkspaceAware
  • com.adobe.PhotoshopWorkspaceData
  • com.adobe.PhotoshopWorkspaceRequest
  • com.adobe.PhotoshopRegisterEvent
  • com.adobe.PhotoshopUnRegisterEvent
  • com.adobe.PhotoshopLoseFocus
  • com.adobe.PhotoshopQueryDockingState

For example, Html extension yields the mouse focus back to Photoshop by sending out com.adobe.PhotoshopLoseFocus event, like below

var csInterface = new CSInterface();
var event = new CSEvent("com.adobe.PhotoshopLoseFocus", "APPLICATION");
event.extensionId = csInterface.getExtensionID();

com.adobe.PhotoshopCallback will be removed in Photoshop 17.0 as adding a listener results in all CS Extensions receiving the event. As of Photoshop CC2015, developers are encouraged to use this alternative with fixes the broadcast issue.

csInterface.addEventListener("com.adobe.PhotoshopJSONCallback" + gExtensionID, PhotoshopCallbackUnique);

A sample of using this event: https://github.com/Adobe-CEP/Samples/tree/master/PhotoshopEvents

Invoke point product's scripts from html extension

First, define a callback function in html extension:

function evalScriptCallback(result)
   // process the result string here.

And then call CSInterface.evalScript with the script you want to call and the callback function:

var script = "app.documents.add";  //Demo script
CSInterface.evalScript(script, evalScriptCallback);

Access Application DOM from Html Extension

There are two separate JavaScript engines here.

  • JavaScript engine of host application - Application DOM/Extend script DOM
  • JavaScript engine of CEP Html runtime - Html DOM

Application DOM is not available in Html extension's engine and Html DOM is not available in host application's engine.

To access Application DOM from Html extensions, CEP JavaScript library provides a API CSInterface.evalScript to execute extend script as so to access application's DOM. Here is the sample JavaScript code snippet in Html extension.

var csInterface = new CSInterface();
csInterface.evalScript('app.documents.add();', function(result) {

Access HTML DOM from extend script

There is no way to access HTML extension's JavaScript DOM directly from Application's ExtendScript. If you need to access it, CEP event based communication can be used as a substitution.

CEP creates a library which uses External Object mechanism of ExtendScript to send CSXS events. The external object provides an ExtendScript class CSXSEvent for creating and dispatching application-level CSXS events. On HTML extension side, event listeners can be registered via the addEventListener API in CSInterface.js to listen to the events.

Some CC applications (Photoshop, Illustrator, Premiere Pro) integrate PlugPlugExternalObject library and have started to support this functionality since CC 2014.

Sample Code

ExtendScript developers need to create external object instance first.

var externalObjectName = "PlugPlugExternalObject";
var mylib = new ExternalObject( "lib:" + externalObjectName );

And then create the CSXSEvent instance.

var eventObj = new CSXSEvent();

At last use this instance to dispatch event:


Below is the sample code of ExtendScript.

var cs = new CSInterface();

cs.addEventListener("documentCreated", function(event) {
    alert('Cool!' + event.data);

var extendScript = 'var externalObjectName = "PlugPlugExternalObject"; var mylib = new ExternalObject( "lib:" + externalObjectName ); app.documents.add(); var eventObj = new CSXSEvent(); eventObj.type="documentCreated"; eventObj.data="blahblah"; eventObj.dispatch();'

Fly out menu

Creating a fly out menu on the native panel of HTML extension is now supported by using the setPanelFlyoutMenu and updatePanelMenuItem APIs in CSInterface.

The "menu" parameter for "setPanelFlyoutMenu" is a XML string. Below is an example:

  <MenuItem Id="menuItemId1" Label="TestExample1" Enabled="true" Checked="false"/>
  <MenuItem Label="TestExample2">
     <MenuItem Label="TestExample2-1" >
        <MenuItem Label="TestExample2-1-1" Enabled="false" Checked="true"/>
     <MenuItem Label="TestExample2-2" Enabled="true" Checked="true"/>
  <MenuItem Label="---" />
  <MenuItem Label="TestExample3" Enabled="false" Checked="false"/>

If you want to be notified of menu item clicks, register "com.adobe.csxs.events.flyoutMenuClicked" event by calling AddEventListener. When an menu item is clicked, the event callback function will be called. The "data" attribute of the event is an object which contains "menuId" and "menuName" attributes.

To get notified when fly-out menu is opened and closed, register event listener for below event types respectively:


Customize Context Menu

Set and Update Context Menu with XML

There are APIs in CSInterface for developers to set and update the customized context menu.


The "menu" parameter for "setContextMenu" is a XML string.

  • Id - Menu item ID. It should be plain text.
  • Label - Menu item label. It supports localized languages.
  • Enabled - Whether the item is enabled or disabled. Default value is true.
  • Checkable - Whether the item can be checked/unchecked. Default value is false.
  • Checked - Whether the item is checked or unchecked. Default value is false.

Here is an example.

   <MenuItem Id="menuItemId1" Label="TestExample1" Enabled="true" Checked="false"/>
   <MenuItem Id="menuItemId2" Label="TestExample2">
     <MenuItem Id="menuItemId2-1" Label="TestExample2-1" >
       <MenuItem Id="menuItemId2-1-1" Label="TestExample2-1-1" Enabled="false" Checkable="true" Checked="true"/>
     <MenuItem Id="menuItemId2-2" Label="TestExample2-2" Enabled="true" Checkable="true" Checked="true"/>
   <MenuItem Label="---" />
   <MenuItem Id="menuItemId3" Label="TestExample3" Enabled="false" Checked="false"/>

The "callback" parameter is the callback function which is called when user clicks a menu item. The only parameter is the ID of clicked menu item.

Set and Update Context Menu with JSON

If you prefer to using a JSON string to set context menu, you can achieve it by calling setContextMenuByJSON. The menu parameter for setContextMenuByJSON is a JSON string.

  • id - Menu item ID. It should be plain text.
  • icon - Menu item icon path. It is a path relative to the extension root path. For optimal display results please supply a 16 x 16px PNG icon as larger dimensions will increase the size of the menu item.
  • label - Menu item label. It supports localized languages.
  • enabled - Whether the item is enabled or disabled. Default value is true.
  • checkable - Whether the item can be checked/unchecked. Default value is false.
  • checked - Whether the item is checked or unchecked. Default value is false.

The items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. Here is an JSON example:

       "menu": [
               "id": "menuItemId1",
               "label": "testExample1",
               "enabled": true,
               "checkable": true,
               "checked": false,
               "icon": "./img/small_16X16.png"
               "id": "menuItemId2",
               "label": "testExample2",
               "menu": [
                       "id": "menuItemId2-1",
                       "label": "testExample2-1",
                       "menu": [
                               "id": "menuItemId2-1-1",
                               "label": "testExample2-1-1",
                               "enabled": false,
                               "checkable": true,
                               "checked": true
                       "id": "menuItemId2-2",
                       "label": "testExample2-2",
                       "enabled": true,
                       "checkable": true,
                       "checked": true
               "label": "---"
               "id": "menuItemId3",
               "label": "testExample3",
               "enabled": false,
               "checkable": true,
               "checked": false

If developers do not set context menu, CEP shows default items (Back, Forward, View Source, etc.). This is compatible with previous releases. If developers set context menu, CEP removes all default items and shows customized items only.

Disable Context Menu

To disable the context menu, you can call setContextMenu with null.

Another way is to add oncontextmenu="return false;" to the HTML tag. For example,

<body oncontextmenu="return false;">

Other Implementation of Context Menu

Please refer to http://www.javascripttoolbox.com/lib/contextmenu/ for examples.

Get Display Status of Html Extension Window

There are two ways to get html extension window status:

  • Register com.adobe.csxs.events.panelWindowStatusChanged CSXS event.
  • Call isWindowVisible JavaScript API.

Register CSXS event

Observe com.adobe.csxs.events.panelWindowStatusChanged CSXS event, this is for PANEL extensions only. If user hides the panel window by clicking "X" or collapsing window, this event is going to be sent to observer with the "true" or "false" string in data attribute, while the event is not going to be sent if the extension is closed. That is to say, currently, only panel extensions which are running on Illustrator and persistent can receive this event, when the extension is hiding, event with "false" data is sent while the extension is shown, event with "true" is sent.

Call isWindowVisible API

Call isWindowVisible JS interface. Both dialog and panel extension enable to access this API, but it always returns true for modal and modeless dialog extensions while it always return false for invisible extensions.

Getting and Changing Extension Content Size

Getting Extension Content Size

Getting extension content size can be done using window.innerWidth and window.innerHeight. However, if you are accessing these properties from inside an IFrame, you are actually accessing the properties of the IFrame's window object, not the ones for the HTML document. To access the top-most one, you will need to do parent.window.innerWidth and parent.window.innerWidth.

Changing Extension Content Size

Changing modal and modeless extension content size is supported in all Adobe applications that support CEP. However, changing panel HTML extension size is not supported in Premiere Pro, Prelude and After Effects.

CSInterface.prototype.resizeContent = function(width, height)

The width and height parameters are expected to be unsigned integers. The function does nothing when parameters of other types are passed.

Please note that extension min/max size constraints as specified in the manifest file apply and take precedence. If the specified size is out of the min/max size range, the min or max bounds will be used. When a panel is docked with other panels, there are chances that it won't resize as expected even when the specified size satisfies the min and max constraints. The restriction is imposed by host applications, not by CEP.

HI-DPI display

CEP JavaScript library provides APIs for detecting the availability of HI-DPI display on the Mac platform. CSInterface.getScaleFactor(). Use this function to retrieve the scale factor of the display on which the calling extension window is located.

var scaleFactor = CSLibrary.getScaleFactor();

Use this function to add a event handler that will be called when calling extension window is moved between HI-DPI and non-HI-DPI displays.

    window.scaleFactorHandler = function() {
        var scaleFactor = CSLibrary.getScaleFactor();
        if (2 == scaleFactor) {
                imgSrc = "../img/PS_AppIcon_r.png"
        } else {
            imgSrc = "../img/PS_AppIcon.png"
        document.getElementById("image").src = imgSrc;


CEP also supports HiDPI on Windows.

Other JavaScript APIs

The JavaScript engine in CEPHTMLEngine had been extended to provide some APIs, including:

  • local file access
  • native process
  • others

These APIs are in the JavaScript DOM and can be used as other built-in JavaScript APIs. You do NOT need to include any JavaScript files. API reference is as below.

Create a folder.

var path = "/tmp/test";
var result = window.cep.fs.makedir(path);
if (0 == result.err) {
     ...// success
else {
     ...// fail

Write a file.

var data = "This is a test.";
var path = "/tmp/test";
var result = window.cep.fs.writeFile(path, data);
if (0 == result.err) {
     ...// success
else {
     ...// fail

Write file with base64 encoding mode

You need to convert the input string to a base64-encoded string before calling writeFile(). The following is an example.

var data = "This is a test.";
var path = "/tmp/test";
data = cep.encoding.convertion.utf8_to_b64(data);

var result = window.cep.fs.writeFile(fileName, data, cep.encoding.Base64);
if (0 == result.err) {
     ...// success
else {
     ...// fail

Read a file

var path = "/tmp/test";
var result = window.cep.fs.readFile(path);
if (0 == result.err) {
     alert(result.data); //result.data is file content
else {
     ...// fail

Read file with base64 encoding mode

The read data after readFile is called is converted to a base-encoded string. You need to decode this string to any format you want. The following is an example

var path = "/tmp/test";
result = window.cep.fs.readFile(path, cep.encoding.Base64);
if (0 == result.err) {
     var base64Data = result.data;
     var data = cep.encoding.convertion.b64_to_utf8(base64Data);
else {
     ...// fail

Create a process and check if it's running

var result = window.cep.process.createProcess("usr/X11/bin/xterm");
        if (0 == result.err) {
            var pid = result.data;
            result = window.cep.process.isRunning(pid);
            if (true == result.data) {
               // running                 

You could use other APIs like delete folder, rename folder, set file permission, delete file, show file open dialog, quit process, etc.


In order to support localization, both the extension and the host application must provide locale information. There are two distinct types of locale information.

  • The License Locale (returned as the applicationLocale by the AMT library)
  • The Effective/Language/UI Locale (which is controlled by the user in the OS settings).

The extension must provide the list of supported locales for both the License Locale and the Language Locale via the HostEnvironment. This is particularly important in cases where the extension has features for a specific locale. PlugPlug library expects the host application to provide the locale information as part of the environmental data. JavaScript API HostEnvironment has the appLocale property in place.

License Locale and locales supported by extension

CEP checks the License Locale of host application against supported locales declared in extensions locale list to determine if the extension is loadable for the host application.

Locale folder structure

The locale folder structure in HTML extensions is similar as Flash extensions. Each property file should be placed in its corresponding locale folder. For example, the en_US property file should be <YourExtension>/locale/en_US/messages.properties. Users can define a default property file (<YourExtension>/locale/message.properties), which will be used when the corresponding locale file is not defined.

     |-locale/                      <-- Directory for localized resources
         |-- message.properties     <-- The one to fallback to if no localized resources is provided for a locale
         |-- en_US/
         |        |- messages.properties
         |-- zh_CN/
                  |- messages.properties

Locale file format is similar to Flash extension. It contains multiple lines of =.


CEP provides a JS interface named initResourceBundle to initialize the locale resources. This should be called during the loading of the extension. CEP initializes the resource bundle for the extension with property values for the current application and UI locale. Then users can access the resource bundle (object) to get the localized strings.

var csInterface = new CSInterface();
csInterface .initResourceBundle();

Sharing localization resources across multiple locales

CEP 6 provides a mechanism to allow multiple locales to share the same localization resources (messages.properties). For example, you want es_MX to use the messages.properties for es_ES. To do so, supply a file named fallback.properties in the es_MX folder as illustrated below.

         |-- message.properties
         |-- es_ES/
         |        |- messages.properties
         |-- es_MX/
                  |- fallback.properties

In the fallback.properties file, you specify which locale's localized resources you want es_MX to use, in below format


Side Notes:

  • The fallback.properties file takes precedence when both messages.properties and fallback.properties exist at the same time.
  • If fallback.properties is malformed, or it specifies a non-existent fallback locale, the messages.properties file in the same directory will be used.

Localized menu

In manifest, it supports to use locale string as menu. For example, in the following manifest, it is using %UI_my_menu_name. UI_my_menu_name is defined as "UI_my_menu_name=xxx" in messages.properties.



Example 1

var cs = new CSInterface();
// Get properties according to current locale of host application.
var resourceBundle = cs.initResourceBundle();
// Use the localized strings.
<script type="text/javascript">document.write(resourceBundle.key1);</script>

Example 2

data-locale is the custom HTML element attribute and you can add to each HTML element that you want to localize.

In this example, there is "key3.value=value3" in the property file. In the HTML file, the input widget has attribute "data-locale" with "key3", then its value is set to "value3".

In this example, there is "key4.innerHTML=value4" in the property file. In the HTML file, the text area widget has attribute "data-locale" with "key4", then its innerHTML is set to "value4".

<script type="text/javascript">
  var cs = new CSInterface();
  // Get properties according to current locale of host application.
  var resourceBundle = cs.initResourceBundle();
  // Use the localized strings.

<input type="submit" value="" data-locale="key3"/>
<textarea rows="10" cols="80" data-locale="key4"></textarea>

Example 3

Use parameters ($1, $2, ...) in localized strings.

var localize = function(key)
  var cs = new CSInterface();
  var resourceBundle = cs.initResourceBundle();
  var localizedStr = resourceBundle[key];
  if (localizedStr)
    var index = 1;
    while (localizedStr.indexOf("$" + index) !== -1)
      localizedStr = localizedStr.replace("$" + index, arguments[index]);
    return localizedStr;
    return '';

Supporting MENA locales

MENA stands for "Middle East and North Africa". Support needs to be provided for Arabic, Hebrew, and NA French languages. Products supporting these languages are: ID, PS, AI, DW and Acrobat.

Language ISO Code
Arabic (Middle East Enabled English Arabic) en_AE
Hebrew (Middle East Enabled English Hebrew) en_IL
NA French fr_MA

If an extension needs to be loaded in host applications in MENA locales, MENA locales must be added to the supported locale list of the extension manifest file. For example:

    <Locale Code="en_AE"/>
    <Locale Code="en_IL"/>
    <Locale Code="fr_MA"/>

Extension localization for MENA locales

Suppose your extension has this directory layout

         |-locale/                                <-- Directory for localized resources
                 |-- message.properties           <-- The one to fallback to if no localized resources is provided for a locale
                 |-- fr_FR/
                 |        |- messages.properties
                 |-- en_GB/
                          |- messages.properties

When CSInterface.initResourceBundle() is called, CEP uses the app UI locale (not app locale) reported by PP to load localized resources. If there is no localized resources for an app UI locale, for example fr_MA, then CEP will fallback to use message.properties located under the “locale” folder.

With MENA feature, PPs map en_AE/en_IL to en_US and fr_MA to fr_FR for app UI locale. In this case, for en_AE and en_IL build of PP, en_US resources will be used if provided and for fr_MA build of PP, fr_FR resources will be used if provided. What extension developers need to do in this case is to provide en_US version of resources for en_AE/en_IL and fr_FR version of resources for fr_MA.

Video/Audio Playback

CEP supports playing video and audio encoded in below formats

Format MIME-Type Misc.
MP4 video/mp4 MPEG 4 files with H.264 video codec and AAC audio codec
Ogg video/ogG Ogg files with Theora video codec and Vorbis audio codec
mp3 audio/mpeg

Here is an example of playing video in your extension

<video poster="http://www.html5rocks.com/en/tutorials/video/basics/star.png" controls>
    <source src="http://www.html5rocks.com/en/tutorials/video/basics/Chrome_ImF.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'/>

One thing to note is that because HTML extensions are hosted in integrating application's windows, video cannot be played in full-screen mode.


WebRTC is targeting to serve stream audio, video capture, like online video conference. In detail, refer to http://www.webrtc.org/. WebRTC is not enabled by default. To enable it, the schema below needS to be added in the manifest file. In detail, refer to Customize CEF command parameters.


For WebRTC related development, CEP runtime just keeps the same experiences as the usage in Chrome. Below is a sample script to demonstrate how to use it in Html pages of extension.

<video id="basic-stream" autoplay></video>

function errorCallback(e) {
    if (e.code == 1) {
        alert('User denied access to their camera');
    } else {
        alert('getUserMedia() not supported in your browser.');
var video = document.querySelector('#basic-stream');
var localMediaStream = null;
if (navigator.getUserMedia) {
    navigator.getUserMedia('video', function(stream) {
    video.src = stream;
    video.controls = true;
    localMediaStream = stream;
    }, errorCallback);
} else if (navigator.webkitGetUserMedia) {
    navigator.webkitGetUserMedia({video: true}, function(stream) {
                        video.src = window.URL.createObjectURL(stream);
                        video.controls = true;
                        localMediaStream = stream;
    }, errorCallback);

For more samples, refer to http://www.html5rocks.com/en/tutorials/getusermedia/intro/.

In addition, there are some limitations of WebRTC support in the CEP runtime due to known issues in CEF3 as below.

Issue. No Description Link Comments
1065 Add support for webrtc based screen sharing/capturing https://code.google.com/p/chromiumembedded/issues/detail?id=1065, http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10982
1139 cefclient w/OSR hangs on exit with the WebRTC Reference App https://code.google.com/p/chromiumembedded/issues/detail?id=1139&q=WebRTC
1144 CEF3 does not support RTCPeerConnection https://code.google.com/p/chromiumembedded/issues/detail?id=1144 Created by CEP. This is a bug on 1453 branch, has been fixed on 1547 and later.
1151 CEF3: Mac: Process is blocked to quit when iframe is using WebRTC https://code.google.com/p/chromiumembedded/issues/detail?id=1151&start=100 Created by CEP.
1153 CEF3: Win: Can not show https://apprtc.appspot.com https://code.google.com/p/chromiumembedded/issues/detail?id=1153&start=100 Created by CEP.

Scroll bar tips

On Mac, scroll bars of panel are smartly hidden by OS (since Lion by design). It can be always shown by settings as below. 1. Click the Apple menu at the top-left of the screen, then select System Preferences. 2. Next, select the General preferences pane; it’s the very first one, up at the top. 3. Under the “Show scroll bars” heading, you’ll find three options: “Automatically based on input device,” “When scrolling,” and “Always.” Chose "Always."

Invisible HTML Extensions

An HTML extension can be invisible during its whole life cycle. This means

  • It always runs invisibly in the background
  • It is never visible

To make an HTML extension invisible:

  • Set extension manifest version to "6.0" or higher.
  • Specify its window type as 'Custom' in the manifest file.
  • Set <AutoVisible> to false in the manifest file.
  • If you do not want the extension to appear in the Window->Extensions menu, do not add the <Menu> tag.
  • If you want the extension to start on specific types of events, specify those events using <StartOn> tag.

Here is an example:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="IamInvisible" ExtensionBundleVersion="1.0" Version="6.0">
        <Extension Id="IamInvisible" Version="1.0"/>
            <Host Name="PHXS" Version="16.0"/>
            <Locale Code="All"/>
            <RequiredRuntime Name="CSXS" Version="6.0"/>
        <Extension Id="IamInvisible">
                        <!-- Photoshop dispatches this event on startup -->
                        <!-- Premiere Pro dispatches this event on startup -->
                        <!-- You can add more events -->

One important thing to note is that not all host applications support Invisible HTML Extension. See table below for more information:

PP Supports Invisible Extension Misc.
Photoshop :white_check_mark:
Premiere Pro :white_check_mark:
Prelude :white_check_mark:
Flash Pro :white_check_mark:
InDesign :x: Doesn't work at all because InDesign doesn't support 'Custom' window type.
InCopy :x: Doesn't work at all because InCopy doesn't support 'Custom' window type.
Illustrator :x: The invisible extension is shown as a visible panel. Possibly that Illustrator treated the 'Custom' window type as 'Panel'. One possible workaround is to use the 'Modeless' window type instead of 'Custom'. However, under certain situations (someone calls PlugPlugLoadExtension for example), the extension will become visible.

Customize CEF Command Line Parameters

Chromium/CEF command line parameters can be passed to CEPHtmlEngine, like --enable-media-stream.

Available Chromium command line parameters can be found on http://peter.sh/experiments/chromium-command-line-switches/. Not all parameters are supported in CEPHtmlEngine:

Parameters Why is it filtered out?
--remote-debugging-port This could overwrite the one in .debug file. Filter out to avoid conflict.
--ignore-certificate-errors This ignores SSL certificate errors. It is a security concern to ignore invalid server certificate, which allows extensions to load files from malicious sites.

All other parameters are passed to underlying CEF. It is up to CEF to decide whether a parameter is supported and what is the behavior.

To use CEF command line parameters 1. Set manifest version to 6.0. 2. Add <CEFCommandLine><Parameter>--param1<Parameter/> ... </CEFCommandLine> in manifest. 3. For key=value parameter, add <CEFCommandLine><Parameter>--param1=value1<Parameter/> ... </CEFCommandLine> in manifest.

Here is an example.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="xx.yy.zz" ExtensionBundleVersion="1.0" Version="6.0">
        <Extension Id="xx.yy.zz" Version="1.0"/>
            <Host Name="PHXS" Version="16.0"/>
            <Host Name="PPRO" Version="9.0"/>
            <Locale Code="All"/>
            <RequiredRuntime Name="CSXS" Version="6.0"/>
        <Extension Id="xx.yy.zz">

Commonly used CEF command parameters:

Parameters Notes
--enable-media-stream Enable media (WebRTC audio/video) streaming.
--enable-speech-input Enable speech input (x-webkit-speech).
--ignore-certificate-errors Ignore SSL certificate errors
--persist-session-cookies Persist session cookies.
--disable-image-loading Disable loading of images from the network. A cached image will still be rendered if requested.
--disable-javascript-open-windows Disable opening of windows via JavaScript.
--disable-javascript-close-windows Disable closing of windows via JavaScript.
--disable-javascript-access-clipboard Disable clipboard access via JavaScript.
--enable-caret-browsing Enable caret browsing.
--proxy-auto-detect This tells Chrome to try and automatically detect your proxy configuration. See more info on http://www.chromium.org/developers/design-documents/network-settings. If you use this parameter in extension, you might not be able to use Charles Proxy (http://www.charlesproxy.com) to capture network traffic on Mac platform, or you might need to configure Charles Proxy properly. Windows platform is not affected.
--user-agent A string used to override the default user agent with a custom one.
--disable-application-cache Disable the ApplicationCache.
--nodejs-disabled Disable NodeJS

Html Extension Persistent

The purpose of persistent is to prevent reloading of the Html extension when it is closed or hidden. Photoshop has provided persistent since version 14.2.

To make a Html extension persistent in Photoshop 1. Upgrade Photoshop version to 14.2 or later 2. Dispatch the event com.adobe.PhotoshopPersistent from Html extension to Photoshop to request persistent

The sample code in extension is like below

function Persistent(inOn) {
    if (inOn) {
        var event = new CSEvent("com.adobe.PhotoshopPersistent", "APPLICATION");
    } else {
        var event = new CSEvent("com.adobe.PhotoshopUnPersistent", "APPLICATION");
    event.extensionId = gExtensionId;

Persistent(true); //persistent to prevent extension from unloading
Persistent(false); //unpersistent

FullScreen API in HtmlExtension

CEP Html extension runtime has not supported fullscreen API yet. The reason is its based library CEF3 does not support fullscreen API, according to https://code.google.com/p/chromiumembedded/issues/detail?id=562

Open URL link in default browser

In Html extension, URL link could be opened in the default browser by calling window.cep.util.openURLInDefaultBrowser(‘http://example.com'), like

<li><button onclick="window.cep.util.openURLInDefaultBrowser('http://www.adobe.com')">Open browser</button></li>

Interaction between extension and PP during extension unload

When an extension is about to be unloaded, the communication channel between extension and PP has been teared down, so there's no easy way for PP to get the extension-related information such as the extension's status.

To fulfill this functionality, the cooperation between extension and PP is needed. CEP did introduce the JavaScript function registerExtensionUnloadCallback but due to issues, this has now been marked deprecated.

Use Node.js APIs

Node.js Support

One of the most prominent feature in CEP is allowing Node.js APIs to be used in HTML extension. Most of the built-in APIs in Node.js version 0.8.22 are available to HTML extensions, with below exceptions:

  • Cluster APIs are not supported.
  • Console APIs are not supported on Windows.

CEP injects following symbols into the root HTML DOM:

  • global, GLOBAL, root - same with the window object
  • Buffer - node's Buffer class
  • process - node's process class
  • require - the magic function that bring you node API
  • module - in node the main script is a special module, this is for the compatibility

Conflicts with Web-Based Require Function

If your app uses libraries like RequireJS that inserts a require function into DOM, you should consider renaming CEP's require function to something else before migrating.

    <script type="text/javascript">window.nodeRequire=window.require && window.require=undefined</script>
    <script type="text/javascript" src="your/require/js/file.js"></script>

Conflicts with Web-Based module Function

If your app uses JQuery that is trying to register itself as nodejs plugin, then you will have to add the script below inside script tag to define window.module as undefined.

<script type="text/javascript">window.module = undefined</script> 

Disable Node.js APIs in iframe

Because of security considerations, CEP provides an option to disable Node.js APIs in iframe. To do so, add a nodejs-disabled="true" attribute to iframe tag. For example:

<iframe id="xxx" class="xxxxx" nodejs-disabled="true">

Forcing the environment implementation.

If you are using RequireJS, and the text plugin tries to detect what environment it is available for loading text resources, Node, XMLHttpRequest (XHR) or Rhino, but sometimes the Node or Rhino environment may have loaded a library that introduces an XHR implementation. You can force the environment implementation to use by passing an "env" module config to the plugin:

        config: {
            text: {
                //Valid values are 'node', 'xhr', or 'rhino'
                env: 'rhino'

Node.js Modules

JavaScript Modules

All third-party node JavaScript modules are supported. The root search path of third-party modules is the directory which contains your html file. For example, when you do require in file:///your_extension/index.html, CEP will lookup modules under file:///your_extension/node_modules, this rule is exactly the same with upstream node.

Native Modules

Node.js native modules are not directly support since CEP is using a different V8 version from the official node.


Use Environment Variables

process.env.ENV_VARIABLE // ENV_VARIABLE is the name of the variable you want to access.

Use Node.js to download files


Limitation of cep.process.stdout/stderr

There is an known limitation of cep.process.stdout/stderr which is targeting to capture one time of stdout/stderr output. For applications that have not integrated CEP 5, there are two workarounds suggested as follows: 1. Embed cep.process.stdout/stderr, like below

function getSTDOutput()
        window.cep.process.stdout(pid, function(output) {
    //your code is here
        var result = window.cep.process.isRunning(pid);
        if (result.data == true)
            setTimeout(getSTDOutput, 1000);
  1. Join all stdout output as one, like below
var str1 = 'abcdef';
var str2 = '12345';
var str3 = 'gghhtt';
console.log(str1 + str2 + str3);

An example on how to get curl downloading progress through stderr:

        var downloadPid = -1;
        function getStdErrOutput()
            window.cep.process.stderr(downloadPid, function(progress) {
                                      var keys = progress.split(new RegExp('[# ]', 'g'));
                                      for(i=0; i<keys.length; i++){
                                        if (keys[i] != '') {
            var result = window.cep.process.isRunning(downloadPid);
            if (result.data == true)
                setTimeout(getStdErrOutput, 100);
        var doDownload = function() {
            var qURL = 'http://code.jquery.com/jquery-1.11.0.min.js';
            var dest = '/tmp/test.js';
            console.log("ext download (curl) " + qURL + " " + dest);
            var result = window.cep.process.createProcess('/usr/bin/curl', qURL, '-#', '-o', dest);
            downloadPid = result.data;
            console.log("download pid is " + downloadPid);


Since CEP 5, NodeJS is integrated into CEP runtime and users could invoke the standard APIs of NodeJS in extension directly. For applications that have integrated CEP 5, refer to http://nodejs.org/api/process.html for how to use the global process object in NodeJS.

Drag and Drop

Use Drag and Drop

CEP support HTML 5 Drag and Drop. There are four types.

  • Drag and drop inside a html extension.
  • Drag and drop between two html extensions
  • Drag and drop between html extension and its host application.
  • Drag and drop between html extension and operating system (e.g. Desktop or Browser).

To learn about HTML 5 Drag and Drop and how to use it by JavaScript, please refer to http://www.w3.org/TR/html5/editing.html#dnd. Here are some demos:

Disable Drag and Drop

Extension developers can disable the default behavior of DnD by JavaScript.

Method 1

<body ondragover="return false" ondrop="return false">

Method 2 (using jQuery)

$(document.body).on('dragover drop', function(e) {

Please read HTML 5 standard for more details. http://www.w3.org/TR/html5/editing.html#event-dragenter

Host Application Support

As far as CEP is concerned, there are four types of Drag and Drop.

  • Drag and Drop inside HTML extension.
  • Drag and Drop between HTML extension.
  • Drag and Drop between HTML extension and operating system (e.g. Desktop or Browser).
  • Drag and Drop between HTML extension and its host application.

For the first three types of Drag and Drop, they do not need the involvement of the host application. This is the current support status:

Drag and Drop Result Comments
Inside one HTML extension :white_check_mark:
From HTML extension to operating system :white_check_mark:
Between two HTML extensions :white_check_mark:

Extending the D&D capabilities requires involvement of the host application. Below is the current support status for extending D&D features to support the following drag types in JavaScript:

Host Application text/plain text/uri-list text/html
After Effects :x: :x: :x:
Dreamweaver :white_check_mark: :x: :x:
Flash :white_check_mark: :x: :x:
Illustrator :white_check_mark: :x: :x:
InCopy :white_check_mark: :x: :x:
InDesign :white_check_mark: :x: :x:
Photoshop :x: :x: :x:
Prelude :x: :x: :x:
Premiere Pro :x: :x: :x:

External JavaScript Libraries

CEPHTMLEngine does not restrict using any extension JavaScript libraries. As long as a library can be used in CEF Client or Chrome browser, it should be usable in CEPHTMLEngine.

Here are some JavaScript which has been used successfully:

Load Multiple JSX files

Html extensions can load jsx files which define functions and objects into ExtendScript environment, thus, the extension can refer them by evalScript. There are two approaches to load jsx files: Define <ScriptPath> node in manifest.xml, and the value of the node is the relative path for the jsx file. For example:

<Extension Id="com.adobe.CEPHTMLTEST.Panel1">
            <ScriptPath>./jsx/example.jsx</ScriptPath> <!-- ExtensionRootPath/jsx/example.jsx -->

Call $.evalFile(jsxFile) to load other jsx files. Probably developers will refer to $.fileName to find out the jsx files path they expect. The value of $.fileName should be the current executed jsx file path. For example:

var extensionPath = $.fileName.split('/').slice(0, -1).join('/') + '/';  
// The value of $.fileName should be ExtensionRootPath/jsx/example.jsx, 
// while the value of extensionPath should be "ExtensionRootPath/jsx/"
$.evalFile(extensionPath + 'example1.jsx');
$.evalFile(extensionPath + 'example2.jsx');
$.evalFile(extensionPath + 'example3.jsx');

But if the $.fileName is referred in the FIRST LOADED jsx file, the value is not correct. That is to say, if the snippet above runs in example.jsx which is referred in the manifest.xml, the error will arise. So, PLEASE AVOID using $.fileName in the FIRST LOADED jsx file, maybe this is a limitation or a bug of ExtendScript. The workaround is refer it in the second loaded and afterward jsx files. For example:

// After finishing loading the jsx file referred in the manifest.xml, please use evalScript
// of CSInterface to load other jsx files. "anotherJSXFile" is not the first loaded jsx file,
// so the value of "$.fileName" in it's stage is correct.
CSInterface.evalScript('$.evalFile(anotherJSXFile)', callback);

// Or in the first loaded jsx file, load another jsx file, 
// and the value of "$.fileName" is correct in this file.
// Given the code is running this example.jsx which is referred in the manifest.xml. 
// In the stage of "hardCodeJSXFile", the value of "$.fileName" is correct too.

NPAPI plug-ins

The support for NPAPI plug-ins had been dropped since CEP 4.2. Don't embed Flash or Java Applet in your extension as they all depend on NPAPI plug-ins.

Increase/Decrease font size in Html Panel

There are couples of JavaScript ways to increase or decrease font size in Html panels either by plain JavaScript or JQuery. The following are the two pieces of a sample snippet to achieve this. One is in plain JavaScript and the other uses JQuery library.

Plain JavaScript

Use document.body.style.fontSize to change font size in page.

   <script type="text/javascript" language="javascript">
        window.onload = function() {
            var fontchange = document.createElement("div");
            var fontchangelink = function(fontsize, desc) {
                var a = document.createElement('a');
                a.style.margin = "5px";
                a.onclick = function() {
                    document.body.style.fontSize = fontsize + 'pt';
                a.innerHTML = desc;
                return a;

            fontchange.appendChild(document.createTextNode("Change font size:"));
            fontchange.appendChild(fontchangelink(9, "1"));
            fontchange.appendChild(fontchangelink(11, "2"));
            fontchange.appendChild(fontchangelink(13, "3"));

            document.body.insertBefore(fontchange, document.body.childNodes[0]);

For the full example, please refer to the html file - ResizeFont_plain_js.html


Use $('html').css('font-size', size) to change font size in page.

    <script type="text/javascript" language="javascript">
                  // Reset Font Size
                  var originalFontSize = $('html').css('font-size');
                    $('html').css('font-size', originalFontSize);

                  // Increase Font Size
                     var currentFontSize = $('html').css('font-size');
                     var currentFontSizeNum = parseFloat(currentFontSize, 10);
                     var newFontSize = currentFontSizeNum*1.2;
                     $('html').css('font-size', newFontSize);
                     return false;

                  // Decrease Font Size
                     var currentFontSize = $('html').css('font-size');
                     var currentFontSizeNum = parseFloat(currentFontSize, 10);
                     var newFontSize = currentFontSizeNum*0.8;
                     $('html').css('font-size', newFontSize);
                     return false;

For the full example, please refer to the html file - ResizeFont_JQuery.html

The ways above are the common used solution for JavaScript developers to increase or decrease fonts.

Obfuscate Html Extension

You could choose to obfuscate it in term of hiding internal Html extensions. But please keep in mind that there is no final/reliable way to obfuscate HTML/JavaScript staff, because it is not so hard to revert it back to plain text. Even if the extension is signed, users still could add entry "PlayerDebugMode=1" in registry/plist to load the modified extension. Even in flash world, there still might be ways to decompile swf to get your IMS client-secret.

So there is no reliable way to prevent user finding your client-secret if IMS APIs are invoked in HTML extension. Alternatively, you could choose to call IMS API in native side, and communicate HTML extensions by CSXS event/Extend Script.

Check Internet Connection

if (navigator.onLine === true)
  //system is online
  //system is offline

Set Mouse Cursor

Please refer to http://jsfiddle.net/BfLAh/1390/

    $("#image").css({left:e.pageX, top:e.pageY});

De-obfuscate JavaScript

Please refer to http://jsbeautifier.org/


Due to the X-Frame-Options header a number of HTTPS websites are unavailable to host in an iframe. An alternative to displaying HTTPS content is to use the window.location.href e.g. iFrame alternative for HTTPS content

// html
<body onLoad="onLoaded()">
<!--iframe src="https://www.trello.com"></iframe--> <!-- this line can be deleted as HTTPS blocks content being displayed in an iframe -->

// javascript
function onLoaded() {
  window.location.href = "https://www.trello.com";


CEP supports HTML title attribute to show the tooltip on Windows. However, it's not supported on Mac due to off-screen rendering. The alternative is use JavaScript instead, please refer to http://www.a2zwebhelp.com/bootstrap-tooltips for good examples.

"onchange" event is not triggered when inputting non-English language on Mac.

If user inputs non-English characters on Mac, the "onchange" event of the html element is not triggered, due to a design limitation.