Pages

Tuesday, June 5, 2012

Building Chrome extensions with GWT


Recently i took a look into Google Chrome Web Store. 
The web store as you can take a look for yourself, is loaded with apps, which many of them are given free.


I decided to understand what it takes from a simple developer like myself to build such an app which I can run on my browser & distribute on the Chrome Web Store.


Surprisingly, it does not take a lot of effort to write such an app/extension, especially if you're already familiar with a little bit of web development.
Google Chrome Extensions: http://code.google.com/chrome/extensions/overview.html


So what you really need is some knowledge of JavaScript, HTML & CSS to make a neat extension.


For me the real story began when I realized that for such requirements, one could actually use the (amazing) GWT framework to write such an extension. 
If you never looked into GWT, my personal recommendation is that you absolutely must in case you're into web-development. GWT is a truly incredible tool from my experience.


If you look at the file structure of such an extension, you can see from the "Google Chrome Extensions - Getting Started" tutorial (http://code.google.com/chrome/extensions/getstarted.html) that we're talking about 4 important files:

  1. manifest.json - This file if you like is some kind of a descriptor of your extension.
  2. An HTML file "hosting" your extension. 
  3. A JavaScript file containing all the cool interaction with your extension, and your script.
  4. A CSS file - for styling.

When you write your GWT application in Java and compile it afterwards, you get such files, except for the first one. That you will have to add yourself to the project.


So enough of "blabing" - Lets dive right into "How to do it" (if you're impatient, just scroll to the bottom of the page to download a zip file of the project):


1. Start a new web-application project in Eclipse, and let it create the default skeleton of the project.



2. After the project has been created, make some clean up in the project's structure by deleting the "server" and the "shared" packages.


3. Edit the .gwt.xml file and make sure the file's contents is something like:
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='mychromeext'>

  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>
  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.clean.Clean'/>
  <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->
  <!-- Other module inherits                                      -->
  <!-- Specify the app entry point class.                         -->
  <entry-point class='com.techdrum.chrome.ext.example.app.client.MyChromeExt'/>
  <!-- Specify the paths for translatable code                    -->
  <source path='client'/>

  <set-property name="user.agent" value="safari"/>
</module>
4. Delete the auto-generated Async file and it's matching service file.

5. Write some GWT code in the MyChromeExt.java file, something very simple like - 

package com.techdrum.chrome.ext.example.app.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class MyChromeExt implements EntryPoint {
/**
* This is the entry point method.
*/
public void onModuleLoad() {
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
// We can add style names to widgets
sendButton.addStyleName("sendButton");
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(nameField);
RootPanel.get("sendButtonContainer").add(sendButton);
// Focus the cursor on the name field when the app loads
nameField.setFocus(true);
nameField.selectAll();
}
}
6. Almost done! Create a new file called "manifest.json" and place it under the "war" directory.
The file should contain the following:

{
  "name": "My GWT ext",
  "version": "1.0",
  "description": "Simple GWT Extension!",
  "browser_action": {
    "default_popup": "MyChromeExt.html"
  },
  "permissions": [
  ],
   "web_accessible_resources": [
    "mychromeext /E949D2D1E7CE643145D4D3D5FC28BB31.cache.html"
  ]
}
*Note the stressed file name!

7. Compile the project.

8. Match the file name with the .cache.html suffix under the directory mychromeext to the one mentioned in paragraph 6. 
So if you have something like BF13DA7B8EE69ECA8CAA8F35290E1E1E.cache.html
then your new manifest.json should look like:

{
  "name": "My GWT ext",
  "version": "1.0",
  "description": "Simple GWT Extension!",
  "browser_action": {
    "default_popup": "MyChromeExt.html"
  },
  "permissions": [
  ],
   "web_accessible_resources": [
    "mychromeext/BF13DA7B8EE69ECA8CAA8F35290E1E1E.cache.html"
  ]
}

9. That's it! Now you're ready to deploy your new extension on Google Chrome.
    To do that - open Chrome, and open the Extensions window:


10. Make sure to check the "Developer Mode" box, and then click on "Load unpacked extension..."
Select the war directory of the compiled project, and your new extension should appear on the Chrome toolbar next to the address bar.
When clicking on it, you'll see your app:


Congratulations!



Get the full code of the project here.

7 comments:

  1. This is strange. I am doing the same things and it is not working. I think with new manifest_version: 2 there are security issues.

    Vishal

    ReplyDelete
    Replies
    1. Hmm... I took a quick look into it, and I think you have a point.
      When defining the manifest version to be 2, it doesn't seem to work.

      I think the reason is because of the "Content Security Policy" of chrome extensions that does not allow inline scripting.
      If you take a look into the compiled GWT files, you can see that it involves HTML files with inlined Javascript in them.
      You can read about it at:
      http://code.google.com/chrome/extensions/contentSecurityPolicy.html

      Hopefully Google will offer some solution to this problem in the future, as I think that building extensions with GWT can be very powerful and fun.

      Maybe in the future they will give the possibility of separating the JS completely from the HTML when compiling, this way bypassing
      this limitation. (You could try to do it manually... but I wouldn't suggest it).

      Delete
  2. "Get the full code of the project here." The file behind the link was deleted. Where can I get the zip file of the example?

    ReplyDelete
    Replies
    1. Hi Jürgen,
      I can see the link is now dead, but instead you can have a look at the QuickPik project I uploaded to GitHub:
      https://github.com/nirgit/Quickpik

      And check out the post related to it as well on the blog here:
      http://tech-drum.blogspot.com/2012/08/gwt-chrome-extension-using-version-2.html

      Good luck!

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I would like to know how to run a java code in the extension created using GWT. I need to create an extension such that on-click of the extension, the java code should be run

    ReplyDelete