mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Merge mozilla-central to autoland
This commit is contained in:
commit
13ce7275a3
1
aclocal.m4
vendored
1
aclocal.m4
vendored
@ -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()
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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, "").
|
||||
|
@ -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}");
|
||||
|
@ -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");
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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 = [];
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
});
|
||||
|
@ -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;"/>
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -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
|
||||
|
43
browser/extensions/loop/bootstrap.js
vendored
43
browser/extensions/loop/bootstrap.js
vendored
@ -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;},
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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);}});},
|
||||
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 () {
|
||||
|
@ -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();}},
|
||||
|
@ -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.
|
||||
|
@ -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);}}
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
146
build/moz.configure/rust.configure
Normal file
146
build/moz.configure/rust.configure
Normal 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)
|
@ -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')
|
||||
|
@ -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, "
|
||||
|
@ -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):
|
||||
|
@ -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() {
|
||||
|
23
devtools/client/aboutdebugging/modules/addon.js
Normal file
23
devtools/client/aboutdebugging/modules/addon.js
Normal 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;
|
||||
}
|
||||
});
|
||||
};
|
@ -3,5 +3,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'addon.js',
|
||||
'worker.js',
|
||||
)
|
||||
|
@ -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 });
|
||||
},
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 });
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
@ -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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -17,7 +17,6 @@ DIRS += [
|
||||
DevToolsModules(
|
||||
'app.js',
|
||||
'constants.js',
|
||||
'devices.js',
|
||||
'index.css',
|
||||
'manager.js',
|
||||
'reducers.js',
|
||||
|
@ -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,
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
});
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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";
|
||||
|
@ -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)),
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
21
devtools/client/responsive.html/utils/enum.js
Normal file
21
devtools/client/responsive.html/utils/enum.js
Normal 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;
|
||||
}
|
||||
|
||||
};
|
@ -6,6 +6,7 @@
|
||||
|
||||
DevToolsModules(
|
||||
'e10s.js',
|
||||
'enum.js',
|
||||
'l10n.js',
|
||||
'message.js',
|
||||
)
|
||||
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
541
dom/base/URL.cpp
541
dom/base/URL.cpp
@ -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
|
173
dom/base/URL.h
173
dom/base/URL.h
@ -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___ */
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user