mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 780379 - Show a custom intent chooser for downloads that can be downloaded by an external app. r=mfinkle
--HG-- rename : mobile/android/chrome/content/HelperApps.js => mobile/android/modules/HelperApps.jsm
This commit is contained in:
parent
8aa06f6035
commit
0becd4b76a
@ -1,196 +0,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/. */
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
|
||||
let ContentAreaUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
|
||||
return ContentAreaUtils;
|
||||
});
|
||||
|
||||
function getBridge() {
|
||||
return Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
|
||||
}
|
||||
|
||||
function sendMessageToJava(aMessage) {
|
||||
return getBridge().handleGeckoMessage(JSON.stringify(aMessage));
|
||||
}
|
||||
|
||||
var HelperApps = {
|
||||
get defaultHttpHandlers() {
|
||||
let protoHandlers = this.getAppsForProtocol("http");
|
||||
|
||||
var results = {};
|
||||
for (var i = 0; i < protoHandlers.length; i++) {
|
||||
try {
|
||||
let protoApp = protoHandlers.queryElementAt(i, Ci.nsIHandlerApp);
|
||||
results[protoApp.name] = protoApp;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
delete this.defaultHttpHandlers;
|
||||
return this.defaultHttpHandlers = results;
|
||||
},
|
||||
|
||||
get protoSvc() {
|
||||
delete this.protoSvc;
|
||||
return this.protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService);
|
||||
},
|
||||
|
||||
get urlHandlerService() {
|
||||
delete this.urlHandlerService;
|
||||
return this.urlHandlerService = Cc["@mozilla.org/uriloader/external-url-handler-service;1"].getService(Ci.nsIExternalURLHandlerService);
|
||||
},
|
||||
|
||||
getAppsForProtocol: function getAppsForProtocol(uri) {
|
||||
let handlerInfoProto = this.protoSvc.getProtocolHandlerInfoFromOS(uri, {});
|
||||
return handlerInfoProto.possibleApplicationHandlers;
|
||||
},
|
||||
|
||||
getAppsForUri: function getAppsFor(uri) {
|
||||
let found = [];
|
||||
let mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
|
||||
// empty action string defaults to android.intent.action.VIEW
|
||||
let msg = {
|
||||
type: "Intent:GetHandlers",
|
||||
mime: mimeType,
|
||||
action: "",
|
||||
url: uri.spec,
|
||||
packageName: "",
|
||||
className: ""
|
||||
};
|
||||
let data = sendMessageToJava(msg);
|
||||
if (!data)
|
||||
return found;
|
||||
|
||||
let apps = this._parseApps(JSON.parse(data));
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let appName = apps[i].name;
|
||||
if (appName.length > 0 && !this.defaultHttpHandlers[appName])
|
||||
found.push(apps[i]);
|
||||
}
|
||||
return found;
|
||||
},
|
||||
|
||||
updatePageAction: function setPageAction(uri) {
|
||||
let apps = this.getAppsForUri(uri);
|
||||
if (apps.length > 0)
|
||||
this._setPageActionFor(uri, apps);
|
||||
else
|
||||
this._removePageAction();
|
||||
},
|
||||
|
||||
_setPageActionFor: function setPageActionFor(uri, apps) {
|
||||
this._pageActionUri = uri;
|
||||
|
||||
// If the pageaction is already added, simply update the URI to be launched when 'onclick' is triggered.
|
||||
if (this._pageActionId != undefined)
|
||||
return;
|
||||
|
||||
this._pageActionId = NativeWindow.pageactions.add({
|
||||
title: Strings.browser.GetStringFromName("openInApp.pageAction"),
|
||||
icon: "drawable://icon_openinapp",
|
||||
clickCallback: function() {
|
||||
if (apps.length == 1)
|
||||
this._launchApp(apps[0], this._pageActionUri);
|
||||
else
|
||||
this.openUriInApp(this._pageActionUri);
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
_removePageAction: function removePageAction() {
|
||||
if(!this._pageActionId)
|
||||
return;
|
||||
|
||||
NativeWindow.pageactions.remove(this._pageActionId);
|
||||
delete this._pageActionId;
|
||||
},
|
||||
|
||||
_launchApp: function launchApp(appData, uri) {
|
||||
let mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
|
||||
let msg = {
|
||||
type: "Intent:Open",
|
||||
mime: mimeType,
|
||||
action: "android.intent.action.VIEW",
|
||||
url: uri.spec,
|
||||
packageName: appData.packageName,
|
||||
className: appData.activityName
|
||||
};
|
||||
sendMessageToJava(msg);
|
||||
},
|
||||
|
||||
openUriInApp: function openUriInApp(uri) {
|
||||
let mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
|
||||
let msg = {
|
||||
type: "Intent:Open",
|
||||
mime: mimeType,
|
||||
action: "",
|
||||
url: uri.spec,
|
||||
packageName: "",
|
||||
className: ""
|
||||
};
|
||||
sendMessageToJava(msg);
|
||||
},
|
||||
|
||||
_parseApps: function _parseApps(aJSON) {
|
||||
// aJSON -> {apps: [app1Label, app1Default, app1PackageName, app1ActivityName, app2Label, app2Defaut, ...]}
|
||||
// see GeckoAppShell.java getHandlersForIntent function for details
|
||||
let appInfo = aJSON.apps;
|
||||
const numAttr = 4; // 4 elements per ResolveInfo: label, default, package name, activity name.
|
||||
let apps = [];
|
||||
for (let i = 0; i < appInfo.length; i += numAttr) {
|
||||
apps.push({"name" : appInfo[i],
|
||||
"isDefault" : appInfo[i+1],
|
||||
"packageName" : appInfo[i+2],
|
||||
"activityName" : appInfo[i+3]});
|
||||
}
|
||||
return apps;
|
||||
},
|
||||
|
||||
showDoorhanger: function showDoorhanger(aUri, aCallback) {
|
||||
let permValue = Services.perms.testPermission(aUri, "native-intent");
|
||||
if (permValue != Services.perms.UNKNOWN_ACTION) {
|
||||
if (permValue == Services.perms.ALLOW_ACTION) {
|
||||
if (aCallback)
|
||||
aCallback(aUri);
|
||||
else
|
||||
this.openUriInApp(aUri);
|
||||
} else if (permValue == Services.perms.DENY_ACTION) {
|
||||
// do nothing
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let apps = this.getAppsForUri(aUri);
|
||||
let strings = Strings.browser;
|
||||
|
||||
let message = "";
|
||||
if (apps.length == 1)
|
||||
message = strings.formatStringFromName("helperapps.openWithApp2", [apps[0].name], 1);
|
||||
else
|
||||
message = strings.GetStringFromName("helperapps.openWithList2");
|
||||
|
||||
let buttons = [{
|
||||
label: strings.GetStringFromName("helperapps.open"),
|
||||
callback: function(aChecked) {
|
||||
if (aChecked)
|
||||
Services.perms.add(aUri, "native-intent", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
if (aCallback)
|
||||
aCallback(aUri);
|
||||
else
|
||||
this.openUriInApp(aUri);
|
||||
}
|
||||
}, {
|
||||
label: strings.GetStringFromName("helperapps.ignore"),
|
||||
callback: function(aChecked) {
|
||||
if (aChecked)
|
||||
Services.perms.add(aUri, "native-intent", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
}
|
||||
}];
|
||||
|
||||
let options = { checkbox: Strings.browser.GetStringFromName("helperapps.dontAskAgain") };
|
||||
NativeWindow.doorhanger.show(message, "helperapps-open", buttons, BrowserApp.selectedTab.id, options);
|
||||
}
|
||||
};
|
@ -53,6 +53,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
|
||||
"resource://gre/modules/Prompt.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "HelperApps",
|
||||
"resource://gre/modules/HelperApps.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
@ -62,7 +65,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
|
||||
// Lazily-loaded browser scripts:
|
||||
[
|
||||
["HelperApps", "chrome://browser/content/HelperApps.js"],
|
||||
["SelectHelper", "chrome://browser/content/SelectHelper.js"],
|
||||
["InputWidgetHelper", "chrome://browser/content/InputWidgetHelper.js"],
|
||||
["AboutReader", "chrome://browser/content/aboutReader.js"],
|
||||
@ -2873,7 +2875,7 @@ Tab.prototype = {
|
||||
this.browser.focus();
|
||||
this.browser.docShellIsActive = true;
|
||||
Reader.updatePageAction(this);
|
||||
HelperApps.updatePageAction(this.browser.currentURI);
|
||||
ExternalApps.updatePageAction(this.browser.currentURI);
|
||||
} else {
|
||||
this.browser.setAttribute("type", "content-targetable");
|
||||
this.browser.docShellIsActive = false;
|
||||
@ -3583,7 +3585,7 @@ Tab.prototype = {
|
||||
|
||||
// Show page actions for helper apps.
|
||||
if (BrowserApp.selectedTab == this)
|
||||
HelperApps.updatePageAction(this.browser.currentURI);
|
||||
ExternalApps.updatePageAction(this.browser.currentURI);
|
||||
|
||||
if (!Reader.isEnabledForParseOnLoad)
|
||||
return;
|
||||
@ -7755,7 +7757,60 @@ var ExternalApps = {
|
||||
openExternal: function(aElement) {
|
||||
let uri = ExternalApps._getMediaLink(aElement);
|
||||
HelperApps.openUriInApp(uri);
|
||||
}
|
||||
},
|
||||
|
||||
updatePageAction: function updatePageAction(uri) {
|
||||
let apps = HelperApps.getAppsForUri(uri);
|
||||
|
||||
if (apps.length > 0)
|
||||
this._setUriForPageAction(uri, apps);
|
||||
else
|
||||
this._removePageAction();
|
||||
},
|
||||
|
||||
_setUriForPageAction: function setUriForPageAction(uri, apps) {
|
||||
this._pageActionUri = uri;
|
||||
|
||||
// If the pageaction is already added, simply update the URI to be launched when 'onclick' is triggered.
|
||||
if (this._pageActionId != undefined)
|
||||
return;
|
||||
|
||||
this._pageActionId = NativeWindow.pageactions.add({
|
||||
title: Strings.browser.GetStringFromName("openInApp.pageAction"),
|
||||
icon: "drawable://icon_openinapp",
|
||||
clickCallback: (function() {
|
||||
let callback = function(app) {
|
||||
app.launch(uri);
|
||||
}
|
||||
|
||||
if (apps.length > 1) {
|
||||
// Use the HelperApps prompt here to filter out any Http handlers
|
||||
HelperApps.prompt(apps, {
|
||||
title: Strings.browser.GetStringFromName("openInApp.pageAction"),
|
||||
buttons: [
|
||||
Strings.browser.GetStringFromName("openInApp.ok"),
|
||||
Strings.browser.GetStringFromName("openInApp.cancel")
|
||||
]
|
||||
}, function(result) {
|
||||
if (result.button != 0)
|
||||
return;
|
||||
|
||||
callback(apps[result.icongrid0]);
|
||||
});
|
||||
} else {
|
||||
callback(apps[0]);
|
||||
}
|
||||
}).bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
_removePageAction: function removePageAction() {
|
||||
if(!this._pageActionId)
|
||||
return;
|
||||
|
||||
NativeWindow.pageactions.remove(this._pageActionId);
|
||||
delete this._pageActionId;
|
||||
},
|
||||
};
|
||||
|
||||
var Distribution = {
|
||||
|
@ -36,7 +36,6 @@ chrome.jar:
|
||||
content/netError.xhtml (content/netError.xhtml)
|
||||
content/SelectHelper.js (content/SelectHelper.js)
|
||||
content/SelectionHandler.js (content/SelectionHandler.js)
|
||||
content/HelperApps.js (content/HelperApps.js)
|
||||
content/dbg-browser-actors.js (content/dbg-browser-actors.js)
|
||||
content/WebAppRT.js (content/WebAppRT.js)
|
||||
content/InputWidgetHelper.js (content/InputWidgetHelper.js)
|
||||
|
@ -1,17 +1,17 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* 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 Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir";
|
||||
const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Prompt.jsm");
|
||||
Cu.import("resource://gre/modules/HelperApps.jsm");
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// HelperApp Launcher Dialog
|
||||
@ -24,9 +24,42 @@ HelperAppLauncherDialog.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
|
||||
|
||||
show: function hald_show(aLauncher, aContext, aReason) {
|
||||
// Save everything by default
|
||||
aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useSystemDefault;
|
||||
aLauncher.saveToDisk(null, false);
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let defaultHandler = new Object();
|
||||
let apps = HelperApps.getAppsForUri(aLauncher.source, {
|
||||
mimeType: aLauncher.MIMEInfo.MIMEType,
|
||||
});
|
||||
|
||||
// Add a fake intent for save to disk at the top of the list
|
||||
apps.unshift({
|
||||
name: bundle.GetStringFromName("helperapps.saveToDisk"),
|
||||
iconUri: "drawable://icon",
|
||||
launch: function() {
|
||||
// Reset the preferredAction here
|
||||
aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk;
|
||||
aLauncher.saveToDisk(null, false);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
let app = apps[0];
|
||||
if (apps.length > 1) {
|
||||
app = HelperApps.prompt(apps, {
|
||||
title: bundle.GetStringFromName("helperapps.pick")
|
||||
});
|
||||
}
|
||||
|
||||
if (app) {
|
||||
aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useHelperApp;
|
||||
if (!app.launch(aLauncher.source)) {
|
||||
aLauncher.cancel();
|
||||
}
|
||||
} else {
|
||||
// Something weird happened. Log an error
|
||||
Services.console.logStringMessage("Unexpected selection from grid: " + app);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
|
||||
@ -63,7 +96,7 @@ HelperAppLauncherDialog.prototype = {
|
||||
// toolkit/content/contentAreaUtils.js.
|
||||
// If you are updating this code, update that code too! We can't share code
|
||||
// here since this is called in a js component.
|
||||
var collisionCount = 0;
|
||||
let collisionCount = 0;
|
||||
while (aLocalFile.exists()) {
|
||||
collisionCount++;
|
||||
if (collisionCount == 1) {
|
||||
|
@ -263,6 +263,8 @@ helperapps.openWithApp2=Open With %S App
|
||||
helperapps.openWithList2=Open With an App
|
||||
helperapps.always=Always
|
||||
helperapps.never=Never
|
||||
helperapps.pick=Complete action using
|
||||
helperapps.saveToDisk=Download
|
||||
|
||||
#Lightweight themes
|
||||
# LOCALIZATION NOTE (lwthemeInstallRequest.message): %S will be replaced with
|
||||
@ -294,3 +296,6 @@ readerMode.exit = Exit Reader Mode
|
||||
|
||||
#Open in App
|
||||
openInApp.pageAction = Open in App
|
||||
openInApp.ok = OK
|
||||
openInApp.cancel = Cancel
|
||||
|
||||
|
139
mobile/android/modules/HelperApps.jsm
Normal file
139
mobile/android/modules/HelperApps.jsm
Normal file
@ -0,0 +1,139 @@
|
||||
/* 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, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Prompt.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
|
||||
let ContentAreaUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
|
||||
return ContentAreaUtils;
|
||||
});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["App","HelperApps"];
|
||||
|
||||
function App(data) {
|
||||
this.name = data.name;
|
||||
this.isDefault = data.isDefault;
|
||||
this.packageName = data.packageName;
|
||||
this.activityName = data.activityName;
|
||||
this.iconUri = "-moz-icon://" + data.packageName;
|
||||
}
|
||||
|
||||
App.prototype = {
|
||||
launch: function(uri) {
|
||||
HelperApps._launchApp(this, uri);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var HelperApps = {
|
||||
get defaultHttpHandlers() {
|
||||
delete this.defaultHttpHandlers;
|
||||
return this.defaultHttpHandlers = this.getAppsForProtocol("http");
|
||||
},
|
||||
|
||||
get protoSvc() {
|
||||
delete this.protoSvc;
|
||||
return this.protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService);
|
||||
},
|
||||
|
||||
get urlHandlerService() {
|
||||
delete this.urlHandlerService;
|
||||
return this.urlHandlerService = Cc["@mozilla.org/uriloader/external-url-handler-service;1"].getService(Ci.nsIExternalURLHandlerService);
|
||||
},
|
||||
|
||||
prompt: function showPicker(apps, promptOptions, callback) {
|
||||
let p = new Prompt(promptOptions).addIconGrid({ items: apps });
|
||||
p.show(callback);
|
||||
},
|
||||
|
||||
getAppsForProtocol: function getAppsForProtocol(scheme) {
|
||||
let protoHandlers = this.protoSvc.getProtocolHandlerInfoFromOS(scheme, {}).possibleApplicationHandlers;
|
||||
|
||||
let results = {};
|
||||
for (let i = 0; i < protoHandlers.length; i++) {
|
||||
try {
|
||||
let protoApp = protoHandlers.queryElementAt(i, Ci.nsIHandlerApp);
|
||||
results[protoApp.name] = new App({
|
||||
name: protoApp.name,
|
||||
description: protoApp.detailedDescription,
|
||||
});
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
getAppsForUri: function getAppsForUri(uri, flags = { filterHttp: true }) {
|
||||
flags.filterHttp = "filterHttp" in flags ? flags.filterHttp : true;
|
||||
|
||||
// Query for apps that can/can't handle the mimetype
|
||||
let msg = this._getMessage("Intent:GetHandlers", uri, flags);
|
||||
let apps = this._parseApps(this._sendMessage(msg).apps);
|
||||
|
||||
if (flags.filterHttp) {
|
||||
apps = apps.filter(function(app) {
|
||||
return app.name && !this.defaultHttpHandlers[app.name];
|
||||
}, this);
|
||||
}
|
||||
|
||||
return apps;
|
||||
},
|
||||
|
||||
launchUri: function launchUri(uri) {
|
||||
let msg = this._getMessage("Intent:Open", uri);
|
||||
this._sendMessage(msg);
|
||||
},
|
||||
|
||||
_parseApps: function _parseApps(appInfo) {
|
||||
// appInfo -> {apps: [app1Label, app1Default, app1PackageName, app1ActivityName, app2Label, app2Defaut, ...]}
|
||||
// see GeckoAppShell.java getHandlersForIntent function for details
|
||||
const numAttr = 4; // 4 elements per ResolveInfo: label, default, package name, activity name.
|
||||
|
||||
let apps = [];
|
||||
for (let i = 0; i < appInfo.length; i += numAttr) {
|
||||
apps.push(new App({"name" : appInfo[i],
|
||||
"isDefault" : appInfo[i+1],
|
||||
"packageName" : appInfo[i+2],
|
||||
"activityName" : appInfo[i+3]}));
|
||||
}
|
||||
|
||||
return apps;
|
||||
},
|
||||
|
||||
_getMessage: function(type, uri, options = {}) {
|
||||
let mimeType = options.mimeType;
|
||||
if (uri && mimeType == undefined)
|
||||
mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
|
||||
|
||||
return {
|
||||
type: type,
|
||||
mime: mimeType,
|
||||
action: options.action || "", // empty action string defaults to android.intent.action.VIEW
|
||||
url: uri ? uri.spec : "",
|
||||
packageName: options.packageName || "",
|
||||
className: options.className || ""
|
||||
};
|
||||
},
|
||||
|
||||
_launchApp: function launchApp(app, uri) {
|
||||
let msg = this._getMessage("Intent:Open", uri, {
|
||||
packageName: app.packageName,
|
||||
className: app.activityName
|
||||
});
|
||||
|
||||
this._sendMessage(msg);
|
||||
},
|
||||
|
||||
_sendMessage: function(msg) {
|
||||
Services.console.logStringMessage("Sending: " + JSON.stringify(msg));
|
||||
let res = Services.androidBridge.handleGeckoMessage(JSON.stringify(msg));
|
||||
return JSON.parse(res);
|
||||
},
|
||||
};
|
@ -6,6 +6,7 @@
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ContactService.jsm',
|
||||
'HelperApps.jsm',
|
||||
'Home.jsm',
|
||||
'JNI.jsm',
|
||||
'LightweightThemeConsumer.jsm',
|
||||
|
Loading…
Reference in New Issue
Block a user