Merge m-c to b2ginbound a=merge
|
@ -1919,7 +1919,8 @@ pref("dom.serviceWorkers.enabled", true);
|
|||
#endif
|
||||
|
||||
pref("browser.pocket.enabled", false);
|
||||
pref("browser.pocket.hostname", "localhost");
|
||||
pref("browser.pocket.api", "api.getpocket.com");
|
||||
pref("browser.pocket.site", "getpocket.com");
|
||||
pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
|
||||
pref("browser.pocket.useLocaleList", true);
|
||||
pref("browser.pocket.enabledLocales", "en-US");
|
||||
|
|
|
@ -6,17 +6,4 @@
|
|||
# of Firefox include these strings in browser.properties
|
||||
|
||||
pocket-button.label = Pocket
|
||||
pocket-button.tooltiptext = Send this page to Pocket
|
||||
pocket-header = Pocket
|
||||
pocket-login-required-tagline = Catch the best content you find online with Pocket.
|
||||
pocket-signup-with-fxa = Sign up with Firefox
|
||||
pocket-signup-with-email = Sign up with email
|
||||
pocket-account-question = Already have an account?
|
||||
pocket-login-now = Log in now
|
||||
pocket-page-saved-header = Page Saved
|
||||
pocket-open-pocket = Open Pocket
|
||||
pocket-remove-page = Remove Page
|
||||
pocket-page-tags-field = Add Tags
|
||||
pocket-page-tags-add = Save
|
||||
pocket-page-suggested-tags-header = Suggested Tags
|
||||
pocket-signup-or = Or
|
||||
pocket-button.tooltiptext = Save to Pocket
|
||||
|
|
|
@ -52,7 +52,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
|
|||
"nsIDNSService");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Pocket",
|
||||
"resource:///modules/Pocket.jsm");
|
||||
|
||||
const nsIWebNavigation = Ci.nsIWebNavigation;
|
||||
|
||||
|
@ -4162,6 +4163,7 @@ var XULBrowserWindow = {
|
|||
BookmarkingUI.onLocationChange();
|
||||
SocialUI.updateState(location);
|
||||
UITour.onLocationChange(location);
|
||||
Pocket.onLocationChange(browser, aLocationURI);
|
||||
}
|
||||
|
||||
// Utility functions for disabling find
|
||||
|
|
|
@ -730,9 +730,9 @@
|
|||
fullscreentoolbar="true" mode="icons" customizable="true"
|
||||
iconsize="small"
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
defaultset="urlbar-container,search-container,developer-button,bookmarks-menu-button,downloads-button,home-button,loop-button"
|
||||
defaultset="urlbar-container,search-container,developer-button,bookmarks-menu-button,pocket-button,downloads-button,home-button,loop-button"
|
||||
#else
|
||||
defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-button"
|
||||
defaultset="urlbar-container,search-container,bookmarks-menu-button,pocket-button,downloads-button,home-button,loop-button"
|
||||
#endif
|
||||
customizationtarget="nav-bar-customization-target"
|
||||
overflowable="true"
|
||||
|
|
|
@ -187,7 +187,7 @@ nsContextMenu.prototype = {
|
|||
(uri.schemeIs("http") || uri.schemeIs("https") ||
|
||||
(uri.schemeIs("about") && ReaderMode.getOriginalUrl(uri.spec)));
|
||||
}
|
||||
this.showItem("context-pocket", canPocket && window.Pocket && Pocket.isLoggedIn);
|
||||
this.showItem("context-pocket", canPocket && window.pktApi && window.pktApi.isUserLoggedIn());
|
||||
},
|
||||
|
||||
initViewItems: function CM_initViewItems() {
|
||||
|
|
|
@ -208,6 +208,15 @@ let CustomizableUIInternal = {
|
|||
"loop-button",
|
||||
];
|
||||
|
||||
// Insert the Pocket button after the bookmarks button if it's present.
|
||||
for (let widgetDefinition of CustomizableWidgets) {
|
||||
if (widgetDefinition.id == "pocket-button") {
|
||||
let idx = navbarPlacements.indexOf("bookmarks-menu-button") + 1;
|
||||
navbarPlacements.splice(idx, 0, widgetDefinition.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref(kPrefWebIDEInNavbar)) {
|
||||
navbarPlacements.push("webide-button");
|
||||
}
|
||||
|
@ -288,19 +297,40 @@ let CustomizableUIInternal = {
|
|||
},
|
||||
|
||||
_introduceNewBuiltinWidgets: function() {
|
||||
if (!gSavedState || gSavedState.currentVersion >= kVersion) {
|
||||
// We should still enter even if gSavedState.currentVersion >= kVersion
|
||||
// because the per-widget pref facility is independent of versioning.
|
||||
if (!gSavedState) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentVersion = gSavedState.currentVersion;
|
||||
for (let [id, widget] of gPalette) {
|
||||
if (widget._introducedInVersion > currentVersion &&
|
||||
widget.defaultArea) {
|
||||
let futurePlacements = gFuturePlacements.get(widget.defaultArea);
|
||||
if (futurePlacements) {
|
||||
futurePlacements.add(id);
|
||||
} else {
|
||||
gFuturePlacements.set(widget.defaultArea, new Set([id]));
|
||||
if (widget.defaultArea) {
|
||||
let shouldAdd = false;
|
||||
let shouldSetPref = false;
|
||||
let prefId = "browser.toolbarbuttons.introduced." + widget.id;
|
||||
if (widget._introducedInVersion === "pref") {
|
||||
try {
|
||||
shouldAdd = !Services.prefs.getBoolPref(prefId);
|
||||
} catch (ex) {
|
||||
// Pref doesn't exist:
|
||||
shouldAdd = true;
|
||||
}
|
||||
shouldSetPref = shouldAdd;
|
||||
} else if (widget._introducedInVersion > currentVersion) {
|
||||
shouldAdd = true;
|
||||
}
|
||||
|
||||
if (shouldAdd) {
|
||||
let futurePlacements = gFuturePlacements.get(widget.defaultArea);
|
||||
if (futurePlacements) {
|
||||
futurePlacements.add(id);
|
||||
} else {
|
||||
gFuturePlacements.set(widget.defaultArea, new Set([id]));
|
||||
}
|
||||
if (shouldSetPref) {
|
||||
Services.prefs.setBoolPref(prefId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2087,6 +2117,14 @@ let CustomizableUIInternal = {
|
|||
// opened - so we know there's no build areas to handle. Also, builtin
|
||||
// widgets are expected to be (mostly) static, so shouldn't affect the
|
||||
// current placement settings.
|
||||
|
||||
// This allows a widget to be both built-in by default but also able to be
|
||||
// destroyed based on criteria that may not be available when the widget is
|
||||
// created -- for example, because some other feature in the browser
|
||||
// supersedes the widget.
|
||||
let conditionalDestroyPromise = aData.conditionalDestroyPromise || null;
|
||||
delete aData.conditionalDestroyPromise;
|
||||
|
||||
let widget = this.normalizeWidget(aData, CustomizableUI.SOURCE_BUILTIN);
|
||||
if (!widget) {
|
||||
ERROR("Error creating builtin widget: " + aData.id);
|
||||
|
@ -2095,6 +2133,16 @@ let CustomizableUIInternal = {
|
|||
|
||||
LOG("Creating built-in widget with id: " + widget.id);
|
||||
gPalette.set(widget.id, widget);
|
||||
|
||||
if (conditionalDestroyPromise) {
|
||||
conditionalDestroyPromise.then(shouldDestroy => {
|
||||
if (shouldDestroy) {
|
||||
this.destroyWidget(widget.id);
|
||||
}
|
||||
}, err => {
|
||||
Cu.reportError(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Returns true if the area will eventually lazily restore (but hasn't yet).
|
||||
|
|
|
@ -26,6 +26,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
|
|||
"resource://gre/modules/CharsetMenu.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
|
||||
"resource://gre/modules/SocialService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
|
||||
const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
|
||||
|
@ -1081,16 +1085,44 @@ if (Services.prefs.getBoolPref("browser.pocket.enabled")) {
|
|||
if (isEnabledForLocale) {
|
||||
let pocketButton = {
|
||||
id: "pocket-button",
|
||||
defaultArea: CustomizableUI.AREA_NAVBAR,
|
||||
introducedInVersion: "pref",
|
||||
type: "view",
|
||||
viewId: "PanelUI-pocketView",
|
||||
label: PocketBundle.GetStringFromName("pocket-button.label"),
|
||||
tooltiptext: PocketBundle.GetStringFromName("pocket-button.tooltiptext"),
|
||||
onViewShowing: Pocket.onPanelViewShowing,
|
||||
onViewHiding: Pocket.onPanelViewHiding,
|
||||
|
||||
// If the user has the "classic" Pocket add-on installed, use that instead
|
||||
// and destroy the widget.
|
||||
conditionalDestroyPromise: new Promise(resolve => {
|
||||
AddonManager.getAddonByID("isreaditlater@ideashower.com", addon => {
|
||||
resolve(addon && addon.isActive);
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
CustomizableWidgets.push(pocketButton);
|
||||
CustomizableUI.addListener(pocketButton);
|
||||
|
||||
// Uninstall the Pocket social provider if it exists, but only if we haven't
|
||||
// already uninstalled it in this manner. That way the user can reinstall
|
||||
// it if they prefer it without its being uninstalled every time they start
|
||||
// the browser.
|
||||
let origin = "https://getpocket.com";
|
||||
SocialService.getProvider(origin, provider => {
|
||||
if (provider) {
|
||||
let pref = "social.backup.getpocket-com";
|
||||
if (!Services.prefs.prefHasUserValue(pref)) {
|
||||
let str = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
str.data = JSON.stringify(provider.manifest);
|
||||
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, str);
|
||||
SocialService.uninstallProvider(origin, () => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,27 @@ function test() {
|
|||
ok(haveNavbarPlacements, "Should have placements for nav-bar");
|
||||
if (haveNavbarPlacements) {
|
||||
let placements = [...gFuturePlacements.get(CustomizableUI.AREA_NAVBAR)];
|
||||
|
||||
// Ignore widgets that are placed using the pref facility and not the
|
||||
// versioned facility. They're independent of kVersion and the saved
|
||||
// state's current version, so they may be present in the placements.
|
||||
for (let i = 0; i < placements.length; ) {
|
||||
if (placements[i] == testWidgetNew.id) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
let pref = "browser.toolbarbuttons.introduced." + placements[i];
|
||||
let introduced = false;
|
||||
try {
|
||||
introduced = Services.prefs.getBoolPref(pref);
|
||||
} catch (ex) {}
|
||||
if (!introduced) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
placements.splice(i, 1);
|
||||
}
|
||||
|
||||
is(placements.length, 1, "Should have 1 newly placed widget in nav-bar");
|
||||
is(placements[0], testWidgetNew.id, "Should have our test widget to be placed in nav-bar");
|
||||
}
|
||||
|
|
|
@ -734,6 +734,7 @@ BrowserGlue.prototype = {
|
|||
LightweightThemeManager.addBuiltInTheme({
|
||||
id: "firefox-devedition@mozilla.org",
|
||||
name: themeName,
|
||||
accentcolor: "transparent",
|
||||
headerURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.header.png",
|
||||
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
|
||||
author: vendorShortName,
|
||||
|
|
|
@ -7,118 +7,14 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["Pocket"];
|
||||
|
||||
Cu.import("resource://gre/modules/Http.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PocketBundle", function() {
|
||||
const kPocketBundle = "chrome://browser/content/browser-pocket.properties";
|
||||
return Services.strings.createBundle(kPocketBundle);
|
||||
});
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||
"resource:///modules/CustomizableUI.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
|
||||
"resource://gre/modules/ReaderMode.jsm");
|
||||
|
||||
let Pocket = {
|
||||
get isLoggedIn() {
|
||||
return !!this._accessToken;
|
||||
},
|
||||
|
||||
prefBranch: Services.prefs.getBranch("browser.pocket.settings."),
|
||||
|
||||
get hostname() Services.prefs.getCharPref("browser.pocket.hostname"),
|
||||
|
||||
get listURL() { return "https://" + Pocket.hostname; },
|
||||
|
||||
get _accessToken() {
|
||||
let sessionId, accessToken;
|
||||
let cookies = Services.cookies.getCookiesFromHost(this.hostname);
|
||||
while (cookies.hasMoreElements()) {
|
||||
let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
|
||||
if (cookie.name == "ftv1")
|
||||
accessToken = cookie.value;
|
||||
else if (cookie.name == "fsv1")
|
||||
sessionId = cookie.value;
|
||||
}
|
||||
|
||||
if (!accessToken)
|
||||
return null;
|
||||
|
||||
let lastSessionId;
|
||||
try {
|
||||
lastSessionId = this.prefBranch.getCharPref("sessionId");
|
||||
} catch (e) { }
|
||||
if (sessionId != lastSessionId)
|
||||
this.prefBranch.deleteBranch("");
|
||||
this.prefBranch.setCharPref("sessionId", sessionId);
|
||||
|
||||
return accessToken;
|
||||
},
|
||||
|
||||
save(url, title) {
|
||||
let since = "0";
|
||||
try {
|
||||
since = this.prefBranch.getCharPref("latestSince");
|
||||
} catch (e) { }
|
||||
|
||||
let data = {url: url, since: since, title: title};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._send("firefox/save", data,
|
||||
data => {
|
||||
this.prefBranch.setCharPref("latestSince", data.since);
|
||||
resolve(data.item);
|
||||
},
|
||||
error => { reject(error); }
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
remove(itemId) {
|
||||
let actions = [{ action: "delete", item_id: itemId }];
|
||||
this._send("send", {actions: JSON.stringify(actions)});
|
||||
},
|
||||
|
||||
tag(itemId, tags) {
|
||||
let actions = [{ action: "tags_add", item_id: itemId, tags: tags }];
|
||||
this._send("send", {actions: JSON.stringify(actions)});
|
||||
},
|
||||
|
||||
_send(url, data, onSuccess, onError) {
|
||||
let token = this._accessToken;
|
||||
if (!token)
|
||||
throw "Attempted to send a request to Pocket while not logged in";
|
||||
|
||||
let browserLocale = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
.getService(Ci.nsIXULChromeRegistry)
|
||||
.getSelectedLocale("browser");
|
||||
|
||||
let postData = [
|
||||
["access_token", token],
|
||||
["consumer_key", "40249-e88c401e1b1f2242d9e441c4"],
|
||||
["locale_lang", browserLocale]
|
||||
];
|
||||
|
||||
for (let key in data)
|
||||
postData.push([key, data[key]]);
|
||||
|
||||
httpRequest("https://" + this.hostname + "/v3/" + url, {
|
||||
headers: [["X-Accept", "application/json"]],
|
||||
postData: postData,
|
||||
onLoad: (responseText) => {
|
||||
if (onSuccess)
|
||||
onSuccess(JSON.parse(responseText));
|
||||
},
|
||||
onError: function(error, responseText, xhr) {
|
||||
if (!onError)
|
||||
return;
|
||||
let errorMessage = xhr.getResponseHeader("X-Error");
|
||||
onError(new Error(error + " - " + errorMessage));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Functions related to the Pocket panel UI.
|
||||
*/
|
||||
|
@ -132,4 +28,28 @@ let Pocket = {
|
|||
let window = event.target.ownerDocument.defaultView;
|
||||
window.pktUI.pocketPanelDidHide(event);
|
||||
},
|
||||
|
||||
// Called on tab/urlbar/location changes and after customization. Update
|
||||
// anything that is tab specific.
|
||||
onLocationChange(browser, locationURI) {
|
||||
if (!locationURI) {
|
||||
return;
|
||||
}
|
||||
let widget = CustomizableUI.getWidget("pocket-button");
|
||||
for (let instance of widget.instances) {
|
||||
let node = instance.node;
|
||||
if (!node ||
|
||||
node.ownerDocument != browser.ownerDocument) {
|
||||
continue;
|
||||
}
|
||||
if (node) {
|
||||
let win = browser.ownerDocument.defaultView;
|
||||
node.disabled = win.pktApi.isUserLoggedIn() &&
|
||||
!locationURI.schemeIs("http") &&
|
||||
!locationURI.schemeIs("https") &&
|
||||
!(locationURI.schemeIs("about") &&
|
||||
locationURI.spec.toLowerCase().startsWith("about:reader?url="));
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18,14 +18,20 @@ browser.jar:
|
|||
content/browser/pocket/panels/img/pocketlogosolo@2x.png (panels/img/pocketlogosolo@2x.png)
|
||||
content/browser/pocket/panels/img/pocketmenuitem16.png (panels/img/pocketmenuitem16.png)
|
||||
content/browser/pocket/panels/img/pocketmenuitem16@2x.png (panels/img/pocketmenuitem16@2x.png)
|
||||
content/browser/pocket/panels/img/pocketmultidevices@1x.png (panels/img/pocketmultidevices@1x.png)
|
||||
content/browser/pocket/panels/img/pocketmultidevices@2x.png (panels/img/pocketmultidevices@2x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_button@1x.png (panels/img/pocketsignup_button@1x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_button@2x.png (panels/img/pocketsignup_button@2x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_devices@1x.png (panels/img/pocketsignup_devices@1x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_devices@2x.png (panels/img/pocketsignup_devices@2x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_hero@1x.png (panels/img/pocketsignup_hero@1x.png)
|
||||
content/browser/pocket/panels/img/pocketsignup_hero@2x.png (panels/img/pocketsignup_hero@2x.png)
|
||||
content/browser/pocket/panels/img/signup_firefoxlogo@1x.png (panels/img/signup_firefoxlogo@1x.png)
|
||||
content/browser/pocket/panels/img/signup_firefoxlogo@2x.png (panels/img/signup_firefoxlogo@2x.png)
|
||||
content/browser/pocket/panels/img/signup_help@1x.png (panels/img/signup_help@1x.png)
|
||||
content/browser/pocket/panels/img/signup_help@2x.png (panels/img/signup_help@2x.png)
|
||||
content/browser/pocket/panels/img/tag_close@1x.png (panels/img/tag_close@1x.png)
|
||||
content/browser/pocket/panels/img/tag_close@2x.png (panels/img/tag_close@2x.png)
|
||||
content/browser/pocket/panels/img/tag_closeactive@1x.png (panels/img/tag_closeactive@1x.png)
|
||||
content/browser/pocket/panels/img/tag_closeactive@2x.png (panels/img/tag_closeactive@2x.png)
|
||||
content/browser/pocket/panels/js/dictionary.js (panels/js/dictionary.js)
|
||||
content/browser/pocket/panels/js/messages.js (panels/js/messages.js)
|
||||
content/browser/pocket/panels/js/saved.js (panels/js/saved.js)
|
||||
|
@ -37,3 +43,4 @@ browser.jar:
|
|||
content/browser/pocket/panels/tmpl/saved_premiumshell.handlebars (panels/tmpl/saved_premiumshell.handlebars)
|
||||
content/browser/pocket/panels/tmpl/saved_shell.handlebars (panels/tmpl/saved_shell.handlebars)
|
||||
content/browser/pocket/panels/tmpl/signup_shell.handlebars (panels/tmpl/signup_shell.handlebars)
|
||||
content/browser/pocket/panels/tmpl/signupstoryboard_shell.handlebars (panels/tmpl/signupstoryboard_shell.handlebars)
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
// TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
|
||||
// TODO : Remove console.log entries
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
|
||||
|
||||
var pktUI = (function() {
|
||||
|
||||
// -- Initialization (on startup and new windows) -- //
|
||||
|
@ -277,16 +280,35 @@ var pktUI = (function() {
|
|||
* Show the sign-up panel
|
||||
*/
|
||||
function showSignUp() {
|
||||
showPanel("chrome://browser/content/pocket/panels/signup.html", {
|
||||
onShow: function() {
|
||||
// Open and resize the panel
|
||||
resizePanel({
|
||||
getFirefoxAccountSignedInUser(function(userdata)
|
||||
{
|
||||
var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
|
||||
var startheight = 490;
|
||||
if (pktApi.getSignupAB() == 'storyboard')
|
||||
{
|
||||
startheight = 460;
|
||||
if (fxasignedin == '1')
|
||||
{
|
||||
startheight = 406;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fxasignedin == '1')
|
||||
{
|
||||
startheight = 436;
|
||||
}
|
||||
}
|
||||
showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + pktApi.getSignupAB(), {
|
||||
onShow: function() {
|
||||
resizePanel({
|
||||
width: 300,
|
||||
height: 550
|
||||
});
|
||||
},
|
||||
onHide: panelDidHide,
|
||||
});
|
||||
height: startheight
|
||||
});
|
||||
},
|
||||
onHide: panelDidHide,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,22 +316,31 @@ var pktUI = (function() {
|
|||
*/
|
||||
function saveAndShowConfirmation(url, title) {
|
||||
|
||||
// Validate parameter
|
||||
// TODO: Show some kind of error
|
||||
if (typeof url === 'undefined') { return; }
|
||||
if (!url.startsWith("http") && !url.startsWith('https')) { return; };
|
||||
// Validate input parameter
|
||||
if (typeof url !== 'undefined' && url.startsWith("about:reader?url=")) {
|
||||
url = ReaderMode.getOriginalUrl(url);
|
||||
}
|
||||
|
||||
showPanel("chrome://browser/content/pocket/panels/saved.html?premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
|
||||
var isValidURL = (typeof url !== 'undefined' && (url.startsWith("http") || url.startsWith('https')));
|
||||
|
||||
showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
|
||||
onShow: function() {
|
||||
// Open and resize the panel
|
||||
resizePanel({
|
||||
width: 350,
|
||||
height: 266
|
||||
height: 263
|
||||
});
|
||||
|
||||
var options = {
|
||||
success: function(data, response) {
|
||||
// Send error message for invalid url
|
||||
if (!isValidURL) {
|
||||
var error = new Error('Only links can be saved');
|
||||
sendErrorMessage('saveLink', error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add url
|
||||
var options = {
|
||||
success: function(data, request) {
|
||||
var item = data.item;
|
||||
var successResponse = {
|
||||
status: "success",
|
||||
|
@ -317,7 +348,14 @@ var pktUI = (function() {
|
|||
};
|
||||
sendMessage('saveLink', successResponse);
|
||||
},
|
||||
error: function(error, response) {
|
||||
error: function(error, request) {
|
||||
// If user is not authorized show singup page
|
||||
if (request.status === 401) {
|
||||
showSignUp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send error message to panel
|
||||
sendErrorMessage('saveLink', error);
|
||||
}
|
||||
}
|
||||
|
@ -388,7 +426,6 @@ var pktUI = (function() {
|
|||
* Called when the signup and saved panel was hidden
|
||||
*/
|
||||
function panelDidHide() {
|
||||
console.log("Panel did hide");
|
||||
}
|
||||
|
||||
// -- Communication to Panels -- //
|
||||
|
@ -478,7 +515,7 @@ var pktUI = (function() {
|
|||
activate = payload.activate;
|
||||
}
|
||||
openTabWithUrl(payload.url, activate);
|
||||
sendMessage("openTabWithUrlResponse", url);
|
||||
sendMessage("openTabWithUrlResponse", payload.url);
|
||||
});
|
||||
|
||||
// Close the panel
|
||||
|
@ -493,7 +530,6 @@ var pktUI = (function() {
|
|||
|
||||
// Callback post initialization to tell background script that panel is "ready" for communication.
|
||||
addMessageListener("listenerReady", function(payload) {
|
||||
console.log('got a listener init');
|
||||
});
|
||||
|
||||
addMessageListener("resizePanel", function(payload) {
|
||||
|
@ -604,13 +640,12 @@ var pktUI = (function() {
|
|||
return _isHidden;
|
||||
}
|
||||
|
||||
function isUserLoggedIntoFxA() {
|
||||
// TODO : verify with Firefox this is the right way to do this
|
||||
var user = fxAccounts.getSignedInUser();
|
||||
if (user && user.email)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
function getFirefoxAccountSignedInUser(callback) {
|
||||
fxAccounts.getSignedInUser().then(userData => {
|
||||
callback(userData);
|
||||
}).then(null, error => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,7 +740,6 @@ var pktUI = (function() {
|
|||
*/
|
||||
return {
|
||||
onLoad: onLoad,
|
||||
onUnload: onUnload,
|
||||
|
||||
pocketButtonOnCommand: pocketButtonOnCommand,
|
||||
pocketPanelDidShow: pocketPanelDidShow,
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,7 +289,6 @@ button,
|
|||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
|
@ -337,17 +334,6 @@ input[type="radio"] {
|
|||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
||||
* `font-size` values of the `input`, it causes the cursor style of the
|
||||
* decrement button to change from `default` to `text`.
|
||||
*/
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
||||
|
@ -355,23 +341,9 @@ input[type="number"]::-webkit-outer-spin-button {
|
|||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
||||
* Safari (but not Chrome) clips the cancel button when the search input has
|
||||
* padding (and `textfield` appearance).
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
@ -424,4 +396,4 @@ table {
|
|||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
text-shadow: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.pkt_ext_cf:after {
|
||||
|
@ -42,17 +41,8 @@
|
|||
|
||||
/*=Loading spinner
|
||||
--------------------------------------------------------------------------------------- */
|
||||
@-moz-keyframes pkt_ext_spin {
|
||||
@keyframes pkt_ext_spin {
|
||||
to {
|
||||
-moz-transform: rotate(1turn);
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes pkt_ext_spin {
|
||||
to {
|
||||
-moz-transform: rotate(1turn);
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +58,6 @@
|
|||
font-size: 10px;
|
||||
text-indent: 999em;
|
||||
overflow: hidden;
|
||||
-moz-animation: pkt_ext_spin 0.7s infinite steps(8);
|
||||
-webkit-animation: pkt_ext_spin 0.7s infinite steps(8);
|
||||
animation: pkt_ext_spin 0.7s infinite steps(8);
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_loadingspinner:before,
|
||||
|
@ -85,28 +73,20 @@
|
|||
border-radius: .2em;
|
||||
background: #eee;
|
||||
box-shadow: 0 1.75em #eee;
|
||||
-webkit-transform-origin: 50% 1.25em;
|
||||
-moz-transform-origin: 50% 1.25em;
|
||||
transform-origin: 50% 1.25em;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_loadingspinner:before {
|
||||
background: #555;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_loadingspinner:after {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
background: #777;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:before {
|
||||
-webkit-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
background: #999;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:after {
|
||||
-webkit-transform: rotate(-135deg);
|
||||
-moz-transform: rotate(-135deg);
|
||||
transform: rotate(-135deg);
|
||||
background: #bbb;
|
||||
}
|
||||
|
@ -125,42 +105,32 @@
|
|||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
.pkt_ext_container_detailactive .pkt_ext_initload,
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_initload {
|
||||
-webkit-transition: opacity 0.2s ease-out;
|
||||
-o-transition: opacity 0.2s ease-out;
|
||||
.pkt_ext_container_detailactive .pkt_ext_initload {
|
||||
transition: opacity 0.2s ease-out;
|
||||
opacity: 0;
|
||||
}
|
||||
.pkt_ext_container_detailactive .pkt_ext_initload .pkt_ext_loadingspinner,
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_initload .pkt_ext_loadingspinner {
|
||||
-moz-animation: none;
|
||||
-webkit-animation: none;
|
||||
.pkt_ext_container_finalstate .pkt_ext_initload .pkt_ext_loadingspinner {
|
||||
animation: none;
|
||||
}
|
||||
.pkt_ext_container_detailactive .pkt_ext_detail {
|
||||
-webkit-transition: opacity 0.2s ease-out 0.4s;
|
||||
-moz-transition: opacity 0.2s ease-out 0.4s;
|
||||
-ms-transition: opacity 0.2s ease-out 0.4s;
|
||||
-o-transition: opacity 0.2s ease-out 0.4s;
|
||||
max-height: 20em;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease-out 0.4s;
|
||||
transition: opacity 0.2s ease-out;
|
||||
}
|
||||
.pkt_ext_container_finalstate .pkt_ext_edit_msg,
|
||||
.pkt_ext_container_finalstate .pkt_ext_tag_detail,
|
||||
.pkt_ext_container_finalstate .pkt_ext_suggestedtag_detail,
|
||||
.pkt_ext_container_finalstate .pkt_ext_item_actions {
|
||||
-webkit-transition: opacity 0.2s ease-out;
|
||||
-moz-transition: opacity 0.2s ease-out;
|
||||
-ms-transition: opacity 0.2s ease-out;
|
||||
-o-transition: opacity 0.2s ease-out;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-out;
|
||||
}
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_edit_msg,
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_tag_detail {
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_tag_detail,
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_suggestedtag_detail,
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_item_actions {
|
||||
display: none;
|
||||
transition: none;
|
||||
}
|
||||
.pkt_ext_containersaved h2 {
|
||||
background: transparent;
|
||||
|
@ -168,17 +138,73 @@
|
|||
color: #333;
|
||||
display: block;
|
||||
float: none;
|
||||
font-size: 1.125em;
|
||||
font-size: 22px;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
line-height: 1;
|
||||
margin: 18px 0 4px;
|
||||
margin: 19px 0 4px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
text-transform: none;
|
||||
}
|
||||
@keyframes fade_in_out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
}
|
||||
51% {
|
||||
opacity: 0;
|
||||
top: 10px;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
@keyframes fade_in_outalt {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
51% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.pkt_ext_container_finalstate h2 {
|
||||
animation: fade_in_out 0.4s ease-out;
|
||||
top: 10px;
|
||||
}
|
||||
.pkt_ext_container_finalerrorstate h2 {
|
||||
animation: fade_int_outalt 0.4s ease-out;
|
||||
color: #d74345;
|
||||
top: 0;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_errordetail {
|
||||
display: none;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
left: 6.4em;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 2.7em;
|
||||
text-align: left;
|
||||
visibility: hidden;
|
||||
}
|
||||
.pkt_ext_container_finalerrorstate .pkt_ext_errordetail {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_logo {
|
||||
background: url(../img/pocketlogosolo@1x.png) center center no-repeat;
|
||||
display: block;
|
||||
|
@ -188,7 +214,7 @@
|
|||
position: relative;
|
||||
width: 44px;
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersaved .pkt_ext_logo {
|
||||
background-image: url(../img/pocketlogosolo@2x.png);
|
||||
background-size: 44px 40px;
|
||||
|
@ -197,10 +223,6 @@
|
|||
.pkt_ext_containersaved .pkt_ext_topdetail {
|
||||
float: left;
|
||||
}
|
||||
.pkt_ext_container_finalerrorstate h2 {
|
||||
line-height: 1.2em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_edit_msg {
|
||||
display: none;
|
||||
font-size: 0.875em;
|
||||
|
@ -241,8 +263,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions li {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: none;
|
||||
border: 0;
|
||||
|
@ -250,7 +270,7 @@
|
|||
list-style: none;
|
||||
line-height: 0.8;
|
||||
height: auto;
|
||||
padding-right: 0.5em;
|
||||
padding-right: 0.4em;
|
||||
width: auto;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions li:before {
|
||||
|
@ -283,7 +303,7 @@
|
|||
}
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions a:hover {
|
||||
color: #008acb;
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions a:before,
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions a:after {
|
||||
|
@ -299,7 +319,7 @@
|
|||
text-align: right;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_removeitem {
|
||||
padding-left: 0.2em;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_close {
|
||||
background: url(../img/tag_close@1x.png) center center no-repeat;
|
||||
|
@ -315,7 +335,7 @@
|
|||
top: -1em;
|
||||
width: 10px;
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersaved .pkt_ext_close {
|
||||
background-image: url(../img/tag_close@2x.png);
|
||||
background-size: 8px 8px;
|
||||
|
@ -335,20 +355,10 @@
|
|||
clear: both;
|
||||
margin: 1.25em 1em;
|
||||
padding: 0;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-moz-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #c3c3c3;
|
||||
|
@ -384,7 +394,6 @@
|
|||
content: none;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input {
|
||||
-webkit-appearance: caret;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
|
@ -398,7 +407,7 @@
|
|||
padding: 3px 2px 1px;
|
||||
text-transform: none;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::-webkit-input-placeholder {
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::-moz-placeholder {
|
||||
color: #a9a9a9;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
|
@ -408,8 +417,6 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_btn {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
float: none;
|
||||
font-size: 0.875em;
|
||||
|
@ -442,8 +449,6 @@
|
|||
}
|
||||
.pkt_ext_container_detailactive .pkt_ext_suggestedtag_detail {
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
|
||||
-o-transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
|
||||
transition: opacity 0.2s ease-out, visibility 0.2s ease-out;
|
||||
visibility: visible;
|
||||
}
|
||||
|
@ -523,6 +528,7 @@
|
|||
}
|
||||
.pkt_ext_containersaved .token_tag:hover {
|
||||
background-color: #008acb;
|
||||
border-color: #008acb;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -530,6 +536,15 @@
|
|||
.pkt_ext_containersaved .token_tag:after {
|
||||
content: none;
|
||||
}
|
||||
.pkt_ext_containersaved .token_tag:hover span {
|
||||
background-image: url(../img/tag_closeactive@1x.png);
|
||||
}
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersaved .token_tag:hover span {
|
||||
background-image: url(../img/tag_closeactive@2x.png);
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag,
|
||||
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag:hover,
|
||||
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail_disabled .token_tag,
|
||||
|
@ -567,10 +582,6 @@
|
|||
/*=Token input/autocomplete
|
||||
--------------------------------------------------------------------------------------- */
|
||||
.token-input-dropdown-tag {
|
||||
-moz-border-radius: 4px;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-border-radius: 4px;
|
||||
-webkit-box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
|
@ -621,11 +632,8 @@
|
|||
.token-input-list li input {
|
||||
border: 0;
|
||||
background-color: white;
|
||||
-webkit-appearance: caret;
|
||||
}
|
||||
.pkt_ext_containersaved .token-input-token {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
background: none;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #c3c3c3;
|
||||
|
@ -676,19 +684,38 @@
|
|||
width: 0;
|
||||
}
|
||||
.pkt_ext_containersaved .token-input-token span {
|
||||
color: #777;
|
||||
background: url(../img/tag_close@1x.png) center center no-repeat;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 0 0 0 10px;
|
||||
vertical-align: top;
|
||||
height: 8px;
|
||||
margin: 0 2px 0 8px;
|
||||
overflow: hidden;
|
||||
width: 8px;
|
||||
text-indent: -99px;
|
||||
}
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersaved .token-input-token span {
|
||||
background-image: url(../img/tag_close@2x.png);
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_containersaved .token-input-selected-token {
|
||||
background-color: #008acb;
|
||||
border-color: #008acb;
|
||||
color: #fff;
|
||||
}
|
||||
.pkt_ext_containersaved .token-input-selected-token span {
|
||||
background-image: url(../img/tag_closeactive@1x.png);
|
||||
}
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersaved .token-input-selected-token span {
|
||||
background-image: url(../img/tag_closeactive@2x.png);
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token span {
|
||||
color: #bbb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Contents:
|
||||
* Global
|
||||
* Core detail
|
||||
* Core detail - storyboard
|
||||
* Buttons
|
||||
* Responsive
|
||||
*/
|
||||
|
@ -24,8 +25,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
.pkt_ext_containersignup_inactive {
|
||||
-moz-animation: pkt_ext_hide 0.3s ease-out;
|
||||
-webkit-animation: pkt_ext_hide 0.3s ease-out;
|
||||
animation: pkt_ext_hide 0.3s ease-out;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
@ -35,20 +34,6 @@
|
|||
display:table;
|
||||
clear:both;
|
||||
}
|
||||
@-webkit-keyframes pkt_ext_hide {
|
||||
0% {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
visibility: visible;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
@keyframes pkt_ext_hide {
|
||||
0% {
|
||||
opacity: 1;
|
||||
|
@ -72,57 +57,48 @@
|
|||
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
line-height: 1.3;
|
||||
margin: 0 auto 1.5em;
|
||||
max-width: 250px;
|
||||
max-width: 260px;
|
||||
}
|
||||
.pkt_ext_containersignup a {
|
||||
color: #4c8fd0;
|
||||
text-decoration: none;
|
||||
}
|
||||
.pkt_ext_containersignup a:hover {
|
||||
color: #3076b9;
|
||||
}
|
||||
.pkt_ext_containersignup .pkt_ext_introrecommend {
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 1em 0.5em;
|
||||
}
|
||||
.pkt_ext_containersignup .pkt_ext_introdetail {
|
||||
background-color: #fbfbfb;
|
||||
border: 1px solid #c1c1c1;
|
||||
border-width: 1px 0;
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
.pkt_ext_containersignup .pkt_ext_logo {
|
||||
background: url(../img/pocketlogo@1x.png) center bottom no-repeat;
|
||||
display: block;
|
||||
height: 38px;
|
||||
height: 32px;
|
||||
margin: 0 auto 15px;
|
||||
padding-top: 25px;
|
||||
position: relative;
|
||||
text-indent: -9999px;
|
||||
width: 147px;
|
||||
width: 123px;
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersignup .pkt_ext_logo {
|
||||
background-image: url(../img/pocketlogo@2x.png);
|
||||
background-size: 147px 38px;
|
||||
background-size: 123px 32px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_containersignup .pkt_ext_introimg {
|
||||
background: url(../img/pocketmultidevices@1x.png) center center no-repeat;
|
||||
background: url(../img/pocketsignup_hero@1x.png) center center no-repeat;
|
||||
display: block;
|
||||
height: 122px;
|
||||
margin: 10px auto 20px;
|
||||
height: 125px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
text-indent: -9999px;
|
||||
width: 171px;
|
||||
width: 255px;
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersignup .pkt_ext_introimg {
|
||||
background-image: url(../img/pocketmultidevices@2x.png);
|
||||
background-size: 171px 122px;
|
||||
background-image: url(../img/pocketsignup_hero@2x.png);
|
||||
background-size: 255px 125px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_containersignup .pkt_ext_tagline {
|
||||
|
@ -162,6 +138,72 @@
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
/*=Core detail - storyboard
|
||||
--------------------------------------------------------------------------------------- */
|
||||
.pkt_ext_introstory {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
}
|
||||
.pkt_ext_introstory:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
.pkt_ext_introstory p {
|
||||
margin-bottom: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.pkt_ext_introstoryone {
|
||||
padding: 20px 18px 15px 20px;
|
||||
}
|
||||
.pkt_ext_introstorytwo {
|
||||
padding: 3px 0 0 20px;
|
||||
}
|
||||
.pkt_ext_introstorytwo .pkt_ext_tagline {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
.pkt_ext_introstory_text {
|
||||
flex: 1;
|
||||
}
|
||||
.pkt_ext_introstoryone_img,
|
||||
.pkt_ext_introstorytwo_img {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-indent: -999px;
|
||||
}
|
||||
.pkt_ext_introstoryone_img {
|
||||
background: url(../img/pocketsignup_button@1x.png) center right no-repeat;
|
||||
height: 82px;
|
||||
padding: 0 0 0 0.7em;
|
||||
width: 82px;
|
||||
}
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_introstoryone_img {
|
||||
background-image: url(../img/pocketsignup_button@2x.png);
|
||||
background-size: 82px 82px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_introstorytwo_img {
|
||||
background: url(../img/pocketsignup_devices@1x.png) bottom right no-repeat;
|
||||
height: 110px;
|
||||
padding: 1em 0 0 0.7em;
|
||||
width: 124px;
|
||||
}
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_introstorytwo_img {
|
||||
background-image: url(../img/pocketsignup_devices@2x.png);
|
||||
background-size: 124px 110px;
|
||||
}
|
||||
}
|
||||
.pkt_ext_introstorydivider {
|
||||
border-top: 1px solid #c1c1c1;
|
||||
height: 1px;
|
||||
margin: 0 auto;
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
/*=Buttons
|
||||
--------------------------------------------------------------------------------------- */
|
||||
.pkt_ext_containersignup .btn {
|
||||
|
@ -218,8 +260,6 @@
|
|||
.pkt_ext_containersignup .signupinterim-btn-signup,
|
||||
.pkt_ext_containersignup .forgot-btn-submit,
|
||||
.pkt_ext_containersignup .forgotreset-btn-change {
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
min-width: 12.125em;
|
||||
padding: 0.8em 1.1875em;
|
||||
box-sizing: content-box;
|
||||
|
@ -242,7 +282,7 @@
|
|||
width: 22px;
|
||||
position: absolute;
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.pkt_ext_containersignup .signup-btn-firefox .logo {
|
||||
background-image: url(../img/signup_firefoxlogo@2x.png);
|
||||
background-size: 22px 22px;
|
||||
|
@ -264,4 +304,4 @@
|
|||
.pkt_ext_containersignup .btn-disabled .text {
|
||||
color: #ccc;
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
}
|
||||
|
|
Двоичные данные
browser/components/pocket/panels/img/pocketlogo@1x.png
До Ширина: | Высота: | Размер: 4.2 KiB После Ширина: | Высота: | Размер: 3.5 KiB |
Двоичные данные
browser/components/pocket/panels/img/pocketlogo@2x.png
До Ширина: | Высота: | Размер: 8.9 KiB После Ширина: | Высота: | Размер: 7.2 KiB |
Двоичные данные
browser/components/pocket/panels/img/pocketmultidevices@1x.png
До Ширина: | Высота: | Размер: 28 KiB |
Двоичные данные
browser/components/pocket/panels/img/pocketmultidevices@2x.png
До Ширина: | Высота: | Размер: 94 KiB |
После Ширина: | Высота: | Размер: 7.1 KiB |
После Ширина: | Высота: | Размер: 13 KiB |
После Ширина: | Высота: | Размер: 21 KiB |
После Ширина: | Высота: | Размер: 66 KiB |
После Ширина: | Высота: | Размер: 40 KiB |
После Ширина: | Высота: | Размер: 130 KiB |
Двоичные данные
browser/components/pocket/panels/img/tag_close@1x.png
До Ширина: | Высота: | Размер: 1.0 KiB После Ширина: | Высота: | Размер: 287 B |
Двоичные данные
browser/components/pocket/panels/img/tag_close@2x.png
До Ширина: | Высота: | Размер: 1.2 KiB После Ширина: | Высота: | Размер: 508 B |
После Ширина: | Высота: | Размер: 208 B |
После Ширина: | Высота: | Размер: 354 B |
|
@ -2,21 +2,26 @@ Translations = {};
|
|||
Translations.en =
|
||||
{
|
||||
addtags: "Add Tags",
|
||||
alreadyhaveacct: "Already have an account?",
|
||||
alreadyhaveacct: "Already a Pocket user?",
|
||||
learnmore: "Learn More",
|
||||
loginnow: "Log in",
|
||||
onlylinkssaved: "Only links can be saved",
|
||||
or: "or",
|
||||
pagenotsaved: "Page Not Saved",
|
||||
pageremoved: "Page Removed",
|
||||
pagesaved: "Saved to Pocket",
|
||||
processingremove: "Removing Page...",
|
||||
processingtags: "Adding tags...",
|
||||
removepage: "Remove Page",
|
||||
save: "Save",
|
||||
signupemail: "Sign Up with email",
|
||||
signuptosave: "Sign up to start saving, it’s totally free.",
|
||||
signupemail: "Sign up with email",
|
||||
signuptosave: "Sign up for Pocket. It’s free.",
|
||||
suggestedtags: "Suggested Tags",
|
||||
tagline: "Save links from Firefox to view in Pocket on any device, anytime.",
|
||||
tagline: "Save articles and videos from Firefox to view in Pocket on any device, any time.",
|
||||
taglinestory_one: "Click the Pocket Button to save any article, video or page from Firefox.",
|
||||
taglinestory_two: "View in Pocket on any device, any time.",
|
||||
tagssaved: "Tags Added",
|
||||
signupfirefox: "Sign Up with Firefox",
|
||||
signinfirefox: "Sign in with Firefox",
|
||||
signupfirefox: "Sign up with Firefox",
|
||||
viewlist: "View List"
|
||||
}
|
|
@ -18,7 +18,7 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
this.dictJSON = {};
|
||||
// TODO: allow the timer to be editable?
|
||||
this.autocloseTiming = 3500;
|
||||
this.autocloseTimingFinalState = 1500;
|
||||
this.autocloseTimingFinalState = 2000;
|
||||
this.mouseInside = false;
|
||||
this.userTags = [];
|
||||
this.cxt_suggested_available = 0;
|
||||
|
@ -44,10 +44,8 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
}
|
||||
};
|
||||
this.fillUserTags = function() {
|
||||
console.log('start of logic for fillUserTags');
|
||||
thePKT_SAVED.sendMessage("getTags",{},function(resp)
|
||||
{
|
||||
console.log('got a big tag response',resp);
|
||||
if (typeof resp == 'object' && typeof resp.tags == 'object')
|
||||
{
|
||||
myself.userTags = resp.tags;
|
||||
|
@ -62,14 +60,12 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
myself.startCloseTimer();
|
||||
return;
|
||||
}
|
||||
console.log('calling suggested tags',myself.savedUrl);
|
||||
thePKT_SAVED.sendMessage("getSuggestedTags",
|
||||
{
|
||||
url: myself.savedUrl || window.location.toString()
|
||||
}, function(resp)
|
||||
{
|
||||
$('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
|
||||
console.log('got suggested tags response',resp);
|
||||
if (resp.status == 'success')
|
||||
{
|
||||
var newtags = [];
|
||||
|
@ -220,7 +216,6 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
}
|
||||
},
|
||||
onReady: function() {
|
||||
console.log('got to autoinput ready');
|
||||
$('.token-input-dropdown').addClass('token-input-dropdown-tag');
|
||||
inputwrapper.find('.token-input-input-token input').attr('placeholder',$('.tag-input').attr('placeholder')).css('width','200px');
|
||||
if ($('.pkt_ext_suggestedtag_detail').length) {
|
||||
|
@ -242,7 +237,7 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
}
|
||||
}).on('keypress','input',function(e) {
|
||||
if (e.which == 13) {
|
||||
if (Date.now() - changestamp > 250) {
|
||||
if (typeof changestamp == 'undefined' || (Date.now() - changestamp > 250)) {
|
||||
e.preventDefault();
|
||||
myself.wrapper.find('.pkt_ext_btn').trigger('click');
|
||||
}
|
||||
|
@ -316,14 +311,12 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
originaltags.push(text);
|
||||
}
|
||||
});
|
||||
console.log('submitting addtags message');
|
||||
thePKT_SAVED.sendMessage("addTags",
|
||||
{
|
||||
url: myself.savedUrl || window.location.toString(),
|
||||
tags: originaltags
|
||||
}, function(resp)
|
||||
{
|
||||
console.log('got a response',resp);
|
||||
if (resp.status == 'success')
|
||||
{
|
||||
myself.showStateFinalMsg(myself.dictJSON.tagssaved);
|
||||
|
@ -345,12 +338,10 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
e.preventDefault();
|
||||
myself.disableInput();
|
||||
$('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingremove);
|
||||
console.log('processing page removal',myself.savedItemId);
|
||||
thePKT_SAVED.sendMessage("deleteItem",
|
||||
{
|
||||
itemId: myself.savedItemId
|
||||
},function(resp) {
|
||||
console.log('got a removal message',resp);
|
||||
if (resp.status == 'success') {
|
||||
myself.showStateFinalMsg(myself.dictJSON.pageremoved);
|
||||
}
|
||||
|
@ -365,7 +356,6 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
$('.pkt_ext_openpocket').click(function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
console.log('sending new tab messsage',$(this).attr('href'));
|
||||
thePKT_SAVED.sendMessage("openTabWithUrl",
|
||||
{
|
||||
url: $(this).attr('href'),
|
||||
|
@ -406,7 +396,6 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
});
|
||||
};
|
||||
this.showStateSaved = function(initobj) {
|
||||
console.log('start of saved state',initobj);
|
||||
this.wrapper.find('.pkt_ext_detail h2').text(this.dictJSON.pagesaved);
|
||||
this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
|
||||
if (typeof initobj.item == 'object')
|
||||
|
@ -449,10 +438,17 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
$(this).off('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd');
|
||||
myself.preventCloseTimerCancel = true;
|
||||
myself.startCloseTimer(myself.autocloseTimingFinalState);
|
||||
myself.wrapper.find('.pkt_ext_detail h2').text(msg);
|
||||
});
|
||||
this.wrapper.addClass('pkt_ext_container_finalstate');
|
||||
this.wrapper.find('.pkt_ext_detail h2').text(msg);
|
||||
};
|
||||
this.showStateError = function(headline,detail) {
|
||||
this.wrapper.find('.pkt_ext_detail h2').text(headline);
|
||||
this.wrapper.find('.pkt_ext_detail h3').text(detail);
|
||||
this.wrapper.addClass('pkt_ext_container_detailactive pkt_ext_container_finalstate pkt_ext_container_finalerrorstate');
|
||||
this.preventCloseTimerCancel = true;
|
||||
this.startCloseTimer(myself.autocloseTimingFinalState);
|
||||
}
|
||||
this.getTranslations = function()
|
||||
{
|
||||
var language = window.navigator.language.toLowerCase();
|
||||
|
@ -540,7 +536,6 @@ var PKT_SAVED_OVERLAY = function (options)
|
|||
PKT_SAVED_OVERLAY.prototype = {
|
||||
create : function()
|
||||
{
|
||||
console.log('creating overlay',this.active);
|
||||
if (this.active)
|
||||
{
|
||||
return;
|
||||
|
@ -550,6 +545,9 @@ PKT_SAVED_OVERLAY.prototype = {
|
|||
// set translations
|
||||
this.getTranslations();
|
||||
|
||||
// set host
|
||||
this.dictJSON.pockethost = this.pockethost;
|
||||
|
||||
// Create actual content
|
||||
$('body').append(Handlebars.templates.saved_shell(this.dictJSON));
|
||||
|
||||
|
@ -568,7 +566,6 @@ PKT_SAVED_OVERLAY.prototype = {
|
|||
{
|
||||
if (this.premiumStatus && !$('.pkt_ext_suggestedtag_detail').length)
|
||||
{
|
||||
console.log('make premium');
|
||||
$('body').append(Handlebars.templates.saved_premiumshell(this.dictJSON));
|
||||
}
|
||||
}
|
||||
|
@ -598,11 +595,16 @@ PKT_SAVED.prototype = {
|
|||
|
||||
create: function() {
|
||||
var myself = this;
|
||||
var url = window.location.href.split('premiumStatus=');
|
||||
if (url.length > 1)
|
||||
var url = window.location.href.match(/premiumStatus=([\w|\d|\.]*)&?/);
|
||||
if (url && url.length > 1)
|
||||
{
|
||||
myself.overlay.premiumStatus = (url[1] == '1');
|
||||
}
|
||||
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
|
||||
if (host && host.length > 1)
|
||||
{
|
||||
myself.overlay.pockethost = host[1];
|
||||
}
|
||||
myself.overlay.create();
|
||||
|
||||
// tell back end we're ready
|
||||
|
@ -611,10 +613,14 @@ PKT_SAVED.prototype = {
|
|||
// wait confirmation of save before flipping to final saved state
|
||||
thePKT_SAVED.addMessageListener("saveLink",function(resp)
|
||||
{
|
||||
console.log('sweet, switch to full mode because of registered hit',resp);
|
||||
if (resp.status == 'error') {
|
||||
myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,myself.overlay.dictJSON.onlylinkssaved);
|
||||
return;
|
||||
}
|
||||
|
||||
myself.overlay.showStateSaved(resp);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@ var PKT_SIGNUP_OVERLAY = function (options)
|
|||
this.closeValid = true;
|
||||
this.mouseInside = false;
|
||||
this.autocloseTimer = null;
|
||||
this.variant = "";
|
||||
this.pockethost = "getpocket.com";
|
||||
this.fxasignedin = false;
|
||||
this.dictJSON = {};
|
||||
this.initCloseTabEvents = function() {
|
||||
$('.btn,.alreadyhave > a').click(function(e)
|
||||
$('.btn,.pkt_ext_learnmore,.alreadyhave > a').click(function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
console.log('sending new tab messsage',$(this).attr('href'));
|
||||
thePKT_SIGNUP.sendMessage("openTabWithUrl",
|
||||
{
|
||||
url: $(this).attr('href'),
|
||||
|
@ -144,6 +146,22 @@ PKT_SIGNUP_OVERLAY.prototype = {
|
|||
{
|
||||
var myself = this;
|
||||
|
||||
var variant = window.location.href.match(/variant=([\w|\.]*)&?/);
|
||||
if (variant && variant.length > 1)
|
||||
{
|
||||
this.variant = variant[1];
|
||||
}
|
||||
var fxasignedin = window.location.href.match(/fxasignedin=([\w|\d|\.]*)&?/);
|
||||
if (fxasignedin && fxasignedin.length > 1)
|
||||
{
|
||||
this.fxasignedin = (fxasignedin[1] == '1');
|
||||
}
|
||||
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
|
||||
if (host && host.length > 1)
|
||||
{
|
||||
this.pockethost = host[1];
|
||||
}
|
||||
|
||||
if (this.active)
|
||||
{
|
||||
return;
|
||||
|
@ -151,10 +169,20 @@ PKT_SIGNUP_OVERLAY.prototype = {
|
|||
this.active = true;
|
||||
|
||||
// set translations
|
||||
myself.getTranslations();
|
||||
this.getTranslations();
|
||||
this.dictJSON.fxasignedin = this.fxasignedin ? 1 : 0;
|
||||
this.dictJSON.variant = (this.variant ? this.variant : 'undefined');
|
||||
this.dictJSON.pockethost = this.pockethost;
|
||||
|
||||
// Create actual content
|
||||
$('body').append(Handlebars.templates.signup_shell(this.dictJSON));
|
||||
if (this.variant == 'storyboard')
|
||||
{
|
||||
$('body').append(Handlebars.templates.signupstoryboard_shell(this.dictJSON));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('body').append(Handlebars.templates.signup_shell(this.dictJSON));
|
||||
}
|
||||
|
||||
// tell background we're ready
|
||||
thePKT_SIGNUP.sendMessage("show");
|
||||
|
|
|
@ -10,9 +10,11 @@ templates['saved_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":fun
|
|||
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
|
||||
return "<div class=\"pkt_ext_initload\"> \n <div class=\"pkt_ext_loadingspinner\"><div></div></div>\n</div> \n<div class=\"pkt_ext_detail\"> \n <div class=\"pkt_ext_logo\"></div>\n <div class=\"pkt_ext_topdetail\">\n <h2>"
|
||||
+ escapeExpression(((helper = (helper = helpers.pagesaved || (depth0 != null ? depth0.pagesaved : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pagesaved","hash":{},"data":data}) : helper)))
|
||||
+ "</h2>\n <nav class=\"pkt_ext_item_actions pkt_ext_cf\">\n <ul>\n <li><a class=\"pkt_ext_removeitem\" href=\"#\">"
|
||||
+ "</h2>\n <h3 class=\"pkt_ext_errordetail\"></h3>\n <nav class=\"pkt_ext_item_actions pkt_ext_cf\">\n <ul>\n <li><a class=\"pkt_ext_removeitem\" href=\"#\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.removepage || (depth0 != null ? depth0.removepage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"removepage","hash":{},"data":data}) : helper)))
|
||||
+ "</a></li>\n <li class=\"pkt_ext_actions_separator\"></li> \n <li><a class=\"pkt_ext_openpocket\" href=\"http://firefox.dev.readitlater.com/a\" target=\"_blank\">"
|
||||
+ "</a></li>\n <li class=\"pkt_ext_actions_separator\"></li> \n <li><a class=\"pkt_ext_openpocket\" href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/a?src=ff_ext_saved\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.viewlist || (depth0 != null ? depth0.viewlist : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"viewlist","hash":{},"data":data}) : helper)))
|
||||
+ "</a></li>\n </ul>\n </nav> \n </div>\n <p class=\"pkt_ext_edit_msg\"></p> \n <div class=\"pkt_ext_tag_detail pkt_ext_cf\">\n <div class=\"pkt_ext_tag_input_wrapper\">\n <div class=\"pkt_ext_tag_input_blocker\"></div>\n <input class=\"pkt_ext_tag_input\" type=\"text\" placeholder=\""
|
||||
+ escapeExpression(((helper = (helper = helpers.addtags || (depth0 != null ? depth0.addtags : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"addtags","hash":{},"data":data}) : helper)))
|
||||
|
@ -20,23 +22,101 @@ templates['saved_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":fun
|
|||
+ escapeExpression(((helper = (helper = helpers.save || (depth0 != null ? depth0.save : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"save","hash":{},"data":data}) : helper)))
|
||||
+ "</a>\n </div>\n</div>";
|
||||
},"useData":true});
|
||||
templates['signup_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
templates['signup_shell'] = template({"1":function(depth0,helpers,partials,data) {
|
||||
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
|
||||
return "<h4 class=\"pkt_ext_introrecommend\">Recommended by Firefox</h4>\n<div class=\"pkt_ext_introdetail\">\n <h2 class=\"pkt_ext_logo\">Pocket</h2>\n <div class=\"pkt_ext_introimg\"></div>\n <p class=\"pkt_ext_tagline\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
|
||||
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://getpocket.com\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
|
||||
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
|
||||
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://firefox.dev.readitlater.com/ff_signup\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
|
||||
return " <p class=\"btn-container\"><a href=\"https://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/ff_signup?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
|
||||
+ "</span></a></p>\n";
|
||||
},"3":function(depth0,helpers,partials,data) {
|
||||
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
|
||||
return " <p class=\"btn-container\"><a href=\"https://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/ff_signup?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
|
||||
+ "</span></a> <a class=\"ff_signuphelp\" href=\"https://www.mozilla.org\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.help || (depth0 != null ? depth0.help : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"help","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n <p class=\"btn-container\"><a href=\"http://firefox.dev.readitlater.com/signup?force=email&src=extension\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
|
||||
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/signup?force=email&src=extension&s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n <p class=\"alreadyhave\">"
|
||||
+ "</a></p>\n";
|
||||
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail\">\n <h2 class=\"pkt_ext_logo\">Pocket</h2>\n <p class=\"pkt_ext_tagline\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
|
||||
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm&src=ff_learn_more\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n <div class=\"pkt_ext_introimg\"></div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
|
||||
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
|
||||
+ "</h4>\n";
|
||||
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
|
||||
if (stack1 != null) { buffer += stack1; }
|
||||
return buffer + " <p class=\"alreadyhave\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
|
||||
+ " <a href=\"http://firefox.dev.readitlater.com/login?ep=3&src=extension\" target=\"_blank\">"
|
||||
+ " <a href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/login?ep=3&src=extension&s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
|
||||
+ "</a>.</p>\n</div>";
|
||||
},"useData":true});
|
||||
templates['signupstoryboard_shell'] = template({"1":function(depth0,helpers,partials,data) {
|
||||
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
|
||||
return " <p class=\"btn-container\"><a href=\"https://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/ff_signup?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
|
||||
+ "</span></a></p>\n";
|
||||
},"3":function(depth0,helpers,partials,data) {
|
||||
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
|
||||
return " <p class=\"btn-container\"><a href=\"https://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/ff_signup?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
|
||||
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/signup?force=email&src=extension&s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n";
|
||||
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail pkt_ext_introdetailstoryboard\">\n <div class=\"pkt_ext_introstory pkt_ext_introstoryone\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.taglinestory_one || (depth0 != null ? depth0.taglinestory_one : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_one","hash":{},"data":data}) : helper)))
|
||||
+ "</p>\n </div>\n <div class=\"pkt_ext_introstoryone_img\"></div>\n </div>\n <div class=\"pkt_ext_introstorydivider\"></div>\n <div class=\"pkt_ext_introstory pkt_ext_introstorytwo\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.taglinestory_two || (depth0 != null ? depth0.taglinestory_two : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_two","hash":{},"data":data}) : helper)))
|
||||
+ "</p>\n <p><a class=\"pkt_ext_learnmore\" href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "?s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm&src=ff_learn_more\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
|
||||
+ "</a></p>\n </div>\n <div class=\"pkt_ext_introstorytwo_img\"></div>\n </div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n <h4>"
|
||||
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
|
||||
+ "</h4>\n";
|
||||
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
|
||||
if (stack1 != null) { buffer += stack1; }
|
||||
return buffer + " <p class=\"alreadyhave\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
|
||||
+ " <a href=\"http://"
|
||||
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
|
||||
+ "/login?ep=3&src=extension&s="
|
||||
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
|
||||
+ "&t=wlm\" target=\"_blank\">"
|
||||
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
|
||||
+ "</a>.</p>\n</div>";
|
||||
},"useData":true});
|
||||
|
|
|
@ -9,7 +9,7 @@ to the license set forth below, "Pocket," "Read It Later" and the Pocket icon
|
|||
and logos (collectively, the “Pocket Marks”) are registered and common law
|
||||
trademarks of Read It Later, Inc. This means that, while you have considerable
|
||||
freedom to redistribute and modify the Software, there are tight restrictions
|
||||
on your ability to use the Pocket Marks. This license does not grant you any
|
||||
on your ability to use the Pocket Marks. This license does not grant you any
|
||||
rights to use the Pocket Marks except as they are embodied in the Software.
|
||||
|
||||
---
|
|
@ -5,11 +5,12 @@
|
|||
<div class="pkt_ext_logo"></div>
|
||||
<div class="pkt_ext_topdetail">
|
||||
<h2>{{pagesaved}}</h2>
|
||||
<h3 class="pkt_ext_errordetail"></h3>
|
||||
<nav class="pkt_ext_item_actions pkt_ext_cf">
|
||||
<ul>
|
||||
<li><a class="pkt_ext_removeitem" href="#">{{removepage}}</a></li>
|
||||
<li class="pkt_ext_actions_separator"></li>
|
||||
<li><a class="pkt_ext_openpocket" href="http://firefox.dev.readitlater.com/a" target="_blank">{{viewlist}}</a></li>
|
||||
<li><a class="pkt_ext_openpocket" href="http://{{pockethost}}/a?src=ff_ext_saved" target="_blank">{{viewlist}}</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<h4 class="pkt_ext_introrecommend">Recommended by Firefox</h4>
|
||||
<div class="pkt_ext_introdetail">
|
||||
<h2 class="pkt_ext_logo">Pocket</h2>
|
||||
<div class="pkt_ext_introimg"></div>
|
||||
<p class="pkt_ext_tagline">{{tagline}}</p>
|
||||
<p><a class="pkt_ext_learnmore" href="http://getpocket.com" target="_blank">{{learnmore}}</a></p>
|
||||
<p><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
|
||||
<div class="pkt_ext_introimg"></div>
|
||||
</div>
|
||||
<div class="pkt_ext_signupdetail">
|
||||
<h4>{{signuptosave}}</h4>
|
||||
<p class="btn-container"><a href="https://firefox.dev.readitlater.com/ff_signup" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a> <a class="ff_signuphelp" href="https://www.mozilla.org" target="_blank">{{help}}</a></p>
|
||||
<p class="btn-container"><a href="http://firefox.dev.readitlater.com/signup?force=email&src=extension" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
|
||||
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://firefox.dev.readitlater.com/login?ep=3&src=extension" target="_blank">{{loginnow}}</a>.</p>
|
||||
{{#if fxasignedin}}
|
||||
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
|
||||
{{else}}
|
||||
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
|
||||
<p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
|
||||
{{/if}}
|
||||
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
<div class="pkt_ext_introdetail pkt_ext_introdetailstoryboard">
|
||||
<div class="pkt_ext_introstory pkt_ext_introstoryone">
|
||||
<div class="pkt_ext_introstory_text">
|
||||
<p class="pkt_ext_tagline">{{taglinestory_one}}</p>
|
||||
</div>
|
||||
<div class="pkt_ext_introstoryone_img"></div>
|
||||
</div>
|
||||
<div class="pkt_ext_introstorydivider"></div>
|
||||
<div class="pkt_ext_introstory pkt_ext_introstorytwo">
|
||||
<div class="pkt_ext_introstory_text">
|
||||
<p class="pkt_ext_tagline">{{taglinestory_two}}</p>
|
||||
<p><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
|
||||
</div>
|
||||
<div class="pkt_ext_introstorytwo_img"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pkt_ext_signupdetail">
|
||||
<h4>{{signuptosave}}</h4>
|
||||
{{#if fxasignedin}}
|
||||
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
|
||||
{{else}}
|
||||
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
|
||||
<p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
|
||||
{{/if}}
|
||||
<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
|
||||
</div>
|
|
@ -50,7 +50,8 @@ var pktApi = (function() {
|
|||
|
||||
// Base url for all api calls
|
||||
// TODO: This is a dev server and will be changed before launch
|
||||
var pocketAPIhost = Services.prefs.getCharPref("browser.pocket.hostname");
|
||||
var pocketAPIhost = Services.prefs.getCharPref("browser.pocket.api");
|
||||
var pocketSiteHost = Services.prefs.getCharPref("browser.pocket.site");
|
||||
|
||||
// Base url for all api calls
|
||||
var baseAPIUrl = "https://" + pocketAPIhost + "/v3";
|
||||
|
@ -139,7 +140,7 @@ var pktApi = (function() {
|
|||
function getCookiesFromPocket() {
|
||||
|
||||
var cookieManager = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
|
||||
var pocketCookies = cookieManager.getCookiesFromHost(pocketAPIhost);
|
||||
var pocketCookies = cookieManager.getCookiesFromHost(pocketSiteHost);
|
||||
var cookies = {};
|
||||
while (pocketCookies.hasMoreElements()) {
|
||||
var cookie = pocketCookies.getNext().QueryInterface(Ci.nsICookie2);
|
||||
|
@ -225,6 +226,12 @@ var pktApi = (function() {
|
|||
|
||||
// TODO: Better error handling
|
||||
if (options.error) {
|
||||
// In case the user did revoke the access token or it's not
|
||||
// valid anymore clear the user data
|
||||
if (request.status === 401) {
|
||||
clearUserData();
|
||||
}
|
||||
|
||||
// Check to handle Pocket error
|
||||
var errorMessage = request.getResponseHeader("X-Error");
|
||||
if (typeof errorMessage !== "undefined") {
|
||||
|
@ -238,9 +245,6 @@ var pktApi = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
// TODO - do we want to pass a special user agent?
|
||||
//request.setRequestHeader("User-Agent" , 'Pocket Firefox ' + this.APP.v);
|
||||
|
||||
// Set headers
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
request.setRequestHeader('X-Accept',' application/json');
|
||||
|
@ -268,6 +272,7 @@ var pktApi = (function() {
|
|||
setSetting("tags", undefined);
|
||||
setSetting("usedTags", undefined);
|
||||
|
||||
setSetting("fsv1", undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -321,8 +326,6 @@ var pktApi = (function() {
|
|||
},
|
||||
error: options.error
|
||||
});
|
||||
|
||||
return sendAction(action, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -444,7 +447,7 @@ var pktApi = (function() {
|
|||
var tagToSave = tags[i].trim();
|
||||
var newUsedTagObject = {
|
||||
"tag": tagToSave,
|
||||
"timestamp": new Date()
|
||||
"timestamp": new Date().getTime()
|
||||
};
|
||||
usedTags[tagToSave] = newUsedTagObject;
|
||||
}
|
||||
|
@ -488,9 +491,9 @@ var pktApi = (function() {
|
|||
}
|
||||
|
||||
// Sort usedTagsObjectArray based on timestamp
|
||||
usedTagsObjectArray.sort(function(a, b) {
|
||||
a = new Date(a.timestamp);
|
||||
b = new Date(b.timestamp);
|
||||
usedTagsObjectArray.sort(function(usedTagA, usedTagB) {
|
||||
var a = usedTagA.timestamp;
|
||||
var b = usedTagB.timestamp;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
});
|
||||
|
||||
|
@ -563,6 +566,26 @@ var pktApi = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get current signup AB group the user is in
|
||||
*/
|
||||
function getSignupAB() {
|
||||
if (!getSetting('signupAB'))
|
||||
{
|
||||
var rand = (Math.floor(Math.random()*2+1));
|
||||
if (rand == 2)
|
||||
{
|
||||
setSetting('signupAB','storyboard');
|
||||
}
|
||||
else
|
||||
{
|
||||
setSetting('signupAB','hero');
|
||||
}
|
||||
|
||||
}
|
||||
return getSetting('signupAB');
|
||||
}
|
||||
|
||||
/**
|
||||
* Public functions
|
||||
*/
|
||||
|
@ -576,6 +599,7 @@ var pktApi = (function() {
|
|||
getTags: getTags,
|
||||
isPremiumUser: isPremiumUser,
|
||||
getSuggestedTagsForItem: getSuggestedTagsForItem,
|
||||
getSuggestedTagsForURL: getSuggestedTagsForURL
|
||||
getSuggestedTagsForURL: getSuggestedTagsForURL,
|
||||
getSignupAB: getSignupAB
|
||||
};
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -71,22 +71,6 @@ this.SessionFile = {
|
|||
write: function (aData) {
|
||||
return SessionFileInternal.write(aData);
|
||||
},
|
||||
/**
|
||||
* Gather telemetry statistics.
|
||||
*
|
||||
*
|
||||
* Most of the work is done off the main thread but there is a main
|
||||
* thread cost involved to send data to the worker thread. This method
|
||||
* should therefore be called only when we know that it will not disrupt
|
||||
* the user's experience, e.g. on idle-daily.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @promise {object} An object holding all the information to be submitted
|
||||
* to Telemetry.
|
||||
*/
|
||||
gatherTelemetry: function(aData) {
|
||||
return SessionFileInternal.gatherTelemetry(aData);
|
||||
},
|
||||
/**
|
||||
* Wipe the contents of the session file, asynchronously.
|
||||
*/
|
||||
|
@ -268,14 +252,6 @@ let SessionFileInternal = {
|
|||
return result;
|
||||
}),
|
||||
|
||||
gatherTelemetry: function(aStateString) {
|
||||
return Task.spawn(function() {
|
||||
let msg = yield SessionWorker.post("gatherTelemetry", [aStateString]);
|
||||
this._recordTelemetry(msg.telemetry);
|
||||
throw new Task.Result(msg.telemetry);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
write: function (aData) {
|
||||
if (RunState.isClosed) {
|
||||
return Promise.reject(new Error("SessionFile is closed"));
|
||||
|
@ -289,25 +265,11 @@ let SessionFileInternal = {
|
|||
RunState.setClosed();
|
||||
}
|
||||
|
||||
let refObj = {};
|
||||
let name = "FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS";
|
||||
let performShutdownCleanup = isFinalWrite &&
|
||||
!sessionStartup.isAutomaticRestoreEnabled();
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
// Start measuring main thread impact.
|
||||
TelemetryStopwatch.start(name, refObj);
|
||||
|
||||
let performShutdownCleanup = isFinalWrite &&
|
||||
!sessionStartup.isAutomaticRestoreEnabled();
|
||||
|
||||
let options = {isFinalWrite, performShutdownCleanup};
|
||||
|
||||
try {
|
||||
resolve(SessionWorker.post("write", [aData, options]));
|
||||
} finally {
|
||||
// Record how long we stopped the main thread.
|
||||
TelemetryStopwatch.finish(name, refObj);
|
||||
}
|
||||
});
|
||||
let options = {isFinalWrite, performShutdownCleanup};
|
||||
let promise = SessionWorker.post("write", [aData, options]);
|
||||
|
||||
// Wait until the write is done.
|
||||
promise = promise.then(msg => {
|
||||
|
@ -322,7 +284,6 @@ let SessionFileInternal = {
|
|||
}
|
||||
}, err => {
|
||||
// Catch and report any errors.
|
||||
TelemetryStopwatch.cancel(name, refObj);
|
||||
console.error("Could not write session state file ", err, err.stack);
|
||||
// By not doing anything special here we ensure that |promise| cannot
|
||||
// be rejected anymore. The shutdown/cleanup code at the end of the
|
||||
|
|
|
@ -246,8 +246,6 @@ let SessionSaverInternal = {
|
|||
* Write the given state object to disk.
|
||||
*/
|
||||
_writeState: function (state) {
|
||||
stopWatchStart("WRITE_STATE_LONGEST_OP_MS");
|
||||
|
||||
// We update the time stamp before writing so that we don't write again
|
||||
// too soon, if saving is requested before the write completes. Without
|
||||
// this update we may save repeatedly if actions cause a runDelayed
|
||||
|
@ -257,14 +255,9 @@ let SessionSaverInternal = {
|
|||
// Write (atomically) to a session file, using a tmp file. Once the session
|
||||
// file is successfully updated, save the time stamp of the last save and
|
||||
// notify the observers.
|
||||
let promise = SessionFile.write(state);
|
||||
stopWatchFinish("WRITE_STATE_LONGEST_OP_MS");
|
||||
|
||||
promise = promise.then(() => {
|
||||
return SessionFile.write(state).then(() => {
|
||||
this.updateLastSaveTime();
|
||||
notify(null, "sessionstore-state-write-complete");
|
||||
}, console.error);
|
||||
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,7 +30,6 @@ const OBSERVING = [
|
|||
"quit-application-requested", "browser-lastwindow-close-granted",
|
||||
"quit-application", "browser:purge-session-history",
|
||||
"browser:purge-domain-data",
|
||||
"gather-telemetry",
|
||||
"idle-daily",
|
||||
];
|
||||
|
||||
|
@ -103,9 +102,6 @@ const TAB_EVENTS = [
|
|||
"TabUnpinned"
|
||||
];
|
||||
|
||||
// The number of milliseconds in a day
|
||||
const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
|
||||
|
@ -591,9 +587,6 @@ let SessionStoreInternal = {
|
|||
case "nsPref:changed": // catch pref changes
|
||||
this.onPrefChange(aData);
|
||||
break;
|
||||
case "gather-telemetry":
|
||||
this.onGatherTelemetry();
|
||||
break;
|
||||
case "idle-daily":
|
||||
this.onIdleDaily();
|
||||
break;
|
||||
|
@ -1616,14 +1609,6 @@ let SessionStoreInternal = {
|
|||
this._resetLocalTabRestoringState(tab);
|
||||
},
|
||||
|
||||
onGatherTelemetry: function() {
|
||||
// On the first gather-telemetry notification of the session,
|
||||
// gather telemetry data.
|
||||
Services.obs.removeObserver(this, "gather-telemetry");
|
||||
let stateString = SessionStore.getBrowserState();
|
||||
return SessionFile.gatherTelemetry(stateString);
|
||||
},
|
||||
|
||||
// Clean up data that has been closed a long time ago.
|
||||
// Do not reschedule a save. This will wait for the next regular
|
||||
// save.
|
||||
|
@ -2405,7 +2390,6 @@ let SessionStoreInternal = {
|
|||
_collectWindowData: function ssi_collectWindowData(aWindow) {
|
||||
if (!this._isWindowLoaded(aWindow))
|
||||
return;
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_SINGLE_WINDOW_DATA_MS");
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
let tabs = tabbrowser.tabs;
|
||||
|
@ -2427,7 +2411,6 @@ let SessionStoreInternal = {
|
|||
aWindow.__SS_lastSessionWindowID;
|
||||
|
||||
DirtyWindows.remove(aWindow);
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_SINGLE_WINDOW_DATA_MS");
|
||||
},
|
||||
|
||||
/* ........ Restoring Functionality .............. */
|
||||
|
@ -2754,7 +2737,8 @@ let SessionStoreInternal = {
|
|||
// fill the array with at least empty tabData objects until |_tPos| or
|
||||
// we'll end up with |null| entries.
|
||||
for (let tab of Array.slice(tabbrowser.tabs, 0, tab._tPos)) {
|
||||
this._windows[window.__SSi].tabs.push(TabState.collect(tab));
|
||||
let emptyState = {entries: [], lastAccessed: tab.lastAccessed};
|
||||
this._windows[window.__SSi].tabs.push(emptyState);
|
||||
}
|
||||
|
||||
// Update the tab state in case we shut down without being notified.
|
||||
|
@ -3076,14 +3060,6 @@ let SessionStoreInternal = {
|
|||
// Attempt to load the session start time from the session state
|
||||
if (state.session && state.session.startTime) {
|
||||
this._sessionStartTime = state.session.startTime;
|
||||
|
||||
// ms to days
|
||||
let sessionLength = (Date.now() - this._sessionStartTime) / MS_PER_DAY;
|
||||
|
||||
if (sessionLength > 0) {
|
||||
// Submit the session length telemetry measurement
|
||||
Services.telemetry.getHistogramById("FX_SESSION_RESTORE_SESSION_LENGTH").add(sessionLength);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -283,16 +283,6 @@ let Agent = {
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract all sorts of useful statistics from a state string,
|
||||
* for use with Telemetry.
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
gatherTelemetry: function (stateString) {
|
||||
return Statistics.collect(stateString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wipes all files holding session data from disk.
|
||||
*/
|
||||
|
@ -388,131 +378,3 @@ function isNoSuchFileEx(aReason) {
|
|||
function getByteLength(str) {
|
||||
return Encoder.encode(JSON.stringify(str)).byteLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools for gathering statistics on a state string.
|
||||
*/
|
||||
let Statistics = {
|
||||
collect: function(stateString) {
|
||||
let start = Date.now();
|
||||
let TOTAL_PREFIX = "FX_SESSION_RESTORE_TOTAL_";
|
||||
let INDIVIDUAL_PREFIX = "FX_SESSION_RESTORE_INDIVIDUAL_";
|
||||
let SIZE_SUFFIX = "_SIZE_BYTES";
|
||||
|
||||
let state = JSON.parse(stateString);
|
||||
|
||||
// Gather all data
|
||||
let subsets = {};
|
||||
this.gatherSimpleData(state, subsets);
|
||||
this.gatherComplexData(state, subsets);
|
||||
|
||||
// Extract telemetry
|
||||
let telemetry = {};
|
||||
for (let k of Object.keys(subsets)) {
|
||||
let obj = subsets[k];
|
||||
telemetry[TOTAL_PREFIX + k + SIZE_SUFFIX] = getByteLength(obj);
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
let size = obj.map(getByteLength);
|
||||
telemetry[INDIVIDUAL_PREFIX + k + SIZE_SUFFIX] = size;
|
||||
}
|
||||
}
|
||||
|
||||
let stop = Date.now();
|
||||
telemetry["FX_SESSION_RESTORE_EXTRACTING_STATISTICS_DURATION_MS"] = stop - start;
|
||||
return {
|
||||
telemetry: telemetry
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Collect data that doesn't require a recursive walk through the
|
||||
* data structure.
|
||||
*/
|
||||
gatherSimpleData: function(state, subsets) {
|
||||
// The subset of sessionstore.js dealing with open windows
|
||||
subsets.OPEN_WINDOWS = state.windows;
|
||||
|
||||
// The subset of sessionstore.js dealing with closed windows
|
||||
subsets.CLOSED_WINDOWS = state._closedWindows;
|
||||
|
||||
// The subset of sessionstore.js dealing with closed tabs
|
||||
// in open windows
|
||||
subsets.CLOSED_TABS_IN_OPEN_WINDOWS = [];
|
||||
|
||||
// The subset of sessionstore.js dealing with cookies
|
||||
// in both open and closed windows
|
||||
subsets.COOKIES = [];
|
||||
|
||||
for (let winData of state.windows) {
|
||||
let closedTabs = winData._closedTabs || [];
|
||||
subsets.CLOSED_TABS_IN_OPEN_WINDOWS.push(...closedTabs);
|
||||
|
||||
let cookies = winData.cookies || [];
|
||||
subsets.COOKIES.push(...cookies);
|
||||
}
|
||||
|
||||
for (let winData of state._closedWindows) {
|
||||
let cookies = winData.cookies || [];
|
||||
subsets.COOKIES.push(...cookies);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Walk through a data structure, recursively.
|
||||
*
|
||||
* @param {object} root The object from which to start walking.
|
||||
* @param {function(key, value)} cb Callback, called for each
|
||||
* item except the root. Returns |true| to walk the subtree rooted
|
||||
* at |value|, |false| otherwise */
|
||||
walk: function(root, cb) {
|
||||
if (!root || typeof root !== "object") {
|
||||
return;
|
||||
}
|
||||
for (let k of Object.keys(root)) {
|
||||
let obj = root[k];
|
||||
let stepIn = cb(k, obj);
|
||||
if (stepIn) {
|
||||
this.walk(obj, cb);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Collect data that requires walking through the data structure
|
||||
*/
|
||||
gatherComplexData: function(state, subsets) {
|
||||
// The subset of sessionstore.js dealing with DOM storage
|
||||
subsets.DOM_STORAGE = [];
|
||||
// The subset of sessionstore.js storing form data
|
||||
subsets.FORMDATA = [];
|
||||
// The subset of sessionstore.js storing history
|
||||
subsets.HISTORY = [];
|
||||
|
||||
|
||||
this.walk(state, function(k, value) {
|
||||
let dest;
|
||||
switch (k) {
|
||||
case "entries":
|
||||
subsets.HISTORY.push(value);
|
||||
return true;
|
||||
case "storage":
|
||||
subsets.DOM_STORAGE.push(value);
|
||||
// Never visit storage, it's full of weird stuff
|
||||
return false;
|
||||
case "formdata":
|
||||
subsets.FORMDATA.push(value);
|
||||
// Never visit formdata, it's full of weird stuff
|
||||
return false;
|
||||
case "cookies": // Don't visit these places, they are full of weird stuff
|
||||
case "extData":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return subsets;
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -265,6 +265,9 @@ let SessionHistoryListener = {
|
|||
if (!SessionHistory.isEmpty(docShell)) {
|
||||
this.collect();
|
||||
}
|
||||
|
||||
// Listen for page title changes.
|
||||
addEventListener("DOMTitleChanged", this);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
|
@ -280,6 +283,10 @@ let SessionHistoryListener = {
|
|||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
this.collect();
|
||||
},
|
||||
|
||||
onFrameTreeCollected: function () {
|
||||
this.collect();
|
||||
},
|
||||
|
|
|
@ -88,6 +88,7 @@ skip-if = buildapp == 'mulet'
|
|||
[browser_history_persist.js]
|
||||
[browser_label_and_icon.js]
|
||||
[browser_merge_closed_tabs.js]
|
||||
[browser_page_title.js]
|
||||
[browser_pageStyle.js]
|
||||
[browser_privatetabs.js]
|
||||
[browser_replace_load.js]
|
||||
|
@ -101,7 +102,6 @@ skip-if = e10s
|
|||
skip-if = e10s # See bug 918634
|
||||
[browser_switch_remoteness.js]
|
||||
run-if = e10s
|
||||
[browser_telemetry.js]
|
||||
[browser_upgrade_backup.js]
|
||||
[browser_windowRestore_perwindowpb.js]
|
||||
[browser_248970_b_perwindowpb.js]
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
"use strict";
|
||||
|
||||
const URL = "data:text/html,<title>initial title</title>";
|
||||
|
||||
add_task(function* () {
|
||||
// Create a new tab.
|
||||
let tab = gBrowser.addTab(URL);
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
// Remove the tab.
|
||||
yield promiseRemoveTab(tab);
|
||||
|
||||
// Check the title.
|
||||
let [{state: {entries}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(entries[0].title, "initial title", "correct title");
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
// Create a new tab.
|
||||
let tab = gBrowser.addTab(URL);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Flush to ensure we collected the initial title.
|
||||
TabState.flush(browser);
|
||||
|
||||
// Set a new title.
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
content.document.title = "new title";
|
||||
});
|
||||
|
||||
// Remove the tab.
|
||||
yield promiseRemoveTab(tab);
|
||||
|
||||
// Check the title.
|
||||
let [{state: {entries}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(entries[0].title, "new title", "correct title");
|
||||
});
|
|
@ -1,270 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
|
||||
let {SessionFile} = tmp;
|
||||
|
||||
// Shortcuts for histogram names
|
||||
let Keys = {};
|
||||
for (let k of ["HISTORY", "FORMDATA", "OPEN_WINDOWS", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS", "DOM_STORAGE"]) {
|
||||
Keys[k] = "FX_SESSION_RESTORE_TOTAL_" + k + "_SIZE_BYTES";
|
||||
}
|
||||
|
||||
function lt(a, b, message) {
|
||||
isnot(a, undefined, message + " (sanity check)");
|
||||
isnot(b, undefined, message + " (sanity check)");
|
||||
ok(a < b, message + " ( " + a + " < " + b + ")");
|
||||
}
|
||||
function gt(a, b, message) {
|
||||
isnot(a, undefined, message + " (sanity check)");
|
||||
isnot(b, undefined, message + " (sanity check)");
|
||||
ok(a > b, message + " ( " + a + " > " + b + ")");
|
||||
}
|
||||
|
||||
add_task(function init() {
|
||||
forgetClosedWindows();
|
||||
for (let i = ss.getClosedTabCount(window) - 1; i >= 0; --i) {
|
||||
ss.forgetClosedTab(window, i);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that Telemetry collection doesn't cause any error.
|
||||
*/
|
||||
add_task(function() {
|
||||
info("Checking a little bit of consistency");
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
for (let k of Object.keys(statistics)) {
|
||||
let data = statistics[k];
|
||||
info("Data for " + k + ": " + data);
|
||||
if (Array.isArray(data)) {
|
||||
ok(data.every(x => x >= 0), "Data for " + k + " is >= 0");
|
||||
} else {
|
||||
ok(data >= 0, "Data for " + k + " is >= 0");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test HISTORY key.
|
||||
*/
|
||||
add_task(function history() {
|
||||
let KEY = Keys.HISTORY;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now changing history");
|
||||
tab.linkedBrowser.loadURI("http://example.org:80/1");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
// We have changed history, so it must have increased
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of HISTORY has increased");
|
||||
|
||||
// Almost nothing else should
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
} finally {
|
||||
if (tab) {
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test CLOSED_TABS_IN_OPEN_WINDOWS key.
|
||||
*/
|
||||
add_task(function close_tab() {
|
||||
let KEY = Keys.CLOSED_TABS_IN_OPEN_WINDOWS;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?close_tab");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now closing a tab");
|
||||
yield promiseRemoveTab(tab);
|
||||
tab = null;
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_TABS_IN_OPEN_WINDOWS has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (tab) {
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test OPEN_WINDOWS key.
|
||||
*/
|
||||
add_task(function open_window() {
|
||||
let KEY = Keys.OPEN_WINDOWS;
|
||||
let win;
|
||||
try {
|
||||
let statistics = yield promiseStats();
|
||||
win = yield promiseNewWindowLoaded("http://example.org:80/?open_window");
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of OPEN_WINDOWS has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (win) {
|
||||
yield promiseWindowClosed(win);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test CLOSED_WINDOWS key.
|
||||
*/
|
||||
add_task(function close_window() {
|
||||
let KEY = Keys.CLOSED_WINDOWS;
|
||||
let win = yield promiseNewWindowLoaded("http://example.org:80/?close_window");
|
||||
|
||||
// We need to add something to the window, otherwise it won't be saved
|
||||
let tab = win.gBrowser.addTab("http://example.org:80/?close_tab");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
let statistics = yield promiseStats();
|
||||
yield promiseWindowClosed(win);
|
||||
win = null;
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_WINDOWS has increased");
|
||||
lt(statistics2[Keys.OPEN_WINDOWS], statistics[Keys.OPEN_WINDOWS], "The total size of OPEN_WINDOWS has decreased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (win) {
|
||||
yield promiseWindowClosed(win);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Test DOM_STORAGE key.
|
||||
*/
|
||||
add_task(function dom_storage() {
|
||||
let KEY = Keys.DOM_STORAGE;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?dom_storage");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now adding some storage");
|
||||
yield modifySessionStorage(tab.linkedBrowser, {foo: "bar"});
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of DOM_STORAGE has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["CLOSED_TABS_IN_OPEN_WINDOWS", "FORMDATA", "CLOSED_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (tab) {
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test FORMDATA key.
|
||||
*/
|
||||
add_task(function formdata() {
|
||||
let KEY = Keys.FORMDATA;
|
||||
let tab = gBrowser.addTab("data:text/html;charset=utf-8,<input%20id='input'>");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now changing form data");
|
||||
|
||||
yield setInputValue(tab.linkedBrowser, {id: "input", value: "This is some form data"});
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of FORMDATA has increased");
|
||||
|
||||
// Almost nothing else should
|
||||
for (let k of ["DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
} finally {
|
||||
if (tab) {
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_sessionRestoreInit() {
|
||||
let info = Cc["@mozilla.org/toolkit/app-startup;1"].
|
||||
getService(Ci.nsIAppStartup).
|
||||
getStartupInfo();
|
||||
ok(info.sessionRestoreInit > info.process, "sessionRestoreInit is after process creation");
|
||||
ok(info.sessionRestoreInit <
|
||||
Date.now() + 10000 /* Date.now() is non-monotonic, let's play it paranoid*/,
|
||||
"sessionRestoreInit is before now");
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the latest statistics.
|
||||
*/
|
||||
function promiseStats() {
|
||||
let state = ss.getBrowserState();
|
||||
info("Stats: " + state);
|
||||
return SessionFile.gatherTelemetry(state);
|
||||
}
|
||||
|
||||
|
||||
function modifySessionStorage(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
|
||||
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
|
||||
}
|
||||
|
||||
function setInputValue(browser, data) {
|
||||
return sendMessage(browser, "ss-test:setInputValue", data);
|
||||
}
|
|
@ -528,6 +528,8 @@ for (let name of FORM_HELPERS) {
|
|||
this[name] = (browser, data) => sendMessage(browser, msg, data);
|
||||
}
|
||||
|
||||
// Removes the given tab immediately and returns a promise that resolves when
|
||||
// all pending status updates (messages) of the closing tab have been received.
|
||||
function promiseRemoveTab(tab) {
|
||||
return BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
|
|
@ -465,10 +465,6 @@ let UI = {
|
|||
});
|
||||
this._reorderTabItemsOnShow = [];
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Restore the full height when showing TabView
|
||||
gTabViewFrame.style.marginTop = "";
|
||||
#endif
|
||||
gTabViewDeck.selectedPanel = gTabViewFrame;
|
||||
gWindow.TabsInTitlebar.allowedBy("tabview-open", false);
|
||||
gTabViewFrame.contentWindow.focus();
|
||||
|
@ -543,12 +539,6 @@ let UI = {
|
|||
});
|
||||
this._reorderTabsOnHide = [];
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Push the top of TabView frame to behind the tabbrowser, so glass can show
|
||||
// XXX bug 586679: avoid shrinking the iframe and squishing iframe contents
|
||||
// as well as avoiding the flash of black as we animate out
|
||||
gTabViewFrame.style.marginTop = gBrowser.boxObject.y + "px";
|
||||
#endif
|
||||
gTabViewDeck.selectedPanel = gBrowserPanel;
|
||||
gWindow.TabsInTitlebar.allowedBy("tabview-open", true);
|
||||
gBrowser.selectedBrowser.focus();
|
||||
|
|
|
@ -223,10 +223,17 @@ MemoryFrontFacade.prototype = {
|
|||
|
||||
yield this.attach();
|
||||
|
||||
let startTime = yield this.startRecordingAllocations({
|
||||
probability: options.allocationsSampleProbability,
|
||||
maxLogLength: options.allocationsMaxLogLength
|
||||
});
|
||||
// Reconstruct our options because the server actor fails when being passed
|
||||
// undefined values in objects.
|
||||
let allocationOptions = {};
|
||||
if (options.allocationsSampleProbability !== void 0) {
|
||||
allocationOptions.probability = options.allocationsSampleProbability;
|
||||
}
|
||||
if (options.allocationsMaxLogLength !== void 0) {
|
||||
allocationOptions.maxLogLength = options.allocationsMaxLogLength;
|
||||
}
|
||||
|
||||
let startTime = yield this.startRecordingAllocations(allocationOptions);
|
||||
|
||||
yield this._pullAllocationSites();
|
||||
|
||||
|
|
|
@ -27,9 +27,8 @@ const DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT = 200; // ms
|
|||
|
||||
// Events to pipe from PerformanceActorsConnection to the PerformanceFront
|
||||
const CONNECTION_PIPE_EVENTS = [
|
||||
"console-profile-start", "console-profile-ending", "console-profile-end",
|
||||
"timeline-data", "profiler-already-active", "profiler-activated",
|
||||
"recording-started", "recording-stopped"
|
||||
"recording-starting", "recording-started", "recording-stopping", "recording-stopped"
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -227,8 +226,6 @@ PerformanceActorsConnection.prototype = {
|
|||
console: true,
|
||||
label: profileLabel
|
||||
}));
|
||||
|
||||
this.emit("console-profile-start", model);
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -271,9 +268,7 @@ PerformanceActorsConnection.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.emit("console-profile-ending", model);
|
||||
yield this.stopRecording(model);
|
||||
this.emit("console-profile-end", model);
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -309,6 +304,8 @@ PerformanceActorsConnection.prototype = {
|
|||
*/
|
||||
startRecording: Task.async(function*(options = {}) {
|
||||
let model = new RecordingModel(options);
|
||||
this.emit("recording-starting", model);
|
||||
|
||||
// All actors are started asynchronously over the remote debugging protocol.
|
||||
// Get the corresponding start times from each one of them.
|
||||
// The timeline and memory actors are target-dependent, so start those as well,
|
||||
|
@ -343,6 +340,13 @@ PerformanceActorsConnection.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Flag the recording as no longer recording, so that `model.isRecording()`
|
||||
// is false. Do this before we fetch all the data, and then subsequently
|
||||
// the recording can be considered "completed".
|
||||
let endTime = Date.now();
|
||||
model._onStoppingRecording(endTime);
|
||||
this.emit("recording-stopping", model);
|
||||
|
||||
// Currently there are two ways profiles stop recording. Either manually in the
|
||||
// performance tool, or via console.profileEnd. Once a recording is done,
|
||||
// we want to deliver the model to the performance tool (either as a return
|
||||
|
@ -485,7 +489,9 @@ PerformanceFront.prototype = {
|
|||
}
|
||||
let actor = this._connection[`_${actorName}`];
|
||||
return actor[method].apply(actor, args);
|
||||
}
|
||||
},
|
||||
|
||||
toString: () => "[object PerformanceFront]"
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,6 +38,7 @@ RecordingModel.prototype = {
|
|||
_console: false,
|
||||
_imported: false,
|
||||
_recording: false,
|
||||
_completed: false,
|
||||
_profilerStartTime: 0,
|
||||
_timelineStartTime: 0,
|
||||
_memoryStartTime: 0,
|
||||
|
@ -94,7 +95,7 @@ RecordingModel.prototype = {
|
|||
// However, we also want to update the view with the elapsed time
|
||||
// even when the actor is not generating data. To do this we get
|
||||
// the local time and use it to compute a reasonable elapsed time.
|
||||
this._localStartTime = Date.now()
|
||||
this._localStartTime = Date.now();
|
||||
|
||||
this._profilerStartTime = info.profilerStartTime;
|
||||
this._timelineStartTime = info.timelineStartTime;
|
||||
|
@ -108,19 +109,30 @@ RecordingModel.prototype = {
|
|||
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the signal was sent to the front to no longer record more
|
||||
* data, and begin fetching the data. There's some delay during fetching,
|
||||
* even though the recording is stopped, the model is not yet completed until
|
||||
* all the data is fetched.
|
||||
*/
|
||||
_onStoppingRecording: function (endTime) {
|
||||
this._duration = endTime - this._localStartTime;
|
||||
this._recording = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets results available from stopping a recording from SharedPerformanceConnection.
|
||||
* Should only be called by SharedPerformanceConnection.
|
||||
*/
|
||||
_onStopRecording: Task.async(function *(info) {
|
||||
this._profile = info.profile;
|
||||
this._duration = info.profilerEndTime - this._profilerStartTime;
|
||||
this._recording = false;
|
||||
this._completed = true;
|
||||
|
||||
// We filter out all samples that fall out of current profile's range
|
||||
// since the profiler is continuously running. Because of this, sample
|
||||
// times are not guaranteed to have a zero epoch, so offset the
|
||||
// timestamps.
|
||||
// TODO move this into FakeProfilerFront in ./actors.js after bug 1154115
|
||||
RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime);
|
||||
|
||||
// Markers need to be sorted ascending by time, to be properly displayed
|
||||
|
@ -248,9 +260,21 @@ RecordingModel.prototype = {
|
|||
return this._console;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether or not this recording model
|
||||
* has finished recording.
|
||||
* There is some delay in fetching data between when the recording stops, and
|
||||
* when the recording is considered completed once it has all the profiler and timeline data.
|
||||
*/
|
||||
isCompleted: function () {
|
||||
return this._completed || this.isImported();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether or not this recording model
|
||||
* is recording.
|
||||
* A model may no longer be recording, yet still not have the profiler data. In that
|
||||
* case, use `isCompleted()`.
|
||||
*/
|
||||
isRecording: function () {
|
||||
return this._recording;
|
||||
|
@ -262,7 +286,7 @@ RecordingModel.prototype = {
|
|||
addTimelineData: function (eventName, ...data) {
|
||||
// If this model isn't currently recording,
|
||||
// ignore the timeline data.
|
||||
if (!this._recording) {
|
||||
if (!this.isRecording()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,13 +63,6 @@ const EVENTS = {
|
|||
// Fired by the PerformanceController when the devtools theme changes.
|
||||
THEME_CHANGED: "Performance:ThemeChanged",
|
||||
|
||||
// When the SharedPerformanceConnection handles profiles created via `console.profile()`,
|
||||
// the controller handles those events and emits the below events for consumption
|
||||
// by other views.
|
||||
CONSOLE_RECORDING_STARTED: "Performance:ConsoleRecordingStarted",
|
||||
CONSOLE_RECORDING_WILL_STOP: "Performance:ConsoleRecordingWillStop",
|
||||
CONSOLE_RECORDING_STOPPED: "Performance:ConsoleRecordingStopped",
|
||||
|
||||
// Emitted by the PerformanceView when the state (display mode) changes,
|
||||
// for example when switching between "empty", "recording" or "recorded".
|
||||
// This causes certain panels to be hidden or visible.
|
||||
|
@ -190,9 +183,7 @@ let PerformanceController = {
|
|||
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onThemeChanged = this._onThemeChanged.bind(this);
|
||||
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
|
||||
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
|
||||
this._onConsoleProfileEnding = this._onConsoleProfileEnding.bind(this);
|
||||
this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
|
||||
|
||||
// All boolean prefs should be handled via the OptionsView in the
|
||||
// ToolbarView, so that they may be accessible via the "gear" menu.
|
||||
|
@ -208,9 +199,10 @@ let PerformanceController = {
|
|||
this._nonBooleanPrefs.registerObserver();
|
||||
this._nonBooleanPrefs.on("pref-changed", this._onPrefChanged);
|
||||
|
||||
gFront.on("console-profile-start", this._onConsoleProfileStart);
|
||||
gFront.on("console-profile-ending", this._onConsoleProfileEnding);
|
||||
gFront.on("console-profile-end", this._onConsoleProfileEnd);
|
||||
gFront.on("recording-starting", this._onRecordingStateChange);
|
||||
gFront.on("recording-started", this._onRecordingStateChange);
|
||||
gFront.on("recording-stopping", this._onRecordingStateChange);
|
||||
gFront.on("recording-stopped", this._onRecordingStateChange);
|
||||
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
|
@ -229,9 +221,10 @@ let PerformanceController = {
|
|||
this._nonBooleanPrefs.unregisterObserver();
|
||||
this._nonBooleanPrefs.off("pref-changed", this._onPrefChanged);
|
||||
|
||||
gFront.off("console-profile-start", this._onConsoleProfileStart);
|
||||
gFront.off("console-profile-ending", this._onConsoleProfileEnding);
|
||||
gFront.off("console-profile-end", this._onConsoleProfileEnd);
|
||||
gFront.off("recording-starting", this._onRecordingStateChange);
|
||||
gFront.off("recording-started", this._onRecordingStateChange);
|
||||
gFront.off("recording-stopping", this._onRecordingStateChange);
|
||||
gFront.off("recording-stopped", this._onRecordingStateChange);
|
||||
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
|
@ -300,12 +293,7 @@ let PerformanceController = {
|
|||
sampleFrequency: this.getPref("profiler-sample-frequency")
|
||||
};
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_START);
|
||||
|
||||
let recording = yield gFront.startRecording(options);
|
||||
this._recordings.push(recording);
|
||||
|
||||
this.emit(EVENTS.RECORDING_STARTED, recording);
|
||||
yield gFront.startRecording(options);
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -315,9 +303,7 @@ let PerformanceController = {
|
|||
stopRecording: Task.async(function *() {
|
||||
let recording = this.getLatestManualRecording();
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, recording);
|
||||
yield gFront.stopRecording(recording);
|
||||
this.emit(EVENTS.RECORDING_STOPPED, recording);
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -344,6 +330,11 @@ let PerformanceController = {
|
|||
if (latest && latest.isRecording()) {
|
||||
yield this.stopRecording();
|
||||
}
|
||||
// If last recording is not recording, but finalizing itself,
|
||||
// wait for that to finish
|
||||
if (latest && !latest.isCompleted()) {
|
||||
yield this.once(EVENTS.RECORDING_STOPPED);
|
||||
}
|
||||
|
||||
this._recordings.length = 0;
|
||||
this.setCurrentRecording(null);
|
||||
|
@ -440,27 +431,33 @@ let PerformanceController = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profile()` is executed.
|
||||
* Fired when a recording model changes state.
|
||||
*
|
||||
* @param {string} state
|
||||
* @param {RecordingModel} model
|
||||
*/
|
||||
_onConsoleProfileStart: function (_, recording) {
|
||||
this._recordings.push(recording);
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_STARTED, recording);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profileEnd()` is executed, and the profile
|
||||
* is stopping soon, as it fetches profiler data.
|
||||
*/
|
||||
_onConsoleProfileEnding: function (_, recording) {
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_WILL_STOP, recording);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when `console.profileEnd()` is executed, and
|
||||
* has a corresponding `console.profile()` session.
|
||||
*/
|
||||
_onConsoleProfileEnd: function (_, recording) {
|
||||
this.emit(EVENTS.CONSOLE_RECORDING_STOPPED, recording);
|
||||
_onRecordingStateChange: function (state, model) {
|
||||
switch (state) {
|
||||
// Fired when a RecordingModel was just created from the front
|
||||
case "recording-starting":
|
||||
// When a recording is just starting, store it internally
|
||||
this._recordings.push(model);
|
||||
this.emit(EVENTS.RECORDING_WILL_START, model);
|
||||
break;
|
||||
// Fired when a RecordingModel has started recording
|
||||
case "recording-started":
|
||||
this.emit(EVENTS.RECORDING_STARTED, model);
|
||||
break;
|
||||
// Fired when a RecordingModel is no longer recording, and
|
||||
// starting to fetch all the profiler data
|
||||
case "recording-stopping":
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, model);
|
||||
break;
|
||||
// Fired when a RecordingModel is finished fetching all of its data
|
||||
case "recording-stopped":
|
||||
this.emit(EVENTS.RECORDING_STOPPED, model);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -480,21 +477,21 @@ let PerformanceController = {
|
|||
* model, like `withTicks`, or `withMemory`.
|
||||
* @option {Array<string>} actors
|
||||
* An array of strings indicating what actors must exist.
|
||||
* @option {boolean} isRecording
|
||||
* A boolean indicating whether the recording must be either recording or not
|
||||
* recording. Setting to undefined will allow either state.
|
||||
* @option {boolean} mustBeCompleted
|
||||
* A boolean indicating whether the recording must be either completed or not.
|
||||
* Setting to undefined will allow either state.
|
||||
* @param {RecordingModel} recording
|
||||
* An optional recording model to use instead of the currently selected.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
isFeatureSupported: function ({ features, actors, isRecording: shouldBeRecording }, recording) {
|
||||
isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
|
||||
recording = recording || this.getCurrentRecording();
|
||||
let recordingConfig = recording ? recording.getConfiguration() : {};
|
||||
let currentRecordingState = recording ? recording.isRecording() : void 0;
|
||||
let currentCompletedState = recording ? recording.isCompleted() : void 0;
|
||||
let actorsSupported = gFront.getActorSupport();
|
||||
|
||||
if (shouldBeRecording != null && shouldBeRecording !== currentRecordingState) {
|
||||
if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
|
||||
return false;
|
||||
}
|
||||
if (actors && !actors.every(a => actorsSupported[a])) {
|
||||
|
|
|
@ -41,12 +41,11 @@ let PerformanceView = {
|
|||
this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
|
||||
this._onImportButtonClick = this._onImportButtonClick.bind(this);
|
||||
this._onClearButtonClick = this._onClearButtonClick.bind(this);
|
||||
this._lockRecordButton = this._lockRecordButton.bind(this);
|
||||
this._unlockRecordButton = this._unlockRecordButton.bind(this);
|
||||
this._lockRecordButtons = this._lockRecordButtons.bind(this);
|
||||
this._unlockRecordButtons = this._unlockRecordButtons.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
|
||||
this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
|
||||
for (let button of $$(".record-button")) {
|
||||
button.addEventListener("click", this._onRecordButtonClick);
|
||||
|
@ -55,11 +54,8 @@ let PerformanceView = {
|
|||
this._clearButton.addEventListener("click", this._onClearButtonClick);
|
||||
|
||||
// Bind to controller events to unlock the record button
|
||||
PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
|
||||
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
|
||||
this.setState("empty");
|
||||
|
@ -82,11 +78,8 @@ let PerformanceView = {
|
|||
this._importButton.removeEventListener("click", this._onImportButtonClick);
|
||||
this._clearButton.removeEventListener("click", this._onClearButtonClick);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
|
||||
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
|
||||
yield ToolbarView.destroy();
|
||||
|
@ -130,31 +123,30 @@ let PerformanceView = {
|
|||
* Adds the `locked` attribute on the record button. This prevents it
|
||||
* from being clicked while recording is started or stopped.
|
||||
*/
|
||||
_lockRecordButton: function () {
|
||||
this._recordButton.setAttribute("locked", "true");
|
||||
_lockRecordButtons: function () {
|
||||
for (let button of $$(".record-button")) {
|
||||
button.setAttribute("locked", "true");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the `locked` attribute on the record button.
|
||||
*/
|
||||
_unlockRecordButton: function () {
|
||||
this._recordButton.removeAttribute("locked");
|
||||
_unlockRecordButtons: function () {
|
||||
for (let button of $$(".record-button")) {
|
||||
button.removeAttribute("locked");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a recording is starting, but not yet completed.
|
||||
* When a recording has started.
|
||||
*/
|
||||
_onRecordingWillStart: function () {
|
||||
this._lockRecordButton();
|
||||
this._recordButton.setAttribute("checked", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a recording is stopping, but not yet completed.
|
||||
*/
|
||||
_onRecordingWillStop: function () {
|
||||
this._lockRecordButton();
|
||||
this._recordButton.removeAttribute("checked");
|
||||
_onRecordingStarted: function (_, recording) {
|
||||
// A stopped recording can be from `console.profileEnd` -- only unlock
|
||||
// the button if it's the main recording that was started via UI.
|
||||
if (!recording.isConsole()) {
|
||||
this._unlockRecordButtons();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -164,7 +156,7 @@ let PerformanceView = {
|
|||
// A stopped recording can be from `console.profileEnd` -- only unlock
|
||||
// the button if it's the main recording that was started via UI.
|
||||
if (!recording.isConsole()) {
|
||||
this._unlockRecordButton();
|
||||
this._unlockRecordButtons();
|
||||
}
|
||||
|
||||
// If the currently selected recording is the one that just stopped,
|
||||
|
@ -187,7 +179,15 @@ let PerformanceView = {
|
|||
_onRecordButtonClick: function (e) {
|
||||
if (this._recordButton.hasAttribute("checked")) {
|
||||
this.emit(EVENTS.UI_STOP_RECORDING);
|
||||
this._lockRecordButtons();
|
||||
for (let button of $$(".record-button")) {
|
||||
button.removeAttribute("checked");
|
||||
}
|
||||
} else {
|
||||
this._lockRecordButtons();
|
||||
for (let button of $$(".record-button")) {
|
||||
button.setAttribute("checked", "true");
|
||||
}
|
||||
this.emit(EVENTS.UI_START_RECORDING);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<hbox id="recordings-controls"
|
||||
class="devtools-toolbarbutton-group">
|
||||
<toolbarbutton id="main-record-button"
|
||||
class="devtools-toolbarbutton record-button"
|
||||
class="devtools-toolbarbutton record-button devtools-thobber"
|
||||
tooltiptext="&profilerUI.recordButton.tooltip;"/>
|
||||
<toolbarbutton id="import-button"
|
||||
class="devtools-toolbarbutton"
|
||||
|
@ -159,8 +159,7 @@
|
|||
flex="1">
|
||||
<label value="&profilerUI.stopNotice1;"/>
|
||||
<button class="devtools-toolbarbutton record-button"
|
||||
standalone="true"
|
||||
checked="true" />
|
||||
standalone="true" />
|
||||
<label value="&profilerUI.stopNotice2;"/>
|
||||
</hbox>
|
||||
<hbox id="console-recording-notice"
|
||||
|
|
|
@ -89,6 +89,7 @@ support-files =
|
|||
[browser_perf-states.js]
|
||||
[browser_perf-refresh.js]
|
||||
[browser_perf-ui-recording.js]
|
||||
[browser_perf-recording-model-01.js]
|
||||
[browser_perf-recording-notices-01.js]
|
||||
[browser_perf-recording-notices-02.js]
|
||||
[browser_perf_recordings-io-01.js]
|
||||
|
|
|
@ -15,12 +15,12 @@ function spawnTest () {
|
|||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
busyWait(WAIT_TIME);
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ function spawnTest () {
|
|||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
profileStart = once(connection, "console-profile-start");
|
||||
profileStart = once(connection, "recording-started");
|
||||
console.profile("rust2");
|
||||
yield profileStart;
|
||||
|
||||
|
@ -38,10 +38,10 @@ function spawnTest () {
|
|||
is(RecordingsView.selectedItem.attachment, recordings[0],
|
||||
"The first console recording should be selected.");
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
profileEnd = once(connection, "console-profile-end");
|
||||
profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust2");
|
||||
yield profileEnd;
|
||||
|
||||
|
|
|
@ -15,15 +15,15 @@ function spawnTest () {
|
|||
yield profilerConnected;
|
||||
let connection = getPerformanceActorsConnection(target);
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
profileStart = once(connection, "console-profile-start");
|
||||
profileStart = once(connection, "recording-started");
|
||||
console.profile("rust2");
|
||||
yield profileStart;
|
||||
|
||||
|
@ -43,7 +43,7 @@ function spawnTest () {
|
|||
is(RecordingsView.selectedItem.attachment, recordings[0],
|
||||
"The first console recording should be selected.");
|
||||
|
||||
profileEnd = once(connection, "console-profile-end");
|
||||
profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust2");
|
||||
yield profileEnd;
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@ function spawnTest () {
|
|||
let connection = getPerformanceActorsConnection(target);
|
||||
let tab = toolbox.doc.getElementById("toolbox-tab-performance");
|
||||
|
||||
let profileStart = once(connection, "console-profile-start");
|
||||
let profileStart = once(connection, "recording-started");
|
||||
console.profile("rust");
|
||||
yield profileStart;
|
||||
|
||||
ok(tab.hasAttribute("highlighted"),
|
||||
"performance tab is highlighted during recording from console.profile when unloaded");
|
||||
|
||||
let profileEnd = once(connection, "console-profile-end");
|
||||
let profileEnd = once(connection, "recording-stopped");
|
||||
console.profileEnd("rust");
|
||||
yield profileEnd;
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the state of a recording rec from start to finish for recording,
|
||||
* completed, and rec data.
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, gFront: front, PerformanceController } = panel.panelWin;
|
||||
|
||||
let rec = yield front.startRecording({ withMarkers: true, withTicks: true, withMemory: true });
|
||||
ok(rec.isRecording(), "RecordingModel is recording when created");
|
||||
yield busyWait(100);
|
||||
yield waitUntil(() => rec.getMemory().length);
|
||||
ok(true, "RecordingModel populates memory while recording");
|
||||
yield waitUntil(() => rec.getTicks().length);
|
||||
ok(true, "RecordingModel populates ticks while recording");
|
||||
yield waitUntil(() => rec.getMarkers().length);
|
||||
ok(true, "RecordingModel populates markers while recording");
|
||||
|
||||
ok(!rec.isCompleted(), "RecordingModel is not completed when still recording");
|
||||
|
||||
let stopping = once(front, "recording-stopping");
|
||||
let stopped = once(front, "recording-stopped");
|
||||
front.stopRecording(rec);
|
||||
|
||||
yield stopping;
|
||||
ok(!rec.isRecording(), "on 'recording-stopping', model is no longer recording");
|
||||
// This handler should be called BEFORE "recording-stopped" is called, as
|
||||
// there is some delay, but in the event where "recording-stopped" finishes
|
||||
// before we check here, ensure that we're atleast in the right state
|
||||
if (rec.getProfile()) {
|
||||
ok(rec.isCompleted(), "recording is completed once it has profile data");
|
||||
} else {
|
||||
ok(!rec.isCompleted(), "recording is not yet completed on 'recording-stopping'");
|
||||
}
|
||||
|
||||
yield stopped;
|
||||
ok(!rec.isRecording(), "on 'recording-stopped', model is still no longer recording");
|
||||
ok(rec.isCompleted(), "on 'recording-stopped', model is considered 'complete'");
|
||||
|
||||
// Export and import a rec, and ensure it has the correct state.
|
||||
let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
||||
yield PerformanceController.exportRecording("", rec, file);
|
||||
yield exported;
|
||||
|
||||
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
|
||||
yield PerformanceController.importRecording("", file);
|
||||
|
||||
yield imported;
|
||||
let importedModel = PerformanceController.getCurrentRecording();
|
||||
|
||||
ok(importedModel.isCompleted(), "All imported recordings should be completed");
|
||||
ok(!importedModel.isRecording(), "All imported recordings should not be recording");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -142,7 +142,7 @@ function handleError(aError) {
|
|||
}
|
||||
|
||||
function once(aTarget, aEventName, aUseCapture = false, spread = false) {
|
||||
info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
|
||||
info(`Waiting for event: '${aEventName}' on ${aTarget}`);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
|
@ -153,6 +153,7 @@ function once(aTarget, aEventName, aUseCapture = false, spread = false) {
|
|||
]) {
|
||||
if ((add in aTarget) && (remove in aTarget)) {
|
||||
aTarget[add](aEventName, function onEvent(...aArgs) {
|
||||
info(`Received event: '${aEventName}' on ${aTarget}`);
|
||||
aTarget[remove](aEventName, onEvent, aUseCapture);
|
||||
deferred.resolve(spread ? aArgs : aArgs[0]);
|
||||
}, aUseCapture);
|
||||
|
@ -305,13 +306,13 @@ function consoleMethod (...args) {
|
|||
}
|
||||
|
||||
function* consoleProfile(win, label) {
|
||||
let profileStart = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STARTED);
|
||||
let profileStart = once(win.PerformanceController, win.EVENTS.RECORDING_STARTED);
|
||||
consoleMethod("profile", label);
|
||||
yield profileStart;
|
||||
}
|
||||
|
||||
function* consoleProfileEnd(win, label) {
|
||||
let ended = once(win.PerformanceController, win.EVENTS.CONSOLE_RECORDING_STOPPED);
|
||||
let ended = once(win.PerformanceController, win.EVENTS.RECORDING_STOPPED);
|
||||
consoleMethod("profileEnd", label);
|
||||
yield ended;
|
||||
}
|
||||
|
@ -475,15 +476,15 @@ function waitUntil(predicate, interval = 10) {
|
|||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dropSelection(graph) {
|
||||
|
|
|
@ -17,7 +17,6 @@ let DetailsSubview = {
|
|||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
|
@ -32,7 +31,6 @@ let DetailsSubview = {
|
|||
clearNamedTimeout("range-change-debounce");
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
|
@ -81,7 +79,7 @@ let DetailsSubview = {
|
|||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, recording) {
|
||||
if (!recording || recording.isRecording()) {
|
||||
if (!recording || !recording.isCompleted()) {
|
||||
return;
|
||||
}
|
||||
if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
|
||||
|
@ -131,7 +129,7 @@ let DetailsSubview = {
|
|||
// All detail views require a recording to be complete, so do not
|
||||
// attempt to render if recording is in progress or does not exist.
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (!recording || recording.isRecording()) {
|
||||
if (!recording || !recording.isCompleted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ let DetailsView = {
|
|||
|
||||
yield this.setAvailableViews();
|
||||
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
|
||||
|
@ -77,7 +76,6 @@ let DetailsView = {
|
|||
component.initialized && (yield component.view.destroy());
|
||||
}
|
||||
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
|
||||
|
@ -90,13 +88,13 @@ let DetailsView = {
|
|||
*/
|
||||
setAvailableViews: Task.async(function* () {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let isRecording = recording && recording.isRecording();
|
||||
let isCompleted = recording && recording.isCompleted();
|
||||
let invalidCurrentView = false;
|
||||
|
||||
for (let [name, { view }] of Iterator(this.components)) {
|
||||
// TODO bug 1160313 get rid of retro mode checks.
|
||||
let isRetro = PerformanceController.getOption("retro-mode");
|
||||
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, false);
|
||||
let isSupported = isRetro ? name === "js-calltree" : this._isViewSupported(name, true);
|
||||
|
||||
// TODO bug 1160313 hide all view buttons, but let js-calltree still be "supported"
|
||||
$(`toolbarbutton[data-view=${name}]`).hidden = isRetro ? true : !isSupported;
|
||||
|
@ -112,12 +110,12 @@ let DetailsView = {
|
|||
//
|
||||
// 1: If we currently have selected a view that is no longer valid due
|
||||
// to feature support, and this isn't the first view, and the current recording
|
||||
// is not recording.
|
||||
// is completed.
|
||||
//
|
||||
// 2. If we have a finished recording and no panel was selected yet,
|
||||
// use a default now that we have the recording configurations
|
||||
if ((this._initialized && !isRecording && invalidCurrentView) ||
|
||||
(!this._initialized && !isRecording && recording)) {
|
||||
if ((this._initialized && isCompleted && invalidCurrentView) ||
|
||||
(!this._initialized && isCompleted && recording)) {
|
||||
yield this.selectDefaultView();
|
||||
}
|
||||
}),
|
||||
|
@ -126,12 +124,12 @@ let DetailsView = {
|
|||
* Takes a view name and optionally if there must be a currently recording in progress.
|
||||
*
|
||||
* @param {string} viewName
|
||||
* @param {boolean?} isRecording
|
||||
* @param {boolean?} mustBeCompleted
|
||||
* @return {boolean}
|
||||
*/
|
||||
_isViewSupported: function (viewName, isRecording) {
|
||||
_isViewSupported: function (viewName, mustBeCompleted) {
|
||||
let { features, actors } = this.components[viewName];
|
||||
return PerformanceController.isFeatureSupported({ features, actors, isRecording });
|
||||
return PerformanceController.isFeatureSupported({ features, actors, mustBeCompleted });
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -238,7 +236,7 @@ let DetailsView = {
|
|||
// All detail views require a recording to be complete, so do not
|
||||
// attempt to render if recording is in progress or does not exist.
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (recording && !recording.isRecording()) {
|
||||
if (recording && recording.isCompleted()) {
|
||||
component.view.shouldUpdateWhenShown = true;
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -66,9 +66,6 @@ let OverviewView = {
|
|||
PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
this.graphs.on("selecting", this._onGraphSelecting);
|
||||
this.graphs.on("rendered", this._onGraphRendered);
|
||||
},
|
||||
|
@ -84,9 +81,6 @@ let OverviewView = {
|
|||
PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_WILL_STOP, this._onRecordingWillStop);
|
||||
this.graphs.off("selecting", this._onGraphSelecting);
|
||||
this.graphs.off("rendered", this._onGraphRendered);
|
||||
yield this.graphs.destroy();
|
||||
|
@ -207,31 +201,25 @@ let OverviewView = {
|
|||
* exist yet, but can just disable from here. This will only trigger for
|
||||
* manual recordings.
|
||||
*/
|
||||
_onRecordingWillStart: Task.async(function* () {
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingWillStart: OverviewViewOnStateChange(Task.async(function* () {
|
||||
yield this._checkSelection();
|
||||
this.graphs.dropSelection();
|
||||
}),
|
||||
})),
|
||||
|
||||
/**
|
||||
* Called when recording actually starts.
|
||||
*/
|
||||
_onRecordingStarted: function (_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
},
|
||||
_onRecordingStarted: OverviewViewOnStateChange(),
|
||||
|
||||
/**
|
||||
* Called when recording will stop.
|
||||
*/
|
||||
_onRecordingWillStop: function(_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
},
|
||||
_onRecordingWillStop: OverviewViewOnStateChange(),
|
||||
|
||||
/**
|
||||
* Called when recording actually stops.
|
||||
*/
|
||||
_onRecordingStopped: Task.async(function* (_, recording) {
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingStopped: OverviewViewOnStateChange(Task.async(function* (_, recording) {
|
||||
// Check to see if the recording that just stopped is the current recording.
|
||||
// If it is, render the high-res graphs. For manual recordings, it will also
|
||||
// be the current recording, but profiles generated by `console.profile` can stop
|
||||
|
@ -242,39 +230,21 @@ let OverviewView = {
|
|||
}
|
||||
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
yield this._checkSelection(recording);
|
||||
}),
|
||||
})),
|
||||
|
||||
/**
|
||||
* Called when a new recording is selected.
|
||||
*/
|
||||
_onRecordingSelected: Task.async(function* (_, recording) {
|
||||
if (!recording) {
|
||||
return;
|
||||
}
|
||||
this._onRecordingStateChange();
|
||||
_onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
|
||||
this._setGraphVisibilityFromRecordingFeatures(recording);
|
||||
|
||||
// If this recording is complete, render the high res graph
|
||||
if (!recording.isRecording()) {
|
||||
if (recording.isCompleted()) {
|
||||
yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||
}
|
||||
yield this._checkSelection(recording);
|
||||
this.graphs.dropSelection();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Called when a recording is starting, stopping, or about to start/stop.
|
||||
* Checks the current recording displayed to determine whether or not
|
||||
* the polling for rendering the overview graph needs to start or stop.
|
||||
*/
|
||||
_onRecordingStateChange: function () {
|
||||
let currentRecording = PerformanceController.getCurrentRecording();
|
||||
if (!currentRecording || (this.isRendering() && !currentRecording.isRecording())) {
|
||||
this._stopPolling();
|
||||
} else if (currentRecording.isRecording() && !this.isRendering()) {
|
||||
this._startPolling();
|
||||
}
|
||||
},
|
||||
})),
|
||||
|
||||
/**
|
||||
* Start the polling for rendering the overview graph.
|
||||
|
@ -303,7 +273,7 @@ let OverviewView = {
|
|||
* based on whether a recording currently exists and is not in progress.
|
||||
*/
|
||||
_checkSelection: Task.async(function* (recording) {
|
||||
let isEnabled = recording ? !recording.isRecording() : false;
|
||||
let isEnabled = recording ? recording.isCompleted() : false;
|
||||
yield this.graphs.selectionEnabled(isEnabled);
|
||||
}),
|
||||
|
||||
|
@ -375,5 +345,35 @@ let OverviewView = {
|
|||
toString: () => "[object OverviewView]"
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility that can wrap a method of OverviewView that
|
||||
* handles a recording state change like when a recording is starting,
|
||||
* stopping, or about to start/stop, and determines whether or not
|
||||
* the polling for rendering the overview graphs needs to start or stop.
|
||||
* Must be called with the OverviewView context.
|
||||
*
|
||||
* @param {function?} fn
|
||||
* @return {function}
|
||||
*/
|
||||
function OverviewViewOnStateChange (fn) {
|
||||
return function _onRecordingStateChange () {
|
||||
let currentRecording = PerformanceController.getCurrentRecording();
|
||||
|
||||
// All these methods require a recording to exist.
|
||||
if (!currentRecording) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isRendering() && !currentRecording.isRecording()) {
|
||||
this._stopPolling();
|
||||
} else if (currentRecording.isRecording() && !this.isRendering()) {
|
||||
this._startPolling();
|
||||
}
|
||||
if (fn) {
|
||||
fn.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decorates the OverviewView as an EventEmitter
|
||||
EventEmitter.decorate(OverviewView);
|
||||
|
|
|
@ -24,8 +24,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
|
|||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||
PerformanceController.on(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
|
@ -37,8 +35,6 @@ let RecordingsView = Heritage.extend(WidgetMethods, {
|
|||
destroy: function() {
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.CONSOLE_RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||
PerformanceController.off(EVENTS.RECORDINGS_CLEARED, this._onRecordingsCleared);
|
||||
this.widget.removeEventListener("select", this._onSelect, false);
|
||||
|
|
|
@ -76,13 +76,13 @@ registerCleanupFunction(() => {
|
|||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ support-files =
|
|||
[browser_graphs-06.js]
|
||||
[browser_graphs-07a.js]
|
||||
[browser_graphs-07b.js]
|
||||
[browser_graphs-07c.js]
|
||||
[browser_graphs-08.js]
|
||||
[browser_graphs-09a.js]
|
||||
[browser_graphs-09b.js]
|
||||
|
|
|
@ -91,21 +91,21 @@ function testGraph(graph) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
let HORIZONTAL_AXIS = 1;
|
||||
|
@ -114,8 +114,8 @@ let VERTICAL_AXIS = 2;
|
|||
function scroll(graph, wheel, axis, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseWheel({ clientX: x, clientY: y, axis, detail: wheel, axis,
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseWheel({ testX: x, testY: y, axis, detail: wheel, axis,
|
||||
HORIZONTAL_AXIS,
|
||||
VERTICAL_AXIS
|
||||
});
|
||||
|
|
|
@ -68,8 +68,8 @@ let VERTICAL_AXIS = 2;
|
|||
function scroll(graph, wheel, axis, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseWheel({ clientX: x, clientY: y, axis, detail: wheel, axis,
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseWheel({ testX: x, testY: y, axis, detail: wheel, axis,
|
||||
HORIZONTAL_AXIS,
|
||||
VERTICAL_AXIS
|
||||
});
|
||||
|
|
|
@ -121,19 +121,19 @@ function testGraph(graph) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ function map(value, istart, istop, ostart, ostop) {
|
|||
function click(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -172,29 +172,29 @@ function testGraph(graph, dragStop) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function click(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function normalDragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function buggyDragStop(graph, x, y = 1) {
|
||||
|
@ -204,13 +204,13 @@ function buggyDragStop(graph, x, y = 1) {
|
|||
// Only fire a mousemove instead of a mouseup.
|
||||
// This happens when the mouseup happens outside of the toolbox,
|
||||
// see Bug 1066504.
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ clientX: x, clientY: y, buttons: 0 });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y, buttons: 0 });
|
||||
}
|
||||
|
||||
function scroll(graph, wheel, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseWheel({ clientX: x, clientY: y, detail: wheel });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseWheel({ testX: x, testY: y, detail: wheel });
|
||||
}
|
||||
|
|
|
@ -48,19 +48,19 @@ function testGraph(graph) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests if movement via event dispatching using screenX / screenY
|
||||
// works. All of the other tests directly use the graph's mouse event
|
||||
// callbacks with textX / testY for convenience.
|
||||
|
||||
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
|
||||
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
|
||||
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let [host, win, doc] = yield createHost();
|
||||
let graph = new LineGraphWidget(doc.body, "fps");
|
||||
yield graph.once("ready");
|
||||
testGraph(graph);
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
graph.setData(TEST_DATA);
|
||||
|
||||
info("Making a selection.");
|
||||
|
||||
dragStart(graph, 300);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should start (1).");
|
||||
is(graph.getSelection().start, 300,
|
||||
"The current selection start value is correct (1).");
|
||||
is(graph.getSelection().end, 300,
|
||||
"The current selection end value is correct (1).");
|
||||
|
||||
hover(graph, 400);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should still be in progress (2).");
|
||||
is(graph.getSelection().start, 300,
|
||||
"The current selection start value is correct (2).");
|
||||
is(graph.getSelection().end, 400,
|
||||
"The current selection end value is correct (2).");
|
||||
|
||||
dragStop(graph, 500);
|
||||
ok(!graph.hasSelectionInProgress(),
|
||||
"The selection should have stopped (3).");
|
||||
is(graph.getSelection().start, 300,
|
||||
"The current selection start value is correct (3).");
|
||||
is(graph.getSelection().end, 500,
|
||||
"The current selection end value is correct (3).");
|
||||
|
||||
info("Making a new selection.");
|
||||
|
||||
dragStart(graph, 200);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should start (4).");
|
||||
is(graph.getSelection().start, 200,
|
||||
"The current selection start value is correct (4).");
|
||||
is(graph.getSelection().end, 200,
|
||||
"The current selection end value is correct (4).");
|
||||
|
||||
hover(graph, 300);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should still be in progress (5).");
|
||||
is(graph.getSelection().start, 200,
|
||||
"The current selection start value is correct (5).");
|
||||
is(graph.getSelection().end, 300,
|
||||
"The current selection end value is correct (5).");
|
||||
|
||||
dragStop(graph, 400);
|
||||
ok(!graph.hasSelectionInProgress(),
|
||||
"The selection should have stopped (6).");
|
||||
is(graph.getSelection().start, 200,
|
||||
"The current selection start value is correct (6).");
|
||||
is(graph.getSelection().end, 400,
|
||||
"The current selection end value is correct (6).");
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function dispatchEvent(graph, x, y, type) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
let quad = graph._canvas.getBoxQuads({
|
||||
relativeTo: window.document
|
||||
})[0];
|
||||
|
||||
let screenX = window.screenX + quad.p1.x + x;
|
||||
let screenY = window.screenY + quad.p1.y + y;
|
||||
|
||||
graph._canvas.dispatchEvent(new MouseEvent(type, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 1,
|
||||
view: window,
|
||||
screenX: screenX,
|
||||
screenY: screenY,
|
||||
}));
|
||||
}
|
||||
|
||||
function hover(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, "mousemove");
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, "mousemove");
|
||||
dispatchEvent(graph, x, y, "mousedown");
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, "mousemove");
|
||||
dispatchEvent(graph, x, y, "mouseup");
|
||||
}
|
|
@ -46,21 +46,21 @@ function testGraph(graph) {
|
|||
function click(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -121,19 +121,19 @@ function* testGraph(host, graph) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -135,19 +135,19 @@ function testGraphs(graph1, graph2) {
|
|||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
|
|
@ -70,20 +70,20 @@ function* testGraph(graph) {
|
|||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseDown({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseUp({ testX: x, testY: y });
|
||||
}
|
||||
|
||||
function scroll(graph, wheel, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseWheel({ clientX: x, clientY: y, detail: wheel });
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseWheel({ testX: x, testY: y, detail: wheel });
|
||||
}
|
||||
|
|
|
@ -731,9 +731,7 @@ FlameGraph.prototype = {
|
|||
* Listener for the "mousemove" event on the graph's container.
|
||||
*/
|
||||
_onMouseMove: function(e) {
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);
|
||||
|
||||
let canvasWidth = this._width;
|
||||
let canvasHeight = this._height;
|
||||
|
@ -783,9 +781,7 @@ FlameGraph.prototype = {
|
|||
* Listener for the "mousedown" event on the graph's container.
|
||||
*/
|
||||
_onMouseDown: function(e) {
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
let {mouseX, mouseY} = this._getRelativeEventCoordinates(e);
|
||||
|
||||
this._selectionDragger.origin = mouseX;
|
||||
this._selectionDragger.anchor.start = this._selection.start;
|
||||
|
@ -817,8 +813,7 @@ FlameGraph.prototype = {
|
|||
* Listener for the "wheel" event on the graph's container.
|
||||
*/
|
||||
_onMouseWheel: function(e) {
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let {mouseX} = this._getRelativeEventCoordinates(e);
|
||||
|
||||
let canvasWidth = this._width;
|
||||
let canvasHeight = this._height;
|
||||
|
@ -941,6 +936,27 @@ FlameGraph.prototype = {
|
|||
return { left: x, top: y };
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a MouseEvent, make it relative to this._canvas.
|
||||
* @return object {mouseX,mouseY}
|
||||
*/
|
||||
_getRelativeEventCoordinates: function(e) {
|
||||
// For ease of testing, testX and testY can be passed in as the event
|
||||
// object.
|
||||
if ("testX" in e && "testY" in e) {
|
||||
return {
|
||||
mouseX: e.testX * this._pixelRatio,
|
||||
mouseY: e.testY * this._pixelRatio
|
||||
};
|
||||
}
|
||||
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
|
||||
return {mouseX,mouseY};
|
||||
},
|
||||
|
||||
/**
|
||||
* Listener for the "resize" event on the graph's parent node.
|
||||
*/
|
||||
|
|
|
@ -157,6 +157,7 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
|||
AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
|
||||
this._iframe = iframe;
|
||||
this._window = iframe.contentWindow;
|
||||
this._topWindow = this._window.top;
|
||||
this._document = iframe.contentDocument;
|
||||
this._pixelRatio = sharpness || this._window.devicePixelRatio;
|
||||
|
||||
|
@ -194,7 +195,6 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
|||
|
||||
this._window.addEventListener("mousemove", this._onMouseMove);
|
||||
this._window.addEventListener("mousedown", this._onMouseDown);
|
||||
this._window.addEventListener("mouseup", this._onMouseUp);
|
||||
this._window.addEventListener("MozMousePixelScroll", this._onMouseWheel);
|
||||
this._window.addEventListener("mouseout", this._onMouseOut);
|
||||
|
||||
|
@ -241,9 +241,10 @@ AbstractCanvasGraph.prototype = {
|
|||
destroy: Task.async(function *() {
|
||||
yield this.ready();
|
||||
|
||||
this._topWindow.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._topWindow.removeEventListener("mouseup", this._onMouseUp);
|
||||
this._window.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._window.removeEventListener("mousedown", this._onMouseDown);
|
||||
this._window.removeEventListener("mouseup", this._onMouseUp);
|
||||
this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);
|
||||
this._window.removeEventListener("mouseout", this._onMouseOut);
|
||||
|
||||
|
@ -939,22 +940,34 @@ AbstractCanvasGraph.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Gets the offset of this graph's container relative to the owner window.
|
||||
*
|
||||
* @return object
|
||||
* The { left, top } offset.
|
||||
* Given a MouseEvent, make it relative to this._canvas.
|
||||
* @return object {mouseX,mouseY}
|
||||
*/
|
||||
_getContainerOffset: function() {
|
||||
let node = this._canvas;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
||||
while (node = node.offsetParent) {
|
||||
x += node.offsetLeft;
|
||||
y += node.offsetTop;
|
||||
_getRelativeEventCoordinates: function(e) {
|
||||
// For ease of testing, testX and testY can be passed in as the event
|
||||
// object. If so, just return this.
|
||||
if ("testX" in e && "testY" in e) {
|
||||
return {
|
||||
mouseX: e.testX * this._pixelRatio,
|
||||
mouseY: e.testY * this._pixelRatio
|
||||
};
|
||||
}
|
||||
|
||||
return { left: x, top: y };
|
||||
let quad = this._canvas.getBoxQuads({
|
||||
relativeTo: this._topWindow.document
|
||||
})[0];
|
||||
|
||||
let x = (e.screenX - this._topWindow.screenX) - quad.p1.x;
|
||||
let y = (e.screenY - this._topWindow.screenY) - quad.p1.y;
|
||||
|
||||
// Don't allow the event coordinates to be bigger than the canvas
|
||||
// or less than 0.
|
||||
let maxX = quad.p2.x - quad.p1.x;
|
||||
let maxY = quad.p3.y - quad.p1.y;
|
||||
let mouseX = Math.max(0, Math.min(x, maxX)) * this._pixelRatio;
|
||||
let mouseY = Math.max(0, Math.min(x, maxY)) * this._pixelRatio;
|
||||
|
||||
return {mouseX,mouseY};
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -964,6 +977,14 @@ AbstractCanvasGraph.prototype = {
|
|||
let resizer = this._selectionResizer;
|
||||
let dragger = this._selectionDragger;
|
||||
|
||||
// Need to stop propagation here, since this function can be bound
|
||||
// to both this._window and this._topWindow. It's only attached to
|
||||
// this._topWindow during a drag event. Null check here since tests
|
||||
// don't pass this method into the event object.
|
||||
if (e.stopPropagation && this._isMouseActive) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
// If a mouseup happened outside the toolbox and the current operation
|
||||
// is causing the selection changed, then end it.
|
||||
if (e.buttons == 0 && (this.hasSelectionInProgress() ||
|
||||
|
@ -972,9 +993,7 @@ AbstractCanvasGraph.prototype = {
|
|||
return this._onMouseUp(e);
|
||||
}
|
||||
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
let {mouseX,mouseY} = this._getRelativeEventCoordinates(e);
|
||||
this._cursor.x = mouseX;
|
||||
this._cursor.y = mouseY;
|
||||
|
||||
|
@ -1032,8 +1051,7 @@ AbstractCanvasGraph.prototype = {
|
|||
*/
|
||||
_onMouseDown: function(e) {
|
||||
this._isMouseActive = true;
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let {mouseX} = this._getRelativeEventCoordinates(e);
|
||||
|
||||
switch (this._canvas.getAttribute("input")) {
|
||||
case "hovering-background":
|
||||
|
@ -1062,6 +1080,11 @@ AbstractCanvasGraph.prototype = {
|
|||
break;
|
||||
}
|
||||
|
||||
// During a drag, bind to the top level window so that mouse movement
|
||||
// outside of this frame will still work.
|
||||
this._topWindow.addEventListener("mousemove", this._onMouseMove);
|
||||
this._topWindow.addEventListener("mouseup", this._onMouseUp);
|
||||
|
||||
this._shouldRedraw = true;
|
||||
this.emit("mousedown");
|
||||
},
|
||||
|
@ -1071,8 +1094,7 @@ AbstractCanvasGraph.prototype = {
|
|||
*/
|
||||
_onMouseUp: function(e) {
|
||||
this._isMouseActive = false;
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let {mouseX} = this._getRelativeEventCoordinates(e);
|
||||
|
||||
switch (this._canvas.getAttribute("input")) {
|
||||
case "hovering-background":
|
||||
|
@ -1108,6 +1130,10 @@ AbstractCanvasGraph.prototype = {
|
|||
break;
|
||||
}
|
||||
|
||||
// No longer dragging, no need to bind to the top level window.
|
||||
this._topWindow.removeEventListener("mousemove", this._onMouseMove);
|
||||
this._topWindow.removeEventListener("mouseup", this._onMouseUp);
|
||||
|
||||
this._shouldRedraw = true;
|
||||
this.emit("mouseup");
|
||||
},
|
||||
|
@ -1120,8 +1146,7 @@ AbstractCanvasGraph.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let {mouseX} = this._getRelativeEventCoordinates(e);
|
||||
let focusX = mouseX;
|
||||
|
||||
let selection = this._selection;
|
||||
|
@ -1181,7 +1206,7 @@ AbstractCanvasGraph.prototype = {
|
|||
this.emit("scroll");
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* Listener for the "mouseout" event on the graph's container.
|
||||
* Clear any active cursors if a drag isn't happening.
|
||||
*/
|
||||
|
|
|
@ -49,6 +49,7 @@ XPCOMUtils.defineLazyGetter(this, "DEFAULT_AREA_PLACEMENTS", function() {
|
|||
"urlbar-container",
|
||||
"search-container",
|
||||
"bookmarks-menu-button",
|
||||
"pocket-button",
|
||||
"downloads-button",
|
||||
"home-button",
|
||||
"social-share-button",
|
||||
|
|
Двоичные данные
browser/themes/linux/Toolbar-inverted.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
browser/themes/linux/Toolbar.png
До Ширина: | Высота: | Размер: 17 KiB После Ширина: | Высота: | Размер: 17 KiB |
Двоичные данные
browser/themes/osx/Toolbar-inverted.png
До Ширина: | Высота: | Размер: 30 KiB После Ширина: | Высота: | Размер: 30 KiB |
Двоичные данные
browser/themes/osx/Toolbar-inverted@2x.png
До Ширина: | Высота: | Размер: 75 KiB После Ширина: | Высота: | Размер: 80 KiB |
Двоичные данные
browser/themes/osx/Toolbar-yosemite.png
До Ширина: | Высота: | Размер: 19 KiB После Ширина: | Высота: | Размер: 19 KiB |
Двоичные данные
browser/themes/osx/Toolbar-yosemite@2x.png
До Ширина: | Высота: | Размер: 45 KiB После Ширина: | Высота: | Размер: 45 KiB |
Двоичные данные
browser/themes/osx/Toolbar.png
До Ширина: | Высота: | Размер: 30 KiB После Ширина: | Высота: | Размер: 30 KiB |
Двоичные данные
browser/themes/osx/Toolbar@2x.png
До Ширина: | Высота: | Размер: 81 KiB После Ширина: | Высота: | Размер: 81 KiB |
Двоичные данные
browser/themes/windows/Toolbar-XP.png
До Ширина: | Высота: | Размер: 18 KiB После Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
browser/themes/windows/Toolbar-aero.png
До Ширина: | Высота: | Размер: 18 KiB После Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
browser/themes/windows/Toolbar-aero@2x.png
До Ширина: | Высота: | Размер: 45 KiB После Ширина: | Высота: | Размер: 44 KiB |
Двоичные данные
browser/themes/windows/Toolbar-inverted.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
browser/themes/windows/Toolbar-inverted@2x.png
До Ширина: | Высота: | Размер: 29 KiB После Ширина: | Высота: | Размер: 29 KiB |
Двоичные данные
browser/themes/windows/Toolbar-lunaSilver.png
До Ширина: | Высота: | Размер: 18 KiB После Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
browser/themes/windows/Toolbar.png
До Ширина: | Высота: | Размер: 6.8 KiB После Ширина: | Высота: | Размер: 6.9 KiB |
Двоичные данные
browser/themes/windows/Toolbar@2x.png
До Ширина: | Высота: | Размер: 16 KiB После Ширина: | Высота: | Размер: 16 KiB |
|
@ -1953,6 +1953,9 @@ public class BrowserApp extends GeckoApp
|
|||
});
|
||||
}
|
||||
} else {
|
||||
if (mDoorHangerPopup != null) {
|
||||
mDoorHangerPopup.disable();
|
||||
}
|
||||
mTabsPanel.show(panel);
|
||||
}
|
||||
}
|
||||
|
@ -1960,6 +1963,9 @@ public class BrowserApp extends GeckoApp
|
|||
@Override
|
||||
public void hideTabs() {
|
||||
mTabsPanel.hide();
|
||||
if (mDoorHangerPopup != null) {
|
||||
mDoorHangerPopup.enable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2234,6 +2240,14 @@ public class BrowserApp extends GeckoApp
|
|||
// Expected to be fixed by bug 915825.
|
||||
hideHomePager(url);
|
||||
loadUrlOrKeywordSearch(url);
|
||||
clearSelectedTabApplicationId();
|
||||
}
|
||||
|
||||
private void clearSelectedTabApplicationId() {
|
||||
final Tab selected = Tabs.getInstance().getSelectedTab();
|
||||
if (selected != null) {
|
||||
selected.setApplicationId(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadUrlOrKeywordSearch(final String url) {
|
||||
|
@ -3572,6 +3586,7 @@ public class BrowserApp extends GeckoApp
|
|||
startActivity(intent);
|
||||
} else if (!maybeSwitchToTab(url, flags)) {
|
||||
openUrlAndStopEditing(url);
|
||||
clearSelectedTabApplicationId();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.provider.Browser;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -1424,27 +1423,33 @@ public abstract class GeckoApp
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads the initial tab at Fennec startup.
|
||||
*
|
||||
* If Fennec was opened with an external URL, that URL will be loaded.
|
||||
* Otherwise, unless there was a session restore, the default URL
|
||||
* (about:home) be loaded.
|
||||
*
|
||||
* @param url External URL to load, or null to load the default URL
|
||||
* Loads the initial tab at Fennec startup. If we don't restore tabs, this
|
||||
* tab will be about:home. If we restore tabs, we don't need to create a new tab.
|
||||
*/
|
||||
protected void loadStartupTab(String url, int flags) {
|
||||
if (url == null) {
|
||||
if (!mShouldRestore) {
|
||||
// Show about:home if we aren't restoring previous session and
|
||||
// there's no external URL.
|
||||
Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
|
||||
}
|
||||
} else {
|
||||
// If given an external URL, load it
|
||||
Tabs.getInstance().loadUrl(url, flags);
|
||||
protected void loadStartupTabWithAboutHome(final int flags) {
|
||||
if (!mShouldRestore) {
|
||||
Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the initial tab at Fennec startup. This tab will load with the given
|
||||
* external URL. If that URL is invalid, about:home will be loaded.
|
||||
*
|
||||
* @param url External URL to load.
|
||||
* @param extraApplicationId Identifies the calling application; delivered with the URL
|
||||
*/
|
||||
protected void loadStartupTabWithExternalUrl(final String url, final String extraApplicationId,
|
||||
final int flags) {
|
||||
// Invalid url
|
||||
if (url == null) {
|
||||
loadStartupTabWithAboutHome(flags);
|
||||
return;
|
||||
}
|
||||
|
||||
Tabs.getInstance().loadUrl(url, extraApplicationId, flags);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
mInitialized = true;
|
||||
|
||||
|
@ -1509,10 +1514,11 @@ public abstract class GeckoApp
|
|||
if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
|
||||
flags |= Tabs.LOADURL_PINNED;
|
||||
}
|
||||
loadStartupTab(passedUri, flags);
|
||||
final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
|
||||
loadStartupTabWithExternalUrl(passedUri, extraApplicationId, flags);
|
||||
} else {
|
||||
if (!mIsRestoringActivity) {
|
||||
loadStartupTab(null, Tabs.LOADURL_NEW_TAB);
|
||||
loadStartupTabWithAboutHome(Tabs.LOADURL_NEW_TAB);
|
||||
}
|
||||
|
||||
Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
|
||||
|
@ -1824,9 +1830,10 @@ public abstract class GeckoApp
|
|||
TabQueueHelper.openQueuedUrls(GeckoApp.this, mProfile, TabQueueHelper.FILE_NAME, true);
|
||||
} else {
|
||||
String uri = intent.getDataString();
|
||||
Tabs.getInstance().loadUrl(uri, Tabs.LOADURL_NEW_TAB |
|
||||
Tabs.LOADURL_USER_ENTERED |
|
||||
Tabs.LOADURL_EXTERNAL);
|
||||
final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
|
||||
Tabs.getInstance().loadUrl(uri, extraApplicationId, Tabs.LOADURL_NEW_TAB |
|
||||
Tabs.LOADURL_USER_ENTERED |
|
||||
Tabs.LOADURL_EXTERNAL);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -50,6 +50,7 @@ public class Tab {
|
|||
private String mTitle;
|
||||
private Bitmap mFavicon;
|
||||
private String mFaviconUrl;
|
||||
private String mApplicationId; // Intended to be null after explicit user action.
|
||||
|
||||
// The set of all available Favicons for this tab, sorted by attractiveness.
|
||||
final TreeSet<RemoteFavicon> mAvailableFavicons = new TreeSet<>();
|
||||
|
@ -194,6 +195,14 @@ public class Tab {
|
|||
return mFavicon;
|
||||
}
|
||||
|
||||
protected String getApplicationId() {
|
||||
return mApplicationId;
|
||||
}
|
||||
|
||||
protected void setApplicationId(final String applicationId) {
|
||||
mApplicationId = applicationId;
|
||||
}
|
||||
|
||||
public BitmapDrawable getThumbnail() {
|
||||
return mThumbnail;
|
||||
}
|
||||
|
|