gecko-dev/mobile/android/modules/Home.jsm
Andrew McCreight 5dec0e0beb Bug 1432992, part 1 - Remove definitions of Ci, Cr, Cc, and Cu. r=florian
This patch was autogenerated by my decomponents.py

It covers almost every file with the extension js, jsm, html, py,
xhtml, or xul.

It removes blank lines after removed lines, when the removed lines are
preceded by either blank lines or the start of a new block. The "start
of a new block" is defined fairly hackily: either the line starts with
//, ends with */, ends with {, <![CDATA[, """ or '''. The first two
cover comments, the third one covers JS, the fourth covers JS embedded
in XUL, and the final two cover JS embedded in Python. This also
applies if the removed line was the first line of the file.

It covers the pattern matching cases like "var {classes: Cc,
interfaces: Ci, utils: Cu, results: Cr} = Components;". It'll remove
the entire thing if they are all either Ci, Cr, Cc or Cu, or it will
remove the appropriate ones and leave the residue behind. If there's
only one behind, then it will turn it into a normal, non-pattern
matching variable definition. (For instance, "const { classes: Cc,
Constructor: CC, interfaces: Ci, utils: Cu } = Components" becomes
"const CC = Components.Constructor".)

MozReview-Commit-ID: DeSHcClQ7cG

--HG--
extra : rebase_source : d9c41878036c1ef7766ef5e91a7005025bc1d72b
2018-02-06 09:36:57 -08:00

483 lines
14 KiB
JavaScript

// -*- 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["Home"];
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/SharedPreferences.jsm");
ChromeUtils.import("resource://gre/modules/Messaging.jsm");
// Keep this in sync with the constant defined in PanelAuthCache.java
const PREFS_PANEL_AUTH_PREFIX = "home_panels_auth_";
// Default weight for a banner message.
const DEFAULT_WEIGHT = 100;
// See bug 915424
function resolveGeckoURI(aURI) {
if (!aURI)
throw "Can't resolve an empty uri";
if (aURI.startsWith("chrome://")) {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
return registry.convertChromeURL(Services.io.newURI(aURI)).spec;
} else if (aURI.startsWith("resource://")) {
let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
return handler.resolveURI(Services.io.newURI(aURI));
}
return aURI;
}
function BannerMessage(options) {
let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
this.id = uuidgen.generateUUID().toString();
if ("text" in options && options.text != null)
this.text = options.text;
if ("icon" in options && options.icon != null)
this.iconURI = resolveGeckoURI(options.icon);
if ("onshown" in options && typeof options.onshown === "function")
this.onshown = options.onshown;
if ("onclick" in options && typeof options.onclick === "function")
this.onclick = options.onclick;
if ("ondismiss" in options && typeof options.ondismiss === "function")
this.ondismiss = options.ondismiss;
let weight = parseInt(options.weight, 10);
this.weight = weight > 0 ? weight : DEFAULT_WEIGHT;
}
// We need this object to have access to the HomeBanner
// private members without leaking it outside Home.jsm.
var HomeBannerMessageHandlers;
var HomeBanner = (function() {
// Whether there is a "HomeBanner:Get" request we couldn't fulfill.
let _pendingRequest = false;
// Functions used to handle messages sent from Java.
HomeBannerMessageHandlers = {
"HomeBanner:Get": function handleBannerGet(data) {
if (Object.keys(_messages).length > 0) {
_sendBannerData();
} else {
_pendingRequest = true;
}
}
};
// Holds the messages that will rotate through the banner.
let _messages = {};
// Choose a random message from the set of messages, biasing towards those with higher weight.
// Weight logic copied from desktop snippets:
// https://github.com/mozilla/snippets-service/blob/7d80edb8b1cddaed075275c2fc7cdf69a10f4003/snippets/base/templates/base/includes/snippet_js.html#L119
let _sendBannerData = function() {
let totalWeight = 0;
for (let key in _messages) {
let message = _messages[key];
totalWeight += message.weight;
message.totalWeight = totalWeight;
}
let threshold = Math.random() * totalWeight;
for (let key in _messages) {
let message = _messages[key];
if (threshold < message.totalWeight) {
EventDispatcher.instance.sendRequestForResult({
type: "HomeBanner:Data",
id: message.id,
text: message.text,
iconURI: message.iconURI
}).then(id => _handleShown(id));
return;
}
}
};
let _handleShown = function(id) {
let message = _messages[id];
if (message.onshown)
message.onshown();
};
let _handleClick = function(id) {
let message = _messages[id];
if (message.onclick)
message.onclick();
};
let _handleDismiss = function(id) {
let message = _messages[id];
if (message.ondismiss)
message.ondismiss();
};
return Object.freeze({
onEvent: function(event, data, callback) {
switch (event) {
case "HomeBanner:Click":
_handleClick(data.id);
break;
case "HomeBanner:Dismiss":
_handleDismiss(data.id);
break;
}
},
/**
* Adds a new banner message to the rotation.
*
* @return id Unique identifer for the message.
*/
add: function(options) {
let message = new BannerMessage(options);
_messages[message.id] = message;
// If this is the first message we're adding, add
// observers to listen for requests from the Java UI.
if (Object.keys(_messages).length == 1) {
EventDispatcher.instance.registerListener(this, [
"HomeBanner:Click",
"HomeBanner:Dismiss",
]);
// Send a message to Java if there's a pending "HomeBanner:Get" request.
if (_pendingRequest) {
_pendingRequest = false;
_sendBannerData();
}
}
return message.id;
},
/**
* Removes a banner message from the rotation.
*
* @param id The id of the message to remove.
*/
remove: function(id) {
if (!(id in _messages)) {
throw "Home.banner: Can't remove message that doesn't exist: id = " + id;
}
delete _messages[id];
// If there are no more messages, remove the observers.
if (Object.keys(_messages).length == 0) {
EventDispatcher.instance.unregisterListener(this, [
"HomeBanner:Click",
"HomeBanner:Dismiss",
]);
}
}
});
})();
// We need this object to have access to the HomePanels
// private members without leaking it outside Home.jsm.
var HomePanelsMessageHandlers;
var HomePanels = (function() {
// Functions used to handle messages sent from Java.
HomePanelsMessageHandlers = {
"HomePanels:Get": function handlePanelsGet(data) {
let requestId = data.requestId;
let ids = data.ids || null;
let panels = [];
for (let id in _registeredPanels) {
// Null ids means we want to fetch all available panels
if (ids == null || ids.includes(id)) {
try {
panels.push(_generatePanel(id));
} catch (e) {
Cu.reportError("Home.panels: Invalid options, panel.id = " + id + ": " + e);
}
}
}
EventDispatcher.instance.sendRequest({
type: "HomePanels:Data",
panels: panels,
requestId: requestId
});
},
"HomePanels:Authenticate": function handlePanelsAuthenticate(data) {
// Generate panel options to get auth handler.
let id = data.id;
let options = _registeredPanels[id]();
if (!options.auth) {
throw "Home.panels: Invalid auth for panel.id = " + id;
}
if (!options.auth.authenticate || typeof options.auth.authenticate !== "function") {
throw "Home.panels: Invalid auth authenticate function: panel.id = " + this.id;
}
options.auth.authenticate();
},
"HomePanels:RefreshView": function handlePanelsRefreshView(data) {
let options = _registeredPanels[data.panelId]();
let view = options.views[data.viewIndex];
if (!view) {
throw "Home.panels: Invalid view for panel.id = " + data.panelId
+ ", view.index = " + data.viewIndex;
}
if (!view.onrefresh || typeof view.onrefresh !== "function") {
throw "Home.panels: Invalid onrefresh for panel.id = " + data.panelId
+ ", view.index = " + data.viewIndex;
}
view.onrefresh();
},
"HomePanels:Installed": function handlePanelsInstalled(data) {
let id = data.id;
_assertPanelExists(id);
let options = _registeredPanels[id]();
if (!options.oninstall) {
return;
}
if (typeof options.oninstall !== "function") {
throw "Home.panels: Invalid oninstall function: panel.id = " + this.id;
}
options.oninstall();
},
"HomePanels:Uninstalled": function handlePanelsUninstalled(data) {
let id = data.id;
_assertPanelExists(id);
let options = _registeredPanels[id]();
if (!options.onuninstall) {
return;
}
if (typeof options.onuninstall !== "function") {
throw "Home.panels: Invalid onuninstall function: panel.id = " + this.id;
}
options.onuninstall();
}
};
// Holds the current set of registered panels that can be
// installed, updated, uninstalled, or unregistered. It maps
// panel ids with the functions that dynamically generate
// their respective panel options. This is used to retrieve
// the current list of available panels in the system.
// See HomePanels:Get handler.
let _registeredPanels = {};
// Valid layouts for a panel.
let Layout = Object.freeze({
FRAME: "frame"
});
// Valid types of views for a dataset.
let View = Object.freeze({
LIST: "list",
GRID: "grid"
});
// Valid item types for a panel view.
let Item = Object.freeze({
ARTICLE: "article",
IMAGE: "image",
ICON: "icon"
});
// Valid item handlers for a panel view.
let ItemHandler = Object.freeze({
BROWSER: "browser",
INTENT: "intent"
});
function Panel(id, options) {
this.id = id;
this.title = options.title;
this.layout = options.layout;
this.views = options.views;
this.default = !!options.default;
if (!this.id || !this.title) {
throw "Home.panels: Can't create a home panel without an id and title!";
}
if (!this.layout) {
// Use FRAME layout by default
this.layout = Layout.FRAME;
} else if (!_valueExists(Layout, this.layout)) {
throw "Home.panels: Invalid layout for panel: panel.id = " + this.id + ", panel.layout =" + this.layout;
}
for (let view of this.views) {
if (!_valueExists(View, view.type)) {
throw "Home.panels: Invalid view type: panel.id = " + this.id + ", view.type = " + view.type;
}
if (!view.itemType) {
if (view.type == View.LIST) {
// Use ARTICLE item type by default in LIST views
view.itemType = Item.ARTICLE;
} else if (view.type == View.GRID) {
// Use IMAGE item type by default in GRID views
view.itemType = Item.IMAGE;
}
} else if (!_valueExists(Item, view.itemType)) {
throw "Home.panels: Invalid item type: panel.id = " + this.id + ", view.itemType = " + view.itemType;
}
if (!view.itemHandler) {
// Use BROWSER item handler by default
view.itemHandler = ItemHandler.BROWSER;
} else if (!_valueExists(ItemHandler, view.itemHandler)) {
throw "Home.panels: Invalid item handler: panel.id = " + this.id + ", view.itemHandler = " + view.itemHandler;
}
if (!view.dataset) {
throw "Home.panels: No dataset provided for view: panel.id = " + this.id + ", view.type = " + view.type;
}
if (view.onrefresh) {
view.refreshEnabled = true;
}
}
if (options.auth) {
if (!options.auth.messageText) {
throw "Home.panels: Invalid auth messageText: panel.id = " + this.id;
}
if (!options.auth.buttonText) {
throw "Home.panels: Invalid auth buttonText: panel.id = " + this.id;
}
this.authConfig = {
messageText: options.auth.messageText,
buttonText: options.auth.buttonText
};
// Include optional image URL if it is specified.
if (options.auth.imageUrl) {
this.authConfig.imageUrl = options.auth.imageUrl;
}
}
if (options.position >= 0) {
this.position = options.position;
}
}
let _generatePanel = function(id) {
let options = _registeredPanels[id]();
return new Panel(id, options);
};
// Helper function used to see if a value is in an object.
let _valueExists = function(obj, value) {
for (let key in obj) {
if (obj[key] == value) {
return true;
}
}
return false;
};
let _assertPanelExists = function(id) {
if (!(id in _registeredPanels)) {
throw "Home.panels: Panel doesn't exist: id = " + id;
}
};
return Object.freeze({
Layout: Layout,
View: View,
Item: Item,
ItemHandler: ItemHandler,
register: function(id, optionsCallback) {
// Bail if the panel already exists
if (id in _registeredPanels) {
throw "Home.panels: Panel already exists: id = " + id;
}
if (!optionsCallback || typeof optionsCallback !== "function") {
throw "Home.panels: Panel callback must be a function: id = " + id;
}
_registeredPanels[id] = optionsCallback;
},
unregister: function(id) {
_assertPanelExists(id);
delete _registeredPanels[id];
},
install: function(id) {
_assertPanelExists(id);
EventDispatcher.instance.sendRequest({
type: "HomePanels:Install",
panel: _generatePanel(id)
});
},
uninstall: function(id) {
_assertPanelExists(id);
EventDispatcher.instance.sendRequest({
type: "HomePanels:Uninstall",
id: id
});
},
update: function(id) {
_assertPanelExists(id);
EventDispatcher.instance.sendRequest({
type: "HomePanels:Update",
panel: _generatePanel(id)
});
},
setAuthenticated: function(id, isAuthenticated) {
_assertPanelExists(id);
let authKey = PREFS_PANEL_AUTH_PREFIX + id;
let sharedPrefs = SharedPreferences.forProfile();
sharedPrefs.setBoolPref(authKey, isAuthenticated);
}
});
})();
// Public API
this.Home = Object.freeze({
banner: HomeBanner,
panels: HomePanels,
// Lazy notification observer registered in browser.js
onEvent: function(event, data, callback) {
if (event in HomeBannerMessageHandlers) {
HomeBannerMessageHandlers[event](data);
} else if (event in HomePanelsMessageHandlers) {
HomePanelsMessageHandlers[event](data);
} else {
Cu.reportError("Home.observe: message handler not found for event: " + event);
}
}
});