lucidd / scala-js-chrome   0.5.8

Contributors Wanted MIT License GitHub

ScalaJS bindings for Chrome Extention/App and ChromeOS APIs

Scala versions: 2.12 2.11 2.10
Scala.js versions: 0.6
sbt plugins: 0.13

Chrome for Scala.js Build Status Gitter Latest version Javadocs

The goal of this project is to provide an easy and typesafe way to create Chrome apps and extensions in Scala using the Scala.js project.

Chrome API bindings

The bindings provide access to the Chrome app and extension APIs. There are two levels for each API. One that provides the raw JavaScript bindings and a second one which wraps the raw API in a more Scala idiomatic way.

The package structure is similar to the original JavaScript API.

// original JavaScript
chrome.system.cpu.getInfo(function(info){
  if (chrome.runtime.lastError === undefined) {
    console.log(info);
  } else {
    console.log("ohoh something went wrong!");
  }
});
// raw bindings
chrome.system.cpu.bindings.CPU.getInfo((info: CPUInfo) => {
    if (chrome.runtime.bindings.Runtime.lastError.isEmpty) {
        println(info)
    } else {
        println("ohoh something went wrong!")
    }
})

// Scala idiomatic way using Future
chrome.system.cpu.CPU.getInfo.onComplete {
  case Success(info) => println(info)
  case Failure(error) => println("ohoh something went wrong!")
}

The Scala idiomatic binding provides the following general changes:

  • Futures instead of callbacks
  • Error handling using types like Future / Try instead of global error variable.
  • Using Option for things that may or may not be defined.

SBT Plugin

The job of the SBT plugin is to help with common tasks for developing Chrome apps/extensions. It also provides a way to configure your app/extension in your SBT file and automatically generate the manifest file.

  • chromePackage will create a ZIP file you can upload to the Chrome Web Store.
  • chromeUnpackedOpt (or chromeUnpackedFast) will build your projects with (or without) optimizations enabled. The output will be in target/chrome/unpacked-opt (or target/chrome/unpacked-fast) and can be loaded by Chrome as an unpacked extension/app.

Getting Started

Add this to your project/plugins.sbt:

addSbtPlugin("net.lullabyte" % "sbt-chrome-plugin" % "0.5.0")

Add this to your project dependencies:

"net.lullabyte" %%% "scala-js-chrome" % "0.5.0"

to use the <project-name>-f{ast,ull}opt-bundle.js generated by scalajs-bundler add the following to your build.sbt:

fastOptJsLib := Attributed.blank((webpack in (Compile, fastOptJS)).value.head)
fullOptJsLib := Attributed.blank((webpack in (Compile, fullOptJS)).value.head)

NOTE: if code seems to be executing duplicate times unintentionally, try removing these lines from the project's build.sbt

scalaJSUseMainModuleInitializer := true
scalaJSUseMainModuleInitializer in Test := false

Creating a basic Window

import chrome.app.runtime.bindings.LaunchData
import chrome.app.window.Window
import utils.ChromeApp

import scalajs.concurrent.JSExecutionContext.Implicits.queue

object ChromeAppExample extends ChromeApp {

  override def onLaunched(launchData: LaunchData): Unit = {
    println("hello world from scala!")
    Window.create("assets/html/App.html").foreach { window =>
      /**
         Access to the document of the newly created window.
         From here you can change the HTML of the window with whatever
         library you want to use.
      */
      window.contentWindow.document
    }
  }

}

For a more complete example see chrome-system-monitor and scala-js-chrome examples.

UI Libraries

There are already multiple libraries to manipulate HTML and build your UI available for Scala.js:

Known Issues

In Chrome apps and extensions there are multiple places where you can run JavaScript. Normally you split your logic into different files and load them into whatever context they need to run. Since Scala.js compiles your whole project into one big file all contexts need to load this big file with all the logic even if they only need a small subset. This can cause your app you use more memory then it need to. In some cases this can be worked around for example the a background page can manipulate the DOM of an App window so you don't need any JavaScript at all in the window itself.