Skip to content

Commit

Permalink
Closes mozilla-mobile#2150: Add engine API to install WebExtensions
Browse files Browse the repository at this point in the history
  • Loading branch information
csadilek committed Feb 22, 2019
1 parent 886d532 commit f77e9ba
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import mozilla.components.concept.engine.EngineSessionState
import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.history.HistoryTrackingDelegate
import mozilla.components.concept.engine.webextension.WebExtension
import org.json.JSONObject
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoWebExecutor
import org.mozilla.geckoview.WebExtension as GeckoWebExtension

/**
* Gecko-based implementation of Engine interface.
Expand Down Expand Up @@ -59,6 +61,19 @@ class GeckoEngine(
executor.speculativeConnect(url)
}

/**
* See [Engine.installWebExtension].
*/
override fun installWebExtension(ext: WebExtension) {
fakeRuntime.registerWebExtension(GeckoWebExtension(ext.id, ext.url))
}

// TODO remove once GV API lands
class FakeRuntime {
fun registerWebExtension(ext: GeckoWebExtension) { }
}
internal var fakeRuntime = FakeRuntime()

override fun name(): String = "Gecko"

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.geckoview

// TODO Remove this mock
data class WebExtension(val id: String, val location: String)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import android.content.Context
import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.UnsupportedSettingException
import mozilla.components.concept.engine.webextension.WebExtension
import mozilla.components.support.test.argumentCaptor
import mozilla.components.support.test.mock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand All @@ -23,6 +25,7 @@ import org.mozilla.geckoview.ContentBlocking
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoRuntimeSettings
import org.mozilla.geckoview.GeckoWebExecutor
import org.mozilla.geckoview.WebExtension as GeckoWebExtension
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment

Expand Down Expand Up @@ -139,9 +142,24 @@ class GeckoEngineTest {
val executor: GeckoWebExecutor = mock()

val engine = GeckoEngine(context, runtime = runtime, executorProvider = { executor })

engine.speculativeConnect("https://www.mozilla.org")

verify(executor).speculativeConnect("https://www.mozilla.org")
}

@Test
fun `install web extension`() {
val runtime = mock(GeckoRuntime::class.java)
val engine = GeckoEngine(context, runtime = runtime)

// TODO remove once GV API lands
engine.fakeRuntime = mock(GeckoEngine.FakeRuntime::class.java)

engine.installWebExtension(WebExtension("test-webext", "resource://android/assets/extensions/test"))

val extCaptor = argumentCaptor<GeckoWebExtension>()
verify(engine.fakeRuntime).registerWebExtension(extCaptor.capture())

assertEquals("test-webext", extCaptor.value.id)
assertEquals("resource://android/assets/extensions/test", extCaptor.value.location)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package mozilla.components.concept.engine

import android.content.Context
import android.util.AttributeSet
import mozilla.components.concept.engine.webextension.WebExtension
import org.json.JSONObject
import java.lang.UnsupportedOperationException

/**
* Entry point for interacting with the engine implementation.
Expand Down Expand Up @@ -56,6 +58,15 @@ interface Engine {
*/
fun speculativeConnect(url: String)

/**
* Installs the provided extension in this engine.
*
* @param ext the [WebExtension] to install.
* @throws UnsupportedOperationException if this engine doesn't support web extensions.
*/
fun installWebExtension(ext: WebExtension): Unit =
throw UnsupportedOperationException("Web extension support is not available in this engine")

/**
* Provides access to the settings of this engine.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.concept.engine.webextension

/**
* Represents a browser extension based on the WebExtension API:
* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions
*
* @property id the unique ID of this extension.
* @property url the url pointing to a resources path for locating the extension
* within the APK file e.g. resource://android/assets/extensions/my_web_ext.
*/
data class WebExtension(
val id: String,
val url: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.concept.engine

import android.content.Context
import android.util.AttributeSet
import mozilla.components.concept.engine.webextension.WebExtension
import org.json.JSONObject
import org.junit.Test
import java.lang.UnsupportedOperationException

class EngineTest {

@Test(expected = UnsupportedOperationException::class)
fun `throws exception if webextensions not supported`() {
val engine = object : Engine {
override fun createView(context: Context, attrs: AttributeSet?): EngineView {
throw NotImplementedError("Not needed for test")
}

override fun createSession(private: Boolean): EngineSession {
throw NotImplementedError("Not needed for test")
}

override fun createSessionState(json: JSONObject): EngineSessionState {
throw NotImplementedError("Not needed for test")
}

override fun name(): String {
throw NotImplementedError("Not needed for test")
}

override fun speculativeConnect(url: String) {
throw NotImplementedError("Not needed for test")
}

override val settings: Settings
get() = throw NotImplementedError("Not needed for test")
}
engine.installWebExtension(WebExtension("my-ext", "resource://path"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.concept.engine.webextension

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Test

class WebExtensionTest {

@Test
fun createWebExtension() {
val ext1 = WebExtension("1", "url1")
val ext2 = WebExtension("2", "url2")

assertNotEquals(ext1, ext2)
assertEquals(ext1, WebExtension("1", "url1"))
assertEquals("1", ext1.id)
assertEquals("url1", ext1.url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ package org.mozilla.samples.browser
import android.content.Context
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.webextension.WebExtension

/**
* Helper class for lazily instantiating components needed by the application.
*/
class Components(private val applicationContext: Context) : DefaultComponents(applicationContext) {
override val engine: Engine by lazy {
GeckoEngine(applicationContext, engineSettings)
GeckoEngine(applicationContext, engineSettings).apply {

// TODO verify how to specify the extension path. This is based on:
// https://phabricator.services.mozilla.com/D16268#change-Fdft9eM6r9i1
installWebExtension(WebExtension("mozac-helloworld", "resource://android/assets/extensions/helloworld"))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
console.log("Hello World!");

document.body.style.border = "5px solid red";
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"manifest_version": 2,
"name": "Mozilla Android Components - Hello World",
"version": "1.0",
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["helloworld.js"],
"run_at": "document_end"
}
]
}

0 comments on commit f77e9ba

Please sign in to comment.