author | Justin D'Arcangelo <jdarcangelo@mozilla.com> |
Wed, 08 Jun 2016 21:37:06 -0400 | |
changeset 301199 | 8b658fb9b51049d588c3ec08d5ff01d4a3641b90 |
parent 301198 | 85bb94bb8c3e98b79c3efe3b71bd4b8d8854d0aa |
child 301200 | 6d21ad903c7a293ffe7a76460f9f6f3fbe0b9104 |
push id | 78247 |
push user | kvijayan@mozilla.com |
push date | Thu, 09 Jun 2016 01:37:17 +0000 |
treeherder | mozilla-inbound@8b658fb9b510 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sebastian |
bugs | 1272102 |
milestone | 50.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -80,16 +80,17 @@ import org.mozilla.gecko.tabs.TabsPanel; import org.mozilla.gecko.telemetry.TelemetryUploadService; import org.mozilla.gecko.telemetry.TelemetryCorePingDelegate; import org.mozilla.gecko.telemetry.measurements.SearchCountMeasurements; import org.mozilla.gecko.toolbar.AutocompleteHandler; import org.mozilla.gecko.toolbar.BrowserToolbar; import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState; import org.mozilla.gecko.toolbar.ToolbarProgressView; import org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt; +import org.mozilla.gecko.updater.PostUpdateHandler; import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.ActivityUtils; import org.mozilla.gecko.util.Clipboard; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.Experiments; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GamepadUtils; import org.mozilla.gecko.util.GeckoEventListener; @@ -305,16 +306,17 @@ public class BrowserApp extends GeckoApp private final DynamicToolbar mDynamicToolbar = new DynamicToolbar(); private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList( (BrowserAppDelegate) new AddToHomeScreenPromotion(), (BrowserAppDelegate) new ScreenshotDelegate(), (BrowserAppDelegate) new BookmarkStateChangeDelegate(), (BrowserAppDelegate) new ReaderViewBookmarkPromotion(), (BrowserAppDelegate) new ContentNotificationsDelegate(), + (BrowserAppDelegate) new PostUpdateHandler(), new TelemetryCorePingDelegate() )); @NonNull private SearchEngineManager mSearchEngineManager; // Contains reference to Context - DO NOT LEAK! private boolean mHasResumed;
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java @@ -154,16 +154,17 @@ OnSharedPreferenceChangeListener private static final String PREFS_SCREEN_ADVANCED = NON_PREF_PREFIX + "advanced_screen"; public static final String PREFS_HOMEPAGE = NON_PREF_PREFIX + "homepage"; public static final String PREFS_HISTORY_SAVED_SEARCH = NON_PREF_PREFIX + "search.search_history.enabled"; private static final String PREFS_FAQ_LINK = NON_PREF_PREFIX + "faq.link"; private static final String PREFS_FEEDBACK_LINK = NON_PREF_PREFIX + "feedback.link"; public static final String PREFS_NOTIFICATIONS_CONTENT = NON_PREF_PREFIX + "notifications.content"; public static final String PREFS_NOTIFICATIONS_CONTENT_LEARN_MORE = NON_PREF_PREFIX + "notifications.content.learn_more"; public static final String PREFS_NOTIFICATIONS_WHATS_NEW = NON_PREF_PREFIX + "notifications.whats_new"; + public static final String PREFS_APP_UPDATE_LAST_BUILD_ID = "app.update.last_build_id"; private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF"; // This isn't a Gecko pref, even if it looks like one. private static final String PREFS_BROWSER_LOCALE = "locale"; public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
new file mode 100644 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/updater/PostUpdateHandler.java @@ -0,0 +1,154 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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.gecko.updater; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import com.keepsafe.switchboard.SwitchBoard; + +import org.mozilla.gecko.AppConstants; +import org.mozilla.gecko.BrowserApp; +import org.mozilla.gecko.delegates.BrowserAppDelegateWithReference; +import org.mozilla.gecko.GeckoSharedPrefs; +import org.mozilla.gecko.preferences.GeckoPreferences; +import org.mozilla.gecko.util.ThreadUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Perform tasks in the background after the app has been installed/updated. + */ +public class PostUpdateHandler extends BrowserAppDelegateWithReference { + private static final String LOGTAG = "PostUpdateHandler"; + + @Override + public void onStart(BrowserApp browserApp) { + final SharedPreferences prefs = GeckoSharedPrefs.forApp(browserApp); + + // Check if this is a new installation or if the app has been updated since the last start. + if (!AppConstants.MOZ_APP_BUILDID.equals(prefs.getString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, null))) { + Log.d(LOGTAG, "Build ID changed since last start: '" + AppConstants.MOZ_APP_BUILDID + "', '" + prefs.getString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, null) + "'"); + + // Copy the bundled system add-ons from the APK to the data directory. + copyFeaturesFromAPK(); + } + } + + /** + * Copies the /assets/features folder out of the APK and into the app's data directory. + */ + private void copyFeaturesFromAPK() { + final BrowserApp browserApp = getBrowserApp(); + if (browserApp == null) { + return; + } + + final String dataDir = browserApp.getApplicationInfo().dataDir; + final String sourceDir = browserApp.getApplicationInfo().sourceDir; + final File applicationPackage = new File(sourceDir); + + final String assetsPrefix = "assets/"; + final String fullPrefix = assetsPrefix + "features/"; + + final SharedPreferences prefs = GeckoSharedPrefs.forApp(browserApp); + + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + Log.d(LOGTAG, "Copying system add-ons from APK to dataDir"); + + try { + final ZipFile zip = new ZipFile(applicationPackage); + final Enumeration<? extends ZipEntry> zipEntries = zip.entries(); + + final byte[] buffer = new byte[1024]; + + while (zipEntries.hasMoreElements()) { + final ZipEntry fileEntry = zipEntries.nextElement(); + final String name = fileEntry.getName(); + + if (fileEntry.isDirectory()) { + // We'll let getDataFile deal with creating the directory hierarchy. + continue; + } + + // Read from "assets/features/**". + if (!name.startsWith(fullPrefix)) { + continue; + } + + // Write to "features/**". + final String nameWithoutPrefix = name.substring(assetsPrefix.length()); + final File outFile = getDataFile(dataDir, nameWithoutPrefix); + if (outFile == null) { + continue; + } + + final InputStream fileStream = zip.getInputStream(fileEntry); + try { + writeStream(fileStream, outFile, fileEntry.getTime(), buffer); + } finally { + fileStream.close(); + } + } + + zip.close(); + } catch (IOException e) { + Log.e(LOGTAG, "Error copying system add-ons from APK.", e); + } + + // Save the Build ID so we don't perform post-update operations again until the app is updated. + prefs.edit().putString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, AppConstants.MOZ_APP_BUILDID).apply(); + } + }); + } + + /** + * Return a File instance in the data directory, ensuring + * that the parent exists. + * + * @return null if the parents could not be created. + */ + private File getDataFile(final String dataDir, final String name) { + File outFile = new File(dataDir, name); + File dir = outFile.getParentFile(); + + if (!dir.exists()) { + Log.d(LOGTAG, "Creating " + dir.getAbsolutePath()); + if (!dir.mkdirs()) { + Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath()); + return null; + } + } + + return outFile; + } + + private void writeStream(InputStream fileStream, File outFile, final long modifiedTime, byte[] buffer) + throws FileNotFoundException, IOException { + final OutputStream outStream = new FileOutputStream(outFile); + try { + int count; + while ((count = fileStream.read(buffer)) > 0) { + outStream.write(buffer, 0, count); + } + + outFile.setLastModified(modifiedTime); + } finally { + outStream.close(); + } + } +}
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -619,16 +619,17 @@ gbjar.sources += ['java/org/mozilla/geck 'toolbar/TabCounter.java', 'toolbar/ToolbarDisplayLayout.java', 'toolbar/ToolbarEditLayout.java', 'toolbar/ToolbarEditText.java', 'toolbar/ToolbarPrefs.java', 'toolbar/ToolbarProgressView.java', 'TouchEventInterceptor.java', 'trackingprotection/TrackingProtectionPrompt.java', + 'updater/PostUpdateHandler.java', 'updater/UpdateService.java', 'updater/UpdateServiceHelper.java', 'util/Experiments.java', 'widget/ActivityChooserModel.java', 'widget/AllCapsTextView.java', 'widget/AnchoredPopup.java', 'widget/AnimatedHeightLayout.java', 'widget/BasicColorPicker.java',
--- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -261,16 +261,20 @@ var Addons = { // Clear all content before filling the addons let list = document.getElementById("addons-list"); list.innerHTML = ""; aAddons.sort(function(a,b) { return a.name.localeCompare(b.name); }); for (let i=0; i<aAddons.length; i++) { + // Don't create item for system add-ons. + if (aAddons[i].isSystem) + continue; + let item = self._createItemForAddon(aAddons[i]); list.appendChild(item); } // Add a "Browse all Firefox Add-ons" item to the bottom of the list. let browseItem = self._createBrowseItem(); list.appendChild(browseItem); });
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/bootstrap.js @@ -0,0 +1,151 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +const {classes: Cc, interfaces: Ci, manager: Cm, results: Cr, utils: Cu, Constructor: CC} = Components; + +Cm.QueryInterface(Ci.nsIComponentRegistrar); + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Console", + "resource://gre/modules/Console.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gFlyWebBundle", function() { + return Services.strings.createBundle("chrome://flyweb/locale/flyweb.properties"); +}); + +const FLYWEB_ENABLED_PREF = "dom.flyweb.enabled"; + +let factory, menuID; + +function AboutFlyWeb() {} + +AboutFlyWeb.prototype = Object.freeze({ + classDescription: "About page for displaying nearby FlyWeb services", + contractID: "@mozilla.org/network/protocol/about;1?what=flyweb", + classID: Components.ID("{baa04ff0-08b5-11e6-a837-0800200c9a66}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), + + getURIFlags: function(aURI) { + return Ci.nsIAboutModule.ALLOW_SCRIPT; + }, + + newChannel: function(aURI, aLoadInfo) { + let uri = Services.io.newURI("chrome://flyweb/content/aboutFlyWeb.xhtml", null, null); + let channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo); + channel.originalURI = aURI; + return channel; + } +}); + +function Factory(component) { + this.createInstance = function(outer, iid) { + if (outer) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + return new component(); + }; + this.register = function() { + Cm.registerFactory(component.prototype.classID, component.prototype.classDescription, component.prototype.contractID, this); + }; + this.unregister = function() { + Cm.unregisterFactory(component.prototype.classID, this); + } + Object.freeze(this); + this.register(); +} + +let windowListener = { + onOpenWindow: function(aWindow) { + // Wait for the window to finish loading + let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + domWindow.addEventListener("UIReady", function onLoad() { + domWindow.removeEventListener("UIReady", onLoad, false); + loadIntoWindow(domWindow); + }, false); + }, + + onCloseWindow: function(aWindow) {}, + onWindowTitleChange: function(aWindow, aTitle) {} +}; + +let FlyWebUI = { + init() { + factory = new Factory(AboutFlyWeb); + + // Load into any existing windows + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + loadIntoWindow(domWindow); + } + + // Load into any new windows + Services.wm.addListener(windowListener); + }, + + uninit() { + factory.unregister(); + + // Stop listening for new windows + Services.wm.removeListener(windowListener); + + // Unload from any existing windows + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + unloadFromWindow(domWindow); + } + } +}; + +function loadIntoWindow(aWindow) { + menuID = aWindow.NativeWindow.menu.add({ + name: gFlyWebBundle.GetStringFromName("flyweb-menu.name"), + callback() { + aWindow.BrowserApp.addTab("about:flyweb"); + } + }); +} + +function unloadFromWindow(aWindow) { + if (!aWindow) { + return; + } + + aWindow.NativeWindow.menu.remove(menuID); +} + +function prefObserver(aSubject, aTopic, aData) { + let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF); + if (enabled) { + FlyWebUI.init(); + } else { + FlyWebUI.uninit(); + } +} + +function install(aData, aReason) {} + +function uninstall(aData, aReason) {} + +function startup(aData, aReason) { + // Observe pref changes and enable/disable as necessary. + Services.prefs.addObserver(FLYWEB_ENABLED_PREF, prefObserver, false); + + // Only initialize if pref is enabled. + let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF); + if (enabled) { + FlyWebUI.init(); + } +} + +function shutdown(aData, aReason) { + Services.prefs.removeObserver(FLYWEB_ENABLED_PREF, prefObserver); + + FlyWebUI.uninit(); +}
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/content/aboutFlyWeb.css @@ -0,0 +1,29 @@ +/* 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/. */ + +include "defines.css" + +.list-item > a { + color: inherit; + text-decoration: none; +} + +.details { + -moz-margin-start: calc(var(--icon-size) + var(--icon-margin) * 2 - 1em); + padding: 1em; +} + +#flyweb-item-template { + display: none; +} + +#flyweb-list-empty { + display: none; +} + +#flyweb-list:empty + #flyweb-list-empty { + display: block; + text-align: center; + padding-top: 3.9em; +}
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/content/aboutFlyWeb.js @@ -0,0 +1,73 @@ +/* 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/. */ +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Console.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gFlyWebBundle", function() { + return Services.strings.createBundle("chrome://flyweb/locale/flyweb.properties"); +}); + +let discoveryManager = new FlyWebDiscoveryManager(); + +let discoveryCallback = { + onDiscoveredServicesChanged(services) { + if (!this.id) { + return; + } + + let list = document.getElementById("flyweb-list"); + while (list.firstChild) { + list.firstChild.remove(); + } + + let template = document.getElementById("flyweb-item-template"); + + for (let service of services) { + let item = template.cloneNode(true); + item.removeAttribute("id"); + + item.setAttribute("data-service-id", service.serviceId); + item.querySelector(".title").setAttribute("value", service.displayName); + item.querySelector(".icon").src = "chrome://flyweb/content/icon-64.png"; + + list.appendChild(item); + } + }, + start() { + this.id = discoveryManager.startDiscovery(this); + }, + stop() { + discoveryManager.stopDiscovery(this.id); + this.id = undefined; + } +}; + +window.addEventListener("DOMContentLoaded", () => { + let list = document.getElementById("flyweb-list"); + list.addEventListener("click", (evt) => { + let serviceId = evt.target.closest("[data-service-id]").getAttribute("data-service-id"); + + discoveryManager.pairWithService(serviceId, { + pairingSucceeded(service) { + window.open(service.uiUrl, "FlyWebWindow_" + serviceId); + }, + + pairingFailed(error) { + console.error("FlyWeb failed to connect to service " + serviceId, error); + } + }); + }); + + discoveryCallback.start(); +}); + +window.addEventListener("unload", () => { + discoveryCallback.stop(); +});
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/content/aboutFlyWeb.xhtml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" + "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [ +<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" > +%brandDTD; +<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" > +%globalDTD; +<!ENTITY % flywebDTD SYSTEM "chrome://flyweb/locale/aboutFlyWeb.dtd" > +%flywebDTD; +]> + +<!-- 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/. --> + +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<head> + <title>&aboutFlyWeb.title;</title> + <meta name="viewport" content="width=device-width; user-scalable=0" /> + <link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" /> + <link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/> + <link rel="stylesheet" href="chrome://flyweb/content/aboutFlyWeb.css" type="text/css"/> +</head> + +<body dir="&locale.dir;"> + <!--template id="flyweb-item-template"--> + <li id="flyweb-item-template" class="list-item" role="button"> + <img class="icon" src=""/> + <div class="details"> + <div class="row"> + <!-- This is a hack so that we can crop this label in its center --> + <xul:label class="title" crop="center" value=""/> + </div> + </div> + </li> + <!--/template--> + + <div class="header"> + <div>&aboutFlyWeb.header;</div> + </div> + <ul id="flyweb-list" class="list"></ul> + <span id="flyweb-list-empty">&aboutFlyWeb.empty;</span> + <script type="application/javascript;version=1.8" src="chrome://flyweb/content/aboutFlyWeb.js"/> +</body> +</html>
new file mode 100644 index 0000000000000000000000000000000000000000..be8ece467b7b1df0683febca9253079af55abad2 GIT binary patch literal 1311 zc$@(r1>pLLP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$!AV3xRCwC#TFq|SKoGWR4m~<gQ1S#x z9-tLG0p!?I@e_m`D^&udN<9Kk5ck%eAWr~Py~nxr2D$Z??Gv;+(Ky{q#`dheSde(6 z6_UNb-_CsV?HbRkt+v`~tL^2QU#9WLPs2d}oanOo@_D#<F#za=_w>&|m!|Pp=`z)2 zqQOw4K7f>f6$pk{LVvoSc;^7ni@DT4y^Ja_>9z@ippBQ)ACwya#x*DhmWv+>76Q9G z0}za7(-HujU`gh%K@YjoV9YD?0F3uLi@w1NmPY$E2^kj#U;qfyq8!w)sM}?pO>{Zb z<$`(FVdC1g0ib<d)(~d8w6+-nboXM7db03|9{iZN`3b3ii$oa<Ts|lO28`X)^8lbP zq+D@O0)4AV>IngV3@t(~iG2XJ#Q>USQ1@E5j}QpuiUYbxZl=wV;98g#ABBhEA*BsD zsEeW16*hy#hgoFlXACyhWt5N$K3iMt{38-{X$BYrA+gR82$c%}y50;s1{azb@A?Q# zo<+J{ScQ^9;v1kB=6OS#a)Zw(yi1a1PKl@)BlBjdxU;<2E5HR!>^tWAMj*xlt^iMn zo&b=c)<#HRbg*+0i!tWo$l=ajGA^!goMB44K6*qT@lanVRZGV+?qoA%haywRFrH`? zX1aY7+qd%C`+~)f($E&{U<VhRH4!*T9+Amk3&}QfZUWAVKnJW+08kjqOUqqOLPMG@ zX#eLaZ8uCfoM&kZAaFgW7)hP$bJf6@4O32ZOX+UwvcR3|S&F1vM##{)9HN(us2IzW zzqp1rqd@P;uz{6K5MnM}<Scw4NJCN&pd2wxSieXpV!N1t@Q?`_MkRP~M$htTLIl%c zk%cG6*`z4uy=liJ#JqwaC<Nh*O@j{7Q<e%_NiQ>W|1(DFMi?p_KOB|0j<dZGkt#vh z*{=mP!IS|YElVv)`h<2(Q@uL755ROe7VI`C$97Rcgrp^Ds~GYI4;kZJ-Q5TY?*T9J zvIYvA4)R8T?UtC&mBpAFjOCbw=EC+o;tOMbQAQ}?>1~!lnC!6?$NoV0<_Z_5NdF5a zgXE@isRwY<R9U1;NwyI8$Mnw~&_YQVHiU6W#Zrvy8w<gcq{=`@u&fsnp%Y3us9`}) z2?()7D;U;EnQ+LcvlgBOisty(X7Pn|Kmqr>Bf0zGstgyq&Wc;SP9M2CW*7E=5=Mo~ zjj?kQ0ds$}1s*A*1S<gmgc5!|%F_CS#LaE;wfFzFx4)AB7((d{J#sQT3Li55GI@SO z+@B-Qu_bWZ2Y`qcDq|<ifhS7_=Mm_=G<F4oD-T$aGkT9<=sXZ2M#KLRj#=1zhaq5! zC51Xck)!OG(FB_;mQOi-hOV-L(==#+kg64s0F%Wip_nqIZn&+K=n8}`PS|z70Umd2 ztF5-$oS9$sfS3ga-s6uakS*q(@j3ok0)Sdz;iZ2#DSRoi^8i?U^8*l0tMY_Kg$^+A zWS`6e@9E!do&^BiE!Eu{0D>*m-5UUQ(y9bNZK>|w05IQD-Ms-|B-~l$J^drZ{sVw{ zJ?~~H5ki%z!B<rh&d(pq-*4Zwz9Y+nFnVn){F^TC_2PfkCzRT1tF5-$s<D3p3;^ms VNp_0}C&vH)002ovPDHLkV1l>jR9XN4
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/install.rdf.in @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +#filter substitution + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:id>flyweb@mozilla.org</em:id> + <em:version>1.0.0</em:version> + <em:type>2</em:type> + <em:bootstrap>true</em:bootstrap> + + <!-- Target Application this theme can install into, + with minimum and maximum supported versions. --> + <em:targetApplication> + <Description> + <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id> + <em:minVersion>@FIREFOX_VERSION@</em:minVersion> + <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion> + </Description> + </em:targetApplication> + + <!-- Front End MetaData --> + <em:name>FlyWeb</em:name> + <em:description>Discover nearby services in the browser</em:description> + </Description> +</RDF>
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/jar.mn @@ -0,0 +1,10 @@ +#filter substitution +# 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/. + +[features/flyweb@mozilla.org] chrome.jar: +% content flyweb %content/ contentaccessible=yes + content/ (content/*) +% locale flyweb en-US %locale/en-US/ + locale/ (locale/*) \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/locale/en-US/aboutFlyWeb.dtd @@ -0,0 +1,7 @@ +<!-- 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/. --> + +<!ENTITY aboutFlyWeb.title "FlyWeb"> +<!ENTITY aboutFlyWeb.header "Nearby FlyWeb Services"> +<!ENTITY aboutFlyWeb.empty "No FlyWeb Services Found">
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/locale/en-US/flyweb.properties @@ -0,0 +1,5 @@ +# 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/. + +flyweb-menu.name = FlyWeb
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/flyweb/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +# DIRS += ['locales'] + +FINAL_TARGET_FILES.features['flyweb@mozilla.org'] += [ + 'bootstrap.js' +] + +FINAL_TARGET_PP_FILES.features['flyweb@mozilla.org'] += [ + 'install.rdf.in' +] + +JAR_MANIFESTS += ['jar.mn']
new file mode 100644 --- /dev/null +++ b/mobile/android/extensions/moz.build @@ -0,0 +1,11 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +# Only include the following system add-ons if building Aurora or Nightly +if 'a' in CONFIG['GRE_MILESTONE']: + DIRS += [ + 'flyweb', + ]
--- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -444,16 +444,19 @@ ; Security Reports @BINPATH@/components/SecurityReporter.manifest @BINPATH@/components/SecurityReporter.js ; [Browser Chrome Files] @BINPATH@/chrome/toolkit@JAREXT@ @BINPATH@/chrome/toolkit.manifest +; Features +@BINPATH@/features/* + ; DevTools @BINPATH@/chrome/devtools@JAREXT@ @BINPATH@/chrome/devtools.manifest ; [Default Preferences] ; All the pref files must be part of base to prevent migration bugs @BINPATH@/@PREF_DIR@/mobile.js @BINPATH@/@PREF_DIR@/channel-prefs.js
--- a/mobile/android/moz.build +++ b/mobile/android/moz.build @@ -14,16 +14,17 @@ DIRS += [ if CONFIG['MOZ_ANDROID_MLS_STUMBLER']: DIRS += ['stumbler'] DIRS += [ 'javaaddons', # Must be built before base. 'base', 'chrome', 'components', + 'extensions', 'modules', 'themes/core', 'app', 'fonts', 'geckoview_library', ] if CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']:
--- a/python/mozbuild/mozbuild/action/package_fennec_apk.py +++ b/python/mozbuild/mozbuild/action/package_fennec_apk.py @@ -22,16 +22,17 @@ from mozpack.files import ( ) from mozpack.mozjar import JarReader import mozpack.path as mozpath def package_fennec_apk(inputs=[], omni_ja=None, classes_dex=None, lib_dirs=[], assets_dirs=[], + features_dirs=[], szip_assets_libs_with=None, root_files=[], verbose=False): jarrer = Jarrer(optimize=False) # First, take input files. The contents of the later files overwrites the # content of earlier files. for input in inputs: @@ -48,16 +49,21 @@ def package_fennec_apk(inputs=[], omni_j print('Packaging %s from %s' % (path, file.path)) if not os.path.exists(abspath): raise ValueError('File %s not found (looked for %s)' % \ (file.path, abspath)) if jarrer.contains(path): jarrer.remove(path) jarrer.add(path, file, compress=compress) + for features_dir in features_dirs: + finder = FileFinder(features_dir, find_executables=False) + for p, f in finder.find('**'): + add(mozpath.join('assets', 'features', p), f, False) + for assets_dir in assets_dirs: finder = FileFinder(assets_dir, find_executables=False) for p, f in finder.find('**'): compress = None # Take default from Jarrer. if p.endswith('.so'): # Asset libraries are special. if szip_assets_libs_with: # We need to szip libraries before packing. The file @@ -100,32 +106,35 @@ def main(args): parser.add_argument('--omnijar', default=None, help='Optional omni.ja to pack into APK file.') parser.add_argument('--classes-dex', default=None, help='Optional classes.dex to pack into APK file.') parser.add_argument('--lib-dirs', nargs='*', default=[], help='Optional lib/ dirs to pack into APK file.') parser.add_argument('--assets-dirs', nargs='*', default=[], help='Optional assets/ dirs to pack into APK file.') + parser.add_argument('--features-dirs', nargs='*', default=[], + help='Optional features/ dirs to pack into APK file.') parser.add_argument('--szip-assets-libs-with', default=None, help='IN PLACE szip assets/**/*.so BEFORE packing ' 'into APK file using the given szip executable.') parser.add_argument('--root-files', nargs='*', default=[], help='Optional files to pack into APK file root.') args = parser.parse_args(args) if buildconfig.substs.get('OMNIJAR_NAME') != 'assets/omni.ja': raise ValueError("Don't know how package Fennec APKs when " " OMNIJAR_NAME is not 'assets/omni.jar'.") jarrer = package_fennec_apk(inputs=args.inputs, omni_ja=args.omnijar, classes_dex=args.classes_dex, lib_dirs=args.lib_dirs, assets_dirs=args.assets_dirs, + features_dirs=args.features_dirs, szip_assets_libs_with=args.szip_assets_libs_with, root_files=args.root_files, verbose=args.verbose) jarrer.copy(args.output) return 0
--- a/toolkit/mozapps/installer/upload-files-APK.mk +++ b/toolkit/mozapps/installer/upload-files-APK.mk @@ -145,16 +145,17 @@ INNER_FENNEC_PACKAGE = \ $(PYTHON) -m mozbuild.action.package_fennec_apk \ --verbose \ --inputs \ $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ \ --omnijar $(STAGEPATH)$(MOZ_PKG_DIR)/$(OMNIJAR_NAME) \ --classes-dex $(GECKO_APP_AP_PATH)/classes.dex \ --lib-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/lib \ --assets-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/assets \ + --features-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/features \ $(if $(COMPILE_ENVIRONMENT),$(if $(MOZ_ENABLE_SZIP),--szip-assets-libs-with $(ABS_DIST)/host/bin/szip)) \ --root-files $(foreach f,$(ROOT_FILES),$(STAGEPATH)$(MOZ_PKG_DIR)/$(f)) \ --output $(PACKAGE:.apk=-unsigned-unaligned.apk) && \ $(call RELEASE_SIGN_ANDROID_APK,$(PACKAGE:.apk=-unsigned-unaligned.apk),$(PACKAGE)) # Packaging produces many optional artifacts. package_fennec = \ $(INNER_FENNEC_PACKAGE) && \