Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-03-03 14:10:55 +01:00
commit ba1773e422
123 changed files with 1948 additions and 316 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -138,7 +138,7 @@
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
<project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="c985699a5140b6dd5c7a43295e4d4397ce9b1267"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="39b3ad41158f71d41b549b31e30a8a63518f7362"/>
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
<project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
</manifest>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "c8ed1085a67490a1ecd7f275e5de9487e1b93b1d",
"git_revision": "4352d56f8c79a51eb44e43658472236a38d6f1d8",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "45fe49a5560cdaa07920739af65722a63b2de4f9",
"revision": "a414e3396742a2e623feda0ba9ed654f6cd525d5",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c8ed1085a67490a1ecd7f275e5de9487e1b93b1d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="4352d56f8c79a51eb44e43658472236a38d6f1d8"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -343,5 +343,63 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
},
/**
* Adds a listener for browser sharing. It will inform the listener straight
* away for the current windowId, and then on every tab change.
*
* Listener parameters:
* - {Object} err If there is a error this will be defined, null otherwise.
* - {Integer} windowId The new windowId for the browser.
*
* @param {Function} listener The listener to receive information on when the
* windowId changes.
*/
addBrowserSharingListener: function(listener) {
if (!this._tabChangeListeners) {
this._tabChangeListeners = new Set();
gBrowser.addEventListener("select", this);
}
this._tabChangeListeners.add(listener);
// Get the first window Id for the listener.
listener(null, gBrowser.selectedTab.linkedBrowser.outerWindowID);
},
/**
* Removes a listener from browser sharing.
*
* @param {Function} listener The listener to remove from the list.
*/
removeBrowserSharingListener: function(listener) {
if (!this._tabChangeListeners) {
return;
}
if (this._tabChangeListeners.has(listener)) {
this._tabChangeListeners.delete(listener);
}
if (!this._tabChangeListeners.size) {
gBrowser.removeEventListener("select", this);
delete this._tabChangeListeners;
}
},
/**
* Handles events from gBrowser.
*/
handleEvent: function(event) {
// We only should get "select" events.
if (event.type != "select") {
return;
}
// We've changed the tab, so get the new window id.
for (let listener of this._tabChangeListeners) {
listener(null, gBrowser.selectedTab.linkedBrowser.outerWindowID);
};
},
};
})();

View File

@ -461,11 +461,14 @@ function findChildShell(aDocument, aDocShell, aSoughtURI) {
var gPopupBlockerObserver = {
_reportButton: null,
onReportButtonClick: function (aEvent)
onReportButtonEvent: function (aEvent)
{
if (aEvent.button != 0 || aEvent.target != this._reportButton)
return;
if ((aEvent.type == "click" && aEvent.button != 0) ||
(aEvent.target != this._reportButton) ||
(aEvent.type == "keypress" && aEvent.charCode != Ci.nsIDOMKeyEvent.DOM_VK_SPACE &&
aEvent.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_RETURN)) {
return; // We're only interested in left click and space and enter keypresses
}
document.getElementById("blockedPopupOptions")
.openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
},

View File

@ -822,11 +822,12 @@
<label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
</box>
<hbox id="urlbar-icons">
<image id="page-report-button"
class="urlbar-icon"
hidden="true"
tooltiptext="&pageReportIcon.tooltip;"
onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
<toolbarbutton id="page-report-button"
class="tabbable urlbar-icon"
hidden="true"
tooltiptext="&pageReportIcon.tooltip;"
onclick="gPopupBlockerObserver.onReportButtonEvent(event);"
onkeypress="gPopupBlockerObserver.onReportButtonEvent(event);"/>
<toolbarbutton id="reader-mode-button"
class="tabbable"
hidden="true"

View File

@ -1204,14 +1204,10 @@
while (list.firstChild)
list.firstChild.remove();
let hiddenList;
try {
let pref =
Services.prefs.getCharPref("browser.search.hiddenOneOffs");
hiddenList = pref ? pref.split(",") : [];
} catch(e) {
hiddenList = [];
}
let Preferences =
Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
let pref = Preferences.get("browser.search.hiddenOneOffs");
let hiddenList = pref ? pref.split(",") : [];
let currentEngineName = Services.search.currentEngine.name;
let engines = Services.search.getVisibleEngines()

View File

@ -196,6 +196,7 @@ function injectLoopAPI(targetWindow) {
let contactsAPI;
let roomsAPI;
let callsAPI;
let savedWindowListeners = new Map();
let api = {
/**
@ -266,10 +267,21 @@ function injectLoopAPI(targetWindow) {
}
},
getActiveTabWindowId: {
/**
* Adds a listener to the most recent window for browser/tab sharing. The
* listener will be notified straight away of the current tab id, then every
* time there is a change of tab.
*
* Listener parameters:
* - {Object} err If there is a error this will be defined, null otherwise.
* - {Number} windowId The new windowId after a change of tab.
*
* @param {Function} listener The listener to handle the windowId changes.
*/
addBrowserSharingListener: {
enumerable: true,
writable: true,
value: function(callback) {
value: function(listener) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
let browser = win && win.gBrowser.selectedTab.linkedBrowser;
if (!win || !browser) {
@ -277,16 +289,39 @@ function injectLoopAPI(targetWindow) {
// window left.
let err = new Error("No tabs available to share.");
MozLoopService.log.error(err);
callback(cloneValueInto(err, targetWindow));
listener(cloneValueInto(err, targetWindow));
return;
}
win.LoopUI.addBrowserSharingListener(listener);
savedWindowListeners.set(listener, Cu.getWeakReference(win));
}
},
/**
* Removes a listener that was previously added.
*
* @param {Function} listener The listener to handle the windowId changes.
*/
removeBrowserSharingListener: {
enumerable: true,
writable: true,
value: function(listener) {
if (!savedWindowListeners.has(listener)) {
return;
}
let mm = browser.messageManager;
mm.addMessageListener("webrtc:response:StartBrowserSharing", function listener(message) {
mm.removeMessageListener("webrtc:response:StartBrowserSharing", listener);
callback(null, message.data.windowID);
});
mm.sendAsyncMessage("webrtc:StartBrowserSharing");
let win = savedWindowListeners.get(listener).get();
// Remove the element, regardless of if the window exists or not so
// that we clean the map.
savedWindowListeners.delete(listener);
if (!win) {
return;
}
win.LoopUI.removeBrowserSharingListener(listener);
}
},

View File

@ -395,6 +395,38 @@ loop.store.ActiveRoomStore = (function() {
this.setStoreState({receivingScreenShare: actionData.receiving});
},
/**
* Handles switching browser (aka tab) sharing to a new window. Should
* only be used for browser sharing.
*
* @param {Number} windowId The new windowId to start sharing.
*/
_handleSwitchBrowserShare: function(err, windowId) {
if (err) {
console.error("Error getting the windowId: " + err);
return;
}
var screenSharingState = this.getStoreState().screenSharingState;
if (screenSharingState === SCREEN_SHARE_STATES.INACTIVE) {
// Screen sharing is still pending, so assume that we need to kick it off.
var options = {
videoSource: "browser",
constraints: {
browserWindow: windowId,
scrollWithPage: true
},
};
this._sdkDriver.startScreenShare(options);
} else if (screenSharingState === SCREEN_SHARE_STATES.ACTIVE) {
// Just update the current share.
this._sdkDriver.switchAcquiredWindow(windowId);
} else {
console.error("Unexpectedly received windowId for browser sharing when pending");
}
},
/**
* Initiates a screen sharing publisher.
*
@ -409,19 +441,12 @@ loop.store.ActiveRoomStore = (function() {
videoSource: actionData.type
};
if (options.videoSource === "browser") {
this._mozLoop.getActiveTabWindowId(function(err, windowId) {
if (err || !windowId) {
this.dispatchAction(new sharedActions.ScreenSharingState({
state: SCREEN_SHARE_STATES.INACTIVE
}));
return;
}
options.constraints = {
browserWindow: windowId,
scrollWithPage: true
};
this._sdkDriver.startScreenShare(options);
}.bind(this));
this._browserSharingListener = this._handleSwitchBrowserShare.bind(this);
// Set up a listener for watching screen shares. This will get notified
// with the first windowId when it is added, so we start off the sharing
// from within the listener.
this._mozLoop.addBrowserSharingListener(this._browserSharingListener);
} else {
this._sdkDriver.startScreenShare(options);
}
@ -431,6 +456,12 @@ loop.store.ActiveRoomStore = (function() {
* Ends an active screenshare session.
*/
endScreenShare: function() {
if (this._browserSharingListener) {
// Remove the browser sharing listener as we don't need it now.
this._mozLoop.removeBrowserSharingListener(this._browserSharingListener);
this._browserSharingListener = null;
}
if (this._sdkDriver.endScreenShare()) {
this.dispatchAction(new sharedActions.ScreenSharingState({
state: SCREEN_SHARE_STATES.INACTIVE

View File

@ -100,6 +100,12 @@ loop.OTSdkDriver = (function() {
* @param {Object} options Hash containing options for the SDK
*/
startScreenShare: function(options) {
// For browser sharing, we store the window Id so that we can avoid unnecessary
// re-triggers.
if (options.videoSource === "browser") {
this._windowId = options.constraints.browserWindow;
}
var config = _.extend(this._getCopyPublisherConfig(), options);
this.screenshare = this.sdk.initPublisher(this.getScreenShareElementFunc(),
@ -108,6 +114,20 @@ loop.OTSdkDriver = (function() {
this.screenshare.on("accessDenied", this._onScreenShareDenied.bind(this));
},
/**
* Initiates switching the browser window that is being shared.
*
* @param {Integer} windowId The windowId of the browser.
*/
switchAcquiredWindow: function(windowId) {
if (windowId === this._windowId) {
return;
}
this._windowId = windowId;
this.screenshare._.switchAcquiredWindow(windowId);
},
/**
* Ends an active screenshare session. Return `true` when an active screen-
* sharing session was ended or `false` when no session is active.
@ -123,6 +143,7 @@ loop.OTSdkDriver = (function() {
this.screenshare.off("accessAllowed accessDenied");
this.screenshare.destroy();
delete this.screenshare;
delete this._windowId;
return true;
},

View File

@ -19,8 +19,9 @@ skip-if = e10s
[browser_mozLoop_prefs.js]
[browser_mozLoop_doNotDisturb.js]
skip-if = buildapp == 'mulet'
[browser_toolbarbutton.js]
[browser_mozLoop_pluralStrings.js]
[browser_mozLoop_tabSharing.js]
[browser_mozLoop_sharingListeners.js]
skip-if = e10s
[browser_mozLoop_telemetry.js]
skip-if = e10s
[browser_toolbarbutton.js]

View File

@ -0,0 +1,125 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* This file contains tests for the window.LoopUI active tab trackers.
*/
"use strict";
const {injectLoopAPI} = Cu.import("resource:///modules/loop/MozLoopAPI.jsm");
gMozLoopAPI = injectLoopAPI({});
let handlers = [
{
resolve: null,
windowId: null,
listener: function(err, windowId) {
handlers[0].windowId = windowId;
handlers[0].resolve();
}
},
{
resolve: null,
windowId: null,
listener: function(err, windowId) {
handlers[1].windowId = windowId;
handlers[1].resolve();
}
}
];
function promiseWindowIdReceivedOnAdd(handler) {
return new Promise(resolve => {
handler.resolve = resolve;
gMozLoopAPI.addBrowserSharingListener(handler.listener);
});
};
let createdTabs = [];
function promiseWindowIdReceivedNewTab(handlers) {
let promiseHandlers = [];
handlers.forEach(handler => {
promiseHandlers.push(new Promise(resolve => {
handler.resolve = resolve;
}));
});
let createdTab = gBrowser.selectedTab = gBrowser.addTab();
createdTabs.push(createdTab);
promiseHandlers.push(promiseTabLoadEvent(createdTab, "about:mozilla"));
return Promise.all(promiseHandlers);
};
function removeTabs() {
for (let createdTab of createdTabs) {
gBrowser.removeTab(createdTab);
}
createdTabs = [];
}
add_task(function* test_singleListener() {
yield promiseWindowIdReceivedOnAdd(handlers[0]);
let initialWindowId = handlers[0].windowId;
Assert.notEqual(initialWindowId, null, "window id should be valid");
// Check that a new tab updates the window id.
yield promiseWindowIdReceivedNewTab([handlers[0]]);
let newWindowId = handlers[0].windowId;
Assert.notEqual(initialWindowId, newWindowId, "Tab contentWindow IDs shouldn't be the same");
// Now remove the listener.
gMozLoopAPI.removeBrowserSharingListener(handlers[0].listener);
removeTabs();
});
add_task(function* test_multipleListener() {
yield promiseWindowIdReceivedOnAdd(handlers[0]);
let initialWindowId0 = handlers[0].windowId;
Assert.notEqual(initialWindowId0, null, "window id should be valid");
yield promiseWindowIdReceivedOnAdd(handlers[1]);
let initialWindowId1 = handlers[1].windowId;
Assert.notEqual(initialWindowId1, null, "window id should be valid");
Assert.equal(initialWindowId0, initialWindowId1, "window ids should be the same");
// Check that a new tab updates the window id.
yield promiseWindowIdReceivedNewTab(handlers);
let newWindowId0 = handlers[0].windowId;
let newWindowId1 = handlers[1].windowId;
Assert.equal(newWindowId0, newWindowId1, "Listeners should have the same windowId");
Assert.notEqual(initialWindowId0, newWindowId0, "Tab contentWindow IDs shouldn't be the same");
// Now remove the first listener.
gMozLoopAPI.removeBrowserSharingListener(handlers[0].listener);
// Check that a new tab updates the window id.
yield promiseWindowIdReceivedNewTab([handlers[1]]);
let nextWindowId0 = handlers[0].windowId;
let nextWindowId1 = handlers[1].windowId;
Assert.equal(newWindowId0, nextWindowId0, "First listener shouldn't have updated");
Assert.notEqual(newWindowId1, nextWindowId1, "Second listener should have updated");
// Cleanup.
gMozLoopAPI.removeBrowserSharingListener(handlers[1].listener);
removeTabs();
});

View File

@ -1,41 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This is an integration test to make sure that passing window IDs is working as
* expected, with or without e10s enabled - rather than just testing MozLoopAPI
* alone.
*/
const {injectLoopAPI} = Cu.import("resource:///modules/loop/MozLoopAPI.jsm");
gMozLoopAPI = injectLoopAPI({});
let promiseTabWindowId = function() {
return new Promise(resolve => {
gMozLoopAPI.getActiveTabWindowId((err, windowId) => {
Assert.equal(null, err, "No error should've occurred.");
Assert.equal(typeof windowId, "number", "We should have a window ID");
resolve(windowId);
});
});
};
add_task(function* test_windowIdFetch_simple() {
Assert.ok(gMozLoopAPI, "mozLoop should exist");
yield promiseTabWindowId();
});
add_task(function* test_windowIdFetch_multipleTabs() {
let previousTab = gBrowser.selectedTab;
let previousTabId = yield promiseTabWindowId();
let tab = gBrowser.selectedTab = gBrowser.addTab();
yield promiseTabLoadEvent(tab, "about:mozilla");
let tabId = yield promiseTabWindowId();
Assert.ok(tabId !== previousTabId, "Tab contentWindow IDs shouldn't be the same");
gBrowser.removeTab(tab);
tabId = yield promiseTabWindowId();
Assert.equal(previousTabId, tabId, "Window IDs should be back to what they were");
});

View File

@ -21,8 +21,10 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(dispatcher, "dispatch");
fakeMozLoop = {
setLoopPref: sandbox.stub(),
addConversationContext: sandbox.stub(),
setLoopPref: sinon.stub(),
addConversationContext: sinon.stub(),
addBrowserSharingListener: sinon.stub(),
removeBrowserSharingListener: sinon.stub(),
rooms: {
get: sinon.stub(),
join: sinon.stub(),
@ -36,11 +38,12 @@ describe("loop.store.ActiveRoomStore", function () {
};
fakeSdkDriver = {
connectSession: sandbox.stub(),
disconnectSession: sandbox.stub(),
forceDisconnectAll: sandbox.stub().callsArg(0),
startScreenShare: sandbox.stub(),
endScreenShare: sandbox.stub().returns(true)
connectSession: sinon.stub(),
disconnectSession: sinon.stub(),
forceDisconnectAll: sinon.stub().callsArg(0),
startScreenShare: sinon.stub(),
switchAcquiredWindow: sinon.stub(),
endScreenShare: sinon.stub().returns(true)
};
fakeMultiplexGum = {
@ -717,12 +720,20 @@ describe("loop.store.ActiveRoomStore", function () {
});
});
it("should invoke the SDK driver with the correct options for tab sharing", function() {
it("should add a browser sharing listener for tab sharing", function() {
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
sinon.assert.calledOnce(fakeMozLoop.getActiveTabWindowId);
sinon.assert.calledOnce(fakeMozLoop.addBrowserSharingListener);
});
it("should invoke the SDK driver with the correct options for tab sharing", function() {
fakeMozLoop.addBrowserSharingListener.callsArgWith(0, null, 42);
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
@ -732,7 +743,31 @@ describe("loop.store.ActiveRoomStore", function () {
scrollWithPage: true
}
});
})
});
});
describe("Screen share Events", function() {
var listener;
beforeEach(function() {
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Listener is the first argument of the first call.
listener = fakeMozLoop.addBrowserSharingListener.args[0][0];
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.ACTIVE
});
});
it("should update the SDK driver when a new window id is received", function() {
listener(null, 72);
sinon.assert.calledOnce(fakeSdkDriver.switchAcquiredWindow);
sinon.assert.calledWithExactly(fakeSdkDriver.switchAcquiredWindow, 72);
});
});
describe("#endScreenShare", function() {
@ -745,6 +780,18 @@ describe("loop.store.ActiveRoomStore", function () {
state: SCREEN_SHARE_STATES.INACTIVE
}));
});
it("should remove the sharing listener", function() {
// Setup the listener.
store.startScreenShare(new sharedActions.StartScreenShare({
type: "browser"
}));
// Now stop the screen share.
store.endScreenShare();
sinon.assert.calledOnce(fakeMozLoop.removeBrowserSharingListener);
});
});
describe("#remotePeerConnected", function() {

View File

@ -46,7 +46,10 @@ describe("loop.OTSdkDriver", function () {
publisher = _.extend({
destroy: sinon.stub(),
publishAudio: sinon.stub(),
publishVideo: sinon.stub()
publishVideo: sinon.stub(),
_: {
switchAcquiredWindow: sinon.stub()
}
}, Backbone.Events);
sdk = {
@ -143,8 +146,10 @@ describe("loop.OTSdkDriver", function () {
// has multiple options.
var options = {
videoSource: "browser",
browserWindow: 42,
scrollWithPage: true
constraints: {
browserWindow: 42,
scrollWithPage: true
}
};
driver.startScreenShare(options);
@ -153,6 +158,37 @@ describe("loop.OTSdkDriver", function () {
});
});
describe("#switchAcquiredWindow", function() {
beforeEach(function() {
var options = {
videoSource: "browser",
constraints: {
browserWindow: 42,
scrollWithPage: true
}
};
driver.getScreenShareElementFunc = function() {
return fakeScreenElement;
};
sandbox.stub(dispatcher, "dispatch");
driver.startScreenShare(options);
});
it("should switch to the acquired window", function() {
driver.switchAcquiredWindow(72);
sinon.assert.calledOnce(publisher._.switchAcquiredWindow);
sinon.assert.calledWithExactly(publisher._.switchAcquiredWindow, 72);
});
it("should not switch if the window is the same as the currently selected one", function() {
driver.switchAcquiredWindow(42);
sinon.assert.notCalled(publisher._.switchAcquiredWindow);
});
});
describe("#endScreenShare", function() {
beforeEach(function() {
driver.getScreenShareElementFunc = function() {};

View File

@ -433,12 +433,13 @@ BrowserGlue.prototype = {
// an engine, and that newly added engines are visible.
if (data == "engine-added" || data == "engine-removed") {
let engineName = subject.QueryInterface(Ci.nsISearchEngine).name;
let hiddenPref =
Services.prefs.getCharPref("browser.search.hiddenOneOffs");
let hiddenEngines = hiddenPref ? hiddenPref.split(",") : [];
hiddenEngines = hiddenEngines.filter(x => x !== engineName);
Services.prefs.setCharPref("browser.search.hiddenOneOffs",
hiddenEngines.join(","));
let Preferences =
Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
let pref = Preferences.get("browser.search.hiddenOneOffs");
let hiddenList = pref ? pref.split(",") : [];
hiddenList = hiddenList.filter(x => x !== engineName);
Preferences.set("browser.search.hiddenOneOffs",
hiddenList.join(","));
}
if (data != "engine-default" && data != "engine-current") {

View File

@ -8,7 +8,7 @@
<!-- One off providers -->
<preference id="browser.search.hiddenOneOffs"
name="browser.search.hiddenOneOffs"
type="string"/>
type="unichar"/>
</preferences>

View File

@ -28,7 +28,7 @@
<!-- One off providers -->
<preference id="browser.search.hiddenOneOffs"
name="browser.search.hiddenOneOffs"
type="string"/>
type="unichar"/>
</preferences>

View File

@ -9,6 +9,7 @@ support-files =
test.html
testEngine.src
testEngine.xml
testEngine_diacritics.xml
testEngine_dupe.xml
testEngine_mozsearch.xml
webapi.html
@ -34,6 +35,7 @@ skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Err
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
[browser_healthreport.js]
[browser_hiddenOneOffs_cleanup.js]
[browser_hiddenOneOffs_diacritics.js]
[browser_private_search_perwindowpb.js]
skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
[browser_yahoo.js]

View File

@ -1,7 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const cachedPref = Services.prefs.getCharPref("browser.search.hiddenOneOffs");
const testPref = "Foo,FooDupe";
function promiseNewEngine(basename) {
@ -67,10 +66,34 @@ add_task(function* test_add() {
"Adding an engine does not remove engines from hidden list.");
});
add_task(function* test_diacritics() {
const diacritic_engine = "Foo \u2661";
let Preferences =
Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
Preferences.set("browser.search.hiddenOneOffs", diacritic_engine);
yield promiseNewEngine("testEngine_diacritics.xml");
let hiddenOneOffs =
Preferences.get("browser.search.hiddenOneOffs").split(",");
is(hiddenOneOffs.some(x => x == diacritic_engine), false,
"Observer cleans up added hidden engines that include a diacritic.");
Preferences.set("browser.search.hiddenOneOffs", diacritic_engine);
info("Removing testEngine_diacritics.xml");
Services.search.removeEngine(Services.search.getEngineByName(diacritic_engine));
hiddenOneOffs =
Preferences.get("browser.search.hiddenOneOffs").split(",");
is(hiddenOneOffs.some(x => x == diacritic_engine), false,
"Observer cleans up removed hidden engines that include a diacritic.");
});
registerCleanupFunction(() => {
info("Removing testEngine.xml");
Services.search.removeEngine(Services.search.getEngineByName("Foo"));
info("Removing testEngine_dupe.xml");
Services.search.removeEngine(Services.search.getEngineByName("FooDupe"));
Services.prefs.setCharPref("browser.search.hiddenOneOffs", cachedPref);
Services.prefs.clearUserPref("browser.search.hiddenOneOffs");
});

View File

@ -0,0 +1,74 @@
/* 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/. */
// Tests that keyboard navigation in the search panel works as designed.
const searchbar = document.getElementById("searchbar");
const textbox = searchbar._textbox;
const searchPopup = document.getElementById("PopupSearchAutoComplete");
const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid",
"searchbar-search-button");
const diacritic_engine = "Foo \u2661";
let Preferences =
Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
// Get an array of the one-off buttons.
function getOneOffs() {
let oneOffs = [];
let oneOff =
document.getAnonymousElementByAttribute(searchPopup, "anonid",
"search-panel-one-offs");
for (oneOff = oneOff.firstChild; oneOff; oneOff = oneOff.nextSibling) {
if (oneOff.classList.contains("dummy"))
break;
oneOffs.push(oneOff);
}
return oneOffs;
}
add_task(function* init() {
let currentEngine = Services.search.currentEngine;
yield promiseNewEngine("testEngine_diacritics.xml", {setAsCurrent: false});
registerCleanupFunction(() => {
Services.search.currentEngine = currentEngine;
Services.prefs.clearUserPref("browser.search.hiddenOneOffs");
});
});
add_task(function* test_hidden() {
Preferences.set("browser.search.hiddenOneOffs", diacritic_engine);
let promise = promiseEvent(searchPopup, "popupshown");
info("Opening search panel");
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
yield promise;
ok(!getOneOffs().some(x => x.getAttribute("tooltiptext") == diacritic_engine),
"Search engines with diacritics are hidden when added to hiddenOneOffs preference.");
promise = promiseEvent(searchPopup, "popuphidden");
info("Closing search panel");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield promise;
});
add_task(function* test_shown() {
Preferences.set("browser.search.hiddenOneOffs", "");
let promise = promiseEvent(searchPopup, "popupshown");
info("Opening search panel");
SimpleTest.executeSoon(() => {
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
});
yield promise;
ok(getOneOffs().some(x => x.getAttribute("tooltiptext") == diacritic_engine),
"Search engines with diacritics are shown when removed from hiddenOneOffs preference.");
promise = promiseEvent(searchPopup, "popuphidden");
searchPopup.hidePopup();
yield promise;
});

View File

@ -136,8 +136,11 @@ function* promiseOnLoad() {
});
}
function promiseNewEngine(basename) {
function promiseNewEngine(basename, options = {}) {
return new Promise((resolve, reject) => {
//Default the setAsCurrent option to true.
let setAsCurrent =
options.setAsCurrent == undefined ? true : options.setAsCurrent;
info("Waiting for engine to be added: " + basename);
Services.search.init({
onInitComplete: function() {
@ -146,9 +149,13 @@ function promiseNewEngine(basename) {
Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
onSuccess: function (engine) {
info("Search engine added: " + basename);
Services.search.currentEngine = engine;
if (setAsCurrent) {
Services.search.currentEngine = engine;
}
registerCleanupFunction(() => {
Services.search.currentEngine = current;
if (setAsCurrent) {
Services.search.currentEngine = current;
}
Services.search.removeEngine(engine);
info("Search engine removed: " + basename);
});

View File

@ -0,0 +1,12 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>Foo &#9825;</ShortName>
<Description>Engine whose ShortName contains non-BMP Unicode characters</Description>
<InputEncoding>utf-8</InputEncoding>
<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
<Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
<Param name="test" value="{searchTerms}"/>
</Url>
<moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
<moz:Alias>diacriticalias</moz:Alias>
</OpenSearchDescription>

View File

@ -1,6 +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/.
readerView.enter=Enter Reader View
readerView.exit=Exit Reader View

View File

@ -139,7 +139,6 @@
locale/browser/preferences/tabs.dtd (%chrome/browser/preferences/tabs.dtd)
locale/browser/preferences/search.dtd (%chrome/browser/preferences/search.dtd)
locale/browser/preferences/translation.dtd (%chrome/browser/preferences/translation.dtd)
locale/browser/readerMode.properties (%chrome/browser/readerMode.properties)
#ifdef MOZ_SERVICES_SYNC
locale/browser/syncBrand.dtd (%chrome/browser/syncBrand.dtd)
locale/browser/syncSetup.dtd (%chrome/browser/syncSetup.dtd)

View File

@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
const gStringBundle = Services.strings.createBundle("chrome://browser/locale/readerMode.properties");
const gStringBundle = Services.strings.createBundle("chrome://global/locale/aboutReader.properties");
let ReaderParent = {
@ -107,7 +107,7 @@ let ReaderParent = {
if (browser.currentURI.spec.startsWith("about:reader")) {
button.setAttribute("readeractive", true);
button.hidden = false;
button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerView.exit"));
button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerView.close"));
} else {
button.removeAttribute("readeractive");
button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerView.enter"));

View File

@ -1616,9 +1616,15 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* Popup blocker button */
#page-report-button {
-moz-appearance: none;
padding: 0;
list-style-image: url("chrome://browser/skin/Info.png");
}
#page-report-button:focus {
outline: 1px dotted;
}
/* Reader mode button */
#reader-mode-button {

View File

@ -2501,10 +2501,21 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
/* POPUP BLOCKER BUTTON */
#page-report-button {
-moz-appearance: none;
padding: 0;
border-width: 0;
list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#page-report-button > .toolbarbutton-icon {
width: 16px;
}
#page-report-button:focus {
@hudButtonFocused@
}
#page-report-button:hover:active,
#page-report-button[open="true"] {
-moz-image-region: rect(0, 32px, 16px, 16px);
@ -2514,7 +2525,6 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
#page-report-button {
list-style-image: url("chrome://browser/skin/urlbar-popup-blocked@2x.png");
-moz-image-region: rect(0, 32px, 32px, 0);
width: 22px;
}
#page-report-button:hover:active,

View File

@ -1563,10 +1563,14 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* popup blocker button */
#page-report-button {
-moz-appearance: none;
padding: 0;
border-width: 0;
list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#page-report-button:focus,
#page-report-button:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}

View File

@ -0,0 +1,178 @@
.. _mozbuild_files_metadata:
==============
Files Metadata
==============
:ref:`mozbuild-files` provide a mechanism for attaching metadata to
files. Essentially, you define some flags to set on a file or file
pattern. Later, some tool or process queries for metadata attached to a
file of interest and it does something intelligent with that data.
Defining Metadata
=================
Files metadata is defined by using the
:ref:`Files Sub-Context <mozbuild_subcontext_Files>` in ``moz.build``
files. e.g.::
with Files('**/Makefile.in'):
BUG_COMPONENT = ('Core', 'Build Config')
This working example says, *for all Makefile.in files in all directories
in this one and underneath it, set the Bugzilla component to
Core :: Build Config*.
For more info, read the
:ref:`docs on Files <mozbuild_subcontext_Files>`.
How Metadata is Read
====================
``Files`` metadata is extracted in :ref:`mozbuild_fs_reading_mode`.
Reading starts by specifying a set of files whose metadata you are
interested in. For each file, the filesystem is walked to the root
of the source directory. Any ``moz.build`` encountered during this
walking are marked as relevant to the file.
Let's say you have the following filesystem content::
/moz.build
/root_file
/dir1/moz.build
/dir1/foo
/dir1/subdir1/foo
/dir2/foo
For ``/root_file``, the relevant ``moz.build`` files are just
``/moz.build``.
For ``/dir1/foo`` and ``/dir1/subdir1/foo``, the relevant files are
``/moz.build`` and ``/dir1/moz.build``.
For ``/dir2``, the relevant file is just ``/moz.build``.
Once the list of relevant ``moz.build`` files is obtained, each
``moz.build`` file is evaluated. Root ``moz.build`` file first,
leaf-most files last. This follows the rules of
:ref:`mozbuild_fs_reading_mode`, with the set of evaluated ``moz.build``
files being controlled by filesystem content, not ``DIRS`` variables.
The file whose metadata is being resolved maps to a set of ``moz.build``
files which in turn evaluates to a list of contexts. For file metadata,
we only care about one of these contexts:
:ref:`Files <mozbuild_subcontext_Files>`.
We start with an empty ``Files`` instance to represent the file. As
we encounter a *files sub-context*, we see if it is appropriate to
this file. If it is, we apply its values. This process is repeated
until all *files sub-contexts* have been applied or skipped. The final
state of the ``Files`` instance is used to represent the metadata for
this particular file.
It may help to visualize this. Say we have 2 ``moz.build`` files::
# /moz.build
with Files('*.cpp'):
BUG_COMPONENT = ('Core', 'XPCOM')
with Files('**/*.js'):
BUG_COMPONENT = ('Firefox', 'General')
# /foo/moz.build
with Files('*.js'):
BUG_COMPONENT = ('Another', 'Component')
Querying for metadata for the file ``/foo/test.js`` will reveal 3
relevant ``Files`` sub-contexts. They are evaluated as follows:
1. ``/moz.build - Files('*.cpp')``. Does ``/*.cpp`` match
``/foo/test.js``? **No**. Ignore this context.
2. ``/moz.build - Files('**/*.js')``. Does ``/**/*.js`` match
``/foo/test.js``? **Yes**. Apply ``BUG_COMPONENT = ('Firefox', 'General')``
to us.
3. ``/foo/moz.build - Files('*.js')``. Does ``/foo/*.js`` match
``/foo/test.js``? **Yes**. Apply
``BUG_COMPONENT = ('Another', 'Component')``.
At the end of execution, we have
``BUG_COMPONENT = ('Another', 'Component')`` as the metadata for
``/foo/test.js``.
One way to look at file metadata is as a stack of data structures.
Each ``Files`` sub-context relevant to a given file is applied on top
of the previous state, starting from an empty state. The final state
wins.
.. _mozbuild_files_metadata_finalizing:
Finalizing Values
=================
The default behavior of ``Files`` sub-context evaluation is to apply new
values on top of old. In most circumstances, this results in desired
behavior. However, there are circumstances where this may not be
desired. There is thus a mechanism to *finalize* or *freeze* values.
Finalizing values is useful for scenarios where you want to prevent
wildcard matches from overwriting previously-set values. This is useful
for one-off files.
Let's take ``Makefile.in`` files as an example. The build system module
policy dictates that ``Makefile.in`` files are part of the ``Build
Config`` module and should be reviewed by peers of that module. However,
there exist ``Makefile.in`` files in many directories in the source
tree. Without finalization, a ``*`` or ``**`` wildcard matching rule
would match ``Makefile.in`` files and overwrite their metadata.
Finalizing of values is performed by setting the ``FINAL`` variable
on ``Files`` sub-contexts. See the
:ref:`Files documentation <mozbuild_subcontext_Files>` for more.
Here is an example with ``Makefile.in`` files, showing how it is
possible to finalize the ``BUG_COMPONENT`` value.::
# /moz.build
with Files('**/Makefile.in'):
BUG_COMPONENT = ('Core', 'Build Config')
FINAL = True
# /foo/moz.build
with Files('**'):
BUG_COMPONENT = ('Another', 'Component')
If we query for metadata of ``/foo/Makefile.in``, both ``Files``
sub-contexts match the file pattern. However, since ``BUG_COMPONENT`` is
marked as finalized by ``/moz.build``, the assignment from
``/foo/moz.build`` is ignored. The final value for ``BUG_COMPONENT``
is ``('Core', 'Build Config')``.
Here is another example::
with Files('*.cpp'):
BUG_COMPONENT = ('One-Off', 'For C++')
FINAL = True
with Files('**'):
BUG_COMPONENT = ('Regular', 'Component')
For every files except ``foo.cpp``, the bug component will be resolved
as ``Regular :: Component``. However, ``foo.cpp`` has its value of
``One-Off :: For C++`` preserved because it is finalized.
.. important::
``FINAL`` only applied to variables defined in a context.
If you want to mark one variable as finalized but want to leave
another mutable, you'll need to use 2 ``Files`` contexts.
Guidelines for Defining Metadata
================================
In general, values defined towards the root of the source tree are
generic and become more specific towards the leaves. For example,
the ``BUG_COMPONENT`` for ``/browser`` might be ``Firefox :: General``
whereas ``/browser/components/preferences`` would list
``Firefox :: Preferences``.

View File

@ -13,6 +13,7 @@ Important Concepts
Mozconfig Files <mozconfigs>
mozbuild-files
mozbuild-symbols
files-metadata
Profile Guided Optimization <pgo>
slow
environment-variables

View File

@ -30,6 +30,8 @@ The following properties make execution of ``moz.build`` files special:
1. The execution environment exposes a limited subset of Python.
2. There is a special set of global symbols and an enforced naming
convention of symbols.
3. Some symbols are inherited from previously-executed ``moz.build``
files.
The limited subset of Python is actually an extremely limited subset.
Only a few symbols from ``__builtins__`` are exposed. These include
@ -68,16 +70,77 @@ sneak into the sandbox without being explicitly defined and documented.
Reading and Traversing moz.build Files
======================================
The process responsible for reading ``moz.build`` files simply starts at
a root ``moz.build`` file, processes it, emits the globals namespace to
a consumer, and then proceeds to process additional referenced
``moz.build`` files from the original file. The consumer then examines
the globals/``UPPERCASE`` variables set as part of execution and then
converts the data therein to Python class instances.
The process for reading ``moz.build`` files roughly consists of:
The executed Python sandbox is essentially represented as a dictionary
of all the special ``UPPERCASE`` variables populated during its
execution.
1. Start at the root ``moz.build`` (``<topsrcdir>/moz.build``).
2. Evaluate the ``moz.build`` file in a new sandbox.
3. Emit the main *context* and any *sub-contexts* from the executed
sandbox.
4. Extract a set of ``moz.build`` files to execute next.
5. For each additional ``moz.build`` file, goto #2 and repeat until all
referenced files have executed.
From the perspective of the consumer, the output of reading is a stream
of :py:class:`mozbuild.frontend.reader.context.Context` instances. Each
``Context`` defines a particular aspect of data. Consumers iterate over
these objects and do something with the data inside. Each object is
essentially a dictionary of all the ``UPPERCASE`` variables populated
during its execution.
.. note::
Historically, there was only one ``context`` per ``moz.build`` file.
As the number of things tracked by ``moz.build`` files grew and more
and more complex processing was desired, it was necessary to split these
contexts into multiple logical parts. It is now common to emit
multiple contexts per ``moz.build`` file.
Build System Reading Mode
-------------------------
The traditional mode of evaluation of ``moz.build`` files is what's
called *build system traversal mode.* In this mode, the ``CONFIG``
variable in each ``moz.build`` sandbox is populated from data coming
from ``config.status``, which is produced by ``configure``.
During evaluation, ``moz.build`` files often make decisions conditional
on the state of the build configuration. e.g. *only compile foo.cpp if
feature X is enabled*.
In this mode, traversal of ``moz.build`` files is governed by variables
like ``DIRS`` and ``TEST_DIRS``. For example, to execute a child
directory, ``foo``, you would add ``DIRS += ['foo']`` to a ``moz.build``
file and ``foo/moz.build`` would be evaluated.
.. _mozbuild_fs_reading_mode:
Filesystem Reading Mode
-----------------------
There is an alternative reading mode that doesn't involve the build
system and doesn't use ``DIRS`` variables to control traversal into
child directories. This mode is called *filesystem reading mode*.
In this reading mode, the ``CONFIG`` variable is a dummy, mostly empty
object. Accessing all but a few special variables will return an empty
value. This means that nearly all ``if CONFIG['FOO']:`` branches will
not be taken.
Instead of using content from within the evaluated ``moz.build``
file to drive traversal into subsequent ``moz.build`` files, the set
of files to evaluate is controlled by the thing doing the reading.
A single ``moz.build`` file is not guaranteed to be executable in
isolation. Instead, we must evaluate all *parent* ``moz.build`` files
first. For example, in order to evaluate ``/foo/moz.build``, one must
execute ``/moz.build`` and have its state influence the execution of
``/foo/moz.build``.
Filesystem reading mode is utilized to power the
:ref:`mozbuild_files_metadata` feature.
Technical Details
-----------------
The code for reading ``moz.build`` files lives in
:py:mod:`mozbuild.frontend.reader`. The Python sandboxes evaluation results
@ -100,9 +163,6 @@ verification step. There are multiple downstream consumers of the
``moz.build``-derived data and many will perform the same actions. This
logic can be complicated, so we have a component dedicated to it.
Other Notes
===========
:py:class:`mozbuild.frontend.reader.BuildReader`` and
:py:class:`mozbuild.frontend.reader.TreeMetadataEmitter`` have a
stream-based API courtesy of generators. When you hook them up properly,

View File

@ -95,7 +95,7 @@ flavors = {
'NetBSD': 'netbsd',
'OpenBSD': 'openbsd',
}
gyp_vars['OS'] = flavors[os]
gyp_vars['OS'] = flavors.get(os)
arches = {
'x86_64': 'x64',

View File

@ -4,6 +4,9 @@
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'Build Config')
# This cannot be named "build" because of bug 922191.
SPHINX_TREES['buildsystem'] = 'docs'

View File

@ -4,6 +4,9 @@
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'Build Config')
NO_DIST_INSTALL = True
# For sanity's sake, we compile nsinstall without the wrapped system
# headers, so that we can use it to set up the wrapped system headers.
@ -30,6 +33,7 @@ if CONFIG['MOZ_SHARED_ICU']:
DEFINES['MOZ_SHARED_ICU'] = True
PYTHON_UNIT_TESTS += [
'tests/test_mozbuild_reading.py',
'tests/unit-expandlibs.py',
'tests/unit-mozunit.py',
'tests/unit-nsinstall.py',

View File

@ -0,0 +1,70 @@
# 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/.
from __future__ import unicode_literals
import os
import unittest
from mozunit import main
from mozbuild.base import MozbuildObject
from mozbuild.frontend.reader import (
BuildReader,
EmptyConfig,
)
class TestMozbuildReading(unittest.TestCase):
# This hack is needed to appease running in automation.
def setUp(self):
self._old_env = dict(os.environ)
os.environ.pop('MOZCONFIG', None)
os.environ.pop('MOZ_OBJDIR', None)
def tearDown(self):
os.environ.clear()
os.environ.update(self._old_env)
def _mozbuilds(self, reader):
if not hasattr(self, '_mozbuild_paths'):
self._mozbuild_paths = set(reader.all_mozbuild_paths())
return self._mozbuild_paths
@unittest.skip('failing in SpiderMonkey builds')
def test_filesystem_traversal_reading(self):
"""Reading moz.build according to filesystem traversal works.
We attempt to read every known moz.build file via filesystem traversal.
If this test fails, it means that metadata extraction will fail.
"""
mb = MozbuildObject.from_environment(detect_virtualenv_mozinfo=False)
config = mb.config_environment
reader = BuildReader(config)
all_paths = self._mozbuilds(reader)
paths, contexts = reader.read_relevant_mozbuilds(all_paths)
self.assertEqual(set(paths), all_paths)
self.assertGreaterEqual(len(contexts), len(paths))
def test_filesystem_traversal_no_config(self):
"""Reading moz.build files via filesystem traversal mode with no build config.
This is similar to the above test except no build config is applied.
This will likely fail in more scenarios than the above test because a
lot of moz.build files assumes certain variables are present.
"""
here = os.path.abspath(os.path.dirname(__file__))
root = os.path.normpath(os.path.join(here, '..', '..'))
config = EmptyConfig(root)
reader = BuildReader(config)
all_paths = self._mozbuilds(reader)
paths, contexts = reader.read_relevant_mozbuilds(all_paths)
self.assertEqual(set(paths.keys()), all_paths)
self.assertGreaterEqual(len(contexts), len(paths))
if __name__ == '__main__':
main()

View File

@ -23,7 +23,7 @@ this.PHONE_NUMBER_META_DATA = {
"500": '["FK","00",,,,,"\\d{5}","[2-7]\\d{4}",]',
"261": '["MG","00","0",,,"$NP$FG","\\d{7,9}","[23]\\d{8}",[["([23]\\d)(\\d{2})(\\d{3})(\\d{2})","$1 $2 $3 $4",,,]]]',
"92": '["PK","00","0",,,"($NP$FG)","\\d{6,12}","1\\d{8}|[2-8]\\d{5,11}|9(?:[013-9]\\d{4,9}|2\\d(?:111\\d{6}|\\d{3,7}))",[["(\\d{2})(111)(\\d{3})(\\d{3})","$1 $2 $3 $4","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)1",,],["(\\d{3})(111)(\\d{3})(\\d{3})","$1 $2 $3 $4","2[349]|45|54|60|72|8[2-5]|9[2-9]",,],["(\\d{2})(\\d{7,8})","$1 $2","(?:2[125]|4[0-246-9]|5[1-35-7]|6[1-8]|7[14]|8[16]|91)[2-9]",,],["(\\d{3})(\\d{6,7})","$1 $2","2[349]|45|54|60|72|8[2-5]|9[2-9]",,],["(3\\d{2})(\\d{7})","$1 $2","3","$NP$FG",],["([15]\\d{3})(\\d{5,6})","$1 $2","58[12]|1",,],["(586\\d{2})(\\d{5})","$1 $2","586",,],["([89]00)(\\d{3})(\\d{2})","$1 $2 $3","[89]00","$NP$FG",]]]',
"234": '["NG","009","0",,,"$NP$FG","\\d{5,14}","[1-6]\\d{5,8}|9\\d{5,9}|[78]\\d{5,13}",[["([129])(\\d{3})(\\d{3,4})","$1 $2 $3","[129]",,],["(\\d{2})(\\d{3})(\\d{2,3})","$1 $2 $3","[3-6]|7(?:[1-79]|0[1-9])|8[2-9]",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","70|8[01]|90[239]",,],["([78]00)(\\d{4})(\\d{4,5})","$1 $2 $3","[78]00",,],["([78]00)(\\d{5})(\\d{5,6})","$1 $2 $3","[78]00",,],["(78)(\\d{2})(\\d{3})","$1 $2 $3","78",,]]]',
"234": '["NG","009","0",,,"$NP$FG","\\d{5,14}","[1-6]\\d{5,8}|9\\d{5,9}|[78]\\d{5,13}",[["([129])(\\d{3})(\\d{3,4})","$1 $2 $3","[129]",,],["(\\d{2})(\\d{3})(\\d{2,3})","$1 $2 $3","[3-6]|7(?:[1-79]|0[1-9])|8[2-9]",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","70|8[01]|90[2359]",,],["([78]00)(\\d{4})(\\d{4,5})","$1 $2 $3","[78]00",,],["([78]00)(\\d{5})(\\d{5,6})","$1 $2 $3","[78]00",,],["(78)(\\d{2})(\\d{3})","$1 $2 $3","78",,]]]',
"350": '["GI","00",,,,,"\\d{8}","[2568]\\d{7}",[["(\\d{3})(\\d{5})","$1 $2","2",,]]]',
"45": '["DK","00",,,,,"\\d{8}","[2-9]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
"963": '["SY","00","0",,,"$NP$FG","\\d{6,9}","[1-59]\\d{7,8}",[["(\\d{2})(\\d{3})(\\d{3,4})","$1 $2 $3","[1-5]",,],["(9\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","9",,]]]',
@ -31,7 +31,7 @@ this.PHONE_NUMBER_META_DATA = {
"974": '["QA","00",,,,,"\\d{7,8}","[2-8]\\d{6,7}",[["([28]\\d{2})(\\d{4})","$1 $2","[28]",,],["([3-7]\\d{3})(\\d{4})","$1 $2","[3-7]",,]]]',
"218": '["LY","00","0",,,"$NP$FG","\\d{7,9}","[25679]\\d{8}",[["([25679]\\d)(\\d{7})","$1-$2",,,]]]',
"51": '["PE","19(?:1[124]|77|90)00","0",,,"($NP$FG)","\\d{6,9}","[14-9]\\d{7,8}",[["(1)(\\d{7})","$1 $2","1",,],["([4-8]\\d)(\\d{6})","$1 $2","[4-7]|8[2-4]",,],["(\\d{3})(\\d{5})","$1 $2","80",,],["(9\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","9","$FG",]]]',
"62": '["ID","0(?:0[1789]|10(?:00|1[67]))","0",,,"$NP$FG","\\d{5,11}","[1-9]\\d{6,10}",[["(\\d{2})(\\d{5,8})","$1 $2","2[124]|[36]1","($NP$FG)",],["(\\d{3})(\\d{5,7})","$1 $2","[4579]|2[035-9]|[36][02-9]","($NP$FG)",],["(8\\d{2})(\\d{3,4})(\\d{3,4})","$1-$2-$3","8[1-35-9]",,],["(177)(\\d{6,8})","$1 $2","1",,],["(800)(\\d{5,7})","$1 $2","800",,],["(80\\d)(\\d)(\\d{3})(\\d{3})","$1 $2 $3 $4","80[79]",,]]]',
"62": '["ID","0(?:0[1789]|10(?:00|1[67]))","0",,,"$NP$FG","\\d{5,11}","[1-9]\\d{6,10}",[["(\\d{2})(\\d{5,8})","$1 $2","2[124]|[36]1","($NP$FG)",],["(\\d{3})(\\d{5,8})","$1 $2","[4579]|2[035-9]|[36][02-9]","($NP$FG)",],["(8\\d{2})(\\d{3,4})(\\d{3,4})","$1-$2-$3","8[1-35-9]",,],["(177)(\\d{6,8})","$1 $2","1",,],["(800)(\\d{5,7})","$1 $2","800",,],["(80\\d)(\\d)(\\d{3})(\\d{3})","$1 $2 $3 $4","80[79]",,]]]',
"298": '["FO","00",,"(10(?:01|[12]0|88))",,,"\\d{6}","[2-9]\\d{5}",[["(\\d{6})","$1",,,]]]',
"381": '["RS","00","0",,,"$NP$FG","\\d{5,12}","[126-9]\\d{4,11}|3(?:[0-79]\\d{3,10}|8[2-9]\\d{2,9})",[["([23]\\d{2})(\\d{4,9})","$1 $2","(?:2[389]|39)0",,],["([1-3]\\d)(\\d{5,10})","$1 $2","1|2(?:[0-24-7]|[389][1-9])|3(?:[0-8]|9[1-9])",,],["(6\\d)(\\d{6,8})","$1 $2","6",,],["([89]\\d{2})(\\d{3,9})","$1 $2","[89]",,],["(7[26])(\\d{4,9})","$1 $2","7[26]",,],["(7[08]\\d)(\\d{4,9})","$1 $2","7[08]",,]]]',
"975": '["BT","00",,,,,"\\d{6,8}","[1-8]\\d{6,7}",[["([17]7)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","1|77",,],["([2-8])(\\d{3})(\\d{3})","$1 $2 $3","[2-68]|7[246]",,]]]',
@ -52,7 +52,7 @@ this.PHONE_NUMBER_META_DATA = {
"692": '["MH","011","1",,,,"\\d{7}","[2-6]\\d{6}",[["(\\d{3})(\\d{4})","$1-$2",,,]]]',
"250": '["RW","00","0",,,,"\\d{8,9}","[027-9]\\d{7,8}",[["(2\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","2","$FG",],["([7-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","[7-9]","$NP$FG",],["(0\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","0",,]]]',
"81": '["JP","010","0",,,"$NP$FG","\\d{8,17}","[1-9]\\d{8,9}|00(?:[36]\\d{7,14}|7\\d{5,7}|8\\d{7})",[["(\\d{3})(\\d{3})(\\d{3})","$1-$2-$3","(?:12|57|99)0",,],["(\\d{3})(\\d{3})(\\d{4})","$1-$2-$3","800",,],["(\\d{4})(\\d{4})","$1-$2","0077","$FG","NA"],["(\\d{4})(\\d{2})(\\d{3,4})","$1-$2-$3","0077","$FG","NA"],["(\\d{4})(\\d{2})(\\d{4})","$1-$2-$3","0088","$FG","NA"],["(\\d{4})(\\d{3})(\\d{3,4})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\d{4})(\\d{4})(\\d{4,5})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\d{4})(\\d{5})(\\d{5,6})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\d{4})(\\d{6})(\\d{6,7})","$1-$2-$3","00(?:37|66)","$FG","NA"],["(\\d{2})(\\d{4})(\\d{4})","$1-$2-$3","[2579]0|80[1-9]",,],["(\\d{4})(\\d)(\\d{4})","$1-$2-$3","1(?:26|3[79]|4[56]|5[4-68]|6[3-5])|5(?:76|97)|499|746|8(?:3[89]|63|47|51)|9(?:49|80|9[16])",,],["(\\d{3})(\\d{2})(\\d{4})","$1-$2-$3","1(?:2[3-6]|3[3-9]|4[2-6]|5[2-8]|[68][2-7]|7[2-689]|9[1-578])|2(?:2[03-689]|3[3-58]|4[0-468]|5[04-8]|6[013-8]|7[06-9]|8[02-57-9]|9[13])|4(?:2[28]|3[689]|6[035-7]|7[05689]|80|9[3-5])|5(?:3[1-36-9]|4[4578]|5[013-8]|6[1-9]|7[2-8]|8[14-7]|9[4-9])|7(?:2[15]|3[5-9]|4[02-9]|6[135-8]|7[0-4689]|9[014-9])|8(?:2[49]|3[3-8]|4[5-8]|5[2-9]|6[35-9]|7[579]|8[03-579]|9[2-8])|9(?:[23]0|4[02-46-9]|5[024-79]|6[4-9]|7[2-47-9]|8[02-7]|9[3-7])",,],["(\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","1|2(?:2[37]|5[5-9]|64|78|8[39]|91)|4(?:2[2689]|64|7[347])|5(?:[2-589]|39)|60|8(?:[46-9]|3[279]|2[124589])|9(?:[235-8]|93)",,],["(\\d{3})(\\d{2})(\\d{4})","$1-$2-$3","2(?:9[14-79]|74|[34]7|[56]9)|82|993",,],["(\\d)(\\d{4})(\\d{4})","$1-$2-$3","3|4(?:2[09]|7[01])|6[1-9]",,],["(\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","[2479][1-9]",,]]]',
"237": '["CM","00",,,,,"\\d{8}","[2357-9]\\d{7}",[["([2357-9]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[23579]|88",,],["(800)(\\d{2})(\\d{3})","$1 $2 $3","80",,]]]',
"237": '["CM","00",,,,,"\\d{8,9}","[235-9]\\d{7,8}",[["([26])(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4 $5","[26]",,],["([2357-9]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[23579]|88",,],["(800)(\\d{2})(\\d{3})","$1 $2 $3","80",,]]]',
"351": '["PT","00",,,,,"\\d{9}","[2-46-9]\\d{8}",[["(2\\d)(\\d{3})(\\d{4})","$1 $2 $3","2[12]",,],["([2-46-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","2[3-9]|[346-9]",,]]]',
"246": '["IO","00",,,,,"\\d{7}","3\\d{6}",[["(\\d{3})(\\d{4})","$1 $2",,,]]]',
"227": '["NE","00",,,,,"\\d{8}","[0289]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[289]|09",,],["(08)(\\d{3})(\\d{3})","$1 $2 $3","08",,]]]',
@ -73,8 +73,8 @@ this.PHONE_NUMBER_META_DATA = {
"357": '["CY","00",,,,,"\\d{8}","[257-9]\\d{7}",[["(\\d{2})(\\d{6})","$1 $2",,,]]]',
"240": '["GQ","00",,,,,"\\d{9}","[23589]\\d{8}",[["(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3","[235]",,],["(\\d{3})(\\d{6})","$1 $2","[89]",,]]]',
"506": '["CR","00",,"(19(?:0[012468]|1[09]|20|66|77|99))",,,"\\d{8,10}","[24-9]\\d{7,9}",[["(\\d{4})(\\d{4})","$1 $2","[24-7]|8[3-9]",,],["(\\d{3})(\\d{3})(\\d{4})","$1-$2-$3","[89]0",,]]]',
"86": '["CN","(1[1279]\\d{3})?00","0","(1[1279]\\d{3})|0",,,"\\d{4,12}","[1-7]\\d{6,11}|8[0-357-9]\\d{6,9}|9\\d{7,9}",[["(80\\d{2})(\\d{4})","$1 $2","80[2678]","$NP$FG",],["([48]00)(\\d{3})(\\d{4})","$1 $2 $3","[48]00",,],["(\\d{5,6})","$1","100|95",,"NA"],["(\\d{2})(\\d{5,6})","$1 $2","(?:10|2\\d)[19]","$NP$FG",],["(\\d{3})(\\d{5,6})","$1 $2","[3-9]","$NP$FG",],["(\\d{3,4})(\\d{4})","$1 $2","[2-9]",,"NA"],["(21)(\\d{4})(\\d{4,6})","$1 $2 $3","21","$NP$FG",],["([12]\\d)(\\d{4})(\\d{4})","$1 $2 $3","10[1-9]|2[02-9]","$NP$FG",],["(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3","3(?:11|7[179])|4(?:[15]1|3[12])|5(?:1|2[37]|3[12]|51|7[13-79]|9[15])|7(?:31|5[457]|6[09]|91)|8(?:71|98)","$NP$FG",],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","3(?:1[02-9]|35|49|5|7[02-68]|9[1-68])|4(?:1[02-9]|2[179]|[35][2-9]|6[4789]|7\\d|8[23])|5(?:3[03-9]|4[36]|5[02-9]|6[1-46]|7[028]|80|9[2-46-9])|6(?:3[1-5]|6[0238]|9[12])|7(?:01|[1579]|2[248]|3[04-9]|4[3-6]|6[2368])|8(?:1[236-8]|2[5-7]|3|5[1-9]|7[02-9]|8[3678]|9[1-7])|9(?:0[1-3689]|1[1-79]|[379]|4[13]|5[1-5])","$NP$FG",],["(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3","1[3-578]",,],["(10800)(\\d{3})(\\d{4})","$1 $2 $3","108",,]]]',
"257": '["BI","00",,,,,"\\d{8}","[27]\\d{7}",[["([27]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
"86": '["CN","(1(?:[129]\\d{3}|79\\d{2}))?00","0","(1(?:[129]\\d{3}|79\\d{2}))|0",,,"\\d{4,12}","[1-7]\\d{6,11}|8[0-357-9]\\d{6,9}|9\\d{7,10}",[["(80\\d{2})(\\d{4})","$1 $2","80[2678]","$NP$FG",],["([48]00)(\\d{3})(\\d{4})","$1 $2 $3","[48]00",,],["(\\d{5,6})","$1","100|95",,"NA"],["(\\d{2})(\\d{5,6})","$1 $2","(?:10|2\\d)[19]","$NP$FG",],["(\\d{3})(\\d{5,6})","$1 $2","[3-9]","$NP$FG",],["(\\d{3,4})(\\d{4})","$1 $2","[2-9]",,"NA"],["(21)(\\d{4})(\\d{4,6})","$1 $2 $3","21","$NP$FG",],["([12]\\d)(\\d{4})(\\d{4})","$1 $2 $3","10[1-9]|2[02-9]","$NP$FG",],["(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3","3(?:11|7[179])|4(?:[15]1|3[12])|5(?:1|2[37]|3[12]|51|7[13-79]|9[15])|7(?:31|5[457]|6[09]|91)|8(?:[57]1|98)","$NP$FG",],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","3(?:1[02-9]|35|49|5|7[02-68]|9[1-68])|4(?:1[02-9]|2[179]|[35][2-9]|6[4789]|7\\d|8[23])|5(?:3[03-9]|4[36]|5[02-9]|6[1-46]|7[028]|80|9[2-46-9])|6(?:3[1-5]|6[0238]|9[12])|7(?:01|[1579]|2[248]|3[04-9]|4[3-6]|6[2368])|8(?:1[236-8]|2[5-7]|3|5[4-9]|7[02-9]|8[3678]|9[1-7])|9(?:0[1-3689]|1[1-79]|[379]|4[13]|5[1-5])","$NP$FG",],["(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3","1[3-578]",,],["(10800)(\\d{3})(\\d{4})","$1 $2 $3","108",,],["(\\d{3})(\\d{7,8})","$1 $2","950",,]]]',
"257": '["BI","00",,,,,"\\d{8}","[267]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
"683": '["NU","00",,,,,"\\d{4}","[1-5]\\d{3}",]',
"43": '["AT","00","0",,,"$NP$FG","\\d{3,13}","[1-9]\\d{3,12}",[["(1)(\\d{3,12})","$1 $2","1",,],["(5\\d)(\\d{3,5})","$1 $2","5[079]",,],["(5\\d)(\\d{3})(\\d{3,4})","$1 $2 $3","5[079]",,],["(5\\d)(\\d{4})(\\d{4,7})","$1 $2 $3","5[079]",,],["(\\d{3})(\\d{3,10})","$1 $2","316|46|51|732|6(?:44|5[0-3579]|[6-9])|7(?:1|[28]0)|[89]",,],["(\\d{4})(\\d{3,9})","$1 $2","2|3(?:1[1-578]|[3-8])|4[2378]|5[2-6]|6(?:[12]|4[1-35-9]|5[468])|7(?:2[1-8]|35|4[1-8]|[5-79])",,]]]',
"247": '["AC","00",,,,,"\\d{4,6}","[2-7]\\d{3,5}",]',
@ -84,7 +84,7 @@ this.PHONE_NUMBER_META_DATA = {
"236": '["CF","00",,,,,"\\d{8}","[278]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
"590": ['["GP","00","0",,,"$NP$FG","\\d{9}","[56]\\d{8}",[["([56]90)(\\d{2})(\\d{4})","$1 $2-$3",,,]]]','["BL","00","0",,,,"\\d{9}","[56]\\d{8}",]','["MF","00","0",,,,"\\d{9}","[56]\\d{8}",]'],
"53": '["CU","119","0",,,"($NP$FG)","\\d{4,8}","[2-57]\\d{5,7}",[["(\\d)(\\d{6,7})","$1 $2","7",,],["(\\d{2})(\\d{4,6})","$1 $2","[2-4]",,],["(\\d)(\\d{7})","$1 $2","5","$NP$FG",]]]',
"64": '["NZ","0(?:0|161)","0",,,"$NP$FG","\\d{7,11}","6[235-9]\\d{6}|[2-57-9]\\d{7,10}",[["([34679])(\\d{3})(\\d{4})","$1-$2 $3","[3467]|9[1-9]",,],["(24099)(\\d{3})","$1 $2","240",,],["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","21",,],["(\\d{2})(\\d{3})(\\d{3,5})","$1 $2 $3","2(?:1[1-9]|[69]|7[0-35-9])|86",,],["(2\\d)(\\d{3,4})(\\d{4})","$1 $2 $3","2[028]",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","2(?:10|74)|5|[89]0",,]]]',
"64": '["NZ","0(?:0|161)","0",,,"$NP$FG","\\d{7,11}","6[235-9]\\d{6}|[2-57-9]\\d{7,10}",[["([34679])(\\d{3})(\\d{4})","$1-$2 $3","[346]|7[2-57-9]|9[1-9]",,],["(24099)(\\d{3})","$1 $2","240",,],["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","21",,],["(\\d{2})(\\d{3})(\\d{3,5})","$1 $2 $3","2(?:1[1-9]|[69]|7[0-35-9])|70|86",,],["(2\\d)(\\d{3,4})(\\d{4})","$1 $2 $3","2[028]",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","2(?:10|74)|5|[89]0",,]]]',
"965": '["KW","00",,,,,"\\d{7,8}","[12569]\\d{6,7}",[["(\\d{4})(\\d{3,4})","$1 $2","[1269]",,],["(5[015]\\d)(\\d{5})","$1 $2","5",,]]]',
"224": '["GN","00",,,,,"\\d{8,9}","[367]\\d{7,8}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","3",,],["(\\d{3})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[67]",,]]]',
"973": '["BH","00",,,,,"\\d{8}","[136-9]\\d{7}",[["(\\d{4})(\\d{4})","$1 $2",,,]]]',
@ -105,7 +105,7 @@ this.PHONE_NUMBER_META_DATA = {
"256": '["UG","00[057]","0",,,"$NP$FG","\\d{5,9}","\\d{9}",[["(\\d{3})(\\d{6})","$1 $2","[7-9]|20(?:[013-8]|2[5-9])|4(?:6[45]|[7-9])",,],["(\\d{2})(\\d{7})","$1 $2","3|4(?:[1-5]|6[0-36-9])",,],["(2024)(\\d{5})","$1 $2","2024",,]]]',
"677": '["SB","0[01]",,,,,"\\d{5,7}","[1-9]\\d{4,6}",[["(\\d{2})(\\d{5})","$1 $2","[7-9]",,]]]',
"377": '["MC","00","0",,,"$NP$FG","\\d{8,9}","[4689]\\d{7,8}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","9","$FG",],["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","4",,],["(6)(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4 $5","6",,],["(\\d{3})(\\d{3})(\\d{2})","$1 $2 $3","8","$FG",]]]',
"382": '["ME","00","0",,,"$NP$FG","\\d{6,9}","[2-9]\\d{7,8}",[["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","[2-57-9]|6[3789]",,],["(67)(9)(\\d{3})(\\d{3})","$1 $2 $3 $4","679",,]]]',
"382": '["ME","00","0",,,"$NP$FG","\\d{6,9}","[2-9]\\d{7,8}",[["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","[2-57-9]|6[036-9]",,],["(67)(9)(\\d{3})(\\d{3})","$1 $2 $3 $4","679",,]]]',
"231": '["LR","00","0",,,"$NP$FG","\\d{7,9}","2\\d{7}|[37-9]\\d{8}|[45]\\d{6}",[["(2\\d)(\\d{3})(\\d{3})","$1 $2 $3","2",,],["([79]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","[79]",,],["([4-6])(\\d{3})(\\d{3})","$1 $2 $3","[4-6]",,],["(\\d{2})(\\d{3})(\\d{4})","$1 $2 $3","[38]",,]]]',
"591": '["BO","00(1\\d)?","0","0(1\\d)?",,,"\\d{7,8}","[23467]\\d{7}",[["([234])(\\d{7})","$1 $2","[234]",,],["([67]\\d{7})","$1","[67]",,]]]',
"808": '["001",,,,,,"\\d{8}","\\d{8}",[["(\\d{4})(\\d{4})","$1 $2",,,]]]',
@ -126,7 +126,7 @@ this.PHONE_NUMBER_META_DATA = {
"213": '["DZ","00","0",,,"$NP$FG","\\d{8,9}","(?:[1-4]|[5-9]\\d)\\d{7}",[["([1-4]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[1-4]",,],["([5-8]\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[5-8]",,],["(9\\d)(\\d{3})(\\d{2})(\\d{2})","$1 $2 $3 $4","9",,]]]',
"371": '["LV","00",,,,,"\\d{8}","[2689]\\d{7}",[["([2689]\\d)(\\d{3})(\\d{3})","$1 $2 $3",,,]]]',
"503": '["SV","00",,,,,"\\d{7,8}|\\d{11}","[267]\\d{7}|[89]\\d{6}(?:\\d{4})?",[["(\\d{4})(\\d{4})","$1 $2","[267]",,],["(\\d{3})(\\d{4})","$1 $2","[89]",,],["(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3","[89]",,]]]',
"685": '["WS","0",,,,,"\\d{5,7}","[2-8]\\d{4,6}",[["(8\\d{2})(\\d{3,4})","$1 $2","8",,],["(7\\d)(\\d{5})","$1 $2","7",,]]]',
"685": '["WS","0",,,,,"\\d{5,7}","[2-8]\\d{4,6}",[["(8\\d{2})(\\d{3,4})","$1 $2","8",,],["(7\\d)(\\d{5})","$1 $2","7",,],["(\\d{5})","$1","[2-6]",,]]]',
"880": '["BD","00[12]?","0",,,"$NP$FG","\\d{6,10}","[2-79]\\d{5,9}|1\\d{9}|8[0-7]\\d{4,8}",[["(2)(\\d{7})","$1-$2","2",,],["(\\d{2})(\\d{4,6})","$1-$2","[3-79]1",,],["(\\d{4})(\\d{3,6})","$1-$2","1|3(?:0|[2-58]2)|4(?:0|[25]2|3[23]|[4689][25])|5(?:[02-578]2|6[25])|6(?:[0347-9]2|[26][25])|7[02-9]2|8(?:[023][23]|[4-7]2)|9(?:[02][23]|[458]2|6[016])",,],["(\\d{3})(\\d{3,7})","$1-$2","[3-79][2-9]|8",,]]]',
"265": '["MW","00","0",,,"$NP$FG","\\d{7,9}","(?:1(?:\\d{2})?|[2789]\\d{2})\\d{6}",[["(\\d)(\\d{3})(\\d{3})","$1 $2 $3","1",,],["(2\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","2",,],["(\\d{3})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[1789]",,]]]',
"65": '["SG","0[0-3]\\d",,,,,"\\d{8,11}","[36]\\d{7}|[17-9]\\d{7,10}",[["([3689]\\d{3})(\\d{4})","$1 $2","[369]|8[1-9]",,],["(1[89]00)(\\d{3})(\\d{4})","$1 $2 $3","1[89]",,],["(7000)(\\d{4})(\\d{3})","$1 $2 $3","70",,],["(800)(\\d{3})(\\d{4})","$1 $2 $3","80",,]]]',
@ -144,7 +144,7 @@ this.PHONE_NUMBER_META_DATA = {
"353": '["IE","00","0",,,"($NP$FG)","\\d{5,10}","[124-9]\\d{6,9}",[["(1)(\\d{3,4})(\\d{4})","$1 $2 $3","1",,],["(\\d{2})(\\d{5})","$1 $2","2[24-9]|47|58|6[237-9]|9[35-9]",,],["(\\d{3})(\\d{5})","$1 $2","40[24]|50[45]",,],["(48)(\\d{4})(\\d{4})","$1 $2 $3","48",,],["(818)(\\d{3})(\\d{3})","$1 $2 $3","81",,],["(\\d{2})(\\d{3})(\\d{3,4})","$1 $2 $3","[24-69]|7[14]",,],["([78]\\d)(\\d{3,4})(\\d{4})","$1 $2 $3","76|8[35-9]","$NP$FG",],["(700)(\\d{3})(\\d{3})","$1 $2 $3","70","$NP$FG",],["(\\d{4})(\\d{3})(\\d{3})","$1 $2 $3","1(?:8[059]|5)","$FG",]]]',
"966": '["SA","00","0",,,"$NP$FG","\\d{7,10}","1\\d{7,8}|(?:[2-467]|92)\\d{7}|5\\d{8}|8\\d{9}",[["([1-467])(\\d{3})(\\d{4})","$1 $2 $3","[1-467]",,],["(1\\d)(\\d{3})(\\d{4})","$1 $2 $3","1[1-467]",,],["(5\\d)(\\d{3})(\\d{4})","$1 $2 $3","5",,],["(92\\d{2})(\\d{5})","$1 $2","92","$FG",],["(800)(\\d{3})(\\d{4})","$1 $2 $3","80","$FG",],["(811)(\\d{3})(\\d{3,4})","$1 $2 $3","81",,]]]',
"380": '["UA","00","0",,,"$NP$FG","\\d{5,9}","[3-689]\\d{8}",[["([3-689]\\d)(\\d{3})(\\d{4})","$1 $2 $3","[38]9|4(?:[45][0-5]|87)|5(?:0|6[37]|7[37])|6[36-8]|9[1-9]",,],["([3-689]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","3[1-8]2|4[13678]2|5(?:[12457]2|6[24])|6(?:[49]2|[12][29]|5[24])|8[0-8]|90",,],["([3-6]\\d{3})(\\d{5})","$1 $2","3(?:5[013-9]|[1-46-8])|4(?:[137][013-9]|6|[45][6-9]|8[4-6])|5(?:[1245][013-9]|6[0135-9]|3|7[4-6])|6(?:[49][013-9]|5[0135-9]|[12][13-8])",,]]]',
"98": '["IR","00","0",,,"$NP$FG","\\d{4,10}","[14-8]\\d{6,9}|[23]\\d{4,9}|9(?:[0-4]\\d{8}|9\\d{2,8})",[["(21)(\\d{3,5})","$1 $2","21",,],["(2[15])(\\d{3})(\\d{3,4})","$1 $2 $3","2(?:1|5[0-47-9])",,],["(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3","2[156]|31|51|71|86",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","[13-9]|2[02-47-9]",,],["(\\d{3})(\\d{2})(\\d{2,3})","$1 $2 $3","[13-9]|2[02-47-9]",,],["(\\d{3})(\\d{3})","$1 $2","[13-9]|2[02-47-9]",,]]]',
"98": '["IR","00","0",,,"$NP$FG","\\d{4,10}","[1-8]\\d{9}|9(?:[0-4]\\d{8}|9\\d{2,8})",[["(21)(\\d{3,5})","$1 $2","21",,],["(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3","[1-8]",,],["(\\d{3})(\\d{3})(\\d{3,4})","$1 $2 $3","9",,],["(\\d{3})(\\d{2})(\\d{2,3})","$1 $2 $3","9",,],["(\\d{3})(\\d{3})","$1 $2","9",,]]]',
"971": '["AE","00","0",,,"$NP$FG","\\d{5,12}","[2-79]\\d{7,8}|800\\d{2,9}",[["([2-4679])(\\d{3})(\\d{4})","$1 $2 $3","[2-4679][2-8]",,],["(5[0256])(\\d{3})(\\d{4})","$1 $2 $3","5",,],["([479]00)(\\d)(\\d{5})","$1 $2 $3","[479]0","$FG",],["([68]00)(\\d{2,9})","$1 $2","60|8","$FG",]]]',
"30": '["GR","00",,,,,"\\d{10}","[26-9]\\d{9}",[["([27]\\d)(\\d{4})(\\d{4})","$1 $2 $3","21|7",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","2[2-9]1|[689]",,],["(2\\d{3})(\\d{6})","$1 $2","2[2-9][02-9]",,]]]',
"228": '["TG","00",,,,,"\\d{8}","[29]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
@ -154,9 +154,9 @@ this.PHONE_NUMBER_META_DATA = {
"372": '["EE","00",,,,,"\\d{4,10}","1\\d{3,4}|[3-9]\\d{6,7}|800\\d{6,7}",[["([3-79]\\d{2})(\\d{4})","$1 $2","[369]|4[3-8]|5(?:[0-2]|5[0-478]|6[45])|7[1-9]",,],["(70)(\\d{2})(\\d{4})","$1 $2 $3","70",,],["(8000)(\\d{3})(\\d{3})","$1 $2 $3","800",,],["([458]\\d{3})(\\d{3,4})","$1 $2","40|5|8(?:00|[1-5])",,]]]',
"598": '["UY","0(?:1[3-9]\\d|0)","0",,,,"\\d{7,8}","[2489]\\d{6,7}",[["(\\d{4})(\\d{4})","$1 $2","[24]",,],["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","9[1-9]","$NP$FG",],["(\\d{3})(\\d{4})","$1 $2","[89]0","$NP$FG",]]]',
"502": '["GT","00",,,,,"\\d{8}(?:\\d{3})?","[2-7]\\d{7}|1[89]\\d{9}",[["(\\d{4})(\\d{4})","$1 $2","[2-7]",,],["(\\d{4})(\\d{3})(\\d{4})","$1 $2 $3","1",,]]]',
"82": '["KR","00(?:[124-68]|[37]\\d{2})","0","0(8[1-46-8]|85\\d{2})?",,"$NP$FG","\\d{4,10}","[1-7]\\d{3,9}|8\\d{8}",[["(\\d{2})(\\d{4})(\\d{4})","$1-$2-$3","1(?:0|1[19]|[69]9|5[458])|[57]0",,],["(\\d{2})(\\d{3,4})(\\d{4})","$1-$2-$3","1(?:[169][2-8]|[78]|5[1-4])|[68]0|[3-6][1-9][1-9]",,],["(\\d{3})(\\d)(\\d{4})","$1-$2-$3","131",,],["(\\d{3})(\\d{2})(\\d{4})","$1-$2-$3","131",,],["(\\d{3})(\\d{3})(\\d{4})","$1-$2-$3","13[2-9]",,],["(\\d{2})(\\d{2})(\\d{3})(\\d{4})","$1-$2-$3-$4","30",,],["(\\d)(\\d{3,4})(\\d{4})","$1-$2-$3","2[1-9]",,],["(\\d)(\\d{3,4})","$1-$2","21[0-46-9]",,],["(\\d{2})(\\d{3,4})","$1-$2","[3-6][1-9]1",,],["(\\d{4})(\\d{4})","$1-$2","1(?:5[46-9]|6[04678])","$FG",]]]',
"82": '["KR","00(?:[124-68]|[37]\\d{2})","0","0(8[1-46-8]|85\\d{2})?",,"$NP$FG","\\d{4,10}","[1-7]\\d{3,9}|8\\d{8}",[["(\\d{2})(\\d{4})(\\d{4})","$1-$2-$3","1(?:0|1[19]|[69]9|5[458])|[57]0",,],["(\\d{2})(\\d{3,4})(\\d{4})","$1-$2-$3","1(?:[169][2-8]|[78]|5[1-4])|[68]0|[3-6][1-9][1-9]",,],["(\\d{3})(\\d)(\\d{4})","$1-$2-$3","131",,],["(\\d{3})(\\d{2})(\\d{4})","$1-$2-$3","131",,],["(\\d{3})(\\d{3})(\\d{4})","$1-$2-$3","13[2-9]",,],["(\\d{2})(\\d{2})(\\d{3})(\\d{4})","$1-$2-$3-$4","30",,],["(\\d)(\\d{3,4})(\\d{4})","$1-$2-$3","2[1-9]",,],["(\\d)(\\d{3,4})","$1-$2","21[0-46-9]",,],["(\\d{2})(\\d{3,4})","$1-$2","[3-6][1-9]1",,],["(\\d{4})(\\d{4})","$1-$2","1(?:5[46-9]|6[04678]|8[0579])","$FG",]]]',
"253": '["DJ","00",,,,,"\\d{8}","[27]\\d{7}",[["(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,,]]]',
"91": '["IN","00","0",,,"$NP$FG","\\d{6,13}","1\\d{7,12}|[2-9]\\d{9,10}",[["(\\d{5})(\\d{5})","$1 $2","7(?:0[2-9]|2[0579]|3[057-9]|4[0-389]|6[0-35-9]|[57]|8[0-79])|8(?:0[015689]|1[0-57-9]|2[2356-9]|3[0-57-9]|[45]|6[02457-9]|7[1-69]|8[0124-9]|9[02-9])|9",,],["(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3","11|2[02]|33|4[04]|79|80[2-46]",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","1(?:2[0-249]|3[0-25]|4[145]|[569][14]|7[1257]|8[1346]|[68][1-9])|2(?:1[257]|3[013]|4[01]|5[0137]|6[0158]|78|8[1568]|9[14])|3(?:26|4[1-3]|5[34]|6[01489]|7[02-46]|8[159])|4(?:1[36]|2[1-47]|3[15]|5[12]|6[126-9]|7[0-24-9]|8[013-57]|9[014-7])|5(?:[136][25]|22|4[28]|5[12]|[78]1|9[15])|6(?:12|[2345]1|57|6[13]|7[14]|80)",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","7(?:12|2[14]|3[134]|4[47]|5[15]|[67]1|88)",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","8(?:16|2[014]|3[126]|6[136]|7[078]|8[34]|91)",,],["(\\d{4})(\\d{3})(\\d{3})","$1 $2 $3","1(?:[23579]|[468][1-9])|[2-8]",,],["(1600)(\\d{2})(\\d{4})","$1 $2 $3","160","$FG",],["(1800)(\\d{4,5})","$1 $2","180","$FG",],["(18[06]0)(\\d{2,4})(\\d{4})","$1 $2 $3","18[06]","$FG",],["(140)(\\d{3})(\\d{4})","$1 $2 $3","140","$FG",],["(\\d{4})(\\d{3})(\\d{4})(\\d{2})","$1 $2 $3 $4","18[06]","$FG",]]]',
"91": '["IN","00","0",,,"$NP$FG","\\d{6,13}","1\\d{7,12}|[2-9]\\d{9,10}",[["(\\d{5})(\\d{5})","$1 $2","7(?:0[2-9]|2[0579]|3[057-9]|4[0-389]|6[0-35-9]|[57]|8[0-79])|8(?:0[015689]|1[0-57-9]|2[2356-9]|3[0-57-9]|[45]|6[02457-9]|7[1-69]|8[0124-9]|9[02-9])|9",,],["(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3","11|2[02]|33|4[04]|79|80[2-46]",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","1(?:2[0-249]|3[0-25]|4[145]|[569][14]|7[1257]|8[1346]|[68][1-9])|2(?:1[257]|3[013]|4[01]|5[0137]|6[0158]|78|8[1568]|9[14])|3(?:26|4[1-3]|5[34]|6[01489]|7[02-46]|8[159])|4(?:1[36]|2[1-47]|3[15]|5[12]|6[0-26-9]|7[0-24-9]|8[013-57]|9[014-7])|5(?:[136][25]|22|4[28]|5[12]|[78]1|9[15])|6(?:12|[2345]1|57|6[13]|7[14]|80)",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","7(?:12|2[14]|3[134]|4[47]|5[15]|[67]1|88)",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","8(?:16|2[014]|3[126]|6[136]|7[078]|8[34]|91)",,],["(\\d{4})(\\d{3})(\\d{3})","$1 $2 $3","1(?:[23579]|[468][1-9])|[2-8]",,],["(1600)(\\d{2})(\\d{4})","$1 $2 $3","160","$FG",],["(1800)(\\d{4,5})","$1 $2","180","$FG",],["(18[06]0)(\\d{2,4})(\\d{4})","$1 $2 $3","18[06]","$FG",],["(140)(\\d{3})(\\d{4})","$1 $2 $3","140","$FG",],["(\\d{4})(\\d{3})(\\d{4})(\\d{2})","$1 $2 $3 $4","18[06]","$FG",]]]',
"389": '["MK","00","0",,,"$NP$FG","\\d{8}","[2-578]\\d{7}",[["(2)(\\d{3})(\\d{4})","$1 $2 $3","2",,],["([347]\\d)(\\d{3})(\\d{3})","$1 $2 $3","[347]",,],["([58]\\d{2})(\\d)(\\d{2})(\\d{2})","$1 $2 $3 $4","[58]",,]]]',
"1": ['["US","011","1",,,,"\\d{7}(?:\\d{3})?","[2-9]\\d{9}",[["(\\d{3})(\\d{4})","$1-$2",,,"NA"],["(\\d{3})(\\d{3})(\\d{4})","($1) $2-$3",,,"$1-$2-$3"]]]','["AI","011","1",,,,"\\d{7}(?:\\d{3})?","[2589]\\d{9}",]','["AS","011","1",,,,"\\d{7}(?:\\d{3})?","[5689]\\d{9}",]','["BB","011","1",,,,"\\d{7}(?:\\d{3})?","[2589]\\d{9}",]','["BM","011","1",,,,"\\d{7}(?:\\d{3})?","[4589]\\d{9}",]','["BS","011","1",,,,"\\d{7}(?:\\d{3})?","[2589]\\d{9}",]','["CA","011","1",,,,"\\d{7}(?:\\d{3})?","[2-9]\\d{9}|3\\d{6}",]','["DM","011","1",,,,"\\d{7}(?:\\d{3})?","[57-9]\\d{9}",]','["DO","011","1",,,,"\\d{7}(?:\\d{3})?","[589]\\d{9}",]','["GD","011","1",,,,"\\d{7}(?:\\d{3})?","[4589]\\d{9}",]','["GU","011","1",,,,"\\d{7}(?:\\d{3})?","[5689]\\d{9}",]','["JM","011","1",,,,"\\d{7}(?:\\d{3})?","[589]\\d{9}",]','["KN","011","1",,,,"\\d{7}(?:\\d{3})?","[589]\\d{9}",]','["KY","011","1",,,,"\\d{7}(?:\\d{3})?","[3589]\\d{9}",]','["LC","011","1",,,,"\\d{7}(?:\\d{3})?","[5789]\\d{9}",]','["MP","011","1",,,,"\\d{7}(?:\\d{3})?","[5689]\\d{9}",]','["MS","011","1",,,,"\\d{7}(?:\\d{3})?","[5689]\\d{9}",]','["PR","011","1",,,,"\\d{7}(?:\\d{3})?","[5789]\\d{9}",]','["SX","011","1",,,,"\\d{7}(?:\\d{3})?","[5789]\\d{9}",]','["TC","011","1",,,,"\\d{7}(?:\\d{3})?","[5689]\\d{9}",]','["TT","011","1",,,,"\\d{7}(?:\\d{3})?","[589]\\d{9}",]','["AG","011","1",,,,"\\d{7}(?:\\d{3})?","[2589]\\d{9}",]','["VC","011","1",,,,"\\d{7}(?:\\d{3})?","[5789]\\d{9}",]','["VG","011","1",,,,"\\d{7}(?:\\d{3})?","[2589]\\d{9}",]','["VI","011","1",,,,"\\d{7}(?:\\d{3})?","[3589]\\d{9}",]'],
"60": '["MY","00","0",,,,"\\d{6,10}","[13-9]\\d{7,9}",[["([4-79])(\\d{3})(\\d{4})","$1-$2 $3","[4-79]","$NP$FG",],["(3)(\\d{4})(\\d{4})","$1-$2 $3","3","$NP$FG",],["([18]\\d)(\\d{3})(\\d{3,4})","$1-$2 $3","1[02-46-9][1-9]|8","$NP$FG",],["(1)([36-8]00)(\\d{2})(\\d{4})","$1-$2-$3-$4","1[36-8]0",,],["(11)(\\d{4})(\\d{4})","$1-$2 $3","11","$NP$FG",],["(15[49])(\\d{3})(\\d{4})","$1-$2 $3","15","$NP$FG",]]]',
@ -166,14 +166,14 @@ this.PHONE_NUMBER_META_DATA = {
"686": '["KI","00",,"0",,,"\\d{5,8}","[2458]\\d{4}|3\\d{4,7}|7\\d{7}",]',
"994": '["AZ","00","0",,,"($NP$FG)","\\d{7,9}","[1-9]\\d{8}",[["(\\d{2})(\\d{3})(\\d{2})(\\d{2})","$1 $2 $3 $4","(?:1[28]|2(?:[45]2|[0-36])|365)",,],["(\\d{2})(\\d{3})(\\d{2})(\\d{2})","$1 $2 $3 $4","[4-8]","$NP$FG",],["(\\d{3})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","9","$NP$FG",]]]',
"979": '["001",,,,,,"\\d{9}","\\d{9}",[["(\\d)(\\d{4})(\\d{4})","$1 $2 $3",,,]]]',
"66": '["TH","00","0",,,"$NP$FG","\\d{4}|\\d{8,10}","[2-9]\\d{7,8}|1\\d{3}(?:\\d{6})?",[["(2)(\\d{3})(\\d{4})","$1 $2 $3","2",,],["([3-9]\\d)(\\d{3})(\\d{3,4})","$1 $2 $3","[3-9]",,],["(1[89]00)(\\d{3})(\\d{3})","$1 $2 $3","1","$FG",]]]',
"66": '["TH","00","0",,,"$NP$FG","\\d{4}|\\d{8,10}","[2-9]\\d{7,8}|1\\d{3}(?:\\d{5,6})?",[["(2)(\\d{3})(\\d{4})","$1 $2 $3","2",,],["([13-9]\\d)(\\d{3})(\\d{3,4})","$1 $2 $3","14|[3-9]",,],["(1[89]00)(\\d{3})(\\d{3})","$1 $2 $3","1","$FG",]]]',
"233": '["GH","00","0",,,"$NP$FG","\\d{7,9}","[235]\\d{8}|8\\d{7}",[["(\\d{2})(\\d{3})(\\d{4})","$1 $2 $3","[235]",,],["(\\d{3})(\\d{5})","$1 $2","8",,]]]',
"593": '["EC","00","0",,,"($NP$FG)","\\d{7,11}","1\\d{9,10}|[2-8]\\d{7}|9\\d{8}",[["(\\d)(\\d{3})(\\d{4})","$1 $2-$3","[247]|[356][2-8]",,"$1-$2-$3"],["(\\d{2})(\\d{3})(\\d{4})","$1 $2 $3","9","$NP$FG",],["(1800)(\\d{3})(\\d{3,4})","$1 $2 $3","1","$FG",]]]',
"509": '["HT","00",,,,,"\\d{8}","[2-489]\\d{7}",[["(\\d{2})(\\d{2})(\\d{4})","$1 $2 $3",,,]]]',
"54": '["AR","00","0","0?(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1267])|3(?:02?|1[467]|2[03-6]|3[13-8]|[49][2-6]|5[2-8]|[67])|4(?:7[3-578]|9)|6(?:[0136]|2[24-6]|4[6-8]?|5[15-8])|80|9(?:0[1-3]|[19]|2\\d|3[1-6]|4[02568]?|5[2-4]|6[2-46]|72?|8[23]?))|3(?:3(?:2[79]|6|8[2578])|4(?:0[124-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6[02-9]|7[126]|8[2379]?|9[1-36-8])|5(?:1|2[1245]|3[237]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|6[24]|7(?:1[1568]|2[15]|3[145]|4[13]|5[14-8]|[069]|7[2-57]|8[126])|8(?:[01]|2[15-7]|3[2578]?|4[13-6]|5[4-8]?|6[1-357-9]|7[36-8]?|8[5-8]?|9[124])))?15)?","9$1","$NP$FG","\\d{6,11}","11\\d{8}|[2368]\\d{9}|9\\d{10}",[["([68]\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","[68]",,],["(\\d{2})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\d{3})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\d{4})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(9)(11)(\\d{4})(\\d{4})","$2 15-$3-$4","911",,"$1 $2 $3-$4"],["(9)(\\d{3})(\\d{3})(\\d{4})","$2 15-$3-$4","9(?:2[234689]|3[3-8])",,"$1 $2 $3-$4"],["(9)(\\d{4})(\\d{2})(\\d{4})","$2 15-$3-$4","9[23]",,"$1 $2 $3-$4"],["(11)(\\d{4})(\\d{4})","$1 $2-$3","1",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2-$3","2(?:2[013]|3[067]|49|6[01346]|80|9[147-9])|3(?:36|4[12358]|5[138]|6[24]|7[069]|8[013578])",,],["(\\d{4})(\\d{2})(\\d{4})","$1 $2-$3","[23]",,],["(\\d{3})","$1","1[012]|911","$FG","NA"]]]',
"54": '["AR","00","0","0?(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1267])|3(?:02?|1[467]|2[03-6]|3[13-8]|[49][2-6]|5[2-8]|[67])|4(?:7[3-578]|9)|6(?:[0136]|2[24-6]|4[6-8]?|5[15-8])|80|9(?:0[1-3]|[19]|2\\d|3[1-6]|4[02568]?|5[2-4]|6[2-46]|72?|8[23]?))|3(?:3(?:2[79]|6|8[2578])|4(?:0[0-24-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6[02-9]|7[126]|8[2379]?|9[1-36-8])|5(?:1|2[1245]|3[237]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|6[24]|7(?:[069]|1[1568]|2[15]|3[145]|4[13]|5[14-8]|7[2-57]|8[126])|8(?:[01]|2[15-7]|3[2578]?|4[13-6]|5[4-8]?|6[1-357-9]|7[36-8]?|8[5-8]?|9[124])))?15)?","9$1","$NP$FG","\\d{6,11}","11\\d{8}|[2368]\\d{9}|9\\d{10}",[["([68]\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","[68]",,],["(\\d{2})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\d{3})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(\\d{4})(\\d{4})","$1-$2","[2-9]","$FG","NA"],["(9)(11)(\\d{4})(\\d{4})","$2 15-$3-$4","911",,"$1 $2 $3-$4"],["(9)(\\d{3})(\\d{3})(\\d{4})","$2 15-$3-$4","9(?:2[234689]|3[3-8])",,"$1 $2 $3-$4"],["(9)(\\d{4})(\\d{2})(\\d{4})","$2 15-$3-$4","9[23]",,"$1 $2 $3-$4"],["(11)(\\d{4})(\\d{4})","$1 $2-$3","1",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2-$3","2(?:2[013]|3[067]|49|6[01346]|80|9[147-9])|3(?:36|4[1-358]|5[138]|6[24]|7[069]|8[013578])",,],["(\\d{4})(\\d{2})(\\d{4})","$1 $2-$3","[23]",,],["(\\d{3})","$1","1[012]|911","$FG","NA"]]]',
"57": '["CO","00(?:4(?:[14]4|56)|[579])","0","0([3579]|4(?:44|56))?",,,"\\d{7,11}","(?:[13]\\d{0,3}|[24-8])\\d{7}",[["(\\d)(\\d{7})","$1 $2","1(?:8[2-9]|9[0-3]|[2-7])|[24-8]","($FG)",],["(\\d{3})(\\d{7})","$1 $2","3",,],["(1)(\\d{3})(\\d{7})","$1-$2-$3","1(?:80|9[04])","$NP$FG","$1 $2 $3"]]]',
"597": '["SR","00",,,,,"\\d{6,7}","[2-8]\\d{5,6}",[["(\\d{3})(\\d{3})","$1-$2","[2-4]|5[2-58]",,],["(\\d{2})(\\d{2})(\\d{2})","$1-$2-$3","56",,],["(\\d{3})(\\d{4})","$1-$2","59|[6-8]",,]]]',
"676": '["TO","00",,,,,"\\d{5,7}","[02-8]\\d{4,6}",[["(\\d{2})(\\d{3})","$1-$2","[1-6]|7[0-4]|8[05]",,],["(\\d{3})(\\d{4})","$1 $2","7[5-9]|8[7-9]",,],["(\\d{4})(\\d{3})","$1 $2","0",,]]]',
"676": '["TO","00",,,,,"\\d{5,7}","[02-8]\\d{4,6}",[["(\\d{2})(\\d{3})","$1-$2","[1-6]|7[0-4]|8[05]",,],["(\\d{3})(\\d{4})","$1 $2","7[5-9]|8[47-9]",,],["(\\d{4})(\\d{3})","$1 $2","0",,]]]',
"505": '["NI","00",,,,,"\\d{8}","[12578]\\d{7}",[["(\\d{4})(\\d{4})","$1 $2",,,]]]',
"850": '["KP","00|99","0",,,"$NP$FG","\\d{6,8}|\\d{10}","1\\d{9}|[28]\\d{7}",[["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","1",,],["(\\d)(\\d{3})(\\d{4})","$1 $2 $3","2",,],["(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","8",,]]]',
"7": ['["RU","810","8",,,"$NP ($FG)","\\d{10}","[3489]\\d{9}",[["(\\d{3})(\\d{2})(\\d{2})","$1-$2-$3","[1-79]","$FG","NA"],["([3489]\\d{2})(\\d{3})(\\d{2})(\\d{2})","$1 $2-$3-$4","[34689]",,],["(7\\d{2})(\\d{3})(\\d{4})","$1 $2 $3","7",,]]]','["KZ","810","8",,,,"\\d{10}","(?:33\\d|7\\d{2}|80[09])\\d{7}",]'],
@ -217,6 +217,6 @@ this.PHONE_NUMBER_META_DATA = {
"960": '["MV","0(?:0|19)",,,,,"\\d{7,10}","[3467]\\d{6}|9(?:00\\d{7}|\\d{6})",[["(\\d{3})(\\d{4})","$1-$2","[3467]|9(?:[1-9]|0[1-9])",,],["(\\d{3})(\\d{3})(\\d{4})","$1 $2 $3","900",,]]]',
"221": '["SN","00",,,,,"\\d{9}","[3789]\\d{8}",[["(\\d{2})(\\d{3})(\\d{2})(\\d{2})","$1 $2 $3 $4","[379]",,],["(\\d{3})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","8",,]]]',
"595": '["PY","00","0",,,,"\\d{5,9}","5[0-5]\\d{4,7}|[2-46-9]\\d{5,8}",[["(\\d{2})(\\d{5,7})","$1 $2","(?:[26]1|3[289]|4[124678]|7[123]|8[1236])","($FG)",],["(\\d{3})(\\d{3,6})","$1 $2","[2-9]0","$NP$FG",],["(\\d{3})(\\d{6})","$1 $2","9[1-9]","$NP$FG",],["(\\d{2})(\\d{3})(\\d{4})","$1 $2 $3","8700",,],["(\\d{3})(\\d{4,6})","$1 $2","[2-8][1-9]","($FG)",]]]',
"977": '["NP","00","0",,,"$NP$FG","\\d{6,10}","[1-8]\\d{7}|9(?:[1-69]\\d{6}|7[2-6]\\d{5,7}|8\\d{8})",[["(1)(\\d{7})","$1-$2","1[2-6]",,],["(\\d{2})(\\d{6})","$1-$2","1[01]|[2-8]|9(?:[1-69]|7[15-9])",,],["(9\\d{2})(\\d{7})","$1-$2","9(?:7[45]|8)",,]]]',
"977": '["NP","00","0",,,"$NP$FG","\\d{6,10}","[1-8]\\d{7}|9(?:[1-69]\\d{6,8}|7[2-6]\\d{5,7}|8\\d{8})",[["(1)(\\d{7})","$1-$2","1[2-6]",,],["(\\d{2})(\\d{6})","$1-$2","1[01]|[2-8]|9(?:[1-69]|7[15-9])",,],["(9\\d{2})(\\d{7})","$1-$2","9(?:6[013]|7[245]|8)",,]]]',
"36": '["HU","00","06",,,"($FG)","\\d{6,9}","[1-9]\\d{7,8}",[["(1)(\\d{3})(\\d{4})","$1 $2 $3","1",,],["(\\d{2})(\\d{3})(\\d{3,4})","$1 $2 $3","[2-9]",,]]]',
};

View File

@ -87,7 +87,7 @@ elif CONFIG['OS_ARCH'] in ('DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD'):
UNIFIED_SOURCES += [
'ProcessUtils_bsd.cpp'
]
elif CONFIG['OS_ARCH'] in ('Darwin'):
elif CONFIG['OS_ARCH'] == 'Darwin':
UNIFIED_SOURCES += [
'ProcessUtils_mac.mm'
]

View File

@ -19,6 +19,7 @@ public class AboutPages {
public static final String FIREFOX = "about:firefox";
public static final String HEALTHREPORT = "about:healthreport";
public static final String HOME = "about:home";
public static final String PASSWORDS = "about:passwords";
public static final String PRIVATEBROWSING = "about:privatebrowsing";
public static final String READER = "about:reader";
public static final String UPDATER = "about:";

View File

@ -2939,6 +2939,7 @@ public class BrowserApp extends GeckoApp
ClearOnShutdownPref.PREF,
new HashSet<String>()).isEmpty();
aMenu.findItem(R.id.quit).setVisible(visible);
aMenu.findItem(R.id.logins).setVisible(AppConstants.NIGHTLY_BUILD);
if (tab == null || tab.getURL() == null) {
bookmark.setEnabled(false);
@ -3167,6 +3168,11 @@ public class BrowserApp extends GeckoApp
return true;
}
if (itemId == R.id.logins) {
Tabs.getInstance().loadUrlInTab(AboutPages.PASSWORDS);
return true;
}
if (itemId == R.id.apps) {
Tabs.getInstance().loadUrlInTab(AboutPages.APPS);
return true;

View File

@ -4,10 +4,6 @@
package org.mozilla.gecko.fxa.activities;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
@ -43,6 +39,14 @@ import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.text.format.DateUtils;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A fragment that displays the status of an AndroidFxAccount.
* <p>
@ -54,6 +58,15 @@ public class FxAccountStatusFragment
implements OnPreferenceClickListener, OnPreferenceChangeListener {
private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
/**
* If a device claims to have synced before this date, we will assume it has never synced.
*/
private static final Date EARLIEST_VALID_SYNCED_DATE;
static {
final Calendar c = GregorianCalendar.getInstance();
c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
EARLIEST_VALID_SYNCED_DATE = c.getTime();
}
// When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
// before trying to sync. Should we kill off the fragment before the sync
// request happens, that's okay: the runnable will run if the UI thread is
@ -529,6 +542,9 @@ public class FxAccountStatusFragment
// This is a helper function similar to TabsAccessor.getLastSyncedString() to calculate relative "Last synced" time span.
private String getLastSyncedString(final long startTime) {
if (new Date(startTime).before(EARLIEST_VALID_SYNCED_DATE)) {
return getActivity().getString(R.string.fxaccount_status_never_synced);
}
final CharSequence relativeTimeSpanString = DateUtils.getRelativeTimeSpanString(startTime);
return getActivity().getResources().getString(R.string.fxaccount_status_last_synced, relativeTimeSpanString);
}

View File

@ -141,7 +141,9 @@
<!ENTITY pref_category_devtools_summary "Remote debugging, paint flashing">
<!ENTITY pref_developer_paint_flashing "Paint flashing">
<!ENTITY pref_developer_remotedebugging "Remote debugging">
<!ENTITY pref_category_logins "Logins">
<!ENTITY pref_remember_signons "Remember passwords">
<!ENTITY pref_manage_logins "Manage logins">
<!ENTITY pref_category_home "Home">
<!ENTITY pref_category_home_summary "Customize your homepage">
@ -287,6 +289,7 @@ size. -->
<!ENTITY quit "Quit">
<!ENTITY addons "Add-ons">
<!ENTITY logins "Logins">
<!ENTITY downloads "Downloads">
<!ENTITY apps "Apps">
<!ENTITY char_encoding "Character Encoding">

View File

@ -115,6 +115,7 @@ OnSharedPreferenceChangeListener
private static final String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
private static final String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
private static final String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
private static final String PREFS_LOGIN_MANAGE = NON_PREF_PREFIX + "signon.manage";
private static final String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
private static final String PREFS_UPDATER_URL = "app.update.url.android";
private static final String PREFS_GEO_REPORTING = NON_PREF_PREFIX + "app.geo.reportdata";
@ -674,6 +675,10 @@ OnSharedPreferenceChangeListener
preferences.removePreference(pref);
i--;
continue;
} else if (!AppConstants.NIGHTLY_BUILD &&
PREFS_LOGIN_MANAGE.equals(key)) {
preferences.removePreference(pref);
i--;
} else if (AppConstants.RELEASE_BUILD &&
PREFS_DISPLAY_REFLOW_ON_ZOOM.equals(key)) {
// Remove UI for reflow on release builds.

View File

@ -16,7 +16,8 @@
<org.mozilla.gecko.widget.GeckoSwipeRefreshLayout
android:id="@id/remote_tabs_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:enabled="false">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -86,6 +86,9 @@
android:icon="@drawable/ic_menu_addons"
android:title="@string/addons"/>
<item android:id="@+id/logins"
android:title="@string/logins"/>
<item android:id="@+id/apps"
android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/>

View File

@ -86,6 +86,9 @@
android:icon="@drawable/ic_menu_addons"
android:title="@string/addons"/>
<item android:id="@+id/logins"
android:title="@string/logins"/>
<item android:id="@+id/apps"
android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/>

View File

@ -87,6 +87,9 @@
android:icon="@drawable/ic_menu_addons"
android:title="@string/addons"/>
<item android:id="@+id/logins"
android:title="@string/logins"/>
<item android:id="@+id/apps"
android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/>

View File

@ -49,6 +49,9 @@
<item android:id="@+id/downloads"
android:title="@string/downloads"/>
<item android:id="@+id/logins"
android:title="@string/logins"/>
<item android:id="@+id/apps"
android:title="@string/apps"/>

View File

@ -36,13 +36,21 @@
android:entryValues="@array/pref_cookies_values"
android:persistent="false" />
<CheckBoxPreference android:key="signon.rememberSignons"
android:title="@string/pref_remember_signons"
android:persistent="false" />
<PreferenceCategory android:title="@string/pref_category_logins">
<CheckBoxPreference android:key="privacy.masterpassword.enabled"
android:title="@string/pref_use_master_password"
android:persistent="false" />
<org.mozilla.gecko.preferences.LinkPreference android:key="android.not_a_preference.signon.manage"
android:title="@string/pref_manage_logins"
url="about:passwords"/>
<CheckBoxPreference android:key="signon.rememberSignons"
android:title="@string/pref_remember_signons"
android:persistent="false" />
<CheckBoxPreference android:key="privacy.masterpassword.enabled"
android:title="@string/pref_use_master_password"
android:persistent="false" />
</PreferenceCategory>
<!-- keys prefixed with "android.not_a_preference." are not synced with Gecko -->
<PreferenceCategory android:title="@string/pref_clear_private_data_category">

View File

@ -174,7 +174,9 @@
<string name="pref_learn_more">&pref_learn_more;</string>
<string name="pref_category_logins">&pref_category_logins;</string>
<string name="pref_remember_signons">&pref_remember_signons;</string>
<string name="pref_manage_logins">&pref_manage_logins;</string>
<string name="pref_cookies_menu">&pref_cookies_menu;</string>
<string name="pref_cookies_accept_all">&pref_cookies_accept_all;</string>
@ -284,6 +286,7 @@
<string name="one_tab">&one_tab;</string>
<string name="num_tabs">&num_tabs2;</string>
<string name="addons">&addons;</string>
<string name="logins">&logins;</string>
<string name="downloads">&downloads;</string>
<string name="apps">&apps;</string>
<string name="char_encoding">&char_encoding;</string>

View File

@ -34,6 +34,7 @@ public class StringHelper {
public static final String ABOUT_DOWNLOADS_URL = "about:downloads";
public static final String ABOUT_HOME_URL = "about:home";
public static final String ABOUT_ADDONS_URL = "about:addons";
public static final String ABOUT_PASSWORDS_URL = "about:passwords";
public static final String ABOUT_APPS_URL = "about:apps";
public static final String ABOUT_ABOUT_URL = "about:about";
public static final String ABOUT_SCHEME = "about:";
@ -184,6 +185,7 @@ public class StringHelper {
public static final String DNT_LABEL = "Do not track";
public static final String COOKIES_LABEL = "Cookies";
public static final String REMEMBER_PASSWORDS_LABEL = "Remember passwords";
public static final String MANAGE_LOGINS_LABEL = "Manage logins";
public static final String MASTER_PASSWORD_LABEL = "Use master password";
public static final String CLEAR_PRIVATE_DATA_LABEL = "Clear now";
@ -226,6 +228,7 @@ public class StringHelper {
public static final String PDF_LABEL = "Save as PDF";
public static final String DOWNLOADS_LABEL = "Downloads";
public static final String ADDONS_LABEL = "Add-ons";
public static final String LOGINS_LABEL = "Logins";
public static final String APPS_LABEL = "Apps";
public static final String SETTINGS_LABEL = "Settings";
public static final String GUEST_MODE_LABEL = "New Guest Session";

View File

@ -59,11 +59,13 @@ public class testSettingsMenuItems extends PixelTest {
// Privacy menu items.
String[] PATH_PRIVACY = { StringHelper.PRIVACY_SECTION_LABEL };
final String[] TRACKING_PROTECTION_LABEL_ARR = { StringHelper.TRACKING_PROTECTION_LABEL };
final String[] MANAGE_LOGINS_ARR = { StringHelper.MANAGE_LOGINS_LABEL };
String[][] OPTIONS_PRIVACY = {
TRACKING_PROTECTION_LABEL_ARR,
{ StringHelper.DNT_LABEL },
{ StringHelper.COOKIES_LABEL, "Enabled", "Enabled, excluding 3rd party", "Disabled" },
{ StringHelper.REMEMBER_PASSWORDS_LABEL },
MANAGE_LOGINS_ARR,
{ StringHelper.MASTER_PASSWORD_LABEL },
{ StringHelper.CLEAR_PRIVATE_DATA_LABEL, "", "Browsing history", "Downloads", "Form & search history", "Cookies & active logins", "Saved passwords", "Cache", "Offline website data", "Site settings", "Clear data" },
};
@ -171,7 +173,9 @@ public class testSettingsMenuItems extends PixelTest {
}
if (!AppConstants.NIGHTLY_BUILD) {
settingsMap.get(PATH_PRIVACY).remove(TRACKING_PROTECTION_LABEL_ARR);
final List<String[]> privacy = settingsMap.get(PATH_PRIVACY);
privacy.remove(TRACKING_PROTECTION_LABEL_ARR);
privacy.remove(MANAGE_LOGINS_ARR);
}
// Automatic updates

View File

@ -1,6 +1,7 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.AppConstants;
/** This patch tests the System Pages first by loading system pages from
* the awesome bar and then from Firefox menu
@ -13,13 +14,14 @@ public class testSystemPages extends PixelTest {
blockForGeckoReady();
final String urls [] = { StringHelper.ABOUT_FIREFOX_URL, StringHelper.ABOUT_RIGHTS_URL,
StringHelper.ABOUT_ADDONS_URL, StringHelper.ABOUT_DOWNLOADS_URL,
StringHelper.ABOUT_ADDONS_URL, StringHelper.ABOUT_DOWNLOADS_URL, StringHelper.ABOUT_PASSWORDS_URL,
StringHelper.ABOUT_BUILDCONFIG_URL, StringHelper.ABOUT_FEEDBACK_URL,
StringHelper.ABOUT_HEALTHREPORT_URL, StringHelper.ABOUT_SCHEME
};
// Pages to be tested from the menu and their expected urls. This if of the form { {{ <path to item> }, { <expected url> }}* }
String menuItems [][][] = {{{ StringHelper.APPS_LABEL }, { StringHelper.ABOUT_APPS_URL }},
{{ StringHelper.DOWNLOADS_LABEL }, { StringHelper.ABOUT_DOWNLOADS_URL}},
{{ StringHelper.LOGINS_LABEL}, { StringHelper.ABOUT_PASSWORDS_URL }},
{{ StringHelper.ADDONS_LABEL }, { StringHelper.ABOUT_ADDONS_URL }},
{{ StringHelper.SETTINGS_LABEL, StringHelper.MOZILLA_SECTION_LABEL, StringHelper.ABOUT_LABEL }, { StringHelper.ABOUT_SCHEME }},
{{ StringHelper.SETTINGS_LABEL, StringHelper.MOZILLA_SECTION_LABEL, StringHelper.FEEDBACK_LABEL }, { StringHelper.ABOUT_FEEDBACK_URL }},
@ -48,6 +50,9 @@ public class testSystemPages extends PixelTest {
// Load from Url the about: pages,verify the Url and the tabs number
public void checkUrl(String urls []) {
for (String url:urls) {
if (skipItemURL(url)) {
continue;
}
loadAndPaint(url);
verifyTabCount(mExpectedTabCount);
verifyUrl(url);
@ -64,6 +69,10 @@ public class testSystemPages extends PixelTest {
String [] pathToItem = item[0];
String expectedUrl = item[1][0];
if (skipItemURL(expectedUrl)) {
continue;
}
expectedTabCount++;
// Set up listeners to catch the page load we're about to do
@ -90,4 +99,11 @@ public class testSystemPages extends PixelTest {
verifyTabCount(expectedTabCount);
}
}
private boolean skipItemURL(String item) {
if (StringHelper.ABOUT_PASSWORDS_URL.equals(item) && !AppConstants.NIGHTLY_BUILD) {
return true;
}
return false;
}
}

View File

@ -35,27 +35,6 @@ let Reader = {
mm.broadcastAsyncMessage("Reader:Removed", { url: aData });
break;
}
case "Gesture:DoubleTap": {
// Ideally, we would just do this all with web APIs in AboutReader.jsm (bug 1118487)
if (!BrowserApp.selectedBrowser.currentURI.spec.startsWith("about:reader")) {
return;
}
let win = BrowserApp.selectedBrowser.contentWindow;
let scrollBy;
// Arbitrary choice of innerHeight (50) to give some context after scroll.
if (JSON.parse(aData).y < (win.innerHeight / 2)) {
scrollBy = - win.innerHeight + 50;
} else {
scrollBy = win.innerHeight - 50;
}
let viewport = BrowserApp.selectedTab.getViewport();
let newY = Math.min(Math.max(viewport.cssY + scrollBy, viewport.cssPageTop), viewport.cssPageBottom);
let newRect = new Rect(viewport.cssX, newY, viewport.cssWidth, viewport.cssHeight);
ZoomHelper.zoomToRect(newRect, -1);
break;
}
}
},
@ -171,7 +150,7 @@ let Reader = {
let browser = tab.browser;
if (browser.currentURI.spec.startsWith("about:reader")) {
this.pageAction.id = PageActions.add({
title: Strings.browser.GetStringFromName("readerView.exit"),
title: Strings.reader.GetStringFromName("readerView.close"),
icon: "drawable://reader_active",
clickCallback: () => this.pageAction.readerModeCallback(tab.id),
important: true
@ -188,7 +167,7 @@ let Reader = {
if (browser.isArticle) {
this.pageAction.id = PageActions.add({
title: Strings.browser.GetStringFromName("readerView.enter"),
title: Strings.reader.GetStringFromName("readerView.enter"),
icon: "drawable://reader",
clickCallback: () => this.pageAction.readerModeCallback(tab.id),
longClickCallback: () => this.pageAction.readerModeActiveCallback(tab.id),

View File

@ -256,7 +256,7 @@ let Passwords = {
},
_filter: function(event) {
let value = event.target.value;
let value = event.target.value.toLowerCase();
let logins = this._logins.filter((login) => {
if (login.hostname.toLowerCase().indexOf(value) != -1) {
return true;

View File

@ -146,7 +146,7 @@ let lazilyLoadedObserverScripts = [
["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
["Reader", ["Reader:FetchContent", "Reader:Removed", "Gesture:DoubleTap"], "chrome://browser/content/Reader.js"],
["Reader", ["Reader:FetchContent", "Reader:Removed"], "chrome://browser/content/Reader.js"],
];
if (AppConstants.MOZ_WEBRTC) {
lazilyLoadedObserverScripts.push(
@ -323,6 +323,7 @@ let Strings = {
init: function () {
XPCOMUtils.defineLazyGetter(Strings, "brand", () => Services.strings.createBundle("chrome://branding/locale/brand.properties"));
XPCOMUtils.defineLazyGetter(Strings, "browser", () => Services.strings.createBundle("chrome://browser/locale/browser.properties"));
XPCOMUtils.defineLazyGetter(Strings, "reader", () => Services.strings.createBundle("chrome://global/locale/aboutReader.properties"));
},
flush: function () {

View File

@ -10,6 +10,16 @@ const Cr = Components.results;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
/* Constants for password prompt telemetry.
* Mirrored in nsLoginManagerPrompter.js */
const PROMPT_DISPLAYED = 0;
const PROMPT_ADD = 1;
const PROMPT_NOTNOW = 2;
const PROMPT_NEVER = 3;
const PROMPT_UPDATE = 1;
/* ==================== LoginManagerPrompter ==================== */
/*
* LoginManagerPrompter
@ -118,6 +128,7 @@ LoginManagerPrompter.prototype = {
*/
promptToSavePassword : function (aLogin) {
this._showSaveLoginNotification(aLogin);
Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED);
},
@ -175,17 +186,20 @@ LoginManagerPrompter.prototype = {
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
// without a getService() call.
var pwmgr = this._pwmgr;
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION");
var buttons = [
{
label: this._getLocalizedString("saveButton"),
callback: function() {
pwmgr.addLogin(aLogin);
promptHistogram.add(PROMPT_ADD);
}
},
{
label: this._getLocalizedString("dontSaveButton"),
callback: function() {
promptHistogram.add(PROMPT_NOTNOW);
// Don't set a permanent exception
}
}
@ -204,6 +218,7 @@ LoginManagerPrompter.prototype = {
*/
promptToChangePassword : function (aOldLogin, aNewLogin) {
this._showChangeLoginNotification(aOldLogin, aNewLogin.password);
Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION").add(PROMPT_DISPLAYED);
},
/*
@ -225,17 +240,20 @@ LoginManagerPrompter.prototype = {
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
// without a getService() call.
var self = this;
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
var buttons = [
{
label: this._getLocalizedString("updateButton"),
callback: function() {
self._updateLogin(aOldLogin, aNewPassword);
promptHistogram.add(PROMPT_UPDATE);
}
},
{
label: this._getLocalizedString("dontUpdateButton"),
callback: function() {
promptHistogram.add(PROMPT_NOTNOW);
// do nothing
}
}

View File

@ -361,10 +361,6 @@ getUserMedia.sharingCamera.message2 = Camera is on
getUserMedia.sharingMicrophone.message2 = Microphone is on
getUserMedia.sharingCameraAndMicrophone.message2 = Camera and microphone are on
#Reader view
readerView.enter = Enter Reader View
readerView.exit = Exit Reader View
# LOCALIZATION NOTE (readerMode.toolbarTip):
# Tip shown to users the first time we hide the reader mode toolbar.
readerMode.toolbarTip=Tap the screen to show reader options

View File

@ -190,6 +190,7 @@
<string name="fxaccount_status_sync_now">&fxaccount_status_sync_now;</string>
<string name="fxaccount_status_syncing">&fxaccount_status_syncing2;</string>
<string name="fxaccount_status_last_synced">&remote_tabs_last_synced;</string>
<string name="fxaccount_status_never_synced">&remote_tabs_never_synced;</string>
<string name="fxaccount_status_device_name">&fxaccount_status_device_name;</string>
<string name="fxaccount_status_sync_server">&fxaccount_status_sync_server;</string>
<string name="fxaccount_status_sync">&fxaccount_status_sync;</string>

View File

@ -4,6 +4,10 @@
# 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/.
with Files('**/Makefile.in'):
BUG_COMPONENT = ('Core', 'Build Config')
FINAL = True
CONFIGURE_SUBST_FILES += [
'config/autoconf.mk',
'config/emptyvars.mk',
@ -66,7 +70,7 @@ if CONFIG['COMPILE_ENVIRONMENT'] and not CONFIG['LIBXUL_SDK']:
DIRS += ['config/external/icu']
DIRS += ['js/src']
if not CONFIG['JS_STANDALONE']:
if not CONFIG['JS_STANDALONE'] and CONFIG['MOZ_BUILD_APP']:
# Bring in the configuration for the configured application.
include('/' + CONFIG['MOZ_BUILD_APP'] + '/app.mozbuild')

View File

@ -33,7 +33,8 @@ LOCAL_INCLUDES += [
]
protocols = CONFIG['NECKO_PROTOCOLS'].copy()
protocols.remove("about")
if 'about' in protocols:
protocols.remove('about')
LOCAL_INCLUDES += sorted([
'/netwerk/protocol/%s' % d for d in protocols
])

View File

@ -4,6 +4,12 @@
# 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/.
with Files('mach/**'):
BUG_COMPONENT = ('Core', 'mach')
with Files('mozbuild/**'):
BUG_COMPONENT = ('Core', 'Build Config')
SPHINX_PYTHON_PACKAGE_DIRS += [
'mach',
'mozbuild/mozbuild',

View File

@ -29,21 +29,41 @@ from mach.decorators import (
class MachCommands(MachCommandBase):
@Command('ide', category='devenv',
description='Generate a project and launch an IDE.')
@CommandArgument('ide', choices=['eclipse', 'visualstudio'])
@CommandArgument('ide', choices=['eclipse', 'visualstudio', 'androidstudio', 'intellij'])
@CommandArgument('args', nargs=argparse.REMAINDER)
def eclipse(self, ide, args):
if ide == 'eclipse':
backend = 'CppEclipse'
elif ide == 'visualstudio':
backend = 'VisualStudio'
elif ide == 'androidstudio' or ide == 'intellij':
# The build backend for Android Studio and IntelliJ is just the regular one.
backend = 'RecursiveMake'
if backend == 'CppEclipse':
if ide == 'eclipse':
try:
which.which('eclipse')
except which.WhichError:
print('Eclipse CDT 8.4 or later must be installed in your PATH.')
print('Download: http://www.eclipse.org/cdt/downloads.php')
return 1
elif ide == 'androidstudio' or ide =='intellij':
studio = ['studio'] if ide == 'androidstudio' else ['idea']
if sys.platform != 'darwin':
try:
which.which(studio[0])
except:
self.print_ide_error(ide)
return 1
else:
# In order of preference!
for d in self.get_mac_ide_preferences(ide):
if os.path.isdir(d):
studio = ['open', '-a', d]
break
else:
print('Android Studio or IntelliJ IDEA 14 is not installed in /Applications.')
return 1
# Here we refresh the whole build. 'build export' is sufficient here and is probably more
# correct but it's also nice having a single target to get a fully built and indexed
@ -52,20 +72,36 @@ class MachCommands(MachCommandBase):
if res != 0:
return 1
# Generate or refresh the eclipse workspace
python = self.virtualenv_manager.python_path
config_status = os.path.join(self.topobjdir, 'config.status')
args = [python, config_status, '--backend=%s' % backend]
res = self._run_command_in_objdir(args=args, pass_thru=True, ensure_exit_code=False)
if res != 0:
return 1
if ide == 'androidstudio' or 'intellij':
res = self._mach_context.commands.dispatch('package', self._mach_context)
if res != 0:
return 1
res = self._mach_context.commands.dispatch('gradle-install', self._mach_context)
if res != 0:
return 1
else:
# Generate or refresh the IDE backend.
python = self.virtualenv_manager.python_path
config_status = os.path.join(self.topobjdir, 'config.status')
args = [python, config_status, '--backend=%s' % backend]
res = self._run_command_in_objdir(args=args, pass_thru=True, ensure_exit_code=False)
if res != 0:
return 1
if backend == 'CppEclipse':
if ide == 'eclipse':
eclipse_workspace_dir = self.get_eclipse_workspace_path()
process = subprocess.check_call(['eclipse', '-data', eclipse_workspace_dir])
elif backend == 'VisualStudio':
elif ide == 'visualstudio':
visual_studio_workspace_dir = self.get_visualstudio_workspace_path()
process = subprocess.check_call(['explorer.exe', visual_studio_workspace_dir])
elif ide == 'androidstudio' or ide == 'intellij':
gradle_dir = None
if self.is_gradle_project_already_imported():
gradle_dir = self.get_gradle_project_path()
else:
gradle_dir = self.get_gradle_import_path()
process = subprocess.check_call(studio + [gradle_dir])
def get_eclipse_workspace_path(self):
return CppEclipseBackend.get_workspace_path(self.topsrcdir, self.topobjdir)
@ -73,3 +109,31 @@ class MachCommands(MachCommandBase):
def get_visualstudio_workspace_path(self):
return os.path.join(self.topobjdir, 'msvc', 'mozilla.sln')
def get_gradle_project_path(self):
return os.path.join(self.topobjdir, 'mobile', 'android', 'gradle')
def get_gradle_import_path(self):
return os.path.join(self.get_gradle_project_path(), 'build.gradle')
def is_gradle_project_already_imported(self):
gradle_project_path = os.path.join(self.get_gradle_project_path(), '.idea')
return os.path.exists(gradle_project_path)
def get_mac_ide_preferences(self, ide):
if sys.platform == 'darwin':
if ide == 'androidstudio':
return ['/Applications/Android Studio.app']
else:
return [
'/Applications/IntelliJ IDEA 14 EAP.app',
'/Applications/IntelliJ IDEA 14.app',
'/Applications/IntelliJ IDEA 14 CE EAP.app',
'/Applications/IntelliJ IDEA 14 CE.app']
def print_ide_error(self, ide):
if ide == 'androidstudio':
print('Android Studio is not installed in your PATH.')
print('You can generate a command-line launcher from Android Studio->Tools->Create Command-line launcher with script name \'studio\'')
elif ide == 'intellij':
print('IntelliJ is not installed in your PATH.')
print('You can generate a command-line launcher from IntelliJ IDEA->Tools->Create Command-line launcher with script name \'idea\'')

View File

@ -19,7 +19,6 @@ from __future__ import unicode_literals
import os
from collections import OrderedDict
from contextlib import contextmanager
from mozbuild.util import (
HierarchicalStringList,
HierarchicalStringListWithFlagsFactory,
@ -31,6 +30,7 @@ from mozbuild.util import (
StrictOrderingOnAppendList,
StrictOrderingOnAppendListWithFlagsFactory,
TypedList,
TypedNamedTuple,
)
import mozpack.path as mozpath
from types import FunctionType
@ -167,7 +167,6 @@ class Context(KeyedDefaultDict):
def _factory(self, key):
"""Function called when requesting a missing key."""
defaults = self._allowed_variables.get(key)
if not defaults:
raise KeyError('global_ns', 'get_unknown', key)
@ -396,6 +395,108 @@ def ContextDerivedTypedList(type, base_class=List):
return _TypedList
BugzillaComponent = TypedNamedTuple('BugzillaComponent',
[('product', unicode), ('component', unicode)])
class Files(SubContext):
"""Metadata attached to files.
It is common to want to annotate files with metadata, such as which
Bugzilla component tracks issues with certain files. This sub-context is
where we stick that metadata.
The argument to this sub-context is a file matching pattern that is applied
against the host file's directory. If the pattern matches a file whose info
is currently being sought, the metadata attached to this instance will be
applied to that file.
Patterns are collections of filename characters with ``/`` used as the
directory separate (UNIX-style paths) and ``*`` and ``**`` used to denote
wildcard matching.
Patterns without the ``*`` character are literal matches and will match at
most one entity.
Patterns with ``*`` or ``**`` are wildcard matches. ``*`` matches files
within a single directory. ``**`` matches files across several directories.
Here are some examples:
``foo.html``
Will match only the ``foo.html`` file in the current directory.
``*.jsm``
Will match all ``.jsm`` files in the current directory.
``**/*.cpp``
Will match all ``.cpp`` files in this and all child directories.
``foo/*.css``
Will match all ``.css`` files in the ``foo/`` directory.
``bar/*``
Will match all files in the ``bar/`` directory but not any files in
child directories of ``bar/``, such as ``bar/dir1/baz``.
``baz/**``
Will match all files in the ``baz/`` directory and all directories
underneath.
"""
VARIABLES = {
'BUG_COMPONENT': (BugzillaComponent, tuple,
"""The bug component that tracks changes to these files.
Values are a 2-tuple of unicode describing the Bugzilla product and
component. e.g. ``('Core', 'Build Config')``.
""", None),
'FINAL': (bool, bool,
"""Mark variable assignments as finalized.
During normal processing, values from newer Files contexts
overwrite previously set values. Last write wins. This behavior is
not always desired. ``FINAL`` provides a mechanism to prevent
further updates to a variable.
When ``FINAL`` is set, the value of all variables defined in this
context are marked as frozen and all subsequent writes to them
are ignored during metadata reading.
See :ref:`mozbuild_files_metadata_finalizing` for more info.
""", None),
}
def __init__(self, parent, pattern=None):
super(Files, self).__init__(parent)
self.pattern = pattern
self.finalized = set()
def __iadd__(self, other):
assert isinstance(other, Files)
for k, v in other.items():
# Ignore updates to finalized flags.
if k in self.finalized:
continue
# Only finalize variables defined in this instance.
if k == 'FINAL':
self.finalized |= set(other) - {'FINAL'}
continue
self[k] = v
return self
def asdict(self):
"""Return this instance as a dict with built-in data structures.
Call this to obtain an object suitable for serializing.
"""
d = {}
if 'BUG_COMPONENT' in self:
bc = self['BUG_COMPONENT']
d['bug_component'] = (bc.product, bc.component)
return d
# This defines functions that create sub-contexts.
#
# Values are classes that are SubContexts. The class name will be turned into
@ -405,6 +506,7 @@ def ContextDerivedTypedList(type, base_class=List):
# argument is always the parent context. It is up to each class to perform
# argument validation.
SUBCONTEXTS = [
Files,
]
for cls in SUBCONTEXTS:

View File

@ -74,7 +74,10 @@ from .data import (
from .reader import SandboxValidationError
from .context import Context
from .context import (
Context,
SubContext,
)
class TreeMetadataEmitter(LoggingMixin):
@ -135,6 +138,11 @@ class TreeMetadataEmitter(LoggingMixin):
raise Exception('Unhandled object of type %s' % type(o))
for out in output:
# Nothing in sub-contexts is currently of interest to us. Filter
# them all out.
if isinstance(out, SubContext):
continue
if isinstance(out, Context):
# Keep all contexts around, we will need them later.
contexts[out.objdir] = out

View File

@ -4,13 +4,17 @@
from __future__ import print_function, unicode_literals
from collections import defaultdict
from mach.decorators import (
CommandArgument,
CommandProvider,
Command
Command,
SubCommand,
)
from mozbuild.base import MachCommandBase
import mozpack.path as mozpath
@CommandProvider
@ -72,3 +76,49 @@ class MozbuildFileCommands(MachCommandBase):
print(line)
return 0
@Command('file-info', category='build-dev',
description='Query for metadata about files.')
def file_info(self):
pass
@SubCommand('file-info', 'bugzilla-component',
'Show Bugzilla component info for files listed.')
@CommandArgument('paths', nargs='+',
help='Paths whose data to query')
def file_info_bugzilla(self, paths):
components = defaultdict(set)
for p, m in self._get_files_info(paths).items():
components[m.get('BUG_COMPONENT')].add(p)
for component, files in sorted(components.items(), key=lambda x: (x is None, x)):
print('%s :: %s' % (component.product, component.component) if component else 'UNKNOWN')
for f in sorted(files):
print(' %s' % f)
@SubCommand('file-info', 'missing-bugzilla',
'Show files missing Bugzilla component info')
@CommandArgument('paths', nargs='+',
help='Paths whose data to query')
def file_info_missing_bugzilla(self, paths):
for p, m in sorted(self._get_files_info(paths).items()):
if 'BUG_COMPONENT' not in m:
print(p)
def _get_reader(self):
from mozbuild.frontend.reader import BuildReader
config = self.config_environment
return BuildReader(config)
def _get_files_info(self, paths):
relpaths = []
for p in paths:
a = mozpath.abspath(p)
if not mozpath.basedir(a, [self.topsrcdir]):
print('path is not inside topsrcdir: %s' % p)
return 1
relpaths.append(mozpath.relpath(a, self.topsrcdir))
reader = self._get_reader()
return reader.files_info(relpaths)

View File

@ -29,10 +29,14 @@ import tokenize
import traceback
import types
from collections import OrderedDict
from collections import (
defaultdict,
OrderedDict,
)
from io import StringIO
from mozbuild.util import (
EmptyValue,
memoize,
ReadOnlyDefaultDict,
ReadOnlyDict,
@ -58,11 +62,13 @@ from .sandbox import (
from .context import (
Context,
ContextDerivedValue,
Files,
FUNCTIONS,
VARIABLES,
DEPRECATION_HINTS,
SPECIAL_VARIABLES,
SUBCONTEXTS,
SubContext,
TemplateContext,
)
@ -78,6 +84,48 @@ def log(logger, level, action, params, formatter):
logger.log(level, formatter, extra={'action': action, 'params': params})
class EmptyConfig(object):
"""A config object that is empty.
This config object is suitable for using with a BuildReader on a vanilla
checkout, without any existing configuration. The config is simply
bootstrapped from a top source directory path.
"""
class PopulateOnGetDict(ReadOnlyDefaultDict):
"""A variation on ReadOnlyDefaultDict that populates during .get().
This variation is needed because CONFIG uses .get() to access members.
Without it, None (instead of our EmptyValue types) would be returned.
"""
def get(self, key, default=None):
return self[key]
def __init__(self, topsrcdir):
self.topsrcdir = topsrcdir
self.topobjdir = ''
self.substs = self.PopulateOnGetDict(EmptyValue, {
# These 2 variables are used semi-frequently and it isn't worth
# changing all the instances.
b'MOZ_APP_NAME': b'empty',
b'MOZ_CHILD_PROCESS_NAME': b'empty',
# Set manipulations are performed within the moz.build files. But
# set() is not an exposed symbol, so we can't create an empty set.
b'NECKO_PROTOCOLS': set(),
# Needed to prevent js/src's config.status from loading.
b'JS_STANDALONE': b'1',
})
udict = {}
for k, v in self.substs.items():
if isinstance(v, str):
udict[k.decode('utf-8')] = v.decode('utf-8')
else:
udict[k] = v
self.substs_unicode = self.PopulateOnGetDict(EmptyValue, udict)
self.defines = self.substs
self.external_source_dir = None
def is_read_allowed(path, config):
"""Whether we are allowed to load a mozbuild file at the specified path.
@ -135,20 +183,25 @@ class MozbuildSandbox(Sandbox):
self.exports = set(exports.keys())
context.update(exports)
self.templates = self.metadata.setdefault('templates', {})
self.special_variables = self.metadata.setdefault('special_variables',
SPECIAL_VARIABLES)
self.functions = self.metadata.setdefault('functions', FUNCTIONS)
self.subcontext_types = self.metadata.setdefault('subcontexts',
SUBCONTEXTS)
def __getitem__(self, key):
if key in SPECIAL_VARIABLES:
return SPECIAL_VARIABLES[key][0](self._context)
if key in FUNCTIONS:
return self._create_function(FUNCTIONS[key])
if key in SUBCONTEXTS:
return self._create_subcontext(SUBCONTEXTS[key])
if key in self.special_variables:
return self.special_variables[key][0](self._context)
if key in self.functions:
return self._create_function(self.functions[key])
if key in self.subcontext_types:
return self._create_subcontext(self.subcontext_types[key])
if key in self.templates:
return self._create_template_function(self.templates[key])
return Sandbox.__getitem__(self, key)
def __setitem__(self, key, value):
if key in SPECIAL_VARIABLES or key in FUNCTIONS or key in SUBCONTEXTS:
if key in self.special_variables or key in self.functions or key in self.subcontext_types:
raise KeyError()
if key in self.exports:
self._context[key] = value
@ -356,12 +409,20 @@ class MozbuildSandbox(Sandbox):
func, code, path = template
def template_function(*args, **kwargs):
context = TemplateContext(VARIABLES, self._context.config)
context = TemplateContext(self._context._allowed_variables,
self._context.config)
context.add_source(self._context.current_path)
for p in self._context.all_paths:
context.add_source(p)
sandbox = MozbuildSandbox(context, {
sandbox = MozbuildSandbox(context, metadata={
# We should arguably set these defaults to something else.
# Templates, for example, should arguably come from the state
# of the sandbox from when the template was declared, not when
# it was instantiated. Bug 1137319.
'functions': self.metadata.get('functions', {}),
'special_variables': self.metadata.get('special_variables', {}),
'subcontexts': self.metadata.get('subcontexts', {}),
'templates': self.metadata.get('templates', {})
})
for k, v in inspect.getcallargs(func, *args, **kwargs).items():
@ -972,6 +1033,11 @@ class BuildReader(object):
sandbox.exec_file(path)
context.execution_time = time.time() - time_start
# Yield main context before doing any processing. This gives immediate
# consumers an opportunity to change state before our remaining
# processing is performed.
yield context
# We first collect directories populated in variables.
dir_vars = ['DIRS']
@ -1016,8 +1082,6 @@ class BuildReader(object):
context['DIRS'].append(mozpath.relpath(gyp_context.objdir, context.objdir))
sandbox.subcontexts.append(gyp_context)
yield context
for subcontext in sandbox.subcontexts:
yield subcontext
@ -1035,12 +1099,11 @@ class BuildReader(object):
mozpath.relpath(d, context.srcdir), var), context)
recurse_info[d] = {}
if 'templates' in sandbox.metadata:
recurse_info[d]['templates'] = dict(
sandbox.metadata['templates'])
if 'exports' in sandbox.metadata:
sandbox.recompute_exports()
recurse_info[d]['exports'] = dict(sandbox.metadata['exports'])
for key in sandbox.metadata:
if key == 'exports':
sandbox.recompute_exports()
recurse_info[d][key] = dict(sandbox.metadata[key])
for path, child_metadata in recurse_info.items():
child_path = path.join('moz.build')
@ -1062,3 +1125,159 @@ class BuildReader(object):
yield res
self._execution_stack.pop()
def _find_relevant_mozbuilds(self, paths):
"""Given a set of filesystem paths, find all relevant moz.build files.
We assume that a moz.build file in the directory ancestry of a given path
is relevant to that path. Let's say we have the following files on disk::
moz.build
foo/moz.build
foo/baz/moz.build
foo/baz/file1
other/moz.build
other/file2
If ``foo/baz/file1`` is passed in, the relevant moz.build files are
``moz.build``, ``foo/moz.build``, and ``foo/baz/moz.build``. For
``other/file2``, the relevant moz.build files are ``moz.build`` and
``other/moz.build``.
Returns a dict of input paths to a list of relevant moz.build files.
The root moz.build file is first and the leaf-most moz.build is last.
"""
root = self.config.topsrcdir
result = {}
@memoize
def exists(path):
return os.path.exists(path)
def itermozbuild(path):
subpath = ''
yield 'moz.build'
for part in mozpath.split(path):
subpath = mozpath.join(subpath, part)
yield mozpath.join(subpath, 'moz.build')
for path in sorted(paths):
path = mozpath.normpath(path)
if os.path.isabs(path):
if not mozpath.basedir(path, [root]):
raise Exception('Path outside topsrcdir: %s' % path)
path = mozpath.relpath(path, root)
result[path] = [p for p in itermozbuild(path)
if exists(mozpath.join(root, p))]
return result
def read_relevant_mozbuilds(self, paths):
"""Read and process moz.build files relevant for a set of paths.
For an iterable of relative-to-root filesystem paths ``paths``,
find all moz.build files that may apply to them based on filesystem
hierarchy and read those moz.build files.
The return value is a 2-tuple. The first item is a dict mapping each
input filesystem path to a list of Context instances that are relevant
to that path. The second item is a list of all Context instances. Each
Context instance is in both data structures.
"""
relevants = self._find_relevant_mozbuilds(paths)
topsrcdir = self.config.topsrcdir
# Source moz.build file to directories to traverse.
dirs = defaultdict(set)
# Relevant path to absolute paths of relevant contexts.
path_mozbuilds = {}
# There is room to improve this code (and the code in
# _find_relevant_mozbuilds) to better handle multiple files in the same
# directory. Bug 1136966 tracks.
for path, mbpaths in relevants.items():
path_mozbuilds[path] = [mozpath.join(topsrcdir, p) for p in mbpaths]
for i, mbpath in enumerate(mbpaths[0:-1]):
source_dir = mozpath.dirname(mbpath)
target_dir = mozpath.dirname(mbpaths[i + 1])
d = mozpath.normpath(mozpath.join(topsrcdir, mbpath))
dirs[d].add(mozpath.relpath(target_dir, source_dir))
# Exporting doesn't work reliably in tree traversal mode. Override
# the function to no-op.
functions = dict(FUNCTIONS)
def export(sandbox):
return lambda varname: None
functions['export'] = tuple([export] + list(FUNCTIONS['export'][1:]))
metadata = {
'functions': functions,
}
contexts = defaultdict(list)
all_contexts = []
for context in self.read_mozbuild(mozpath.join(topsrcdir, 'moz.build'),
self.config, metadata=metadata):
# Explicitly set directory traversal variables to override default
# traversal rules.
if not isinstance(context, SubContext):
for v in ('DIRS', 'GYP_DIRS', 'TEST_DIRS'):
context[v][:] = []
context['DIRS'] = sorted(dirs[context.main_path])
contexts[context.main_path].append(context)
all_contexts.append(context)
result = {}
for path, paths in path_mozbuilds.items():
result[path] = reduce(lambda x, y: x + y, (contexts[p] for p in paths), [])
return result, all_contexts
def files_info(self, paths):
"""Obtain aggregate data from Files for a set of files.
Given a set of input paths, determine which moz.build files may
define metadata for them, evaluate those moz.build files, and
apply file metadata rules defined within to determine metadata
values for each file requested.
Essentially, for each input path:
1. Determine the set of moz.build files relevant to that file by
looking for moz.build files in ancestor directories.
2. Evaluate moz.build files starting with the most distant.
3. Iterate over Files sub-contexts.
4. If the file pattern matches the file we're seeking info on,
apply attribute updates.
5. Return the most recent value of attributes.
"""
paths, _ = self.read_relevant_mozbuilds(paths)
r = {}
for path, ctxs in paths.items():
flags = Files(Context())
for ctx in ctxs:
if not isinstance(ctx, Files):
continue
relpath = mozpath.relpath(path, ctx.relsrcdir)
pattern = ctx.pattern
# Only do wildcard matching if the '*' character is present.
# Otherwise, mozpath.match will match directories, which we've
# arbitrarily chosen to not allow.
if pattern == relpath or \
('*' in pattern and mozpath.match(relpath, pattern)):
flags += ctx
r[path] = flags
return r

View File

@ -0,0 +1,2 @@
with Files('*'):
BUG_COMPONENT = 'bad value'

View File

@ -0,0 +1,4 @@
with Files('*.jsm'):
BUG_COMPONENT = ('Firefox', 'JS')
with Files('*.cpp'):
BUG_COMPONENT = ('Firefox', 'C++')

View File

@ -0,0 +1,3 @@
with Files('**/Makefile.in'):
BUG_COMPONENT = ('Core', 'Build Config')
FINAL = True

View File

@ -0,0 +1,2 @@
with Files('**'):
BUG_COMPONENT = ('Another', 'Component')

View File

@ -0,0 +1,2 @@
with Files('**'):
BUG_COMPONENT = ('default_product', 'default_component')

View File

@ -0,0 +1,2 @@
with Files('*'):
BUG_COMPONENT = ('Core', 'Build Config')

View File

@ -0,0 +1,5 @@
with Files('foo'):
BUG_COMPONENT = ('FooProduct', 'FooComponent')
with Files('bar'):
BUG_COMPONENT = ('BarProduct', 'BarComponent')

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