Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-11-30 13:47:32 +01:00
commit 590590b407
762 changed files with 8990 additions and 6611 deletions

157
.eslintignore Normal file
View File

@ -0,0 +1,157 @@
# Always ignore node_modules.
**/node_modules/**/*.*
# Exclude expected objdirs.
obj*/**
# We ignore all these directories by default, until we get them enabled.
# If you are enabling a directory, please add directory specific exclusions
# below.
accessible/**
addon-sdk/**
b2g/**
build/**
caps/**
chrome/**
config/**
db/**
docshell/**
dom/**
editor/**
embedding/**
extensions/**
gfx/**
gradle/**
hal/**
image/**
intl/**
ipc/**
js/**
layout/**
media/**
memory/**
mfbt/**
modules/**
mozglue/**
netwerk/**
nsprpub/**
other-licenses/**
parser/**
probes/**
python/**
rdf/**
security/**
services/**
startupcache/**
storage/**
testing/**
toolkit/**
tools/**
uriloader/**
view/**
webapprt/**
widget/**
xpcom/**
xpfe/**
xulrunner/**
# browser/ exclusions
browser/app/**
browser/base/**
browser/branding/**
browser/components/**
browser/config/**
browser/docs/**
browser/experiments/**
browser/extensions/pdfjs/**
browser/extensions/shumway/**
browser/fuel/**
browser/installer/**
browser/locales/**
browser/modules/**
browser/themes/**
# Loop specific exclusions
# This file currently uses a non-standard (and not on a standards track)
# if statement within catch.
browser/extensions/loop/content/modules/MozLoopWorker.js
# This file currently uses es7 features eslint issue:
# https://github.com/eslint/espree/issues/125
browser/extensions/loop/content/modules/MozLoopAPI.jsm
# Need to fix the configuration for this.
browser/extensions/loop/bootstrap.js
# Need to drop the preprocessing (bug 1212428)
browser/extensions/loop/content/preferences/prefs.js
# Libs we don't need to check
browser/extensions/loop/content/panels/vendor
browser/extensions/loop/content/shared/vendor
browser/extensions/loop/standalone/content/libs
# Libs we don't need to check
browser/extensions/loop/test/shared/vendor
# Coverage files
browser/extensions/loop/test/coverage
# These are generated react files that we don't need to check
browser/extensions/loop/content/panels/js/conversation.js
browser/extensions/loop/content/panels/js/conversationViews.js
browser/extensions/loop/content/panels/js/panel.js
browser/extensions/loop/content/panels/js/roomViews.js
browser/extensions/loop/content/panels/js/feedbackViews.js
browser/extensions/loop/content/shared/js/textChatView.js
browser/extensions/loop/content/shared/js/linkifiedTextView.js
browser/extensions/loop/content/shared/js/views.js
browser/extensions/loop/standalone/content/js/standaloneRoomViews.js
browser/extensions/loop/standalone/content/js/webapp.js
browser/extensions/loop/ui/ui-showcase.js
# Don't need to check the built tree
browser/extensions/loop/standalone/dist
# devtools/ exclusions
# Ignore d3
devtools/client/shared/d3.js
devtools/client/webaudioeditor/lib/dagre-d3.js
# Ignore codemirror
devtools/client/sourceeditor/codemirror/*.js
devtools/client/sourceeditor/codemirror/**/*.js
# Ignore jquery test libs
devtools/client/markupview/test/lib_*
# Ignore pre-processed files
devtools/client/framework/toolbox-process-window.js
devtools/client/performance/system.js
devtools/client/webide/webide-prefs.js
# Ignore various libs
devtools/shared/jsbeautify/*
devtools/shared/acorn/*
devtools/shared/tern/*
devtools/shared/pretty-fast/*
devtools/shared/sourcemap/*
# mobile/android/ exclusions
mobile/android/chrome/content
mobile/android/tests/
# Uses `#filter substitution`
mobile/android/b2gdroid/app/b2gdroid.js
mobile/android/app/mobile.js
mobile/android/chrome/content/healthreport-prefs.js
# Uses `#expand`
mobile/android/chrome/content/about.js
# Not much JS to lint and non-standard at that
mobile/android/installer/
mobile/android/locales/
# Pretty sure we're disabling this one anyway
mobile/android/modules/ContactService.jsm
# es7 proposed: array comprehensions
# https://github.com/eslint/espree/issues/125
mobile/android/modules/WebappManager.jsm
# Non-standard `(catch ex if ...)`
mobile/android/components/Snippets.js

View File

@ -80,9 +80,15 @@ GRTAGS
GSYMS
GPATH
# Unit tests for Loop
# Various items for Loop
^browser/components/loop/standalone/content/config\.js$
^browser/components/loop/standalone/node_modules/
^browser/extensions/loop/.*/node_modules/
^browser/extensions/loop/.*\.module-cache
^browser/extensions/loop/test/coverage/desktop
^browser/extensions/loop/test/coverage/shared_standalone
^browser/extensions/loop/test/visual-regression/diff
^browser/extensions/loop/test/visual-regression/new
^browser/extensions/loop/test/visual-regression/refs
# Git clone directory for updating web-platform-tests
^testing/web-platform/sync/
@ -98,6 +104,9 @@ GPATH
^testing/mozharness/logs/
^testing/mozharness/build/
# Ignore tox generated dir
.tox/
# Ignore node_modules from eslint-plugin-mozilla
^testing/eslint-plugin-mozilla/node_modules/

View File

@ -552,7 +552,7 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
NSPoint tmpPoint = NSMakePoint(point.x,
[mainView frame].size.height - point.y);
nsIntPoint geckoPoint = nsCocoaUtils::
LayoutDeviceIntPoint geckoPoint = nsCocoaUtils::
CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
mozAccessible* nativeChild = nil;

View File

@ -1376,42 +1376,6 @@ pref("shumway.disabled", true);
// (This is intentionally on the high side; see bug 746055.)
pref("image.mem.max_decoded_image_kb", 256000);
pref("loop.enabled", true);
pref("loop.textChat.enabled", true);
pref("loop.server", "https://loop.services.mozilla.com/v0");
pref("loop.linkClicker.url", "https://hello.firefox.com/");
pref("loop.gettingStarted.seen", false);
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
pref("loop.gettingStarted.resumeOnFirstJoin", false);
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/");
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/firefox-hello/");
pref("loop.do_not_disturb", false);
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
pref("loop.retry_delay.start", 60000);
pref("loop.retry_delay.limit", 300000);
pref("loop.ping.interval", 1800000);
pref("loop.ping.timeout", 10000);
pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
pref("loop.feedback.product", "Loop");
pref("loop.debug.loglevel", "Error");
pref("loop.debug.dispatcher", false);
pref("loop.debug.sdk", false);
pref("loop.debug.twoWayMediaTelemetry", false);
pref("loop.feedback.dateLastSeenSec", 0);
pref("loop.feedback.periodSec", 15770000); // 6 months.
pref("loop.feedback.formURL", "https://www.mozilla.org/firefox/hello/npssurvey/");
pref("loop.feedback.manualFormURL", "https://www.mozilla.org/firefox/hello/feedbacksurvey/");
#ifdef DEBUG
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
#else
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
#endif
pref("loop.fxa_oauth.tokendata", "");
pref("loop.fxa_oauth.profile", "");
pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
pref("loop.browserSharing.showInfoBar", true);
pref("social.sidebar.unload_timeout_ms", 10000);
// Activation from inside of share panel is possible if activationPanelEnabled

View File

@ -1,613 +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/.
// the "exported" symbols
var LoopUI;
(function() {
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kBrowserSharingNotificationId = "loop-sharing-notification";
const kPrefBrowserSharingInfoBar = "browserSharing.showInfoBar";
LoopUI = {
/**
* @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
* instance for this window.
*/
get toolbarButton() {
delete this.toolbarButton;
return this.toolbarButton = CustomizableUI.getWidget("loop-button").forWindow(window);
},
/**
* @var {XULElement} panel Getter for the Loop panel element.
*/
get panel() {
delete this.panel;
return this.panel = document.getElementById("loop-notification-panel");
},
/**
* @var {XULElement|null} browser Getter for the Loop panel browser element.
* Will be NULL if the panel hasn't loaded yet.
*/
get browser() {
let browser = document.querySelector("#loop-notification-panel > #loop-panel-iframe");
if (browser) {
delete this.browser;
this.browser = browser;
}
return browser;
},
/**
* @var {String|null} selectedTab Getter for the name of the currently selected
* tab inside the Loop panel. Will be NULL if
* the panel hasn't loaded yet.
*/
get selectedTab() {
if (!this.browser) {
return null;
}
let selectedTab = this.browser.contentDocument.querySelector(".tab-view > .selected");
return selectedTab && selectedTab.getAttribute("data-tab-name");
},
/**
* @return {Promise}
*/
promiseDocumentVisible(aDocument) {
if (!aDocument.hidden) {
return Promise.resolve(aDocument);
}
return new Promise((resolve) => {
aDocument.addEventListener("visibilitychange", function onVisibilityChanged() {
aDocument.removeEventListener("visibilitychange", onVisibilityChanged);
resolve(aDocument);
});
});
},
/**
* Toggle between opening or hiding the Loop panel.
*
* @param {DOMEvent} [event] Optional event that triggered the call to this
* function.
* @param {String} [tabId] Optional name of the tab to select after the panel
* has opened. Does nothing when the panel is hidden.
* @return {Promise}
*/
togglePanel: function(event, tabId = null) {
if (!this.panel) {
// We're on the hidden window! What fun!
let obs = win => {
Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
win.LoopUI.togglePanel(event, tabId);
};
Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
return OpenBrowserWindow();
}
if (this.panel.state == "open") {
return new Promise(resolve => {
this.panel.hidePopup();
resolve();
});
}
return this.openCallPanel(event, tabId).then(doc => {
let fm = Services.focus;
fm.moveFocus(doc.defaultView, null, fm.MOVEFOCUS_FIRST, fm.FLAG_NOSCROLL);
}).catch(err => {
Cu.reportError(x);
});
},
/**
* Opens the panel for Loop and sizes it appropriately.
*
* @param {event} event The event opening the panel, used to anchor
* the panel to the button which triggers it.
* @param {String} [tabId] Identifier of the tab to select when the panel is
* opened. Example: 'rooms', 'contacts', etc.
* @return {Promise}
*/
openCallPanel: function(event, tabId = null) {
return new Promise((resolve) => {
let callback = iframe => {
// Helper function to show a specific tab view in the panel.
function showTab() {
if (!tabId) {
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
return;
}
let win = iframe.contentWindow;
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
detail: {
action: "selectTab",
tab: tabId
}
}, win));
win.dispatchEvent(ev);
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
}
// If the panel has been opened and initialized before, we can skip waiting
// for the content to load - because it's already there.
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
showTab();
return;
}
let documentDOMLoaded = () => {
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
// Handle window.close correctly on the panel.
this.hookWindowCloseForPanelClose(iframe.contentWindow);
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
iframe.contentWindow.removeEventListener("loopPanelInitialized",
loopPanelInitialized);
showTab();
});
};
iframe.addEventListener("DOMContentLoaded", documentDOMLoaded, true);
};
// Used to clear the temporary "login" state from the button.
Services.obs.notifyObservers(null, "loop-status-changed", null);
this.shouldResumeTour().then((resume) => {
if (resume) {
// Assume the conversation with the visitor wasn't open since we would
// have resumed the tour as soon as the visitor joined if it was (and
// the pref would have been set to false already.
this.MozLoopService.resumeTour("waiting");
resolve();
return;
}
this.LoopAPI.initialize();
let anchor = event ? event.target : this.toolbarButton.anchor;
let setHeight = 410;
if (gBrowser.selectedBrowser.getAttribute("remote") === "true") {
setHeight = 262;
}
this.PanelFrame.showPopup(window, anchor,
"loop", null, "about:looppanel",
// Loop wants a fixed size for the panel. This also stops it dynamically resizing.
{ width: 330, height: setHeight },
callback);
});
});
},
/**
* Method to know whether actions to open the panel should instead resume the tour.
*
* We need the panel to be opened via UITour so that it gets @noautohide.
*
* @return {Promise} resolving with a {Boolean} of whether the tour should be resumed instead of
* opening the panel.
*/
shouldResumeTour: Task.async(function* () {
// Resume the FTU tour if this is the first time a room was joined by
// someone else since the tour.
if (!Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin")) {
return false;
}
if (!this.LoopRooms.participantsCount) {
// Nobody is in the rooms
return false;
}
let roomsWithNonOwners = yield this.roomsWithNonOwners();
if (!roomsWithNonOwners.length) {
// We were the only one in a room but we want to know about someone else joining.
return false;
}
return true;
}),
/**
* @return {Promise} resolved with an array of Rooms with participants (excluding owners)
*/
roomsWithNonOwners: function() {
return new Promise(resolve => {
this.LoopRooms.getAll((error, rooms) => {
let roomsWithNonOwners = [];
for (let room of rooms) {
if (!("participants" in room)) {
continue;
}
let numNonOwners = room.participants.filter(participant => !participant.owner).length;
if (!numNonOwners) {
continue;
}
roomsWithNonOwners.push(room);
}
resolve(roomsWithNonOwners);
});
});
},
/**
* Triggers the initialization of the loop service. Called by
* delayedStartup.
*/
init: function() {
// Add observer notifications before the service is initialized
Services.obs.addObserver(this, "loop-status-changed", false);
// This is a promise for test purposes, but we don't want to be logging
// expected errors to the console, so we catch them here.
this.MozLoopService.initialize().catch(ex => {
if (!ex.message ||
(!ex.message.contains("not enabled") &&
!ex.message.contains("not needed"))) {
console.error(ex);
}
});
this.updateToolbarState();
},
uninit: function() {
Services.obs.removeObserver(this, "loop-status-changed");
},
// Implements nsIObserver
observe: function(subject, topic, data) {
if (topic != "loop-status-changed") {
return;
}
this.updateToolbarState(data);
},
/**
* Updates the toolbar/menu-button state to reflect Loop status.
*
* @param {string} [aReason] Some states are only shown if
* a related reason is provided.
*
* aReason="login": Used after a login is completed
* successfully. This is used so the state can be
* temporarily shown until the next state change.
*/
updateToolbarState: function(aReason = null) {
if (!this.toolbarButton.node) {
return;
}
let state = "";
let mozL10nId = "loop-call-button3";
let suffix = ".tooltiptext";
if (this.MozLoopService.errors.size) {
state = "error";
mozL10nId += "-error";
} else if (this.MozLoopService.screenShareActive) {
state = "action";
mozL10nId += "-screensharing";
} else if (aReason == "login" && this.MozLoopService.userProfile) {
state = "active";
mozL10nId += "-active";
suffix += "2";
} else if (this.MozLoopService.doNotDisturb) {
state = "disabled";
mozL10nId += "-donotdisturb";
} else if (this.MozLoopService.roomsParticipantsCount > 0) {
state = "active";
this.roomsWithNonOwners().then(roomsWithNonOwners => {
if (roomsWithNonOwners.length > 0) {
mozL10nId += "-participantswaiting";
} else {
mozL10nId += "-active";
}
suffix += "2";
this.updateTooltiptext(mozL10nId + suffix);
this.toolbarButton.node.setAttribute("state", state);
});
return;
} else {
suffix += "2";
}
this.toolbarButton.node.setAttribute("state", state);
this.updateTooltiptext(mozL10nId + suffix);
},
/**
* Updates the tootltiptext to reflect Loop status.
*
* @param {string} [mozL10nId] l10n ID that refelct the current
* Loop status.
*/
updateTooltiptext: function(mozL10nId) {
this.toolbarButton.node.setAttribute("tooltiptext", mozL10nId);
var tooltiptext = CustomizableUI.getLocalizedProperty(this.toolbarButton, "tooltiptext");
this.toolbarButton.node.setAttribute("tooltiptext", tooltiptext);
},
/**
* Show a desktop notification when 'do not disturb' isn't enabled.
*
* @param {Object} options Set of options that may tweak the appearance and
* behavior of the notification.
* Option params:
* - {String} title Notification title message
* - {String} [message] Notification body text
* - {String} [icon] Notification icon
* - {String} [sound] Sound to play
* - {String} [selectTab] Tab to select when the panel
* opens
* - {Function} [onclick] Callback to invoke when
* the notification is clicked.
* Opens the panel by default.
*/
showNotification: function(options) {
if (this.MozLoopService.doNotDisturb) {
return;
}
if (!options.title) {
throw new Error("Missing title, can not display notification");
}
let notificationOptions = {
body: options.message || ""
};
if (options.icon) {
notificationOptions.icon = options.icon;
}
if (options.sound) {
// This will not do anything, until bug bug 1105222 is resolved.
notificationOptions.mozbehavior = {
soundFile: ""
};
this.playSound(options.sound);
}
let notification = new window.Notification(options.title, notificationOptions);
notification.addEventListener("click", e => {
if (window.closed) {
return;
}
try {
window.focus();
} catch (ex) {}
// We need a setTimeout here, otherwise the panel won't show after the
// window received focus.
window.setTimeout(() => {
if (typeof options.onclick == "function") {
options.onclick();
} else {
// Open the Loop panel as a default action.
this.openCallPanel(null, options.selectTab || null);
}
}, 0);
});
},
/**
* Play a sound in this window IF there's no sound playing yet.
*
* @param {String} name Name of the sound, like 'ringtone' or 'room-joined'
*/
playSound: function(name) {
if (this.ActiveSound || this.MozLoopService.doNotDisturb) {
return;
}
this.activeSound = new window.Audio();
this.activeSound.src = `chrome://browser/content/loop/shared/sounds/${name}.ogg`;
this.activeSound.load();
this.activeSound.play();
this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
},
/**
* Start listening to selected tab changes and notify any content page that's
* listening to 'BrowserSwitch' push messages.
*
* Push message parameters:
* - {Integer} windowId The new windowId for the browser.
*/
startBrowserSharing: function() {
if (!this._listeningToTabSelect) {
gBrowser.tabContainer.addEventListener("TabSelect", this);
this._listeningToTabSelect = true;
}
this._maybeShowBrowserSharingInfoBar();
// Get the first window Id for the listener.
this.LoopAPI.broadcastPushMessage("BrowserSwitch",
gBrowser.selectedBrowser.outerWindowID);
},
/**
* Stop listening to selected tab changes.
*/
stopBrowserSharing: function() {
if (!this._listeningToTabSelect) {
return;
}
this._hideBrowserSharingInfoBar();
gBrowser.tabContainer.removeEventListener("TabSelect", this);
this._listeningToTabSelect = false;
},
/**
* Helper function to fetch a localized string via the MozLoopService API.
* It's currently inconveniently wrapped inside a string of stringified JSON.
*
* @param {String} key The element id to get strings for.
* @return {String}
*/
_getString: function(key) {
let str = this.MozLoopService.getStrings(key);
if (str) {
str = JSON.parse(str).textContent;
}
return str;
},
/**
* Shows an infobar notification at the top of the browser window that warns
* the user that their browser tabs are being broadcasted through the current
* conversation.
*/
_maybeShowBrowserSharingInfoBar: function() {
this._hideBrowserSharingInfoBar();
// Don't show the infobar if it's been permanently disabled from the menu.
if (!this.MozLoopService.getLoopPref(kPrefBrowserSharingInfoBar)) {
return;
}
let box = gBrowser.getNotificationBox();
let paused = false;
let bar = box.appendNotification(
this._getString("infobar_screenshare_browser_message"),
kBrowserSharingNotificationId,
// Icon is defined in browser theme CSS.
null,
box.PRIORITY_WARNING_LOW,
[{
label: this._getString("infobar_button_pause_label"),
accessKey: this._getString("infobar_button_pause_accesskey"),
isDefault: false,
callback: (event, buttonInfo, buttonNode) => {
paused = !paused;
bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
this._getString("infobar_screenshare_browser_message");
bar.classList.toggle("paused", paused);
buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
this._getString("infobar_button_pause_label");
buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
this._getString("infobar_button_pause_accesskey");
return true;
}
},
{
label: this._getString("infobar_button_stop_label"),
accessKey: this._getString("infobar_button_stop_accesskey"),
isDefault: true,
callback: () => {
this._hideBrowserSharingInfoBar();
LoopUI.MozLoopService.hangupAllChatWindows();
}
}]
);
// Keep showing the notification bar until the user explicitly closes it.
bar.persistence = -1;
},
/**
* Hides the infobar, permanantly if requested.
*
* @param {Boolean} permanently Flag that determines if the infobar will never
* been shown again. Defaults to `false`.
* @return {Boolean} |true| if the infobar was hidden here.
*/
_hideBrowserSharingInfoBar: function(permanently = false, browser) {
browser = browser || gBrowser.selectedBrowser;
let box = gBrowser.getNotificationBox(browser);
let notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
let removed = false;
if (notification) {
box.removeNotification(notification);
removed = true;
}
if (permanently) {
this.MozLoopService.setLoopPref(kPrefBrowserSharingInfoBar, false);
}
return removed;
},
/**
* Handles events from gBrowser.
*/
handleEvent: function(event) {
// We only should get "select" events.
if (event.type != "TabSelect") {
return;
}
let wasVisible = false;
// Hide the infobar from the previous tab.
if (event.detail.previousTab) {
wasVisible = this._hideBrowserSharingInfoBar(false,
event.detail.previousTab.linkedBrowser);
}
// We've changed the tab, so get the new window id.
this.LoopAPI.broadcastPushMessage("BrowserSwitch",
gBrowser.selectedBrowser.outerWindowID);
if (wasVisible) {
// If the infobar was visible before, we should show it again after the
// switch.
this._maybeShowBrowserSharingInfoBar();
}
},
/**
* Fetch the favicon of the currently selected tab in the format of a data-uri.
*
* @param {Function} callback Function to be invoked with an error object as
* its first argument when an error occurred or
* a string as second argument when the favicon
* has been fetched.
*/
getFavicon: function(callback) {
let pageURI = gBrowser.selectedTab.linkedBrowser.currentURI.spec;
// If the tab pages url starts with http(s), fetch icon.
if (!/^https?:/.test(pageURI)) {
callback();
return;
}
this.PlacesUtils.promiseFaviconLinkUrl(pageURI).then(uri => {
// We XHR the favicon to get a File object, which we can pass to the FileReader
// object. The FileReader turns the File object into a data-uri.
let xhr = new XMLHttpRequest();
xhr.open("get", uri.spec, true);
xhr.responseType = "blob";
xhr.overrideMimeType("image/x-icon");
xhr.onload = () => {
if (xhr.status != 200) {
callback(new Error("Invalid status code received for favicon XHR: " + xhr.status));
return;
}
let reader = new FileReader();
reader.onload = reader.onload = () => callback(null, reader.result);
reader.onerror = callback;
reader.readAsDataURL(xhr.response);
};
xhr.onerror = callback;
xhr.send();
}).catch(err => {
callback(err || new Error("No favicon found"));
});
}
};
})();
XPCOMUtils.defineLazyModuleGetter(LoopUI, "hookWindowCloseForPanelClose", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopAPI", "resource:///modules/loop/MozLoopAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopRooms", "resource:///modules/loop/LoopRooms.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "MozLoopService", "resource:///modules/loop/MozLoopService.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "PanelFrame", "resource:///modules/PanelFrame.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");

View File

@ -902,11 +902,6 @@ notification[value="translation"] {
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
}
/* Loop/ Hello */
notification[value="loop-sharing-notification"] .close-icon {
display: none;
}
/* Social */
/* Note the chatbox 'width' values are duplicated in socialchat.xml */
chatbox {

View File

@ -274,7 +274,6 @@ var gInitialPages = [
#include browser-fullScreen.js
#include browser-fullZoom.js
#include browser-gestureSupport.js
#include browser-loop.js
#include browser-places.js
#include browser-plugins.js
#include browser-safebrowsing.js
@ -1357,8 +1356,6 @@ var gBrowserInit = {
gDataNotificationInfoBar.init();
#endif
LoopUI.init();
gBrowserThumbnails.init();
// Add Devtools menuitems and listeners
@ -1540,7 +1537,6 @@ var gBrowserInit = {
TabView.uninit();
SocialUI.uninit();
gBrowserThumbnails.uninit();
LoopUI.uninit();
FullZoom.destroy();
Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");

View File

@ -108,12 +108,12 @@ static RedirEntry kRedirMap[] = {
{
"debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "loopconversation", "chrome://browser/content/loop/conversation.html",
{ "loopconversation", "chrome://loop/content/panels/conversation.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
nsIAboutModule::ENABLE_INDEXED_DB },
{ "looppanel", "chrome://browser/content/loop/panel.html",
{ "looppanel", "chrome://loop/content/panels/panel.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT |

View File

@ -950,42 +950,6 @@ const CustomizableWidgets = [
let win = aEvent.view;
win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser)
}
}, {
id: "loop-button",
type: "custom",
label: "loop-call-button3.label",
tooltiptext: "loop-call-button3.tooltiptext2",
privateBrowsingTooltiptext: "loop-call-button3-pb.tooltiptext",
defaultArea: CustomizableUI.AREA_NAVBAR,
introducedInVersion: 4,
onBuild: function(aDocument) {
// If we're not supposed to see the button, return zip.
if (!Services.prefs.getBoolPref("loop.enabled")) {
return null;
}
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
node.setAttribute("id", this.id);
node.classList.add("toolbarbutton-1");
node.classList.add("chromeclass-toolbar-additional");
node.classList.add("badged-button");
node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
if (isWindowPrivate)
node.setAttribute("disabled", "true");
let tooltiptext = isWindowPrivate ?
CustomizableUI.getLocalizedProperty(this, "privateBrowsingTooltiptext",
[CustomizableUI.getLocalizedProperty(this, "label")]) :
CustomizableUI.getLocalizedProperty(this, "tooltiptext");
node.setAttribute("tooltiptext", tooltiptext);
node.setAttribute("removable", "true");
node.addEventListener("command", function(event) {
aDocument.defaultView.LoopUI.togglePanel(event);
});
return node;
}
}, {
id: "web-apps-button",
label: "web-apps-button.label",

View File

@ -1,3 +1,5 @@
"use strict";
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
@ -376,6 +378,14 @@ extensions.registerAPI((extension, context) => {
runSafe(context, callback, TabManager.convert(extension, tab));
},
getCurrent(callback) {
let tab;
if (context.tabId) {
tab = TabManager.convert(extension, TabManager.getTab(context.tabId));
}
runSafe(context, callback, tab);
},
getAllInWindow: function(...args) {
let window, callback;
if (args.length == 1) {

View File

@ -22,6 +22,7 @@ support-files =
[browser_ext_getViews.js]
[browser_ext_tabs_executeScript.js]
[browser_ext_tabs_query.js]
[browser_ext_tabs_getCurrent.js]
[browser_ext_tabs_update.js]
[browser_ext_tabs_onUpdated.js]
[browser_ext_tabs_sendMessage.js]

View File

@ -38,18 +38,10 @@ add_task(function* testDisabled() {
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
yield extension.startup();
yield extension.awaitMessage("ready");
yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
let elem = document.getElementById(browserActionId);
function click() {
EventUtils.synthesizeMouseAtCenter(elem, {}, window);
return new Promise(SimpleTest.executeSoon);
}
yield click();
yield clickBrowserAction(extension);
extension.sendMessage("check-clicked", true);
yield extension.awaitMessage("next-test");
@ -57,7 +49,7 @@ add_task(function* testDisabled() {
extension.sendMessage("disable");
yield extension.awaitMessage("next-test");
yield click();
yield clickBrowserAction(extension);
extension.sendMessage("check-clicked", false);
yield extension.awaitMessage("next-test");
@ -65,7 +57,7 @@ add_task(function* testDisabled() {
extension.sendMessage("enable");
yield extension.awaitMessage("next-test");
yield click();
yield clickBrowserAction(extension);
extension.sendMessage("check-clicked", true);
yield extension.awaitMessage("next-test");

View File

@ -115,13 +115,10 @@ add_task(function* testPageActionPopup() {
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let panelId = makeWidgetId(extension.id) + "-panel";
extension.onMessage("send-click", () => {
let button = document.getElementById(browserActionId);
EventUtils.synthesizeMouseAtCenter(button, {}, window);
clickBrowserAction(extension);
});
extension.onMessage("next-test", Task.async(function* () {

View File

@ -126,10 +126,7 @@ add_task(function* testPageActionPopup() {
let panelId = makeWidgetId(extension.id) + "-panel";
extension.onMessage("send-click", () => {
let image = document.getElementById(pageActionId);
let evt = new MouseEvent("click", {});
image.dispatchEvent(evt);
clickPageAction(extension);
});
extension.onMessage("next-test", Task.async(function* () {
@ -146,7 +143,8 @@ add_task(function* testPageActionPopup() {
}));
yield Promise.all([extension.startup(), extension.awaitFinish("pageaction-tests-done")]);
yield extension.startup();
yield extension.awaitFinish("pageaction-tests-done");
yield extension.unload();
@ -192,21 +190,15 @@ add_task(function* testPageActionSecurity() {
},
});
yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
yield extension.startup();
yield extension.awaitMessage("ready");
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let browserAction = document.getElementById(browserActionId);
let evt = new CustomEvent("command", {});
browserAction.dispatchEvent(evt);
let pageAction = document.getElementById(pageActionId);
evt = new MouseEvent("click", {});
pageAction.dispatchEvent(evt);
yield clickBrowserAction(extension);
yield clickPageAction(extension);
yield extension.unload();
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let node = document.getElementById(pageActionId);
is(node, undefined, "pageAction image removed from document");

View File

@ -0,0 +1,66 @@
"use strict";
add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
"browser_action": { "default_popup": "popup.html" },
},
files: {
"tab.js": function() {
let url = document.location.href;
browser.tabs.getCurrent(currentTab => {
browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
// Activate the tab.
browser.tabs.onActivated.addListener(function listener({ tabId }) {
if (tabId == currentTab.id) {
browser.tabs.onActivated.removeListener(listener);
browser.tabs.getCurrent(currentTab => {
browser.test.assertEq(currentTab.id, tabId, "in active background tab");
browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
browser.test.sendMessage("tab-finished");
browser.tabs.remove(tabId);
});
}
});
browser.tabs.update(currentTab.id, { active: true });
});
},
"popup.js": function() {
browser.tabs.getCurrent(tab => {
browser.test.assertEq(tab, undefined, "getCurrent in popup script");
browser.test.sendMessage("popup-finished");
});
},
"tab.html": `<head><meta charset="utf-8"><script src="tab.js"></script></head>`,
"popup.html": `<head><meta charset="utf-8"><script src="popup.js"></script></head>`,
},
background: function() {
browser.tabs.getCurrent(tab => {
browser.test.assertEq(tab, undefined, "getCurrent in background script");
browser.test.sendMessage("background-finished");
});
browser.tabs.create({ url: "tab.html", active: false });
},
});
yield extension.startup();
yield extension.awaitMessage("background-finished");
yield extension.awaitMessage("tab-finished");
clickBrowserAction(extension);
yield extension.awaitMessage("popup-finished");
yield extension.unload();
});

View File

@ -23,3 +23,27 @@ var focusWindow = Task.async(function* focusWindow(win)
win.focus();
yield promise;
});
function clickBrowserAction(extension, win = window) {
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let elem = win.document.getElementById(browserActionId);
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
return new Promise(SimpleTest.executeSoon);
}
function clickPageAction(extension, win = window) {
// This would normally be set automatically on navigation, and cleared
// when the user types a value into the URL bar, to show and hide page
// identity info and icons such as page action buttons.
//
// Unfortunately, that doesn't happen automatically in browser chrome
// tests.
SetPageProxyState("valid");
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let elem = win.document.getElementById(pageActionId);
EventUtils.synthesizeMouse(elem, 8, 8, {}, win);
return new Promise(SimpleTest.executeSoon);
}

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<!-- 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>
<head>
<meta charset="utf-8">
<!-- Title is set in conversation.js -->
<title></title>
<link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/conversation.css">
</head>
<body class="fx-embedded">
<div id="messages"></div>
<div id="main"></div>
<script type="text/javascript" src="loop/libs/l10n.js"></script>
<script type="text/javascript" src="loop/js/otconfig.js"></script>
<script type="text/javascript" src="loop/libs/sdk.js"></script>
<script type="text/javascript" src="loop/shared/libs/react-0.13.3.js"></script>
<script type="text/javascript" src="loop/shared/libs/lodash-3.9.3.js"></script>
<script type="text/javascript" src="loop/shared/libs/backbone-1.2.1.js"></script>
<script type="text/javascript" src="loop/shared/libs/classnames-2.2.0.js"></script>
<script type="text/javascript" src="loop/shared/js/loopapi-client.js"></script>
<script type="text/javascript" src="loop/shared/js/utils.js"></script>
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script>
<script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/views.js"></script>
<script type="text/javascript" src="loop/js/feedbackViews.js"></script>
<script type="text/javascript" src="loop/shared/js/textChatStore.js"></script>
<script type="text/javascript" src="loop/shared/js/textChatView.js"></script>
<script type="text/javascript" src="loop/shared/js/linkifiedTextView.js"></script>
<script type="text/javascript" src="loop/shared/js/urlRegExps.js"></script>
<script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
<script type="text/javascript" src="loop/js/roomStore.js"></script>
<script type="text/javascript" src="loop/js/roomViews.js"></script>
<script type="text/javascript" src="loop/js/conversation.js"></script>
</body>
</html>

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<!-- 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>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/css/panel.css">
</head>
<body class="panel">
<div id="main"></div>
<script type="text/javascript" src="loop/shared/libs/react-0.13.3.js"></script>
<script type="text/javascript" src="loop/libs/l10n.js"></script>
<script type="text/javascript" src="loop/shared/libs/lodash-3.9.3.js"></script>
<script type="text/javascript" src="loop/shared/libs/backbone-1.2.1.js"></script>
<script type="text/javascript" src="loop/shared/libs/classnames-2.2.0.js"></script>
<script type="text/javascript" src="loop/shared/js/loopapi-client.js"></script>
<script type="text/javascript" src="loop/shared/js/utils.js"></script>
<script type="text/javascript" src="loop/shared/js/models.js"></script>
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
<script type="text/javascript" src="loop/shared/js/views.js"></script>
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="loop/js/roomStore.js"></script>
<script type="text/javascript" src="loop/js/panel.js"></script>
</body>
</html>

View File

@ -1,136 +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/.
browser.jar:
# Desktop html files
content/browser/loop/conversation.html (content/conversation.html)
content/browser/loop/panel.html (content/panel.html)
# Desktop libs (see bottom of this file for TokBox sdk assets)
content/browser/loop/libs/l10n.js (content/libs/l10n.js)
# Desktop script
content/browser/loop/js/conversation.js (content/js/conversation.js)
content/browser/loop/js/conversationAppStore.js (content/js/conversationAppStore.js)
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
content/browser/loop/js/panel.js (content/js/panel.js)
content/browser/loop/js/roomStore.js (content/js/roomStore.js)
content/browser/loop/js/roomViews.js (content/js/roomViews.js)
content/browser/loop/js/feedbackViews.js (content/js/feedbackViews.js)
# Desktop styles
content/browser/loop/css/panel.css (content/css/panel.css)
# Shared styles
content/browser/loop/shared/css/reset.css (content/shared/css/reset.css)
content/browser/loop/shared/css/common.css (content/shared/css/common.css)
content/browser/loop/shared/css/conversation.css (content/shared/css/conversation.css)
# Shared images
content/browser/loop/shared/img/helloicon.svg (content/shared/img/helloicon.svg)
content/browser/loop/shared/img/icon_32.png (content/shared/img/icon_32.png)
content/browser/loop/shared/img/icon_64.png (content/shared/img/icon_64.png)
content/browser/loop/shared/img/spinner.svg (content/shared/img/spinner.svg)
# XXX could get rid of the png spinner usages and replace them with the svg
# one?
content/browser/loop/shared/img/spinner.png (content/shared/img/spinner.png)
content/browser/loop/shared/img/spinner@2x.png (content/shared/img/spinner@2x.png)
content/browser/loop/shared/img/sad_hello_icon_64x64.svg (content/shared/img/sad_hello_icon_64x64.svg)
content/browser/loop/shared/img/chatbubble-arrow-left.svg (content/shared/img/chatbubble-arrow-left.svg)
content/browser/loop/shared/img/chatbubble-arrow-right.svg (content/shared/img/chatbubble-arrow-right.svg)
content/browser/loop/shared/img/facemute-14x14.png (content/shared/img/facemute-14x14.png)
content/browser/loop/shared/img/facemute-14x14@2x.png (content/shared/img/facemute-14x14@2x.png)
content/browser/loop/shared/img/hangup-inverse-14x14.png (content/shared/img/hangup-inverse-14x14.png)
content/browser/loop/shared/img/hangup-inverse-14x14@2x.png (content/shared/img/hangup-inverse-14x14@2x.png)
content/browser/loop/shared/img/mute-inverse-14x14.png (content/shared/img/mute-inverse-14x14.png)
content/browser/loop/shared/img/mute-inverse-14x14@2x.png (content/shared/img/mute-inverse-14x14@2x.png)
content/browser/loop/shared/img/svg/glyph-email-16x16.svg (content/shared/img/svg/glyph-email-16x16.svg)
content/browser/loop/shared/img/svg/glyph-facebook-16x16.svg (content/shared/img/svg/glyph-facebook-16x16.svg)
content/browser/loop/shared/img/svg/glyph-help-16x16.svg (content/shared/img/svg/glyph-help-16x16.svg)
content/browser/loop/shared/img/svg/glyph-link-16x16.svg (content/shared/img/svg/glyph-link-16x16.svg)
content/browser/loop/shared/img/svg/glyph-user-16x16.svg (content/shared/img/svg/glyph-user-16x16.svg)
content/browser/loop/shared/img/svg/exit.svg (content/shared/img/svg/exit.svg)
content/browser/loop/shared/img/svg/audio.svg (content/shared/img/svg/audio.svg)
content/browser/loop/shared/img/svg/audio-hover.svg (content/shared/img/svg/audio-hover.svg)
content/browser/loop/shared/img/svg/audio-mute.svg (content/shared/img/svg/audio-mute.svg)
content/browser/loop/shared/img/svg/audio-mute-hover.svg (content/shared/img/svg/audio-mute-hover.svg)
content/browser/loop/shared/img/svg/video.svg (content/shared/img/svg/video.svg)
content/browser/loop/shared/img/svg/video-hover.svg (content/shared/img/svg/video-hover.svg)
content/browser/loop/shared/img/svg/video-mute.svg (content/shared/img/svg/video-mute.svg)
content/browser/loop/shared/img/svg/video-mute-hover.svg (content/shared/img/svg/video-mute-hover.svg)
content/browser/loop/shared/img/svg/settings.svg (content/shared/img/svg/settings.svg)
content/browser/loop/shared/img/svg/settings-hover.svg (content/shared/img/svg/settings-hover.svg)
content/browser/loop/shared/img/svg/sharing.svg (content/shared/img/svg/sharing.svg)
content/browser/loop/shared/img/svg/sharing-active.svg (content/shared/img/svg/sharing-active.svg)
content/browser/loop/shared/img/svg/sharing-pending.svg (content/shared/img/svg/sharing-pending.svg)
content/browser/loop/shared/img/svg/sharing-hover.svg (content/shared/img/svg/sharing-hover.svg)
content/browser/loop/shared/img/svg/media-group.svg (content/shared/img/svg/media-group.svg)
content/browser/loop/shared/img/svg/media-group-left-hover.svg (content/shared/img/svg/media-group-left-hover.svg)
content/browser/loop/shared/img/svg/media-group-right-hover.svg (content/shared/img/svg/media-group-right-hover.svg)
content/browser/loop/shared/img/audio-call-avatar.svg (content/shared/img/audio-call-avatar.svg)
content/browser/loop/shared/img/beta-ribbon.svg (content/shared/img/beta-ribbon.svg)
content/browser/loop/shared/img/check.svg (content/shared/img/check.svg)
content/browser/loop/shared/img/icons-10x10.svg (content/shared/img/icons-10x10.svg)
content/browser/loop/shared/img/icons-14x14.svg (content/shared/img/icons-14x14.svg)
content/browser/loop/shared/img/icons-16x16.svg (content/shared/img/icons-16x16.svg)
content/browser/loop/shared/img/movistar.png (content/shared/img/movistar.png)
content/browser/loop/shared/img/movistar@2x.png (content/shared/img/movistar@2x.png)
content/browser/loop/shared/img/vivo.png (content/shared/img/vivo.png)
content/browser/loop/shared/img/vivo@2x.png (content/shared/img/vivo@2x.png)
content/browser/loop/shared/img/02.png (content/shared/img/02.png)
content/browser/loop/shared/img/02@2x.png (content/shared/img/02@2x.png)
content/browser/loop/shared/img/telefonica.png (content/shared/img/telefonica.png)
content/browser/loop/shared/img/hello_logo.svg (content/shared/img/hello_logo.svg)
content/browser/loop/shared/img/telefonica@2x.png (content/shared/img/telefonica@2x.png)
content/browser/loop/shared/img/ellipsis-v.svg (content/shared/img/ellipsis-v.svg)
content/browser/loop/shared/img/empty_conversations.svg (content/shared/img/empty_conversations.svg)
content/browser/loop/shared/img/empty_search.svg (content/shared/img/empty_search.svg)
content/browser/loop/shared/img/animated-spinner.svg (content/shared/img/animated-spinner.svg)
content/browser/loop/shared/img/avatars.svg (content/shared/img/avatars.svg)
content/browser/loop/shared/img/firefox-avatar.svg (content/shared/img/firefox-avatar.svg)
content/browser/loop/shared/img/pause-12x12.svg (content/shared/img/pause-12x12.svg)
content/browser/loop/shared/img/play-12x12.svg (content/shared/img/play-12x12.svg)
content/browser/loop/shared/img/stop-12x12.svg (content/shared/img/stop-12x12.svg)
# Shared scripts
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
content/browser/loop/shared/js/store.js (content/shared/js/store.js)
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
content/browser/loop/shared/js/linkifiedTextView.js (content/shared/js/linkifiedTextView.js)
content/browser/loop/shared/js/loopapi-client.js (content/shared/js/loopapi-client.js)
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
content/browser/loop/shared/js/textChatStore.js (content/shared/js/textChatStore.js)
content/browser/loop/shared/js/textChatView.js (content/shared/js/textChatView.js)
content/browser/loop/shared/js/urlRegExps.js (content/shared/js/urlRegExps.js)
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
# Shared libs
#ifdef DEBUG
content/browser/loop/shared/libs/react-0.13.3.js (content/shared/libs/react-0.13.3.js)
#else
content/browser/loop/shared/libs/react-0.13.3.js (content/shared/libs/react-0.13.3-prod.js)
#endif
content/browser/loop/shared/libs/lodash-3.9.3.js (content/shared/libs/lodash-3.9.3.js)
content/browser/loop/shared/libs/backbone-1.2.1.js (content/shared/libs/backbone-1.2.1.js)
content/browser/loop/shared/libs/classnames-2.2.0.js (content/shared/libs/classnames-2.2.0.js)
# Shared sounds
content/browser/loop/shared/sounds/ringtone.ogg (content/shared/sounds/ringtone.ogg)
content/browser/loop/shared/sounds/connecting.ogg (content/shared/sounds/connecting.ogg)
content/browser/loop/shared/sounds/connected.ogg (content/shared/sounds/connected.ogg)
content/browser/loop/shared/sounds/terminated.ogg (content/shared/sounds/terminated.ogg)
content/browser/loop/shared/sounds/room-joined.ogg (content/shared/sounds/room-joined.ogg)
content/browser/loop/shared/sounds/room-joined-in.ogg (content/shared/sounds/room-joined-in.ogg)
content/browser/loop/shared/sounds/room-left.ogg (content/shared/sounds/room-left.ogg)
content/browser/loop/shared/sounds/failure.ogg (content/shared/sounds/failure.ogg)
content/browser/loop/shared/sounds/message.ogg (content/shared/sounds/message.ogg)
# Partner SDK assets
content/browser/loop/libs/sdk.js (content/shared/libs/sdk.js)
content/browser/loop/sdk-content/js/dynamic_config.min.js (content/shared/libs/sdk-content/js/dynamic_config.min.js)

View File

@ -1,3 +0,0 @@
{
"extends": "../.eslintrc-gecko"
}

View File

@ -13,6 +13,7 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsWindowsMigrationUtils.h"
#include "nsStringGlue.h"
#define NS_HANDLE_JET_ERROR(err) { \
if (err < JET_errSuccess) { \
@ -199,12 +200,15 @@ nsEdgeReadingListExtractor::ConvertJETError(const JET_ERR &aError)
return NS_ERROR_FILE_NOT_FOUND;
case JET_errDatabaseDirtyShutdown:
return NS_ERROR_FILE_CORRUPTED;
default:
nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
wchar_t* msg = new wchar_t[80];
swprintf(msg, 80, MOZ_UTF16("Unexpected JET error from ESE database: %ld"), aError);
consoleService->LogStringMessage(msg);
default: {
nsCOMPtr<nsIConsoleService>
consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
nsAutoString msg;
msg.AppendLiteral("Unexpected JET error from ESE database: ");
msg.AppendInt(aError);
consoleService->LogStringMessage(msg.get());
return NS_ERROR_FAILURE;
}
}
}

View File

@ -11,7 +11,6 @@ DIRS += [
'downloads',
'extensions',
'feeds',
'loop',
'migration',
'newtab',
'places',

View File

@ -9,8 +9,8 @@ var gContentWindow;
var loopButton;
var loopPanel = document.getElementById("loop-notification-panel");
const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
const { LoopRooms } = Components.utils.import("chrome://loop/content/modules/LoopRooms.jsm", {});
const { MozLoopServiceInternal } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
function test() {
UITourTest();

View File

@ -1,12 +1,16 @@
# This file currently uses a non-standard (and not on a standards track)
# if statement within catch.
modules/MozLoopWorker.js
content/modules/MozLoopWorker.js
# This file currently uses es7 features eslint issue:
# https://github.com/eslint/espree/issues/125
modules/MozLoopAPI.jsm
content/modules/MozLoopAPI.jsm
# Need to fix the configuration for this.
bootstrap.js
# Need to drop the preprocessing (bug 1212428)
content/preferences/prefs.js
# Libs we don't need to check
content/libs
content/shared/libs
content/panels/vendor
content/shared/vendor
standalone/content/libs
standalone/node_modules
# Libs we don't need to check
@ -15,11 +19,11 @@ test/shared/vendor
test/coverage
test/node_modules
# These are generated react files that we don't need to check
content/js/conversation.js
content/js/conversationViews.js
content/js/panel.js
content/js/roomViews.js
content/js/feedbackViews.js
content/panels/js/conversation.js
content/panels/js/conversationViews.js
content/panels/js/panel.js
content/panels/js/roomViews.js
content/panels/js/feedbackViews.js
content/shared/js/textChatView.js
content/shared/js/linkifiedTextView.js
content/shared/js/views.js

View File

@ -7,9 +7,10 @@
"blockBindings": true,
"destructuring": true,
"generators": true,
"objectLiteralShorthandMethods": true,
"restParams": true,
"spread": true,
"objectLiteralShorthandMethods": true,
"templateStrings": true,
},
"globals": {
// Gecko + Loop Globals.
@ -41,6 +42,7 @@
"MozLoopPushHandler": true,
"MozLoopService": true,
"OS": false,
"PrivateBrowsingUtils": false,
"roomsPushNotification": true,
"Services": false,
"Social": false,

View File

@ -22,9 +22,9 @@ using npm in order to compile the .jsx files into regular .js ones:
npm install -g react-tools@0.12.2
Once installed, run build-jsx with the --watch option from
browser/components/loop, eg.:
browser/extensions/loop, eg.:
cd browser/components/loop
cd browser/extensions/loop
./build-jsx --watch
build-jsx can also be do a one-time compile pass instead of watching if
@ -36,7 +36,7 @@ Hacking
=======
Please be sure to execute
browser/components/loop/run-all-loop-tests.sh
browser/extensions/loop/run-all-loop-tests.sh
from the top level before requesting review on a patch.
@ -52,7 +52,7 @@ If you install eslint and the react plugin globally:
npm install -g eslint
npm install -g eslint-plugin-react
You can also run it by hand in the browser/components/loop directory:
You can also run it by hand in the browser/extensions/loop directory:
eslint --ext .js --ext .jsx --ext .jsm .
@ -88,7 +88,7 @@ install that is properly configured. From the top-level gecko directory,
execute:
export LOOP_SERVER=/Users/larry/src/loop-server
./mach marionette-test browser/components/loop/test/functional/manifest.ini
./mach marionette-test browser/extensions/loop/test/functional/manifest.ini
Once the automation is complete, we'll include this in run-all-loop-tests.sh
as well.

849
browser/extensions/loop/bootstrap.js vendored Normal file
View File

@ -0,0 +1,849 @@
/* 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 { interfaces: Ci, utils: Cu, classes: Cc } = Components;
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kBrowserSharingNotificationId = "loop-sharing-notification";
const kPrefBrowserSharingInfoBar = "browserSharing.showInfoBar";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
/**
* This window listener gets loaded into each browser.xul window and is used
* to provide the required loop functions for the window.
*/
var WindowListener = {
/**
* Sets up the chrome integration within browser windows for Loop.
*
* @param {Object} window The window to inject the integration into.
*/
setupBrowserUI: function(window) {
let document = window.document;
let gBrowser = window.gBrowser;
let xhrClass = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
let FileReader = window.FileReader;
// the "exported" symbols
var LoopUI = {
/**
* @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
* instance for this window.
*/
get toolbarButton() {
delete this.toolbarButton;
return (this.toolbarButton = CustomizableUI.getWidget("loop-button").forWindow(window));
},
/**
* @var {XULElement} panel Getter for the Loop panel element.
*/
get panel() {
delete this.panel;
return (this.panel = document.getElementById("loop-notification-panel"));
},
/**
* @var {XULElement|null} browser Getter for the Loop panel browser element.
* Will be NULL if the panel hasn't loaded yet.
*/
get browser() {
let browser = document.querySelector("#loop-notification-panel > #loop-panel-iframe");
if (browser) {
delete this.browser;
this.browser = browser;
}
return browser;
},
/**
* @var {String|null} selectedTab Getter for the name of the currently selected
* tab inside the Loop panel. Will be NULL if
* the panel hasn't loaded yet.
*/
get selectedTab() {
if (!this.browser) {
return null;
}
let selectedTab = this.browser.contentDocument.querySelector(".tab-view > .selected");
return selectedTab && selectedTab.getAttribute("data-tab-name");
},
/**
* @return {Promise}
*/
promiseDocumentVisible(aDocument) {
if (!aDocument.hidden) {
return Promise.resolve(aDocument);
}
return new Promise((resolve) => {
aDocument.addEventListener("visibilitychange", function onVisibilityChanged() {
aDocument.removeEventListener("visibilitychange", onVisibilityChanged);
resolve(aDocument);
});
});
},
/**
* Toggle between opening or hiding the Loop panel.
*
* @param {DOMEvent} [event] Optional event that triggered the call to this
* function.
* @param {String} [tabId] Optional name of the tab to select after the panel
* has opened. Does nothing when the panel is hidden.
* @return {Promise}
*/
togglePanel: function(event, tabId = null) {
if (!this.panel) {
// We're on the hidden window! What fun!
let obs = win => {
Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
win.LoopUI.togglePanel(event, tabId);
};
Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
return window.OpenBrowserWindow();
}
if (this.panel.state == "open") {
return new Promise(resolve => {
this.panel.hidePopup();
resolve();
});
}
return this.openCallPanel(event, tabId).then(doc => {
let fm = Services.focus;
fm.moveFocus(doc.defaultView, null, fm.MOVEFOCUS_FIRST, fm.FLAG_NOSCROLL);
}).catch(err => {
Cu.reportError(err);
});
},
/**
* Opens the panel for Loop and sizes it appropriately.
*
* @param {event} event The event opening the panel, used to anchor
* the panel to the button which triggers it.
* @param {String} [tabId] Identifier of the tab to select when the panel is
* opened. Example: 'rooms', 'contacts', etc.
* @return {Promise}
*/
openCallPanel: function(event, tabId = null) {
return new Promise((resolve) => {
let callback = iframe => {
// Helper function to show a specific tab view in the panel.
function showTab() {
if (!tabId) {
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
return;
}
let win = iframe.contentWindow;
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
detail: {
action: "selectTab",
tab: tabId
}
}, win));
win.dispatchEvent(ev);
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
}
// If the panel has been opened and initialized before, we can skip waiting
// for the content to load - because it's already there.
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
showTab();
return;
}
let documentDOMLoaded = () => {
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
// Handle window.close correctly on the panel.
this.hookWindowCloseForPanelClose(iframe.contentWindow);
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
iframe.contentWindow.removeEventListener("loopPanelInitialized",
loopPanelInitialized);
showTab();
});
};
iframe.addEventListener("DOMContentLoaded", documentDOMLoaded, true);
};
// Used to clear the temporary "login" state from the button.
Services.obs.notifyObservers(null, "loop-status-changed", null);
this.shouldResumeTour().then((resume) => {
if (resume) {
// Assume the conversation with the visitor wasn't open since we would
// have resumed the tour as soon as the visitor joined if it was (and
// the pref would have been set to false already.
this.MozLoopService.resumeTour("waiting");
resolve();
return;
}
this.LoopAPI.initialize();
let anchor = event ? event.target : this.toolbarButton.anchor;
let setHeight = 410;
if (gBrowser.selectedBrowser.getAttribute("remote") === "true") {
setHeight = 262;
}
this.PanelFrame.showPopup(window, anchor,
"loop", null, "about:looppanel",
// Loop wants a fixed size for the panel. This also stops it dynamically resizing.
{ width: 330, height: setHeight },
callback);
});
});
},
/**
* Method to know whether actions to open the panel should instead resume the tour.
*
* We need the panel to be opened via UITour so that it gets @noautohide.
*
* @return {Promise} resolving with a {Boolean} of whether the tour should be resumed instead of
* opening the panel.
*/
shouldResumeTour: Task.async(function* () {
// Resume the FTU tour if this is the first time a room was joined by
// someone else since the tour.
if (!Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin")) {
return false;
}
if (!this.LoopRooms.participantsCount) {
// Nobody is in the rooms
return false;
}
let roomsWithNonOwners = yield this.roomsWithNonOwners();
if (!roomsWithNonOwners.length) {
// We were the only one in a room but we want to know about someone else joining.
return false;
}
return true;
}),
/**
* @return {Promise} resolved with an array of Rooms with participants (excluding owners)
*/
roomsWithNonOwners: function() {
return new Promise(resolve => {
this.LoopRooms.getAll((error, rooms) => {
let roomsWithNonOwners = [];
for (let room of rooms) {
if (!("participants" in room)) {
continue;
}
let numNonOwners = room.participants.filter(participant => !participant.owner).length;
if (!numNonOwners) {
continue;
}
roomsWithNonOwners.push(room);
}
resolve(roomsWithNonOwners);
});
});
},
/**
* Triggers the initialization of the loop service. Called by
* delayedStartup.
*/
init: function() {
// Cleanup when the window unloads.
window.addEventListener("unload", () => {
this.uninit();
});
// Add observer notifications before the service is initialized
Services.obs.addObserver(this, "loop-status-changed", false);
// This is a promise for test purposes, but we don't want to be logging
// expected errors to the console, so we catch them here.
this.MozLoopService.initialize().catch(ex => {
if (!ex.message ||
(!ex.message.contains("not enabled") &&
!ex.message.contains("not needed"))) {
console.error(ex);
}
});
this.updateToolbarState();
},
uninit: function() {
Services.obs.removeObserver(this, "loop-status-changed");
},
// Implements nsIObserver
observe: function(subject, topic, data) {
if (topic != "loop-status-changed") {
return;
}
this.updateToolbarState(data);
},
/**
* Updates the toolbar/menu-button state to reflect Loop status.
*
* @param {string} [aReason] Some states are only shown if
* a related reason is provided.
*
* aReason="login": Used after a login is completed
* successfully. This is used so the state can be
* temporarily shown until the next state change.
*/
updateToolbarState: function(aReason = null) {
if (!this.toolbarButton.node) {
return;
}
let state = "";
let mozL10nId = "loop-call-button3";
let suffix = ".tooltiptext";
if (this.MozLoopService.errors.size) {
state = "error";
mozL10nId += "-error";
} else if (this.MozLoopService.screenShareActive) {
state = "action";
mozL10nId += "-screensharing";
} else if (aReason == "login" && this.MozLoopService.userProfile) {
state = "active";
mozL10nId += "-active";
suffix += "2";
} else if (this.MozLoopService.doNotDisturb) {
state = "disabled";
mozL10nId += "-donotdisturb";
} else if (this.MozLoopService.roomsParticipantsCount > 0) {
state = "active";
this.roomsWithNonOwners().then(roomsWithNonOwners => {
if (roomsWithNonOwners.length > 0) {
mozL10nId += "-participantswaiting";
} else {
mozL10nId += "-active";
}
suffix += "2";
this.updateTooltiptext(mozL10nId + suffix);
this.toolbarButton.node.setAttribute("state", state);
});
return;
} else {
suffix += "2";
}
this.toolbarButton.node.setAttribute("state", state);
this.updateTooltiptext(mozL10nId + suffix);
},
/**
* Updates the tootltiptext to reflect Loop status.
*
* @param {string} [mozL10nId] l10n ID that refelct the current
* Loop status.
*/
updateTooltiptext: function(mozL10nId) {
this.toolbarButton.node.setAttribute("tooltiptext", mozL10nId);
var tooltiptext = CustomizableUI.getLocalizedProperty(this.toolbarButton, "tooltiptext");
this.toolbarButton.node.setAttribute("tooltiptext", tooltiptext);
},
/**
* Show a desktop notification when 'do not disturb' isn't enabled.
*
* @param {Object} options Set of options that may tweak the appearance and
* behavior of the notification.
* Option params:
* - {String} title Notification title message
* - {String} [message] Notification body text
* - {String} [icon] Notification icon
* - {String} [sound] Sound to play
* - {String} [selectTab] Tab to select when the panel
* opens
* - {Function} [onclick] Callback to invoke when
* the notification is clicked.
* Opens the panel by default.
*/
showNotification: function(options) {
if (this.MozLoopService.doNotDisturb) {
return;
}
if (!options.title) {
throw new Error("Missing title, can not display notification");
}
let notificationOptions = {
body: options.message || ""
};
if (options.icon) {
notificationOptions.icon = options.icon;
}
if (options.sound) {
// This will not do anything, until bug bug 1105222 is resolved.
notificationOptions.mozbehavior = {
soundFile: ""
};
this.playSound(options.sound);
}
let notification = new window.Notification(options.title, notificationOptions);
notification.addEventListener("click", e => {
if (window.closed) {
return;
}
try {
window.focus();
} catch (ex) {
// Do nothing.
}
// We need a setTimeout here, otherwise the panel won't show after the
// window received focus.
window.setTimeout(() => {
if (typeof options.onclick == "function") {
options.onclick();
} else {
// Open the Loop panel as a default action.
this.openCallPanel(null, options.selectTab || null);
}
}, 0);
});
},
/**
* Play a sound in this window IF there's no sound playing yet.
*
* @param {String} name Name of the sound, like 'ringtone' or 'room-joined'
*/
playSound: function(name) {
if (this.ActiveSound || this.MozLoopService.doNotDisturb) {
return;
}
this.activeSound = new window.Audio();
this.activeSound.src = `chrome://loop/content/shared/sounds/${name}.ogg`;
this.activeSound.load();
this.activeSound.play();
this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
},
/**
* Start listening to selected tab changes and notify any content page that's
* listening to 'BrowserSwitch' push messages.
*
* Push message parameters:
* - {Integer} windowId The new windowId for the browser.
*/
startBrowserSharing: function() {
if (!this._listeningToTabSelect) {
gBrowser.tabContainer.addEventListener("TabSelect", this);
this._listeningToTabSelect = true;
}
this._maybeShowBrowserSharingInfoBar();
// Get the first window Id for the listener.
this.LoopAPI.broadcastPushMessage("BrowserSwitch",
gBrowser.selectedBrowser.outerWindowID);
},
/**
* Stop listening to selected tab changes.
*/
stopBrowserSharing: function() {
if (!this._listeningToTabSelect) {
return;
}
this._hideBrowserSharingInfoBar();
gBrowser.tabContainer.removeEventListener("TabSelect", this);
this._listeningToTabSelect = false;
},
/**
* Helper function to fetch a localized string via the MozLoopService API.
* It's currently inconveniently wrapped inside a string of stringified JSON.
*
* @param {String} key The element id to get strings for.
* @return {String}
*/
_getString: function(key) {
let str = this.MozLoopService.getStrings(key);
if (str) {
str = JSON.parse(str).textContent;
}
return str;
},
/**
* Shows an infobar notification at the top of the browser window that warns
* the user that their browser tabs are being broadcasted through the current
* conversation.
*/
_maybeShowBrowserSharingInfoBar: function() {
this._hideBrowserSharingInfoBar();
// Don't show the infobar if it's been permanently disabled from the menu.
if (!this.MozLoopService.getLoopPref(kPrefBrowserSharingInfoBar)) {
return;
}
let box = gBrowser.getNotificationBox();
let paused = false;
let bar = box.appendNotification(
this._getString("infobar_screenshare_browser_message"),
kBrowserSharingNotificationId,
// Icon is defined in browser theme CSS.
null,
box.PRIORITY_WARNING_LOW,
[{
label: this._getString("infobar_button_pause_label"),
accessKey: this._getString("infobar_button_pause_accesskey"),
isDefault: false,
callback: (event, buttonInfo, buttonNode) => {
paused = !paused;
bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
this._getString("infobar_screenshare_browser_message");
bar.classList.toggle("paused", paused);
buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
this._getString("infobar_button_pause_label");
buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
this._getString("infobar_button_pause_accesskey");
return true;
}
},
{
label: this._getString("infobar_button_stop_label"),
accessKey: this._getString("infobar_button_stop_accesskey"),
isDefault: true,
callback: () => {
this._hideBrowserSharingInfoBar();
LoopUI.MozLoopService.hangupAllChatWindows();
}
}]
);
// Keep showing the notification bar until the user explicitly closes it.
bar.persistence = -1;
},
/**
* Hides the infobar, permanantly if requested.
*
* @param {Boolean} permanently Flag that determines if the infobar will never
* been shown again. Defaults to `false`.
* @return {Boolean} |true| if the infobar was hidden here.
*/
_hideBrowserSharingInfoBar: function(permanently = false, browser) {
browser = browser || gBrowser.selectedBrowser;
let box = gBrowser.getNotificationBox(browser);
let notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
let removed = false;
if (notification) {
box.removeNotification(notification);
removed = true;
}
if (permanently) {
this.MozLoopService.setLoopPref(kPrefBrowserSharingInfoBar, false);
}
return removed;
},
/**
* Handles events from gBrowser.
*/
handleEvent: function(event) {
// We only should get "select" events.
if (event.type != "TabSelect") {
return;
}
let wasVisible = false;
// Hide the infobar from the previous tab.
if (event.detail.previousTab) {
wasVisible = this._hideBrowserSharingInfoBar(false,
event.detail.previousTab.linkedBrowser);
}
// We've changed the tab, so get the new window id.
this.LoopAPI.broadcastPushMessage("BrowserSwitch",
gBrowser.selectedBrowser.outerWindowID);
if (wasVisible) {
// If the infobar was visible before, we should show it again after the
// switch.
this._maybeShowBrowserSharingInfoBar();
}
},
/**
* Fetch the favicon of the currently selected tab in the format of a data-uri.
*
* @param {Function} callback Function to be invoked with an error object as
* its first argument when an error occurred or
* a string as second argument when the favicon
* has been fetched.
*/
getFavicon: function(callback) {
let pageURI = gBrowser.selectedTab.linkedBrowser.currentURI.spec;
// If the tab pages url starts with http(s), fetch icon.
if (!/^https?:/.test(pageURI)) {
callback();
return;
}
this.PlacesUtils.promiseFaviconLinkUrl(pageURI).then(uri => {
// We XHR the favicon to get a File object, which we can pass to the FileReader
// object. The FileReader turns the File object into a data-uri.
let xhr = xhrClass.createInstance(Ci.nsIXMLHttpRequest);
xhr.open("get", uri.spec, true);
xhr.responseType = "blob";
xhr.overrideMimeType("image/x-icon");
xhr.onload = () => {
if (xhr.status != 200) {
callback(new Error("Invalid status code received for favicon XHR: " + xhr.status));
return;
}
let reader = new FileReader();
reader.onload = reader.onload = () => callback(null, reader.result);
reader.onerror = callback;
reader.readAsDataURL(xhr.response);
};
xhr.onerror = callback;
xhr.send();
}).catch(err => {
callback(err || new Error("No favicon found"));
});
}
};
XPCOMUtils.defineLazyModuleGetter(LoopUI, "hookWindowCloseForPanelClose", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopAPI", "chrome://loop/content/modules/MozLoopAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopRooms", "chrome://loop/content/modules/LoopRooms.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "MozLoopService", "chrome://loop/content/modules/MozLoopService.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "PanelFrame", "resource:///modules/PanelFrame.jsm");
XPCOMUtils.defineLazyModuleGetter(LoopUI, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
LoopUI.init();
window.LoopUI = LoopUI;
},
tearDownBrowserUI: function(window) {
let document = window.document;
// Take any steps to remove UI or anything from the browser window
// document.getElementById() etc. will work here
// XXX Add in tear-down of the panel.
},
// nsIWindowMediatorListener functions.
onOpenWindow: function(xulWindow) {
// A new window has opened.
let domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
// Wait for it to finish loading.
domWindow.addEventListener("load", function listener() {
domWindow.removeEventListener("load", listener, false);
// If this is a browser window then setup its UI.
if (domWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser") {
WindowListener.setupBrowserUI(domWindow);
}
}, false);
},
onCloseWindow: function(xulWindow) {
},
onWindowTitleChange: function(xulWindow, newTitle) {
}
};
/**
* Creates the loop button on the toolbar. Due to loop being a system-addon
* CustomizableUI already has a placement location for the button, so that
* we can be on the toolbar.
*/
function createLoopButton() {
CustomizableUI.createWidget({
id: "loop-button",
type: "custom",
label: "loop-call-button3.label",
tooltiptext: "loop-call-button3.tooltiptext2",
privateBrowsingTooltiptext: "loop-call-button3-pb.tooltiptext",
defaultArea: CustomizableUI.AREA_NAVBAR,
removable: true,
onBuild: function(aDocument) {
// If we're not supposed to see the button, return zip.
if (!Services.prefs.getBoolPref("loop.enabled")) {
return null;
}
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
node.setAttribute("id", this.id);
node.classList.add("toolbarbutton-1");
node.classList.add("chromeclass-toolbar-additional");
node.classList.add("badged-button");
node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
if (isWindowPrivate) {
node.setAttribute("disabled", "true");
}
let tooltiptext = isWindowPrivate ?
CustomizableUI.getLocalizedProperty(this, "privateBrowsingTooltiptext",
[CustomizableUI.getLocalizedProperty(this, "label")]) :
CustomizableUI.getLocalizedProperty(this, "tooltiptext");
node.setAttribute("tooltiptext", tooltiptext);
node.setAttribute("removable", "true");
node.addEventListener("command", function(event) {
aDocument.defaultView.LoopUI.togglePanel(event);
});
return node;
}
});
}
/**
* Loads the default preferences from the prefs file. This loads the preferences
* into the default branch, so they don't appear as user preferences.
*/
function loadDefaultPrefs() {
var branch = Services.prefs.getDefaultBranch("");
Services.scriptloader.loadSubScript("chrome://loop/content/preferences/prefs.js", {
pref: (key, val) => {
switch (typeof val) {
case "boolean":
branch.setBoolPref(key, val);
break;
case "number":
branch.setIntPref(key, val);
break;
case "string":
branch.setCharPref(key, val);
break;
}
}
});
}
/**
* Called when the add-on is started, e.g. when installed or when Firefox starts.
*/
function startup() {
loadDefaultPrefs();
createLoopButton();
// Attach to hidden window (for OS X).
try {
WindowListener.setupBrowserUI(Services.appShell.hiddenDOMWindow);
} catch (ex) {
// Hidden window didn't exist, so wait until startup is done.
let topic = "browser-delayed-startup-finished";
Services.obs.addObserver(function observer() {
Services.obs.removeObserver(observer, topic);
WindowListener.setupBrowserUI(Services.appShell.hiddenDOMWindow);
}, topic, false);
}
// Attach to existing browser windows, for modifying UI.
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
WindowListener.setupBrowserUI(domWindow);
}
// Wait for any new browser windows to open.
wm.addListener(WindowListener);
// Load our stylesheets.
let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
let sheets = ["chrome://loop-shared/skin/loop.css",
"chrome://loop/skin/platform.css"];
for (let sheet of sheets) {
let styleSheetURI = Services.io.newURI(sheet, null, null);
// XXX We would love to specify AUTHOR_SHEET here and in shutdown, however
// bug 1228542 prevents us from doing that as we'd cause a lot of assertions
// in debug mode for tests. Once that is fixed, we should be able to change
// this, and remove the !important attributes from our syle sheets.
styleSheetService.loadAndRegisterSheet(styleSheetURI,
styleSheetService.USER_SHEET);
}
}
/**
* Called when the add-on is shutting down, could be for re-installation
* or just uninstall.
*/
function shutdown() {
// Close any open chat windows
Cu.import("resource:///modules/Chat.jsm");
let isLoopURL = ({ src }) => /^about:loopconversation#/.test(src);
[...Chat.chatboxes].filter(isLoopURL).forEach(chatbox => {
chatbox.content.contentWindow.close();
});
// Detach from hidden window (for OS X).
WindowListener.tearDownBrowserUI(Services.appShell.hiddenDOMWindow);
// Detach from browser windows.
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
WindowListener.tearDownBrowserUI(domWindow);
}
// Stop waiting for browser windows to open.
wm.removeListener(WindowListener);
CustomizableUI.destroyWidget("loop-button");
// Unload stylesheets.
let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
let sheets = ["chrome://loop/content/addon/css/loop.css",
"chrome://loop/skin/platform.css"];
for (let sheet of sheets) {
let styleSheetURI = Services.io.newURI(sheet, null, null);
if (styleSheetService.sheetRegistered(styleSheetURI,
styleSheetService.USER_SHEET)) {
styleSheetService.unregisterSheet(styleSheetURI,
styleSheetService.USER_SHEET);
}
}
// Unload modules.
Cu.unload("chrome://loop/content/modules/MozLoopAPI.jsm");
Cu.unload("chrome://loop/content/modules/LoopRooms.jsm");
Cu.unload("chrome://loop/content/modules/MozLoopService.jsm");
}
function install() {}
function uninstall() {}

View File

@ -74,7 +74,7 @@ def find_react_command():
return [node, jsx_path]
SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "libs")
SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "vendor")
REACT_VERSION=find_react_version(SHARED_LIBS_DIR)
src_files = [] # files to be compiled
@ -99,7 +99,7 @@ else:
# parse the CLI arguments
description = 'Loop build tool for JSX files. ' + \
'Will scan entire loop directory and compile them in place. ' + \
'Must be executed from browser/components/loop directory.'
'Must be executed from browser/extensions/loop directory.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('--watch', '-w', action='store_true', help='continuous' +

View File

@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc-gecko"
}

View File

@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
const { MozLoopService, LOOP_SESSION_TYPE } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
const { MozLoopService, LOOP_SESSION_TYPE } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
@ -27,11 +27,11 @@ XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
});
XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache",
"resource:///modules/loop/LoopRoomsCache.jsm");
"chrome://loop/content/modules/LoopRoomsCache.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
"resource:///modules/loop/utils.js", "utils");
"chrome://loop/content/modules/utils.js", "utils");
XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
"resource:///modules/loop/crypto.js", "LoopCrypto");
"chrome://loop/content/shared/js/crypto.js", "LoopCrypto");
XPCOMUtils.defineLazyModuleGetter(this, "ObjectUtils",
"resource://gre/modules/ObjectUtils.jsm");

View File

@ -9,7 +9,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const { MozLoopService, LOOP_SESSION_TYPE } =
Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
"resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");

View File

@ -9,8 +9,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/loop/MozLoopService.jsm");
Cu.import("resource:///modules/loop/LoopRooms.jsm");
Cu.import("chrome://loop/content/modules/MozLoopService.jsm");
Cu.import("chrome://loop/content/modules/LoopRooms.jsm");
Cu.importGlobalProperties(["Blob"]);
XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",

View File

@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
const { MozLoopService } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
const { MozLoopService } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
const consoleLog = MozLoopService.log;
this.EXPORTED_SYMBOLS = ["MozLoopPushHandler"];

View File

@ -121,14 +121,14 @@ XPCOMUtils.defineConstant(this, "ROOM_DELETE", ROOM_DELETE);
XPCOMUtils.defineConstant(this, "ROOM_CONTEXT_ADD", ROOM_CONTEXT_ADD);
XPCOMUtils.defineLazyModuleGetter(this, "LoopAPI",
"resource:///modules/loop/MozLoopAPI.jsm");
"chrome://loop/content/modules/MozLoopAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
"resource://gre/modules/media/RTCStatsReport.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
"resource:///modules/loop/utils.js", "utils");
"chrome://loop/content/modules/utils.js", "utils");
XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
"resource:///modules/loop/crypto.js", "LoopCrypto");
"chrome://loop/content/shared/js/crypto.js", "LoopCrypto");
XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
@ -151,13 +151,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
"resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoopRooms",
"resource:///modules/loop/LoopRooms.jsm");
"chrome://loop/content/modules/LoopRooms.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "roomsPushNotification",
"resource:///modules/loop/LoopRooms.jsm");
"chrome://loop/content/modules/LoopRooms.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
"resource:///modules/loop/MozLoopPushHandler.jsm");
"chrome://loop/content/modules/MozLoopPushHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<!-- 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>
<head>
<meta charset="utf-8">
<!-- Title is set in conversation.js -->
<title></title>
<base href="chrome://loop/content">
<link rel="stylesheet" type="text/css" href="shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="shared/css/common.css">
<link rel="stylesheet" type="text/css" href="shared/css/conversation.css">
</head>
<body class="fx-embedded">
<div id="messages"></div>
<div id="main"></div>
<script type="text/javascript" src="panels/vendor/l10n.js"></script>
<script type="text/javascript" src="panels/js/otconfig.js"></script>
<script type="text/javascript" src="shared/vendor/sdk.js"></script>
<script type="text/javascript" src="shared/vendor/react-0.13.3.js"></script>
<script type="text/javascript" src="shared/vendor/lodash-3.9.3.js"></script>
<script type="text/javascript" src="shared/vendor/backbone-1.2.1.js"></script>
<script type="text/javascript" src="shared/vendor/classnames-2.2.0.js"></script>
<script type="text/javascript" src="shared/js/loopapi-client.js"></script>
<script type="text/javascript" src="shared/js/utils.js"></script>
<script type="text/javascript" src="shared/js/mixins.js"></script>
<script type="text/javascript" src="shared/js/actions.js"></script>
<script type="text/javascript" src="shared/js/validate.js"></script>
<script type="text/javascript" src="shared/js/dispatcher.js"></script>
<script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
<script type="text/javascript" src="shared/js/store.js"></script>
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="shared/js/views.js"></script>
<script type="text/javascript" src="shared/js/textChatStore.js"></script>
<script type="text/javascript" src="shared/js/textChatView.js"></script>
<script type="text/javascript" src="shared/js/linkifiedTextView.js"></script>
<script type="text/javascript" src="shared/js/urlRegExps.js"></script>
<script type="text/javascript" src="panels/js/conversationAppStore.js"></script>
<script type="text/javascript" src="panels/js/feedbackViews.js"></script>
<script type="text/javascript" src="panels/js/roomStore.js"></script>
<script type="text/javascript" src="panels/js/roomViews.js"></script>
<script type="text/javascript" src="panels/js/conversation.js"></script>
</body>
</html>

View File

@ -12,7 +12,7 @@ body {
/* Beta Ribbon */
.beta-ribbon {
background: url("../shared/img/beta-ribbon.svg") no-repeat;
background: url("../../shared/img/beta-ribbon.svg") no-repeat;
background-size: 30px;
width: 30px;
height: 30px;
@ -158,7 +158,7 @@ body {
transform: translateY(-50%);
padding-top: 11rem;
padding-bottom: 1rem;
background-image: url("../shared/img/empty_conversations.svg");
background-image: url("../../shared/img/empty_conversations.svg");
background-repeat: no-repeat;
background-position: top center;
}
@ -324,11 +324,11 @@ body {
}
.room-list > .room-entry:hover > h2 > .copy-link {
background-image: url(../shared/img/icons-16x16.svg#copy);
background-image: url(../../shared/img/icons-16x16.svg#copy);
}
.room-list > .room-entry:hover > h2 > .delete-link {
background-image: url(../shared/img/icons-16x16.svg#trash);
background-image: url(../../shared/img/icons-16x16.svg#trash);
}
/* scale this up to 1.1x and then back to the original size */
@ -338,7 +338,7 @@ body {
}
.room-list > .room-entry > h2 > .copy-link.checked {
background: transparent url(../shared/img/icons-16x16.svg#checkmark);
background: transparent url(../../shared/img/icons-16x16.svg#checkmark);
animation: pulse .150s;
animation-timing-function: ease-in-out;
top: 0;
@ -362,7 +362,7 @@ body {
/* Room entry edit button */
.room-entry-context-edit-btn {
background-image: url("../shared/img/icons-10x10.svg#edit-darkgrey");
background-image: url("../../shared/img/icons-10x10.svg#edit-darkgrey");
background-position: center;
background-repeat: no-repeat;
background-size: 12px;
@ -399,7 +399,7 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
.button-close {
background-color: transparent;
background-image: url(../shared/img/icons-10x10.svg#close);
background-image: url(../../shared/img/icons-10x10.svg#close);
background-repeat: no-repeat;
background-size: 8px 8px;
border: none;
@ -428,7 +428,7 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
}
.spinner.busy {
background-image: url(../shared/img/spinner.png);
background-image: url(../../shared/img/spinner.png);
animation-name: spinnerRotate;
animation-duration: 1s;
animation-timing-function: linear;
@ -437,7 +437,7 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
@media (min-resolution: 2dppx) {
.spinner.busy {
background-image: url(../shared/img/spinner@2x.png);
background-image: url(../../shared/img/spinner@2x.png);
}
}
@ -502,7 +502,7 @@ html[dir="rtl"] .generate-url-spinner {
margin-left: 10px;
margin-right: 10px;
vertical-align: middle;
background-image: url("../shared/img/telefonica.png");
background-image: url("../../shared/img/telefonica.png");
background-size: 72px 20px;
width: 72px;
height: 20px;
@ -510,21 +510,21 @@ html[dir="rtl"] .generate-url-spinner {
#powered-by-logo.en-GB,
#powered-by-logo.de {
background-image: url("../shared/img/02.png");
background-image: url("../../shared/img/02.png");
background-size: 21px 20px;
width: 21px;
height: 20px;
}
#powered-by-logo.pt-BR {
background-image: url("../shared/img/vivo.png");
background-image: url("../../shared/img/vivo.png");
background-size: 53px 26px;
width: 53px;
height: 26px;
}
#powered-by-logo[class^="es-"] {
background-image: url("../shared/img/movistar.png");
background-image: url("../../shared/img/movistar.png");
background-size: 92px 20px;
width: 92px;
height: 20px;
@ -532,20 +532,20 @@ html[dir="rtl"] .generate-url-spinner {
@media (min-resolution: 2dppx) {
#powered-by-logo {
background-image: url("../shared/img/telefonica@2x.png");
background-image: url("../../shared/img/telefonica@2x.png");
}
#powered-by-logo.en-GB,
#powered-by-logo.de {
background-image: url("../shared/img/02@2x.png");
background-image: url("../../shared/img/02@2x.png");
}
#powered-by-logo.pt-BR {
background-image: url("../shared/img/vivo@2x.png");
background-image: url("../../shared/img/vivo@2x.png");
}
#powered-by-logo[class^="es-"] {
background-image: url("../shared/img/movistar@2x.png");
background-image: url("../../shared/img/movistar@2x.png");
}
}
@ -594,7 +594,7 @@ html[dir="rtl"] .generate-url-spinner {
border: none;
cursor: pointer;
vertical-align: middle;
background: transparent url("../shared/img/icons-10x10.svg#settings-cog");
background: transparent url("../../shared/img/icons-10x10.svg#settings-cog");
background-position: center;
background-repeat: no-repeat;
background-size: cover;

View File

@ -29,7 +29,7 @@ loop.panel = (function(_, mozL10n) {
return (
React.createElement("div", {className: "fte-get-started-content"},
React.createElement("header", {className: "fte-title"},
React.createElement("img", {src: "loop/shared/img/hello_logo.svg"}),
React.createElement("img", {src: "shared/img/hello_logo.svg"}),
React.createElement("div", {className: "fte-subheader"},
mozL10n.get("first_time_experience_subheading")
)
@ -379,7 +379,7 @@ loop.panel = (function(_, mozL10n) {
_renderDefaultIcon: function() {
return (
React.createElement("div", {className: "room-entry-context-item"},
React.createElement("img", {src: "loop/shared/img/icons-16x16.svg#globe"})
React.createElement("img", {src: "shared/img/icons-16x16.svg#globe"})
)
);
},
@ -390,7 +390,7 @@ loop.panel = (function(_, mozL10n) {
React.createElement("a", {href: roomUrl.location,
onClick: this.handleClick,
title: roomUrl.description},
React.createElement("img", {src: roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"})
React.createElement("img", {src: roomUrl.thumbnail || "shared/img/icons-16x16.svg#globe"})
)
)
);
@ -717,7 +717,7 @@ loop.panel = (function(_, mozL10n) {
React.createElement("div", {className: "room-list"},
this._renderNewRoomButton(),
React.createElement("div", {className: "room-list-loading"},
React.createElement("img", {src: "loop/shared/img/animated-spinner.svg"})
React.createElement("img", {src: "shared/img/animated-spinner.svg"})
)
)
);
@ -884,7 +884,7 @@ loop.panel = (function(_, mozL10n) {
return (
React.createElement("div", {className: "error-content"},
React.createElement("header", {className: "error-title"},
React.createElement("img", {src: "loop/shared/img/sad_hello_icon_64x64.svg"}),
React.createElement("img", {src: "shared/img/sad_hello_icon_64x64.svg"}),
React.createElement("p", {className: "error-subheader"},
mozL10n.get("e10s_not_supported_subheading", {
brandShortname: mozL10n.get("clientShortname2")

View File

@ -29,7 +29,7 @@ loop.panel = (function(_, mozL10n) {
return (
<div className="fte-get-started-content">
<header className="fte-title">
<img src="loop/shared/img/hello_logo.svg" />
<img src="shared/img/hello_logo.svg" />
<div className="fte-subheader">
{mozL10n.get("first_time_experience_subheading")}
</div>
@ -379,7 +379,7 @@ loop.panel = (function(_, mozL10n) {
_renderDefaultIcon: function() {
return (
<div className="room-entry-context-item">
<img src="loop/shared/img/icons-16x16.svg#globe" />
<img src="shared/img/icons-16x16.svg#globe" />
</div>
);
},
@ -390,7 +390,7 @@ loop.panel = (function(_, mozL10n) {
<a href={roomUrl.location}
onClick={this.handleClick}
title={roomUrl.description}>
<img src={roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"} />
<img src={roomUrl.thumbnail || "shared/img/icons-16x16.svg#globe"} />
</a>
</div>
);
@ -717,7 +717,7 @@ loop.panel = (function(_, mozL10n) {
<div className="room-list">
{this._renderNewRoomButton()}
<div className="room-list-loading">
<img src="loop/shared/img/animated-spinner.svg" />
<img src="shared/img/animated-spinner.svg" />
</div>
</div>
);
@ -884,7 +884,7 @@ loop.panel = (function(_, mozL10n) {
return (
<div className="error-content">
<header className="error-title">
<img src="loop/shared/img/sad_hello_icon_64x64.svg" />
<img src="shared/img/sad_hello_icon_64x64.svg" />
<p className="error-subheader">
{mozL10n.get("e10s_not_supported_subheading", {
brandShortname: mozL10n.get("clientShortname2")

View File

@ -346,14 +346,14 @@ loop.roomViews = (function(mozL10n) {
"triggered": this.state.copiedUrl
}),
onClick: this.handleCopyButtonClick},
React.createElement("img", {src: "loop/shared/img/svg/glyph-link-16x16.svg"}),
React.createElement("img", {src: "shared/img/glyph-link-16x16.svg"}),
React.createElement("p", null, mozL10n.get(this.state.copiedUrl ?
"invite_copied_link_button" : "invite_copy_link_button"))
),
React.createElement("div", {className: "btn-email invite-button",
onClick: this.handleEmailButtonClick,
onMouseOver: this.resetTriggeredButtons},
React.createElement("img", {src: "loop/shared/img/svg/glyph-email-16x16.svg"}),
React.createElement("img", {src: "shared/img/glyph-email-16x16.svg"}),
React.createElement("p", null, mozL10n.get("invite_email_link_button"))
)
),
@ -536,7 +536,7 @@ loop.roomViews = (function(mozL10n) {
}
var url = this._getURL();
var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
var thumbnail = url && url.thumbnail || "shared/img/icons-16x16.svg#globe";
var urlDescription = url && url.description || "";
var location = url && url.location || "";

View File

@ -346,14 +346,14 @@ loop.roomViews = (function(mozL10n) {
"triggered": this.state.copiedUrl
})}
onClick={this.handleCopyButtonClick}>
<img src="loop/shared/img/svg/glyph-link-16x16.svg" />
<img src="shared/img/glyph-link-16x16.svg" />
<p>{mozL10n.get(this.state.copiedUrl ?
"invite_copied_link_button" : "invite_copy_link_button")}</p>
</div>
<div className="btn-email invite-button"
onClick={this.handleEmailButtonClick}
onMouseOver={this.resetTriggeredButtons}>
<img src="loop/shared/img/svg/glyph-email-16x16.svg" />
<img src="shared/img/glyph-email-16x16.svg" />
<p>{mozL10n.get("invite_email_link_button")}</p>
</div>
</div>
@ -536,7 +536,7 @@ loop.roomViews = (function(mozL10n) {
}
var url = this._getURL();
var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
var thumbnail = url && url.thumbnail || "shared/img/icons-16x16.svg#globe";
var urlDescription = url && url.description || "";
var location = url && url.location || "";

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<!-- 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>
<head>
<meta charset="utf-8">
<base href="chrome://loop/content">
<link rel="stylesheet" type="text/css" href="shared/css/reset.css">
<link rel="stylesheet" type="text/css" href="shared/css/common.css">
<link rel="stylesheet" type="text/css" href="panels/css/panel.css">
</head>
<body class="panel">
<div id="main"></div>
<script type="text/javascript" src="panels/vendor/l10n.js"></script>
<script type="text/javascript" src="shared/vendor/react-0.13.3.js"></script>
<script type="text/javascript" src="shared/vendor/lodash-3.9.3.js"></script>
<script type="text/javascript" src="shared/vendor/backbone-1.2.1.js"></script>
<script type="text/javascript" src="shared/vendor/classnames-2.2.0.js"></script>
<script type="text/javascript" src="shared/js/loopapi-client.js"></script>
<script type="text/javascript" src="shared/js/utils.js"></script>
<script type="text/javascript" src="shared/js/models.js"></script>
<script type="text/javascript" src="shared/js/mixins.js"></script>
<script type="text/javascript" src="shared/js/views.js"></script>
<script type="text/javascript" src="shared/js/validate.js"></script>
<script type="text/javascript" src="shared/js/actions.js"></script>
<script type="text/javascript" src="shared/js/dispatcher.js"></script>
<script type="text/javascript" src="shared/js/store.js"></script>
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="panels/js/roomStore.js"></script>
<script type="text/javascript" src="panels/js/panel.js"></script>
</body>
</html>

View File

@ -0,0 +1,33 @@
pref("loop.enabled", true);
pref("loop.textChat.enabled", true);
pref("loop.server", "https://loop.services.mozilla.com/v0");
pref("loop.linkClicker.url", "https://hello.firefox.com/");
pref("loop.gettingStarted.seen", false);
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
pref("loop.gettingStarted.resumeOnFirstJoin", false);
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/");
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/firefox-hello/");
pref("loop.do_not_disturb", false);
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
pref("loop.retry_delay.start", 60000);
pref("loop.retry_delay.limit", 300000);
pref("loop.ping.interval", 1800000);
pref("loop.ping.timeout", 10000);
pref("loop.debug.loglevel", "Error");
pref("loop.debug.dispatcher", false);
pref("loop.debug.sdk", false);
pref("loop.debug.twoWayMediaTelemetry", false);
pref("loop.feedback.dateLastSeenSec", 0);
pref("loop.feedback.periodSec", 15770000); // 6 months.
pref("loop.feedback.formURL", "https://www.mozilla.org/firefox/hello/npssurvey/");
pref("loop.feedback.manualFormURL", "https://www.mozilla.org/firefox/hello/feedbacksurvey/");
#ifdef DEBUG
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
#else
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
#endif
pref("loop.fxa_oauth.tokendata", "");
pref("loop.fxa_oauth.profile", "");
pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
pref("loop.browserSharing.showInfoBar", true);

View File

@ -65,7 +65,7 @@ html[dir="rtl"] .conversation-toolbar > li {
background-position: center;
background-repeat: no-repeat;
background-color: transparent;
background-image: url("../img/svg/media-group.svg");
background-image: url("../img/media-group.svg");
background-size: cover;
height: 28px;
width: 67px;
@ -73,25 +73,25 @@ html[dir="rtl"] .conversation-toolbar > li {
.conversation-toolbar-media-btn-group-box > button:last-child:active,
.conversation-toolbar-media-btn-group-box > button:last-child:hover {
background-image: url("../img/svg/media-group-right-hover.svg");
background-image: url("../img/media-group-right-hover.svg");
background-size: cover;
}
html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:last-child:active,
html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:last-child:hover {
background-image: url("../img/svg/media-group-left-hover.svg");
background-image: url("../img/media-group-left-hover.svg");
background-size: cover;
}
.conversation-toolbar-media-btn-group-box > button:first-child:active,
.conversation-toolbar-media-btn-group-box > button:first-child:hover {
background-image: url("../img/svg/media-group-left-hover.svg");
background-image: url("../img/media-group-left-hover.svg");
background-size: cover;
}
html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:first-child:active,
html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:first-child:hover {
background-image: url("../img/svg/media-group-right-hover.svg");
background-image: url("../img/media-group-right-hover.svg");
background-size: cover;
}
@ -106,76 +106,76 @@ html[dir="rtl"] .conversation-toolbar-btn-box.btn-edit-entry {
/* conversationViews.jsx */
.conversation-toolbar .btn-hangup {
background-image: url("../img/svg/exit.svg");
background-image: url("../img/exit.svg");
border: 0;
}
/* Audio mute button */
.btn-mute-audio:after {
content: url("../img/svg/audio.svg");
content: url("../img/audio.svg");
}
.btn-mute-audio.muted:after {
content: url("../img/svg/audio-mute.svg");
content: url("../img/audio-mute.svg");
}
.btn-mute-audio:hover:after,
.btn-mute-audio:active:after {
content: url("../img/svg/audio-hover.svg");
content: url("../img/audio-hover.svg");
}
.btn-mute-audio.muted:hover:after,
.btn-mute-audio.muted:active:after {
content: url("../img/svg/audio-mute-hover.svg");
content: url("../img/audio-mute-hover.svg");
}
/* Video mute button */
.btn-mute-video:after {
content: url("../img/svg/video.svg");
content: url("../img/video.svg");
}
.btn-mute-video:active:after,
.btn-mute-video:hover:after {
content: url("../img/svg/video-hover.svg");
content: url("../img/video-hover.svg");
}
.btn-mute-video.muted:after {
content: url("../img/svg/video-mute.svg");
content: url("../img/video-mute.svg");
}
.btn-mute-video.muted:hover:after,
.btn-mute-video.muted:active:after {
content: url("../img/svg/video-mute-hover.svg");
content: url("../img/video-mute-hover.svg");
}
.btn-settings {
width: 28px;
height: 28px;
background-size: 28px;
background-image: url("../img/svg/settings.svg");
background-image: url("../img/settings.svg");
background-color: transparent;
}
.btn-settings:hover,
.btn-settings:active {
background-image: url("../img/svg/settings-hover.svg");
background-image: url("../img/settings-hover.svg");
}
.btn-screen-share {
background-image: url("../img/svg/sharing.svg");
background-image: url("../img/sharing.svg");
}
.btn-screen-share:hover,
.btn-screen-share:active {
background-image: url("../img/svg/sharing-hover.svg");
background-image: url("../img/sharing-hover.svg");
}
.btn-screen-share.active {
background-image: url("../img/svg/sharing-active.svg");
background-image: url("../img/sharing-active.svg");
}
.btn-screen-share.disabled {
/* The screen share button is in its pending state when its disabled. */
background-image: url("../img/svg/sharing-pending.svg");
background-image: url("../img/sharing-pending.svg");
}
/* General Call (incoming or outgoing). */

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 700 B

View File

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 532 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 535 B

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 225 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 373 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 635 B

After

Width:  |  Height:  |  Size: 635 B

View File

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 451 B

View File

Before

Width:  |  Height:  |  Size: 832 B

After

Width:  |  Height:  |  Size: 832 B

View File

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 556 B

View File

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 188 B

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Some files were not shown because too many files have changed in this diff Show More