Merge mozilla-central to autoland

This commit is contained in:
Carsten "Tomcat" Book 2016-06-29 16:22:44 +02:00
commit 13ce7275a3
513 changed files with 8394 additions and 5663 deletions

1
aclocal.m4 vendored
View File

@ -32,7 +32,6 @@ builtin(include, build/autoconf/clang-plugin.m4)dnl
builtin(include, build/autoconf/alloc.m4)dnl
builtin(include, build/autoconf/ios.m4)dnl
builtin(include, build/autoconf/jemalloc.m4)dnl
builtin(include, build/autoconf/rust.m4)dnl
MOZ_PROG_CHECKMSYS()

View File

@ -215,17 +215,6 @@ int main(int argc, _CONST char* argv[])
(void)setsid();
#endif
int gotCounters;
#if defined(XP_UNIX)
struct rusage initialRUsage;
gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
#elif defined(XP_WIN)
IO_COUNTERS ioCounters;
gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
#else
#error "Unknown platform" // having this here keeps cppcheck happy
#endif
#ifdef HAS_DLL_BLOCKLIST
DllBlocklist_Initialize();
#endif
@ -251,32 +240,6 @@ int main(int argc, _CONST char* argv[])
return 255;
}
if (gotCounters) {
#if defined(XP_WIN)
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS,
int(ioCounters.ReadOperationCount));
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER,
int(ioCounters.ReadTransferCount / 1024));
IO_COUNTERS newIoCounters;
if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) {
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS,
int(newIoCounters.ReadOperationCount - ioCounters.ReadOperationCount));
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER,
int((newIoCounters.ReadTransferCount - ioCounters.ReadTransferCount) / 1024));
}
#elif defined(XP_UNIX)
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS,
int(initialRUsage.ru_majflt));
struct rusage newRUsage;
if (!getrusage(RUSAGE_SELF, &newRUsage)) {
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS,
int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
}
#else
#error "Unknown platform" // having this here keeps cppcheck happy
#endif
}
int result;
{
ScopedLogging log;

View File

@ -362,17 +362,6 @@ int main(int argc, char* argv[], char* envp[])
TriggerQuirks();
#endif
int gotCounters;
#if defined(XP_UNIX)
struct rusage initialRUsage;
gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
#elif defined(XP_WIN)
IO_COUNTERS ioCounters;
gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
#else
#error "Unknown platform" // having this here keeps cppcheck happy
#endif
nsIFile *xreDirectory;
#ifdef HAS_DLL_BLOCKLIST
@ -394,32 +383,6 @@ int main(int argc, char* argv[], char* envp[])
XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
if (gotCounters) {
#if defined(XP_WIN)
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS,
int(ioCounters.ReadOperationCount));
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER,
int(ioCounters.ReadTransferCount / 1024));
IO_COUNTERS newIoCounters;
if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) {
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS,
int(newIoCounters.ReadOperationCount - ioCounters.ReadOperationCount));
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER,
int((newIoCounters.ReadTransferCount - ioCounters.ReadTransferCount) / 1024));
}
#elif defined(XP_UNIX)
XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS,
int(initialRUsage.ru_majflt));
struct rusage newRUsage;
if (!getrusage(RUSAGE_SELF, &newRUsage)) {
XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS,
int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
}
#else
#error "Unknown platform" // having this here keeps cppcheck happy
#endif
}
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
XRE_EnableSameExecutableForContentProc();
#endif

View File

@ -229,7 +229,7 @@ var gFxAccounts = {
this.panelUIFooter.removeAttribute("fxaprofileimage");
this.panelUIAvatar.style.removeProperty("list-style-image");
let showErrorBadge = false;
if (!this._inCustomizationMode && userData) {
if (userData) {
// At this point we consider the user as logged-in (but still can be in an error state)
if (this.loginFailed) {
let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1);
@ -260,7 +260,7 @@ var gFxAccounts = {
}
let updateWithProfile = (profile) => {
if (!this._inCustomizationMode && profileInfoEnabled) {
if (profileInfoEnabled) {
if (profile.displayName) {
this.panelUILabel.setAttribute("label", profile.displayName);
}

View File

@ -987,7 +987,6 @@ var gHistorySwipeAnimation = {
let canvas = null;
TelemetryStopwatch.start("FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE");
try {
let browser = gBrowser.selectedBrowser;
let r = browser.getBoundingClientRect();
@ -1006,7 +1005,6 @@ var gHistorySwipeAnimation = {
ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
} finally {
TelemetryStopwatch.finish("FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE");
}
TelemetryStopwatch.start("FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE");

View File

@ -243,6 +243,10 @@ toolbar[customizing] > .overflow-button {
-moz-appearance: -moz-window-button-box;
}
#personal-bookmarks {
-moz-window-dragging: inherit;
}
%ifdef XP_MACOSX
#titlebar-fullscreen-button {
-moz-appearance: -moz-mac-fullscreen-button;
@ -286,8 +290,8 @@ toolbar[customizing] > .overflow-button {
}
#main-window[tabsintitlebar] #TabsToolbar,
#main-window[tabsintitlebar] #toolbar-menubar:not([autohide=true]),
#main-window[tabsintitlebar] #navigator-toolbox > toolbar:not(#toolbar-menubar):-moz-lwtheme {
#main-window[tabsintitlebar] #toolbar-menubar,
#main-window[tabsintitlebar] #navigator-toolbox > toolbar:-moz-lwtheme {
-moz-window-dragging: drag;
}
%endif
@ -360,16 +364,6 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
-moz-box-flex: 1;
}
/* Ensure that empty parts of the bookmarks container can be dragged on OSX, and on other OSes
* only when a lwtheme is in use. */
%ifdef XP_MACOSX
#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks {
%else
#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks:-moz-lwtheme {
%endif
-moz-window-dragging: drag;
}
#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
display: -moz-box;
}

View File

@ -6574,13 +6574,25 @@ var gIdentityHandler = {
delete this._identityBox;
return this._identityBox = document.getElementById("identity-box");
},
get _identityPopupMultiView () {
delete _identityPopupMultiView;
return document.getElementById("identity-popup-multiView");
},
get _identityPopupContentHosts () {
delete this._identityPopupContentHosts;
return this._identityPopupContentHosts = [...document.querySelectorAll(".identity-popup-headline.host")];
let selector = ".identity-popup-headline.host";
return this._identityPopupContentHosts = [
...this._identityPopupMultiView._mainView.querySelectorAll(selector),
...document.querySelectorAll(selector)
];
},
get _identityPopupContentHostless () {
delete this._identityPopupContentHostless;
return this._identityPopupContentHostless = [...document.querySelectorAll(".identity-popup-headline.hostless")];
let selector = ".identity-popup-headline.hostless";
return this._identityPopupContentHostless = [
...this._identityPopupMultiView._mainView.querySelectorAll(selector),
...document.querySelectorAll(selector)
];
},
get _identityPopupContentOwner () {
delete this._identityPopupContentOwner;
@ -6640,7 +6652,7 @@ var gIdentityHandler = {
},
toggleSubView(name, anchor) {
let view = document.getElementById("identity-popup-multiView");
let view = this._identityPopupMultiView;
if (view.showingSubView) {
view.showMainView();
} else {

View File

@ -5625,10 +5625,6 @@
return;
}
Services.telemetry.getHistogramById(aTab.closing ?
"FX_TAB_ANIM_CLOSE_MS" :
"FX_TAB_ANIM_OPEN_MS")
.add(Date.now() - aTab._animStartTime);
aTab._animStartTime = 0;
// Handle tab animation smoothness telemetry/logging of frame intervals and paint times

View File

@ -767,7 +767,6 @@ function assertMixedContentBlockingState(tabbrowser, states = {}) {
// Make sure the correct icon is visible in the Control Center.
// This logic is controlled with CSS, so this helps prevent regressions there.
let securityView = doc.getElementById("identity-popup-securityView");
let securityContent = doc.getElementById("identity-popup-security-content");
let securityViewBG = tabbrowser.ownerGlobal.getComputedStyle(securityView, "").
getPropertyValue("background-image");
let securityContentBG = tabbrowser.ownerGlobal.getComputedStyle(securityView, "").

View File

@ -18,6 +18,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
const kBookmarksFileName = "360sefav.db";
function copyToTempUTF8File(file, charset) {
let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
@ -95,7 +97,7 @@ function getHash(aStr) {
function Bookmarks(aProfileFolder) {
let file = aProfileFolder.clone();
file.append("360sefav.db");
file.append(kBookmarksFileName);
this._file = file;
}
@ -298,6 +300,23 @@ Qihoo360seProfileMigrator.prototype.getResources = function(aProfile) {
return resources.filter(r => r.exists);
};
Qihoo360seProfileMigrator.prototype.getLastUsedDate = function() {
let bookmarksPaths = this.sourceProfiles.map(({id}) => {
return OS.Path.join(this._usersDir.path, id, kBookmarksFileName);
});
if (!bookmarksPaths.length) {
return Promise.resolve(new Date(0));
}
let datePromises = bookmarksPaths.map(path => {
return OS.File.stat(path).catch(_ => null).then(info => {
return info ? info.lastModificationDate : 0;
});
});
return Promise.all(datePromises).then(dates => {
return new Date(Math.max.apply(Math, dates));
});
};
Qihoo360seProfileMigrator.prototype.classDescription = "360 Secure Browser Profile Migrator";
Qihoo360seProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=360se";
Qihoo360seProfileMigrator.prototype.classID = Components.ID("{d0037b95-296a-4a4e-94b2-c3d075d20ab1}");

View File

@ -8,13 +8,19 @@ this.EXPORTED_SYMBOLS = ["AutoMigrate"];
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
const kAutoMigrateStartedPref = "browser.migrate.automigrate-started";
const kAutoMigrateFinishedPref = "browser.migrate.automigrate-finished";
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const AutoMigrate = {
get resourceTypesToUse() {
let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
return BOOKMARKS | HISTORY | FORMDATA | PASSWORDS;
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
return BOOKMARKS | HISTORY | PASSWORDS;
},
/**
@ -25,35 +31,40 @@ const AutoMigrate = {
* failed for some reason.
*/
migrate(profileStartup, migratorKey, profileToMigrate) {
let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_SUCCEEDED");
histogram.add("initialized");
let histogram = Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_PROCESS_SUCCESS");
histogram.add(0);
let migrator = this.pickMigrator(migratorKey);
histogram.add("got-browser");
histogram.add(5);
profileToMigrate = this.pickProfile(migrator, profileToMigrate);
histogram.add("got-profile");
histogram.add(10);
let resourceTypes = migrator.getMigrateData(profileToMigrate, profileStartup);
if (!(resourceTypes & this.resourceTypesToUse)) {
throw new Error("No usable resources were found for the selected browser!");
}
histogram.add("got-data");
histogram.add(15);
let sawErrors = false;
let migrationObserver = function(subject, topic, data) {
if (topic == "Migration:ItemError") {
sawErrors = true;
} else if (topic == "Migration:Ended") {
histogram.add(sawErrors ? "finished-with-errors" : "finished");
histogram.add(25);
if (sawErrors) {
histogram.add(26);
}
Services.obs.removeObserver(migrationObserver, "Migration:Ended");
Services.obs.removeObserver(migrationObserver, "Migration:ItemError");
Services.prefs.setCharPref(kAutoMigrateFinishedPref, Date.now().toString());
}
};
Services.obs.addObserver(migrationObserver, "Migration:Ended", false);
Services.obs.addObserver(migrationObserver, "Migration:ItemError", false);
Services.prefs.setCharPref(kAutoMigrateStartedPref, Date.now().toString());
migrator.migrate(this.resourceTypesToUse, profileStartup, profileToMigrate);
histogram.add("migrate-called-without-exceptions");
histogram.add(20);
},
/**
@ -109,5 +120,59 @@ const AutoMigrate = {
}
return profiles ? profiles[0].id : null;
},
getUndoRange() {
let start, finish;
try {
start = parseInt(Services.prefs.getCharPref(kAutoMigrateStartedPref), 10);
finish = parseInt(Services.prefs.getCharPref(kAutoMigrateFinishedPref), 10);
} catch (ex) {
Cu.reportError(ex);
}
if (!finish || !start) {
return null;
}
return [new Date(start), new Date(finish)];
},
canUndo() {
if (!this.getUndoRange()) {
return Promise.resolve(false);
}
// Return a promise resolving to false if we're signed into sync, resolve
// to true otherwise.
let {fxAccounts} = Cu.import("resource://gre/modules/FxAccounts.jsm", {});
return fxAccounts.getSignedInUser().then(user => !user, () => Promise.resolve(true));
},
undo: Task.async(function* () {
if (!(yield this.canUndo())) {
throw new Error("Can't undo!");
}
yield PlacesUtils.bookmarks.eraseEverything();
// NB: we drop the start time of the migration for now. This is because
// imported history will always end up being 'backdated' to the actual
// visit time recorded by the browser from which we imported. As a result,
// a lower bound on this item doesn't really make sense.
// Note that for form data this could be different, but we currently don't
// support form data import from any non-Firefox browser, so it isn't
// imported from other browsers by the automigration code, nor do we
// remove it here.
let range = this.getUndoRange();
yield PlacesUtils.history.removeVisitsByFilter({
beginDate: new Date(0),
endDate: range[1]
});
try {
Services.logins.removeAllLogins();
} catch (ex) {
// ignore failure.
}
Services.prefs.clearUserPref("browser.migrate.automigrate-started");
Services.prefs.clearUserPref("browser.migrate.automigrate-finished");
}),
};

View File

@ -24,6 +24,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Console.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
@ -136,6 +138,27 @@ ChromeProfileMigrator.prototype.getResources =
return [];
};
ChromeProfileMigrator.prototype.getLastUsedDate =
function Chrome_getLastUsedDate() {
let datePromises = this.sourceProfiles.map(profile => {
let profileFolder = this._chromeUserDataFolder.clone();
let basePath = OS.Path.join(this._chromeUserDataFolder.path, profile.id);
let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(leafName => {
let path = OS.Path.join(basePath, leafName);
return OS.File.stat(path).catch(_ => null).then(info => {
return info ? info.lastModificationDate : 0;
});
});
return Promise.all(fileDatePromises).then(dates => {
return Math.max.apply(Math, dates);
});
});
return Promise.all(datePromises).then(dates => {
dates.push(0);
return new Date(Math.max.apply(Math, dates));
});
};
Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
get: function Chrome_sourceProfiles() {
if ("__sourceProfiles" in this)

View File

@ -4,10 +4,11 @@
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource:///modules/MSMigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@ -420,6 +421,34 @@ EdgeProfileMigrator.prototype.getResources = function() {
return resources.filter(r => r.exists);
};
EdgeProfileMigrator.prototype.getLastUsedDate = function() {
// Don't do this if we don't have a single profile (see the comment for
// sourceProfiles) or if we can't find the database file:
if (this.sourceProfiles !== null || !gEdgeDatabase) {
return Promise.resolve(new Date(0));
}
let logFilePath = OS.Path.join(gEdgeDatabase.parent.path, "LogFiles", "edb.log");
let dbPath = gEdgeDatabase.path;
let cookieMigrator = MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE);
let cookiePaths = cookieMigrator._cookiesFolders.map(f => f.path);
let datePromises = [logFilePath, dbPath, ... cookiePaths].map(path => {
return OS.File.stat(path).catch(_ => null).then(info => {
return info ? info.lastModificationDate : 0;
});
});
datePromises.push(new Promise(resolve => {
let typedURLs = new Map();
try {
typedURLs = MSMigrationUtils.getTypedURLs(kEdgeRegistryRoot);
} catch (ex) {}
let times = [0, ... typedURLs.values()];
resolve(Math.max.apply(Math, times));
}));
return Promise.all(datePromises).then(dates => {
return new Date(Math.max.apply(Math, dates));
});
};
/* Somewhat counterintuitively, this returns:
* - |null| to indicate "There is only 1 (default) profile" (on win10+)
* - |[]| to indicate "There are no profiles" (on <=win8.1) which will avoid using this migrator.

View File

@ -97,6 +97,13 @@ FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
return this._getResourcesInternal(sourceProfileDir, currentProfileDir, aProfile);
};
FirefoxProfileMigrator.prototype.getLastUsedDate = function() {
// We always pretend we're really old, so that we don't mess
// up the determination of which browser is the most 'recent'
// to import from.
return Promise.resolve(new Date(0));
};
FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileDir, currentProfileDir, aProfile) {
let getFileResource = function(aMigrationType, aFileNames) {
let files = [];

View File

@ -13,9 +13,10 @@ const kLoginsKey = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storag
const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource:///modules/MSMigrationUtils.jsm");
Cu.import("resource://gre/modules/LoginHelper.jsm");
@ -495,6 +496,26 @@ IEProfileMigrator.prototype.getResources = function IE_getResources() {
return resources.filter(r => r.exists);
};
IEProfileMigrator.prototype.getLastUsedDate = function IE_getLastUsedDate() {
let datePromises = ["Favs", "CookD"].map(dirId => {
let {path} = Services.dirsvc.get(dirId, Ci.nsIFile);
return OS.File.stat(path).catch(_ => null).then(info => {
return info ? info.lastModificationDate : 0;
});
});
datePromises.push(new Promise(resolve => {
let typedURLs = new Map();
try {
typedURLs = MSMigrationUtils.getTypedURLs("Software\\Microsoft\\Internet Explorer");
} catch (ex) {}
let dates = [0, ... typedURLs.values()];
resolve(Math.max.apply(Math, dates));
}));
return Promise.all(datePromises).then(dates => {
return new Date(Math.max.apply(Math, dates));
});
};
Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
get: function IE_get_sourceHomePageURL() {
let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,

View File

@ -137,6 +137,20 @@ this.MigratorPrototype = {
throw new Error("getResources must be overridden");
},
/**
* OVERRIDE in order to provide an estimate of when the last time was
* that somebody used the browser. It is OK that this is somewhat fuzzy -
* history may not be available (or be wiped or not present due to e.g.
* incognito mode).
*
* @return a Promise that resolves to the last used date.
*
* @note If not overridden, the promise will resolve to the unix epoch.
*/
getLastUsedDate() {
return Promise.resolve(new Date(0));
},
/**
* OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now,
* that is just the Firefox migrator, see bug 737381). Default: false.

View File

@ -9,10 +9,11 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
@ -640,6 +641,24 @@ SafariProfileMigrator.prototype.getResources = function SM_getResources() {
return resources;
};
SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate() {
let profileDir;
if (AppConstants.platform == "macosx") {
profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
} else {
profileDir = FileUtils.getDir("AppData", ["Apple Computer", "Safari"], false);
}
let datePromises = ["Bookmarks.plist", "History.plist"].map(file => {
let path = OS.Path.join(profileDir.path, file);
return OS.File.stat(path).catch(_ => null).then(info => {
return info ? info.lastModificationDate : 0;
});
});
return Promise.all(datePromises).then(dates => {
return new Date(Math.max.apply(Math, dates));
});
};
Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", {
get: function get_mainPreferencesPropertyList() {
if (this._mainPreferencesPropertyList === undefined) {

View File

@ -83,10 +83,10 @@ var MigrationWizard = {
this._wiz.canRewind = false;
var selectedMigrator = null;
this._availableMigrators = [];
// Figure out what source apps are are available to import from:
var group = document.getElementById("importSourceGroup");
var availableMigratorCount = 0;
for (var i = 0; i < group.childNodes.length; ++i) {
var migratorKey = group.childNodes[i].id;
if (migratorKey != "nothing") {
@ -96,7 +96,7 @@ var MigrationWizard = {
// one, or if it is the migrator that was passed to us.
if (!selectedMigrator || this._source == migratorKey)
selectedMigrator = group.childNodes[i];
availableMigratorCount++;
this._availableMigrators.push([migratorKey, migrator]);
} else {
// Hide this option
group.childNodes[i].hidden = true;
@ -105,7 +105,7 @@ var MigrationWizard = {
}
if (this.isInitialMigration) {
Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT")
.add(availableMigratorCount);
.add(this._availableMigrators.length);
let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser();
// This will record 0 for unknown default browser IDs.
defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser);
@ -448,6 +448,9 @@ var MigrationWizard = {
label.removeAttribute("style");
break;
case "Migration:Ended":
if (this.isInitialMigration) {
this.reportDataRecencyTelemetry();
}
if (this._autoMigrate) {
Services.telemetry.getKeyedHistogramById("FX_MIGRATION_HOMEPAGE_IMPORTED")
.add(this._source, !!this._newHomePage);
@ -532,5 +535,31 @@ var MigrationWizard = {
this._wiz.getButton("cancel").disabled = true;
this._wiz.canRewind = false;
this._listItems("doneItems");
}
},
reportDataRecencyTelemetry() {
let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_DATA_RECENCY");
let lastUsedPromises = [];
for (let [key, migrator] of this._availableMigrators) {
// No block-scoped let in for...of loop conditions, so get the source:
let localKey = key;
lastUsedPromises.push(migrator.getLastUsedDate().then(date => {
const ONE_YEAR = 24 * 365;
let diffInHours = Math.round((Date.now() - date) / (60 * 60 * 1000));
if (diffInHours > ONE_YEAR) {
diffInHours = ONE_YEAR;
}
histogram.add(localKey, diffInHours);
return [localKey, diffInHours];
}));
}
Promise.all(lastUsedPromises).then(migratorUsedTimeDiff => {
// Sort low to high.
migratorUsedTimeDiff.sort(([keyA, diffA], [keyB, diffB]) => diffA - diffB);
let usedMostRecentBrowser = migratorUsedTimeDiff.length && this._source == migratorUsedTimeDiff[0][0];
let usedRecentBrowser =
Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_USED_RECENT_BROWSER");
usedRecentBrowser.add(this._source, usedMostRecentBrowser);
});
},
};

View File

@ -43,6 +43,12 @@ interface nsIBrowserProfileMigrator : nsISupports
*/
unsigned short getMigrateData(in jsval aProfile, in boolean aDoingStartup);
/**
* Get the last time data from this browser was modified
* @return a promise that resolves to a JS Date object
*/
jsval getLastUsedDate();
/**
* Whether or not there is any data that can be imported from this
* browser (i.e. whether or not it is installed, and there exists

View File

@ -1,9 +1,14 @@
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
Cu.import("resource://testing-common/PlacesTestUtils.jsm");
let AutoMigrateBackstage = Cu.import("resource:///modules/AutoMigrate.jsm");
let gShimmedMigratorKeyPicker = null;
let gShimmedMigrator = null;
const kUsecPerMin = 60 * 1000000;
// This is really a proxy on MigrationUtils, but if we specify that directly,
// we get in trouble because the object itself is frozen, and Proxies can't
// return a different value to an object when directly proxying a frozen
@ -112,8 +117,133 @@ add_task(function* checkIntegration() {
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
"getMigrateData called with 'null' as a profile");
let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | FORMDATA | PASSWORDS;
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
"getMigrateData called with 'null' as a profile");
"migrate called with 'null' as a profile");
});
/**
* Test the undo preconditions and a no-op undo in the automigrator.
*/
add_task(function* checkUndoPreconditions() {
gShimmedMigrator = {
get sourceProfiles() {
do_print("Read sourceProfiles");
return null;
},
getMigrateData(profileToMigrate) {
this._getMigrateDataArgs = profileToMigrate;
return Ci.nsIBrowserProfileMigrator.BOOKMARKS;
},
migrate(types, startup, profileToMigrate) {
this._migrateArgs = [types, startup, profileToMigrate];
TestUtils.executeSoon(function() {
Services.obs.notifyObservers(null, "Migration:Ended", undefined);
});
},
};
gShimmedMigratorKeyPicker = function() {
return "gobbledygook";
};
AutoMigrate.migrate("startup");
let migrationFinishedPromise = TestUtils.topicObserved("Migration:Ended");
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
"getMigrateData called with 'null' as a profile");
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
"migrate called with 'null' as a profile");
yield migrationFinishedPromise;
Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-started"),
"Should have set start time pref");
Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-finished"),
"Should have set finish time pref");
Assert.ok((yield AutoMigrate.canUndo()), "Should be able to undo migration");
let [beginRange, endRange] = AutoMigrate.getUndoRange();
let stringRange = `beginRange: ${beginRange}; endRange: ${endRange}`;
Assert.ok(beginRange <= endRange,
"Migration should have started before or when it ended " + stringRange);
yield AutoMigrate.undo();
Assert.ok(true, "Should be able to finish an undo cycle.");
});
/**
* Fake a migration and then try to undo it to verify all data gets removed.
*/
add_task(function* checkUndoRemoval() {
let startTime = "" + Date.now();
Services.prefs.setCharPref("browser.migrate.automigrate-started", startTime);
// Insert a login and check that that worked.
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
login.init("www.mozilla.org", "http://www.mozilla.org", null, "user", "pass", "userEl", "passEl");
Services.logins.addLogin(login);
let storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
"http://www.mozilla.org", null);
Assert.equal(storedLogins.length, 1, "Should have 1 login");
// Insert a bookmark and check that we have exactly 1 bookmark for that URI.
yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: "http://www.example.org/",
title: "Some example bookmark",
});
let bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
Assert.ok(bookmark, "Should have a bookmark before undo");
Assert.equal(bookmark.title, "Some example bookmark", "Should have correct bookmark before undo.");
// Insert 2 history visits - one in the current migration time, one from before.
let now_uSec = Date.now() * 1000;
let visitedURI = Services.io.newURI("http://www.example.com/", null, null);
yield PlacesTestUtils.addVisits([
{uri: visitedURI, visitDate: now_uSec},
{uri: visitedURI, visitDate: now_uSec - 100 * kUsecPerMin},
]);
// Verify that both visits get reported.
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
let query = PlacesUtils.history.getNewQuery();
query.uri = visitedURI;
let visits = PlacesUtils.history.executeQuery(query, opts);
visits.root.containerOpen = true;
Assert.equal(visits.root.childCount, 2, "Should have 2 visits");
// Clean up:
visits.root.containerOpen = false;
// Now set finished pref:
let endTime = "" + Date.now();
Services.prefs.setCharPref("browser.migrate.automigrate-finished", endTime);
// Verify that we can undo, then undo:
Assert.ok(yield AutoMigrate.canUndo(), "Should be possible to undo migration");
yield AutoMigrate.undo();
// Check that the undo removed the history visits:
visits = PlacesUtils.history.executeQuery(query, opts);
visits.root.containerOpen = true;
Assert.equal(visits.root.childCount, 0, "Should have no more visits");
visits.root.containerOpen = false;
// Check that the undo removed the bookmarks:
bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
Assert.ok(!bookmark, "Should have no bookmarks after undo");
// Check that the undo removed the passwords:
storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
"http://www.mozilla.org", null);
Assert.equal(storedLogins.length, 0, "Should have no logins");
// Finally check prefs got cleared:
Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-started"),
"Should no longer have pref for migration start time.");
Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-finished"),
"Should no longer have pref for migration finish time.");
});

View File

@ -68,7 +68,7 @@
<menuitem value="x-western" label="&font.langGroup.latin;"/>
<menuitem value="x-mlym" label="&font.langGroup.malayalam;"/>
<menuitem value="x-math" label="&font.langGroup.math;"/>
<menuitem value="x-orya" label="&font.langGroup.oriya;"/>
<menuitem value="x-orya" label="&font.langGroup.odia;"/>
<menuitem value="x-sinh" label="&font.langGroup.sinhala;"/>
<menuitem value="x-tamil" label="&font.langGroup.tamil;"/>
<menuitem value="x-telu" label="&font.langGroup.telugu;"/>

View File

@ -136,9 +136,9 @@
</hbox>
</vbox>
<vbox>
<description>&doNotTrack.pre.label;<label
class="text-link" id="doNotTrackSettings" href="#"
>&doNotTrack.settings.label;</label>&doNotTrack.post.label;</description>
<description>&doNotTrack.pre.label;<html:a
class="inline-link" id="doNotTrackSettings" href="#"
>&doNotTrack.settings.label;</html:a>&doNotTrack.post.label;</description>
</vbox>
</groupbox>
@ -165,11 +165,11 @@
<vbox flex="1">
<description>&rememberDescription.label;</description>
<separator class="thin"/>
<description>&rememberActions.pre.label;<label
class="text-link" id="historyRememberClear" href="#"
>&rememberActions.clearHistory.label;</label>&rememberActions.middle.label;<label
class="text-link" id="historyRememberCookies" href="#"
>&rememberActions.removeCookies.label;</label>&rememberActions.post.label;</description>
<description>&rememberActions.pre.label;<html:a
class="inline-link" id="historyRememberClear" href="#"
>&rememberActions.clearHistory.label;</html:a>&rememberActions.middle.label;<html:a
class="inline-link" id="historyRememberCookies" href="#"
>&rememberActions.removeCookies.label;</html:a>&rememberActions.post.label;</description>
</vbox>
</hbox>
</vbox>
@ -178,9 +178,9 @@
<vbox flex="1">
<description>&dontrememberDescription.label;</description>
<separator class="thin"/>
<description>&dontrememberActions.pre.label;<label
class="text-link" id="historyDontRememberClear" href="#"
>&dontrememberActions.clearHistory.label;</label>&dontrememberActions.post.label;</description>
<description>&dontrememberActions.pre.label;<html:a
class="inline-link" id="historyDontRememberClear" href="#"
>&dontrememberActions.clearHistory.label;</html:a>&dontrememberActions.post.label;</description>
</vbox>
</hbox>
</vbox>

View File

@ -344,25 +344,29 @@ var Agent = {
let exn = null;
let iterator = new File.DirectoryIterator(path);
if (!iterator.exists()) {
return;
}
for (let entry in iterator) {
if (entry.isDir) {
continue;
try {
if (!iterator.exists()) {
return;
}
if (!prefix || entry.name.startsWith(prefix)) {
try {
File.remove(entry.path);
} catch (ex) {
// Don't stop immediately
exn = exn || ex;
for (let entry in iterator) {
if (entry.isDir) {
continue;
}
if (!prefix || entry.name.startsWith(prefix)) {
try {
File.remove(entry.path);
} catch (ex) {
// Don't stop immediately
exn = exn || ex;
}
}
}
}
if (exn) {
throw exn;
if (exn) {
throw exn;
}
} finally {
iterator.close();
}
},
};

View File

@ -1,9 +1,8 @@
MOZ_AUTOMATION_BUILD_SYMBOLS=0
MOZ_AUTOMATION_L10N_CHECK=0
NO_CACHE=1
. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
. "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
. "$topsrcdir/build/mozconfig.common.override"
ac_add_options --enable-artifact-builds
unset CC

View File

@ -876,6 +876,25 @@ var WindowListener = {
*/
_browserSharePaused: false,
/**
* Stores details about the last notification.
*
* @type {Object}
*/
_lastNotification: {},
/**
* Used to determine if the browser sharing info bar is currently being
* shown or not.
*/
_showingBrowserSharingInfoBar: function _showingBrowserSharingInfoBar() {
var browser = gBrowser.selectedBrowser;
var box = gBrowser.getNotificationBox(browser);
var notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
return !!notification;},
/**
* Shows an infobar notification at the top of the browser window that warns
* the user that their browser tabs are being broadcasted through the current
@ -884,10 +903,25 @@ var WindowListener = {
* @return {void}
*/
_maybeShowBrowserSharingInfoBar: function _maybeShowBrowserSharingInfoBar(currentRoomToken) {var _this13 = this;
this._hideBrowserSharingInfoBar();
var participantsCount = this.LoopRooms.getNumParticipants(currentRoomToken);
if (this._showingBrowserSharingInfoBar()) {
// When we first open the room, there will be one or zero partipicants
// in the room. The notification box changes when there's more than one,
// so work that out here.
var notAlone = participantsCount > 1;
var previousNotAlone = this._lastNotification.participantsCount <= 1;
// If we're not actually changing the notification bar, then don't
// re-display it. This avoids the bar sliding in twice.
if (notAlone !== previousNotAlone &&
this._browserSharePaused === this._lastNotification.paused) {
return;}
this._hideBrowserSharingInfoBar();}
var initStrings = this._setInfoBarStrings(participantsCount > 1, this._browserSharePaused);
var box = gBrowser.getNotificationBox();
@ -935,7 +969,10 @@ var WindowListener = {
bar.classList.toggle("paused", !!this._browserSharePaused);
// Keep showing the notification bar until the user explicitly closes it.
bar.persistence = -1;},
bar.persistence = -1;
this._lastNotification.participantsCount = participantsCount;
this._lastNotification.paused = this._browserSharePaused;},
/**

View File

@ -1105,19 +1105,19 @@ var MozLoopServiceInternal = {
// It's common for unit tests to overload Chat.open, so check if we actually
// got a DOM node back.
} else if (chatboxInstance.setAttribute) {
// Set properties that influence visual appearance of the chatbox right
// away to circumvent glitches.
chatboxInstance.setAttribute("customSize", "loopDefault");
chatboxInstance.parentNode.setAttribute("customSize", "loopDefault");
var buttons = "minimize,";
if (MozLoopService.getLoopPref("conversationPopOut.enabled")) {
buttons += "swap,";}
// Set properties that influence visual appearance of the chatbox right
// away to circumvent glitches.
chatboxInstance.setAttribute("customSize", "loopDefault");
chatboxInstance.parentNode.setAttribute("customSize", "loopDefault");
var buttons = "minimize,";
if (MozLoopService.getLoopPref("conversationPopOut.enabled")) {
buttons += "swap,";}
Chat.loadButtonSet(chatboxInstance, buttons + kChatboxHangupButton.id);
// Final fall-through in case a unit test overloaded Chat.open. Here we can
// immediately resolve the promise.
} else {
resolve(windowId);}});},
Chat.loadButtonSet(chatboxInstance, buttons + kChatboxHangupButton.id);
// Final fall-through in case a unit test overloaded Chat.open. Here we can
// immediately resolve the promise.
} else {
resolve(windowId);}});},

View File

@ -185,20 +185,11 @@ loop.roomViews = function (mozL10n) {
nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
publisherConfig: this.getDefaultPublisherConfig({
publishVideo: !this.state.videoMuted }) }));}
publishVideo: !this.state.videoMuted }) }));}},
// Now that we're ready to share, automatically start sharing a tab only
// if we're not already connected to the room via the sdk, e.g. not in the
// case a remote participant just left.
if (nextState.roomState === ROOM_STATES.SESSION_CONNECTED &&
!(this.state.roomState === ROOM_STATES.SESSION_CONNECTED ||
this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS)) {
this.props.dispatcher.dispatch(new sharedActions.StartBrowserShare());}},
/**
* User clicked on the "Leave" button.

View File

@ -370,44 +370,11 @@ describe("loop.roomViews", function () {
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.SetupStreamElements({
publisherConfig: {
fake: "config" } }));});
fake: "config" } }));});});
it("should dispatch a `StartBrowserShare` action when the SESSION_CONNECTED state is entered", function () {
activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
mountTestComponent();
activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.StartBrowserShare());});
it("should not dispatch a `StartBrowserShare` action when the previous state was HAS_PARTICIPANTS", function () {
activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
mountTestComponent();
activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
sinon.assert.notCalled(dispatcher.dispatch);});
it("should not dispatch a `StartBrowserShare` action when the previous state was SESSION_CONNECTED", function () {
activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
mountTestComponent();
activeRoomStore.setStoreState({
roomState: ROOM_STATES.SESSION_CONNECTED,
// Additional change to force an update.
screenSharingState: "fake" });
sinon.assert.notCalled(dispatcher.dispatch);});});
describe("#render", function () {
it("should set document.title to store.serverData.roomName", function () {

View File

@ -718,8 +718,16 @@ loop.store.ActiveRoomStore = function (mozL10n) {
this._sdkDriver.connectSession(actionData);
loop.request("AddConversationContext", this._storeState.windowId,
actionData.sessionId, "");},
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 may start off the sharing
// from within the listener.
loop.subscribe("BrowserSwitch", this._browserSharingListener);
loop.requestMulti(["AddConversationContext", this._storeState.windowId,
actionData.sessionId, ""],
["AddBrowserSharingListener", this.getStoreState().windowId]);},
/**
@ -780,7 +788,14 @@ loop.store.ActiveRoomStore = function (mozL10n) {
this.setStoreState({
remoteAudioEnabled: actionData.hasAudio,
remoteVideoEnabled: actionData.hasVideo,
remoteSrcMediaElement: actionData.srcMediaElement });},
remoteSrcMediaElement: actionData.srcMediaElement });
// We start browser sharing here so that it starts *after* the audio/video
// has connected. This is to attempt to help performance when a room is
// initially joined.
if (this._isDesktop) {
this.startBrowserShare();}},
@ -866,10 +881,9 @@ loop.store.ActiveRoomStore = function (mozL10n) {
if (Array.isArray(windowId)) {
windowId = windowId[0];}
if (!windowId) {
return;}
if (windowId.isError) {
// There was an error getting the windowId, so lets just reset things.
if (windowId && windowId.isError) {
console.error("Error getting the windowId: " + windowId.message);
this.dispatchAction(new sharedActions.ScreenSharingState({
state: SCREEN_SHARE_STATES.INACTIVE }));
@ -877,8 +891,27 @@ loop.store.ActiveRoomStore = function (mozL10n) {
return;}
// If there's no windowId, see if we've got one saved.
if (!windowId) {
// If we really don't have a window Id, then don't do anything.
if (!this._savedWindowId) {
return;}
windowId = this._savedWindowId;
delete this._savedWindowId;}
var screenSharingState = this.getStoreState().screenSharingState;
// If we're inactive, or screen sharing is paused, just save the windowId
// for when we're ready.
if (screenSharingState === SCREEN_SHARE_STATES.INACTIVE ||
this._storeState.sharingPaused) {
this._savedWindowId = windowId;
return;}
if (screenSharingState === SCREEN_SHARE_STATES.PENDING) {
// Screen sharing is still pending, so assume that we need to kick it off.
var options = {
@ -947,14 +980,10 @@ loop.store.ActiveRoomStore = function (mozL10n) {
state: SCREEN_SHARE_STATES.PENDING }));
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.
loop.request("AddBrowserSharingListener", this.getStoreState().windowId).
then(this._browserSharingListener);
loop.subscribe("BrowserSwitch", this._browserSharingListener);},
// This attempts to start the actual browser sharing - we assume we've
// already got a windowId due to having requested it before connecting the
// media.
this._handleSwitchBrowserShare();},
/**
@ -985,6 +1014,16 @@ loop.store.ActiveRoomStore = function (mozL10n) {
sharingPaused: !actionData.enabled });
// If we've un-paused screen sharing, but we haven't started sharing, then
// we need to start that off.
if (actionData.enabled &&
this._storeState.screenSharingState === SCREEN_SHARE_STATES.PENDING) {
this._handleSwitchBrowserShare();} else
{
// Otherwise just toggle the stream.
this._sdkDriver.toggleBrowserSharing(actionData.enabled);}
// If unpausing, check the context as it might have changed.
if (actionData.enabled) {
this._checkTabContext();}},

View File

@ -41,8 +41,7 @@ loop.OTSdkDriver = function () {
this.dispatcher.register(this, [
"setupStreamElements",
"setMute",
"toggleBrowserSharing"]);
"setMute"]);
// Set loop.debug.sdk to true in the browser, or in standalone:
@ -237,11 +236,12 @@ loop.OTSdkDriver = function () {
/**
* Paused or resumes an active screenshare session as appropriate.
*
* @param {sharedActions.ToggleBrowserSharing} actionData The data associated with the
* action. See action.js.
* @param {Boolean} enabled True if browser sharing should be enabled.
*/
toggleBrowserSharing: function toggleBrowserSharing(actionData) {
this.screenshare.publishVideo(actionData.enabled);},
toggleBrowserSharing: function toggleBrowserSharing(enabled) {
if (this.screenshare) {
this.screenshare.publishVideo(enabled);}},
/**
@ -537,10 +537,8 @@ loop.OTSdkDriver = function () {
_handleRemoteScreenShareCreated: function _handleRemoteScreenShareCreated(stream) {
// Let the stores know first if the screen sharing is paused or not so
// they can update the display properly
if (!stream[STREAM_PROPERTIES.HAS_VIDEO]) {
this.dispatcher.dispatch(new sharedActions.VideoScreenStreamChanged({
hasVideo: false }));}
this.dispatcher.dispatch(new sharedActions.VideoScreenStreamChanged({
hasVideo: stream[STREAM_PROPERTIES.HAS_VIDEO] }));
// Let the stores know so they can update the display if needed.

View File

@ -361,17 +361,17 @@ if (inChrome) {
// MediaStreamTrack is the older version of the API, implemented originally
// by Google Chrome.
} else if ("MediaStreamTrack" in rootObject &&
"getSources" in rootObject.MediaStreamTrack) {
rootObject.MediaStreamTrack.getSources(function (result) {
function checkForInput(device) {
return device.kind === "audio" || device.kind === "video";}
"getSources" in rootObject.MediaStreamTrack) {
rootObject.MediaStreamTrack.getSources(function (result) {
function checkForInput(device) {
return device.kind === "audio" || device.kind === "video";}
callback(result.some(checkForInput));});} else
callback(result.some(checkForInput));});} else
{
// We don't know, so assume true.
callback(true);}}
{
// We don't know, so assume true.
callback(true);}}

View File

@ -51,6 +51,7 @@ describe("loop.store.ActiveRoomStore", function () {
retryPublishWithoutVideo: sinon.stub(),
startScreenShare: sinon.stub(),
switchAcquiredWindow: sinon.stub(),
toggleBrowserSharing: sinon.stub(),
endScreenShare: sinon.stub().returns(true) };
@ -195,7 +196,12 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(loop, "unsubscribe");
// Setup the listener.
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
// Now simulate room failure.
store.roomFailure(new sharedActions.RoomFailure({
@ -1206,7 +1212,12 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(loop, "unsubscribe");
// Setup the listener.
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
// Now simulate connection failure.
store.connectionFailure(connectionFailureAction);
@ -1337,7 +1348,25 @@ describe("loop.store.ActiveRoomStore", function () {
expect(store.getStoreState().localVideoEnabled).eql(false);
expect(store.getStoreState().remoteVideoEnabled).eql(true);
expect(store.getStoreState().remoteAudioEnabled).eql(true);});});
expect(store.getStoreState().remoteAudioEnabled).eql(true);});
it("should call startBrowserShare when is desktop", function () {
sandbox.stub(store, "startBrowserShare");
store._isDesktop = true;
store.setStoreState({
localVideoEnabled: false,
remoteVideoEnabled: false });
store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
hasAudio: true,
hasVideo: true,
isLocal: false,
srcMediaElement: fakeStreamElement }));
sinon.assert.calledOnce(store.startBrowserShare);});});
@ -1494,18 +1523,7 @@ describe("loop.store.ActiveRoomStore", function () {
describe("#startBrowserShare", function () {
var getSelectedTabMetadataStub;
beforeEach(function () {
getSelectedTabMetadataStub = sinon.stub();
LoopMochaUtils.stubLoopRequest({
GetSelectedTabMetadata: getSelectedTabMetadataStub.returns({
title: "fakeTitle",
favicon: "fakeFavicon",
url: "http://www.fakeurl.com" }) });
store.setStoreState({
roomState: ROOM_STATES.JOINED,
roomToken: "fakeToken",
@ -1519,7 +1537,8 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(console, "error");});
sandbox.stub(console, "error");
sandbox.stub(store, "_handleSwitchBrowserShare");});
afterEach(function () {
@ -1556,13 +1575,140 @@ describe("loop.store.ActiveRoomStore", function () {
it("should add a browser sharing listener for tab sharing", function () {
it("should call _handleSwitchBrowserShare", function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
sinon.assert.calledOnce(requestStubs.AddBrowserSharingListener);});
sinon.assert.calledOnce(store._handleSwitchBrowserShare);});});
it("should invoke the SDK driver with the correct options for tab sharing", function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
describe("Screen share Events", function () {
it("should call _handleSwitchBrowserShare", function () {
sandbox.stub(store, "_handleSwitchBrowserShare");
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
LoopMochaUtils.publish("BrowserSwitch", 72);
sinon.assert.calledOnce(store._handleSwitchBrowserShare);});});
describe("#_handleSwitchBrowserShare", function () {
var getSelectedTabMetadataStub;
beforeEach(function () {
getSelectedTabMetadataStub = sinon.stub();
LoopMochaUtils.stubLoopRequest({
GetSelectedTabMetadata: getSelectedTabMetadataStub.returns({
title: "fakeTitle",
favicon: "fakeFavicon",
url: "http://www.fakeurl.com" }) });
store.setStoreState({
roomState: ROOM_STATES.JOINED,
roomToken: "fakeToken",
screenSharingState: SCREEN_SHARE_STATES.ACTIVE,
sessionToken: "1627384950",
participants: [{
displayName: "Owner",
owner: true },
{
displayName: "Guest",
owner: false }] });
// Stub to prevent errors surfacing in the console.
sandbox.stub(console, "error");});
afterEach(function () {
store.endScreenShare();});
it("should log an error in the console", function () {
var err = new Error("foo");
err.isError = true;
store._handleSwitchBrowserShare(err);
sinon.assert.calledOnce(console.error);});
it("should end the screen sharing session when the listener receives an error", function () {
var err = new Error("foo");
err.isError = true;
store._handleSwitchBrowserShare(err);
// The dispatcher was already called once in beforeEach().
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.ScreenSharingState({
state: SCREEN_SHARE_STATES.INACTIVE }));
sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);});
it("should save the windowId when the state is INACTIVE", function () {
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.INACTIVE });
store._handleSwitchBrowserShare(72);
expect(store._savedWindowId, 72);});
it("should not do anything else when the state is INACTIVE", function () {
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.INACTIVE });
store._handleSwitchBrowserShare(72);
sinon.assert.notCalled(dispatcher.dispatch);
sinon.assert.notCalled(fakeSdkDriver.startScreenShare);
sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);});
it("should save the windowId when sharing is paused", function () {
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.ACTIVE,
sharingPaused: true });
store._handleSwitchBrowserShare(72);
expect(store._savedWindowId, 72);});
it("should not do anything else when the state is paused", function () {
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.ACTIVE,
sharingPaused: true });
store._handleSwitchBrowserShare(72);
sinon.assert.notCalled(dispatcher.dispatch);
sinon.assert.notCalled(fakeSdkDriver.startScreenShare);
sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);});
it("should invoke the SDK driver with the correct options if the state is pending", function () {
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.PENDING });
store._handleSwitchBrowserShare(42);
sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
videoSource: "browser",
@ -1573,13 +1719,31 @@ describe("loop.store.ActiveRoomStore", function () {
it("should update the SDK driver when the state is active", function () {
store._handleSwitchBrowserShare(72);
sinon.assert.calledOnce(fakeSdkDriver.switchAcquiredWindow);
sinon.assert.calledWithExactly(fakeSdkDriver.switchAcquiredWindow, 72);});
it("should log an error if the state is unexpected", function () {
store.setStoreState({
screenSharingState: "invalid" });
store._handleSwitchBrowserShare(72);
sinon.assert.calledOnce(console.error);});
it("should request the new metadata when the browser being shared change", function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
store._handleSwitchBrowserShare(42);
clock.tick(500);
sinon.assert.calledOnce(getSelectedTabMetadataStub);
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch.getCall(1),
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomContext({
newRoomDescription: "fakeTitle",
newRoomThumbnail: "fakeFavicon",
@ -1591,13 +1755,14 @@ describe("loop.store.ActiveRoomStore", function () {
it("should process only one request", function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
// Simulates multiple requests.
LoopMochaUtils.publish("BrowserSwitch", 72);
LoopMochaUtils.publish("BrowserSwitch", 72);
store._handleSwitchBrowserShare(42);
store._handleSwitchBrowserShare(42);
clock.tick(500);
sinon.assert.calledThrice(getSelectedTabMetadataStub);
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch.getCall(1),
sinon.assert.calledTwice(getSelectedTabMetadataStub);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateRoomContext({
newRoomDescription: "fakeTitle",
newRoomThumbnail: "fakeFavicon",
@ -1612,11 +1777,12 @@ describe("loop.store.ActiveRoomStore", function () {
favicon: "fakeFavicon" });
store.startBrowserShare(new sharedActions.StartBrowserShare());
store._handleSwitchBrowserShare(42);
clock.tick(500);
sinon.assert.calledOnce(getSelectedTabMetadataStub);
sinon.assert.calledOnce(dispatcher.dispatch);});
sinon.assert.notCalled(dispatcher.dispatch);});
it("should not process a request if sharing is paused", function () {
@ -1624,11 +1790,11 @@ describe("loop.store.ActiveRoomStore", function () {
sharingPaused: true });
store.startBrowserShare(new sharedActions.StartBrowserShare());
store._handleSwitchBrowserShare(42);
clock.tick(500);
sinon.assert.notCalled(getSelectedTabMetadataStub);
sinon.assert.calledOnce(dispatcher.dispatch);});
sinon.assert.notCalled(dispatcher.dispatch);});
it("should not process a request if no-one is in the room", function () {
@ -1639,57 +1805,12 @@ describe("loop.store.ActiveRoomStore", function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
store._handleSwitchBrowserShare(42);
clock.tick(500);
sinon.assert.notCalled(getSelectedTabMetadataStub);
sinon.assert.calledOnce(dispatcher.dispatch);});});
describe("Screen share Events", function () {
beforeEach(function () {
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.setStoreState({
screenSharingState: SCREEN_SHARE_STATES.ACTIVE });
// Stub to prevent errors surfacing in the console.
sandbox.stub(window.console, "error");});
afterEach(function () {
store.endScreenShare();});
it("should log an error in the console", function () {
var err = new Error("foo");
err.isError = true;
LoopMochaUtils.publish("BrowserSwitch", err);
sinon.assert.calledOnce(console.error);});
it("should update the SDK driver when a new window id is received", function () {
LoopMochaUtils.publish("BrowserSwitch", 72);
sinon.assert.calledOnce(fakeSdkDriver.switchAcquiredWindow);
sinon.assert.calledWithExactly(fakeSdkDriver.switchAcquiredWindow, 72);});
it("should end the screen sharing session when the listener receives an error", function () {
var err = new Error("foo");
err.isError = true;
LoopMochaUtils.publish("BrowserSwitch", err);
// The dispatcher was already called once in beforeEach().
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.ScreenSharingState({
state: SCREEN_SHARE_STATES.INACTIVE }));
sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);});});
sinon.assert.notCalled(dispatcher.dispatch);});});
@ -1706,7 +1827,12 @@ describe("loop.store.ActiveRoomStore", function () {
it("should remove the sharing listener", function () {
// Setup the listener.
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
// Now stop the screen share.
store.endScreenShare();
@ -1923,7 +2049,12 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(loop, "unsubscribe");
// Setup the listener.
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
// Now unload the window.
store.windowUnload();
@ -1992,7 +2123,12 @@ describe("loop.store.ActiveRoomStore", function () {
sandbox.stub(loop, "unsubscribe");
// Setup the listener.
store.startBrowserShare(new sharedActions.StartBrowserShare());
store.joinedRoom(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0 }));
// Now leave the room.
store.leaveRoom();

View File

@ -1,11 +1,11 @@
/**
* @license OpenTok.js v2.7.5 ccd6792 HEAD
* @license OpenTok.js v2.7.6 cff9122 HEAD
*
* Copyright (c) 2010-2015 TokBox, Inc.
* Released under the MIT license
* http://opensource.org/licenses/MIT
*
* Date: March 18 10:46:23 2016
* Date: June 24 07:05:09 2016
*/
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
@ -3816,12 +3816,12 @@ var makeEverythingAttachToOTHelpers = require('./makeEverythingAttachToOTHelpers
var util = exports;
/**
* @license Common JS Helpers on OpenTok v2.7.5 ccd6792 HEAD
* @license Common JS Helpers on OpenTok v2.7.6 cff9122 HEAD
* http://www.tokbox.com/
*
* Copyright (c) 2016 TokBox, Inc.
*
* Date: March 18 10:46:49 2016
* Date: June 24 07:05:32 2016
*
*/
@ -14169,12 +14169,40 @@ var substr = 'ab'.substr(-1) === 'b'
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
(function () {
try {
cachedSetTimeout = setTimeout;
} catch (e) {
cachedSetTimeout = function () {
throw new Error('setTimeout is not defined');
}
}
try {
cachedClearTimeout = clearTimeout;
} catch (e) {
cachedClearTimeout = function () {
throw new Error('clearTimeout is not defined');
}
}
} ())
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
@ -14190,7 +14218,7 @@ function drainQueue() {
if (draining) {
return;
}
var timeout = setTimeout(cleanUpNextTick);
var timeout = cachedSetTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
@ -14207,7 +14235,7 @@ function drainQueue() {
}
currentQueue = null;
draining = false;
clearTimeout(timeout);
cachedClearTimeout(timeout);
}
process.nextTick = function (fun) {
@ -14219,7 +14247,7 @@ process.nextTick = function (fun) {
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
setTimeout(drainQueue, 0);
cachedSetTimeout(drainQueue, 0);
}
};
@ -20254,9 +20282,9 @@ if (WebSocket) ws.prototype = WebSocket.prototype;
},{}],111:[function(require,module,exports){
module.exports = {
"version": "v2.7.5",
"build": "ccd6792",
"buildTime": "March 18 10:46:49 2016",
"version": "v2.7.6",
"build": "cff9122",
"buildTime": "June 24 07:05:32 2016",
"debug": "false",
"websiteURL": "http://www.tokbox.com",
"cdnURL": "http://static.opentok.com",
@ -29954,6 +29982,7 @@ var PeerConnection = function(config) {
subscribeProcessor(
_peerConnection,
config.numberOfSimulcastStreams,
config.offerConstraints,
// Success: Relay Offer
function(offer) {
@ -30423,9 +30452,14 @@ module.exports = function PublisherPeerConnection(
var pcConfig = {
iceServers: iceServers,
channels: channels,
numberOfSimulcastStreams: numberOfSimulcastStreams
numberOfSimulcastStreams: numberOfSimulcastStreams,
offerConstraints: {}
};
if (session.sessionInfo.reconnection) {
pcConfig.offerConstraints.iceRestart = true;
}
setCertificates(pcConfig, function(err, pcConfigWithCerts) {
if (err) {
completion(err);
@ -31224,6 +31258,7 @@ var SDPHelpers = require('./sdp_helpers.js');
module.exports = function subscribeProcessor(
peerConnection,
numberOfSimulcastStreams,
offerConstraints,
success,
failure
) {
@ -31270,9 +31305,7 @@ module.exports = function subscribeProcessor(
generateErrorCallback('Error while creating Offer', 'CreateOffer'),
// Constraints
{
iceRestart: true
}
offerConstraints
);
};
@ -31486,7 +31519,13 @@ module.exports = function SubscriberPeerConnection(remoteConnection, session, st
// Init
this.init = function(completion) {
var pcConfig = {};
var pcConfig = {
offerConstraints: {}
};
if (session.sessionInfo.reconnection) {
pcConfig.offerConstraints.iceRestart = true;
}
setCertificates(pcConfig, function(err, pcConfigWithCerts) {
if (err) {

View File

@ -7,7 +7,7 @@
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
## use "..." if \u2026 doesn't suit traditions in your locale.
loopMenuItem_label=Start a conversation…
loopMenuItem_label=Start a Conversation…
loopMenuItem_accesskey=t
## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):

View File

@ -33,7 +33,7 @@ first_time_experience_subheading_button_above=Trykk på knappen ovanfor for å s
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
## ways to use Hello project.
first_time_experience_content=Bruk han til å planleggja, arbeida, og ha det moro i lag.
first_time_experience_content=Bruk Hello til å planleggja, arbeida, og ha det moro i lag.
first_time_experience_content2=Bruk han til å få gjort ting: planlegging, arbeid og for å ha det moro i lag.
first_time_experience_button_label2=Sjå korleis det fungerer

View File

@ -9,7 +9,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>loop@mozilla.org</em:id>
<em:bootstrap>true</em:bootstrap>
<em:version>1.4.1</em:version>
<em:version>1.4.2</em:version>
<em:type>2</em:type>
<!-- Target Application this extension can install into,

View File

@ -29,7 +29,7 @@ LOOPDIR=browser/extensions/loop
TESTS="
${LOOPDIR}/chrome/test/mochitest
browser/components/uitour/test/browser_UITour_loop_panel.js
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
browser/base/content/test/webrtc/browser_devices_get_user_media_about_urls.js
browser/base/content/test/general/browser_parsable_css.js
"
@ -41,7 +41,7 @@ do
# UITour & get user media aren't compatible with e10s currenly.
if [ "$1" != "--skip-e10s" ] && \
[ "$test" != "browser/components/uitour/test/browser_UITour_loop.js" ] && \
[ "$test" != "browser/base/content/test/general/browser_devices_get_user_media_about_urls.js" ];
[ "$test" != "browser/base/content/test/webrtc/browser_devices_get_user_media_about_urls.js" ];
then
./mach mochitest $test
fi

View File

@ -47,7 +47,7 @@
<!ENTITY font.langGroup.khmer "Khmer">
<!ENTITY font.langGroup.malayalam "Malayalam">
<!ENTITY font.langGroup.math "Mathematics">
<!ENTITY font.langGroup.oriya "Oriya">
<!ENTITY font.langGroup.odia "Odia">
<!ENTITY font.langGroup.telugu "Telugu">
<!ENTITY font.langGroup.kannada "Kannada">
<!ENTITY font.langGroup.sinhala "Sinhala">

View File

@ -94,7 +94,7 @@
#customization-palette,
#customization-empty {
padding: 0 25px 25px;
padding: 5px 25px 25px;
}
#customization-header {
@ -229,6 +229,10 @@ toolbarpaletteitem[place="panel"] {
opacity: 1;
}
toolbarpaletteitem toolbarbutton[disabled] {
color: inherit !important;
}
toolbarpaletteitem[notransition].panel-customization-placeholder,
toolbarpaletteitem[notransition][place="toolbar"],
toolbarpaletteitem[notransition][place="palette"],
@ -277,10 +281,13 @@ toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
/* Delay adding the focusring back until after the transform transition completes. */
transition: outline-width .01s linear var(--drag-drop-transition-duration);
outline: 1px dotted rgba(0,0,0,.5);
outline-offset: -5px;
-moz-outline-radius: 2.5px;
}
toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
outline-offset: -5px;
}
#wrapper-edit-controls[place="palette"] > #edit-controls > toolbarbutton,
#wrapper-edit-controls[place="palette"] > #edit-controls > separator,
#wrapper-zoom-controls[place="palette"] > #zoom-controls > toolbarbutton,

View File

@ -526,7 +526,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
margin: 0;
}
#main-window[customizing] #PanelUI-footer-fxa > toolbarseparator {
#main-window[customizing] #PanelUI-footer-fxa {
display: none;
}
@ -798,7 +798,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
display: none;
}
#PanelUI-fxa-icon[syncstatus="active"] {
#PanelUI-fxa-icon[syncstatus="active"]:not([disabled]) {
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar.png);
}
@ -842,12 +842,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
display: none;
}
#PanelUI-fxa-avatar[disabled],
#PanelUI-fxa-status[disabled],
#PanelUI-fxa-icon[disabled] {
display: none;
}
#PanelUI-fxa-status[disabled] {
pointer-events: none;
}
@ -886,7 +882,11 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
}
#PanelUI-help[disabled],
#PanelUI-quit[disabled] {
#PanelUI-quit[disabled],
#PanelUI-fxa-icon[disabled],
#PanelUI-fxa-avatar[disabled],
#PanelUI-fxa-label[disabled] > .toolbarbutton-icon,
#PanelUI-fxa-status::after {
opacity: 0.4;
}
@ -1671,7 +1671,7 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
}
#PanelUI-fxa-icon[syncstatus="active"] {
#PanelUI-fxa-icon[syncstatus="active"]:not([disabled]) {
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
}

View File

@ -261,8 +261,7 @@
}
/* Disable dragging like in the default theme: */
#main-window[tabsintitlebar] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):-moz-lwtheme,
#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks:-moz-lwtheme {
#main-window[tabsintitlebar] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):-moz-lwtheme {
-moz-window-dragging: no-drag;
}

View File

@ -95,7 +95,9 @@ AC_DEFUN([MOZ_ANDROID_STLPORT],
if test "$OS_TARGET" = "Android"; then
cpu_arch_dir="$ANDROID_CPU_ARCH"
if test "$MOZ_THUMB2" = 1; then
# NDK r12 removed the arm/thumb library split and just made everything
# thumb by default. Attempt to compensate.
if test "$MOZ_THUMB2" = 1 -a -d "$cpu_arch_dir/thumb"; then
cpu_arch_dir="$cpu_arch_dir/thumb"
fi
@ -129,6 +131,12 @@ if test "$OS_TARGET" = "Android"; then
fi
STLPORT_LIBS="-L$cxx_libs -lc++_static"
# NDK r12 split the libc++ runtime libraries into pieces.
for lib in c++abi unwind android_support; do
if test -e "$cxx_libs/lib${lib}.a"; then
STLPORT_LIBS="$STLPORT_LIBS -l${lib}"
fi
done
# Add android/support/include/ for prototyping long double math
# functions, locale-specific C library functions, multibyte support,
# etc.

View File

@ -37,6 +37,12 @@ if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then
for var in AS CC CXX CPP LD AR RANLIB STRIP; do
ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'"
done
old_cflags="$CFLAGS"
# The libffi sources (especially the ARM ones) are written expecting gas
# syntax, and clang's integrated assembler doesn't handle all of gas syntax.
if test -n "$CLANG_CC" -a "$CPU_ARCH" = arm; then
CFLAGS="-no-integrated-as $CFLAGS"
fi
if test "$CROSS_COMPILE"; then
export CPPFLAGS CFLAGS LDFLAGS
fi
@ -77,6 +83,7 @@ if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then
AC_OUTPUT_SUBDIRS(js/src/ctypes/libffi)
ac_configure_args="$_SUBDIR_CONFIG_ARGS"
CONFIG_FILES=$old_config_files
CFLAGS="$old_cflags"
fi
fi

View File

@ -228,7 +228,6 @@ def old_configure_options(*options):
'--enable-reflow-perf',
'--enable-release',
'--enable-require-all-d3dc-versions',
'--enable-rust',
'--enable-safe-browsing',
'--enable-sandbox',
'--enable-signmar',

View File

@ -0,0 +1,146 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
option('--enable-rust', help='Include Rust language sources')
@depends('--enable-rust')
def rust_compiler_names(value):
if value:
return ['rustc']
rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)
@depends_if(rustc)
@checking('rustc version')
@imports('subprocess')
def rustc_version(rustc):
try:
# TODO: We should run `rustc --version -v` and parse that output instead.
version = Version(subprocess.check_output(
[rustc, '--version']
).splitlines()[0].split()[1])
return version
except subprocess.CalledProcessError as e:
die('Failed to get rustc version: %s', e.message)
@depends('--enable-rust', rustc, rustc_version)
@imports(_from='textwrap', _import='dedent')
def rust_compiler(value, rustc, rustc_version):
if value:
if not rustc:
die(dedent('''\
Rust compiler not found.
To compile rust language sources, you must have 'rustc' in your path.
See http://www.rust-lang.org/ for more information.
'''))
if rustc_version < '1.5':
die(dedent('''\
Rust compiler {} is too old.
To compile Rust language sources please install at least
version 1.5 of the 'rustc' toolchain and make sure it is
first in your path.
You can verify this by typing 'rustc --version'.
'''.format(rustc_version)))
return True
set_config('MOZ_RUST', rust_compiler)
@depends(rust_compiler, rustc, target, cross_compiling)
@imports('os')
@imports('subprocess')
@imports(_from='mozbuild.configure.util', _import='LineIO')
@imports(_from='mozbuild.shellutil', _import='quote')
@imports(_from='tempfile', _import='mkstemp')
def rust_target(rust_compiler, rustc, target, cross_compiling):
if rust_compiler:
# Rust's --target options are similar to, but not exactly the same
# as, the autoconf-derived targets we use. An example would be that
# Rust uses distinct target triples for targetting the GNU C++ ABI
# and the MSVC C++ ABI on Win32, whereas autoconf has a single
# triple and relies on the user to ensure that everything is
# compiled for the appropriate ABI. We need to perform appropriate
# munging to get the correct option to rustc.
#
# The canonical list of targets supported can be derived from:
#
# https://github.com/rust-lang/rust/tree/master/mk/cfg
# Avoid having to write out os+kernel for all the platforms where
# they don't differ.
os_or_kernel = target.kernel if target.kernel == 'Linux' and target.os != 'Android' else target.os
rustc_target = {
# DragonFly
('x86_64', 'Dragonfly'): 'x86_64-unknown-dragonfly',
# FreeBSD, GNU/kFreeBSD
('x86', 'FreeBSD'): 'i686-unknown-freebsd',
('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd',
# NetBSD
('x86_64', 'NetBSD'): 'x86_64-unknown-netbsd',
# OpenBSD
('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd',
# Linux
('x86', 'Linux'): 'i686-unknown-linux-gnu',
# Linux
('x86_64', 'Linux'): 'x86_64-unknown-linux-gnu',
# OS X and iOS
('x86', 'OSX'): 'i686-apple-darwin',
('x86', 'iOS'): 'i386-apple-ios',
('x86_64', 'OSX'): 'x86_64-apple-darwin',
# Android
('x86', 'Android'): 'i686-linux-android',
('arm', 'Android'): 'arm-linux-androideabi',
# Windows
# XXX better detection of CXX needed here, to figure out whether
# we need i686-pc-windows-gnu instead, since mingw32 builds work.
('x86', 'WINNT'): 'i686-pc-windows-msvc',
('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
}.get((target.cpu, os_or_kernel), None)
if rustc_target is None:
if cross_compiling:
die("Don't know how to translate {} for rustc".format(target.alias))
# Fall back to implicit (native) target when not cross-compiling
return None
# Check to see whether our rustc has a reasonably functional stdlib
# for our chosen target.
target_arg = '--target=' + rustc_target
in_fd, in_path = mkstemp(prefix='conftest', suffix='.rs')
out_fd, out_path = mkstemp(prefix='conftest', suffix='.rlib')
os.close(out_fd)
try:
source = 'pub extern fn hello() { println!("Hello world"); }'
log.debug('Creating `%s` with content:', in_path)
with LineIO(lambda l: log.debug('| %s', l)) as out:
out.write(source)
os.write(in_fd, source)
os.close(in_fd)
cmd = [
rustc,
'--crate-type', 'staticlib',
target_arg,
'-o', out_path,
in_path,
]
def failed():
die('Cannot compile for {} with {}'.format(target.alias, rustc))
check_cmd_output(*cmd, onerror=failed)
if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
failed()
finally:
os.remove(in_path)
os.remove(out_path)
# This target is usable.
return target_arg
set_config('RUST_TARGET', rust_target)
# Until we remove all the other Rust checks in old-configure.
add_old_configure_assignment('MOZ_RUST', rust_compiler)
add_old_configure_assignment('RUSTC', rustc)
add_old_configure_assignment('RUST_TARGET', rust_target)

View File

@ -640,3 +640,5 @@ def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
set_config('MOZ_DEBUG_FLAGS', debug_flags)
add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
include('rust.configure')

View File

@ -70,9 +70,10 @@ def normsep(path):
@imports('itertools')
@imports(_from='os', _import='pathsep')
def find_program(file, paths=None):
if is_absolute_or_relative(file):
return os.path.abspath(file) if os.path.isfile(file) else None
try:
if is_absolute_or_relative(file):
return normsep(which(os.path.basename(file),
[os.path.dirname(file)]))
if paths:
if not isinstance(paths, (list, tuple)):
die("Paths provided to find_program must be a list of strings, "

View File

@ -919,11 +919,11 @@ ifdef MOZ_RUST
# in the target's LIBS.
$(RSOBJS):
$(REPORT_BUILD)
$(RUSTC) $(RUSTFLAGS) --crate-type rlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
$(RUSTC) $(RUST_TARGET) $(RUSTFLAGS) --crate-type rlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
$(RS_STATICLIB_CRATE_OBJ):
$(REPORT_BUILD)
$(RUSTC) $(RUSTFLAGS) --crate-type staticlib $(RLIB_EXTERN_CRATE_OPTIONS) --emit dep-info=$(MDDEPDIR)/$(call mk_global_crate_libname,$(RS_STATICLIB_CRATE_SRC)).pp,link=$@ $(RS_STATICLIB_CRATE_SRC)
$(RUSTC) $(RUST_TARGET) $(RUSTFLAGS) --crate-type staticlib $(RLIB_EXTERN_CRATE_OPTIONS) --emit dep-info=$(MDDEPDIR)/$(call mk_global_crate_libname,$(RS_STATICLIB_CRATE_SRC)).pp,link=$@ $(RS_STATICLIB_CRATE_SRC)
endif
$(SOBJS):

View File

@ -3,15 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env browser */
/* globals BrowserToolboxProcess */
"use strict";
loader.lazyImporter(this, "BrowserToolboxProcess",
"resource://devtools/client/framework/ToolboxProcess.jsm");
const { createClass, DOM: dom } =
require("devtools/client/shared/vendor/react");
const { debugAddon } = require("../../modules/addon");
const Services = require("Services");
const Strings = Services.strings.createBundle(
@ -22,7 +19,7 @@ module.exports = createClass({
debug() {
let { target } = this.props;
BrowserToolboxProcess.init({ addonID: target.addonID });
debugAddon(target.addonID);
},
reload() {

View File

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
loader.lazyImporter(this, "BrowserToolboxProcess",
"resource://devtools/client/framework/ToolboxProcess.jsm");
let toolbox = null;
exports.debugAddon = function (addonID) {
if (toolbox) {
toolbox.close();
}
toolbox = BrowserToolboxProcess.init({
addonID,
onClose: () => {
toolbox = null;
}
});
};

View File

@ -3,5 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'addon.js',
'worker.js',
)

View File

@ -27,6 +27,7 @@ const {
} = require("devtools/client/shared/widgets/view-helpers");
const { Task } = require("devtools/shared/task");
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
const { gDevTools } = require("devtools/client/framework/devtools");
const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft";
@ -905,7 +906,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
* Opens selected item source in a new tab.
*/
_onNewTabCommand: function () {
let win = Services.wm.getMostRecentWindow("navigator:browser");
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
let selected = this.selectedItem.attachment;
win.openUILinkIn(selected.source.url, "tab", { relatedToCurrent: true });
},

View File

@ -134,8 +134,10 @@ function Eyedropper(chromeWindow, opts = { copyOnSelect: true, context: "other"
height: CANVAS_WIDTH // height of canvas
};
let mm = this._contentTab.linkedBrowser.messageManager;
mm.loadFrameScript("resource://devtools/client/eyedropper/eyedropper-child.js", true);
if (this._contentTab) {
let mm = this._contentTab.linkedBrowser.messageManager;
mm.loadFrameScript("resource://devtools/client/eyedropper/eyedropper-child.js", true);
}
// record if this was opened via the picker or standalone
var telemetry = new Telemetry();
@ -192,7 +194,7 @@ Eyedropper.prototype = {
},
get _contentTab() {
return this._chromeWindow.gBrowser.selectedTab;
return this._chromeWindow.gBrowser && this._chromeWindow.gBrowser.selectedTab;
},
/**
@ -202,6 +204,10 @@ Eyedropper.prototype = {
* Promise that resolves with the screenshot as a dataURL
*/
getContentScreenshot: function () {
if (!this._contentTab) {
return promise.resolve(null);
}
let deferred = defer();
let mm = this._contentTab.linkedBrowser.messageManager;
@ -224,29 +230,29 @@ Eyedropper.prototype = {
// the eyedropper is aready open, don't create another panel.
return promise.resolve();
}
let deferred = defer();
this.isOpen = true;
this._showCrosshairs();
// Get screenshot of content so we can inspect colors
this.getContentScreenshot().then((dataURL) => {
this._contentImage = new this._chromeWindow.Image();
this._contentImage.src = dataURL;
return this.getContentScreenshot().then((dataURL) => {
// The data url may be null, e.g. if there is no content tab
if (dataURL) {
this._contentImage = new this._chromeWindow.Image();
this._contentImage.src = dataURL;
// Wait for screenshot to load
this._contentImage.onload = () => {
// Then start showing the eyedropper UI
this._chromeDocument.addEventListener("mousemove", this._onFirstMouseMove);
deferred.resolve();
this.isStarted = true;
this.emit("started");
};
// Wait for screenshot to load
let imageLoaded = promise.defer();
this._contentImage.onload = imageLoaded.resolve
return imageLoaded.promise;
}
}).then(() => {
// Then start showing the eyedropper UI
this._chromeDocument.addEventListener("mousemove", this._onFirstMouseMove);
this.isStarted = true;
this.emit("started");
});
return deferred.promise;
},
/**
@ -282,8 +288,9 @@ Eyedropper.prototype = {
* y-coordinate of mouse relative to browser window.
*/
_isInContent: function (clientX, clientY) {
let box = this._contentTab.linkedBrowser.getBoundingClientRect();
if (clientX > box.left &&
let box = this._contentTab && this._contentTab.linkedBrowser.getBoundingClientRect();
if (box &&
clientX > box.left &&
clientX < box.right &&
clientY > box.top &&
clientY < box.bottom) {

View File

@ -52,18 +52,18 @@ this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aO
// all three arguments
if (typeof aOnClose === "object") {
if (aOnClose.onClose) {
this.on("close", aOnClose.onClose);
this.once("close", aOnClose.onClose);
}
if (aOnClose.onRun) {
this.on("run", aOnClose.onRun);
this.once("run", aOnClose.onRun);
}
this._options = aOnClose;
} else {
if (aOnClose) {
this.on("close", aOnClose);
this.once("close", aOnClose);
}
if (aOnRun) {
this.on("run", aOnRun);
this.once("run", aOnRun);
}
this._options = aOptions || {};
}
@ -132,7 +132,7 @@ BrowserToolboxProcess.prototype = {
dumpn("Created a separate loader instance for the DebuggerServer.");
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
this.debuggerServer.on("connectionchange", this.emit);
this.debuggerServer.init();
this.debuggerServer.addBrowserActors();
@ -249,6 +249,7 @@ BrowserToolboxProcess.prototype = {
this._telemetry.toolClosed("jsbrowserdebugger");
if (this.debuggerServer) {
this.debuggerServer.off("connectionchange", this.emit);
this.debuggerServer.destroy();
this.debuggerServer = null;
}
@ -260,6 +261,9 @@ BrowserToolboxProcess.prototype = {
this._dbgProcess = null;
this._options = null;
if (this.loader) {
this.loader.destroy();
}
this.loader = null;
this._telemetry = null;
}

View File

@ -738,7 +738,7 @@ Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished", f
// Fake end of browser window load event for all already opened windows
// that is already fully loaded.
let enumerator = Services.wm.getEnumerator("navigator:browser");
let enumerator = Services.wm.getEnumerator(gDevTools.chromeWindowType);
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {

View File

@ -50,6 +50,10 @@ this.DevTools = function DevTools() {
};
DevTools.prototype = {
// The windowtype of the main window, used in various tools. This may be set
// to something different by other gecko apps.
chromeWindowType: "navigator:browser",
registerDefaults() {
// Ensure registering items in the sorted order (getDefault* functions
// return sorted lists)

View File

@ -29,44 +29,56 @@ if (url.search.length > 1) {
// Specify the default tool to open
let tool = url.searchParams.get("tool");
if (url.searchParams.has("target")) {
// Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
// mozbrowser>) whose reference is set on the host iframe.
Task.spawn(function* () {
let target;
if (url.searchParams.has("target")) {
// Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
// mozbrowser>) whose reference is set on the host iframe.
// `iframe` is the targeted document to debug
let iframe = host.wrappedJSObject ? host.wrappedJSObject.target
: host.target;
// Need to use a xray and query some interfaces to have
// attributes and behavior expected by devtools codebase
iframe = XPCNativeWrapper(iframe);
iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
// `iframe` is the targeted document to debug
let iframe = host.wrappedJSObject ? host.wrappedJSObject.target
: host.target;
// Need to use a xray and query some interfaces to have
// attributes and behavior expected by devtools codebase
iframe = XPCNativeWrapper(iframe);
iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
if (iframe) {
// Fake a xul:tab object as we don't have one.
// linkedBrowser is the only one attribute being queried by client.getTab
let tab = { linkedBrowser: iframe };
if (iframe) {
// Fake a xul:tab object as we don't have one.
// linkedBrowser is the only one attribute being queried by client.getTab
let tab = { linkedBrowser: iframe };
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let client = new DebuggerClient(DebuggerServer.connectPipe());
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let client = new DebuggerClient(DebuggerServer.connectPipe());
Task.spawn(function* () {
yield client.connect();
// Creates a target for a given browser iframe.
let response = yield client.getTab({ tab });
let form = response.tab;
let target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
let options = { customIframe: host };
yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
});
target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
} else {
alert("Unable to find the targetted iframe to debug");
}
} else {
target = yield targetFromURL(url);
}
} else {
targetFromURL(url).then(target => {
let options = { customIframe: host };
return gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
}).then(null, e => {
window.alert("Unable to start the toolbox:" + e.message);
let options = { customIframe: host };
let toolbox = yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
// Watch for toolbox.xul unload in order to cleanup things when we close
// about:devtools-toolbox tabs
function onUnload() {
window.removeEventListener("unload", onUnload);
toolbox.destroy();
}
window.addEventListener("unload", onUnload, true);
toolbox.on("destroy", function () {
window.removeEventListener("unload", onUnload);
});
}
}).catch(error => {
console.error("Exception while loading the toolbox", error);
});
}

View File

@ -18,6 +18,14 @@ responsive.editDeviceList=Edit list…
# LOCALIZATION NOTE (responsive.exit): tooltip text of the exit button.
responsive.exit=Close Responsive Design Mode
# LOCALIZATION NOTE (responsive.deviceListLoading): placeholder text for
# device selector when it's still fetching devices
responsive.deviceListLoading=Loading…
# LOCALIZATION NOTE (responsive.deviceListError): placeholder text for
# device selector when an error occured
responsive.deviceListError=No list available
# LOCALIZATION NOTE (responsive.done): button text in the device list modal
responsive.done=Done

View File

@ -16,8 +16,7 @@
export.fileOverwriteConfirmation=File exists. Overwrite?
# LOCALIZATION NOTE (browserWindow.unavailable): This error message is shown
# when Scratchpad does not find any recently active window of navigator:browser
# type.
# when Scratchpad does not find any recently active main browser window.
browserWindow.unavailable=Scratchpad cannot find any browser window to execute the code in.
# LOCALIZATION NOTE (scratchpadContext.invalid): This error message is shown

View File

@ -30,6 +30,7 @@ const {LocalizationHelper} = require("devtools/client/shared/l10n");
const {PrefsHelper} = require("devtools/client/shared/prefs");
const {ViewHelpers, Heritage, WidgetMethods, setNamedTimeout} =
require("devtools/client/shared/widgets/view-helpers");
const {gDevTools} = require("devtools/client/framework/devtools");
/**
* Localization convenience methods.
@ -714,7 +715,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* Opens selected item in a new tab.
*/
openRequestInTab: function () {
let win = Services.wm.getMostRecentWindow("navigator:browser");
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
let selected = this.selectedItem.attachment;
win.openUILinkIn(selected.url, "tab", { relatedToCurrent: true });
},

View File

@ -9,6 +9,7 @@ const tabs = require("sdk/tabs");
const tabUtils = require("sdk/tabs/utils");
const { viewFor } = require("sdk/view/core");
const { waitForDelayedStartupFinished } = require("devtools/client/performance/test/helpers/wait-utils");
const { gDevTools } = require("devtools/client/framework/devtools");
/**
* Gets a random integer in between an interval. Used to uniquely identify
@ -77,7 +78,7 @@ exports.removeTab = function (tab, options = {}) {
* Adds a browser window with the provided options.
*/
exports.addWindow = function* (options) {
let { OpenBrowserWindow } = Services.wm.getMostRecentWindow("navigator:browser");
let { OpenBrowserWindow } = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
let win = OpenBrowserWindow(options);
yield waitForDelayedStartupFinished(win);
return win;

View File

@ -7,12 +7,70 @@
const {
ADD_DEVICE,
ADD_DEVICE_TYPE,
LOAD_DEVICE_LIST_START,
LOAD_DEVICE_LIST_ERROR,
LOAD_DEVICE_LIST_END,
UPDATE_DEVICE_DISPLAYED,
UPDATE_DEVICE_MODAL_OPEN,
} = require("./index");
const { GetDevices } = require("devtools/client/shared/devices");
const Services = require("Services");
const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
/**
* Returns an object containing the user preference of displayed devices.
*
* @return {Object} containing two Sets:
* - added: Names of the devices that were explicitly enabled by the user
* - removed: Names of the devices that were explicitly removed by the user
*/
function loadPreferredDevices() {
let preferredDevices = {
"added": new Set(),
"removed": new Set(),
};
if (Services.prefs.prefHasUserValue(DISPLAYED_DEVICES_PREF)) {
try {
let savedData = Services.prefs.getCharPref(DISPLAYED_DEVICES_PREF);
savedData = JSON.parse(savedData);
if (savedData.added && savedData.removed) {
preferredDevices.added = new Set(savedData.added);
preferredDevices.removed = new Set(savedData.removed);
}
} catch (e) {
console.error(e);
}
}
return preferredDevices;
}
/**
* Update the displayed device list preference with the given device list.
*
* @param {Object} containing two Sets:
* - added: Names of the devices that were explicitly enabled by the user
* - removed: Names of the devices that were explicitly removed by the user
*/
function updatePreferredDevices(devices) {
let devicesToSave = {
added: Array.from(devices.added),
removed: Array.from(devices.removed),
};
devicesToSave = JSON.stringify(devicesToSave);
Services.prefs.setCharPref(DISPLAYED_DEVICES_PREF, devicesToSave);
}
module.exports = {
// This function is only exported for testing purposes
_loadPreferredDevices: loadPreferredDevices,
updatePreferredDevices: updatePreferredDevices,
addDevice(device, deviceType) {
return {
type: ADD_DEVICE,
@ -37,6 +95,39 @@ module.exports = {
};
},
loadDevices() {
return function* (dispatch, getState) {
yield dispatch({ type: LOAD_DEVICE_LIST_START });
let preferredDevices = loadPreferredDevices();
let devices;
try {
devices = yield GetDevices();
} catch (e) {
console.error("Could not load device list: " + e);
dispatch({ type: LOAD_DEVICE_LIST_ERROR });
return;
}
for (let type of devices.TYPES) {
dispatch(module.exports.addDeviceType(type));
for (let device of devices[type]) {
if (device.os == "fxos") {
continue;
}
let newDevice = Object.assign({}, device, {
displayed: preferredDevices.added.has(device.name) ||
(device.featured && !(preferredDevices.removed.has(device.name))),
});
dispatch(module.exports.addDevice(newDevice, type));
}
}
dispatch({ type: LOAD_DEVICE_LIST_END });
};
},
updateDeviceModalOpen(isOpen) {
return {
type: UPDATE_DEVICE_MODAL_OPEN,

View File

@ -8,6 +8,8 @@
// central list of constants makes it easy to see all possible action names at
// a glance. Please add a comment with each new action type.
const { createEnum } = require("../utils/enum");
createEnum([
// Add a new device.
@ -41,6 +43,15 @@ createEnum([
// Update the device display state in the device selector.
"UPDATE_DEVICE_DISPLAYED",
// Indicates that the device list is being loaded
"LOAD_DEVICE_LIST_START",
// Indicates that the device list loading action threw an error
"LOAD_DEVICE_LIST_ERROR",
// Indicates that the device list has been loaded successfully
"LOAD_DEVICE_LIST_END",
// Update the device modal open state.
"UPDATE_DEVICE_MODAL_OPEN",
@ -48,15 +59,3 @@ createEnum([
"UPDATE_TOUCH_SIMULATION_ENABLED",
], module.exports);
/**
* Create a simple enum-like object with keys mirrored to values from an array.
* This makes comparison to a specfic value simpler without having to repeat and
* mis-type the value.
*/
function createEnum(array, target) {
for (let key of array) {
target[key] = key;
}
return target;
}

View File

@ -13,6 +13,7 @@ const { connect } = require("devtools/client/shared/vendor/react-redux");
const {
updateDeviceDisplayed,
updateDeviceModalOpen,
updatePreferredDevices,
} = require("./actions/devices");
const {
changeDevice,
@ -24,7 +25,6 @@ const { updateTouchSimulationEnabled } = require("./actions/touch-simulation");
const DeviceModal = createFactory(require("./components/device-modal"));
const GlobalToolbar = createFactory(require("./components/global-toolbar"));
const Viewports = createFactory(require("./components/viewports"));
const { updatePreferredDevices } = require("./devices");
const Types = require("./types");
let App = createClass({
@ -82,15 +82,13 @@ let App = createClass({
this.props.dispatch(updateDeviceModalOpen(isOpen));
},
onUpdateTouchSimulationEnabled() {
let { enabled } = this.props.touchSimulation;
onUpdateTouchSimulation(isEnabled) {
window.postMessage({
type: "update-touch-simulation",
enabled,
enabled: isEnabled,
}, "*");
this.props.dispatch(updateTouchSimulationEnabled(!enabled));
this.props.dispatch(updateTouchSimulationEnabled(isEnabled));
},
render() {
@ -113,7 +111,7 @@ let App = createClass({
onScreenshot,
onUpdateDeviceDisplayed,
onUpdateDeviceModalOpen,
onUpdateTouchSimulationEnabled,
onUpdateTouchSimulation,
} = this;
return dom.div(
@ -125,7 +123,7 @@ let App = createClass({
touchSimulation,
onExit,
onScreenshot,
onUpdateTouchSimulationEnabled,
onUpdateTouchSimulation,
}),
Viewports({
devices,

View File

@ -73,26 +73,45 @@ module.exports = createClass({
selectClass += " selected";
}
let state = devices.listState;
let listContent;
if (state == Types.deviceListState.LOADED) {
listContent = [dom.option({
value: "",
disabled: true,
hidden: true,
}, getStr("responsive.noDeviceSelected")),
options.map(device => {
return dom.option({
key: device.name,
value: device.name,
}, device.name);
}),
dom.option({
value: OPEN_DEVICE_MODAL_VALUE,
}, getStr("responsive.editDeviceList"))];
} else if (state == Types.deviceListState.LOADING
|| state == Types.deviceListState.INITIALIZED) {
listContent = [dom.option({
value: "",
disabled: true,
}, getStr("responsive.deviceListLoading"))];
} else if (state == Types.deviceListState.ERROR) {
listContent = [dom.option({
value: "",
disabled: true,
}, getStr("responsive.deviceListError"))];
}
return dom.select(
{
className: selectClass,
value: selectedDevice,
onChange: this.onSelectChange,
disabled: (state !== Types.deviceListState.LOADED),
},
dom.option({
value: "",
disabled: true,
hidden: true,
}, getStr("responsive.noDeviceSelected")),
options.map(device => {
return dom.option({
key: device.name,
value: device.name,
}, device.name);
}),
dom.option({
value: OPEN_DEVICE_MODAL_VALUE,
}, getStr("responsive.editDeviceList"))
...listContent
);
},

View File

@ -17,7 +17,7 @@ module.exports = createClass({
touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
onExit: PropTypes.func.isRequired,
onScreenshot: PropTypes.func.isRequired,
onUpdateTouchSimulationEnabled: PropTypes.func.isRequired,
onUpdateTouchSimulation: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
@ -28,7 +28,7 @@ module.exports = createClass({
touchSimulation,
onExit,
onScreenshot,
onUpdateTouchSimulationEnabled
onUpdateTouchSimulation
} = this.props;
let touchButtonClass = "toolbar-button devtools-button";
@ -49,7 +49,7 @@ module.exports = createClass({
dom.button({
id: "global-touch-simulation-button",
className: touchButtonClass,
onClick: onUpdateTouchSimulationEnabled,
onClick: () => onUpdateTouchSimulation(!touchSimulation.enabled),
}),
dom.button({
id: "global-screenshot-button",

View File

@ -1,88 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Services = require("Services");
const { Task } = require("devtools/shared/task");
const { GetDevices } = require("devtools/client/shared/devices");
const { addDevice, addDeviceType } = require("./actions/devices");
const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
/**
* Get the device catalog and load the devices onto the store.
*
* @param {Function} dispatch
* Action dispatch function
*/
let initDevices = Task.async(function* (dispatch) {
let preferredDevices = loadPreferredDevices();
let devices = yield GetDevices();
for (let type of devices.TYPES) {
dispatch(addDeviceType(type));
for (let device of devices[type]) {
if (device.os == "fxos") {
continue;
}
let newDevice = Object.assign({}, device, {
displayed: preferredDevices.added.has(device.name) ||
(device.featured && !(preferredDevices.removed.has(device.name))),
});
dispatch(addDevice(newDevice, type));
}
}
});
/**
* Returns an object containing the user preference of displayed devices.
*
* @return {Object} containing two Sets:
* - added: Names of the devices that were explicitly enabled by the user
* - removed: Names of the devices that were explicitly removed by the user
*/
function loadPreferredDevices() {
let preferredDevices = {
"added": new Set(),
"removed": new Set(),
};
if (Services.prefs.prefHasUserValue(DISPLAYED_DEVICES_PREF)) {
try {
let savedData = Services.prefs.getCharPref(DISPLAYED_DEVICES_PREF);
savedData = JSON.parse(savedData);
if (savedData.added && savedData.removed) {
preferredDevices.added = new Set(savedData.added);
preferredDevices.removed = new Set(savedData.removed);
}
} catch (e) {
console.error(e);
}
}
return preferredDevices;
}
/**
* Update the displayed device list preference with the given device list.
*
* @param {Object} containing two Sets:
* - added: Names of the devices that were explicitly enabled by the user
* - removed: Names of the devices that were explicitly removed by the user
*/
function updatePreferredDevices(devices) {
let devicesToSave = {
added: Array.from(devices.added),
removed: Array.from(devices.removed),
};
devicesToSave = JSON.stringify(devicesToSave);
Services.prefs.setCharPref(DISPLAYED_DEVICES_PREF, devicesToSave);
}
exports.initDevices = initDevices;
exports.loadPreferredDevices = loadPreferredDevices;
exports.updatePreferredDevices = updatePreferredDevices;

View File

@ -187,6 +187,7 @@ html, body {
text-align: center;
text-overflow: ellipsis;
width: 150px;
font-size: 11px;
}
.viewport-device-selector.selected {
@ -382,13 +383,17 @@ html, body {
}
.device-header {
font-size: 11px;
font-weight: bold;
text-transform: capitalize;
padding: 0 0 3px 23px;
}
.device-label {
font-size: 11px;
padding-bottom: 3px;
display: flex;
align-items: center;
}
.device-input-checkbox {

View File

@ -23,11 +23,11 @@ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const message = require("./utils/message");
const { initDevices } = require("./devices");
const App = createFactory(require("./app"));
const Store = require("./store");
const { changeLocation } = require("./actions/location");
const { addViewport, resizeViewport } = require("./actions/viewports");
const { loadDevices } = require("./actions/devices");
let bootstrap = {
@ -43,7 +43,7 @@ let bootstrap = {
"agent");
this.telemetry.toolOpened("responsive");
let store = this.store = Store();
yield initDevices(this.dispatch.bind(this));
this.dispatch(loadDevices());
let provider = createElement(Provider, { store }, App());
ReactDOM.render(provider, document.querySelector("#root"));
message.post(window, "init:done");
@ -121,12 +121,21 @@ window.setViewportSize = (width, height) => {
};
/**
* Called by manager.js when tests want to use the viewport's browser to access
* the content inside. We mock the format of a <xul:browser> to make this
* easily usable with ContentTask.spawn(), which expects an object with a
* `messageManager` property.
* Called by manager.js to access the viewport's browser, either for testing
* purposes or to reload it when touch simulation is enabled.
* A messageManager getter is added on the object to provide an easy access
* to the message manager without pulling the frame loader.
*/
window.getViewportBrowser = () => {
let { messageManager } = document.querySelector("iframe.browser").frameLoader;
return { messageManager };
let browser = document.querySelector("iframe.browser");
if (!browser.messageManager) {
Object.defineProperty(browser, "messageManager", {
get() {
return this.frameLoader.messageManager;
},
configurable: true,
enumerable: true,
});
}
return browser;
};

View File

@ -277,8 +277,7 @@ ResponsiveUI.prototype = {
yield message.request(toolWindow, "init");
toolWindow.addInitialViewport("about:blank");
yield message.wait(toolWindow, "browser-mounted");
toolViewportContentBrowser =
toolWindow.document.querySelector("iframe.browser");
toolViewportContentBrowser = ui.getViewportBrowser();
return toolViewportContentBrowser;
})
});
@ -400,7 +399,10 @@ ResponsiveUI.prototype = {
updateTouchSimulation: Task.async(function* (enabled) {
if (enabled) {
this.touchEventSimulator.start();
let reloadNeeded = yield this.touchEventSimulator.start();
if (reloadNeeded) {
this.getViewportBrowser().reload();
}
} else {
this.touchEventSimulator.stop();
}
@ -422,7 +424,7 @@ ResponsiveUI.prototype = {
}),
/**
* Helper for tests. Assumes a single viewport for now.
* Helper for tests/reloading the viewport. Assumes a single viewport for now.
*/
getViewportBrowser() {
return this.toolWindow.getViewportBrowser();

View File

@ -17,7 +17,6 @@ DIRS += [
DevToolsModules(
'app.js',
'constants.js',
'devices.js',
'index.css',
'manager.js',
'reducers.js',

View File

@ -7,13 +7,19 @@
const {
ADD_DEVICE,
ADD_DEVICE_TYPE,
LOAD_DEVICE_LIST_START,
LOAD_DEVICE_LIST_ERROR,
LOAD_DEVICE_LIST_END,
UPDATE_DEVICE_DISPLAYED,
UPDATE_DEVICE_MODAL_OPEN,
} = require("../actions/index");
const Types = require("../types");
const INITIAL_DEVICES = {
types: [],
isModalOpen: false,
listState: Types.deviceListState.INITIALIZED,
};
let reducers = {
@ -45,6 +51,24 @@ let reducers = {
});
},
[LOAD_DEVICE_LIST_START](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.LOADING,
});
},
[LOAD_DEVICE_LIST_ERROR](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.ERROR,
});
},
[LOAD_DEVICE_LIST_END](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.LOADED,
});
},
[UPDATE_DEVICE_MODAL_OPEN](devices, { isOpen }) {
return Object.assign({}, devices, {
isModalOpen: isOpen,

View File

@ -11,6 +11,7 @@ support-files =
!/devtools/client/framework/test/shared-head.js
!/devtools/client/framework/test/shared-redux-head.js
[browser_device_modal_error.js]
[browser_device_modal_exit.js]
[browser_device_modal_submit.js]
[browser_device_width.js]

View File

@ -0,0 +1,35 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test to check that RDM can handle properly an error in the device list
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
const { getStr } = require("devtools/client/responsive.html/utils/l10n");
// Set a wrong URL for the device list file
add_task(function* () {
yield SpecialPowers.pushPrefEnv({
set: [["devtools.devices.url", TEST_URI_ROOT + "wrong_devices_file.json"]],
});
});
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let select = document.querySelector(".viewport-device-selector");
// Wait until the viewport has been added and the device list state indicates
// an error
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.ERROR);
// The device selector placeholder should be set accordingly
let placeholder = select.options[select.selectedIndex].innerHTML;
ok(placeholder == getStr("responsive.deviceListError"),
"Device selector indicates an error");
// The device selector should be disabled
ok(select.disabled, "Device selector is disabled");
});

View File

@ -6,18 +6,20 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
// Test submitting display device changes on the device modal
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
let modal = document.querySelector(".device-modal");
let closeButton = document.querySelector("#device-close-button");
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
openDeviceModal(ui);
let preferredDevicesBefore = loadPreferredDevices();
let preferredDevicesBefore = _loadPreferredDevices();
info("Check the first unchecked device and exit the modal.");
let uncheckedCb = [...document.querySelectorAll(".device-input-checkbox")]
@ -30,7 +32,7 @@ addRDMTask(TEST_URL, function* ({ ui }) {
"The device modal is hidden on exit.");
info("Check that the device list remains unchanged after exitting.");
let preferredDevicesAfter = loadPreferredDevices();
let preferredDevicesAfter = _loadPreferredDevices();
is(preferredDevicesBefore.added.size, preferredDevicesAfter.added.size,
"Got expected number of added devices.");

View File

@ -19,6 +19,7 @@ const addedDevice = {
};
const TEST_URL = "data:text/html;charset=utf-8,";
const Types = require("devtools/client/responsive.html/types");
addRDMTask(TEST_URL, function* ({ ui }) {
let { store, document } = ui.toolWindow;
@ -26,8 +27,9 @@ addRDMTask(TEST_URL, function* ({ ui }) {
let select = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
openDeviceModal(ui);
@ -63,7 +65,7 @@ addRDMTask(TEST_URL, function* ({ ui }) {
"The device modal is hidden on submit.");
info("Checking that the new device is added to the user preference list.");
let preferredDevices = loadPreferredDevices();
let preferredDevices = _loadPreferredDevices();
ok(preferredDevices.added.has(value), value + " in user added list.");
info("Checking new device is added to the device selector.");
@ -88,7 +90,7 @@ addRDMTask(TEST_URL, function* ({ ui }) {
submitButton.click();
info("Checking that the device is removed from the user preference list.");
preferredDevices = loadPreferredDevices();
preferredDevices = _loadPreferredDevices();
ok(preferredDevices.removed.has(checkedVal), checkedVal + " in removed list");
info("Checking that the device is not in the device selector.");
@ -123,7 +125,7 @@ addRDMTask(TEST_URL, function* ({ ui }) {
return subtotal + ((device.os != "fxos" && device.featured) ? 1 : 0);
}, 0);
}, 0);
let preferredDevices = loadPreferredDevices();
let preferredDevices = _loadPreferredDevices();
// Tests to prove that reloading the RDM didn't break our device list
info("Checking new featured device appears in the device selector.");

View File

@ -38,7 +38,7 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
});
const { ResponsiveUIManager } = require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
const { loadPreferredDevices } = require("devtools/client/responsive.html/devices");
const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
const { getOwnerWindow } = require("sdk/tabs/utils");
const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";

View File

@ -5,6 +5,7 @@
"use strict";
const { PropTypes } = require("devtools/client/shared/vendor/react");
const { createEnum } = require("./utils/enum");
// React PropTypes are used to describe the expected "shape" of various common
// objects that get passed down as props to components.
@ -40,6 +41,16 @@ const device = {
};
/**
* An enum containing the possible values for the device list state
*/
exports.deviceListState = createEnum([
"INITIALIZED",
"LOADING",
"LOADED",
"ERROR",
]);
/**
* A list of devices and their types that can be displayed in the viewport.
*/
@ -69,6 +80,9 @@ exports.devices = {
// Whether or not the device modal is open
isModalOpen: PropTypes.bool,
// Device list state, possible values are exported above in an enum
listState: PropTypes.oneOf(Object.keys(exports.deviceListState)),
};
/**

View File

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.exports = {
/**
* Create a simple enum-like object with keys mirrored to values from an array.
* This makes comparison to a specfic value simpler without having to repeat and
* mis-type the value.
*/
createEnum(array, target = {}) {
for (let key of array) {
target[key] = key;
}
return target;
}
};

View File

@ -6,6 +6,7 @@
DevToolsModules(
'e10s.js',
'enum.js',
'l10n.js',
'message.js',
)

View File

@ -434,11 +434,11 @@ var Scratchpad = {
},
/**
* Get the most recent chrome window of type navigator:browser.
* Get the most recent main chrome browser window
*/
get browserWindow()
{
return Services.wm.getMostRecentWindow("navigator:browser");
return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
},
/**
@ -2061,9 +2061,8 @@ var Scratchpad = {
openDocumentationPage: function SP_openDocumentationPage()
{
let url = this.strings.GetStringFromName("help.openDocumentationPage");
let newTab = this.gBrowser.addTab(url);
this.browserWindow.openUILinkIn(url,"tab");
this.browserWindow.focus();
this.gBrowser.selectedTab = newTab;
},
};

View File

@ -31,6 +31,8 @@ var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
var { LoadContextInfo } = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { gDevTools } = require("devtools/client/framework/devtools");
var Services = require("Services");
var promise = require("promise");
var defer = require("devtools/shared/defer");
@ -294,9 +296,9 @@ AppCacheUtils.prototype = {
viewEntry: function ACU_viewEntry(key) {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
let win = wm.getMostRecentWindow("navigator:browser");
win.gBrowser.selectedTab = win.gBrowser.addTab(
"about:cache-entry?storage=appcache&context=&eid=&uri=" + key);
let win = wm.getMostRecentWindow(gDevTools.chromeWindowType);
let url = "about:cache-entry?storage=appcache&context=&eid=&uri=" + key;
win.openUILinkIn(url, "tab");
},
clearAll: function ACU_clearAll() {

View File

@ -6,7 +6,6 @@
const { getJSON } = require("devtools/client/shared/getjson");
const Services = require("Services");
const defer = require("devtools/shared/defer");
const DEVICES_URL = "devtools.devices.url";
const Strings = Services.strings.createBundle("chrome://devtools/locale/device.properties");
@ -48,10 +47,8 @@ exports.AddDevice = AddDevice;
// Get the complete devices catalog.
function GetDevices() {
let deferred = defer();
// Fetch common devices from Mozilla's CDN.
getJSON(DEVICES_URL).then(devices => {
return getJSON(DEVICES_URL).then(devices => {
for (let type in localDevices) {
if (!devices[type]) {
devices.TYPES.push(type);
@ -59,10 +56,8 @@ function GetDevices() {
}
devices[type] = localDevices[type].concat(devices[type]);
}
deferred.resolve(devices);
return devices;
});
return deferred.promise;
}
exports.GetDevices = GetDevices;

View File

@ -6,6 +6,7 @@
const {CC} = require("chrome");
const defer = require("devtools/shared/defer");
const promise = require("promise");
const Services = require("Services");
loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
@ -41,7 +42,10 @@ exports.getJSON = function (prefName) {
function readFromStorage(networkError) {
asyncStorage.getItem(prefName + "_cache").then(function (json) {
deferred.resolve(json);
if (!json) {
return promise.reject("Empty cache for " + prefName);
}
return deferred.resolve(json);
}).catch(function (e) {
deferred.reject("JSON not available, CDN error: " + networkError +
", storage error: " + e);

View File

@ -5,7 +5,7 @@ const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
const TOOL_DELAY = 1000;
add_task(function* () {
info("Activating the storage inspector");

View File

@ -7,7 +7,7 @@
var { Task } = require("devtools/shared/task");
var Services = require("Services");
var {gDevTools} = require("devtools/client/framework/devtools");
var { gDevTools } = require("devtools/client/framework/devtools");
var { getSourceText } = require("devtools/client/debugger/content/queries");
/**
@ -157,8 +157,8 @@ exports.viewSourceInScratchpad = Task.async(function* (sourceURL, sourceLine) {
exports.viewSource = Task.async(function* (toolbox, sourceURL, sourceLine) {
// Attempt to access view source via a browser first, which may display it in
// a tab, if enabled.
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWin) {
let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
if (browserWin && browserWin.BrowserViewSourceOfDocument) {
return browserWin.BrowserViewSourceOfDocument({
URL: sourceURL,
lineNumber: sourceLine

View File

@ -27,6 +27,7 @@
const Services = require("Services");
const defer = require("devtools/shared/defer");
const {getCSSLexer} = require("devtools/shared/css-lexer");
const {gDevTools} = require("devtools/client/framework/devtools");
// Parameters for the XHR request
// see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
@ -42,8 +43,6 @@ const PAGE_LINK_PARAMS =
var PAGE_LINK_URL = "https://developer.mozilla.org/docs/Web/CSS/";
exports.PAGE_LINK_URL = PAGE_LINK_URL;
const BROWSER_WINDOW = "navigator:browser";
const PROPERTY_NAME_COLOR = "theme-fg-color5";
const PROPERTY_VALUE_COLOR = "theme-fg-color1";
const COMMENT_COLOR = "theme-comment";
@ -256,12 +255,11 @@ function MdnDocsWidget(tooltipDocument) {
l10n.strings.GetStringFromName("docsTooltip.visitMDN");
// listen for clicks and open in the browser window instead
let browserWindow = Services.wm.getMostRecentWindow(BROWSER_WINDOW);
let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
this.elements.linkToMdn.addEventListener("click", function (e) {
e.stopPropagation();
e.preventDefault();
let link = e.target.href;
browserWindow.gBrowser.addTab(link);
mainWindow.openUILinkIn(e.target.href, "tab", { inBackground: true });
});
}

View File

@ -16,6 +16,7 @@ const EventEmitter = require("devtools/shared/event-emitter");
const {colorUtils} = require("devtools/client/shared/css-color");
const Heritage = require("sdk/core/heritage");
const {Eyedropper} = require("devtools/client/eyedropper/eyedropper");
const {gDevTools} = require("devtools/client/framework/devtools");
const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
@ -920,11 +921,11 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
let windowType = chromeWindow.document.documentElement
.getAttribute("windowtype");
let toolboxWindow;
if (windowType != "navigator:browser") {
if (windowType != gDevTools.chromeWindowType) {
// this means the toolbox is in a seperate window. We need to make
// sure we'll be inspecting the browser window instead
toolboxWindow = chromeWindow;
chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
chromeWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
chromeWindow.focus();
}
let dropper = new Eyedropper(chromeWindow, { copyOnSelect: false,

View File

@ -1,5 +1,3 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
/* 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/. */
@ -65,12 +63,12 @@ HUD_SERVICE.prototype =
lastFinishedRequest: null,
/**
* Firefox-specific current tab getter
* Get the current context, which is the main application window.
*
* @returns nsIDOMWindow
*/
currentContext: function HS_currentContext() {
return Services.wm.getMostRecentWindow("navigator:browser");
return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
},
/**
@ -295,7 +293,7 @@ function WebConsole(aTarget, aIframeWindow, aChromeWindow)
this.browserWindow = this.chromeWindow.top;
let element = this.browserWindow.document.documentElement;
if (element.getAttribute("windowtype") != "navigator:browser") {
if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
this.browserWindow = HUDService.currentContext();
}
@ -438,7 +436,7 @@ WebConsole.prototype = {
viewSource: function WC_viewSource(aSourceURL, aSourceLine) {
// Attempt to access view source via a browser first, which may display it in
// a tab, if enabled.
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
if (browserWin && browserWin.BrowserViewSourceOfDocument) {
return browserWin.BrowserViewSourceOfDocument({
URL: aSourceURL,

View File

@ -5,14 +5,17 @@
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
document.querySelector("#aboutaddons").onclick = function () {
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
browserWin.BrowserOpenAddonsMgr("addons://list/extension");
let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
if (browserWin && browserWin.BrowserOpenAddonsMgr) {
browserWin.BrowserOpenAddonsMgr("addons://list/extension");
}
};
document.querySelector("#close").onclick = CloseUI;
GetAvailableAddons().then(BuildUI, (e) => {

View File

@ -238,11 +238,10 @@ var UI = {
openInBrowser: function (url) {
// Open a URL in a Firefox window
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWin) {
let gBrowser = browserWin.gBrowser;
gBrowser.selectedTab = gBrowser.addTab(url);
browserWin.focus();
let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
if (mainWindow) {
mainWindow.openUILinkIn(url, "tab");
mainWindow.focus()
} else {
window.open(url);
}

View File

@ -82,6 +82,15 @@ this.DevToolsLoader = function DevToolsLoader() {
};
DevToolsLoader.prototype = {
destroy: function (reason = "shutdown") {
Services.obs.removeObserver(this, "devtools-unload");
if (this._provider) {
this._provider.unload(reason);
delete this._provider;
}
},
get provider() {
if (!this._provider) {
this._loadProvider();
@ -177,12 +186,7 @@ DevToolsLoader.prototype = {
if (topic != "devtools-unload") {
return;
}
Services.obs.removeObserver(this, "devtools-unload");
if (this._provider) {
this._provider.unload(data);
delete this._provider;
}
this.destroy(data);
},
/**

View File

@ -69,8 +69,8 @@ exports.items = [{
root.appendChild(link);
link.addEventListener("click", () => {
let gBrowser = context.environment.chromeWindow.gBrowser;
gBrowser.selectedTab = gBrowser.addTab(result.url);
let mainWindow = context.environment.chromeWindow;
mainWindow.openUILinkIn(result.url, "tab");
});
let summary = document.createElement("p");

View File

@ -153,8 +153,8 @@ exports.items = [
root.style.cursor = "pointer";
root.addEventListener("click", () => {
if (imageSummary.href) {
const gBrowser = context.environment.chromeWindow.gBrowser;
gBrowser.selectedTab = gBrowser.addTab(imageSummary.href);
let mainWindow = context.environment.chromeWindow;
mainWindow.openUILinkIn(imageSummary.href, "tab");
} else if (imageSummary.filename) {
const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(imageSummary.filename);
@ -276,12 +276,15 @@ function createScreenshotData(document, args) {
const currentX = window.scrollX;
const currentY = window.scrollY;
let filename = getFilename(args.filename);
if (args.fullpage) {
// Bug 961832: GCLI screenshot shows fixed position element in wrong
// position if we don't scroll to top
window.scrollTo(0,0);
width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
filename = filename.replace(".png", "-fullpage.png");
}
else if (args.selector) {
({ top, left, width, height } = getRect(window, args.selector, window));
@ -323,7 +326,7 @@ function createScreenshotData(document, args) {
data: data,
height: height,
width: width,
filename: getFilename(args.filename),
filename: filename,
});
}

View File

@ -1,541 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "URL.h"
#include "nsGlobalWindow.h"
#include "DOMMediaStream.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MediaSource.h"
#include "mozilla/dom/URLBinding.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsServiceManagerUtils.h"
#include "nsIIOService.h"
#include "nsEscape.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIURL.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URL, mParent, mSearchParams)
NS_IMPL_CYCLE_COLLECTING_ADDREF(URL)
NS_IMPL_CYCLE_COLLECTING_RELEASE(URL)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
URL::URL(nsISupports* aParent, already_AddRefed<nsIURI> aURI)
: mParent(aParent)
, mURI(aURI)
{
}
JSObject*
URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return URLBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<URL>
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
URL& aBase, ErrorResult& aRv)
{
return Constructor(aGlobal.GetAsSupports(), aUrl, aBase.GetURI(), aRv);
}
/* static */ already_AddRefed<URL>
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
const Optional<nsAString>& aBase, ErrorResult& aRv)
{
if (aBase.WasPassed()) {
return Constructor(aGlobal.GetAsSupports(), aUrl, aBase.Value(), aRv);
}
return Constructor(aGlobal.GetAsSupports(), aUrl, nullptr, aRv);
}
/* static */ already_AddRefed<URL>
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
const nsAString& aBase, ErrorResult& aRv)
{
return Constructor(aGlobal.GetAsSupports(), aUrl, aBase, aRv);
}
/* static */ already_AddRefed<URL>
URL::Constructor(nsISupports* aParent, const nsAString& aUrl,
const nsAString& aBase, ErrorResult& aRv)
{
nsCOMPtr<nsIURI> baseUri;
nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr,
nsContentUtils::GetIOService());
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.ThrowTypeError<MSG_INVALID_URL>(aBase);
return nullptr;
}
return Constructor(aParent, aUrl, baseUri, aRv);
}
/* static */
already_AddRefed<URL>
URL::Constructor(nsISupports* aParent, const nsAString& aUrl, nsIURI* aBase,
ErrorResult& aRv)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, aBase,
nsContentUtils::GetIOService());
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.ThrowTypeError<MSG_INVALID_URL>(aUrl);
return nullptr;
}
RefPtr<URL> url = new URL(aParent, uri.forget());
return url.forget();
}
void
URL::CreateObjectURL(const GlobalObject& aGlobal,
Blob& aBlob,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError)
{
CreateObjectURLInternal(aGlobal, aBlob.Impl(),
NS_LITERAL_CSTRING(BLOBURI_SCHEME), aOptions, aResult,
aError);
}
void
URL::CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream,
const mozilla::dom::objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError)
{
CreateObjectURLInternal(aGlobal, &aStream,
NS_LITERAL_CSTRING(MEDIASTREAMURI_SCHEME), aOptions,
aResult, aError);
}
void
URL::CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError)
{
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(aGlobal.Get());
nsCString url;
nsresult rv = nsHostObjectProtocolHandler::
AddDataEntry(NS_LITERAL_CSTRING(MEDIASOURCEURI_SCHEME),
&aSource, principal, url);
if (NS_FAILED(rv)) {
aError.Throw(rv);
return;
}
nsCOMPtr<nsIRunnable> revocation = NS_NewRunnableFunction(
[url] {
nsHostObjectProtocolHandler::RemoveDataEntry(url);
});
nsContentUtils::RunInStableState(revocation.forget());
CopyASCIItoUTF16(url, aResult);
}
void
URL::CreateObjectURLInternal(const GlobalObject& aGlobal, nsISupports* aObject,
const nsACString& aScheme,
const objectURLOptions& aOptions,
nsAString& aResult, ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(aGlobal.Get());
nsAutoCString url;
nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(aScheme, aObject,
principal, url);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
global->RegisterHostObjectURI(url);
CopyASCIItoUTF16(url, aResult);
}
void
URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal.Get());
NS_LossyConvertUTF16toASCII asciiurl(aURL);
nsIPrincipal* urlPrincipal =
nsHostObjectProtocolHandler::GetDataEntryPrincipal(asciiurl);
if (urlPrincipal && principal->Subsumes(urlPrincipal)) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
global->UnregisterHostObjectURI(asciiurl);
nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
}
}
void
URL::GetHref(nsAString& aHref) const
{
aHref.Truncate();
nsAutoCString href;
nsresult rv = mURI->GetSpec(href);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(href, aHref);
}
}
void
URL::SetHref(const nsAString& aHref, ErrorResult& aRv)
{
NS_ConvertUTF16toUTF8 href(aHref);
nsresult rv;
nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
nsCOMPtr<nsIURI> uri;
rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
if (NS_FAILED(rv)) {
aRv.ThrowTypeError<MSG_INVALID_URL>(aHref);
return;
}
mURI = uri;
UpdateURLSearchParams();
}
void
URL::GetOrigin(nsAString& aOrigin) const
{
nsContentUtils::GetUTFOrigin(mURI, aOrigin);
}
void
URL::GetProtocol(nsAString& aProtocol) const
{
nsAutoCString protocol;
if (NS_SUCCEEDED(mURI->GetScheme(protocol))) {
aProtocol.Truncate();
}
CopyASCIItoUTF16(protocol, aProtocol);
aProtocol.Append(char16_t(':'));
}
void
URL::SetProtocol(const nsAString& aProtocol)
{
nsAString::const_iterator start, end;
aProtocol.BeginReading(start);
aProtocol.EndReading(end);
nsAString::const_iterator iter(start);
FindCharInReadable(':', iter, end);
// Changing the protocol of a URL, changes the "nature" of the URI
// implementation. In order to do this properly, we have to serialize the
// existing URL and reparse it in a new object.
nsCOMPtr<nsIURI> clone;
nsresult rv = mURI->Clone(getter_AddRefs(clone));
if (NS_WARN_IF(NS_FAILED(rv)) || !clone) {
return;
}
rv = clone->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoCString href;
rv = clone->GetSpec(href);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), href);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
mURI = uri;
}
#define URL_GETTER( value, func ) \
value.Truncate(); \
nsAutoCString tmp; \
nsresult rv = mURI->func(tmp); \
if (NS_SUCCEEDED(rv)) { \
CopyUTF8toUTF16(tmp, value); \
}
void
URL::GetUsername(nsAString& aUsername) const
{
URL_GETTER(aUsername, GetUsername);
}
void
URL::SetUsername(const nsAString& aUsername)
{
mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
}
void
URL::GetPassword(nsAString& aPassword) const
{
URL_GETTER(aPassword, GetPassword);
}
void
URL::SetPassword(const nsAString& aPassword)
{
mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
}
void
URL::GetHost(nsAString& aHost) const
{
URL_GETTER(aHost, GetHostPort);
}
void
URL::SetHost(const nsAString& aHost)
{
mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
}
void
URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
{
MOZ_ASSERT(mSearchParams);
MOZ_ASSERT(mSearchParams == aSearchParams);
nsAutoString search;
mSearchParams->Serialize(search);
SetSearchInternal(search);
}
void
URL::UpdateURLSearchParams()
{
if (!mSearchParams) {
return;
}
nsAutoCString search;
nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
if (url) {
nsresult rv = url->GetQuery(search);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get the query from a nsIURL.");
}
}
mSearchParams->ParseInput(search);
}
void
URL::GetHostname(nsAString& aHostname) const
{
aHostname.Truncate();
nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname);
}
void
URL::SetHostname(const nsAString& aHostname)
{
// nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
// The return code is silently ignored
mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
}
void
URL::GetPort(nsAString& aPort) const
{
aPort.Truncate();
int32_t port;
nsresult rv = mURI->GetPort(&port);
if (NS_SUCCEEDED(rv) && port != -1) {
nsAutoString portStr;
portStr.AppendInt(port, 10);
aPort.Assign(portStr);
}
}
void
URL::SetPort(const nsAString& aPort)
{
nsresult rv;
nsAutoString portStr(aPort);
int32_t port = -1;
// nsIURI uses -1 as default value.
if (!portStr.IsEmpty()) {
port = portStr.ToInteger(&rv);
if (NS_FAILED(rv)) {
return;
}
}
mURI->SetPort(port);
}
void
URL::GetPathname(nsAString& aPathname) const
{
aPathname.Truncate();
nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
if (!url) {
nsAutoCString path;
nsresult rv = mURI->GetPath(path);
if (NS_FAILED(rv)){
// Do not throw! Not having a valid URI or URL should result in an empty
// string.
return;
}
CopyUTF8toUTF16(path, aPathname);
return;
}
nsAutoCString file;
nsresult rv = url->GetFilePath(file);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(file, aPathname);
}
}
void
URL::SetPathname(const nsAString& aPathname)
{
nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
if (!url) {
// Ignore failures to be compatible with NS4.
return;
}
url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
}
void
URL::GetSearch(nsAString& aSearch) const
{
aSearch.Truncate();
nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
if (!url) {
// Do not throw! Not having a valid URI or URL should result in an empty
// string.
return;
}
nsAutoCString search;
nsresult rv = url->GetQuery(search);
if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
}
}
void
URL::SetSearch(const nsAString& aSearch)
{
SetSearchInternal(aSearch);
UpdateURLSearchParams();
}
void
URL::SetSearchInternal(const nsAString& aSearch)
{
nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
if (!url) {
// Ignore failures to be compatible with NS4.
return;
}
url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
}
URLSearchParams*
URL::SearchParams()
{
CreateSearchParamsIfNeeded();
return mSearchParams;
}
void
URL::GetHash(nsAString& aHash) const
{
aHash.Truncate();
nsAutoCString ref;
nsresult rv = mURI->GetRef(ref);
if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
aHash.Assign(char16_t('#'));
if (nsContentUtils::GettersDecodeURLHash()) {
NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
}
AppendUTF8toUTF16(ref, aHash);
}
}
void
URL::SetHash(const nsAString& aHash)
{
mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
}
bool IsChromeURI(nsIURI* aURI)
{
bool isChrome = false;
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
return isChrome;
return false;
}
void
URL::CreateSearchParamsIfNeeded()
{
if (!mSearchParams) {
mSearchParams = new URLSearchParams(mParent, this);
UpdateURLSearchParams();
}
}
} // namespace dom
} // namespace mozilla

View File

@ -1,173 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef URL_h___
#define URL_h___
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/URLSearchParams.h"
#include "nsCycleCollectionParticipant.h"
#include "nsString.h"
#include "nsWrapperCache.h"
class nsISupports;
class nsIURI;
namespace mozilla {
class ErrorResult;
class DOMMediaStream;
namespace dom {
class Blob;
class MediaSource;
class GlobalObject;
struct objectURLOptions;
namespace workers {
class URLProxy;
} // namespace workers
class URL final : public URLSearchParamsObserver
, public nsWrapperCache
{
~URL() {}
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URL)
URL(nsISupports* aParent, already_AddRefed<nsIURI> aURI);
// WebIDL methods
nsISupports* GetParentObject() const
{
return mParent;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<URL>
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
URL& aBase, ErrorResult& aRv);
static already_AddRefed<URL>
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
const Optional<nsAString>& aBase, ErrorResult& aRv);
// Versions of Constructor that we can share with workers and other code.
static already_AddRefed<URL>
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
const nsAString& aBase, ErrorResult& aRv);
static already_AddRefed<URL>
Constructor(nsISupports* aParent, const nsAString& aUrl,
const nsAString& aBase, ErrorResult& aRv);
static already_AddRefed<URL>
Constructor(nsISupports* aParent, const nsAString& aUrl,
nsIURI* aBase, ErrorResult& aRv);
static void CreateObjectURL(const GlobalObject& aGlobal,
Blob& aBlob,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError);
static void CreateObjectURL(const GlobalObject& aGlobal,
DOMMediaStream& aStream,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError);
static void CreateObjectURL(const GlobalObject& aGlobal,
MediaSource& aSource,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError);
static void RevokeObjectURL(const GlobalObject& aGlobal,
const nsAString& aURL,
ErrorResult& aRv);
void GetHref(nsAString& aHref) const;
void SetHref(const nsAString& aHref, ErrorResult& aRv);
void GetOrigin(nsAString& aOrigin) const;
void GetProtocol(nsAString& aProtocol) const;
void SetProtocol(const nsAString& aProtocol);
void GetUsername(nsAString& aUsername) const;
void SetUsername(const nsAString& aUsername);
void GetPassword(nsAString& aPassword) const;
void SetPassword(const nsAString& aPassword);
void GetHost(nsAString& aHost) const;
void SetHost(const nsAString& aHost);
void GetHostname(nsAString& aHostname) const;
void SetHostname(const nsAString& aHostname);
void GetPort(nsAString& aPort) const;
void SetPort(const nsAString& aPort);
void GetPathname(nsAString& aPathname) const;
void SetPathname(const nsAString& aPathname);
void GetSearch(nsAString& aRetval) const;
void SetSearch(const nsAString& aArg);
URLSearchParams* SearchParams();
void GetHash(nsAString& aRetval) const;
void SetHash(const nsAString& aArg);
void Stringify(nsAString& aRetval) const
{
GetHref(aRetval);
}
// URLSearchParamsObserver
void URLSearchParamsUpdated(URLSearchParams* aSearchParams) override;
private:
nsIURI* GetURI() const
{
return mURI;
}
void CreateSearchParamsIfNeeded();
void SetSearchInternal(const nsAString& aSearch);
void UpdateURLSearchParams();
static void CreateObjectURLInternal(const GlobalObject& aGlobal,
nsISupports* aObject,
const nsACString& aScheme,
const objectURLOptions& aOptions,
nsAString& aResult,
ErrorResult& aError);
nsCOMPtr<nsISupports> mParent;
nsCOMPtr<nsIURI> mURI;
RefPtr<URLSearchParams> mSearchParams;
friend class mozilla::dom::workers::URLProxy;
};
bool IsChromeURI(nsIURI* aURI);
} // namespace dom
} // namespace mozilla
#endif /* URL_h___ */

View File

@ -200,8 +200,6 @@ EXPORTS.mozilla.dom += [
'SubtleCrypto.h',
'Text.h',
'TreeWalker.h',
'URL.h',
'URLSearchParams.h',
'WebKitCSSMatrix.h',
'WebSocket.h',
'WindowOrientationObserver.h',
@ -336,8 +334,6 @@ UNIFIED_SOURCES += [
'TextInputProcessor.cpp',
'ThirdPartyUtil.cpp',
'TreeWalker.cpp',
'URL.cpp',
'URLSearchParams.cpp',
'WebKitCSSMatrix.cpp',
'WebSocket.cpp',
'WindowNamedPropertiesHandler.cpp',

View File

@ -1914,6 +1914,9 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
case QUERY_CHARACTER_AT_POINT:
message = eQueryCharacterAtPoint;
break;
case QUERY_TEXT_RECT_ARRAY:
message = eQueryTextRectArray;
break;
default:
return NS_ERROR_INVALID_ARG;
}
@ -2031,6 +2034,9 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
case eQuerySelectedText:
queryEvent.InitForQuerySelectedText(selectionType, options);
break;
case eQueryTextRectArray:
queryEvent.InitForQueryTextRectArray(aOffset, aLength, options);
break;
default:
queryEvent.Init(options);
break;

View File

@ -284,7 +284,6 @@ static bool gMouseDown = false;
static bool gDragServiceDisabled = false;
static FILE *gDumpFile = nullptr;
static uint32_t gSerialCounter = 0;
static uint32_t gTimeoutsRecentlySet = 0;
static TimeStamp gLastRecordedRecentTimeouts;
#define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
@ -9236,8 +9235,6 @@ nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgumen
return nullptr;
}
Telemetry::Accumulate(Telemetry::DOM_WINDOW_SHOWMODALDIALOG_USED, true);
RefPtr<DialogValueHolder> argHolder =
new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument);
@ -12076,7 +12073,6 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
timeout->mPrincipal = ourPrincipal;
}
++gTimeoutsRecentlySet;
TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
if (!IsFrozen() && !mTimeoutsSuspendDepth) {
@ -12459,9 +12455,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL);
if (gLastRecordedRecentTimeouts.IsNull() ||
now - gLastRecordedRecentTimeouts > recordingInterval) {
uint32_t count = gTimeoutsRecentlySet;
gTimeoutsRecentlySet = 0;
Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count);
gLastRecordedRecentTimeouts = now;
}
@ -12481,8 +12474,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
// the logic in ResetTimersForNonBackgroundWindow will need to change.
mTimeoutInsertionPoint = dummy_timeout;
Telemetry::AutoCounter<Telemetry::DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT> timeoutsRan;
for (nsTimeout *timeout = mTimeouts.getFirst();
timeout != dummy_timeout && !IsFrozen();
timeout = nextTimeout) {
@ -12516,7 +12507,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
}
// This timeout is good to run
++timeoutsRan;
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
if (timeout_was_cleared) {

View File

@ -4,9 +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/. */
#include "nsQueryContentEventResult.h"
#include "nsIWidget.h"
#include "nsPoint.h"
#include "nsQueryContentEventResult.h"
#include "mozilla/Move.h"
#include "mozilla/TextEvents.h"
using namespace mozilla;
@ -199,9 +200,30 @@ nsQueryContentEventResult::GetTentativeCaretOffsetNotFound(bool* aNotFound)
return NS_OK;
}
NS_IMETHODIMP
nsQueryContentEventResult::GetCharacterRect(int32_t aOffset,
int32_t* aLeft, int32_t* aTop,
int32_t* aWidth, int32_t* aHeight)
{
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(mEventMessage == eQueryTextRectArray,
NS_ERROR_NOT_AVAILABLE);
if (NS_WARN_IF(mRectArray.Length() <= static_cast<uint32_t>(aOffset))) {
return NS_ERROR_FAILURE;
}
*aLeft = mRectArray[aOffset].x;
*aTop = mRectArray[aOffset].y;
*aWidth = mRectArray[aOffset].width;
*aHeight = mRectArray[aOffset].height;
return NS_OK;
}
void
nsQueryContentEventResult::SetEventResult(nsIWidget* aWidget,
const WidgetQueryContentEvent &aEvent)
WidgetQueryContentEvent &aEvent)
{
mEventMessage = aEvent.mMessage;
mSucceeded = aEvent.mSucceeded;
@ -210,6 +232,9 @@ nsQueryContentEventResult::SetEventResult(nsIWidget* aWidget,
mOffset = aEvent.mReply.mOffset;
mTentativeCaretOffset = aEvent.mReply.mTentativeCaretOffset;
mString = aEvent.mReply.mString;
mRectArray = mozilla::Move(aEvent.mReply.mRectArray);
// Mark as result that is longer used.
aEvent.mSucceeded = false;
if (!IsRectRelatedPropertyAvailable(mEventMessage) ||
!aWidget || !mSucceeded) {
@ -225,4 +250,7 @@ nsQueryContentEventResult::SetEventResult(nsIWidget* aWidget,
LayoutDeviceIntPoint offset =
aWidget->WidgetToScreenOffset() - topWidget->WidgetToScreenOffset();
mRect.MoveBy(-offset);
for (size_t i = 0; i < mRectArray.Length(); i++) {
mRectArray[i].MoveBy(-offset);
}
}

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