Merge f-t to m-c, a=merge
|
@ -141,16 +141,6 @@ function initResponsiveDesign() {
|
|||
|
||||
// Enable touch events
|
||||
responsive.enableTouch();
|
||||
|
||||
// Automatically toggle responsive design mode
|
||||
let width = 320, height = 480;
|
||||
// We have to take into account padding and border introduced with the
|
||||
// device look'n feel:
|
||||
width += 15*2; // Horizontal padding
|
||||
width += 1*2; // Vertical border
|
||||
height += 60; // Top Padding
|
||||
height += 1; // Top border
|
||||
responsive.setSize(width, height);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -1359,7 +1359,7 @@ var BookmarkingUI = {
|
|||
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.asyncExecuteLegacyQueries([query], 1, options, {
|
||||
handleResult: function (aResultSet) {
|
||||
let onItemClick = function (aEvent) {
|
||||
let onItemCommand = function (aEvent) {
|
||||
let item = aEvent.target;
|
||||
openUILink(item.getAttribute("targetURI"), aEvent);
|
||||
CustomizableUI.hidePanelForNode(item);
|
||||
|
@ -1379,7 +1379,7 @@ var BookmarkingUI = {
|
|||
item.setAttribute("targetURI", uri);
|
||||
item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
|
||||
extraCSSClass);
|
||||
item.addEventListener("click", onItemClick);
|
||||
item.addEventListener("command", onItemCommand);
|
||||
if (icon) {
|
||||
let iconURL = "moz-anno:favicon:" + icon;
|
||||
item.setAttribute("image", iconURL);
|
||||
|
|
|
@ -259,8 +259,6 @@ SocialUI = {
|
|||
// called on tab/urlbar/location changes and after customization. Update
|
||||
// anything that is tab specific.
|
||||
updateState: function() {
|
||||
if (location == "about:customizing")
|
||||
return;
|
||||
goSetCommandEnabled("Social:PageShareOrMark", this.canShareOrMarkPage(gBrowser.currentURI));
|
||||
if (!SocialUI.enabled)
|
||||
return;
|
||||
|
|
|
@ -183,9 +183,6 @@ if (AppConstants.MOZ_SAFE_BROWSING) {
|
|||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader",
|
||||
"resource:///modules/CustomizationTabPreloader.jsm", "CustomizationTabPreloader");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
|
@ -2344,10 +2341,12 @@ function URLBarSetURI(aURI) {
|
|||
// 2. if remote newtab is enabled and it's the default remote newtab page
|
||||
let defaultRemoteURL = gAboutNewTabService.remoteEnabled &&
|
||||
uri.spec === gAboutNewTabService.newTabURL;
|
||||
if (gInitialPages.includes(uri.spec) || defaultRemoteURL)
|
||||
value = gBrowser.selectedBrowser.hasContentOpener ? uri.spec : "";
|
||||
else
|
||||
if ((gInitialPages.includes(uri.spec) || defaultRemoteURL) &&
|
||||
checkEmptyPageOrigin(gBrowser.selectedBrowser, uri)) {
|
||||
value = "";
|
||||
} else {
|
||||
value = losslessDecodeURI(uri);
|
||||
}
|
||||
|
||||
valid = !isBlankPageURL(uri.spec);
|
||||
}
|
||||
|
@ -4327,7 +4326,7 @@ var XULBrowserWindow = {
|
|||
// Do not update urlbar if there was a subframe navigation
|
||||
|
||||
if (aWebProgress.isTopLevel) {
|
||||
if ((location == "about:blank" && !gBrowser.selectedBrowser.hasContentOpener) ||
|
||||
if ((location == "about:blank" && checkEmptyPageOrigin()) ||
|
||||
location == "") { // Second condition is for new tabs, otherwise
|
||||
// reload function is enabled until tab is refreshed.
|
||||
this.reloadCommand.setAttribute("disabled", "true");
|
||||
|
@ -4339,7 +4338,7 @@ var XULBrowserWindow = {
|
|||
URLBarSetURI(aLocationURI);
|
||||
|
||||
BookmarkingUI.onLocationChange();
|
||||
SocialUI.updateState(location);
|
||||
SocialUI.updateState();
|
||||
UITour.onLocationChange(location);
|
||||
gTabletModePageCounter.inc();
|
||||
}
|
||||
|
@ -4387,12 +4386,11 @@ var XULBrowserWindow = {
|
|||
|
||||
// Try not to instantiate gCustomizeMode as much as possible,
|
||||
// so don't use CustomizeMode.jsm to check for URI or customizing.
|
||||
let customizingURI = "about:customizing";
|
||||
if (location == customizingURI) {
|
||||
if (location == "about:blank" &&
|
||||
gBrowser.selectedTab.hasAttribute("customizemode")) {
|
||||
gCustomizeMode.enter();
|
||||
} else if (location != customizingURI &&
|
||||
(CustomizationHandler.isEnteringCustomizeMode ||
|
||||
CustomizationHandler.isCustomizing())) {
|
||||
} else if (CustomizationHandler.isEnteringCustomizeMode ||
|
||||
CustomizationHandler.isCustomizing()) {
|
||||
gCustomizeMode.exit();
|
||||
}
|
||||
}
|
||||
|
@ -6390,11 +6388,14 @@ function isTabEmpty(aTab) {
|
|||
if (aTab.hasAttribute("busy"))
|
||||
return false;
|
||||
|
||||
if (aTab.hasAttribute("customizemode"))
|
||||
return false;
|
||||
|
||||
let browser = aTab.linkedBrowser;
|
||||
if (!isBlankPageURL(browser.currentURI.spec))
|
||||
return false;
|
||||
|
||||
if (browser.hasContentOpener)
|
||||
if (!checkEmptyPageOrigin(browser))
|
||||
return false;
|
||||
|
||||
if (browser.canGoForward || browser.canGoBack)
|
||||
|
@ -6403,6 +6404,56 @@ function isTabEmpty(aTab) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a page can be considered as 'empty', that its URI
|
||||
* reflects its origin, and that if it's loaded in a tab, that tab
|
||||
* could be considered 'empty' (e.g. like the result of opening
|
||||
* a 'blank' new tab).
|
||||
*
|
||||
* We have to do more than just check the URI, because especially
|
||||
* for things like about:blank, it is possible that the opener or
|
||||
* some other page has control over the contents of the page.
|
||||
*
|
||||
* @param browser {Browser}
|
||||
* The browser whose page we're checking (the selected browser
|
||||
* in this window if omitted).
|
||||
* @param uri {nsIURI}
|
||||
* The URI against which we're checking (the browser's currentURI
|
||||
* if omitted).
|
||||
*
|
||||
* @return false if the page was opened by or is controlled by arbitrary web
|
||||
* content, unless that content corresponds with the URI.
|
||||
* true if the page is blank and controlled by a principal matching
|
||||
* that URI (or the system principal if the principal has no URI)
|
||||
*/
|
||||
function checkEmptyPageOrigin(browser = gBrowser.selectedBrowser,
|
||||
uri = browser.currentURI) {
|
||||
// If another page opened this page with e.g. window.open, this page might
|
||||
// be controlled by its opener - return false.
|
||||
if (browser.hasContentOpener) {
|
||||
return false;
|
||||
}
|
||||
let contentPrincipal = browser.contentPrincipal;
|
||||
if (gMultiProcessBrowser && browser.isRemoteBrowser &&
|
||||
!contentPrincipal && uri.spec == "about:blank") {
|
||||
// Need to specialcase this because of how stopping an about:blank
|
||||
// load from chrome on e10s causes a permanently null contentPrincipal,
|
||||
// see bug 1249362.
|
||||
return true;
|
||||
}
|
||||
// Not all principals have URIs...
|
||||
if (contentPrincipal.URI) {
|
||||
if (uri.spec == "about:blank" && contentPrincipal.isNullPrincipal) {
|
||||
return true;
|
||||
}
|
||||
return contentPrincipal.URI.equals(uri);
|
||||
}
|
||||
// ... so for those that don't have them, enforce that the page has the
|
||||
// system principal (this matches e.g. on about:home).
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
return ssm.isSystemPrincipal(contentPrincipal);
|
||||
}
|
||||
|
||||
function BrowserOpenSyncTabs() {
|
||||
if (Services.prefs.getBoolPref("services.sync.syncedTabsUIRefresh")) {
|
||||
gSyncUI.openSyncedTabsPanel();
|
||||
|
@ -7292,7 +7343,6 @@ function switchToTabHavingURI(aURI, aOpenNew, aOpenParams={}) {
|
|||
// window being in private browsing mode:
|
||||
const kPrivateBrowsingWhitelist = new Set([
|
||||
"about:addons",
|
||||
"about:customizing",
|
||||
]);
|
||||
|
||||
let ignoreFragment = aOpenParams.ignoreFragment;
|
||||
|
|
|
@ -1398,6 +1398,11 @@
|
|||
|
||||
crop = "center";
|
||||
|
||||
} else if (aTab.hasAttribute("customizemode")) {
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
title = gNavigatorBundle.getFormattedString("customizeMode.tabTitle",
|
||||
[ brandShortName ]);
|
||||
} else // Still no title? Fall back to our untitled string.
|
||||
title = this.mStringBundle.getString("tabs.emptyTabTitle");
|
||||
}
|
||||
|
@ -1926,11 +1931,6 @@
|
|||
|
||||
b.droppedLinkHandler = handleDroppedLink;
|
||||
|
||||
// Swap in a preloaded customize tab, if available.
|
||||
if (aURI == "about:customizing") {
|
||||
usingPreloadedContent = gCustomizationTabPreloader.newTab(t);
|
||||
}
|
||||
|
||||
// Dispatch a new tab notification. We do this once we're
|
||||
// entirely done, so that things are in a consistent state
|
||||
// even if the event listener opens or closes tabs.
|
||||
|
@ -2474,33 +2474,6 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="swapNewTabWithBrowser">
|
||||
<parameter name="aNewTab"/>
|
||||
<parameter name="aBrowser"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// The browser must be standalone.
|
||||
if (aBrowser.getTabBrowser())
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
|
||||
// The tab is definitely not loading.
|
||||
aNewTab.removeAttribute("busy");
|
||||
if (aNewTab.selected) {
|
||||
this.mIsBusy = false;
|
||||
}
|
||||
|
||||
this._swapBrowserDocShells(aNewTab, aBrowser);
|
||||
|
||||
// Update the new tab's title.
|
||||
this.setTabTitle(aNewTab);
|
||||
|
||||
if (aNewTab.selected) {
|
||||
this.updateCurrentBrowser(true);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="swapBrowsersAndCloseOther">
|
||||
<parameter name="aOurTab"/>
|
||||
<parameter name="aOtherTab"/>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[browser_urlbar_blanking.js]
|
||||
support-files =
|
||||
file_blank_but_not_blank.html
|
|
@ -0,0 +1,35 @@
|
|||
"use strict";
|
||||
|
||||
add_task(function*() {
|
||||
for (let page of gInitialPages) {
|
||||
if (page == "about:newtab") {
|
||||
// New tab preloading makes this a pain to test, so skip
|
||||
continue;
|
||||
}
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, page);
|
||||
ok(!gURLBar.value, "The URL bar should be empty if we load a plain " + page + " page.");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
const URI = "http://www.example.com/browser/browser/base/content/test/urlbar/file_blank_but_not_blank.html";
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URI);
|
||||
is(gURLBar.value, URI, "The URL bar should match the URI");
|
||||
let browserLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
ContentTask.spawn(tab.linkedBrowser, null, function() {
|
||||
content.document.querySelector('a').click();
|
||||
});
|
||||
yield browserLoaded;
|
||||
ok(gURLBar.value.startsWith("javascript"), "The URL bar should have the JS URI");
|
||||
// When reloading, the javascript: uri we're using will throw an exception.
|
||||
// That's deliberate, so we need to tell mochitest to ignore it:
|
||||
SimpleTest.expectUncaughtException(true);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
|
||||
// This is sync, so by the time we return we should have changed the URL bar.
|
||||
content.location.reload();
|
||||
});
|
||||
ok(!!gURLBar.value, "URL bar should not be blank.");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
SimpleTest.expectUncaughtException(false);
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
<script>var q = 1;</script>
|
||||
<a href="javascript:q">Click me</a>
|
|
@ -23,6 +23,7 @@ BROWSER_CHROME_MANIFESTS += [
|
|||
'content/test/popupNotifications/browser.ini',
|
||||
'content/test/referrer/browser.ini',
|
||||
'content/test/social/browser.ini',
|
||||
'content/test/urlbar/browser.ini',
|
||||
]
|
||||
|
||||
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
|
||||
|
|
|
@ -99,8 +99,6 @@ static RedirEntry kRedirMap[] = {
|
|||
#endif
|
||||
{ "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "loopconversation", "chrome://loop/content/panels/conversation.html",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
|
|
|
@ -107,7 +107,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
|
|
|
@ -203,7 +203,7 @@ const CustomizableWidgets = [
|
|||
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.asyncExecuteLegacyQueries([query], 1, options, {
|
||||
handleResult: function (aResultSet) {
|
||||
let onItemClick = function (aEvent) {
|
||||
let onItemCommand = function (aEvent) {
|
||||
let item = aEvent.target;
|
||||
win.openUILink(item.getAttribute("targetURI"), aEvent);
|
||||
CustomizableUI.hidePanelForNode(item);
|
||||
|
@ -219,7 +219,7 @@ const CustomizableWidgets = [
|
|||
item.setAttribute("label", title || uri);
|
||||
item.setAttribute("targetURI", uri);
|
||||
item.setAttribute("class", "subviewbutton");
|
||||
item.addEventListener("click", onItemClick);
|
||||
item.addEventListener("command", onItemCommand);
|
||||
if (icon) {
|
||||
let iconURL = "moz-anno:favicon:" + icon;
|
||||
item.setAttribute("image", iconURL);
|
||||
|
|
|
@ -11,7 +11,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
||||
const kPrefCustomizationAnimation = "browser.uiCustomization.disableAnimation";
|
||||
const kPaletteId = "customization-palette";
|
||||
const kAboutURI = "about:customizing";
|
||||
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
|
||||
const kPlaceholderClass = "panel-customization-placeholder";
|
||||
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
|
||||
|
@ -36,6 +35,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
|||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
let gDebug;
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
|
@ -56,6 +57,23 @@ var gDisableAnimation = null;
|
|||
|
||||
var gDraggingInToolbars;
|
||||
|
||||
var gTab;
|
||||
|
||||
function closeGlobalTab() {
|
||||
let win = gTab.ownerGlobal;
|
||||
if (win.gBrowser.browsers.length == 1) {
|
||||
win.BrowserOpenTab();
|
||||
}
|
||||
win.gBrowser.removeTab(gTab);
|
||||
}
|
||||
|
||||
function unregisterGlobalTab() {
|
||||
gTab.removeEventListener("TabClose", unregisterGlobalTab);
|
||||
gTab.ownerGlobal.removeEventListener("unload", unregisterGlobalTab);
|
||||
gTab.removeAttribute("customizemode");
|
||||
gTab = null;
|
||||
}
|
||||
|
||||
function CustomizeMode(aWindow) {
|
||||
if (gDisableAnimation === null) {
|
||||
gDisableAnimation = Services.prefs.getPrefType(kPrefCustomizationAnimation) == Ci.nsIPrefBranch.PREF_BOOL &&
|
||||
|
@ -140,6 +158,36 @@ CustomizeMode.prototype = {
|
|||
lwthemeIcon.style.backgroundImage = "url(" + imageURL + ")";
|
||||
},
|
||||
|
||||
setTab: function(aTab) {
|
||||
if (gTab == aTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gTab) {
|
||||
closeGlobalTab();
|
||||
}
|
||||
|
||||
gTab = aTab;
|
||||
|
||||
gTab.setAttribute("customizemode", "true");
|
||||
SessionStore.persistTabAttribute("customizemode");
|
||||
|
||||
gTab.linkedBrowser.stop();
|
||||
|
||||
let win = gTab.ownerGlobal;
|
||||
|
||||
win.gBrowser.setTabTitle(gTab);
|
||||
win.gBrowser.setIcon(gTab,
|
||||
"chrome://browser/skin/customizableui/customizeFavicon.ico");
|
||||
|
||||
gTab.addEventListener("TabClose", unregisterGlobalTab);
|
||||
win.addEventListener("unload", unregisterGlobalTab);
|
||||
|
||||
if (gTab.selected) {
|
||||
win.gCustomizeMode.enter();
|
||||
}
|
||||
},
|
||||
|
||||
enter: function() {
|
||||
this._wantToBeInCustomizeMode = true;
|
||||
|
||||
|
@ -154,13 +202,18 @@ CustomizeMode.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// We don't need to switch to kAboutURI, or open a new tab at
|
||||
// kAboutURI if we're already on it.
|
||||
if (this.browser.selectedBrowser.currentURI.spec != kAboutURI) {
|
||||
this.window.switchToTabHavingURI(kAboutURI, true, {
|
||||
skipTabAnimation: true,
|
||||
});
|
||||
if (!gTab) {
|
||||
this.setTab(this.browser.loadOneTab("about:blank",
|
||||
{ inBackground: false,
|
||||
forceNotRemote: true,
|
||||
skipAnimation: true }));
|
||||
return;
|
||||
}
|
||||
if (!gTab.selected) {
|
||||
gTab.ownerGlobal.gBrowser.selectedTab = gTab;
|
||||
}
|
||||
gTab.ownerGlobal.focus();
|
||||
if (gTab.ownerDocument != this.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -313,10 +366,6 @@ CustomizeMode.prototype = {
|
|||
delete this._enableOutlinesTimeout;
|
||||
}, 0);
|
||||
|
||||
// It's possible that we didn't enter customize mode via the menu panel,
|
||||
// meaning we didn't kick off about:customizing preloading. If that's
|
||||
// the case, let's kick it off for the next time we load this mode.
|
||||
window.gCustomizationTabPreloader.ensurePreloading();
|
||||
if (!this._wantToBeInCustomizeMode) {
|
||||
this.exit();
|
||||
}
|
||||
|
@ -402,31 +451,14 @@ CustomizeMode.prototype = {
|
|||
|
||||
Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
|
||||
|
||||
let browser = document.getElementById("browser");
|
||||
if (this.browser.selectedBrowser.currentURI.spec == kAboutURI) {
|
||||
let custBrowser = this.browser.selectedBrowser;
|
||||
if (custBrowser.canGoBack) {
|
||||
// If there's history to this tab, just go back.
|
||||
// Note that this throws an exception if the previous document has a
|
||||
// problematic URL (e.g. about:idontexist)
|
||||
try {
|
||||
custBrowser.goBack();
|
||||
} catch (ex) {
|
||||
log.error(ex);
|
||||
}
|
||||
if (this.browser.selectedTab == gTab) {
|
||||
if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
|
||||
closeGlobalTab();
|
||||
} else {
|
||||
// If we can't go back, we're removing the about:customization tab.
|
||||
// We only do this if we're the top window for this window (so not
|
||||
// a dialog window, for example).
|
||||
if (window.getTopWin(true) == window) {
|
||||
let customizationTab = this.browser.selectedTab;
|
||||
if (this.browser.browsers.length == 1) {
|
||||
window.BrowserOpenTab();
|
||||
}
|
||||
this.browser.removeTab(customizationTab);
|
||||
}
|
||||
unregisterGlobalTab();
|
||||
}
|
||||
}
|
||||
let browser = document.getElementById("browser");
|
||||
browser.parentNode.selectedPanel = browser;
|
||||
let customizer = document.getElementById("customization-container");
|
||||
customizer.hidden = true;
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<window id="aboutCustomizingWindow"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&customizeMode.tabTitle;">
|
||||
<html:head>
|
||||
<html:link rel="icon" type="image/x-icon"
|
||||
href="chrome://browser/skin/customizableui/customizeFavicon.ico"/>
|
||||
</html:head>
|
||||
</window>
|
|
@ -3,7 +3,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/customizableui/aboutCustomizing.xul
|
||||
content/browser/customizableui/panelUI.css
|
||||
content/browser/customizableui/panelUI.js
|
||||
content/browser/customizableui/panelUI.xml
|
||||
|
|
|
@ -157,10 +157,6 @@ const PanelUI = {
|
|||
|
||||
this.panel.addEventListener("popupshown", function onPopupShown() {
|
||||
this.removeEventListener("popupshown", onPopupShown);
|
||||
// As an optimization for the customize mode transition, we preload
|
||||
// about:customizing in the background once the menu panel is first
|
||||
// shown.
|
||||
gCustomizationTabPreloader.ensurePreloading();
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ skip-if = os == "mac"
|
|||
[browser_886323_buildArea_removable_nodes.js]
|
||||
[browser_887438_currentset_shim.js]
|
||||
[browser_888817_currentset_updating.js]
|
||||
[browser_889120_customize_tab_merging.js]
|
||||
[browser_890140_orphaned_placeholders.js]
|
||||
[browser_890262_destroyWidget_after_add_to_panel.js]
|
||||
[browser_892955_isWidgetRemovable_for_removed_widgets.js]
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const kTestToolbarId = "test-empty-drag";
|
||||
|
||||
// Attempting to switch quickly from one tab to another to see whether the state changes
|
||||
// correctly.
|
||||
add_task(function* CheckBasicCustomizeMode() {
|
||||
yield startCustomizing();
|
||||
ok(CustomizationHandler.isCustomizing(), "We should be in customize mode");
|
||||
yield endCustomizing();
|
||||
ok(!CustomizationHandler.isCustomizing(), "We should not be in customize mode");
|
||||
});
|
||||
add_task(function* CheckQuickCustomizeModeSwitch() {
|
||||
let tab1 = gBrowser.addTab("about:newtab");
|
||||
gBrowser.selectedTab = tab1;
|
||||
let tab2 = gBrowser.addTab("about:customizing");
|
||||
let tab3 = gBrowser.addTab("about:newtab");
|
||||
gBrowser.selectedTab = tab2;
|
||||
try {
|
||||
yield waitForCondition(() => CustomizationHandler.isEnteringCustomizeMode);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
ok(CustomizationHandler.isEnteringCustomizeMode, "Should be entering customize mode");
|
||||
gBrowser.selectedTab = tab3;
|
||||
try {
|
||||
yield waitForCondition(() => !CustomizationHandler.isEnteringCustomizeMode && !CustomizationHandler.isCustomizing());
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
ok(!CustomizationHandler.isCustomizing(), "Should not be entering customize mode");
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
gBrowser.removeTab(tab3);
|
||||
});
|
||||
|
||||
add_task(function* asyncCleanup() {
|
||||
yield endCustomizing();
|
||||
});
|
||||
|
|
@ -199,7 +199,7 @@ function endCustomizing(aWindow=window) {
|
|||
newTabBrowser.stop();
|
||||
|
||||
// If we stop early enough, this might actually be about:blank.
|
||||
if (newTabBrowser.contentDocument.location.href == "about:blank") {
|
||||
if (newTabBrowser.currentURI.spec == "about:blank") {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ function endCustomizing(aWindow=window) {
|
|||
deferredLoadNewTab.resolve();
|
||||
}
|
||||
newTabBrowser.addEventListener("load", onNewTabLoaded, true);
|
||||
newTabBrowser.contentDocument.location.replace("about:blank");
|
||||
newTabBrowser.loadURI("about:blank");
|
||||
return deferredLoadNewTab.promise;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -70,6 +70,12 @@ DistributionCustomizer.prototype = {
|
|||
return this._locale;
|
||||
},
|
||||
|
||||
get _language() {
|
||||
let language = this._locale.split("-")[0];
|
||||
this.__defineGetter__("_language", () => language);
|
||||
return this._language;
|
||||
},
|
||||
|
||||
get _prefSvc() {
|
||||
let svc = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService);
|
||||
|
@ -112,6 +118,8 @@ DistributionCustomizer.prototype = {
|
|||
|
||||
if (keys.indexOf(key + "." + this._locale) >= 0) {
|
||||
key += "." + this._locale;
|
||||
} else if (keys.indexOf(key + "." + this._language) >= 0) {
|
||||
key += "." + this._language;
|
||||
}
|
||||
|
||||
if (!items[itemIndex])
|
||||
|
@ -323,6 +331,8 @@ DistributionCustomizer.prototype = {
|
|||
try {
|
||||
if (globalPrefs["about." + this._locale]) {
|
||||
partnerAbout.data = this._ini.getString("Global", "about." + this._locale);
|
||||
} else if (globalPrefs["about." + this._language]) {
|
||||
partnerAbout.data = this._ini.getString("Global", "about." + this._language);
|
||||
} else {
|
||||
partnerAbout.data = this._ini.getString("Global", "about");
|
||||
}
|
||||
|
@ -367,6 +377,17 @@ DistributionCustomizer.prototype = {
|
|||
try {
|
||||
let value = eval(this._ini.getString("LocalizablePreferences", key));
|
||||
value = value.replace(/%LOCALE%/g, this._locale);
|
||||
value = value.replace(/%LANGUAGE%/g, this._language);
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
}
|
||||
}
|
||||
|
||||
if (sections["LocalizablePreferences-" + this._language]) {
|
||||
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._language))) {
|
||||
try {
|
||||
let value = eval(this._ini.getString("LocalizablePreferences-" + this._language, key));
|
||||
localizedStr.data = "data:text/plain," + key + "=" + value;
|
||||
defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
|
||||
} catch (e) { /* ignore bad prefs and move on */ }
|
||||
|
|
|
@ -632,7 +632,7 @@ global.WindowManager = {
|
|||
convert(extension, window, getInfo) {
|
||||
let result = {
|
||||
id: this.getId(window),
|
||||
focused: window == WindowManager.topWindow,
|
||||
focused: window.document.hasFocus(),
|
||||
top: window.screenY,
|
||||
left: window.screenX,
|
||||
width: window.outerWidth,
|
||||
|
|
|
@ -58,9 +58,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
|
||||
"resource:///modules/CustomizationTabPreloader.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
|
||||
"resource://pdf.js/PdfJs.jsm");
|
||||
|
||||
|
@ -1072,7 +1069,6 @@ BrowserGlue.prototype = {
|
|||
|
||||
SelfSupportBackend.uninit();
|
||||
|
||||
CustomizationTabPreloader.uninit();
|
||||
WebappManager.uninit();
|
||||
|
||||
NewTabPrefsProvider.prefs.uninit();
|
||||
|
@ -1812,10 +1808,16 @@ BrowserGlue.prototype = {
|
|||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 36;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
|
||||
let currentUIVersion;
|
||||
if (Services.prefs.prefHasUserValue("browser.migration.version")) {
|
||||
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
||||
} catch(ex) {}
|
||||
} else {
|
||||
// This is a new profile, nothing to migrate.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentUIVersion >= UI_VERSION)
|
||||
return;
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@ Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
|||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
Components.utils.import("resource:///modules/ShellService.jsm");
|
||||
Components.utils.import("resource:///modules/TransientPrefs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
@ -101,7 +105,6 @@ var gMainPane = {
|
|||
#endif
|
||||
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
||||
|
@ -185,6 +188,16 @@ var gMainPane = {
|
|||
Cu.reportError("Failed to toggle separate profile mode: " + error);
|
||||
}
|
||||
}
|
||||
function createOrRemoveSpecialDevEditionFile(onSuccess) {
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
||||
if (separateProfileModeCheckbox.checked) {
|
||||
OS.File.remove(ignoreSeparateProfile).then(onSuccess, revertCheckbox);
|
||||
} else {
|
||||
OS.File.writeAtomic(ignoreSeparateProfile, new Uint8Array()).then(onSuccess, revertCheckbox);
|
||||
}
|
||||
}
|
||||
|
||||
const Cc = Components.classes, Ci = Components.interfaces;
|
||||
let separateProfileModeCheckbox = document.getElementById("separateProfileMode");
|
||||
|
@ -194,30 +207,43 @@ var gMainPane = {
|
|||
"featureEnableRequiresRestart" : "featureDisableRequiresRestart",
|
||||
[brandName]);
|
||||
let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
|
||||
let shouldProceed = Services.prompt.confirm(window, title, msg)
|
||||
if (shouldProceed) {
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
||||
"restart");
|
||||
shouldProceed = !cancelQuit.data;
|
||||
let check = {value: false};
|
||||
let prompts = Services.prompt;
|
||||
let flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_IS_STRING +
|
||||
prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_CANCEL +
|
||||
prompts.BUTTON_POS_2 * prompts.BUTTON_TITLE_IS_STRING;
|
||||
let button0Title = bundle.getString("restartNowButton");
|
||||
let button2Title = bundle.getString("restartLaterButton");
|
||||
let button_index = prompts.confirmEx(window, title, msg, flags,
|
||||
button0Title, null, button2Title, null, check)
|
||||
let RESTART_NOW_BUTTON_INDEX = 0;
|
||||
let CANCEL_BUTTON_INDEX = 1;
|
||||
let RESTART_LATER_BUTTON_INDEX = 2;
|
||||
|
||||
if (shouldProceed) {
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
||||
if (separateProfileModeCheckbox.checked) {
|
||||
OS.File.remove(ignoreSeparateProfile).then(quitApp, revertCheckbox);
|
||||
} else {
|
||||
OS.File.writeAtomic(ignoreSeparateProfile, new Uint8Array()).then(quitApp, revertCheckbox);
|
||||
}
|
||||
switch (button_index) {
|
||||
case CANCEL_BUTTON_INDEX:
|
||||
revertCheckbox();
|
||||
return;
|
||||
}
|
||||
}
|
||||
case RESTART_NOW_BUTTON_INDEX:
|
||||
let shouldProceed = false;
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
||||
"restart");
|
||||
shouldProceed = !cancelQuit.data;
|
||||
|
||||
// Revert the checkbox in case we didn't quit
|
||||
revertCheckbox();
|
||||
if (shouldProceed) {
|
||||
createOrRemoveSpecialDevEditionFile(quitApp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Revert the checkbox in case we didn't quit
|
||||
revertCheckbox();
|
||||
return;
|
||||
case RESTART_LATER_BUTTON_INDEX:
|
||||
createOrRemoveSpecialDevEditionFile();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
onGetStarted: function (aEvent) {
|
||||
|
|
|
@ -425,19 +425,6 @@ var gSyncPane = {
|
|||
}
|
||||
},
|
||||
|
||||
// Called whenever one of the sync engine preferences is changed.
|
||||
onPreferenceChanged: function() {
|
||||
let prefElts = document.querySelectorAll("#syncEnginePrefs > preference");
|
||||
let syncEnabled = false;
|
||||
for (let elt of prefElts) {
|
||||
if (elt.name.startsWith("services.sync.") && elt.value) {
|
||||
syncEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Services.prefs.setBoolPref("services.sync.enabled", syncEnabled);
|
||||
},
|
||||
|
||||
startOver: function (showDialog) {
|
||||
if (showDialog) {
|
||||
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
<!-- Sync panel -->
|
||||
|
||||
<preferences id="syncEnginePrefs" hidden="true" data-category="paneSync"
|
||||
onchange="gSyncPane.onPreferenceChanged();">
|
||||
<preferences id="syncEnginePrefs" hidden="true" data-category="paneSync">
|
||||
<preference id="engine.addons"
|
||||
name="services.sync.engine.addons"
|
||||
type="bool"/>
|
||||
|
|
|
@ -801,6 +801,8 @@ var SessionStoreInternal = {
|
|||
tab.label = activePageData.url;
|
||||
tab.crop = "center";
|
||||
}
|
||||
} else if (tab.hasAttribute("customizemode")) {
|
||||
win.gCustomizeMode.setTab(tab);
|
||||
}
|
||||
|
||||
// Restore the tab icon.
|
||||
|
@ -3299,6 +3301,10 @@ var SessionStoreInternal = {
|
|||
* optional load arguments used for loadURI()
|
||||
*/
|
||||
restoreTabContent: function (aTab, aLoadArguments = null) {
|
||||
if (aTab.hasAttribute("customizemode")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let browser = aTab.linkedBrowser;
|
||||
let window = aTab.ownerDocument.defaultView;
|
||||
let tabbrowser = window.gBrowser;
|
||||
|
|
|
@ -6,50 +6,138 @@
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["LanguageDetector"];
|
||||
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
// Since Emscripten can handle heap growth, but not heap shrinkage, we
|
||||
// need to refresh the worker after we've processed a particularly large
|
||||
// string in order to prevent unnecessary resident memory growth.
|
||||
//
|
||||
// These values define the cut-off string length and the idle timeout
|
||||
// (in milliseconds) before destroying a worker. Once a string of the
|
||||
// maximum size has been processed, the worker is marked for
|
||||
// destruction, and is terminated as soon as it has been idle for the
|
||||
// given timeout.
|
||||
//
|
||||
// 1.5MB. This is the approximate string length that forces heap growth
|
||||
// for a 2MB heap.
|
||||
var LARGE_STRING = 1.5 * 1024 * 1024;
|
||||
var IDLE_TIMEOUT = 10 * 1000;
|
||||
|
||||
const WORKER_URL = "resource:///modules/translation/cld-worker.js";
|
||||
|
||||
var detectionQueue = [];
|
||||
var workerManager = {
|
||||
detectionQueue: [],
|
||||
|
||||
var workerReady = false;
|
||||
var pendingStrings = [];
|
||||
detectLanguage(aParams) {
|
||||
return this.workerReady.then(worker => {
|
||||
return new Promise(resolve => {
|
||||
this.detectionQueue.push({resolve});
|
||||
worker.postMessage(aParams);
|
||||
});
|
||||
}).then(result => {
|
||||
// We have our asynchronous result from the worker.
|
||||
//
|
||||
// Determine if our input was large enough to trigger heap growth,
|
||||
// or if we're already waiting to destroy the worker when it's
|
||||
// idle. If so, schedule termination after the idle timeout.
|
||||
if (aParams.text.length >= LARGE_STRING || this._idleTimeout != null)
|
||||
this.flushWorker();
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "worker", () => {
|
||||
let worker = new Worker(WORKER_URL);
|
||||
worker.onmessage = function(aMsg) {
|
||||
if (aMsg.data == "ready") {
|
||||
workerReady = true;
|
||||
for (let string of pendingStrings)
|
||||
worker.postMessage(string);
|
||||
pendingStrings = [];
|
||||
return result;
|
||||
})
|
||||
},
|
||||
|
||||
_worker: null,
|
||||
_workerReadyPromise: null,
|
||||
|
||||
get workerReady() {
|
||||
if (!this._workerReadyPromise)
|
||||
this._workerReadyPromise = new Promise(resolve => {
|
||||
let worker = new Worker(WORKER_URL);
|
||||
worker.onmessage = (aMsg) => {
|
||||
if (aMsg.data == "ready")
|
||||
resolve(worker);
|
||||
else
|
||||
this.detectionQueue.shift().resolve(aMsg.data);
|
||||
};
|
||||
this._worker = worker;
|
||||
});
|
||||
|
||||
return this._workerReadyPromise;
|
||||
},
|
||||
|
||||
// Holds the ID of the current pending idle cleanup setTimeout.
|
||||
_idleTimeout: null,
|
||||
|
||||
// Schedule the current worker to be terminated after the idle timeout.
|
||||
flushWorker() {
|
||||
if (this._idleTimeout != null)
|
||||
clearTimeout(this._idleTimeout);
|
||||
|
||||
this._idleTimeout = setTimeout(this._flushWorker.bind(this), IDLE_TIMEOUT);
|
||||
},
|
||||
|
||||
// Immediately terminate the worker, as long as there no pending
|
||||
// results. Otherwise, reschedule termination until after the next
|
||||
// idle timeout.
|
||||
_flushWorker() {
|
||||
if (this.detectionQueue.length)
|
||||
this.flushWorker();
|
||||
else {
|
||||
if (this._worker)
|
||||
this._worker.terminate();
|
||||
|
||||
this._worker = null;
|
||||
this._workerReadyPromise = null;
|
||||
this._idleTimeout = null;
|
||||
}
|
||||
else
|
||||
detectionQueue.shift().resolve(aMsg.data);
|
||||
}
|
||||
return worker;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
this.LanguageDetector = {
|
||||
/**
|
||||
* Detect the language of a given string
|
||||
* Detect the language of a given string.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* The argument may be either a string containing the text to analyze,
|
||||
* or an object with the following properties:
|
||||
*
|
||||
* - 'text' The text to analyze.
|
||||
*
|
||||
* - 'isHTML' (optional) A boolean, indicating whether the text
|
||||
* should be analyzed as HTML rather than plain text.
|
||||
*
|
||||
* - 'language' (optional) A string indicating the expected language.
|
||||
* For text extracted from HTTP documents, this is expected to
|
||||
* come from the Content-Language header.
|
||||
*
|
||||
* - 'tld' (optional) A string indicating the top-level domain of the
|
||||
* document the text was extracted from.
|
||||
*
|
||||
* - 'encoding' (optional) A string describing the encoding of the
|
||||
* document the string was extracted from. Note that, regardless
|
||||
* of the value of this property, the 'text' property must be a
|
||||
* UTF-16 JavaScript string.
|
||||
*
|
||||
* @returns {Promise<Object>}
|
||||
* @resolves When detection is finished, with a object containing
|
||||
* these fields:
|
||||
* - 'language' (string with a language code)
|
||||
* - 'confident' (boolean).
|
||||
* - 'confident' (boolean) Whether the detector is confident of the
|
||||
* result.
|
||||
* - 'languages' (array) An array of up to three elements, containing
|
||||
* the most prevalent languages detected. It contains a
|
||||
* 'languageCode' property, containing the ISO language code of
|
||||
* the language, and a 'percent' property, describing the
|
||||
* approximate percentage of the input which is in that language.
|
||||
* For text of an unknown language, the result may contain an
|
||||
* entry with the languge code 'un', indicating the percent of
|
||||
* the text which is unknown.
|
||||
*/
|
||||
detectLanguage: function(aString) {
|
||||
let deferred = Promise.defer();
|
||||
detectionQueue.push(deferred);
|
||||
if (worker && workerReady)
|
||||
worker.postMessage(aString);
|
||||
else
|
||||
pendingStrings.push(aString);
|
||||
return deferred.promise;
|
||||
}
|
||||
detectLanguage: function(aParams) {
|
||||
if (typeof aParams == "string")
|
||||
aParams = { text: aParams };
|
||||
|
||||
return workerManager.detectLanguage(aParams);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,8 +1,29 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
CC=emcc
|
||||
PYTHON2 ?= python2
|
||||
|
||||
EMSCRIPTEN_ROOT := $(shell if which emcc >/dev/null 2>&1; \
|
||||
then dirname `which emcc`; \
|
||||
else echo /usr/lib/emscripten; \
|
||||
fi)
|
||||
|
||||
EMCC ?= $(EMSCRIPTEN_ROOT)/emcc
|
||||
|
||||
WEBIDL ?= $(PYTHON2) $(EMSCRIPTEN_ROOT)/tools/webidl_binder.py
|
||||
|
||||
# A 2MB heap is to analyze most web pages. For the outliers, we need to either
|
||||
# allow for heap growth, or allocate an unreasonable amount of memory at the
|
||||
# outset.
|
||||
# Unfortunately, once the heap has been enlarged, there is no shrinking, so
|
||||
# analyzing one 20MB web page gives us a 30-40MB heap for the life of the
|
||||
# worker.
|
||||
FLAGS=-s -O3 -s INLINING_LIMIT=1 -s NO_FILESYSTEM=1 -s NO_EXIT_RUNTIME=1 -s INVOKE_RUN=0 \
|
||||
-s TOTAL_STACK=8192 -s TOTAL_MEMORY=2097152 -s ALLOW_MEMORY_GROWTH=1 \
|
||||
--llvm-lto 1 --memory-init-file 1 --closure 1
|
||||
|
||||
export EMCC_CLOSURE_ARGS = --language_in ECMASCRIPT6 --language_out ES5_STRICT
|
||||
|
||||
SOURCES= \
|
||||
internal/cldutil.cc \
|
||||
|
@ -32,13 +53,22 @@ SOURCES= \
|
|||
cldapp.cc \
|
||||
$(NULL)
|
||||
|
||||
%.o: %.cc
|
||||
$(CC) -Os -I. -o $@ $<
|
||||
OBJECTS=$(SOURCES:.cc=.o)
|
||||
|
||||
FLAGS=-s USE_TYPED_ARRAYS=2 -O3 -s INLINING_LIMIT=1 --llvm-lto 1 --memory-init-file 1 --closure 1
|
||||
default: all
|
||||
|
||||
all: $(SOURCES:.cc=.o)
|
||||
$(CC) $(FLAGS) -I. -o cld-worker.js $^ --post-js post.js -s EXPORTED_FUNCTIONS="['_detectLangCode', '_lastResultReliable']"
|
||||
%.o: %.cc Makefile
|
||||
$(EMCC) -Os -I. -o $@ $<
|
||||
|
||||
cldapp.o: cld.cpp
|
||||
|
||||
%.cpp %.js: %.idl
|
||||
$(WEBIDL) $< $*
|
||||
|
||||
all: cld-worker.js
|
||||
|
||||
cld-worker.js: $(OBJECTS) post.js cld.js
|
||||
$(EMCC) $(FLAGS) -I. -o cld-worker.js $(OBJECTS) --post-js cld.js --post-js post.js
|
||||
|
||||
clean:
|
||||
rm -rf $(SOURCES:.cc=.o)
|
||||
rm -f $(OBJECTS) cld.cpp cld.js before.js
|
||||
|
|
Двоичные данные
browser/components/translation/cld2/cld-worker.js.mem
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Note: This is a variant of WebIDL, but its semantics differ from our
|
||||
// internal WebIDL implementation. Some particular differences that are
|
||||
// relevent here include:
|
||||
//
|
||||
// - Attribute declarations refer directly to member variables of the
|
||||
// underlying class, and are not forwarded to explicit getter methods.
|
||||
//
|
||||
// - Attribute declarations also do not create getters on the JavaScript
|
||||
// wrapper object, but instead generate "get_foo()" and "set_foo()"
|
||||
// methods, which must be called in order to access the value. In the case
|
||||
// of array attributes, the callers must also pass the index they wish to
|
||||
// access.
|
||||
//
|
||||
// - Method overloading is fairly crude. Only explicitly declared variants
|
||||
// are supported, and selection is based entirely on index of the first
|
||||
// parameter whose value is undefined.
|
||||
//
|
||||
// - DOMString attributes are nullable by default. Null values are not
|
||||
// converted to empty strings, and non-null values are converted to
|
||||
// null-terminated, UTF-8 byte arrays.
|
||||
|
||||
interface Language {
|
||||
[Const] DOMString getLanguageCode();
|
||||
};
|
||||
|
||||
interface LanguageGuess {
|
||||
byte getPercent();
|
||||
};
|
||||
|
||||
interface LanguageInfo {
|
||||
static LanguageInfo detectLanguage(DOMString buffer, boolean isPlainText);
|
||||
|
||||
static LanguageInfo detectLanguage(DOMString buffer, boolean isPlainText,
|
||||
DOMString? tldHint, long encodingHint,
|
||||
DOMString? languageHint);
|
||||
|
||||
boolean getIsReliable();
|
||||
|
||||
[BoundsChecked,Const] readonly attribute LanguageGuess[] languages;
|
||||
};
|
||||
|
||||
LanguageGuess implements Language;
|
||||
LanguageInfo implements Language;
|
|
@ -4,20 +4,104 @@
|
|||
|
||||
#include "public/compact_lang_det.h"
|
||||
|
||||
extern "C" {
|
||||
#define MAX_RESULTS 3
|
||||
|
||||
using namespace CLD2;
|
||||
class Language {
|
||||
public:
|
||||
Language(CLD2::Language lang) : mLang(lang) {}
|
||||
|
||||
bool g_is_reliable;
|
||||
const char* getLanguageCode() const
|
||||
{
|
||||
return CLD2::LanguageCode(mLang);
|
||||
}
|
||||
|
||||
const char* detectLangCode(const char* src) {
|
||||
return LanguageCode(DetectLanguage(src, strlen(src),
|
||||
true /* is_plain_text */,
|
||||
&g_is_reliable));
|
||||
}
|
||||
private:
|
||||
const CLD2::Language mLang;
|
||||
};
|
||||
|
||||
bool lastResultReliable(void) {
|
||||
return g_is_reliable;
|
||||
}
|
||||
class LanguageGuess : public Language {
|
||||
public:
|
||||
LanguageGuess(CLD2::Language lang, char percent) :
|
||||
Language(lang), mPercent(percent) {}
|
||||
|
||||
}
|
||||
char getPercent() const
|
||||
{
|
||||
return mPercent;
|
||||
}
|
||||
|
||||
private:
|
||||
const char mPercent;
|
||||
};
|
||||
|
||||
|
||||
class LanguageInfo : public Language {
|
||||
public:
|
||||
static LanguageInfo* detectLanguage(const char* buffer, bool isPlainText)
|
||||
{
|
||||
CLD2::Language languages[MAX_RESULTS] = {};
|
||||
int percentages[MAX_RESULTS] = {};
|
||||
bool isReliable = false;
|
||||
|
||||
// This is ignored.
|
||||
int textBytes;
|
||||
|
||||
CLD2::Language bestGuess = DetectLanguageSummary(
|
||||
buffer, strlen(buffer), isPlainText,
|
||||
languages, percentages, &textBytes,
|
||||
&isReliable);
|
||||
|
||||
return new LanguageInfo(isReliable, bestGuess, languages, percentages);
|
||||
}
|
||||
|
||||
static LanguageInfo* detectLanguage(const char* buffer, bool isPlainText,
|
||||
const char* tldHint, int encodingHint,
|
||||
const char* languageHint)
|
||||
{
|
||||
CLD2::CLDHints hints = {languageHint, tldHint, encodingHint, CLD2::UNKNOWN_LANGUAGE};
|
||||
|
||||
CLD2::Language languages[MAX_RESULTS] = {};
|
||||
int percentages[MAX_RESULTS] = {};
|
||||
bool isReliable = false;
|
||||
|
||||
// These are ignored.
|
||||
double scores[MAX_RESULTS];
|
||||
int textBytes;
|
||||
|
||||
CLD2::Language bestGuess = ExtDetectLanguageSummary(
|
||||
buffer, strlen(buffer), isPlainText,
|
||||
&hints, 0,
|
||||
languages, percentages, scores,
|
||||
nullptr, &textBytes, &isReliable);
|
||||
|
||||
return new LanguageInfo(isReliable, bestGuess, languages, percentages);
|
||||
}
|
||||
|
||||
~LanguageInfo()
|
||||
{
|
||||
for (int i = 0; i < MAX_RESULTS; i++) {
|
||||
delete languages[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool getIsReliable() const
|
||||
{
|
||||
return mIsReliable;
|
||||
}
|
||||
|
||||
const LanguageGuess* languages[MAX_RESULTS];
|
||||
|
||||
private:
|
||||
LanguageInfo(bool isReliable, CLD2::Language bestGuess,
|
||||
CLD2::Language languageIDs[MAX_RESULTS],
|
||||
int percentages[MAX_RESULTS]) :
|
||||
Language(bestGuess), mIsReliable(isReliable)
|
||||
{
|
||||
for (int i = 0; i < MAX_RESULTS; i++) {
|
||||
languages[i] = new LanguageGuess(languageIDs[i], percentages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const bool mIsReliable;
|
||||
};
|
||||
|
||||
#include "cld.cpp"
|
||||
|
|
|
@ -2,31 +2,170 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// The WebIDL binder places static methods on the prototype, rather than
|
||||
// on the constructor, which is a bit clumsy, and is definitely not
|
||||
// idiomatic.
|
||||
LanguageInfo.detectLanguage = LanguageInfo.prototype.detectLanguage;
|
||||
|
||||
// Closure is overzealous in its function call optimization, and tries
|
||||
// to turn these singleton methods into unbound function calls.
|
||||
ensureCache.alloc = ensureCache.alloc.bind(ensureCache);
|
||||
ensureCache.prepare = ensureCache.prepare.bind(ensureCache);
|
||||
|
||||
// From public/encodings.h. Unfortunately, the WebIDL binder doesn't
|
||||
// allow us to define or automatically derive these in the IDL.
|
||||
var Encodings = {
|
||||
'ISO_8859_1' : 0,
|
||||
'ISO_8859_2' : 1,
|
||||
'ISO_8859_3' : 2,
|
||||
'ISO_8859_4' : 3,
|
||||
'ISO_8859_5' : 4,
|
||||
'ISO_8859_6' : 5,
|
||||
'ISO_8859_7' : 6,
|
||||
'ISO_8859_8' : 7,
|
||||
'ISO_8859_9' : 8,
|
||||
'ISO_8859_10' : 9,
|
||||
'JAPANESE_EUC_JP' : 10,
|
||||
'EUC_JP' : 10,
|
||||
'JAPANESE_SHIFT_JIS' : 11,
|
||||
'SHIFT_JIS' : 11,
|
||||
'JAPANESE_JIS' : 12,
|
||||
'JIS' : 12,
|
||||
'CHINESE_BIG5' : 13,
|
||||
'BIG5' : 13,
|
||||
'CHINESE_GB' : 14,
|
||||
'CHINESE_EUC_CN' : 15,
|
||||
'EUC_CN' : 15,
|
||||
'KOREAN_EUC_KR' : 16,
|
||||
'EUC_KR' : 16,
|
||||
'UNICODE_UNUSED' : 17,
|
||||
'CHINESE_EUC_DEC' : 18,
|
||||
'EUC_DEC' : 18,
|
||||
'CHINESE_CNS' : 19,
|
||||
'CNS' : 19,
|
||||
'CHINESE_BIG5_CP950' : 20,
|
||||
'BIG5_CP950' : 20,
|
||||
'JAPANESE_CP932' : 21,
|
||||
'CP932' : 21,
|
||||
'UTF8' : 22,
|
||||
'UNKNOWN_ENCODING' : 23,
|
||||
'ASCII_7BIT' : 24,
|
||||
'RUSSIAN_KOI8_R' : 25,
|
||||
'KOI8_R' : 25,
|
||||
'RUSSIAN_CP1251' : 26,
|
||||
'CP1251' : 26,
|
||||
'MSFT_CP1252' : 27,
|
||||
'CP1252' : 27,
|
||||
'RUSSIAN_KOI8_RU' : 28,
|
||||
'KOI8_RU' : 28,
|
||||
'MSFT_CP1250' : 29,
|
||||
'CP1250' : 29,
|
||||
'ISO_8859_15' : 30,
|
||||
'MSFT_CP1254' : 31,
|
||||
'CP1254' : 31,
|
||||
'MSFT_CP1257' : 32,
|
||||
'CP1257' : 32,
|
||||
'ISO_8859_11' : 33,
|
||||
'MSFT_CP874' : 34,
|
||||
'CP874' : 34,
|
||||
'MSFT_CP1256' : 35,
|
||||
'CP1256' : 35,
|
||||
'MSFT_CP1255' : 36,
|
||||
'CP1255' : 36,
|
||||
'ISO_8859_8_I' : 37,
|
||||
'HEBREW_VISUAL' : 38,
|
||||
'CZECH_CP852' : 39,
|
||||
'CP852' : 39,
|
||||
'CZECH_CSN_369103' : 40,
|
||||
'CSN_369103' : 40,
|
||||
'MSFT_CP1253' : 41,
|
||||
'CP1253' : 41,
|
||||
'RUSSIAN_CP866' : 42,
|
||||
'CP866' : 42,
|
||||
'ISO_8859_13' : 43,
|
||||
'ISO_2022_KR' : 44,
|
||||
'GBK' : 45,
|
||||
'GB18030' : 46,
|
||||
'BIG5_HKSCS' : 47,
|
||||
'ISO_2022_CN' : 48,
|
||||
'TSCII' : 49,
|
||||
'TAMIL_MONO' : 50,
|
||||
'TAMIL_BI' : 51,
|
||||
'JAGRAN' : 52,
|
||||
'MACINTOSH_ROMAN' : 53,
|
||||
'UTF7' : 54,
|
||||
'BHASKAR' : 55,
|
||||
'HTCHANAKYA' : 56,
|
||||
'UTF16BE' : 57,
|
||||
'UTF16LE' : 58,
|
||||
'UTF32BE' : 59,
|
||||
'UTF32LE' : 60,
|
||||
'BINARYENC' : 61,
|
||||
'HZ_GB_2312' : 62,
|
||||
'UTF8UTF8' : 63,
|
||||
'TAM_ELANGO' : 64,
|
||||
'TAM_LTTMBARANI' : 65,
|
||||
'TAM_SHREE' : 66,
|
||||
'TAM_TBOOMIS' : 67,
|
||||
'TAM_TMNEWS' : 68,
|
||||
'TAM_WEBTAMIL' : 69,
|
||||
'KDDI_SHIFT_JIS' : 70,
|
||||
'DOCOMO_SHIFT_JIS' : 71,
|
||||
'SOFTBANK_SHIFT_JIS' : 72,
|
||||
'KDDI_ISO_2022_JP' : 73,
|
||||
'ISO_2022_JP' : 73,
|
||||
'SOFTBANK_ISO_2022_JP' : 74,
|
||||
};
|
||||
|
||||
// Accept forms both with and without underscores/hypens.
|
||||
for (let code of Object.keys(Encodings)) {
|
||||
if (code['includes']("_"))
|
||||
Encodings[code.replace(/_/g, "")] = Encodings[code];
|
||||
}
|
||||
|
||||
addOnPreMain(function() {
|
||||
|
||||
onmessage = function(aMsg){
|
||||
let data = aMsg['data'];
|
||||
|
||||
// Convert the string to an array of UTF8 bytes.
|
||||
var encoder = new TextEncoder();
|
||||
encoder['encoding'] = "utf-8";
|
||||
var utf8Array = encoder['encode'](aMsg.data);
|
||||
let langInfo;
|
||||
if (data['tld'] == undefined && data['encoding'] == undefined && data['language'] == undefined) {
|
||||
langInfo = LanguageInfo.detectLanguage(data['text'], !data['isHTML']);
|
||||
} else {
|
||||
// Do our best to find the given encoding in the encodings table.
|
||||
// Otherwise, just fall back to unknown.
|
||||
let enc = String(data['encoding']).toUpperCase().replace(/[_-]/g, "");
|
||||
|
||||
// Copy the UTF8 byte array to the heap.
|
||||
var strLength = utf8Array.length;
|
||||
var ptr = Module['_malloc'](strLength + 1);
|
||||
var heap = Module['HEAPU8'];
|
||||
new Uint8Array(heap.buffer, ptr, strLength).set(utf8Array);
|
||||
// Add a \0 at the end of the C string.
|
||||
heap[ptr + strLength] = 0;
|
||||
let encoding;
|
||||
if (Encodings.hasOwnProperty(enc))
|
||||
encoding = Encodings[enc];
|
||||
else
|
||||
encoding = Encodings['UNKNOWN_ENCODING'];
|
||||
|
||||
var lang = Pointer_stringify(_detectLangCode(ptr));
|
||||
var confident = !!Module['ccall']("lastResultReliable", "number");
|
||||
postMessage({'language': lang,
|
||||
'confident': confident});
|
||||
langInfo = LanguageInfo.detectLanguage(data['text'], !data['isHTML'],
|
||||
data['tld'] || null,
|
||||
encoding,
|
||||
data['language'] || null);
|
||||
}
|
||||
|
||||
Module['_free'](ptr);
|
||||
postMessage({
|
||||
'language': langInfo.getLanguageCode(),
|
||||
'confident': langInfo.getIsReliable(),
|
||||
|
||||
'languages': new Array(3).fill(0).map((_, index) => {
|
||||
let lang = langInfo.get_languages(index);
|
||||
return {
|
||||
'languageCode': lang.getLanguageCode(),
|
||||
'percent': lang.getPercent(),
|
||||
};
|
||||
}).filter(lang => {
|
||||
// Ignore empty results.
|
||||
return lang['languageCode'] != "un" || lang['percent'] > 0;
|
||||
}),
|
||||
});
|
||||
|
||||
Module.destroy(langInfo);
|
||||
};
|
||||
|
||||
postMessage("ready");
|
||||
|
||||
});
|
||||
|
|
|
@ -308,7 +308,7 @@ const kTestPairs = [
|
|||
["bg", "BULGARIAN", kTeststr_bg_Cyrl],
|
||||
["ca", "CATALAN", kTeststr_ca_Latn],
|
||||
["ceb", "CEBUANO", kTeststr_ceb_Latn],
|
||||
["hr", "CROATIAN", kTeststr_hr_Latn],
|
||||
["hr", "CROATIAN", kTeststr_hr_Latn, [false, 0, "el", 4]],
|
||||
["cs", "CZECH", kTeststr_cs_Latn],
|
||||
["da", "DANISH", kTeststr_da_Latn],
|
||||
["nl", "DUTCH", kTeststr_nl_Latn],
|
||||
|
@ -335,7 +335,7 @@ const kTestPairs = [
|
|||
["mk", "MACEDONIAN", kTeststr_mk_Cyrl],
|
||||
["ms", "MALAY", kTeststr_ms_Latn],
|
||||
["mt", "MALTESE", kTeststr_mt_Latn],
|
||||
["mr", "MARATHI", kTeststr_mr_Deva],
|
||||
["mr", "MARATHI", kTeststr_mr_Deva, [false, 0, "te", 3]],
|
||||
["ne", "NEPALI", kTeststr_ne_Deva],
|
||||
["no", "NORWEGIAN", kTeststr_no_Latn],
|
||||
["fa", "PERSIAN", kTeststr_fa_Arab],
|
||||
|
@ -370,11 +370,11 @@ const kTestPairs = [
|
|||
["bs", "BOSNIAN", kTeststr_bs_Latn],
|
||||
|
||||
// 2 statistically-close languages
|
||||
["id", "INDONESIAN", kTeststr_id_close, true],
|
||||
["id", "INDONESIAN", kTeststr_id_close, [true, 80], []],
|
||||
["ms", "MALAY", kTeststr_ms_close],
|
||||
|
||||
// Simple intermixed French/English text
|
||||
["fr", "FRENCH", kTeststr_fr_en_Latn],
|
||||
["fr", "FRENCH", kTeststr_fr_en_Latn, [false, 80, "en", 32]],
|
||||
|
||||
// Cross-check the main quadgram table build date
|
||||
// Change the expected language each time it is rebuilt
|
||||
|
@ -382,14 +382,92 @@ const kTestPairs = [
|
|||
["az", "AZERBAIJANI", kTeststr_version] // 2014.01.31
|
||||
];
|
||||
|
||||
Components.utils.import("resource:///modules/translation/LanguageDetector.jsm");
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm");
|
||||
let detectorModule = Components.utils.import("resource:///modules/translation/LanguageDetector.jsm");
|
||||
|
||||
add_task(function test_pairs() {
|
||||
for (let pair of kTestPairs) {
|
||||
let result = yield LanguageDetector.detectLanguage(pair[2]);
|
||||
do_check_eq(result.language, pair[0]);
|
||||
do_check_eq(result.confident, !pair[3]);
|
||||
function check_result(result, langCode, expected) {
|
||||
equal(result.language, langCode, "Expected language code");
|
||||
|
||||
// Round percentage up to the nearest 5%, since most strings are
|
||||
// detected at slightly less than 100%, and we don't want to
|
||||
// encode each exact value.
|
||||
let percent = result.languages[0].percent;
|
||||
percent = Math.ceil(percent / 20) * 20;
|
||||
|
||||
equal(result.languages[0].languageCode, langCode, "Expected first guess language code");
|
||||
equal(percent, expected[1] || 100, "Expected first guess language percent");
|
||||
|
||||
if (expected.length < 3) {
|
||||
// We're not expecting a second language.
|
||||
equal(result.languages.length, 1, "Expected only one language result");
|
||||
} else {
|
||||
equal(result.languages.length, 2, "Expected two language results");
|
||||
|
||||
equal(result.languages[1].languageCode, expected[2], "Expected second guess language code");
|
||||
equal(result.languages[1].percent, expected[3], "Expected second guess language percent");
|
||||
}
|
||||
|
||||
equal(result.confident, !expected[0], "Expected confidence");
|
||||
}
|
||||
|
||||
add_task(function* test_pairs() {
|
||||
for (let item of kTestPairs) {
|
||||
let params = [item[2],
|
||||
{ text: item[2], tld: "com", language: item[0], encoding: "utf-8" }]
|
||||
|
||||
for (let [i, param] of params.entries()) {
|
||||
// For test items with different expected results when using the
|
||||
// language hint, use those for the hinted version of the API.
|
||||
// Otherwise, fall back to the first set of expected values.
|
||||
let expected = item[3 + i] || item[3] || [];
|
||||
|
||||
let result = yield LanguageDetector.detectLanguage(param);
|
||||
check_result(result, item[0], expected);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var run_test = run_next_test;
|
||||
// Test that the worker is flushed shortly after processing a large
|
||||
// string.
|
||||
add_task(function* test_worker_flush() {
|
||||
let test_string = kTeststr_fr_en_Latn;
|
||||
let test_item = kTestPairs.find(item => item[2] == test_string);
|
||||
|
||||
// Set shorter timeouts and lower string lengths to make things easier
|
||||
// on the test infrastructure.
|
||||
detectorModule.LARGE_STRING = test_string.length - 1;
|
||||
detectorModule.IDLE_TIMEOUT = 1000;
|
||||
|
||||
equal(detectorModule.workerManager._idleTimeout, null,
|
||||
"Should have no idle timeout to start with");
|
||||
|
||||
let result = yield LanguageDetector.detectLanguage(test_string);
|
||||
|
||||
// Make sure the results are still correct.
|
||||
check_result(result, test_item[0], test_item[3]);
|
||||
|
||||
// We should have an idle timeout after processing the string.
|
||||
ok(detectorModule.workerManager._idleTimeout != null,
|
||||
"Should have an idle timeout");
|
||||
ok(detectorModule.workerManager._worker != null,
|
||||
"Should have a worker instance");
|
||||
ok(detectorModule.workerManager._workerReadyPromise != null,
|
||||
"Should have a worker promise");
|
||||
|
||||
// Wait for the idle timeout to elapse.
|
||||
yield new Promise(resolve => setTimeout(resolve, detectorModule.IDLE_TIMEOUT));
|
||||
|
||||
equal(detectorModule.workerManager._idleTimeout, null,
|
||||
"Should have no idle timeout after it has elapsed");
|
||||
equal(detectorModule.workerManager._worker, null,
|
||||
"Should have no worker instance after idle timeout");
|
||||
equal(detectorModule.workerManager._workerReadyPromise, null,
|
||||
"Should have no worker promise after idle timeout");
|
||||
|
||||
// We should still be able to use the language detector after its
|
||||
// worker has been flushed.
|
||||
result = yield LanguageDetector.detectLanguage(test_string);
|
||||
|
||||
// Make sure the results are still correct.
|
||||
check_result(result, test_item[0], test_item[3]);
|
||||
});
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.4.64
|
||||
Current extension version is: 1.4.83
|
||||
|
|
|
@ -72,9 +72,15 @@ function getFindBar(domWindow) {
|
|||
tab = tabbrowser.getTabForBrowser(browser);
|
||||
return tabbrowser.getFindBar(tab);
|
||||
} catch (e) {
|
||||
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
|
||||
var chromeWindow = browser.ownerDocument.defaultView;
|
||||
return chromeWindow.gFindBar;
|
||||
try {
|
||||
// FF22 has no _getTabForBrowser, and FF24 has no getFindBar
|
||||
var chromeWindow = browser.ownerDocument.defaultView;
|
||||
return chromeWindow.gFindBar;
|
||||
} catch (ex) {
|
||||
// Suppress errors for PDF files opened in the bookmark sidebar, see
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1248959.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +358,7 @@ ChromeActions.prototype = {
|
|||
|
||||
// ... or when the new find events code exists.
|
||||
var findBar = getFindBar(this.domWindow);
|
||||
return findBar && ('updateControlState' in findBar);
|
||||
return !!findBar && ('updateControlState' in findBar);
|
||||
},
|
||||
supportsDocumentFonts: function() {
|
||||
var prefBrowser = getIntPref('browser.display.use_document_fonts', 1);
|
||||
|
|
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
|
|||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.4.64';
|
||||
var pdfjsBuild = '2f145d8';
|
||||
var pdfjsVersion = '1.4.83';
|
||||
var pdfjsBuild = '0629fd0';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
|
@ -1629,7 +1629,7 @@ AnnotationElementFactory.prototype =
|
|||
return new StrikeOutAnnotationElement(parameters);
|
||||
|
||||
default:
|
||||
throw new Error('Unimplemented annotation type "' + subtype + '"');
|
||||
return new AnnotationElement(parameters);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1639,14 +1639,17 @@ AnnotationElementFactory.prototype =
|
|||
* @alias AnnotationElement
|
||||
*/
|
||||
var AnnotationElement = (function AnnotationElementClosure() {
|
||||
function AnnotationElement(parameters) {
|
||||
function AnnotationElement(parameters, isRenderable) {
|
||||
this.isRenderable = isRenderable || false;
|
||||
this.data = parameters.data;
|
||||
this.layer = parameters.layer;
|
||||
this.page = parameters.page;
|
||||
this.viewport = parameters.viewport;
|
||||
this.linkService = parameters.linkService;
|
||||
|
||||
this.container = this._createContainer();
|
||||
if (isRenderable) {
|
||||
this.container = this._createContainer();
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationElement.prototype = /** @lends AnnotationElement.prototype */ {
|
||||
|
@ -1761,7 +1764,7 @@ var AnnotationElement = (function AnnotationElementClosure() {
|
|||
*/
|
||||
var LinkAnnotationElement = (function LinkAnnotationElementClosure() {
|
||||
function LinkAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
AnnotationElement.call(this, parameters, true);
|
||||
}
|
||||
|
||||
Util.inherit(LinkAnnotationElement, AnnotationElement, {
|
||||
|
@ -1843,7 +1846,9 @@ var LinkAnnotationElement = (function LinkAnnotationElementClosure() {
|
|||
*/
|
||||
var TextAnnotationElement = (function TextAnnotationElementClosure() {
|
||||
function TextAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
var isRenderable = !!(parameters.data.hasPopup ||
|
||||
parameters.data.title || parameters.data.contents);
|
||||
AnnotationElement.call(this, parameters, isRenderable);
|
||||
}
|
||||
|
||||
Util.inherit(TextAnnotationElement, AnnotationElement, {
|
||||
|
@ -1897,7 +1902,9 @@ var TextAnnotationElement = (function TextAnnotationElementClosure() {
|
|||
*/
|
||||
var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() {
|
||||
function WidgetAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
var isRenderable = !parameters.data.hasAppearance &&
|
||||
!!parameters.data.fieldValue;
|
||||
AnnotationElement.call(this, parameters, isRenderable);
|
||||
}
|
||||
|
||||
Util.inherit(WidgetAnnotationElement, AnnotationElement, {
|
||||
|
@ -1964,7 +1971,8 @@ var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() {
|
|||
*/
|
||||
var PopupAnnotationElement = (function PopupAnnotationElementClosure() {
|
||||
function PopupAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
var isRenderable = !!(parameters.data.title || parameters.data.contents);
|
||||
AnnotationElement.call(this, parameters, isRenderable);
|
||||
}
|
||||
|
||||
Util.inherit(PopupAnnotationElement, AnnotationElement, {
|
||||
|
@ -2154,7 +2162,7 @@ var PopupElement = (function PopupElementClosure() {
|
|||
var HighlightAnnotationElement = (
|
||||
function HighlightAnnotationElementClosure() {
|
||||
function HighlightAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
AnnotationElement.call(this, parameters, true);
|
||||
}
|
||||
|
||||
Util.inherit(HighlightAnnotationElement, AnnotationElement, {
|
||||
|
@ -2181,7 +2189,7 @@ var HighlightAnnotationElement = (
|
|||
var UnderlineAnnotationElement = (
|
||||
function UnderlineAnnotationElementClosure() {
|
||||
function UnderlineAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
AnnotationElement.call(this, parameters, true);
|
||||
}
|
||||
|
||||
Util.inherit(UnderlineAnnotationElement, AnnotationElement, {
|
||||
|
@ -2207,7 +2215,7 @@ var UnderlineAnnotationElement = (
|
|||
*/
|
||||
var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
|
||||
function SquigglyAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
AnnotationElement.call(this, parameters, true);
|
||||
}
|
||||
|
||||
Util.inherit(SquigglyAnnotationElement, AnnotationElement, {
|
||||
|
@ -2234,7 +2242,7 @@ var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
|
|||
var StrikeOutAnnotationElement = (
|
||||
function StrikeOutAnnotationElementClosure() {
|
||||
function StrikeOutAnnotationElement(parameters) {
|
||||
AnnotationElement.call(this, parameters);
|
||||
AnnotationElement.call(this, parameters, true);
|
||||
}
|
||||
|
||||
Util.inherit(StrikeOutAnnotationElement, AnnotationElement, {
|
||||
|
@ -2281,7 +2289,7 @@ var AnnotationLayer = (function AnnotationLayerClosure() {
|
|||
|
||||
for (var i = 0, ii = parameters.annotations.length; i < ii; i++) {
|
||||
var data = parameters.annotations[i];
|
||||
if (!data || !data.hasHtml) {
|
||||
if (!data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2293,7 +2301,9 @@ var AnnotationLayer = (function AnnotationLayerClosure() {
|
|||
linkService: parameters.linkService
|
||||
};
|
||||
var element = annotationElementFactory.create(properties);
|
||||
parameters.div.appendChild(element.render());
|
||||
if (element.isRenderable) {
|
||||
parameters.div.appendChild(element.render());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -6565,7 +6575,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
|||
* title: string,
|
||||
* bold: boolean,
|
||||
* italic: boolean,
|
||||
* color: rgb array,
|
||||
* color: rgb Uint8Array,
|
||||
* dest: dest obj,
|
||||
* url: string,
|
||||
* items: array of more items like this
|
||||
|
|
|
@ -27,12 +27,12 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||
<title>PDF.js viewer</title>
|
||||
|
||||
<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
|
||||
<base href="resource://pdf.js/web/" />
|
||||
<base href="resource://pdf.js/web/">
|
||||
<script src="l10n.js"></script>
|
||||
<script src="../build/pdf.js"></script>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="viewer.css"/>
|
||||
<link rel="stylesheet" href="viewer.css">
|
||||
|
||||
|
||||
|
||||
|
@ -279,7 +279,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||
</div>
|
||||
<div class="row">
|
||||
<!-- The type="password" attribute is set via script, to prevent warnings in Firefox for all http:// documents. -->
|
||||
<input id="password" class="toolbarField" />
|
||||
<input id="password" class="toolbarField">
|
||||
</div>
|
||||
<div class="buttonRow">
|
||||
<button id="passwordCancel" class="overlayButton"><span data-l10n-id="password_cancel">Cancel</span></button>
|
||||
|
|
|
@ -34,6 +34,7 @@ RequestExecutionLevel user
|
|||
Var TmpVal
|
||||
Var InstallType
|
||||
Var AddStartMenuSC
|
||||
Var AddTaskbarSC
|
||||
Var AddQuickLaunchSC
|
||||
Var AddDesktopSC
|
||||
Var InstallMaintenanceService
|
||||
|
@ -395,10 +396,12 @@ Section "-Application" APP_IDX
|
|||
|
||||
; If we are writing to HKLM and create either the desktop or start menu
|
||||
; shortcuts set IconsVisible to 1 otherwise to 0.
|
||||
; Taskbar shortcuts imply having a start menu shortcut.
|
||||
${StrFilter} "${FileMainEXE}" "+" "" "" $R9
|
||||
StrCpy $0 "Software\Clients\StartMenuInternet\$R9\InstallInfo"
|
||||
${If} $AddDesktopSC == 1
|
||||
${OrIf} $AddStartMenuSC == 1
|
||||
${OrIf} $AddTaskbarSC == 1
|
||||
WriteRegDWORD HKLM "$0" "IconsVisible" 1
|
||||
${Else}
|
||||
WriteRegDWORD HKLM "$0" "IconsVisible" 0
|
||||
|
@ -412,10 +415,12 @@ Section "-Application" APP_IDX
|
|||
|
||||
; If we create either the desktop or start menu shortcuts, then
|
||||
; set IconsVisible to 1 otherwise to 0.
|
||||
; Taskbar shortcuts imply having a start menu shortcut.
|
||||
${StrFilter} "${FileMainEXE}" "+" "" "" $R9
|
||||
StrCpy $0 "Software\Clients\StartMenuInternet\$R9\InstallInfo"
|
||||
${If} $AddDesktopSC == 1
|
||||
${OrIf} $AddStartMenuSC == 1
|
||||
${OrIf} $AddTaskbarSC == 1
|
||||
WriteRegDWORD HKCU "$0" "IconsVisible" 1
|
||||
${Else}
|
||||
WriteRegDWORD HKCU "$0" "IconsVisible" 0
|
||||
|
@ -601,12 +606,10 @@ Section "-InstallEndCleanup"
|
|||
UAC::ExecCodeSegment $0
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
|
||||
${MigrateTaskBarShortcut}
|
||||
${EndUnless}
|
||||
|
||||
${GetShortcutsLogPath} $0
|
||||
WriteIniStr "$0" "TASKBAR" "Migrated" "true"
|
||||
; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
|
||||
${MigrateTaskBarShortcut}
|
||||
|
||||
; Add the Firewall entries during install
|
||||
Call AddFirewallEntries
|
||||
|
|
|
@ -1130,17 +1130,24 @@ ${EndIf}
|
|||
ClearErrors
|
||||
WriteIniStr "$0" "TASKBAR" "Migrated" "true"
|
||||
${If} ${AtLeastWin7}
|
||||
; No need to check the default on Win8 and later
|
||||
${If} ${AtMostWin2008R2}
|
||||
; Check if the Firefox is the http handler for this user
|
||||
SetShellVarContext current ; Set SHCTX to the current user
|
||||
${IsHandlerForInstallDir} "http" $R9
|
||||
${If} $TmpVal == "HKLM"
|
||||
SetShellVarContext all ; Set SHCTX to all users
|
||||
; If we didn't run the stub installer, AddTaskbarSC will be empty.
|
||||
; We determine whether to pin based on whether we're the default
|
||||
; browser, or if we're on win8 or later, we always pin.
|
||||
${If} $AddTaskbarSC == ""
|
||||
; No need to check the default on Win8 and later
|
||||
${If} ${AtMostWin2008R2}
|
||||
; Check if the Firefox is the http handler for this user
|
||||
SetShellVarContext current ; Set SHCTX to the current user
|
||||
${IsHandlerForInstallDir} "http" $R9
|
||||
${If} $TmpVal == "HKLM"
|
||||
SetShellVarContext all ; Set SHCTX to all users
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${If} "$R9" == "true"
|
||||
${OrIf} ${AtLeastWin8}
|
||||
${If} "$R9" == "true"
|
||||
${OrIf} ${AtLeastWin8}
|
||||
${PinToTaskBar}
|
||||
${EndIf}
|
||||
${ElseIf} $AddTaskbarSC == "1"
|
||||
${PinToTaskBar}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
@ -1486,7 +1493,7 @@ Function SetAsDefaultAppUserHKCU
|
|||
${EndUnless}
|
||||
${EndIf}
|
||||
${RemoveDeprecatedKeys}
|
||||
${PinToTaskBar}
|
||||
${MigrateTaskBarShortcut}
|
||||
FunctionEnd
|
||||
|
||||
; Helper for updating the shortcut application model IDs.
|
||||
|
|
|
@ -1564,6 +1564,14 @@ Function OnDownload
|
|||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
|
||||
; Don't create the QuickLaunch or Taskbar shortcut from the launched installer
|
||||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false"
|
||||
|
||||
; Either avoid or force adding a taskbar pin based on the checkbox value:
|
||||
${If} $CheckboxShortcutOnBar == 0
|
||||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
|
||||
${Else}
|
||||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
|
||||
${EndIf}
|
||||
|
||||
${If} $CheckboxShortcutOnDesktop == 1
|
||||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
|
||||
${Else}
|
||||
|
@ -1586,14 +1594,10 @@ Function OnDownload
|
|||
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
|
||||
!endif
|
||||
|
||||
; Write migrated to the shortcuts.ini file to prevent the installer
|
||||
; from creating a taskbar shortcut (Bug 791613).
|
||||
; Delete the taskbar shortcut history to ensure we do the right thing based on
|
||||
; the config file above.
|
||||
${GetShortcutsLogPath} $0
|
||||
Delete "$0"
|
||||
; Workaround to prevent pinning to the taskbar.
|
||||
${If} $CheckboxShortcutOnBar == 0
|
||||
WriteIniStr "$0" "TASKBAR" "Migrated" "true"
|
||||
${EndIf}
|
||||
|
||||
GetFunctionAddress $0 RemoveFileProgressCallback
|
||||
${RemovePrecompleteEntries} $0
|
||||
|
|
|
@ -844,7 +844,6 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
|||
<!ENTITY social.directory.introText "Click on a service to add it to &brandShortName;.">
|
||||
<!ENTITY social.directory.viewmore.text "View More">
|
||||
|
||||
<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
|
||||
<!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
|
||||
<!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
|
||||
<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
|
||||
|
|
|
@ -689,6 +689,9 @@ customizeTips.tip0 = %1$S: You can customize %2$S to work the way you do. Simply
|
|||
customizeTips.tip0.hint = Hint
|
||||
customizeTips.tip0.learnMore = Learn more
|
||||
|
||||
# LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
|
||||
customizeMode.tabTitle = Customize %S
|
||||
|
||||
# LOCALIZATION NOTE(appmenu.*.description, appmenu.*.label): these are used for
|
||||
# the appmenu labels and buttons that appear when an update is staged for
|
||||
# installation or a background update has failed and a manual download is required.
|
||||
|
|
|
@ -171,6 +171,9 @@ featureEnableRequiresRestart=%S must restart to enable this feature.
|
|||
featureDisableRequiresRestart=%S must restart to disable this feature.
|
||||
shouldRestartTitle=Restart %S
|
||||
|
||||
restartNow=Restart Now
|
||||
restartLater=Restart Later
|
||||
|
||||
#### e10S
|
||||
# LOCALIZATION NOTE (e10sFeedbackAfterRestart): This message appears when the user
|
||||
# unchecks "Enable multi-process" on the "General" preferences tab.
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["CustomizationTabPreloader"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "HiddenFrame",
|
||||
"resource:///modules/HiddenFrame.jsm");
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const XUL_PAGE = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='win'/>";
|
||||
const CUSTOMIZATION_URL = "about:customizing";
|
||||
|
||||
// The interval between swapping in a preload docShell and kicking off the
|
||||
// next preload in the background.
|
||||
const PRELOADER_INTERVAL_MS = 600;
|
||||
|
||||
function createTimer(obj, delay) {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.init(obj, delay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
return timer;
|
||||
}
|
||||
|
||||
function clearTimer(timer) {
|
||||
if (timer) {
|
||||
timer.cancel();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
this.CustomizationTabPreloader = {
|
||||
uninit: function () {
|
||||
CustomizationTabPreloaderInternal.uninit();
|
||||
},
|
||||
|
||||
newTab: function (aTab) {
|
||||
return CustomizationTabPreloaderInternal.newTab(aTab);
|
||||
},
|
||||
|
||||
/**
|
||||
* ensurePreloading starts the preloading of the about:customizing
|
||||
* content page. This function is idempotent (until a call to uninit),
|
||||
* so multiple calls to it are fine.
|
||||
*/
|
||||
ensurePreloading: function() {
|
||||
CustomizationTabPreloaderInternal.ensurePreloading();
|
||||
},
|
||||
};
|
||||
|
||||
Object.freeze(CustomizationTabPreloader);
|
||||
|
||||
this.CustomizationTabPreloaderInternal = {
|
||||
_browser: null,
|
||||
|
||||
uninit: function () {
|
||||
if (this._browser) {
|
||||
this._browser.destroy();
|
||||
this._browser = null;
|
||||
}
|
||||
},
|
||||
|
||||
newTab: function (aTab) {
|
||||
let win = aTab.ownerDocument.defaultView;
|
||||
if (win.gBrowser && this._browser) {
|
||||
return this._browser.swapWithNewTab(aTab);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
ensurePreloading: function () {
|
||||
if (!this._browser) {
|
||||
this._browser = new HiddenBrowser();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function HiddenBrowser() {
|
||||
this._createBrowser();
|
||||
}
|
||||
|
||||
HiddenBrowser.prototype = {
|
||||
_timer: null,
|
||||
_hiddenFrame: null,
|
||||
|
||||
get isPreloaded() {
|
||||
return this._browser &&
|
||||
this._browser.contentDocument &&
|
||||
this._browser.contentDocument.readyState === "complete" &&
|
||||
this._browser.currentURI.spec === CUSTOMIZATION_URL;
|
||||
},
|
||||
|
||||
swapWithNewTab: function (aTab) {
|
||||
if (!this.isPreloaded || this._timer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let win = aTab.ownerDocument.defaultView;
|
||||
let tabbrowser = win.gBrowser;
|
||||
|
||||
if (!tabbrowser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Swap docShells.
|
||||
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
|
||||
|
||||
// Load all default frame scripts attached to the target window.
|
||||
let mm = aTab.linkedBrowser.messageManager;
|
||||
let scripts = win.getGroupMessageManager("browsers").getDelayedFrameScripts();
|
||||
Array.forEach(scripts, ([script, runGlobal]) => mm.loadFrameScript(script, true, runGlobal));
|
||||
|
||||
// Remove the browser, it will be recreated by a timer.
|
||||
this._removeBrowser();
|
||||
|
||||
// Start a timer that will kick off preloading the next page.
|
||||
this._timer = createTimer(this, PRELOADER_INTERVAL_MS);
|
||||
|
||||
// Signal that we swapped docShells.
|
||||
return true;
|
||||
},
|
||||
|
||||
observe: function () {
|
||||
this._timer = null;
|
||||
|
||||
// Start pre-loading the customization page.
|
||||
this._createBrowser();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this._removeBrowser();
|
||||
if (this._hiddenFrame) {
|
||||
this._hiddenFrame.destroy();
|
||||
this._hiddenFrame = null;
|
||||
}
|
||||
this._timer = clearTimer(this._timer);
|
||||
},
|
||||
|
||||
_createBrowser: function () {
|
||||
if (!this._hiddenFrame) {
|
||||
this._hiddenFrame = new HiddenFrame();
|
||||
}
|
||||
|
||||
this._hiddenFrame.get().then(aFrame => {
|
||||
let doc = aFrame.document;
|
||||
this._browser = doc.createElementNS(XUL_NS, "browser");
|
||||
this._browser.permanentKey = {};
|
||||
this._browser.setAttribute("type", "content");
|
||||
this._browser.setAttribute("src", CUSTOMIZATION_URL);
|
||||
this._browser.style.width = "400px";
|
||||
this._browser.style.height = "400px";
|
||||
doc.getElementById("win").appendChild(this._browser);
|
||||
});
|
||||
},
|
||||
|
||||
_removeBrowser: function () {
|
||||
if (this._browser) {
|
||||
this._browser.remove();
|
||||
this._browser = null;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -22,7 +22,6 @@ EXTRA_JS_MODULES += [
|
|||
'ContentObservers.jsm',
|
||||
'ContentSearch.jsm',
|
||||
'ContentWebRTC.jsm',
|
||||
'CustomizationTabPreloader.jsm',
|
||||
'DirectoryLinksProvider.jsm',
|
||||
'E10SUtils.jsm',
|
||||
'Feeds.jsm',
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
--chrome-secondary-background-color: #f5f6f7;
|
||||
--chrome-navigator-toolbox-separator-color: #cccccc;
|
||||
--chrome-nav-bar-separator-color: #B6B6B8;
|
||||
--chrome-nav-buttons-background: #fcfcfc;
|
||||
--chrome-nav-buttons-background: #ffffff; /* --theme-body-background */
|
||||
--chrome-nav-buttons-hover-background: #DADBDB;
|
||||
--chrome-nav-bar-controls-border-color: #ccc;
|
||||
--chrome-selection-color: #f5f7fa;
|
||||
|
@ -85,9 +85,7 @@
|
|||
--tab-hover-background-color: #D7D8DA;
|
||||
--tab-selection-color: #f5f7fa;
|
||||
--tab-selection-background-color: #4c9ed9;
|
||||
--tab-selection-box-shadow: 0 2px 0 #9FDFFF inset,
|
||||
0 -2px 0 rgba(0,0,0,.05) inset,
|
||||
0 -1px 0 rgba(0,0,0,.2) inset;
|
||||
--tab-selection-box-shadow: none;
|
||||
--pinned-tab-glow: radial-gradient(22px at center calc(100% - 2px), rgba(76,158,217,0.9) 13%, transparent 16%);
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ function test() {
|
|||
// Focus the console and add event listener to track whether it loses focus
|
||||
// (Must happen after generateMouseClickInTab() call)
|
||||
let consoleLostFocus = false;
|
||||
jsterm.inputNode.focus();
|
||||
jsterm.focus();
|
||||
jsterm.inputNode.addEventListener('blur', () => {consoleLostFocus = true;});
|
||||
|
||||
is(gThreadClient.paused, true,
|
||||
|
|
|
@ -1049,7 +1049,7 @@ InspectorPanel.prototype = {
|
|||
let jsterm = panel.hud.jsterm;
|
||||
|
||||
jsterm.execute("inspect($0)");
|
||||
jsterm.inputNode.focus();
|
||||
jsterm.focus();
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
max-width: 150px;
|
||||
}
|
||||
|
||||
.inspector-tabpanel {
|
||||
.inspector-tabpanel > * {
|
||||
/*
|
||||
* Override `-moz-user-focus:ignore;` from toolkit/content/minimal-xul.css
|
||||
*/
|
||||
|
|
|
@ -98,6 +98,8 @@ skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work wit
|
|||
[browser_rules_edit-property_08.js]
|
||||
[browser_rules_edit-property_09.js]
|
||||
[browser_rules_edit-selector-click.js]
|
||||
[browser_rules_edit-selector-click-on-scrollbar.js]
|
||||
skip-if = os == "mac" # Bug 1245996 : click on scrollbar not working on OSX
|
||||
[browser_rules_edit-selector-commit.js]
|
||||
[browser_rules_edit-selector_01.js]
|
||||
[browser_rules_edit-selector_02.js]
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Testing ruleview inplace-editor is not blurred when clicking on the ruleview
|
||||
// container scrollbar.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
div.testclass {
|
||||
color: black;
|
||||
}
|
||||
.a {
|
||||
color: #aaa;
|
||||
}
|
||||
.b {
|
||||
color: #bbb;
|
||||
}
|
||||
.c {
|
||||
color: #ccc;
|
||||
}
|
||||
.d {
|
||||
color: #ddd;
|
||||
}
|
||||
.e {
|
||||
color: #eee;
|
||||
}
|
||||
.f {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
<div class="testclass a b c d e f">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function*() {
|
||||
info("Toolbox height should be small enough to force scrollbars to appear");
|
||||
yield new Promise(done => {
|
||||
let options = {"set": [
|
||||
["devtools.toolbox.footer.height", 200],
|
||||
]};
|
||||
SpecialPowers.pushPrefEnv(options, done);
|
||||
});
|
||||
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode(".testclass", inspector);
|
||||
|
||||
info("Check we have an overflow on the ruleview container.");
|
||||
let container = view.element;
|
||||
let hasScrollbar = container.offsetHeight < container.scrollHeight;
|
||||
ok(hasScrollbar, "The rule view container should have a vertical scrollbar.");
|
||||
|
||||
info("Focusing an existing selector name in the rule-view.");
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let editor = yield focusEditableField(view, ruleEditor.selectorText);
|
||||
is(inplaceEditor(ruleEditor.selectorText), editor,
|
||||
"The selector editor is focused.");
|
||||
|
||||
info("Click on the scrollbar element.");
|
||||
yield clickOnRuleviewScrollbar(view);
|
||||
|
||||
is(editor.input, view.styleDocument.activeElement,
|
||||
"The editor input should still be focused.");
|
||||
|
||||
info("Check a new value can still be committed in the editable field");
|
||||
let newValue = ".testclass.a.b.c.d.e.f";
|
||||
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||
|
||||
info("Enter new value and commit.");
|
||||
editor.input.value = newValue;
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
yield onRuleViewChanged;
|
||||
ok(getRuleViewRule(view, newValue), "Rule with '" + newValue + " 'exists.");
|
||||
});
|
||||
|
||||
function* clickOnRuleviewScrollbar(view) {
|
||||
let container = view.element;
|
||||
let onScroll = once(container, "scroll");
|
||||
let rect = container.getBoundingClientRect();
|
||||
// click 5 pixels before the bottom-right corner should hit the scrollbar
|
||||
EventUtils.synthesizeMouse(container, rect.width - 5, rect.height - 5,
|
||||
{}, view.styleWindow);
|
||||
yield onScroll;
|
||||
|
||||
ok(true, "The rule view container scrolled after clicking on the scrollbar.");
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
const { setTheme } = require("devtools/client/shared/theme");
|
||||
|
||||
const LIGHT_BG = "#fcfcfc";
|
||||
const LIGHT_BG = "white";
|
||||
const DARK_BG = "#14171a";
|
||||
|
||||
setTheme("dark");
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
%sourceEditorStrings;
|
||||
]>
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="theme-body">
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="theme-body theme-light">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ support-files =
|
|||
skip-if = true # Bug 1173950
|
||||
[browser_projecteditor_delete_file.js]
|
||||
skip-if = e10s # Frequent failures in e10s - Bug 1020027
|
||||
[browser_projecteditor_rename_file.js]
|
||||
[browser_projecteditor_rename_file_01.js]
|
||||
[browser_projecteditor_rename_file_02.js]
|
||||
[browser_projecteditor_editing_01.js]
|
||||
[browser_projecteditor_editors_image.js]
|
||||
[browser_projecteditor_external_change.js]
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test file rename functionality
|
||||
|
||||
add_task(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
ok(true, "ProjectEditor has loaded");
|
||||
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child),
|
||||
".renamed");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
ok(true, "ProjectEditor has loaded");
|
||||
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
|
||||
let childrenList = new Array();
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child),
|
||||
".ren\u0061\u0308med");
|
||||
childrenList.push(child.basename + ".ren\u0061\u0308med");
|
||||
}
|
||||
for (let child of root.children) {
|
||||
is (childrenList.indexOf(child.basename) == -1, false,
|
||||
"Failed to update tree with non-ascii character");
|
||||
}
|
||||
});
|
||||
|
||||
function openContextMenuOn(node) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
node,
|
||||
{button: 2, type: "contextmenu"},
|
||||
node.ownerDocument.defaultView
|
||||
);
|
||||
}
|
||||
|
||||
function renameWithContextMenu(projecteditor, container, newName) {
|
||||
let defer = promise.defer();
|
||||
let popup = projecteditor.contextMenuPopup;
|
||||
let resource = container.resource;
|
||||
info ("Going to attempt renaming for: " + resource.path);
|
||||
|
||||
onPopupShow(popup).then(function () {
|
||||
let renameCommand = popup.querySelector("[command=cmd-rename]");
|
||||
ok (renameCommand, "Rename command exists in popup");
|
||||
is (renameCommand.getAttribute("hidden"), "", "Rename command is visible");
|
||||
is (renameCommand.getAttribute("disabled"), "", "Rename command is enabled");
|
||||
|
||||
projecteditor.project.on("refresh-complete", function refreshComplete() {
|
||||
projecteditor.project.off("refresh-complete", refreshComplete);
|
||||
OS.File.stat(resource.path + newName).then(() => {
|
||||
ok (true, "File is renamed");
|
||||
defer.resolve();
|
||||
}, (ex) => {
|
||||
ok (false, "Failed to rename file");
|
||||
defer.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
renameCommand.click();
|
||||
popup.hidePopup();
|
||||
let input = container.elt.childNodes[0].childNodes[1];
|
||||
input.value = resource.basename + newName;
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
|
||||
});
|
||||
|
||||
openContextMenuOn(container.label);
|
||||
return defer.promise;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test file rename functionality
|
||||
|
||||
add_task(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
ok(true, "ProjectEditor has loaded");
|
||||
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child), ".renamed");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test file rename functionality with non ascii characters
|
||||
|
||||
add_task(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
ok(true, "ProjectEditor has loaded");
|
||||
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
|
||||
let childrenList = [];
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child), ".ren\u0061\u0308med");
|
||||
childrenList.push(child.basename + ".ren\u0061\u0308med");
|
||||
}
|
||||
for (let child of root.children) {
|
||||
is(childrenList.indexOf(child.basename) == -1, false,
|
||||
"Failed to update tree with non-ascii character");
|
||||
}
|
||||
});
|
|
@ -284,6 +284,46 @@ function* getFileData(file) {
|
|||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the resource of the provided container using the context menu.
|
||||
*
|
||||
* @param {ProjectEditor} projecteditor the current project editor instance
|
||||
* @param {Shell} container for the resource to rename
|
||||
* @param {String} newName the name to use for renaming the resource
|
||||
* @return {Promise} a promise that resolves when the resource has been renamed
|
||||
*/
|
||||
var renameWithContextMenu = Task.async(function* (projecteditor,
|
||||
container, newName) {
|
||||
let popup = projecteditor.contextMenuPopup;
|
||||
let resource = container.resource;
|
||||
info("Going to attempt renaming for: " + resource.path);
|
||||
|
||||
let waitForPopupShow = onPopupShow(popup);
|
||||
openContextMenu(container.label);
|
||||
yield waitForPopupShow;
|
||||
|
||||
let renameCommand = popup.querySelector("[command=cmd-rename]");
|
||||
ok(renameCommand, "Rename command exists in popup");
|
||||
is(renameCommand.getAttribute("hidden"), "", "Rename command is visible");
|
||||
is(renameCommand.getAttribute("disabled"), "", "Rename command is enabled");
|
||||
|
||||
renameCommand.click();
|
||||
popup.hidePopup();
|
||||
let input = container.elt.childNodes[0].childNodes[1];
|
||||
input.value = resource.basename + newName;
|
||||
|
||||
let waitForProjectRefresh = onceProjectRefreshed(projecteditor);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
|
||||
yield waitForProjectRefresh;
|
||||
|
||||
try {
|
||||
yield OS.File.stat(resource.path + newName);
|
||||
ok(true, "File is renamed");
|
||||
} catch (e) {
|
||||
ok(false, "Failed to rename file");
|
||||
}
|
||||
});
|
||||
|
||||
function onceEditorCreated(projecteditor) {
|
||||
let def = promise.defer();
|
||||
projecteditor.once("onEditorCreated", (editor) => {
|
||||
|
@ -316,6 +356,15 @@ function onceEditorSave(projecteditor) {
|
|||
return def.promise;
|
||||
}
|
||||
|
||||
function onceProjectRefreshed(projecteditor) {
|
||||
return new Promise(resolve => {
|
||||
projecteditor.project.on("refresh-complete", function refreshComplete() {
|
||||
projecteditor.project.off("refresh-complete", refreshComplete);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onPopupShow(menu) {
|
||||
let defer = promise.defer();
|
||||
menu.addEventListener("popupshown", function onpopupshown() {
|
||||
|
@ -333,3 +382,11 @@ function onPopupHidden(menu) {
|
|||
});
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
function openContextMenu(node) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
node,
|
||||
{button: 2, type: "contextmenu"},
|
||||
node.ownerDocument.defaultView
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
since they aren't loaded in this context (within commandlineoutput.xhtml
|
||||
and commandlinetooltip.xhtml). */
|
||||
:root[devtoolstheme="light"] {
|
||||
--gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #18191a; /* --theme-body-color */
|
||||
--gcli-border-color: #aaaaaa; /* --theme-splitter-color */
|
||||
--gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #393f4c; /* --theme-body-color */
|
||||
--gcli-border-color: #dde1e4; /* --theme-splitter-color */
|
||||
}
|
||||
|
||||
:root[devtoolstheme="dark"] {
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
We are copy/pasting variables from light-theme and dark-theme,
|
||||
since they aren't loaded in this context (within browser.css). */
|
||||
:root[devtoolstheme="light"] #developer-toolbar {
|
||||
--gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-background: #f0f1f2; /* --theme-toolbar-background */
|
||||
--gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #18191a; /* --theme-body-color */
|
||||
--gcli-border-color: #aaaaaa; /* --theme-splitter-color */
|
||||
--gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
|
||||
--gcli-input-background: #fcfcfc; /* --theme-toolbar-background */
|
||||
--gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
|
||||
--gcli-input-color: #393f4c; /* --theme-body-color */
|
||||
--gcli-border-color: #dde1e4; /* --theme-splitter-color */
|
||||
--selection-background: #4c9ed9; /* --theme-selection-background */
|
||||
--selection-color: #f5f7fa; /* --theme-selection-color */
|
||||
}
|
||||
|
|
|
@ -68,13 +68,13 @@ body {
|
|||
}
|
||||
|
||||
.theme-gutter {
|
||||
background-color: #0f171f;
|
||||
background-color: var(--theme-tab-toolbar-background);
|
||||
color: var(--theme-content-color3);
|
||||
border-color: #303b47;
|
||||
border-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.theme-separator { /* grey */
|
||||
border-color: #303b47;
|
||||
.theme-separator {
|
||||
border-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.theme-fg-color1,
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg width="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
|
||||
<path fill="#f7f7f7" d="M7,11.6 7,.4 1.5,6z"/>
|
||||
<path fill="#ababab" d="M7,0 6,0 0,6 6,12 7,12 7,11.6 1.5,6 7,.4z"/>
|
||||
<path fill="#ffffff" d="M7,11.6 7,.4 1.5,6z"/>
|
||||
<path fill="#dde1e4" d="M7,0 6,0 0,6 6,12 7,12 7,11.6 1.5,6 7,.4z"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 421 B После Ширина: | Высота: | Размер: 421 B |
|
@ -2,6 +2,6 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg width="7" xmlns="http://www.w3.org/2000/svg" height="12" viewBox="0 0 7 12">
|
||||
<path fill="#f7f7f7" d="M0,11.6 0,.4 5.5,6z"/>
|
||||
<path fill="#ababab" d="M1,0 0,0 0,.4 5.5,6 0,11.6 0,12 1,12 7,6z"/>
|
||||
<path fill="#ffffff" d="M0,11.6 0,.4 5.5,6z"/>
|
||||
<path fill="#dde1e4" d="M1,0 0,0 0,.4 5.5,6 0,11.6 0,12 1,12 7,6z"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 421 B После Ширина: | Высота: | Размер: 421 B |
|
@ -67,9 +67,9 @@ body {
|
|||
}
|
||||
|
||||
.theme-gutter {
|
||||
background-color: hsl(0,0%,90%);
|
||||
background-color: var(--theme-tab-toolbar-background);
|
||||
color: var(--theme-content-color3);
|
||||
border-color: hsl(0,0%,65%);
|
||||
border-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.theme-separator { /* grey */
|
||||
|
@ -173,7 +173,7 @@ body {
|
|||
.devtools-sidebar-tabs tabs,
|
||||
.devtools-sidebar-alltabs,
|
||||
.cm-s-mozilla .CodeMirror-dialog { /* General toolbar styling */
|
||||
color: var(--theme-body-color-alt);
|
||||
color: var(--theme-body-color);
|
||||
background-color: var(--theme-toolbar-background);
|
||||
border-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:root {
|
||||
color: #18191a;
|
||||
}
|
||||
|
||||
.view-project-detail {
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,21 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Splitters */
|
||||
:root[devtoolstheme="light"] .devtools-horizontal-splitter {
|
||||
/* These variables are used in browser.xul but inside the toolbox they are overridden by --theme-splitter-color */
|
||||
--devtools-splitter-color: #dde1e4;
|
||||
}
|
||||
|
||||
:root[devtoolstheme="dark"] .devtools-horizontal-splitter {
|
||||
--devtools-splitter-color: #42484f;
|
||||
}
|
||||
|
||||
.devtools-horizontal-splitter {
|
||||
-moz-appearance: none;
|
||||
background-image: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(118, 121, 125, .5);
|
||||
border-bottom: 1px solid var(--devtools-splitter-color);
|
||||
min-height: 3px;
|
||||
height: 3px;
|
||||
margin-top: -3px;
|
||||
|
@ -20,7 +29,7 @@
|
|||
background-image: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
-moz-border-end: 1px solid rgba(118, 121, 125, .5);
|
||||
-moz-border-end: 1px solid var(--devtools-splitter-color);
|
||||
min-width: 3px;
|
||||
width: 3px;
|
||||
-moz-margin-start: -3px;
|
||||
|
|
|
@ -818,10 +818,6 @@
|
|||
border-bottom-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.theme-light .devtools-tabbar {
|
||||
box-shadow: 0 -2px 0 rgba(170,170,170,.1) inset;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-tabbar {
|
||||
box-shadow: 0 -2px 0 rgba(0,0,0,.1) inset;
|
||||
}
|
||||
|
@ -920,12 +916,6 @@
|
|||
0 -2px 0 rgba(0,0,0,.2) inset;
|
||||
}
|
||||
|
||||
.theme-light .devtools-tabbar .devtools-tab[selected] {
|
||||
box-shadow: 0 2px 0 #d7f1ff inset,
|
||||
0 8px 3px -5px #2b82bf inset,
|
||||
0 -2px 0 rgba(0,0,0,.06) inset;
|
||||
}
|
||||
|
||||
#toolbox-tabs .devtools-tab[selected],
|
||||
#toolbox-tabs .devtools-tab[highlighted] {
|
||||
border-width: 0;
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
*/
|
||||
|
||||
:root.theme-light {
|
||||
--theme-body-background: #fcfcfc;
|
||||
--theme-sidebar-background: #f7f7f7;
|
||||
--theme-body-background: white;
|
||||
--theme-sidebar-background: white;
|
||||
--theme-contrast-background: #e6b064;
|
||||
|
||||
--theme-tab-toolbar-background: #ebeced;
|
||||
--theme-toolbar-background: #f0f1f2;
|
||||
--theme-tab-toolbar-background: #fcfcfc;
|
||||
--theme-toolbar-background: #fcfcfc;
|
||||
--theme-selection-background: #4c9ed9;
|
||||
--theme-selection-background-semitransparent: rgba(76, 158, 217, .23);
|
||||
--theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15);
|
||||
--theme-selection-color: #f5f7fa;
|
||||
--theme-splitter-color: #aaaaaa;
|
||||
--theme-comment: #757873;
|
||||
--theme-splitter-color: #dde1e4;
|
||||
--theme-comment: #696969;
|
||||
|
||||
--theme-body-color: #18191a;
|
||||
--theme-body-color: #393f4c;
|
||||
--theme-body-color-alt: #585959;
|
||||
--theme-content-color1: #292e33;
|
||||
--theme-content-color2: #8fa1b2;
|
||||
|
|
|
@ -6,14 +6,12 @@
|
|||
.theme-dark {
|
||||
--table-splitter-color: rgba(255,255,255,0.15);
|
||||
--table-zebra-background: rgba(255,255,255,0.05);
|
||||
--smw-margin: #000;
|
||||
--smw-item-top-border: rgba(0,0,0,0.2);
|
||||
--smw-item-bottom-border: rgba(128,128,128,0.15);
|
||||
}
|
||||
.theme-light {
|
||||
--table-splitter-color: rgba(0,0,0,0.15);
|
||||
--table-zebra-background: rgba(0,0,0,0.05);
|
||||
--smw-margin: #aaa;
|
||||
--smw-item-top-border: rgba(128,128,128,0.15);
|
||||
--smw-item-bottom-border: transparent;
|
||||
}
|
||||
|
@ -357,12 +355,12 @@
|
|||
|
||||
.side-menu-widget-container:-moz-locale-dir(ltr),
|
||||
.side-menu-widget-empty-text:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 var(--smw-margin);
|
||||
box-shadow: inset -1px 0 0 var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.side-menu-widget-container:-moz-locale-dir(rtl),
|
||||
.side-menu-widget-empty-text:-moz-locale-dir(rtl) {
|
||||
box-shadow: inset 1px 0 0 var(--smw-margin);
|
||||
box-shadow: inset 1px 0 0 var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.side-menu-widget-group {
|
||||
|
|
|
@ -106,8 +106,7 @@ function* populateInputHistory(hud) {
|
|||
*/
|
||||
function* testNaviatingHistoryInUI(hud) {
|
||||
let jsterm = hud.jsterm;
|
||||
let {inputNode} = jsterm;
|
||||
inputNode.focus();
|
||||
jsterm.focus();
|
||||
|
||||
// Count backwards from original input and make sure that pressing up
|
||||
// restores this.
|
||||
|
|
|
@ -21,8 +21,6 @@ add_task(function* () {
|
|||
function testClosingAfterCompletion(hud, browser) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let inputNode = hud.jsterm.inputNode;
|
||||
|
||||
let errorWhileClosing = false;
|
||||
function errorListener() {
|
||||
errorWhileClosing = true;
|
||||
|
@ -30,8 +28,8 @@ function testClosingAfterCompletion(hud, browser) {
|
|||
|
||||
browser.addEventListener("error", errorListener, false);
|
||||
|
||||
// Focus the inputNode and perform the keycombo to close the WebConsole.
|
||||
inputNode.focus();
|
||||
// Focus the jsterm and perform the keycombo to close the WebConsole.
|
||||
hud.jsterm.focus();
|
||||
|
||||
gDevTools.once("toolbox-destroyed", function() {
|
||||
browser.removeEventListener("error", errorListener, false);
|
||||
|
|
|
@ -25,7 +25,7 @@ function setup(HUD) {
|
|||
jsterm = HUD.jsterm;
|
||||
inputNode = jsterm.inputNode;
|
||||
|
||||
inputNode.focus();
|
||||
jsterm.focus();
|
||||
|
||||
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ function test() {
|
|||
|
||||
yield executeJS();
|
||||
yield clickMessageAndShowVariablesView();
|
||||
jsterm.inputNode.focus();
|
||||
jsterm.focus();
|
||||
|
||||
yield testHideVariablesViewAfterEscape();
|
||||
|
||||
|
@ -144,7 +144,7 @@ function test() {
|
|||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
jsterm.inputNode.focus();
|
||||
jsterm.focus();
|
||||
jsterm.setInputValue("document.location.");
|
||||
EventUtils.sendKey("TAB", hud.iframeWindow);
|
||||
|
||||
|
|
|
@ -577,7 +577,7 @@ WebConsoleFrame.prototype = {
|
|||
this._addFocusCallback(this.outputNode, (evt) => {
|
||||
if ((evt.target.nodeName.toLowerCase() != "a") &&
|
||||
(evt.target.parentNode.nodeName.toLowerCase() != "a")) {
|
||||
this.jsterm.inputNode.focus();
|
||||
this.jsterm.focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -589,7 +589,7 @@ WebConsoleFrame.prototype = {
|
|||
});
|
||||
|
||||
// focus input node
|
||||
this.jsterm.inputNode.focus();
|
||||
this.jsterm.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -607,7 +607,7 @@ WebConsoleFrame.prototype = {
|
|||
* @private
|
||||
*/
|
||||
_onPanelSelected: function() {
|
||||
this.jsterm.inputNode.focus();
|
||||
this.jsterm.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3090,9 +3090,8 @@ JSTerm.prototype = {
|
|||
},
|
||||
|
||||
focus: function() {
|
||||
let inputNode = this.inputNode;
|
||||
if (!inputNode.getAttribute("focused")) {
|
||||
inputNode.focus();
|
||||
if (!this.inputNode.getAttribute("focused")) {
|
||||
this.inputNode.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3461,7 +3460,7 @@ JSTerm.prototype = {
|
|||
}
|
||||
|
||||
this._sidebarDestroy();
|
||||
this.inputNode.focus();
|
||||
this.focus();
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
|
@ -3854,7 +3853,7 @@ JSTerm.prototype = {
|
|||
// Ctrl-N is also used to focus the Network category button on
|
||||
// MacOSX. The preventDefault() call doesn't prevent the focus
|
||||
// from moving away from the input.
|
||||
inputNode.focus();
|
||||
this.focus();
|
||||
}
|
||||
this.clearCompletion();
|
||||
break;
|
||||
|
@ -3870,7 +3869,7 @@ JSTerm.prototype = {
|
|||
// Ctrl-P may also be used to focus some category button on MacOSX.
|
||||
// The preventDefault() call doesn't prevent the focus from moving
|
||||
// away from the input.
|
||||
inputNode.focus();
|
||||
this.focus();
|
||||
}
|
||||
this.clearCompletion();
|
||||
break;
|
||||
|
|
|
@ -10853,6 +10853,24 @@ nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsIDocument::GetMozDocumentURIIfNotForErrorPages()
|
||||
{
|
||||
if (mFailedChannel) {
|
||||
nsCOMPtr<nsIURI> failedURI;
|
||||
if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
|
||||
return failedURI.forget();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
|
||||
if (!uri) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return uri.forget();
|
||||
}
|
||||
|
||||
nsIHTMLCollection*
|
||||
nsIDocument::Children()
|
||||
{
|
||||
|
|
|
@ -2601,6 +2601,8 @@ public:
|
|||
|
||||
void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
|
||||
|
||||
already_AddRefed<nsIURI> GetMozDocumentURIIfNotForErrorPages();
|
||||
|
||||
// ParentNode
|
||||
nsIHTMLCollection* Children();
|
||||
uint32_t ChildElementCount();
|
||||
|
|
|
@ -6,7 +6,7 @@ support-files =
|
|||
plugin_no_scroll_div.html
|
||||
|
||||
[browser_bug1163570.js]
|
||||
skip-if = (!e10s || os != "win")
|
||||
skip-if = true # Bug 1249878
|
||||
[browser_bug1196539.js]
|
||||
skip-if = (!e10s || os != "win")
|
||||
[browser_tabswitchbetweenplugins.js]
|
||||
|
|
|
@ -39,20 +39,18 @@ function promiseWaitForEvent(object, eventName, capturing = false, chrome = fals
|
|||
|
||||
add_task(function* () {
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("browser.uiCustomization.disableAnimation");
|
||||
window.focus();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
|
||||
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
|
||||
|
||||
let pluginTab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
let customizeTab = gBrowser.addTab();
|
||||
let prefTab = gBrowser.addTab();
|
||||
|
||||
yield promiseTabLoad(pluginTab, gTestRoot + "plugin_test.html");
|
||||
yield promiseTabLoad(customizeTab, "about:customizing");
|
||||
yield promiseTabLoad(prefTab, "about:preferences");
|
||||
|
||||
let result = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
|
||||
let doc = content.document;
|
||||
|
@ -62,10 +60,8 @@ add_task(function* () {
|
|||
|
||||
is(result, true, "plugin is loaded");
|
||||
|
||||
let cpromise = promiseWaitForEvent(window.gNavToolbox, "customizationready");
|
||||
let ppromise = promiseWaitForEvent(window, "MozAfterPaint");
|
||||
gBrowser.selectedTab = customizeTab;
|
||||
yield cpromise;
|
||||
gBrowser.selectedTab = prefTab;
|
||||
yield ppromise;
|
||||
|
||||
// We're going to switch tabs using actual mouse clicks, which helps
|
||||
|
@ -77,13 +73,11 @@ add_task(function* () {
|
|||
info("-> " + tabStripContainer.firstChild.tagName); // tab
|
||||
info("-> " + tabStripContainer.childNodes[0].label); // test harness tab
|
||||
info("-> " + tabStripContainer.childNodes[1].label); // plugin tab
|
||||
info("-> " + tabStripContainer.childNodes[2].label); // customize tab
|
||||
info("-> " + tabStripContainer.childNodes[2].label); // preferences tab
|
||||
|
||||
for (let iteration = 0; iteration < 5; iteration++) {
|
||||
cpromise = promiseWaitForEvent(window.gNavToolbox, "aftercustomization");
|
||||
ppromise = promiseWaitForEvent(window, "MozAfterPaint");
|
||||
EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[1], {}, window);
|
||||
yield cpromise;
|
||||
yield ppromise;
|
||||
|
||||
result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
|
||||
|
@ -94,10 +88,8 @@ add_task(function* () {
|
|||
|
||||
is(result, true, "plugin is visible");
|
||||
|
||||
cpromise = promiseWaitForEvent(window.gNavToolbox, "customizationready");
|
||||
ppromise = promiseWaitForEvent(window, "MozAfterPaint");
|
||||
EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[2], {}, window);
|
||||
yield cpromise;
|
||||
yield ppromise;
|
||||
|
||||
result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
|
||||
|
@ -108,11 +100,6 @@ add_task(function* () {
|
|||
is(result, false, "plugin is hidden");
|
||||
}
|
||||
|
||||
// wait for customize view to shutdown cleanly otherwise we get
|
||||
// a ton of error spew on shutdown.
|
||||
cpromise = promiseWaitForEvent(window.gNavToolbox, "aftercustomization");
|
||||
gBrowser.removeTab(customizeTab);
|
||||
yield cpromise;
|
||||
|
||||
gBrowser.removeTab(prefTab);
|
||||
gBrowser.removeTab(pluginTab);
|
||||
});
|
||||
|
|
|
@ -373,6 +373,10 @@ partial interface Document {
|
|||
[ChromeOnly] readonly attribute DOMString contentLanguage;
|
||||
|
||||
[ChromeOnly] readonly attribute nsILoadGroup? documentLoadGroup;
|
||||
|
||||
// like documentURI, except that for error pages, it returns the URI we were
|
||||
// trying to load when we hit an error, rather than the error page's own URI.
|
||||
[ChromeOnly] readonly attribute URI? mozDocumentURIIfNotForErrorPages;
|
||||
};
|
||||
|
||||
// Extension to give chrome JS the ability to determine when a document was
|
||||
|
|
|
@ -228,7 +228,7 @@ nsTextBoxFrame::UpdateAttributes(nsIAtom* aAttribute,
|
|||
if (aAttribute == nullptr || aAttribute == nsGkAtoms::crop) {
|
||||
static nsIContent::AttrValuesArray strings[] =
|
||||
{&nsGkAtoms::left, &nsGkAtoms::start, &nsGkAtoms::center,
|
||||
&nsGkAtoms::right, &nsGkAtoms::end, nullptr};
|
||||
&nsGkAtoms::right, &nsGkAtoms::end, &nsGkAtoms::none, nullptr};
|
||||
CroppingStyle cropType;
|
||||
switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
|
||||
strings, eCaseMatters)) {
|
||||
|
@ -243,9 +243,12 @@ nsTextBoxFrame::UpdateAttributes(nsIAtom* aAttribute,
|
|||
case 4:
|
||||
cropType = CropRight;
|
||||
break;
|
||||
default:
|
||||
case 5:
|
||||
cropType = CropNone;
|
||||
break;
|
||||
default:
|
||||
cropType = CropAuto;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cropType != mCropType) {
|
||||
|
@ -647,31 +650,37 @@ nsTextBoxFrame::CalculateTitleForWidth(nsRenderingContext& aRenderingContext,
|
|||
}
|
||||
|
||||
const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
|
||||
// start with an ellipsis
|
||||
mCroppedTitle.Assign(kEllipsis);
|
||||
if (mCropType != CropNone) {
|
||||
// start with an ellipsis
|
||||
mCroppedTitle.Assign(kEllipsis);
|
||||
|
||||
// see if the width is even smaller than the ellipsis
|
||||
// if so, clear the text (XXX set as many '.' as we can?).
|
||||
fm->SetTextRunRTL(false);
|
||||
titleWidth = nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm,
|
||||
drawTarget);
|
||||
// see if the width is even smaller than the ellipsis
|
||||
// if so, clear the text (XXX set as many '.' as we can?).
|
||||
fm->SetTextRunRTL(false);
|
||||
titleWidth = nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm,
|
||||
drawTarget);
|
||||
|
||||
if (titleWidth > aWidth) {
|
||||
mCroppedTitle.SetLength(0);
|
||||
return 0;
|
||||
if (titleWidth > aWidth) {
|
||||
mCroppedTitle.SetLength(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if the ellipsis fits perfectly, no use in trying to insert
|
||||
if (titleWidth == aWidth)
|
||||
return titleWidth;
|
||||
|
||||
aWidth -= titleWidth;
|
||||
} else {
|
||||
mCroppedTitle.Truncate(0);
|
||||
titleWidth = 0;
|
||||
}
|
||||
|
||||
// if the ellipsis fits perfectly, no use in trying to insert
|
||||
if (titleWidth == aWidth)
|
||||
return titleWidth;
|
||||
|
||||
aWidth -= titleWidth;
|
||||
|
||||
// XXX: This whole block should probably take surrogates into account
|
||||
// XXX and clusters!
|
||||
// ok crop things
|
||||
switch (mCropType)
|
||||
{
|
||||
case CropAuto:
|
||||
case CropNone:
|
||||
case CropRight:
|
||||
{
|
||||
|
@ -1126,7 +1135,7 @@ nsTextBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
|
|||
DISPLAY_MIN_SIZE(this, size);
|
||||
|
||||
// if there is cropping our min width becomes our border and padding
|
||||
if (mCropType != CropNone) {
|
||||
if (mCropType != CropNone && mCropType != CropAuto) {
|
||||
if (GetWritingMode().IsVertical()) {
|
||||
size.height = 0;
|
||||
} else {
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState) override;
|
||||
virtual void MarkIntrinsicISizesDirty() override;
|
||||
|
||||
enum CroppingStyle { CropNone, CropLeft, CropRight, CropCenter };
|
||||
enum CroppingStyle { CropNone, CropLeft, CropRight, CropCenter, CropAuto };
|
||||
|
||||
friend nsIFrame* NS_NewTextBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
|
||||
private void showBookmarkRemovedSnackbar() {
|
||||
SnackbarHelper.showSnackbar(this, getResources().getString(R.string.bookmark_removed), Snackbar.LENGTH_SHORT);
|
||||
SnackbarHelper.showSnackbar(this, getResources().getString(R.string.bookmark_removed), Snackbar.LENGTH_LONG);
|
||||
}
|
||||
|
||||
private void showSwitchToReadingListSnackbar(String message) {
|
||||
|
@ -456,7 +456,7 @@ public class BrowserApp extends GeckoApp
|
|||
public void onRemovedFromReadingList(String url) {
|
||||
SnackbarHelper.showSnackbar(this,
|
||||
getResources().getString(R.string.reading_list_removed),
|
||||
Snackbar.LENGTH_SHORT);
|
||||
Snackbar.LENGTH_LONG);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2016,7 +2016,7 @@ public class BrowserApp extends GeckoApp
|
|||
if (Tabs.getInstance().getDisplayCount() == 0)
|
||||
return;
|
||||
|
||||
hideFirstrunPager(TelemetryContract.Method.TABSTRAY);
|
||||
hideFirstrunPager(TelemetryContract.Method.BUTTON);
|
||||
|
||||
if (ensureTabsPanelExists()) {
|
||||
// If we've just inflated the tabs panel, only show it once the current
|
||||
|
|
|
@ -219,7 +219,7 @@ public class EditBookmarkDialog {
|
|||
public void onPostExecute(Void result) {
|
||||
SnackbarHelper.showSnackbar((Activity) context,
|
||||
context.getString(R.string.bookmark_updated),
|
||||
Snackbar.LENGTH_SHORT);
|
||||
Snackbar.LENGTH_LONG);
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
|
|
|
@ -576,7 +576,7 @@ public abstract class GeckoApp
|
|||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SnackbarHelper.showSnackbar(GeckoApp.this, getString(resId), Snackbar.LENGTH_SHORT);
|
||||
SnackbarHelper.showSnackbar(GeckoApp.this, getString(resId), Snackbar.LENGTH_LONG);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1014,12 +1014,12 @@ public abstract class GeckoApp
|
|||
File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
|
||||
if (!dcimDir.mkdirs() && !dcimDir.isDirectory()) {
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_path_fail), Snackbar.LENGTH_SHORT);
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_path_fail), Snackbar.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
String path = Media.insertImage(getContentResolver(),image, null, null);
|
||||
if (path == null) {
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_path_fail), Snackbar.LENGTH_SHORT);
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_path_fail), Snackbar.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
|
||||
|
@ -1036,7 +1036,7 @@ public abstract class GeckoApp
|
|||
};
|
||||
ActivityHandlerHelper.startIntentForActivity(this, chooser, handler);
|
||||
} else {
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_fail), Snackbar.LENGTH_SHORT);
|
||||
SnackbarHelper.showSnackbar(this, getString(R.string.set_image_fail), Snackbar.LENGTH_LONG);
|
||||
}
|
||||
} catch(OutOfMemoryError ome) {
|
||||
Log.e(LOGTAG, "Out of Memory when converting to byte array", ome);
|
||||
|
|
|
@ -2670,7 +2670,7 @@ public class GeckoAppShell
|
|||
private static final void showImageShareFailureSnackbar() {
|
||||
SnackbarHelper.showSnackbar((Activity) getContext(),
|
||||
getApplicationContext().getString(R.string.share_image_failed),
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_LONG
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.nio.charset.Charset;
|
|||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -48,6 +49,7 @@ public final class GeckoProfile {
|
|||
|
||||
// The path in the profile to the file containing the client ID.
|
||||
private static final String CLIENT_ID_FILE_PATH = "datareporting/state.json";
|
||||
private static final String FHR_CLIENT_ID_FILE_PATH = "healthreport/state.json";
|
||||
// In the client ID file, the attribute title in the JSON object containing the client ID value.
|
||||
private static final String CLIENT_ID_JSON_ATTR = "clientID";
|
||||
|
||||
|
@ -599,7 +601,8 @@ public final class GeckoProfile {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Gecko client ID from the filesystem.
|
||||
* Retrieves the Gecko client ID from the filesystem. If the client ID does not exist, we attempt to migrate and
|
||||
* persist it from FHR and, if that fails, we attempt to create a new one ourselves.
|
||||
*
|
||||
* This method assumes the client ID is located in a file at a hard-coded path within the profile. The format of
|
||||
* this file is a JSONObject which at the bottom level contains a String -> String mapping containing the client ID.
|
||||
|
@ -608,21 +611,84 @@ public final class GeckoProfile {
|
|||
* robust way to access it. However, we don't want to rely on Gecko running in order to get
|
||||
* the client ID so instead we access the file this module accesses directly. However, it's
|
||||
* possible the format of this file (and the access calls in the jsm) will change, leaving
|
||||
* this code to fail.
|
||||
*
|
||||
* TODO: Write tests to prevent regressions. Mention them here. Test both file location and file format.
|
||||
* this code to fail. There are tests in TestGeckoProfile to verify the file format but be
|
||||
* warned: THIS IS NOT FOOLPROOF.
|
||||
*
|
||||
* [1]: https://mxr.mozilla.org/mozilla-central/source/toolkit/modules/ClientID.jsm
|
||||
*
|
||||
* @throws IOException if the client ID could not be retrieved.
|
||||
*/
|
||||
// Mimics ClientID.jsm – _doLoadClientID.
|
||||
@WorkerThread
|
||||
public String getClientId() throws IOException {
|
||||
final JSONObject obj = readJSONObjectFromFile(CLIENT_ID_FILE_PATH);
|
||||
try {
|
||||
return obj.getString(CLIENT_ID_JSON_ATTR);
|
||||
} catch (final JSONException e) {
|
||||
// Don't log to avoid leaking data in JSONObject.
|
||||
throw new IOException("Client ID does not exist in JSONObject");
|
||||
return getValidClientIdFromDisk(CLIENT_ID_FILE_PATH);
|
||||
} catch (final IOException e) {
|
||||
// Avoid log spam: don't log the full Exception w/ the stack trace.
|
||||
Log.d(LOGTAG, "Could not get client ID - attempting to migrate ID from FHR: " + e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
String clientIdToWrite;
|
||||
try {
|
||||
clientIdToWrite = getValidClientIdFromDisk(FHR_CLIENT_ID_FILE_PATH);
|
||||
} catch (final IOException e) {
|
||||
// Avoid log spam: don't log the full Exception w/ the stack trace.
|
||||
Log.d(LOGTAG, "Could not migrate client ID from FHR – creating a new one: " + e.getLocalizedMessage());
|
||||
clientIdToWrite = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
// There is a possibility Gecko is running and the Gecko telemetry implementation decided it's time to generate
|
||||
// the client ID, writing client ID underneath us. Since it's highly unlikely (e.g. we run in onStart before
|
||||
// Gecko is started), we don't handle that possibility besides writing the ID and then reading from the file
|
||||
// again (rather than just returning the value we generated before writing).
|
||||
//
|
||||
// In the event it does happen, any discrepancy will be resolved after a restart. In the mean time, both this
|
||||
// implementation and the Gecko implementation could upload documents with inconsistent IDs.
|
||||
//
|
||||
// In any case, if we get an exception, intentionally throw - there's nothing more to do here.
|
||||
persistClientId(clientIdToWrite);
|
||||
return getValidClientIdFromDisk(CLIENT_ID_FILE_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a valid client ID
|
||||
* @throws IOException if a valid client ID could not be retrieved
|
||||
*/
|
||||
@WorkerThread
|
||||
private String getValidClientIdFromDisk(final String filePath) throws IOException {
|
||||
final JSONObject obj = readJSONObjectFromFile(filePath);
|
||||
final String clientId = obj.optString(CLIENT_ID_JSON_ATTR);
|
||||
if (isClientIdValid(clientId)) {
|
||||
return clientId;
|
||||
}
|
||||
throw new IOException("Received client ID is invalid: " + clientId);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void persistClientId(final String clientId) throws IOException {
|
||||
if (!ensureParentDirs(CLIENT_ID_FILE_PATH)) {
|
||||
throw new IOException("Could not create client ID parent directories");
|
||||
}
|
||||
|
||||
final JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put(CLIENT_ID_JSON_ATTR, clientId);
|
||||
} catch (final JSONException e) {
|
||||
throw new IOException("Could not create client ID JSON object", e);
|
||||
}
|
||||
|
||||
// ClientID.jsm overwrites the file to store the client ID so it's okay if we do it too.
|
||||
Log.d(LOGTAG, "Attempting to write new client ID");
|
||||
writeFile(CLIENT_ID_FILE_PATH, obj.toString()); // Logs errors within function: ideally we'd throw.
|
||||
}
|
||||
|
||||
// From ClientID.jsm - isValidClientID.
|
||||
public static boolean isClientIdValid(final String clientId) {
|
||||
// We could use UUID.fromString but, for consistency, we take the implementation from ClientID.jsm.
|
||||
if (TextUtils.isEmpty(clientId)) {
|
||||
return false;
|
||||
}
|
||||
return clientId.matches("(?i:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -702,6 +768,20 @@ public final class GeckoProfile {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the parent director(y|ies) of the given filename exist by making them
|
||||
* if they don't already exist..
|
||||
*
|
||||
* @param filename The path to the file whose parents should be made directories
|
||||
* @return true if the parent directory exists, false otherwise
|
||||
*/
|
||||
@WorkerThread
|
||||
protected boolean ensureParentDirs(final String filename) {
|
||||
final File file = new File(getDir(), filename);
|
||||
final File parentFile = file.getParentFile();
|
||||
return parentFile.mkdirs() || parentFile.isDirectory();
|
||||
}
|
||||
|
||||
public void writeFile(final String filename, final String data) {
|
||||
File file = new File(getDir(), filename);
|
||||
BufferedWriter bufferedWriter = null;
|
||||
|
|
|
@ -188,8 +188,8 @@ public interface TelemetryContract {
|
|||
// Action triggered from a suggestion provided to the user.
|
||||
SUGGESTION("suggestion"),
|
||||
|
||||
// Action triggered from the Tabs tray.
|
||||
TABSTRAY("tabstray"),
|
||||
// Action triggered from an OS system action.
|
||||
SYSTEM("system"),
|
||||
|
||||
// Action triggered from a SuperToast.
|
||||
// Note: Only used in JavaScript for now, but here for completeness.
|
||||
|
|
|
@ -419,7 +419,7 @@ public abstract class HomeFragment extends Fragment {
|
|||
public void onPostExecute(Void result) {
|
||||
SnackbarHelper.showSnackbar((Activity) mContext,
|
||||
mContext.getString(R.string.page_removed),
|
||||
Snackbar.LENGTH_SHORT);
|
||||
Snackbar.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -655,7 +655,7 @@ OnSharedPreferenceChangeListener
|
|||
|
||||
SnackbarHelper.showSnackbar(GeckoPreferences.this,
|
||||
getString(stringRes),
|
||||
Snackbar.LENGTH_SHORT);
|
||||
Snackbar.LENGTH_LONG);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
|
||||
|
|
|
@ -94,7 +94,7 @@ public class SearchEnginePreference extends CustomListPreference {
|
|||
|
||||
SnackbarHelper.showSnackbar(activity,
|
||||
activity.getString(R.string.pref_search_last_toast),
|
||||
Snackbar.LENGTH_SHORT);
|
||||
Snackbar.LENGTH_LONG);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -172,8 +172,7 @@ public class TelemetryUploadService extends BackgroundService {
|
|||
try {
|
||||
clientId = profile.getClientId();
|
||||
} catch (final IOException e) {
|
||||
// Don't log the exception to avoid leaking the profile path.
|
||||
Log.w(LOGTAG, "Unable to get client ID to generate core ping: returning.");
|
||||
Log.w(LOGTAG, "Unable to get client ID to generate core ping: returning.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
До Ширина: | Высота: | Размер: 46 KiB После Ширина: | Высота: | Размер: 18 KiB |
До Ширина: | Высота: | Размер: 38 KiB После Ширина: | Высота: | Размер: 16 KiB |
|
@ -348,7 +348,7 @@ var ActionBarHandler = {
|
|||
clipboard.copyString(selectedText);
|
||||
|
||||
let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
|
||||
Snackbars.show(msg, Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(msg, Snackbars.LENGTH_LONG);
|
||||
|
||||
// Then cut the selection text.
|
||||
ActionBarHandler._getSelection(element, win).deleteFromDocument();
|
||||
|
@ -383,7 +383,7 @@ var ActionBarHandler = {
|
|||
clipboard.copyString(selectedText);
|
||||
|
||||
let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
|
||||
Snackbars.show(msg, Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(msg, Snackbars.LENGTH_LONG);
|
||||
|
||||
ActionBarHandler._uninit();
|
||||
UITelemetry.addEvent("action.1", "actionbar", null, "copy");
|
||||
|
|
|
@ -63,7 +63,7 @@ var MasterPassword = {
|
|||
} catch(e) {
|
||||
dump("MasterPassword.removePassword: " + e + "\n");
|
||||
}
|
||||
Snackbars.show(Strings.browser.GetStringFromName("masterPassword.incorrect"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(Strings.browser.GetStringFromName("masterPassword.incorrect"), Snackbars.LENGTH_LONG);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -180,7 +180,7 @@ var Reader = {
|
|||
|
||||
case "Reader:ToolbarHidden":
|
||||
if (!this._hasUsedToolbar) {
|
||||
Snackbars.show(Strings.browser.GetStringFromName("readerMode.toolbarTip"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(Strings.browser.GetStringFromName("readerMode.toolbarTip"), Snackbars.LENGTH_LONG);
|
||||
Services.prefs.setBoolPref("reader.has_used_toolbar", true);
|
||||
this._hasUsedToolbar = true;
|
||||
}
|
||||
|
|
|
@ -1090,7 +1090,7 @@ var SelectionHandler = {
|
|||
if (selectedText.length) {
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(selectedText);
|
||||
Snackbars.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), Snackbars.LENGTH_LONG);
|
||||
}
|
||||
this._closeSelection();
|
||||
},
|
||||
|
|
|
@ -31,10 +31,10 @@ function copyStringShowSnackbar(string, notifyString) {
|
|||
try {
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(string);
|
||||
Snackbars.show(notifyString, Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(notifyString, Snackbars.LENGTH_LONG);
|
||||
} catch (e) {
|
||||
debug("Error copying from about:logins");
|
||||
Snackbars.show(gStringBundle.GetStringFromName("loginsDetails.copyFailed"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(gStringBundle.GetStringFromName("loginsDetails.copyFailed"), Snackbars.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ var Logins = {
|
|||
if ((newUsername === origUsername) &&
|
||||
(newPassword === origPassword) &&
|
||||
(newDomain === origDomain) ) {
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_LONG);
|
||||
this._showList();
|
||||
return;
|
||||
}
|
||||
|
@ -310,10 +310,10 @@ var Logins = {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.couldNotSave"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.couldNotSave"), Snackbars.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_SHORT);
|
||||
Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_LONG);
|
||||
this._showList();
|
||||
},
|
||||
|
||||
|
|