mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Merge fx-team to m-c
This commit is contained in:
commit
98152d91df
@ -76,9 +76,6 @@ XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
|
||||
'nsICaptivePortalDetector');
|
||||
#endif
|
||||
|
||||
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
let { RootActor } = devtools.require("devtools/server/actors/root");
|
||||
|
||||
function getContentWindow() {
|
||||
return shell.contentBrowser.contentWindow;
|
||||
}
|
||||
@ -971,6 +968,8 @@ let RemoteDebugger = {
|
||||
deviceActor: DebuggerServer.globalActorFactories.deviceActor,
|
||||
} : DebuggerServer.globalActorFactories
|
||||
};
|
||||
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
let { RootActor } = devtools.require("devtools/server/actors/root");
|
||||
let root = new RootActor(connection, parameters);
|
||||
root.applicationType = "operating-system";
|
||||
return root;
|
||||
|
@ -278,7 +278,7 @@ pref("browser.startup.page", 1);
|
||||
pref("browser.startup.homepage", "chrome://branding/locale/browserconfig.properties");
|
||||
|
||||
pref("browser.slowStartup.notificationDisabled", false);
|
||||
pref("browser.slowStartup.timeThreshold", 60000);
|
||||
pref("browser.slowStartup.timeThreshold", 45000);
|
||||
pref("browser.slowStartup.maxSamples", 5);
|
||||
|
||||
// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
|
||||
@ -1568,6 +1568,8 @@ pref("browser.cache.frecency_experiment", 0);
|
||||
|
||||
pref("browser.translation.detectLanguage", false);
|
||||
pref("browser.translation.neverForLanguages", "");
|
||||
// Show the translation UI bits, like the info bar, notification icon and preferences.
|
||||
pref("browser.translation.ui.show", false);
|
||||
|
||||
// Telemetry experiments settings.
|
||||
pref("experiments.enabled", true);
|
||||
|
@ -4428,7 +4428,8 @@ var TabsInTitlebar = {
|
||||
|
||||
// Get the full height of the tabs toolbar:
|
||||
let tabsToolbar = $("TabsToolbar");
|
||||
let fullTabsHeight = rect(tabsToolbar).height;
|
||||
let tabsStyles = window.getComputedStyle(tabsToolbar);
|
||||
let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
|
||||
// Buttons first:
|
||||
let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
|
||||
|
||||
@ -4437,15 +4438,11 @@ var TabsInTitlebar = {
|
||||
// No need to look up the menubar stuff on OS X:
|
||||
let menuHeight = 0;
|
||||
let fullMenuHeight = 0;
|
||||
// Instead, look up the titlebar padding:
|
||||
let titlebarPadding = parseInt(window.getComputedStyle(titlebar).paddingTop, 10);
|
||||
#else
|
||||
// Otherwise, get the height and margins separately for the menubar
|
||||
let menuHeight = rect(menubar).height;
|
||||
let menuStyles = window.getComputedStyle(menubar);
|
||||
let fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
|
||||
let tabsStyles = window.getComputedStyle(tabsToolbar);
|
||||
fullTabsHeight += verticalMargins(tabsStyles);
|
||||
#endif
|
||||
|
||||
// And get the height of what's in the titlebar:
|
||||
@ -4488,7 +4485,6 @@ var TabsInTitlebar = {
|
||||
// We need to increase the titlebar content's outer height (ie including margins)
|
||||
// to match the tab and menu height:
|
||||
let extraMargin = tabAndMenuHeight - titlebarContentHeight;
|
||||
// On non-OSX, we can just use bottom margin:
|
||||
#ifndef XP_MACOSX
|
||||
titlebarContent.style.marginBottom = extraMargin + "px";
|
||||
#endif
|
||||
|
@ -3704,6 +3704,7 @@ function XULWidgetSingleWrapper(aWidgetId, aNode, aDocument) {
|
||||
}
|
||||
|
||||
const LAZY_RESIZE_INTERVAL_MS = 200;
|
||||
const OVERFLOW_PANEL_HIDE_DELAY_MS = 500
|
||||
|
||||
function OverflowableToolbar(aToolbarNode) {
|
||||
this._toolbar = aToolbarNode;
|
||||
@ -3747,6 +3748,8 @@ OverflowableToolbar.prototype = {
|
||||
let chevronId = this._toolbar.getAttribute("overflowbutton");
|
||||
this._chevron = doc.getElementById(chevronId);
|
||||
this._chevron.addEventListener("command", this);
|
||||
this._chevron.addEventListener("dragover", this);
|
||||
this._chevron.addEventListener("dragend", this);
|
||||
|
||||
let panelId = this._toolbar.getAttribute("overflowpanel");
|
||||
this._panel = doc.getElementById(panelId);
|
||||
@ -3781,6 +3784,8 @@ OverflowableToolbar.prototype = {
|
||||
window.gNavToolbox.removeEventListener("customizationstarting", this);
|
||||
window.gNavToolbox.removeEventListener("aftercustomization", this);
|
||||
this._chevron.removeEventListener("command", this);
|
||||
this._chevron.removeEventListener("dragover", this);
|
||||
this._chevron.removeEventListener("dragend", this);
|
||||
this._panel.removeEventListener("popuphiding", this);
|
||||
CustomizableUI.removeListener(this);
|
||||
CustomizableUIInternal.removePanelCloseListeners(this._panel);
|
||||
@ -3788,8 +3793,8 @@ OverflowableToolbar.prototype = {
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
switch(aEvent.type) {
|
||||
case "resize":
|
||||
this._onResize(aEvent);
|
||||
case "aftercustomization":
|
||||
this._enable();
|
||||
break;
|
||||
case "command":
|
||||
if (aEvent.target == this._chevron) {
|
||||
@ -3798,15 +3803,20 @@ OverflowableToolbar.prototype = {
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
break;
|
||||
case "popuphiding":
|
||||
this._onPanelHiding(aEvent);
|
||||
break;
|
||||
case "customizationstarting":
|
||||
this._disable();
|
||||
break;
|
||||
case "aftercustomization":
|
||||
this._enable();
|
||||
case "dragover":
|
||||
this._showWithTimeout();
|
||||
break;
|
||||
case "dragend":
|
||||
this._panel.hidePopup();
|
||||
break;
|
||||
case "popuphiding":
|
||||
this._onPanelHiding(aEvent);
|
||||
break;
|
||||
case "resize":
|
||||
this._onResize(aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
@ -3824,8 +3834,11 @@ OverflowableToolbar.prototype = {
|
||||
this._panel.openPopup(anchor || this._chevron);
|
||||
this._chevron.open = true;
|
||||
|
||||
this._panel.addEventListener("popupshown", function onPopupShown() {
|
||||
let overflowableToolbarInstance = this;
|
||||
this._panel.addEventListener("popupshown", function onPopupShown(aEvent) {
|
||||
this.removeEventListener("popupshown", onPopupShown);
|
||||
this.addEventListener("dragover", overflowableToolbarInstance);
|
||||
this.addEventListener("dragend", overflowableToolbarInstance);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
@ -3843,6 +3856,8 @@ OverflowableToolbar.prototype = {
|
||||
|
||||
_onPanelHiding: function(aEvent) {
|
||||
this._chevron.open = false;
|
||||
this._panel.removeEventListener("dragover", this);
|
||||
this._panel.removeEventListener("dragend", this);
|
||||
let doc = aEvent.target.ownerDocument;
|
||||
let contextMenu = doc.getElementById(this._panel.getAttribute("context"));
|
||||
gELS.removeSystemEventListener(contextMenu, 'command', this, true);
|
||||
@ -4077,6 +4092,21 @@ OverflowableToolbar.prototype = {
|
||||
}
|
||||
return this._target;
|
||||
},
|
||||
|
||||
_hideTimeoutId: null,
|
||||
_showWithTimeout: function() {
|
||||
this.show();
|
||||
let window = this._toolbar.ownerDocument.defaultView;
|
||||
if (this._hideTimeoutId) {
|
||||
window.clearTimeout(this._hideTimeoutId);
|
||||
this._hideTimeoutId = null;
|
||||
}
|
||||
this._hideTimeoutId = window.setTimeout(() => {
|
||||
if (!this._panel.firstChild.mozMatchesSelector(":hover")) {
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
}, OVERFLOW_PANEL_HIDE_DELAY_MS);
|
||||
},
|
||||
};
|
||||
|
||||
CustomizableUIInternal.initialize();
|
||||
|
@ -69,6 +69,7 @@ skip-if = e10s # Bug ?????? - test uses promiseTabLoadEvent() which isn't e10s f
|
||||
[browser_948985_non_removable_defaultArea.js]
|
||||
[browser_952963_areaType_getter_no_area.js]
|
||||
[browser_956602_remove_special_widget.js]
|
||||
[browser_962069_drag_to_overflow_chevron.js]
|
||||
[browser_962884_opt_in_disable_hyphens.js]
|
||||
[browser_963639_customizing_attribute_non_customizable_toolbar.js]
|
||||
[browser_967000_button_charEncoding.js]
|
||||
|
@ -31,7 +31,7 @@ add_task(function() {
|
||||
setToolbarVisibility(bookmarksToolbar, true);
|
||||
setToolbarVisibility(navbar, false);
|
||||
isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
|
||||
is(navbar.getBoundingClientRect().height, 1, "navbar should have a height=1 (due to border)");
|
||||
ok(navbar.getBoundingClientRect().height <= 1, "navbar should have height=0 or 1 (due to border)");
|
||||
is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
|
||||
|
||||
yield startCustomizing();
|
||||
|
@ -0,0 +1,39 @@
|
||||
/* 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";
|
||||
|
||||
let originalWindowWidth;
|
||||
|
||||
// Drag to overflow chevron should open the overflow panel.
|
||||
add_task(function*() {
|
||||
originalWindowWidth = window.outerWidth;
|
||||
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||
let oldChildCount = navbar.customizationTarget.childElementCount;
|
||||
window.resizeTo(400, window.outerHeight);
|
||||
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
|
||||
|
||||
let widgetOverflowPanel = document.getElementById("widget-overflow");
|
||||
let panelShownPromise = promisePanelElementShown(window, widgetOverflowPanel);
|
||||
let identityBox = document.getElementById("identity-box");
|
||||
let overflowChevron = document.getElementById("nav-bar-overflow-button");
|
||||
ChromeUtils.synthesizeDrop(identityBox, overflowChevron, [], null);
|
||||
yield panelShownPromise;
|
||||
|
||||
ok(true, "Overflow panel is shown.");
|
||||
|
||||
let panelHiddenPromise = promisePanelElementHidden(window, widgetOverflowPanel);
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield panelHiddenPromise;
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||
});
|
@ -16,6 +16,13 @@ var gContentPane = {
|
||||
menulist.insertItemAt(0, "", "", "");
|
||||
menulist.selectedIndex = 0;
|
||||
}
|
||||
|
||||
// Show translation preferences if we may:
|
||||
const prefName = "browser.translation.ui.show";
|
||||
if (Services.prefs.getBoolPref(prefName)) {
|
||||
let row = document.getElementById("translationBox");
|
||||
row.removeAttribute("hidden");
|
||||
}
|
||||
},
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
|
@ -143,7 +143,7 @@
|
||||
accesskey="&chooseButton.accesskey;"
|
||||
oncommand="gContentPane.showLanguages();"/>
|
||||
</row>
|
||||
<row hidden="true">
|
||||
<row id="translationBox" hidden="true">
|
||||
<checkbox id="translate" preference="browser.translation.detectLanguage" flex="1"
|
||||
label="&translateWebPages.label;" accesskey="&translateWebPages.accesskey;"
|
||||
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
||||
|
@ -437,9 +437,6 @@
|
||||
|
||||
<separator/>
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
<vbox>
|
||||
#endif
|
||||
<hbox>
|
||||
<button id="viewCertificatesButton"
|
||||
label="&viewCerts.label;" accesskey="&viewCerts.accesskey;"
|
||||
@ -448,18 +445,11 @@
|
||||
<button id="verificationButton"
|
||||
label="&verify2.label;" accesskey="&verify2.accesskey;"
|
||||
oncommand="gAdvancedPane.showOCSP();"/>
|
||||
#ifdef XP_MACOSX
|
||||
</hbox>
|
||||
<hbox>
|
||||
#endif
|
||||
<button id="viewSecurityDevicesButton"
|
||||
label="&viewSecurityDevices.label;" accesskey="&viewSecurityDevices.accesskey;"
|
||||
oncommand="gAdvancedPane.showSecurityDevices();"
|
||||
preference="security.disable_button.openDeviceManager"/>
|
||||
</hbox>
|
||||
#ifdef XP_MACOSX
|
||||
</vbox>
|
||||
#endif
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
@ -15,6 +15,13 @@ var gContentPane = {
|
||||
menulist.insertItemAt(0, "", "", "");
|
||||
menulist.selectedIndex = 0;
|
||||
}
|
||||
|
||||
// Show translation preferences if we may:
|
||||
const prefName = "browser.translation.ui.show";
|
||||
if (Services.prefs.getBoolPref(prefName)) {
|
||||
let row = document.getElementById("translationBox");
|
||||
row.removeAttribute("hidden");
|
||||
}
|
||||
},
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
|
@ -8,6 +8,8 @@ this.EXPORTED_SYMBOLS = ["Translation"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
const TRANSLATION_PREF_SHOWUI = "browser.translation.ui.show";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.Translation = {
|
||||
@ -26,6 +28,9 @@ this.Translation = {
|
||||
},
|
||||
|
||||
languageDetected: function(aBrowser, aDetectedLanguage) {
|
||||
if (!Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI))
|
||||
return;
|
||||
|
||||
if (this.supportedSourceLanguages.indexOf(aDetectedLanguage) != -1 &&
|
||||
aDetectedLanguage != this.defaultTargetLanguage) {
|
||||
if (!aBrowser.translationUI)
|
||||
|
@ -67,7 +67,12 @@ TranslationContentHandler.prototype = {
|
||||
Cu.import("resource:///modules/translation/TranslationDocument.jsm");
|
||||
Cu.import("resource:///modules/translation/BingTranslator.jsm");
|
||||
|
||||
let translationDocument = new TranslationDocument(this.global.content.document);
|
||||
// If a TranslationDocument already exists for this document, it should
|
||||
// be used instead of creating a new one so that we can use the original
|
||||
// content of the page for the new translation instead of the newly
|
||||
// translated text.
|
||||
let translationDocument = this.global.content.translationDocument ||
|
||||
new TranslationDocument(this.global.content.document);
|
||||
let bingTranslation = new BingTranslation(translationDocument,
|
||||
msg.data.from,
|
||||
msg.data.to);
|
||||
|
@ -115,21 +115,27 @@ this.TranslationDocument.prototype = {
|
||||
* Besides generating the string, it's also stored in the "original"
|
||||
* field of the TranslationItem object, which needs to be stored for
|
||||
* later to be used in the "Show Original" functionality.
|
||||
* If this function had already been called for the given item (determined
|
||||
* by the presence of the "original" array in the item), the text will
|
||||
* be regenerated from the "original" data instead of from the related
|
||||
* DOM nodes (because the nodes might contain translated data).
|
||||
*
|
||||
* @param item A TranslationItem object
|
||||
*
|
||||
* @returns A string representation of the TranslationItem.
|
||||
*/
|
||||
generateTextForItem: function(item) {
|
||||
if (item.original) {
|
||||
return regenerateTextFromOriginalHelper(item);
|
||||
}
|
||||
|
||||
if (item.isSimpleRoot) {
|
||||
let text = item.nodeRef.firstChild.nodeValue.trim();
|
||||
item.original = [text];
|
||||
return text;
|
||||
}
|
||||
|
||||
let localName = item.isRoot ? "div" : "b";
|
||||
let str = '<' + localName + ' id="n' + item.id + '">';
|
||||
|
||||
let str = "";
|
||||
item.original = [];
|
||||
|
||||
for (let child of item.nodeRef.childNodes) {
|
||||
@ -141,15 +147,18 @@ this.TranslationDocument.prototype = {
|
||||
}
|
||||
|
||||
let objInMap = this.itemsMap.get(child);
|
||||
if (objInMap) {
|
||||
if (objInMap && !objInMap.isRoot) {
|
||||
// If this childNode is present in the itemsMap, it means
|
||||
// it's a translation node: it has useful content for translation.
|
||||
// In this case, we need to stringify this node.
|
||||
// However, if this item is a root, we should skip it here in this
|
||||
// object's child list (and just add a placeholder for it), because
|
||||
// it will be stringfied separately for being a root.
|
||||
item.original.push(objInMap);
|
||||
str += this.generateTextForItem(objInMap);
|
||||
} else {
|
||||
// Otherwise, if this node doesn't contain any useful content,
|
||||
// we can simply replace it by a placeholder node.
|
||||
// or if it is a root itself, we can replace it with a placeholder node.
|
||||
// We can't simply eliminate this node from our string representation
|
||||
// because that could change the HTML structure (e.g., it would
|
||||
// probably merge two separate text nodes).
|
||||
@ -157,8 +166,7 @@ this.TranslationDocument.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
str += '</' + localName + '>';
|
||||
return str;
|
||||
return generateTranslationHtmlForItem(item, str);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -316,6 +324,58 @@ TranslationItem.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the outer HTML representation for a given item.
|
||||
*
|
||||
* @param item A TranslationItem object.
|
||||
* param content The inner content for this item.
|
||||
* @returns string The outer HTML needed for translation
|
||||
* of this item.
|
||||
*/
|
||||
function generateTranslationHtmlForItem(item, content) {
|
||||
let localName = item.isRoot ? "div" : "b";
|
||||
return '<' + localName + ' id="n' + item.id + '">' +
|
||||
content +
|
||||
"</" + localName + ">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the text string that represents a TranslationItem object,
|
||||
* with data from its "original" array. The array must have already
|
||||
* been created by TranslationDocument.generateTextForItem().
|
||||
*
|
||||
* @param item A TranslationItem object
|
||||
*
|
||||
* @returns A string representation of the TranslationItem.
|
||||
*/
|
||||
function regenerateTextFromOriginalHelper(item) {
|
||||
if (item.isSimpleRoot) {
|
||||
return item.original[0];
|
||||
}
|
||||
|
||||
let str = "";
|
||||
|
||||
let wasLastItemText = false;
|
||||
for (let child of item.original) {
|
||||
if (child instanceof TranslationItem) {
|
||||
str += regenerateTextFromOriginalHelper(child);
|
||||
wasLastItemText = false;
|
||||
} else {
|
||||
// The non-significant elements (which were replaced with <br/>
|
||||
// during the first call to generateTextForItem) are not stored
|
||||
// in the original array. If they are not properly re-generated,
|
||||
// two adjacent text nodes would be merged into one.
|
||||
if (wasLastItemText) {
|
||||
str += "<br/>";
|
||||
}
|
||||
str += child;
|
||||
wasLastItemText = true;
|
||||
}
|
||||
}
|
||||
|
||||
return generateTranslationHtmlForItem(item, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to parse a HTML doc result.
|
||||
* How it works:
|
||||
|
@ -52,7 +52,6 @@
|
||||
}
|
||||
|
||||
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
|
||||
margin-top: -@tabToolbarNavbarOverlap@; /* Move up into the TabsToolbar */
|
||||
/* Position the toolbar above the bottom of background tabs */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@ -1768,6 +1767,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin-bottom: -@tabToolbarNavbarOverlap@;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -244,7 +244,6 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
|
||||
skin/classic/browser/devtools/itemArrow-rtl.svg (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (../shared/devtools/images/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (../shared/devtools/images/noise.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (../shared/devtools/images/dropmarker.png)
|
||||
skin/classic/browser/devtools/layoutview.css (../shared/devtools/layoutview.css)
|
||||
|
@ -110,7 +110,6 @@ toolbarseparator {
|
||||
}
|
||||
|
||||
#TabsToolbar:not([collapsed="true"]) + #nav-bar {
|
||||
margin-top: -@tabToolbarNavbarOverlap@; /* Move up into the TabsToolbar */
|
||||
/* Position the toolbar above the bottom of background tabs */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@ -2797,6 +2796,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
position: relative;
|
||||
-moz-appearance: none;
|
||||
background-repeat: repeat-x;
|
||||
margin-bottom: -@tabToolbarNavbarOverlap@;
|
||||
}
|
||||
|
||||
#TabsToolbar:not(:-moz-lwtheme) {
|
||||
|
@ -364,7 +364,6 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
|
||||
skin/classic/browser/devtools/itemArrow-rtl.svg (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (../shared/devtools/images/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (../shared/devtools/images/noise.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (../shared/devtools/images/dropmarker.png)
|
||||
skin/classic/browser/devtools/layoutview.css (../shared/devtools/layoutview.css)
|
||||
|
@ -18,12 +18,12 @@
|
||||
}
|
||||
|
||||
.theme-dark .notice-container {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .notice-container {
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
background-color: #f0f1f2; /* Toolbars */
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
@ -439,13 +439,11 @@
|
||||
|
||||
.theme-dark #snapshot-filmstrip {
|
||||
border-top: 1px solid #000;
|
||||
background-image: url(background-noise-toolbar.png);
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light #snapshot-filmstrip {
|
||||
border-top: 1px solid #aaa;
|
||||
background-image: url(background-noise-toolbar.png);
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
min-height: 32px;
|
||||
background-image: url(devtools/background-noise-toolbar.png), linear-gradient(#303840, #2d3640);
|
||||
background-color: #343C45; /* Toolbars */
|
||||
border-top: 1px solid #060a0d;
|
||||
box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(206,37%,4%,.1) inset;
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
@ -417,7 +417,7 @@ label.requests-menu-status-code {
|
||||
/* Network request details tabpanels */
|
||||
|
||||
.theme-dark .tabpanel-content {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
@ -461,12 +461,12 @@ label.requests-menu-status-code {
|
||||
}
|
||||
|
||||
.theme-dark #response-content-info-header {
|
||||
background: url(background-noise-toolbar.png), #eb5368; /* Red highlight */
|
||||
background-color: #eb5368; /* Red highlight */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light #response-content-info-header {
|
||||
background: url(background-noise-toolbar.png), #ed2655; /* Red highlight */
|
||||
background-color: #ed2655; /* Red highlight */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
@ -531,13 +531,13 @@ label.requests-menu-status-code {
|
||||
.theme-dark #requests-menu-footer {
|
||||
border-top: 1px solid @table_itemDarkStartBorder@;
|
||||
box-shadow: 0 1px 0 @table_itemDarkEndBorder@ inset;
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.theme-light #requests-menu-footer {
|
||||
border-top: 1px solid @table_itemLightStartBorder@;
|
||||
box-shadow: 0 1px 0 @table_itemLightEndBorder@ inset;
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
background-color: #f0f1f2; /* Toolbars */
|
||||
}
|
||||
|
||||
.requests-menu-footer-button,
|
||||
@ -656,11 +656,11 @@ label.requests-menu-status-code {
|
||||
}
|
||||
|
||||
.theme-dark #network-statistics-charts {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.theme-light #network-statistics-charts {
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
background-color: #f0f1f2; /* Toolbars */
|
||||
}
|
||||
|
||||
#network-statistics-charts .pie-chart-container {
|
||||
|
@ -9,12 +9,12 @@
|
||||
}
|
||||
|
||||
.theme-dark .notice-container {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .notice-container {
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
background-color: #f0f1f2; /* Toolbars */
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
.theme-dark .splitview-nav-container {
|
||||
background: url(background-noise-toolbar.png), #343c45;
|
||||
background-color: #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.splitview-nav {
|
||||
|
@ -8,12 +8,12 @@
|
||||
}
|
||||
|
||||
.theme-dark .notice-container {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .notice-container {
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
background-color: #f0f1f2; /* Toolbars */
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
|
@ -372,7 +372,7 @@
|
||||
|
||||
.theme-dark .side-menu-widget-container,
|
||||
.theme-dark .side-menu-widget-empty-text {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.theme-dark .side-menu-widget-container:-moz-locale-dir(ltr),
|
||||
@ -522,11 +522,11 @@
|
||||
}
|
||||
|
||||
.theme-dark .side-menu-widget-item-other {
|
||||
background: url(background-noise-toolbar.png), rgba(0,0,0,.1);
|
||||
background-color: rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.theme-light .side-menu-widget-item-other {
|
||||
background: url(background-noise-toolbar.png), rgba(128,128,128,.1);
|
||||
background-color: rgba(128,128,128,.1);
|
||||
}
|
||||
|
||||
.theme-dark .side-menu-widget-item.selected .side-menu-widget-item-other {
|
||||
@ -568,7 +568,7 @@
|
||||
}
|
||||
|
||||
.theme-dark .side-menu-widget-empty-text {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
color: #b6babf; /* Foreground (Text) - Grey */
|
||||
}
|
||||
|
||||
@ -1067,7 +1067,7 @@
|
||||
|
||||
.theme-dark .table-widget-body,
|
||||
.theme-dark .table-widget-empty-text {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
background-color: #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.theme-dark .table-widget-body:-moz-locale-dir(ltr) {
|
||||
|
@ -282,7 +282,6 @@ browser.jar:
|
||||
skin/classic/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
|
||||
skin/classic/browser/devtools/itemArrow-rtl.svg (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (../shared/devtools/images/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (../shared/devtools/images/noise.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (../shared/devtools/images/dropmarker.png)
|
||||
skin/classic/browser/devtools/layoutview.css (../shared/devtools/layoutview.css)
|
||||
@ -674,7 +673,6 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
|
||||
skin/classic/aero/browser/devtools/itemArrow-rtl.svg (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/aero/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/aero/browser/devtools/background-noise-toolbar.png (../shared/devtools/images/background-noise-toolbar.png)
|
||||
skin/classic/aero/browser/devtools/noise.png (../shared/devtools/images/noise.png)
|
||||
skin/classic/aero/browser/devtools/dropmarker.png (../shared/devtools/images/dropmarker.png)
|
||||
skin/classic/aero/browser/devtools/layoutview.css (../shared/devtools/layoutview.css)
|
||||
|
13
configure.in
13
configure.in
@ -4051,6 +4051,19 @@ if test -z "$MOZ_GOOGLE_API_KEY"; then
|
||||
fi
|
||||
AC_SUBST(MOZ_GOOGLE_API_KEY)
|
||||
|
||||
# Allow specifying a Bing API key file that contains the client ID and the
|
||||
# secret key to be used for the Bing Translation API requests.
|
||||
MOZ_ARG_WITH_STRING(bing-api-keyfile,
|
||||
[ --with-bing-api-keyfile=file Use the client id and secret key contained in the given keyfile for Bing API requests],
|
||||
[MOZ_BING_API_CLIENTID=`cat $withval | cut -f 1 -d " "`
|
||||
MOZ_BING_API_KEY=`cat $withval | cut -f 2 -d " "`])
|
||||
if test -z "$MOZ_BING_API_CLIENTID"; then
|
||||
MOZ_BING_API_CLIENTID=no-bing-api-clientid
|
||||
MOZ_BING_API_KEY=no-bing-api-key
|
||||
fi
|
||||
AC_SUBST(MOZ_BING_API_CLIENTID)
|
||||
AC_SUBST(MOZ_BING_API_KEY)
|
||||
|
||||
# Allow the application to influence configure with a confvars.sh script.
|
||||
AC_MSG_CHECKING([if app-specific confvars.sh exists])
|
||||
if test -f "${srcdir}/${MOZ_BUILD_APP}/confvars.sh" ; then
|
||||
|
@ -798,7 +798,7 @@ public abstract class GeckoApp
|
||||
});
|
||||
}
|
||||
|
||||
protected ButtonToast getButtonToast() {
|
||||
public ButtonToast getButtonToast() {
|
||||
if (mToast != null) {
|
||||
return mToast;
|
||||
}
|
||||
|
@ -438,6 +438,8 @@ public class BrowserContract {
|
||||
public static final class SuggestedSites implements CommonColumns, URLColumns {
|
||||
private SuggestedSites() {}
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "suggestedsites");
|
||||
|
||||
public static final String IMAGE_URL = "image_url";
|
||||
public static final String BG_COLOR = "bg_color";
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
package org.mozilla.gecko.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MatrixCursor.RowBuilder;
|
||||
@ -22,9 +23,11 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.util.RawResource;
|
||||
|
||||
/**
|
||||
@ -150,6 +153,11 @@ public class SuggestedSites {
|
||||
return sites;
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
|
||||
return prefs.getBoolean(GeckoPreferences.PREFS_SUGGESTED_SITES, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Cursor} with the list of suggested websites.
|
||||
*
|
||||
@ -166,14 +174,20 @@ public class SuggestedSites {
|
||||
* @param excludeUrls list of URLs to be excluded from the list.
|
||||
*/
|
||||
public Cursor get(int limit, List<String> excludeUrls) {
|
||||
final MatrixCursor cursor = new MatrixCursor(COLUMNS);
|
||||
|
||||
// Return an empty cursor if suggested sites have been
|
||||
// disabled by the user.
|
||||
if (!isEnabled()) {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
List<Site> sites = cachedSites.get();
|
||||
if (sites == null) {
|
||||
Log.d(LOGTAG, "No cached sites, refreshing.");
|
||||
sites = refresh();
|
||||
}
|
||||
|
||||
final MatrixCursor cursor = new MatrixCursor(COLUMNS);
|
||||
|
||||
// Return empty cursor if there was an error when
|
||||
// loading the suggested sites or the list is empty.
|
||||
if (sites == null || sites.isEmpty()) {
|
||||
@ -199,6 +213,9 @@ public class SuggestedSites {
|
||||
row.add(site.bgColor);
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(context.getContentResolver(),
|
||||
BrowserContract.SuggestedSites.CONTENT_URI);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
@ -6,11 +6,13 @@
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.EditBookmarkDialog;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.ReaderModeUtils;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
@ -21,6 +23,7 @@ import org.mozilla.gecko.home.TopSitesGridView.TopSitesGridContextMenuInfo;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.UiAsyncTask;
|
||||
import org.mozilla.gecko.widget.ButtonToast;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@ -176,8 +179,10 @@ abstract class HomeFragment extends Fragment {
|
||||
}
|
||||
|
||||
int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND;
|
||||
if (item.getItemId() == R.id.home_open_private_tab)
|
||||
final boolean isPrivate = (item.getItemId() == R.id.home_open_private_tab);
|
||||
if (isPrivate) {
|
||||
flags |= Tabs.LOADURL_PRIVATE;
|
||||
}
|
||||
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
|
||||
|
||||
@ -185,8 +190,26 @@ abstract class HomeFragment extends Fragment {
|
||||
|
||||
// Some pinned site items have "user-entered" urls. URLs entered in the PinSiteDialog are wrapped in
|
||||
// a special URI until we can get a valid URL. If the url is a user-entered url, decode the URL before loading it.
|
||||
Tabs.getInstance().loadUrl(decodeUserEnteredUrl(url), flags);
|
||||
Toast.makeText(context, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
|
||||
final Tab newTab = Tabs.getInstance().loadUrl(decodeUserEnteredUrl(url), flags);
|
||||
final int newTabId = newTab.getId(); // We don't want to hold a reference to the Tab.
|
||||
|
||||
final String message = isPrivate ?
|
||||
getResources().getString(R.string.new_private_tab_opened) :
|
||||
getResources().getString(R.string.new_tab_opened);
|
||||
final GeckoApp geckoApp = (GeckoApp) context;
|
||||
geckoApp.getButtonToast().show(false,
|
||||
message,
|
||||
null,
|
||||
R.drawable.select_opened_tab,
|
||||
new ButtonToast.ToastListener() {
|
||||
@Override
|
||||
public void onButtonClicked() {
|
||||
Tabs.getInstance().selectTab(newTabId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToastHidden(ButtonToast.ReasonHidden reason) { }
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ $(dir-strings-xml)/strings.xml: $(strings-xml-preqs)
|
||||
$< \
|
||||
-o $@)
|
||||
|
||||
suggestedsites-srcdir := $(dir $(abspath $(call MERGE_FILE,suggestedsites/list.txt)))
|
||||
suggestedsites-srcdir := $(if $(filter en-US,$(AB_CD)),,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/mobile/chrome)
|
||||
|
||||
# Determine the ../res/raw[-*] path. This can be ../res/raw when no
|
||||
# locale is explicitly specified.
|
||||
@ -105,5 +105,5 @@ $(suggestedsites-dstdir-raw)/suggestedsites.json: FORCE
|
||||
--android-package-name=$(ANDROID_PACKAGE_NAME) \
|
||||
--resources=$(srcdir)/../resources \
|
||||
$(if $(filter en-US,$(AB_CD)),,--srcdir=$(suggestedsites-srcdir)) \
|
||||
--srcdir=$(addprefix $(srcdir)/,en-US/suggestedsites) \
|
||||
--srcdir=$(topsrcdir)/mobile/locales/en-US/chrome \
|
||||
$@)
|
||||
|
@ -62,6 +62,7 @@
|
||||
We can't use android plural forms, sadly. See bug #753859. -->
|
||||
<!ENTITY num_tabs2 "&formatD; tabs">
|
||||
<!ENTITY new_tab_opened "New tab opened">
|
||||
<!ENTITY new_private_tab_opened "New private tab opened">
|
||||
|
||||
<!ENTITY settings "Settings">
|
||||
<!ENTITY settings_title "Settings">
|
||||
@ -115,6 +116,8 @@
|
||||
<!ENTITY pref_home_updates "Automatic updates">
|
||||
<!ENTITY pref_home_updates_enabled "Enabled">
|
||||
<!ENTITY pref_home_updates_wifi "Only over Wi-Fi">
|
||||
<!ENTITY pref_home_suggested_sites "Show site suggestions">
|
||||
<!ENTITY pref_home_suggested_sites_summary "Display shortcuts to sites on your homepage that we think you might find interesting">
|
||||
|
||||
<!-- Localization note: These are shown in the left sidebar on tablets -->
|
||||
<!ENTITY pref_header_customize "Customize">
|
||||
|
@ -1,3 +0,0 @@
|
||||
{ "title": "Firefox Marketplace",
|
||||
"url": "https://marketplace.firefox.com/",
|
||||
"bgcolor": "#0095dd" }
|
@ -1,2 +0,0 @@
|
||||
mozilla
|
||||
fxmarketplace
|
@ -1,3 +0,0 @@
|
||||
{ "title": "Mozilla",
|
||||
"url": "http://www.mozilla.org/en-US/",
|
||||
"bgcolor": "#c13832" }
|
@ -415,6 +415,7 @@ gbjar.sources += [
|
||||
'widget/DoorHanger.java',
|
||||
'widget/EllipsisTextView.java',
|
||||
'widget/FaviconView.java',
|
||||
'widget/FloatingHintEditText.java',
|
||||
'widget/FlowLayout.java',
|
||||
'widget/GeckoActionProvider.java',
|
||||
'widget/GeckoPopupMenu.java',
|
||||
|
@ -29,9 +29,11 @@ import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
|
||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
|
||||
import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
|
||||
import org.mozilla.gecko.home.HomePanelPicker;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.FloatingHintEditText;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
@ -40,6 +42,7 @@ import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@ -112,6 +115,7 @@ OnSharedPreferenceChangeListener
|
||||
private static final String PREFS_BROWSER_LOCALE = "locale";
|
||||
|
||||
public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
|
||||
public static final String PREFS_SUGGESTED_SITES = NON_PREF_PREFIX + "home_suggested_sites";
|
||||
|
||||
// These values are chosen to be distinct from other Activity constants.
|
||||
private static final int REQUEST_CODE_PREF_SCREEN = 5;
|
||||
@ -901,6 +905,12 @@ OnSharedPreferenceChangeListener
|
||||
if (PREFS_BROWSER_LOCALE.equals(key)) {
|
||||
onLocaleSelected(BrowserLocaleManager.getLanguageTag(lastLocale),
|
||||
sharedPreferences.getString(key, null));
|
||||
} else if (PREFS_SUGGESTED_SITES.equals(key)) {
|
||||
final ContentResolver cr = getApplicationContext().getContentResolver();
|
||||
|
||||
// This will force all active suggested sites cursors
|
||||
// to request a refresh (e.g. cursor loaders).
|
||||
cr.notifyChange(SuggestedSites.CONTENT_URI, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -962,7 +972,7 @@ OnSharedPreferenceChangeListener
|
||||
}
|
||||
|
||||
private EditText getTextBox(int aHintText) {
|
||||
EditText input = new EditText(GeckoAppShell.getContext());
|
||||
EditText input = new FloatingHintEditText(GeckoAppShell.getContext());
|
||||
int inputtype = InputType.TYPE_CLASS_TEXT;
|
||||
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
input.setInputType(inputtype);
|
||||
|
@ -12,6 +12,7 @@ import java.util.GregorianCalendar;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.widget.AllCapsTextView;
|
||||
import org.mozilla.gecko.widget.DateTimePicker;
|
||||
import org.mozilla.gecko.widget.FloatingHintEditText;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
@ -62,7 +63,7 @@ public class PromptInput {
|
||||
}
|
||||
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
EditText input = new EditText(context);
|
||||
EditText input = new FloatingHintEditText(context);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(mValue);
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Holo blue for focus -->
|
||||
<item android:state_focused="true" android:color="@color/text_color_hint_floating_focused" />
|
||||
|
||||
<!-- Default gray for all other states -->
|
||||
<item android:color="@color/text_color_hint"/>
|
||||
</selector>
|
Binary file not shown.
After Width: | Height: | Size: 875 B |
Binary file not shown.
After Width: | Height: | Size: 689 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="30"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="30" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="60"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="60" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="90"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="90" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="120"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="120" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="150"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="150" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="180"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="180" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="210"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="210" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="240"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="240" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="270"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="270" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="300"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="300" />
|
@ -0,0 +1,6 @@
|
||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:fromDegrees="330"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toDegrees="330" />
|
@ -0,0 +1,44 @@
|
||||
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:oneshot="false" >
|
||||
|
||||
<!-- Forgive me, but this is actually a reasonable way to get a
|
||||
frame-by-frame rotation animation without adding lots of small
|
||||
resources. The inner drawables rotate the master drawable. -->
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_030"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_060"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_090"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_120"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_150"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_180"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_210"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_240"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_270"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_300"
|
||||
android:duration="100"/>
|
||||
<item
|
||||
android:drawable="@drawable/tabs_synced_330"
|
||||
android:duration="100"/>
|
||||
|
||||
</animation-list>
|
@ -9,20 +9,23 @@
|
||||
android:orientation="vertical"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<EditText android:id="@+id/edit_bookmark_name"
|
||||
<org.mozilla.gecko.widget.FloatingHintEditText
|
||||
android:id="@+id/edit_bookmark_name"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:hint="@string/bookmark_edit_name"/>
|
||||
|
||||
<EditText android:id="@+id/edit_bookmark_location"
|
||||
<org.mozilla.gecko.widget.FloatingHintEditText
|
||||
android:id="@+id/edit_bookmark_location"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:hint="@string/bookmark_edit_location"
|
||||
android:inputType="textNoSuggestions"/>
|
||||
|
||||
<EditText android:id="@+id/edit_bookmark_keyword"
|
||||
<org.mozilla.gecko.widget.FloatingHintEditText
|
||||
android:id="@+id/edit_bookmark_keyword"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
|
@ -60,6 +60,7 @@
|
||||
<item name="android:actionModeCopyDrawable">@drawable/ab_copy</item>
|
||||
<item name="android:actionModePasteDrawable">@drawable/ab_paste</item>
|
||||
<item name="android:actionModeSelectAllDrawable">@drawable/ab_select_all</item>
|
||||
<item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -244,5 +244,9 @@
|
||||
<attr name="ellipsizeAtLine" format="integer"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="FloatingHintEditText">
|
||||
<attr name="floatingHintEditTextStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
<!-- Hint colors -->
|
||||
<color name="text_color_hint">#666666</color>
|
||||
<color name="text_color_hint_inverse">#7F828A</color>
|
||||
<color name="text_color_hint_floating_focused">#33b5e5</color>
|
||||
|
||||
<!-- Highlight colors -->
|
||||
<color name="text_color_highlight">#FF9500</color>
|
||||
|
@ -684,4 +684,8 @@
|
||||
<item name="android:minHeight">@dimen/menu_item_row_height</item>
|
||||
</style>
|
||||
|
||||
<style name="FloatingHintEditText" parent="android:style/Widget.EditText">
|
||||
<item name="android:paddingTop">0dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -92,6 +92,7 @@
|
||||
<item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
|
||||
<item name="menuItemActionModeStyle">@style/GeckoActionBar.Button</item>
|
||||
<item name="menuItemShareActionButtonStyle">@style/Widget.MenuItemSecondaryActionBar</item>
|
||||
<item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
|
||||
</style>
|
||||
|
||||
<style name="Gecko.Preferences" parent="GeckoPreferencesBase"/>
|
||||
|
@ -13,6 +13,11 @@
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_category_home_content_settings">
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.home_suggested_sites"
|
||||
android:title="@string/pref_home_suggested_sites"
|
||||
android:summary="@string/pref_home_suggested_sites_summary"
|
||||
android:defaultValue="true" />
|
||||
|
||||
<ListPreference android:key="home.sync.updateMode"
|
||||
android:title="@string/pref_home_updates"
|
||||
android:entries="@array/pref_home_updates_entries"
|
||||
|
@ -130,6 +130,8 @@
|
||||
<string name="pref_home_updates">&pref_home_updates;</string>
|
||||
<string name="pref_home_updates_enabled">&pref_home_updates_enabled;</string>
|
||||
<string name="pref_home_updates_wifi">&pref_home_updates_wifi;</string>
|
||||
<string name="pref_home_suggested_sites">&pref_home_suggested_sites;</string>
|
||||
<string name="pref_home_suggested_sites_summary">&pref_home_suggested_sites_summary;</string>
|
||||
|
||||
<string name="pref_header_customize">&pref_header_customize;</string>
|
||||
<string name="pref_header_display">&pref_header_display;</string>
|
||||
@ -234,6 +236,7 @@
|
||||
<string name="site_security">&site_security;</string>
|
||||
<string name="close_tab">&close_tab;</string>
|
||||
<string name="new_tab_opened">&new_tab_opened;</string>
|
||||
<string name="new_private_tab_opened">&new_private_tab_opened;</string>
|
||||
<string name="one_tab">&one_tab;</string>
|
||||
<string name="num_tabs">&num_tabs2;</string>
|
||||
<string name="addons">&addons;</string>
|
||||
|
@ -5,28 +5,41 @@
|
||||
package org.mozilla.gecko.tabspanel;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.TabsAccessor;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.tabspanel.TabsPanel.Panel;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.GeckoSwipeRefreshLayout;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Provides a container to wrap the list of synced tabs and provide swipe-to-refresh support. The
|
||||
* only child view should be an instance of {@link RemoteTabsList}.
|
||||
* Provides a container to wrap the list of synced tabs and provide
|
||||
* swipe-to-refresh support. The only child view should be an instance of
|
||||
* {@link RemoteTabsList}.
|
||||
*/
|
||||
public class RemoteTabsContainerPanel extends GeckoSwipeRefreshLayout
|
||||
implements TabsPanel.PanelView {
|
||||
private static final String[] STAGES_TO_SYNC_ON_REFRESH = new String[] { "tabs" };
|
||||
|
||||
/**
|
||||
* Refresh indicators (the swipe-to-refresh "laser show" and the spinning
|
||||
* icon) will never be shown for less than the following duration, in
|
||||
* milliseconds.
|
||||
*/
|
||||
private static final long MINIMUM_REFRESH_INDICATOR_DURATION_IN_MS = 12 * 100; // 12 frames, 100 ms each.
|
||||
|
||||
private final Context context;
|
||||
private final RemoteTabsSyncObserver syncListener;
|
||||
private TabsPanel panel;
|
||||
private RemoteTabsList list;
|
||||
|
||||
// Whether or not a sync status listener is attached.
|
||||
@ -54,8 +67,8 @@ public class RemoteTabsContainerPanel extends GeckoSwipeRefreshLayout
|
||||
|
||||
@Override
|
||||
public boolean canChildScrollUp() {
|
||||
// We are not supporting swipe-to-refresh for old sync. This disables the swipe gesture if
|
||||
// no FxA are detected.
|
||||
// We are not supporting swipe-to-refresh for old sync. This disables
|
||||
// the swipe gesture if no FxA are detected.
|
||||
if (FirefoxAccounts.firefoxAccountsExist(getContext())) {
|
||||
return super.canChildScrollUp();
|
||||
} else {
|
||||
@ -65,12 +78,18 @@ public class RemoteTabsContainerPanel extends GeckoSwipeRefreshLayout
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
this.panel = panel;
|
||||
list.setTabsPanel(panel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
// Start fetching remote tabs.
|
||||
TabsAccessor.getTabs(context, list);
|
||||
// The user can trigger a tabs sync, so we want to be very certain the
|
||||
// locally-persisted tabs are fresh (tab writes are batched and delayed).
|
||||
Tabs.getInstance().persistAllTabs();
|
||||
|
||||
if (!isListening) {
|
||||
isListening = true;
|
||||
FirefoxAccounts.addSyncStatusListener(syncListener);
|
||||
@ -103,6 +122,10 @@ public class RemoteTabsContainerPanel extends GeckoSwipeRefreshLayout
|
||||
}
|
||||
|
||||
private class RemoteTabsSyncObserver implements FirefoxAccounts.SyncStatusListener {
|
||||
// Written on the main thread, and read off the main thread, but no need
|
||||
// to synchronize.
|
||||
protected volatile long lastSyncStarted = 0;
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return RemoteTabsContainerPanel.this.getContext();
|
||||
@ -113,16 +136,50 @@ public class RemoteTabsContainerPanel extends GeckoSwipeRefreshLayout
|
||||
return FirefoxAccounts.getFirefoxAccount(getContext());
|
||||
}
|
||||
|
||||
public void onSyncFinished() {
|
||||
public void onSyncStarted() {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TabsAccessor.getTabs(context, list);
|
||||
setRefreshing(false);
|
||||
lastSyncStarted = System.currentTimeMillis();
|
||||
|
||||
// Get the sync icon and start the drawable's animation.
|
||||
final Drawable iconDrawable = panel.getIconDrawable(Panel.REMOTE_TABS);
|
||||
if (iconDrawable instanceof AnimationDrawable) {
|
||||
((AnimationDrawable) iconDrawable).start();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onSyncStarted() {}
|
||||
public void onSyncFinished() {
|
||||
final Handler uiHandler = ThreadUtils.getUiHandler();
|
||||
|
||||
// We want to update the list immediately ...
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TabsAccessor.getTabs(context, list);
|
||||
}
|
||||
});
|
||||
|
||||
// ... but we want the refresh indicators to persist for long enough
|
||||
// to be visible.
|
||||
final long last = lastSyncStarted;
|
||||
final long now = System.currentTimeMillis();
|
||||
final long delay = Math.max(0, MINIMUM_REFRESH_INDICATOR_DURATION_IN_MS - (now - last));
|
||||
|
||||
uiHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setRefreshing(false);
|
||||
|
||||
// Get the sync icon and stop the drawable's animation.
|
||||
final Drawable iconDrawable = panel.getIconDrawable(Panel.REMOTE_TABS);
|
||||
if (iconDrawable instanceof AnimationDrawable) {
|
||||
((AnimationDrawable) iconDrawable).stop();
|
||||
}
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import org.mozilla.gecko.widget.IconTabWidget;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
@ -140,7 +141,8 @@ public class TabsPanel extends LinearLayout
|
||||
mTabWidget.addTab(R.drawable.tabs_private, R.string.tabs_private);
|
||||
|
||||
if (!GeckoProfile.get(mContext).inGuestMode()) {
|
||||
mTabWidget.addTab(R.drawable.tabs_synced, R.string.tabs_synced);
|
||||
// n.b.: the animation does not start automatically.
|
||||
mTabWidget.addTab(R.drawable.tabs_synced_animation, R.string.tabs_synced);
|
||||
}
|
||||
|
||||
mTabWidget.setTabSelectionListener(this);
|
||||
@ -461,4 +463,13 @@ public class TabsPanel extends LinearLayout
|
||||
if (mLayoutChangeListener != null)
|
||||
mLayoutChangeListener.onTabsLayoutChange(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the Drawable icon corresponding to the given panel.
|
||||
* @param panel to fetch icon for.
|
||||
* @return Drawable instance, or null if no icon is being displayed, or the icon does not exist.
|
||||
*/
|
||||
public Drawable getIconDrawable(Panel panel) {
|
||||
return mTabWidget.getIconDrawable(panel.ordinal());
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,9 @@ import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ButtonToast {
|
||||
@SuppressWarnings("unused")
|
||||
private final static String LOGTAG = "GeckoButtonToast";
|
||||
|
||||
private final static int TOAST_DURATION = 5000;
|
||||
|
||||
private final View mView;
|
||||
@ -37,7 +39,6 @@ public class ButtonToast {
|
||||
private final Button mButton;
|
||||
private final Handler mHideHandler = new Handler();
|
||||
|
||||
private final ToastListener mListener;
|
||||
private final LinkedList<Toast> mQueue = new LinkedList<Toast>();
|
||||
private Toast mCurrentToast;
|
||||
|
||||
@ -70,7 +71,6 @@ public class ButtonToast {
|
||||
|
||||
public ButtonToast(View view) {
|
||||
mView = view;
|
||||
mListener = null;
|
||||
mMessageView = (TextView) mView.findViewById(R.id.toast_message);
|
||||
mButton = (Button) mView.findViewById(R.id.toast_button);
|
||||
mButton.setOnClickListener(new View.OnClickListener() {
|
||||
@ -90,6 +90,13 @@ public class ButtonToast {
|
||||
hide(true, ReasonHidden.STARTUP);
|
||||
}
|
||||
|
||||
public void show(boolean immediate, CharSequence message,
|
||||
CharSequence buttonMessage, int buttonDrawableId,
|
||||
ToastListener listener) {
|
||||
final Drawable d = mView.getContext().getResources().getDrawable(buttonDrawableId);
|
||||
show(false, message, buttonMessage, d, listener);
|
||||
}
|
||||
|
||||
public void show(boolean immediate, CharSequence message,
|
||||
CharSequence buttonMessage, Drawable buttonDrawable,
|
||||
ToastListener listener) {
|
||||
@ -106,10 +113,16 @@ public class ButtonToast {
|
||||
mCurrentToast = t;
|
||||
mButton.setEnabled(true);
|
||||
|
||||
mMessageView.setText(t.message);
|
||||
mButton.setText(t.buttonMessage);
|
||||
mButton.setCompoundDrawablePadding(mView.getContext().getResources().getDimensionPixelSize(R.dimen.toast_button_padding));
|
||||
mButton.setCompoundDrawablesWithIntrinsicBounds(t.buttonDrawable, null, null, null);
|
||||
// Our toast is re-used, so we update all fields to clear any old values.
|
||||
mMessageView.setText(null != t.message ? t.message : "");
|
||||
mButton.setText(null != t.buttonMessage ? t.buttonMessage : "");
|
||||
if (null != t.buttonDrawable) {
|
||||
mButton.setCompoundDrawablePadding(mView.getContext().getResources().getDimensionPixelSize(R.dimen.toast_button_padding));
|
||||
mButton.setCompoundDrawablesWithIntrinsicBounds(t.buttonDrawable, null, null, null);
|
||||
} else {
|
||||
mButton.setCompoundDrawablePadding(0);
|
||||
mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
}
|
||||
|
||||
mHideHandler.removeCallbacks(mHideRunnable);
|
||||
mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION);
|
||||
|
168
mobile/android/base/widget/FloatingHintEditText.java
Normal file
168
mobile/android/base/widget/FloatingHintEditText.java
Normal file
@ -0,0 +1,168 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
public class FloatingHintEditText extends EditText {
|
||||
private static enum Animation { NONE, SHRINK, GROW }
|
||||
private static final float HINT_SCALE = 0.6f;
|
||||
private static final int ANIMATION_STEPS = 6;
|
||||
|
||||
private final Paint floatingHintPaint = new Paint();
|
||||
private final ColorStateList floatingHintColors;
|
||||
private final ColorStateList normalHintColors;
|
||||
private final int defaultFloatingHintColor;
|
||||
private final int defaultNormalHintColor;
|
||||
|
||||
private boolean wasEmpty;
|
||||
|
||||
private int animationFrame;
|
||||
private Animation animation = Animation.NONE;
|
||||
|
||||
public FloatingHintEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FloatingHintEditText(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.floatingHintEditTextStyle);
|
||||
}
|
||||
|
||||
public FloatingHintEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
floatingHintColors = getResources().getColorStateList(R.color.floating_hint_text);
|
||||
normalHintColors = getHintTextColors();
|
||||
defaultFloatingHintColor = floatingHintColors.getDefaultColor();
|
||||
defaultNormalHintColor = normalHintColors.getDefaultColor();
|
||||
wasEmpty = TextUtils.isEmpty(getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCompoundPaddingTop() {
|
||||
final FontMetricsInt metrics = getPaint().getFontMetricsInt();
|
||||
final int floatingHintHeight = (int) ((metrics.bottom - metrics.top) * HINT_SCALE);
|
||||
return super.getCompoundPaddingTop() + floatingHintHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
|
||||
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||||
|
||||
final boolean isEmpty = TextUtils.isEmpty(getText());
|
||||
|
||||
// The empty state hasn't changed, so the hint stays the same.
|
||||
if (wasEmpty == isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
wasEmpty = isEmpty;
|
||||
|
||||
// Don't animate if we aren't visible.
|
||||
if (!isShown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
animation = Animation.GROW;
|
||||
|
||||
// The TextView will show a hint since the field is empty, but since we're animating
|
||||
// from the floating hint, we don't want the normal hint to appear yet. We set it to
|
||||
// transparent here, then restore the hint color after the animation has finished.
|
||||
setHintTextColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
animation = Animation.SHRINK;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (TextUtils.isEmpty(getHint())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isAnimating = (animation != Animation.NONE);
|
||||
|
||||
// The large hint is drawn by Android, so do nothing.
|
||||
if (!isAnimating && TextUtils.isEmpty(getText())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Paint paint = getPaint();
|
||||
final float hintPosX = getCompoundPaddingLeft() + getScrollX();
|
||||
final float normalHintPosY = getBaseline();
|
||||
final float floatingHintPosY = normalHintPosY + paint.getFontMetricsInt().top + getScrollY();
|
||||
final float normalHintSize = getTextSize();
|
||||
final float floatingHintSize = normalHintSize * HINT_SCALE;
|
||||
final int[] stateSet = getDrawableState();
|
||||
final int floatingHintColor = floatingHintColors.getColorForState(stateSet, defaultFloatingHintColor);
|
||||
|
||||
floatingHintPaint.set(paint);
|
||||
|
||||
// If we're not animating, we're showing the floating hint, so draw it and bail.
|
||||
if (!isAnimating) {
|
||||
drawHint(canvas, floatingHintSize, floatingHintColor, hintPosX, floatingHintPosY);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are animating, so draw the linearly interpolated frame.
|
||||
final int normalHintColor = normalHintColors.getColorForState(stateSet, defaultNormalHintColor);
|
||||
if (animation == Animation.SHRINK) {
|
||||
drawAnimationFrame(canvas, normalHintSize, floatingHintSize,
|
||||
hintPosX, normalHintPosY, floatingHintPosY, normalHintColor, floatingHintColor);
|
||||
} else {
|
||||
drawAnimationFrame(canvas, floatingHintSize, normalHintSize,
|
||||
hintPosX, floatingHintPosY, normalHintPosY, floatingHintColor, normalHintColor);
|
||||
}
|
||||
|
||||
animationFrame++;
|
||||
|
||||
if (animationFrame == ANIMATION_STEPS) {
|
||||
// After the grow animation has finished, restore the normal TextView hint color that we
|
||||
// removed in our onTextChanged listener.
|
||||
if (animation == Animation.GROW) {
|
||||
setHintTextColor(normalHintColors);
|
||||
}
|
||||
|
||||
animation = Animation.NONE;
|
||||
animationFrame = 0;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void drawAnimationFrame(Canvas canvas, float fromSize, float toSize,
|
||||
float hintPosX, float fromY, float toY, int fromColor, int toColor) {
|
||||
final float textSize = lerp(fromSize, toSize);
|
||||
final float hintPosY = lerp(fromY, toY);
|
||||
final int color = Color.rgb((int) lerp(Color.red(fromColor), Color.red(toColor)),
|
||||
(int) lerp(Color.green(fromColor), Color.green(toColor)),
|
||||
(int) lerp(Color.blue(fromColor), Color.blue(toColor)));
|
||||
drawHint(canvas, textSize, color, hintPosX, hintPosY);
|
||||
}
|
||||
|
||||
private void drawHint(Canvas canvas, float textSize, int color, float x, float y) {
|
||||
floatingHintPaint.setTextSize(textSize);
|
||||
floatingHintPaint.setColor(color);
|
||||
canvas.drawText(getHint().toString(), x, y, floatingHintPaint);
|
||||
}
|
||||
|
||||
private float lerp(float from, float to) {
|
||||
final float alpha = (float) animationFrame / (ANIMATION_STEPS - 1);
|
||||
return from * (1 - alpha) + to * alpha;
|
||||
}
|
||||
}
|
@ -8,12 +8,13 @@ import org.mozilla.gecko.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TabWidget;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class IconTabWidget extends TabWidget {
|
||||
private OnTabChangedListener mListener;
|
||||
@ -72,4 +73,24 @@ public class IconTabWidget extends TabWidget {
|
||||
mListener.onTabChanged(mIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the Drawable icon corresponding to the given panel.
|
||||
* @param panel to fetch icon for.
|
||||
* @return Drawable instance, or null if no icon is being displayed, or the icon does not exist.
|
||||
*/
|
||||
public Drawable getIconDrawable(int index) {
|
||||
if (!mIsIcon) {
|
||||
return null;
|
||||
}
|
||||
// We can have multiple views in the tray for each child. This finds the
|
||||
// first view corresponding to the given tab. This varies by Android
|
||||
// version. The first view should always be our ImageButton, but let's
|
||||
// be safe.
|
||||
final View view = getChildTabViewAt(index);
|
||||
if (view instanceof ImageButton) {
|
||||
return ((ImageButton) view).getDrawable();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ var Devices = {
|
||||
this.updateDeviceList();
|
||||
}, false);
|
||||
|
||||
let manual = document.getElementById("connect");
|
||||
manual.addEventListener("click", (evt) => {
|
||||
this.connectManually(evt);
|
||||
}, false);
|
||||
|
||||
this._savedSearchInterval = SimpleServiceDiscovery.search(SEARCH_INTERVAL_IN_MILLISECONDS);
|
||||
|
||||
this.updateDeviceList();
|
||||
@ -87,6 +92,46 @@ var Devices = {
|
||||
this.updateDeviceList();
|
||||
}
|
||||
},
|
||||
|
||||
_fixedTargetForType: function(type, ip) {
|
||||
let fixedTarget = {};
|
||||
if (type == "roku") {
|
||||
fixedTarget.target = "roku:ecp";
|
||||
fixedTarget.location = "http://" + ip + ":8060";
|
||||
} else if (type == "chromecast") {
|
||||
fixedTarget.target = "urn:dial-multiscreen-org:service:dial:1";
|
||||
fixedTarget.location = "http://" + ip + ":8008";
|
||||
}
|
||||
return fixedTarget;
|
||||
},
|
||||
|
||||
connectManually: function(evt) {
|
||||
// Since there is no form submit event, this is not validated. However,
|
||||
// after we process this event, the element's validation state is updated.
|
||||
let ip = document.getElementById("ip");
|
||||
if (!ip.checkValidity()) {
|
||||
dump("Manually entered IP address is not valid!");
|
||||
return;
|
||||
}
|
||||
|
||||
let fixedTargets = [];
|
||||
try {
|
||||
fixedTargets = JSON.parse(Services.prefs.getCharPref("browser.casting.fixedTargets"));
|
||||
} catch (e) {}
|
||||
|
||||
let type = document.getElementById("type").value;
|
||||
let fixedTarget = this._fixedTargetForType(type, ip.value);
|
||||
|
||||
// Early abort if we're already looking for this target.
|
||||
if (fixedTargets.indexOf(fixedTarget) > -1)
|
||||
return;
|
||||
|
||||
fixedTargets.push(fixedTarget);
|
||||
Services.prefs.setCharPref("browser.casting.fixedTargets", JSON.stringify(fixedTargets));
|
||||
|
||||
// The backend does not yet listen for pref changes, so we trigger a scan.
|
||||
this.updateDeviceList();
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener("load", Devices.init.bind(Devices), false);
|
||||
|
@ -30,6 +30,20 @@
|
||||
|
||||
<button id="refresh">&aboutDevices.refresh;</button>
|
||||
|
||||
<h1>&aboutDevices.addDeviceHeader;</h1>
|
||||
|
||||
<div id="manual">
|
||||
<select id="type">
|
||||
<option value="roku">&aboutDevices.roku;</option>
|
||||
<option value="chromecast">&aboutDevices.chromecast;</option>
|
||||
</select>
|
||||
|
||||
<input id="ip" type="text" required="required"
|
||||
pattern="((^|\.)((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]?\d))){4}$"
|
||||
placeholder="&aboutDevices.placeholder;" />
|
||||
<button id="connect">&aboutDevices.connectManually;</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript;version=1.8" src="chrome://browser/content/aboutDevices.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -485,11 +485,16 @@ var BrowserApp = {
|
||||
function(aTarget) {
|
||||
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
|
||||
ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
|
||||
BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id });
|
||||
let tab = BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id });
|
||||
|
||||
let newtabStrings = Strings.browser.GetStringFromName("newtabpopup.opened");
|
||||
let label = PluralForm.get(1, newtabStrings).replace("#1", 1);
|
||||
NativeWindow.toast.show(label, "short");
|
||||
NativeWindow.toast.show(label, "long", {
|
||||
button: {
|
||||
icon: "drawable://select_opened_tab",
|
||||
callback: () => { BrowserApp.selectTab(tab); },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.openInPrivateTab"),
|
||||
@ -497,11 +502,16 @@ var BrowserApp = {
|
||||
function(aTarget) {
|
||||
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
|
||||
ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
|
||||
BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id, isPrivate: true });
|
||||
let tab = BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id, isPrivate: true });
|
||||
|
||||
let newtabStrings = Strings.browser.GetStringFromName("newprivatetabpopup.opened");
|
||||
let label = PluralForm.get(1, newtabStrings).replace("#1", 1);
|
||||
NativeWindow.toast.show(label, "short");
|
||||
NativeWindow.toast.show(label, "long", {
|
||||
button: {
|
||||
icon: "drawable://select_opened_tab",
|
||||
callback: () => { BrowserApp.selectTab(tab); },
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyLink"),
|
||||
@ -1764,12 +1774,20 @@ var NativeWindow = {
|
||||
|
||||
if (aOptions && aOptions.button) {
|
||||
msg.button = {
|
||||
label: aOptions.button.label,
|
||||
id: uuidgen.generateUUID().toString(),
|
||||
};
|
||||
|
||||
// null is badly handled by the receiver, so try to avoid including nulls.
|
||||
if (aOptions.button.label) {
|
||||
msg.button.label = aOptions.button.label;
|
||||
}
|
||||
|
||||
if (aOptions.button.icon) {
|
||||
// If the caller specified a button, make sure we convert any chrome urls
|
||||
// to jar:jar urls so that the frontend can show them
|
||||
icon: aOptions.button.icon ? resolveGeckoURI(aOptions.button.icon) : null,
|
||||
msg.button.icon = resolveGeckoURI(aOptions.button.icon);
|
||||
};
|
||||
|
||||
this._callbacks[msg.button.id] = aOptions.button.callback;
|
||||
}
|
||||
|
||||
|
@ -5,3 +5,10 @@
|
||||
<!ENTITY aboutDevices.title "Devices">
|
||||
<!ENTITY aboutDevices.header "Your devices">
|
||||
<!ENTITY aboutDevices.refresh "Refresh">
|
||||
<!ENTITY aboutDevices.addDeviceHeader "Add a device">
|
||||
<!ENTITY aboutDevices.roku "Roku">
|
||||
<!ENTITY aboutDevices.chromecast "Chromecast">
|
||||
<!-- Localization note (aboutDevices.placeholder): this is the hint shown to the
|
||||
user prompting them to input the IP address of a casting device. -->
|
||||
<!ENTITY aboutDevices.placeholder "IP address">
|
||||
<!ENTITY aboutDevices.connectManually "Connect manually">
|
||||
|
@ -31,3 +31,21 @@ gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?
|
||||
# selection UI
|
||||
browser.contentHandlers.types.0.title=My Yahoo!
|
||||
browser.contentHandlers.types.0.uri=https://add.my.yahoo.com/rss?url=%s
|
||||
|
||||
# Order of suggested websites displayed in the Top Sites panel.
|
||||
# Values for these keys must correspond to the name used in the keys that
|
||||
# define each suggested website's details. For example:
|
||||
# browser.suggestedsites.list.0=NAME
|
||||
# browser.suggestedsites.NAME.title=Displayed name
|
||||
# browser.suggestedsites.NAME.url=Website URL
|
||||
# browser.suggestedsites.NAME.bgcolor= Color (hex format)
|
||||
browser.suggestedsites.list.0=mozilla
|
||||
browser.suggestedsites.list.1=fxmarketplace
|
||||
|
||||
browser.suggestedsites.mozilla.title=Mozilla
|
||||
browser.suggestedsites.mozilla.url=https://mozilla.org/en-US/
|
||||
browser.suggestedsites.mozilla.bgcolor=#c13832
|
||||
|
||||
browser.suggestedsites.fxmarketplace.title=Firefox Marketplace
|
||||
browser.suggestedsites.fxmarketplace.url=https://marketplace.firefox.com/
|
||||
browser.suggestedsites.fxmarketplace.bgcolor=#0095dd
|
@ -7,18 +7,21 @@
|
||||
|
||||
This script follows these steps:
|
||||
|
||||
1. Looks for a 'list.txt' file in one of the given source directories
|
||||
(see srcdir). The list.txt contains the list of site names, one per line.
|
||||
1. Read the region.properties file in all the given source directories
|
||||
(see srcdir option). Merge all properties into a single dict accounting for
|
||||
the priority of source directories.
|
||||
|
||||
2. For each site name found in 'list.txt', it tries to find a matching
|
||||
.json file in one of the source directories.
|
||||
2. Read the list of sites from the 'browser.suggestedsites.list.INDEX'
|
||||
properties with value of these keys being an identifier for each suggested site
|
||||
e.g. browser.suggestedsites.list.0=mozilla, browser.suggestedsites.list.1=fxmarketplace.
|
||||
|
||||
3. For each json file, load it and define the respective imageurl
|
||||
based on a image URL template composed by the target Android package name
|
||||
and the site name.
|
||||
3. For each site identifier defined by the list keys, look for matching branches
|
||||
containing the respective properties i.e. url, title, etc. For example,
|
||||
for a 'mozilla' identifier, we'll look for keys like:
|
||||
browser.suggestedsites.mozilla.url, browser.suggestedsites.mozilla.title, etc.
|
||||
|
||||
4. Join the JSON representation of each site into a JSON array and write
|
||||
the result to suggestedsites.json on the locale-specific raw resource
|
||||
4. Generate a JSON representation of each site, join them in a JSON array, and
|
||||
write the result to suggestedsites.json on the locale-specific raw resource
|
||||
directory e.g. raw/suggestedsites.json, raw-pt-rBR/suggestedsites.json.
|
||||
'''
|
||||
|
||||
@ -26,6 +29,7 @@ from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
@ -38,6 +42,59 @@ from mozpack.files import (
|
||||
import mozpack.path as mozpath
|
||||
|
||||
|
||||
def read_properties_file(filename):
|
||||
"""Reads a properties file into a dict.
|
||||
|
||||
Ignores empty, comment lines, and keys not starting with the prefix for
|
||||
suggested sites ('browser.suggestedsites'). Removes the prefix from all
|
||||
matching keys i.e. turns 'browser.suggestedsites.foo' into simply 'foo'
|
||||
"""
|
||||
prefix = 'browser.suggestedsites.'
|
||||
properties = {}
|
||||
for l in open(filename, 'rt').readlines():
|
||||
line = l.strip()
|
||||
if not line.startswith(prefix):
|
||||
continue
|
||||
(k, v) = re.split('\s*=\s*', line, 1)
|
||||
properties[k[len(prefix):]] = v
|
||||
return properties
|
||||
|
||||
|
||||
def merge_properties(filename, srcdirs):
|
||||
"""Merges properties from the given file in the given source directories."""
|
||||
properties = {}
|
||||
for srcdir in srcdirs:
|
||||
path = mozpath.join(srcdir, filename)
|
||||
try:
|
||||
properties.update(read_properties_file(path))
|
||||
except IOError, e:
|
||||
# Ignore non-existing files
|
||||
continue
|
||||
return properties
|
||||
|
||||
|
||||
def get_site_list_from_properties(properties):
|
||||
"""Turns {'list.0':'foo', 'list.1':'bar'} into ['foo', 'bar']."""
|
||||
prefix = 'list.'
|
||||
indexes = []
|
||||
for k, v in properties.iteritems():
|
||||
if not k.startswith(prefix):
|
||||
continue
|
||||
indexes.append(int(k[len(prefix):]))
|
||||
return [properties[prefix + str(index)] for index in sorted(indexes)]
|
||||
|
||||
|
||||
def get_site_from_properties(name, properties):
|
||||
"""Turns {'foo.title':'title', ...} into {'title':'title', ...}."""
|
||||
prefix = '{name}.'.format(name=name)
|
||||
try:
|
||||
site = dict((k, properties[prefix + k]) for k in ('title', 'url', 'bgcolor'))
|
||||
except IndexError, e:
|
||||
raise Exception("Could not find required property for '{name}: {error}'"
|
||||
.format(name=name, error=str(e)))
|
||||
return site
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--verbose', '-v', default=False, action='store_true',
|
||||
@ -57,15 +114,9 @@ def main(args):
|
||||
help='output')
|
||||
opts = parser.parse_args(args)
|
||||
|
||||
def resolve_filename(filename):
|
||||
for srcdir in opts.srcdir:
|
||||
path = mozpath.join(srcdir, filename)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
# The list.txt file has one site name per line.
|
||||
names = [s.strip() for s in open(resolve_filename('list.txt'), 'rt').readlines()]
|
||||
# Use reversed order so that the first srcdir has higher priority to override keys.
|
||||
all_properties = merge_properties('region.properties', reversed(opts.srcdir))
|
||||
names = get_site_list_from_properties(all_properties)
|
||||
if opts.verbose:
|
||||
print('Reading {len} suggested sites: {names}'.format(len=len(names), names=names))
|
||||
|
||||
@ -73,15 +124,11 @@ def main(args):
|
||||
image_url_template = 'android.resource://%s/drawable/suggestedsites_{name}' % opts.android_package_name
|
||||
drawables_template = 'drawable*/suggestedsites_{name}.*'
|
||||
|
||||
# Load json files corresponding to each site name and define their
|
||||
# Load properties corresponding to each site name and define their
|
||||
# respective image URL.
|
||||
sites = []
|
||||
for name in names:
|
||||
filename = resolve_filename(name + '.json')
|
||||
if opts.verbose:
|
||||
print("Reading '{name}' from {filename}"
|
||||
.format(name=name, filename=filename))
|
||||
site = json.load(open(filename, 'rt'))
|
||||
site = get_site_from_properties(name, all_properties)
|
||||
site['imageurl'] = image_url_template.format(name=name)
|
||||
sites.append(site)
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export:: mozilla_api_key google_api_key
|
||||
export:: mozilla_api_key google_api_key bing_api_key
|
||||
|
||||
EXTRA_PP_COMPONENTS_FLAGS = -I mozilla_api_key -I google_api_key
|
||||
EXTRA_PP_COMPONENTS_FLAGS = -I mozilla_api_key -I google_api_key -I bing_api_key
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
@ -15,4 +15,8 @@ mozilla_api_key:
|
||||
google_api_key:
|
||||
@echo '#define MOZ_GOOGLE_API_KEY $(MOZ_GOOGLE_API_KEY)' > $@
|
||||
|
||||
GARBAGE += google_api_key moz_google_api_key
|
||||
bing_api_key:
|
||||
@echo '#define MOZ_BING_API_KEY $(MOZ_BING_API_KEY)' > $@
|
||||
@echo '#define MOZ_BING_API_CLIENTID $(MOZ_BING_API_CLIENTID)' >> $@
|
||||
|
||||
GARBAGE += mozilla_api_key google_api_key bing_api_key
|
||||
|
@ -105,6 +105,8 @@ nsURLFormatterService.prototype = {
|
||||
CHANNEL: function() UpdateChannel.get(),
|
||||
MOZILLA_API_KEY: function() "@MOZ_MOZILLA_API_KEY@",
|
||||
GOOGLE_API_KEY: function() "@MOZ_GOOGLE_API_KEY@",
|
||||
BING_API_CLIENTID:function() "@MOZ_BING_API_CLIENTID@",
|
||||
BING_API_KEY: function() "@MOZ_BING_API_KEY@",
|
||||
DISTRIBUTION: function() this.distribution.id,
|
||||
DISTRIBUTION_VERSION: function() this.distribution.version
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user