зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c a=merge CLOSED TREE
This commit is contained in:
Коммит
8161ac8243
|
@ -8,6 +8,7 @@
|
|||
|
||||
# UITour
|
||||
host uitour 1 www.mozilla.org
|
||||
host uitour 1 self-repair.mozilla.org
|
||||
host uitour 1 support.mozilla.org
|
||||
host uitour 1 about:home
|
||||
|
||||
|
|
|
@ -307,10 +307,11 @@ function onSearchSubmit(aEvent)
|
|||
if (engineName && searchTerms.length > 0) {
|
||||
// Send an event that will perform a search and Firefox Health Report will
|
||||
// record that a search from about:home has occurred.
|
||||
|
||||
let useNewTab = aEvent && aEvent.button == 1;
|
||||
let eventData = {
|
||||
engineName: engineName,
|
||||
searchTerms: searchTerms
|
||||
searchTerms: searchTerms,
|
||||
useNewTab: useNewTab,
|
||||
};
|
||||
|
||||
if (searchText.hasAttribute("selection-index")) {
|
||||
|
|
|
@ -1064,3 +1064,38 @@ addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
|
|||
dataURL: canvas.toDataURL("image/jpeg", ""),
|
||||
});
|
||||
});
|
||||
|
||||
addMessageListener("ContextMenu:MediaCommand", (message) => {
|
||||
let media = message.objects.element;
|
||||
|
||||
switch (message.data.command) {
|
||||
case "play":
|
||||
media.play();
|
||||
break;
|
||||
case "pause":
|
||||
media.pause();
|
||||
break;
|
||||
case "mute":
|
||||
media.muted = true;
|
||||
break;
|
||||
case "unmute":
|
||||
media.muted = false;
|
||||
break;
|
||||
case "playbackRate":
|
||||
media.playbackRate = message.data.data;
|
||||
break;
|
||||
case "hidecontrols":
|
||||
media.removeAttribute("controls");
|
||||
break;
|
||||
case "showcontrols":
|
||||
media.setAttribute("controls", "true");
|
||||
break;
|
||||
case "hidestats":
|
||||
case "showstats":
|
||||
let event = media.ownerDocument.createEvent("CustomEvent");
|
||||
event.initCustomEvent("media-showStatistics", false, true,
|
||||
message.data.command == "showstats");
|
||||
media.dispatchEvent(event);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,11 +49,12 @@ let gSearch = {
|
|||
let searchText = this._nodes.text;
|
||||
let searchStr = searchText.value;
|
||||
if (this.currentEngineName && searchStr.length) {
|
||||
|
||||
let useNewTab = event && event.button == 1;
|
||||
let eventData = {
|
||||
engineName: this.currentEngineName,
|
||||
searchString: searchStr,
|
||||
whence: "newtab",
|
||||
useNewTab: useNewTab,
|
||||
}
|
||||
|
||||
if (searchText.hasAttribute("selection-index")) {
|
||||
|
@ -244,7 +245,7 @@ let gSearch = {
|
|||
let parent = document.getElementById("newtab-scrollbox");
|
||||
this._suggestionController =
|
||||
new SearchSuggestionUIController(this._nodes.text, parent,
|
||||
() => this.search());
|
||||
event => this.search(event));
|
||||
}
|
||||
this._suggestionController.engineName = engine.name;
|
||||
},
|
||||
|
|
|
@ -1668,37 +1668,10 @@ nsContextMenu.prototype = {
|
|||
},
|
||||
|
||||
mediaCommand : function CM_mediaCommand(command, data) {
|
||||
var media = this.target;
|
||||
|
||||
switch (command) {
|
||||
case "play":
|
||||
media.play();
|
||||
break;
|
||||
case "pause":
|
||||
media.pause();
|
||||
break;
|
||||
case "mute":
|
||||
media.muted = true;
|
||||
break;
|
||||
case "unmute":
|
||||
media.muted = false;
|
||||
break;
|
||||
case "playbackRate":
|
||||
media.playbackRate = data;
|
||||
break;
|
||||
case "hidecontrols":
|
||||
media.removeAttribute("controls");
|
||||
break;
|
||||
case "showcontrols":
|
||||
media.setAttribute("controls", "true");
|
||||
break;
|
||||
case "hidestats":
|
||||
case "showstats":
|
||||
var event = media.ownerDocument.createEvent("CustomEvent");
|
||||
event.initCustomEvent("media-showStatistics", false, true, command == "showstats");
|
||||
media.dispatchEvent(event);
|
||||
break;
|
||||
}
|
||||
let mm = this.browser.messageManager;
|
||||
mm.sendAsyncMessage("ContextMenu:MediaCommand",
|
||||
{command: command, data: data},
|
||||
{element: this.target});
|
||||
},
|
||||
|
||||
copyMediaLocation : function () {
|
||||
|
|
|
@ -32,7 +32,8 @@ const HTML_NS = "http://www.w3.org/1999/xhtml";
|
|||
* @param onClick
|
||||
* A function that's called when a search suggestion is clicked. Ideally
|
||||
* we could call submit() on inputElement's ancestor form, but that
|
||||
* doesn't trigger submit listeners.
|
||||
* doesn't trigger submit listeners. The function is passed one argument,
|
||||
* the click event.
|
||||
* @param idPrefix
|
||||
* The IDs of elements created by the object will be prefixed with this
|
||||
* string.
|
||||
|
@ -234,6 +235,9 @@ SearchSuggestionUIController.prototype = {
|
|||
},
|
||||
|
||||
_onMousedown: function (event) {
|
||||
if (event.button == 2) {
|
||||
return;
|
||||
}
|
||||
let idx = this._indexOfTableRowOrDescendent(event.target);
|
||||
let suggestion = this.suggestionAtIndex(idx);
|
||||
this._stickyInputValue = suggestion;
|
||||
|
@ -251,7 +255,7 @@ SearchSuggestionUIController.prototype = {
|
|||
this.input.setAttribute("selection-kind", "mouse");
|
||||
this._hideSuggestions();
|
||||
if (this.onClick) {
|
||||
this.onClick.call(null);
|
||||
this.onClick.call(null, event);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -984,7 +984,7 @@
|
|||
<stylesheet src="chrome://browser/skin/searchbar.css"/>
|
||||
</resources>
|
||||
<content ignorekeys="true" level="top" consumeoutsideclicks="never">
|
||||
<xul:hbox xbl:inherits="collapsed=showonlysettings" anonid="searchbar-engine"
|
||||
<xul:hbox anonid="searchbar-engine" xbl:inherits="showonlysettings"
|
||||
class="search-panel-header search-panel-current-engine">
|
||||
<xul:image class="searchbar-engine-image" xbl:inherits="src"/>
|
||||
<xul:label anonid="searchbar-engine-name" flex="1" crop="end"
|
||||
|
@ -1000,8 +1000,7 @@
|
|||
</xul:tree>
|
||||
<xul:deck anonid="search-panel-one-offs-header"
|
||||
selectedIndex="0"
|
||||
class="search-panel-header search-panel-current-input"
|
||||
xbl:inherits="hidden=showonlysettings">
|
||||
class="search-panel-header search-panel-current-input">
|
||||
<xul:label anonid="searchbar-oneoffheader-search" value="&searchWithHeader.label;"/>
|
||||
<xul:hbox anonid="search-panel-searchforwith"
|
||||
class="search-panel-current-input">
|
||||
|
@ -1011,11 +1010,9 @@
|
|||
</xul:hbox>
|
||||
</xul:deck>
|
||||
<xul:description anonid="search-panel-one-offs"
|
||||
class="search-panel-one-offs"
|
||||
xbl:inherits="hidden=showonlysettings"/>
|
||||
class="search-panel-one-offs"/>
|
||||
<xul:vbox anonid="add-engines"/>
|
||||
<xul:button anonid="search-settings"
|
||||
xbl:inherits="showonlysettings"
|
||||
oncommand="BrowserUITelemetry.countSearchSettingsEvent('searchbar');openPreferences('paneSearch')"
|
||||
class="search-setting-button search-panel-header"
|
||||
label="&changeSearchSettings.button;"/>
|
||||
|
|
|
@ -10,6 +10,8 @@ this.EXPORTED_SYMBOLS = ["CustomizableWidgets"];
|
|||
Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
||||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
|
||||
|
@ -1012,6 +1014,7 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
|
|||
this._ensureSanitizer();
|
||||
this._sanitizer.range = this._getSanitizeRange(doc);
|
||||
let group = doc.getElementById("PanelUI-panic-timeSpan");
|
||||
BrowserUITelemetry.countPanicEvent(group.selectedItem.id);
|
||||
group.selectedItem = doc.getElementById("PanelUI-panic-5min");
|
||||
let itemsToClear = [
|
||||
"cookies", "history", "openWindows", "formdata", "sessions", "cache", "downloads"
|
||||
|
|
|
@ -730,6 +730,11 @@ let MozLoopServiceInternal = {
|
|||
let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
|
||||
gLocalizedStrings.set(string.key, string.value);
|
||||
}
|
||||
// Supply the strings from the branding bundle on a per-need basis.
|
||||
let brandBundle =
|
||||
Services.strings.createBundle("chrome://branding/locale/brand.properties");
|
||||
// Unfortunately the `brandShortName` string is used by Loop with a lowercase 'N'.
|
||||
gLocalizedStrings.set("brandShortname", brandBundle.GetStringFromName("brandShortName"));
|
||||
|
||||
return gLocalizedStrings;
|
||||
},
|
||||
|
|
|
@ -202,6 +202,8 @@ loop.shared.actions = (function() {
|
|||
* Used to start a screen share.
|
||||
*/
|
||||
StartScreenShare: Action.define("startScreenShare", {
|
||||
// The part of the screen to share, e.g. "window" or "browser".
|
||||
type: String
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
|
@ -89,14 +89,13 @@ loop.OTSdkDriver = (function() {
|
|||
/**
|
||||
* Initiates a screen sharing publisher.
|
||||
*/
|
||||
startScreenShare: function() {
|
||||
startScreenShare: function(actionData) {
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.PENDING
|
||||
}));
|
||||
|
||||
var config = this._getCopyPublisherConfig();
|
||||
// This is temporary until we get a sharing type selector
|
||||
config.videoSource = "window";
|
||||
config.videoSource = actionData.type;
|
||||
|
||||
this.screenshare = this.sdk.initPublisher(this.getScreenShareElementFunc(),
|
||||
config);
|
||||
|
|
|
@ -143,14 +143,16 @@ loop.shared.utils = (function(mozL10n) {
|
|||
return;
|
||||
}
|
||||
navigator.mozLoop.composeEmail(
|
||||
mozL10n.get("share_email_subject4", {
|
||||
clientShortname: mozL10n.get("clientShortname2")
|
||||
mozL10n.get("share_email_subject5", {
|
||||
clientShortname2: mozL10n.get("clientShortname2")
|
||||
}),
|
||||
mozL10n.get("share_email_body4", {
|
||||
mozL10n.get("share_email_body5", {
|
||||
callUrl: callUrl,
|
||||
clientShortname: mozL10n.get("clientShortname2"),
|
||||
brandShortname: mozL10n.get("brandShortname"),
|
||||
clientShortname2: mozL10n.get("clientShortname2"),
|
||||
clientSuperShortname: mozL10n.get("clientSuperShortname"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopPref("learnMoreUrl")
|
||||
}),
|
||||
}).replace(/\r\n/g, "\n").replace(/\n/g, "\r\n"),
|
||||
recipient
|
||||
);
|
||||
}
|
||||
|
|
|
@ -102,8 +102,18 @@ loop.shared.views = (function(_, l10n) {
|
|||
}
|
||||
},
|
||||
|
||||
_startScreenShare: function(type) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({
|
||||
type: type
|
||||
}));
|
||||
},
|
||||
|
||||
_handleShareTabs: function() {
|
||||
this._startScreenShare("browser");
|
||||
},
|
||||
|
||||
_handleShareWindows: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({}));
|
||||
this._startScreenShare("window");
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
|
@ -143,6 +153,9 @@ loop.shared.views = (function(_, l10n) {
|
|||
isActive ? null : React.createElement("span", {className: "chevron"})
|
||||
),
|
||||
React.createElement("ul", {ref: "menu", className: dropdownMenuClasses},
|
||||
React.createElement("li", {onClick: this._handleShareTabs, className: "disabled"},
|
||||
l10n.get("share_tabs_button_title")
|
||||
),
|
||||
React.createElement("li", {onClick: this._handleShareWindows},
|
||||
l10n.get("share_windows_button_title")
|
||||
)
|
||||
|
|
|
@ -102,8 +102,18 @@ loop.shared.views = (function(_, l10n) {
|
|||
}
|
||||
},
|
||||
|
||||
_startScreenShare: function(type) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({
|
||||
type: type
|
||||
}));
|
||||
},
|
||||
|
||||
_handleShareTabs: function() {
|
||||
this._startScreenShare("browser");
|
||||
},
|
||||
|
||||
_handleShareWindows: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({}));
|
||||
this._startScreenShare("window");
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
|
@ -143,6 +153,9 @@ loop.shared.views = (function(_, l10n) {
|
|||
{isActive ? null : <span className="chevron"/>}
|
||||
</button>
|
||||
<ul ref="menu" className={dropdownMenuClasses}>
|
||||
<li onClick={this._handleShareTabs} className="disabled">
|
||||
{l10n.get("share_tabs_button_title")}
|
||||
</li>
|
||||
<li onClick={this._handleShareWindows}>
|
||||
{l10n.get("share_windows_button_title")}
|
||||
</li>
|
||||
|
|
|
@ -139,7 +139,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
|
||||
it("should dispatch a `ScreenSharingState` action", function() {
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare({
|
||||
type: "window"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
|
@ -149,7 +151,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
});
|
||||
|
||||
it("should initialize a publisher", function() {
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare({
|
||||
type: "window"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(sdk.initPublisher);
|
||||
sinon.assert.calledWithMatch(sdk.initPublisher,
|
||||
|
@ -161,7 +165,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
beforeEach(function() {
|
||||
driver.getScreenShareElementFunc = function() {};
|
||||
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare({
|
||||
type: "window"
|
||||
}));
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
|
@ -611,7 +617,9 @@ describe("loop.OTSdkDriver", function () {
|
|||
|
||||
driver.getScreenShareElementFunc = function() {};
|
||||
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare({
|
||||
type: "window"
|
||||
}));
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
|
|
@ -152,8 +152,8 @@ describe("loop.shared.utils", function() {
|
|||
// fake mozL10n
|
||||
sandbox.stub(navigator.mozL10n, "get", function(id) {
|
||||
switch(id) {
|
||||
case "share_email_subject4": return "subject";
|
||||
case "share_email_body4": return "body";
|
||||
case "share_email_subject5": return "subject";
|
||||
case "share_email_body5": return "body";
|
||||
}
|
||||
});
|
||||
composeEmail = sandbox.spy();
|
||||
|
|
|
@ -152,7 +152,7 @@ describe("loop.shared.views", function() {
|
|||
expect(comp.state.showMenu).eql(true);
|
||||
});
|
||||
|
||||
it("should dispatch a StartScreenShare action on option click in screenshare dropdown",
|
||||
it("should dispatch a 'browser' StartScreenShare action on option click",
|
||||
function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
|
@ -166,7 +166,24 @@ describe("loop.shared.views", function() {
|
|||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.StartScreenShare({}));
|
||||
new sharedActions.StartScreenShare({ type: "browser" }));
|
||||
});
|
||||
|
||||
it("should dispatch a 'window' StartScreenShare action on option click",
|
||||
function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
|
||||
TestUtils.Simulate.click(comp.getDOMNode().querySelector(
|
||||
".conversation-window-dropdown > li:last-child"));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.StartScreenShare({ type: "window" }));
|
||||
});
|
||||
|
||||
it("should dispatch a EndScreenShare action on click when the state is active",
|
||||
|
|
|
@ -29,9 +29,6 @@ skip-if = e10s && (os == 'linux' || os == 'mac') # Bug 1116457
|
|||
skip-if = e10s
|
||||
|
||||
[browser_forgetthissite_single.js]
|
||||
# disabled for very frequent oranges - bug 551540
|
||||
skip-if = true
|
||||
|
||||
[browser_library_commands.js]
|
||||
[browser_drag_bookmarks_on_toolbar.js]
|
||||
skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be the same. - Got 0, expected 1"
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* 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";
|
||||
|
||||
/**
|
||||
* Test we correctly migrate Library left pane to the latest version.
|
||||
* Note: this test MUST be the first between browser chrome tests, or results
|
||||
|
@ -12,39 +14,7 @@
|
|||
|
||||
const TEST_URI = "http://www.mozilla.org/";
|
||||
|
||||
function onLibraryReady(organizer) {
|
||||
// Check left pane.
|
||||
ok(PlacesUIUtils.leftPaneFolderId > 0,
|
||||
"Left pane folder correctly created");
|
||||
var leftPaneItems =
|
||||
PlacesUtils.annotations
|
||||
.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(leftPaneItems.length, 1,
|
||||
"We correctly have only 1 left pane folder");
|
||||
var leftPaneRoot = leftPaneItems[0];
|
||||
is(leftPaneRoot, PlacesUIUtils.leftPaneFolderId,
|
||||
"leftPaneFolderId getter has correct value");
|
||||
// Check version has been upgraded.
|
||||
var version =
|
||||
PlacesUtils.annotations.getItemAnnotation(leftPaneRoot,
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
|
||||
"Left pane version has been correctly upgraded");
|
||||
|
||||
// Check left pane is populated.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery('History');
|
||||
is(organizer.PlacesOrganizer._places.selectedNode.itemId,
|
||||
PlacesUIUtils.leftPaneQueries["History"],
|
||||
"Library left pane is populated and working");
|
||||
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
// No need to cleanup anything, we have a correct left pane now.
|
||||
finish();
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
add_task(function* () {
|
||||
// Sanity checks.
|
||||
ok(PlacesUtils, "PlacesUtils is running in chrome context");
|
||||
ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
|
||||
|
@ -52,42 +22,69 @@ function test() {
|
|||
"Left pane version in chrome context, current version is: " + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION );
|
||||
|
||||
// Check if we have any left pane folder already set, remove it eventually.
|
||||
var leftPaneItems = PlacesUtils.annotations
|
||||
let leftPaneItems = PlacesUtils.annotations
|
||||
.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
if (leftPaneItems.length > 0) {
|
||||
// The left pane has already been created, touching it now would cause
|
||||
// next tests to rely on wrong values (and possibly crash)
|
||||
is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
|
||||
// Check version.
|
||||
var version = PlacesUtils.annotations.getItemAnnotation(leftPaneItems[0],
|
||||
let version = PlacesUtils.annotations.getItemAnnotation(leftPaneItems[0],
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, "Left pane version is actual");
|
||||
ok(true, "left pane has already been created, skipping test");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a fake left pane folder with an old version (current version - 1).
|
||||
var fakeLeftPaneRoot =
|
||||
PlacesUtils.bookmarks.createFolder(PlacesUtils.placesRootId, "",
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
PlacesUtils.annotations.setItemAnnotation(fakeLeftPaneRoot,
|
||||
let folder = yield PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.rootGuid,
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: ""
|
||||
});
|
||||
|
||||
let folderId = yield PlacesUtils.promiseItemId(folder.guid);
|
||||
PlacesUtils.annotations.setItemAnnotation(folderId,
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
|
||||
PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1,
|
||||
0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
|
||||
// Check fake left pane root has been correctly created.
|
||||
var leftPaneItems =
|
||||
leftPaneItems =
|
||||
PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
|
||||
is(leftPaneItems[0], fakeLeftPaneRoot, "left pane root itemId is correct");
|
||||
is(leftPaneItems[0], folderId, "left pane root itemId is correct");
|
||||
|
||||
// Check version.
|
||||
var version = PlacesUtils.annotations.getItemAnnotation(fakeLeftPaneRoot,
|
||||
let version = PlacesUtils.annotations.getItemAnnotation(folderId,
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1, "Left pane version correctly set");
|
||||
|
||||
// Open Library, this will upgrade our left pane version.
|
||||
openLibrary(onLibraryReady);
|
||||
}
|
||||
let organizer = yield promiseLibrary();
|
||||
|
||||
// Check left pane.
|
||||
ok(PlacesUIUtils.leftPaneFolderId > 0, "Left pane folder correctly created");
|
||||
leftPaneItems =
|
||||
PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
|
||||
let leftPaneRoot = leftPaneItems[0];
|
||||
is(leftPaneRoot, PlacesUIUtils.leftPaneFolderId,
|
||||
"leftPaneFolderId getter has correct value");
|
||||
|
||||
// Check version has been upgraded.
|
||||
version = PlacesUtils.annotations.getItemAnnotation(leftPaneRoot,
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
|
||||
"Left pane version has been correctly upgraded");
|
||||
|
||||
// Check left pane is populated.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
is(organizer.PlacesOrganizer._places.selectedNode.itemId,
|
||||
PlacesUIUtils.leftPaneQueries["History"],
|
||||
"Library left pane is populated and working");
|
||||
|
||||
yield promiseLibraryClosed(organizer);
|
||||
});
|
||||
|
|
|
@ -2,139 +2,113 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function add_bookmark(aURI) {
|
||||
return PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
aURI, PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"bookmark/" + aURI.spec);
|
||||
}
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
const TEST_URL = Services.io.newURI("http://example.com/", null, null);
|
||||
const MOZURISPEC = Services.io.newURI("http://mozilla.com/", null, null);
|
||||
|
||||
const TEST_URL = "http://example.com/";
|
||||
const MOZURISPEC = "http://mozilla.com/";
|
||||
add_task(function* () {
|
||||
let organizer = yield promiseLibrary();
|
||||
|
||||
let gLibrary;
|
||||
let PlacesOrganizer;
|
||||
let ContentTree;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gLibrary = openLibrary(onLibraryReady);
|
||||
}
|
||||
|
||||
function onLibraryReady() {
|
||||
ok(PlacesUtils, "PlacesUtils in scope");
|
||||
ok(PlacesUIUtils, "PlacesUIUtils in scope");
|
||||
|
||||
PlacesOrganizer = gLibrary.PlacesOrganizer;
|
||||
let PlacesOrganizer = organizer.PlacesOrganizer;
|
||||
ok(PlacesOrganizer, "Places organizer in scope");
|
||||
|
||||
ContentTree = gLibrary.ContentTree;
|
||||
let ContentTree = organizer.ContentTree;
|
||||
ok(ContentTree, "ContentTree is in scope");
|
||||
|
||||
tests.makeHistVisit(function() {
|
||||
tests.makeTag();
|
||||
tests.focusTag();
|
||||
waitForClipboard(function(aData) !!aData,
|
||||
tests.copyHistNode,
|
||||
onClipboardReady,
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
let visits = {uri: MOZURISPEC, transition: PlacesUtils.history.TRANSITION_TYPED};
|
||||
yield PlacesTestUtils.addVisits(visits);
|
||||
|
||||
// create an initial tag to work with
|
||||
let bm = yield PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
title: "bookmark/" + TEST_URL.spec,
|
||||
url: TEST_URL
|
||||
});
|
||||
}
|
||||
|
||||
function onClipboardReady() {
|
||||
tests.focusTag();
|
||||
ok(bm, "A bookmark was added");
|
||||
PlacesUtils.tagging.tagURI(TEST_URL, ["foo"]);
|
||||
let tags = PlacesUtils.tagging.getTagsForURI(TEST_URL);
|
||||
is(tags[0], "foo", "tag is foo");
|
||||
|
||||
// focus the new tag
|
||||
focusTag(PlacesOrganizer);
|
||||
|
||||
let populate = () => copyHistNode(PlacesOrganizer, ContentTree);
|
||||
yield promiseClipboard(populate, PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
focusTag(PlacesOrganizer);
|
||||
PlacesOrganizer._places.controller.paste();
|
||||
tests.historyNode();
|
||||
tests.checkForBookmarkInUI();
|
||||
|
||||
gLibrary.close();
|
||||
// re-focus the history again
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
|
||||
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
|
||||
ok(histNode, "histNode exists: " + histNode.title);
|
||||
|
||||
// check to see if the history node is tagged!
|
||||
tags = PlacesUtils.tagging.getTagsForURI(MOZURISPEC);
|
||||
ok(tags.length == 1, "history node is tagged: " + tags.length);
|
||||
|
||||
// check if a bookmark was created
|
||||
let bookmarks = [];
|
||||
yield PlacesUtils.bookmarks.fetch({url: MOZURISPEC}, bm => {
|
||||
bookmarks.push(bm);
|
||||
});
|
||||
ok(bookmarks.length > 0, "bookmark exists for the tagged history item");
|
||||
|
||||
// is the bookmark visible in the UI?
|
||||
// get the Unsorted Bookmarks node
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
|
||||
// now we can see what is in the ContentTree tree
|
||||
let unsortedNode = ContentTree.view.view.nodeForTreeIndex(1);
|
||||
ok(unsortedNode, "unsortedNode is not null: " + unsortedNode.uri);
|
||||
is(unsortedNode.uri, MOZURISPEC.spec, "node uri's are the same");
|
||||
|
||||
yield promiseLibraryClosed(organizer);
|
||||
|
||||
// Remove new Places data we created.
|
||||
PlacesUtils.tagging.untagURI(NetUtil.newURI(MOZURISPEC), ["foo"]);
|
||||
PlacesUtils.tagging.untagURI(NetUtil.newURI(TEST_URL), ["foo"]);
|
||||
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(TEST_URL));
|
||||
PlacesUtils.tagging.untagURI(MOZURISPEC, ["foo"]);
|
||||
PlacesUtils.tagging.untagURI(TEST_URL, ["foo"]);
|
||||
tags = PlacesUtils.tagging.getTagsForURI(TEST_URL);
|
||||
is(tags.length, 0, "tags are gone");
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
function focusTag(PlacesOrganizer) {
|
||||
PlacesOrganizer.selectLeftPaneQuery("Tags");
|
||||
let tags = PlacesOrganizer._places.selectedNode;
|
||||
tags.containerOpen = true;
|
||||
let fooTag = tags.getChild(0);
|
||||
let tagNode = fooTag;
|
||||
PlacesOrganizer._places.selectNode(fooTag);
|
||||
is(tagNode.title, 'foo', "tagNode title is foo");
|
||||
let ip = PlacesOrganizer._places.insertionPoint;
|
||||
ok(ip.isTag, "IP is a tag");
|
||||
}
|
||||
|
||||
let tests = {
|
||||
|
||||
makeHistVisit: function(aCallback) {
|
||||
// need to add a history object
|
||||
let testURI1 = NetUtil.newURI(MOZURISPEC);
|
||||
isnot(testURI1, null, "testURI is not null");
|
||||
PlacesTestUtils.addVisits(
|
||||
{uri: testURI1, transition: PlacesUtils.history.TRANSITION_TYPED}
|
||||
).then(aCallback);
|
||||
},
|
||||
|
||||
makeTag: function() {
|
||||
// create an initial tag to work with
|
||||
let bmId = add_bookmark(NetUtil.newURI(TEST_URL));
|
||||
ok(bmId > 0, "A bookmark was added");
|
||||
PlacesUtils.tagging.tagURI(NetUtil.newURI(TEST_URL), ["foo"]);
|
||||
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(TEST_URL));
|
||||
is(tags[0], 'foo', "tag is foo");
|
||||
},
|
||||
|
||||
focusTag: function (){
|
||||
// focus the new tag
|
||||
PlacesOrganizer.selectLeftPaneQuery("Tags");
|
||||
let tags = PlacesOrganizer._places.selectedNode;
|
||||
tags.containerOpen = true;
|
||||
let fooTag = tags.getChild(0);
|
||||
let tagNode = fooTag;
|
||||
PlacesOrganizer._places.selectNode(fooTag);
|
||||
is(tagNode.title, 'foo', "tagNode title is foo");
|
||||
let ip = PlacesOrganizer._places.insertionPoint;
|
||||
ok(ip.isTag, "IP is a tag");
|
||||
},
|
||||
|
||||
copyHistNode: function (){
|
||||
// focus the history object
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
|
||||
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
|
||||
ContentTree.view.selectNode(histNode);
|
||||
is(histNode.uri, MOZURISPEC,
|
||||
"historyNode exists: " + histNode.uri);
|
||||
// copy the history node
|
||||
ContentTree.view.controller.copy();
|
||||
},
|
||||
|
||||
historyNode: function (){
|
||||
// re-focus the history again
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
|
||||
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
|
||||
ok(histNode, "histNode exists: " + histNode.title);
|
||||
// check to see if the history node is tagged!
|
||||
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(MOZURISPEC));
|
||||
ok(tags.length == 1, "history node is tagged: " + tags.length);
|
||||
// check if a bookmark was created
|
||||
let isBookmarked = PlacesUtils.bookmarks.isBookmarked(NetUtil.newURI(MOZURISPEC));
|
||||
is(isBookmarked, true, MOZURISPEC + " is bookmarked");
|
||||
let bookmarkIds = PlacesUtils.bookmarks.getBookmarkIdsForURI(
|
||||
NetUtil.newURI(histNode.uri));
|
||||
ok(bookmarkIds.length > 0, "bookmark exists for the tagged history item: " + bookmarkIds);
|
||||
},
|
||||
|
||||
checkForBookmarkInUI: function(){
|
||||
// is the bookmark visible in the UI?
|
||||
// get the Unsorted Bookmarks node
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
// now we can see what is in the ContentTree tree
|
||||
let unsortedNode = ContentTree.view.view.nodeForTreeIndex(1);
|
||||
ok(unsortedNode, "unsortedNode is not null: " + unsortedNode.uri);
|
||||
is(unsortedNode.uri, MOZURISPEC, "node uri's are the same");
|
||||
},
|
||||
};
|
||||
function copyHistNode(PlacesOrganizer, ContentTree) {
|
||||
// focus the history object
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
|
||||
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
|
||||
ContentTree.view.selectNode(histNode);
|
||||
is(histNode.uri, MOZURISPEC.spec,
|
||||
"historyNode exists: " + histNode.uri);
|
||||
// copy the history node
|
||||
ContentTree.view.controller.copy();
|
||||
}
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = "http://example.com/";
|
||||
|
||||
let gLibrary;
|
||||
let gItemId;
|
||||
let PlacesOrganizer;
|
||||
let ContentTree;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gLibrary = openLibrary(onLibraryReady);
|
||||
}
|
||||
|
||||
function onLibraryReady() {
|
||||
PlacesOrganizer = gLibrary.PlacesOrganizer;
|
||||
ContentTree = gLibrary.ContentTree;
|
||||
add_task(function* () {
|
||||
let organizer = yield promiseLibrary();
|
||||
let PlacesOrganizer = organizer.PlacesOrganizer;
|
||||
let ContentTree = organizer.ContentTree;
|
||||
|
||||
// Sanity checks.
|
||||
ok(PlacesUtils, "PlacesUtils in scope");
|
||||
|
@ -24,49 +16,44 @@ function onLibraryReady() {
|
|||
ok(PlacesOrganizer, "PlacesOrganizer in scope");
|
||||
ok(ContentTree, "ContentTree is in scope");
|
||||
|
||||
gItemId = PlacesUtils.bookmarks.insertBookmark(
|
||||
PlacesUtils.toolbarFolderId, NetUtil.newURI(TEST_URL),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, "test"
|
||||
);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: TEST_URL
|
||||
});
|
||||
|
||||
selectBookmarkIn("BookmarksToolbar");
|
||||
yield selectBookmarkIn(organizer, bm, "BookmarksToolbar");
|
||||
|
||||
waitForClipboard(function(aData) !!aData,
|
||||
cutSelection,
|
||||
onClipboardReady,
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
}
|
||||
yield promiseClipboard(() => {
|
||||
info("Cutting selection");
|
||||
ContentTree.view.controller.cut();
|
||||
}, PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
info("Selecting UnfiledBookmarks in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
info("Pasting clipboard");
|
||||
ContentTree.view.controller.paste();
|
||||
|
||||
yield selectBookmarkIn(organizer, bm, "UnfiledBookmarks");
|
||||
|
||||
yield promiseLibraryClosed(organizer);
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
});
|
||||
|
||||
let selectBookmarkIn = Task.async(function* (organizer, bm, aLeftPaneQuery) {
|
||||
let PlacesOrganizer = organizer.PlacesOrganizer;
|
||||
let ContentTree = organizer.ContentTree;
|
||||
|
||||
function selectBookmarkIn(aLeftPaneQuery) {
|
||||
info("Selecting " + aLeftPaneQuery + " in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
|
||||
let rootId = PlacesUtils.getConcreteItemId(PlacesOrganizer._places.selectedNode);
|
||||
is(PlacesUtils.bookmarks.getFolderIdForItem(gItemId), rootId,
|
||||
|
||||
bm = yield PlacesUtils.bookmarks.fetch(bm.guid);
|
||||
is((yield PlacesUtils.promiseItemId(bm.parentGuid)), rootId,
|
||||
"Bookmark has the right parent");
|
||||
|
||||
info("Selecting the bookmark in the right pane");
|
||||
ContentTree.view.selectItems([gItemId]);
|
||||
ContentTree.view.selectItems([yield PlacesUtils.promiseItemId(bm.guid)]);
|
||||
let bookmarkNode = ContentTree.view.selectedNode;
|
||||
is(bookmarkNode.uri, TEST_URL, "Found the expected bookmark");
|
||||
}
|
||||
|
||||
function cutSelection() {
|
||||
info("Cutting selection");
|
||||
ContentTree.view.controller.cut();
|
||||
}
|
||||
|
||||
function pasteClipboard(aLeftPaneQuery) {
|
||||
info("Selecting " + aLeftPaneQuery + " in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
|
||||
info("Pasting clipboard");
|
||||
ContentTree.view.controller.paste();
|
||||
}
|
||||
|
||||
function onClipboardReady() {
|
||||
pasteClipboard("UnfiledBookmarks");
|
||||
selectBookmarkIn("UnfiledBookmarks");
|
||||
|
||||
// Cleanup.
|
||||
gLibrary.close();
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,54 +1,66 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Deep copy of bookmark data, using the front-end codepath:
|
||||
|
||||
- create test folder A
|
||||
- add a subfolder to folder A, and add items to it
|
||||
- validate folder A (sanity check)
|
||||
- copy folder A, creating new folder B, using the front-end path
|
||||
- validate folder B
|
||||
- undo copy transaction
|
||||
- validate folder B (empty)
|
||||
- redo copy transaction
|
||||
- validate folder B's contents
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
// sanity check
|
||||
ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
|
||||
ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
|
||||
|
||||
/*
|
||||
Deep copy of bookmark data, using the front-end codepath:
|
||||
let toolbarId = PlacesUtils.toolbarFolderId;
|
||||
let toolbarNode = PlacesUtils.getFolderContents(toolbarId).root;
|
||||
|
||||
- create test folder A
|
||||
- add a subfolder to folder A, and add items to it
|
||||
- validate folder A (sanity check)
|
||||
- copy folder A, creating new folder B, using the front-end path
|
||||
- validate folder B
|
||||
- undo copy transaction
|
||||
- validate folder B (empty)
|
||||
- redo copy transaction
|
||||
- validate folder B's contents
|
||||
|
||||
*/
|
||||
|
||||
var toolbarId = PlacesUtils.toolbarFolderId;
|
||||
var toolbarNode = PlacesUtils.getFolderContents(toolbarId).root;
|
||||
|
||||
var oldCount = toolbarNode.childCount;
|
||||
var testRootId = PlacesUtils.bookmarks.createFolder(toolbarId, "test root", -1);
|
||||
let oldCount = toolbarNode.childCount;
|
||||
let testRoot = yield PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "test root"
|
||||
});
|
||||
is(toolbarNode.childCount, oldCount+1, "confirm test root node is a container, and is empty");
|
||||
var testRootNode = toolbarNode.getChild(toolbarNode.childCount-1);
|
||||
|
||||
let testRootNode = toolbarNode.getChild(toolbarNode.childCount-1);
|
||||
testRootNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
testRootNode.containerOpen = true;
|
||||
is(testRootNode.childCount, 0, "confirm test root node is a container, and is empty");
|
||||
|
||||
// create folder A, fill it, validate its contents
|
||||
var folderAId = PlacesUtils.bookmarks.createFolder(testRootId, "A", -1);
|
||||
populate(folderAId);
|
||||
var folderANode = PlacesUtils.getFolderContents(folderAId).root;
|
||||
let folderA = yield PlacesUtils.bookmarks.insert({
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: testRoot.guid,
|
||||
title: "A"
|
||||
});
|
||||
|
||||
yield populate(folderA);
|
||||
|
||||
let folderAId = yield PlacesUtils.promiseItemId(folderA.guid);
|
||||
let folderANode = PlacesUtils.getFolderContents(folderAId).root;
|
||||
validate(folderANode);
|
||||
is(testRootNode.childCount, 1, "create test folder");
|
||||
|
||||
// copy it, using the front-end helper functions
|
||||
var serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
|
||||
var rawNode = PlacesUtils.unwrapNodes(serializedNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER).shift();
|
||||
let serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
|
||||
let rawNode = PlacesUtils.unwrapNodes(serializedNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER).shift();
|
||||
// confirm serialization
|
||||
ok(rawNode.type, "confirm json node");
|
||||
folderANode.containerOpen = false;
|
||||
|
||||
var transaction = PlacesUIUtils.makeTransaction(rawNode,
|
||||
let testRootId = yield PlacesUtils.promiseItemId(testRoot.guid);
|
||||
let transaction = PlacesUIUtils.makeTransaction(rawNode,
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
|
||||
testRootId,
|
||||
-1,
|
||||
|
@ -59,7 +71,7 @@ function test() {
|
|||
is(testRootNode.childCount, 2, "create test folder via copy");
|
||||
|
||||
// validate the copy
|
||||
var folderBNode = testRootNode.getChild(1);
|
||||
let folderBNode = testRootNode.getChild(1);
|
||||
validate(folderBNode);
|
||||
|
||||
// undo the transaction, confirm the removal
|
||||
|
@ -78,14 +90,28 @@ function test() {
|
|||
|
||||
// clean up
|
||||
PlacesUtils.transactionManager.undoTransaction();
|
||||
PlacesUtils.bookmarks.removeItem(folderAId);
|
||||
}
|
||||
yield PlacesUtils.bookmarks.remove(folderA.guid);
|
||||
});
|
||||
|
||||
function populate(aFolderId) {
|
||||
var folderId = PlacesUtils.bookmarks.createFolder(aFolderId, "test folder", -1);
|
||||
PlacesUtils.bookmarks.insertBookmark(folderId, PlacesUtils._uri("http://foo"), -1, "test bookmark");
|
||||
PlacesUtils.bookmarks.insertSeparator(folderId, -1);
|
||||
}
|
||||
let populate = Task.async(function* (parentFolder) {
|
||||
let folder = yield PlacesUtils.bookmarks.insert({
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: parentFolder.guid,
|
||||
title: "test folder"
|
||||
});
|
||||
|
||||
yield PlacesUtils.bookmarks.insert({
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: folder.guid,
|
||||
title: "test bookmark",
|
||||
url: "http://foo"
|
||||
});
|
||||
|
||||
yield PlacesUtils.bookmarks.insert({
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
parentGuid: folder.guid
|
||||
});
|
||||
});
|
||||
|
||||
function validate(aNode) {
|
||||
PlacesUtils.asContainer(aNode);
|
||||
|
|
|
@ -2,69 +2,77 @@
|
|||
* 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 TEST_URIs = [
|
||||
"http://www.mozilla.org/test1",
|
||||
"http://www.mozilla.org/test2"
|
||||
];
|
||||
|
||||
// This test makes sure that the Forget This Site command is hidden for multiple
|
||||
// selections.
|
||||
function test() {
|
||||
// initialization
|
||||
waitForExplicitFinish();
|
||||
|
||||
add_task(function* () {
|
||||
// Add a history entry.
|
||||
let TEST_URIs = ["http://www.mozilla.org/test1", "http://www.mozilla.org/test2"];
|
||||
ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
|
||||
let places = [];
|
||||
TEST_URIs.forEach(function(TEST_URI) {
|
||||
places.push({uri: PlacesUtils._uri(TEST_URI),
|
||||
transition: PlacesUtils.history.TRANSITION_TYPED});
|
||||
});
|
||||
PlacesTestUtils.addVisits(places).then(() => {
|
||||
testForgetThisSiteVisibility(1, function() {
|
||||
testForgetThisSiteVisibility(2, function() {
|
||||
// Cleanup
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testForgetThisSiteVisibility(selectionCount, funcNext) {
|
||||
openLibrary(function (organizer) {
|
||||
// Select History in the left pane.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery('History');
|
||||
let PO = organizer.PlacesOrganizer;
|
||||
let histContainer = PO._places.selectedNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
histContainer.containerOpen = true;
|
||||
PO._places.selectNode(histContainer.getChild(0));
|
||||
// Select the first history entry.
|
||||
let doc = organizer.document;
|
||||
let tree = PO._content;
|
||||
let selection = tree.view.selection;
|
||||
selection.clearSelection();
|
||||
selection.rangedSelect(0, selectionCount - 1, true);
|
||||
is(selection.count, selectionCount,
|
||||
"The selected range is as big as expected");
|
||||
// Open the context menu
|
||||
let contextmenu = doc.getElementById("placesContext");
|
||||
contextmenu.addEventListener("popupshown", function() {
|
||||
contextmenu.removeEventListener("popupshown", arguments.callee, true);
|
||||
let forgetThisSite = doc.getElementById("placesContext_deleteHost");
|
||||
let hideForgetThisSite = (selectionCount != 1);
|
||||
is(forgetThisSite.hidden, hideForgetThisSite,
|
||||
"The Forget this site menu item should " + (hideForgetThisSite ? "" : "not ") +
|
||||
"be hidden with " + selectionCount + " items selected");
|
||||
// Close the context menu
|
||||
contextmenu.hidePopup();
|
||||
// Wait for the Organizer window to actually be closed
|
||||
organizer.addEventListener("unload", function () {
|
||||
organizer.removeEventListener("unload", arguments.callee, false);
|
||||
// Proceed
|
||||
funcNext();
|
||||
}, false);
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
}, true);
|
||||
// Get cell coordinates
|
||||
var rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text");
|
||||
// Initiate a context menu for the selected cell
|
||||
EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {type: "contextmenu"}, organizer);
|
||||
});
|
||||
}
|
||||
let places = [];
|
||||
let transition = PlacesUtils.history.TRANSITION_TYPED;
|
||||
TEST_URIs.forEach(uri => places.push({uri: PlacesUtils._uri(uri), transition}));
|
||||
|
||||
yield PlacesTestUtils.addVisits(places);
|
||||
yield testForgetThisSiteVisibility(1);
|
||||
yield testForgetThisSiteVisibility(2);
|
||||
|
||||
// Cleanup.
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
let testForgetThisSiteVisibility = Task.async(function* (selectionCount) {
|
||||
let organizer = yield promiseLibrary();
|
||||
|
||||
// Select History in the left pane.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
let PO = organizer.PlacesOrganizer;
|
||||
let histContainer = PO._places.selectedNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
histContainer.containerOpen = true;
|
||||
PO._places.selectNode(histContainer.getChild(0));
|
||||
|
||||
// Select the first history entry.
|
||||
let doc = organizer.document;
|
||||
let tree = doc.getElementById("placeContent");
|
||||
let selection = tree.view.selection;
|
||||
selection.clearSelection();
|
||||
selection.rangedSelect(0, selectionCount - 1, true);
|
||||
is(selection.count, selectionCount, "The selected range is as big as expected");
|
||||
|
||||
// Open the context menu.
|
||||
let contextmenu = doc.getElementById("placesContext");
|
||||
let popupShown = promisePopupShown(contextmenu);
|
||||
|
||||
// Get cell coordinates.
|
||||
let rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "text");
|
||||
// Initiate a context menu for the selected cell.
|
||||
EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {type: "contextmenu", button: 2}, organizer);
|
||||
yield popupShown;
|
||||
|
||||
let forgetThisSite = doc.getElementById("placesContext_deleteHost");
|
||||
let hideForgetThisSite = (selectionCount != 1);
|
||||
is(forgetThisSite.hidden, hideForgetThisSite,
|
||||
`The Forget this site menu item should ${hideForgetThisSite ? "" : "not "}` +
|
||||
` be hidden with ${selectionCount} items selected`);
|
||||
|
||||
// Close the context menu.
|
||||
contextmenu.hidePopup();
|
||||
|
||||
// Close the library window.
|
||||
yield promiseLibraryClosed(organizer);
|
||||
});
|
||||
|
||||
function promisePopupShown(popup) {
|
||||
return new Promise(resolve => {
|
||||
popup.addEventListener("popupshown", function onShown() {
|
||||
popup.removeEventListener("popupshown", onShown, true);
|
||||
resolve();
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,17 +43,31 @@ function openLibrary(callback, aLeftPaneRoot) {
|
|||
* Hierarchy to open and select in the left pane.
|
||||
*/
|
||||
function promiseLibrary(aLeftPaneRoot) {
|
||||
let deferred = Promise.defer();
|
||||
let library = Services.wm.getMostRecentWindow("Places:Organizer");
|
||||
if (library && !library.closed) {
|
||||
if (aLeftPaneRoot)
|
||||
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
|
||||
deferred.resolve(library);
|
||||
}
|
||||
else {
|
||||
openLibrary(aLibrary => deferred.resolve(aLibrary), aLeftPaneRoot);
|
||||
}
|
||||
return deferred.promise;
|
||||
return new Promise(resolve => {
|
||||
let library = Services.wm.getMostRecentWindow("Places:Organizer");
|
||||
if (library && !library.closed) {
|
||||
if (aLeftPaneRoot) {
|
||||
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
|
||||
}
|
||||
resolve(library);
|
||||
}
|
||||
else {
|
||||
openLibrary(resolve, aLeftPaneRoot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function promiseLibraryClosed(organizer) {
|
||||
return new Promise(resolve => {
|
||||
// Wait for the Organizer window to actually be closed
|
||||
organizer.addEventListener("unload", function onUnload() {
|
||||
organizer.removeEventListener("unload", onUnload);
|
||||
resolve();
|
||||
});
|
||||
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,12 +81,9 @@ function promiseLibrary(aLeftPaneRoot) {
|
|||
* Data flavor to expect.
|
||||
*/
|
||||
function promiseClipboard(aPopulateClipboardFn, aFlavor) {
|
||||
let deferred = Promise.defer();
|
||||
waitForClipboard(function (aData) !!aData,
|
||||
aPopulateClipboardFn,
|
||||
function () { deferred.resolve(); },
|
||||
aFlavor);
|
||||
return deferred.promise;
|
||||
return new Promise(resolve => {
|
||||
waitForClipboard(data => !!data, aPopulateClipboardFn, resolve, aFlavor);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -108,6 +108,37 @@ var gFontsDialog = {
|
|||
{
|
||||
var useDocumentFonts = document.getElementById("useDocumentFonts");
|
||||
return useDocumentFonts.checked ? 1 : 0;
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeAccept: function ()
|
||||
{
|
||||
// Only care in in-content prefs
|
||||
if (!window.frameElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let preferences = document.querySelectorAll("preference[id*='font.minimum-size']");
|
||||
// It would be good if we could avoid touching languages the pref pages won't use, but
|
||||
// unfortunately the language group APIs (deducing language groups from language codes)
|
||||
// are C++ - only. So we just check all the things the user touched:
|
||||
// Don't care about anything up to 24px, or if this value is the same as set previously:
|
||||
preferences = Array.filter(preferences, prefEl => {
|
||||
return prefEl.value > 24 && prefEl.value != prefEl.valueFromPreferences;
|
||||
});
|
||||
if (!preferences.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let strings = document.getElementById("bundlePreferences");
|
||||
let title = strings.getString("veryLargeMinimumFontTitle");
|
||||
let confirmLabel = strings.getString("acceptVeryLargeMinimumFont");
|
||||
let warningMessage = strings.getString("veryLargeMinimumFontWarning");
|
||||
let {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {});
|
||||
let flags = Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL |
|
||||
Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING |
|
||||
Services.prompt.BUTTON_POS_1_DEFAULT;
|
||||
let buttonChosen = Services.prompt.confirmEx(window, title, warningMessage, flags, confirmLabel, null, "", "", {});
|
||||
return buttonChosen == 0;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
title="&fontsDialog.title;"
|
||||
dlgbuttons="accept,cancel,help"
|
||||
ondialoghelp="openPrefsHelp()"
|
||||
onbeforeaccept="return gFontsDialog.onBeforeAccept();"
|
||||
style="">
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
|
|
@ -1063,6 +1063,12 @@
|
|||
let selectedButton = this.selectedButton;
|
||||
let buttons = this.getSelectableButtons(aCycleEngines);
|
||||
|
||||
let suggestionsHidden;
|
||||
if (!aSkipSuggestions && popup.hasAttribute("showonlysettings")) {
|
||||
aSkipSuggestions = true;
|
||||
suggestionsHidden = true;
|
||||
}
|
||||
|
||||
// If the last suggestion is selected, DOWN selects the first button.
|
||||
if (!aSkipSuggestions && aForward &&
|
||||
popup.selectedIndex + 1 == popup.view.rowCount) {
|
||||
|
@ -1083,7 +1089,7 @@
|
|||
else
|
||||
this.selectedButton = null;
|
||||
|
||||
if (this.selectedButton || aCycleEngines)
|
||||
if (this.selectedButton || aCycleEngines || suggestionsHidden)
|
||||
return true;
|
||||
|
||||
// Set the selectedIndex to something that will make
|
||||
|
@ -1104,7 +1110,7 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!aForward && (aCycleEngines ||
|
||||
if (!aForward && (aCycleEngines || suggestionsHidden ||
|
||||
(!aSkipSuggestions && popup.selectedIndex == -1))) {
|
||||
// the last button.
|
||||
this.selectedButton = buttons[buttons.length - 1];
|
||||
|
@ -1123,15 +1129,6 @@
|
|||
if (!popup.popupOpen)
|
||||
return;
|
||||
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN &&
|
||||
popup.hasAttribute("showonlysettings")) {
|
||||
// If the suggestion tree is collapsed, don't let the down arrow
|
||||
// key event reach the tree.
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
let list = document.getAnonymousElementByAttribute(popup, "anonid",
|
||||
"search-panel-one-offs");
|
||||
if (!list) // remove this check when removing the old search UI.
|
||||
|
|
|
@ -44,4 +44,5 @@ skip-if = e10s || true # Bug ??????, Bug 1100301 - leaks windows until shutdown
|
|||
[browser_searchbar_openpopup.js]
|
||||
skip-if = os == "linux" || e10s # Linux has different focus behaviours and e10s seems to have timing issues.
|
||||
[browser_searchbar_keyboard_navigation.js]
|
||||
[browser_searchbar_smallpanel_keyboard_navigation.js]
|
||||
[browser_webapi.js]
|
||||
|
|
|
@ -191,6 +191,27 @@ add_task(function* focus_change_closes_popup() {
|
|||
textbox.value = "";
|
||||
});
|
||||
|
||||
// Moving focus away from the search box should close the small popup
|
||||
add_task(function* focus_change_closes_small_popup() {
|
||||
gURLBar.focus();
|
||||
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
// For some reason sending the mouse event immediately doesn't open the popup.
|
||||
SimpleTest.executeSoon(() => {
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
});
|
||||
yield promise;
|
||||
is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup");
|
||||
|
||||
is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
|
||||
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
let promise2 = promiseEvent(searchbar, "blur");
|
||||
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
|
||||
yield promise;
|
||||
yield promise2;
|
||||
});
|
||||
|
||||
// Pressing escape should close the popup.
|
||||
add_task(function* escape_closes_popup() {
|
||||
gURLBar.focus();
|
||||
|
@ -442,4 +463,6 @@ add_task(function* dont_rollup_oncaretmove() {
|
|||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promise;
|
||||
|
||||
textbox.value = "";
|
||||
});
|
||||
|
|
|
@ -0,0 +1,377 @@
|
|||
// Tests that keyboard navigation in the search panel works as designed.
|
||||
|
||||
const searchbar = document.getElementById("searchbar");
|
||||
const textbox = searchbar._textbox;
|
||||
const searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid",
|
||||
"searchbar-search-button");
|
||||
|
||||
const kValues = ["foo1", "foo2", "foo3"];
|
||||
|
||||
// Get an array of the one-off buttons.
|
||||
function getOneOffs() {
|
||||
let oneOffs = [];
|
||||
let oneOff = document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"search-panel-one-offs");
|
||||
for (oneOff = oneOff.firstChild; oneOff; oneOff = oneOff.nextSibling) {
|
||||
if (oneOff.classList.contains("dummy"))
|
||||
break;
|
||||
oneOffs.push(oneOff);
|
||||
}
|
||||
|
||||
return oneOffs;
|
||||
}
|
||||
|
||||
function getOpenSearchItems() {
|
||||
let os = [];
|
||||
|
||||
let addEngineList =
|
||||
document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"add-engines");
|
||||
for (let item = addEngineList.firstChild; item; item = item.nextSibling)
|
||||
os.push(item);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
yield promiseNewEngine("testEngine.xml");
|
||||
|
||||
// First cleanup the form history in case other tests left things there.
|
||||
yield new Promise((resolve, reject) => {
|
||||
info("cleanup the search history");
|
||||
searchbar.FormHistory.update({op: "remove", fieldname: "searchbar-history"},
|
||||
{handleCompletion: resolve,
|
||||
handleError: reject});
|
||||
});
|
||||
|
||||
yield new Promise((resolve, reject) => {
|
||||
info("adding search history values: " + kValues);
|
||||
let ops = kValues.map(value => { return {op: "add",
|
||||
fieldname: "searchbar-history",
|
||||
value: value}
|
||||
});
|
||||
searchbar.FormHistory.update(ops, {
|
||||
handleCompletion: function() {
|
||||
registerCleanupFunction(() => {
|
||||
info("removing search history values: " + kValues);
|
||||
let ops =
|
||||
kValues.map(value => { return {op: "remove",
|
||||
fieldname: "searchbar-history",
|
||||
value: value}
|
||||
});
|
||||
searchbar.FormHistory.update(ops);
|
||||
});
|
||||
resolve();
|
||||
},
|
||||
handleError: reject
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_arrows() {
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
yield promise;
|
||||
info("textbox.mController.searchString = " + textbox.mController.searchString);
|
||||
is(textbox.mController.searchString, "", "The search string should be empty");
|
||||
|
||||
// Check the initial state of the panel before sending keyboard events.
|
||||
is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup");
|
||||
// Having suggestions populated (but hidden) is important, because if there
|
||||
// are none we can't ensure the keyboard events don't reach them.
|
||||
is(searchPopup.view.rowCount, kValues.length, "There should be 3 suggestions");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
|
||||
// The tests will be less meaningful if the first, second, last, and
|
||||
// before-last one-off buttons aren't different. We should always have more
|
||||
// than 4 default engines, but it's safer to check this assumption.
|
||||
let oneOffs = getOneOffs();
|
||||
ok(oneOffs.length >= 4, "we have at least 4 one-off buttons displayed")
|
||||
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
|
||||
// Pressing should select the first one-off.
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// now cycle through the one-off items, the first one is already selected.
|
||||
for (let i = 0; i < oneOffs.length; ++i) {
|
||||
is(textbox.selectedButton, oneOffs[i],
|
||||
"the one-off button #" + (i + 1) + " should be selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
}
|
||||
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
// We should now be back to the initial situation.
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
|
||||
info("now test the up arrow key");
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
|
||||
// cycle through the one-off items, the first one is already selected.
|
||||
for (let i = oneOffs.length; i; --i) {
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton, oneOffs[i - 1],
|
||||
"the one-off button #" + i + " should be selected");
|
||||
}
|
||||
|
||||
// Another press on up should clear the one-off selection.
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
});
|
||||
|
||||
add_task(function* test_tab() {
|
||||
is(Services.focus.focusedElement, textbox.inputField,
|
||||
"the search bar should be focused"); // from the previous test.
|
||||
|
||||
let oneOffs = getOneOffs();
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
|
||||
// Pressing tab should select the first one-off without selecting suggestions.
|
||||
// now cycle through the one-off items, the first one is already selected.
|
||||
for (let i = 0; i < oneOffs.length; ++i) {
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
is(textbox.selectedButton, oneOffs[i],
|
||||
"the one-off button #" + (i + 1) + " should be selected");
|
||||
}
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// One more <tab> selects the settings button.
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
|
||||
// Pressing tab again should close the panel...
|
||||
let promise = promiseEvent(searchPopup, "popuphidden");
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
yield promise;
|
||||
|
||||
// ... and move the focus out of the searchbox.
|
||||
isnot(Services.focus.focusedElement, textbox.inputField,
|
||||
"the search bar no longer be focused");
|
||||
});
|
||||
|
||||
add_task(function* test_shift_tab() {
|
||||
// First reopen the panel.
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
SimpleTest.executeSoon(() => {
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
});
|
||||
yield promise;
|
||||
|
||||
let oneOffs = getOneOffs();
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup");
|
||||
|
||||
// Press up once to select the last button.
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
|
||||
// Press up again to select the last one-off button.
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
|
||||
// Pressing shift+tab should cycle through the one-off items.
|
||||
for (let i = oneOffs.length - 1; i >= 0; --i) {
|
||||
is(textbox.selectedButton, oneOffs[i],
|
||||
"the one-off button #" + (i + 1) + " should be selected");
|
||||
if (i)
|
||||
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
|
||||
}
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// Pressing shift+tab again should close the panel...
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
|
||||
yield promise;
|
||||
|
||||
// ... and move the focus out of the searchbox.
|
||||
isnot(Services.focus.focusedElement, textbox.inputField,
|
||||
"the search bar no longer be focused");
|
||||
});
|
||||
|
||||
add_task(function* test_alt_down() {
|
||||
// First reopen the panel.
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
SimpleTest.executeSoon(() => {
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
});
|
||||
yield promise;
|
||||
|
||||
// and check it's in a correct initial state.
|
||||
is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup");
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// Pressing alt+down should select the first one-off without selecting suggestions
|
||||
// and cycle through the one-off items.
|
||||
let oneOffs = getOneOffs();
|
||||
for (let i = 0; i < oneOffs.length; ++i) {
|
||||
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
|
||||
is(textbox.selectedButton, oneOffs[i],
|
||||
"the one-off button #" + (i + 1) + " should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
}
|
||||
|
||||
// One more alt+down keypress and nothing should be selected.
|
||||
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
|
||||
// another one and the first one-off should be selected.
|
||||
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
|
||||
is(textbox.selectedButton, oneOffs[0],
|
||||
"the first one-off button should be selected");
|
||||
|
||||
// Clear the selection with an alt+up keypress
|
||||
EventUtils.synthesizeKey("VK_UP", {altKey: true});
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
});
|
||||
|
||||
add_task(function* test_alt_up() {
|
||||
// Check the initial state of the panel
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// Pressing alt+up should select the last one-off without selecting suggestions
|
||||
// and cycle up through the one-off items.
|
||||
let oneOffs = getOneOffs();
|
||||
for (let i = oneOffs.length - 1; i >= 0; --i) {
|
||||
EventUtils.synthesizeKey("VK_UP", {altKey: true});
|
||||
is(textbox.selectedButton, oneOffs[i],
|
||||
"the one-off button #" + (i + 1) + " should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
}
|
||||
|
||||
// One more alt+down keypress and nothing should be selected.
|
||||
EventUtils.synthesizeKey("VK_UP", {altKey: true});
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
|
||||
// another one and the last one-off should be selected.
|
||||
EventUtils.synthesizeKey("VK_UP", {altKey: true});
|
||||
is(textbox.selectedButton, oneOffs[oneOffs.length - 1],
|
||||
"the last one-off button should be selected");
|
||||
|
||||
// Cleanup for the next test.
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
ok(!textbox.selectedButton, "no one-off should be selected anymore");
|
||||
});
|
||||
|
||||
add_task(function* test_tab_and_arrows() {
|
||||
// Check the initial state is as expected.
|
||||
ok(!textbox.selectedButton, "no one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
is(textbox.value, "", "the textfield value should be unmodified");
|
||||
|
||||
// After pressing down, the first one-off should be selected.
|
||||
let oneOffs = getOneOffs();
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(textbox.selectedButton, oneOffs[0],
|
||||
"the first one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
|
||||
// After pressing tab, the second one-off should be selected.
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
is(textbox.selectedButton, oneOffs[1],
|
||||
"the second one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
|
||||
// After pressing up, the first one-off should be selected again.
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton, oneOffs[0],
|
||||
"the first one-off button should be selected");
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
|
||||
// Finally close the panel.
|
||||
let promise = promiseEvent(searchPopup, "popuphidden");
|
||||
searchPopup.hidePopup();
|
||||
yield promise;
|
||||
});
|
||||
|
||||
add_task(function* test_open_search() {
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
content.location = rootDir + "opensearch.html";
|
||||
|
||||
yield deferred.promise;
|
||||
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
yield promise;
|
||||
is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup");
|
||||
|
||||
let engines = getOpenSearchItems();
|
||||
is(engines.length, 2, "the opensearch.html page exposes 2 engines")
|
||||
|
||||
// Check that there's initially no selection.
|
||||
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
|
||||
ok(!textbox.selectedButton, "no button should be selected");
|
||||
|
||||
// Pressing up once selects the setting button...
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
|
||||
// ...and then pressing up selects open search engines.
|
||||
for (let i = engines.length; i; --i) {
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
let selectedButton = textbox.selectedButton;
|
||||
is(selectedButton, engines[i - 1],
|
||||
"the engine #" + i + " should be selected");
|
||||
ok(selectedButton.classList.contains("addengine-item"),
|
||||
"the button is themed as an engine item");
|
||||
}
|
||||
|
||||
// Pressing up again should select the last one-off button.
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(textbox.selectedButton, getOneOffs().pop(),
|
||||
"the last one-off button should be selected");
|
||||
|
||||
info("now check that the down key navigates open search items as expected");
|
||||
for (let i = 0; i < engines.length; ++i) {
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(textbox.selectedButton, engines[i],
|
||||
"the engine #" + (i + 1) + " should be selected");
|
||||
}
|
||||
|
||||
// Pressing down on the last engine item selects the settings button.
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(textbox.selectedButton.getAttribute("anonid"), "search-settings",
|
||||
"the settings item should be selected");
|
||||
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
searchPopup.hidePopup();
|
||||
yield promise;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
|
@ -136,9 +136,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
* - staged: true to stage the item to be appended later
|
||||
*/
|
||||
addSource: function(aSource, aOptions = {}) {
|
||||
if (!(aSource.url || aSource.introductionUrl)) {
|
||||
// These would be most likely eval scripts introduced in inline
|
||||
// JavaScript in HTML, and we don't show those yet (bug 1097873)
|
||||
if (!aSource.url) {
|
||||
// We don't show any unnamed eval scripts yet (see bug 1124106)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -170,21 +169,10 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
},
|
||||
|
||||
_parseUrl: function(aSource) {
|
||||
let fullUrl = aSource.url || aSource.introductionUrl;
|
||||
let fullUrl = aSource.url;
|
||||
let url = fullUrl.split(" -> ").pop();
|
||||
let label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
|
||||
let group;
|
||||
|
||||
if (!aSource.url && aSource.introductionUrl) {
|
||||
label += ' > ' + aSource.introductionType;
|
||||
group = L10N.getStr("evalGroupLabel");
|
||||
}
|
||||
else if(aSource.addonID) {
|
||||
group = aSource.addonID;
|
||||
}
|
||||
else {
|
||||
group = SourceUtils.getSourceGroup(url);
|
||||
}
|
||||
let group = aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url);
|
||||
|
||||
return {
|
||||
label: label,
|
||||
|
|
|
@ -407,7 +407,7 @@ skip-if = e10s # Bug 1093535
|
|||
[browser_dbg_sources-cache.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_sources-eval-01.js]
|
||||
skip-if = e10s && debug
|
||||
skip-if = true # non-named eval sources turned off for now, bug 1124106
|
||||
[browser_dbg_sources-eval-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_sources-labels.js]
|
||||
|
|
|
@ -28,16 +28,16 @@ function test() {
|
|||
function run() {
|
||||
return Task.spawn(function*() {
|
||||
let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
|
||||
callInTab(gTab, "evalSource");
|
||||
callInTab(gTab, "evalSourceWithSourceURL");
|
||||
yield newSource;
|
||||
|
||||
yield gPanel.addBreakpoint({ actor: gSources.values[1], line: 2 });
|
||||
yield gPanel.addBreakpoint({ actor: gSources.values[0], line: 2 });
|
||||
yield ensureThreadClientState(gPanel, "resumed");
|
||||
|
||||
const paused = waitForThreadEvents(gPanel, "paused");
|
||||
callInTab(gTab, "bar");
|
||||
let frame = (yield paused).frame;
|
||||
is(frame.where.source.actor, gSources.values[1], "Should have broken on the eval'ed source");
|
||||
is(frame.where.source.actor, gSources.values[0], "Should have broken on the eval'ed source");
|
||||
is(frame.where.line, 2, "Should break on line 2");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.0.1130
|
||||
Current extension version is: 1.0.1149
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.0.1130';
|
||||
PDFJS.build = 'e4f0ae2';
|
||||
PDFJS.version = '1.0.1149';
|
||||
PDFJS.build = 'bc7a110';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -356,6 +356,7 @@ function shadow(obj, prop, value) {
|
|||
writable: false });
|
||||
return value;
|
||||
}
|
||||
PDFJS.shadow = shadow;
|
||||
|
||||
var PasswordResponses = PDFJS.PasswordResponses = {
|
||||
NEED_PASSWORD: 1,
|
||||
|
|
|
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '1.0.1130';
|
||||
PDFJS.build = 'e4f0ae2';
|
||||
PDFJS.version = '1.0.1149';
|
||||
PDFJS.build = 'bc7a110';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -356,6 +356,7 @@ function shadow(obj, prop, value) {
|
|||
writable: false });
|
||||
return value;
|
||||
}
|
||||
PDFJS.shadow = shadow;
|
||||
|
||||
var PasswordResponses = PDFJS.PasswordResponses = {
|
||||
NEED_PASSWORD: 1,
|
||||
|
@ -4800,7 +4801,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() {
|
|||
data.annotationType = AnnotationType.LINK;
|
||||
|
||||
var action = dict.get('A');
|
||||
if (action) {
|
||||
if (action && isDict(action)) {
|
||||
var linkType = action.get('S').name;
|
||||
if (linkType === 'URI') {
|
||||
var url = action.get('URI');
|
||||
|
@ -15743,6 +15744,10 @@ var ToUnicodeMap = (function ToUnicodeMapClosure() {
|
|||
}
|
||||
},
|
||||
|
||||
has: function(i) {
|
||||
return this._map[i] !== undefined;
|
||||
},
|
||||
|
||||
get: function(i) {
|
||||
return this._map[i];
|
||||
},
|
||||
|
@ -15772,6 +15777,10 @@ var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() {
|
|||
}
|
||||
},
|
||||
|
||||
has: function (i) {
|
||||
return this.firstChar <= i && i <= this.lastChar;
|
||||
},
|
||||
|
||||
get: function (i) {
|
||||
if (this.firstChar <= i && i <= this.lastChar) {
|
||||
return String.fromCharCode(i);
|
||||
|
@ -16204,7 +16213,6 @@ var Font = (function FontClosure() {
|
|||
var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
|
||||
var isIdentityUnicode =
|
||||
properties.toUnicode instanceof IdentityToUnicodeMap;
|
||||
var isCidFontType2 = (properties.type === 'CIDFontType2');
|
||||
var newMap = Object.create(null);
|
||||
var toFontChar = [];
|
||||
var usedFontCharCodes = [];
|
||||
|
@ -16215,17 +16223,11 @@ var Font = (function FontClosure() {
|
|||
var fontCharCode = originalCharCode;
|
||||
// First try to map the value to a unicode position if a non identity map
|
||||
// was created.
|
||||
if (!isIdentityUnicode) {
|
||||
if (toUnicode.get(originalCharCode) !== undefined) {
|
||||
var unicode = toUnicode.get(fontCharCode);
|
||||
// TODO: Try to map ligatures to the correct spot.
|
||||
if (unicode.length === 1) {
|
||||
fontCharCode = unicode.charCodeAt(0);
|
||||
}
|
||||
} else if (isCidFontType2) {
|
||||
// For CIDFontType2, move characters not present in toUnicode
|
||||
// to the private use area (fixes bug 1028735 and issue 4881).
|
||||
fontCharCode = nextAvailableFontCharCode;
|
||||
if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
|
||||
var unicode = toUnicode.get(fontCharCode);
|
||||
// TODO: Try to map ligatures to the correct spot.
|
||||
if (unicode.length === 1) {
|
||||
fontCharCode = unicode.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
// Try to move control characters, special characters and already mapped
|
||||
|
@ -16715,13 +16717,20 @@ var Font = (function FontClosure() {
|
|||
var offset = font.getInt32() >>> 0;
|
||||
var useTable = false;
|
||||
|
||||
if (platformId === 1 && encodingId === 0) {
|
||||
if (platformId === 0 && encodingId === 0) {
|
||||
useTable = true;
|
||||
// Continue the loop since there still may be a higher priority
|
||||
// table.
|
||||
} else if (!isSymbolicFont && platformId === 3 && encodingId === 1) {
|
||||
} else if (platformId === 1 && encodingId === 0) {
|
||||
useTable = true;
|
||||
canBreak = true;
|
||||
// Continue the loop since there still may be a higher priority
|
||||
// table.
|
||||
} else if (platformId === 3 && encodingId === 1 &&
|
||||
(!isSymbolicFont || !potentialTable)) {
|
||||
useTable = true;
|
||||
if (!isSymbolicFont) {
|
||||
canBreak = true;
|
||||
}
|
||||
} else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
|
||||
useTable = true;
|
||||
canBreak = true;
|
||||
|
@ -17066,6 +17075,7 @@ var Font = (function FontClosure() {
|
|||
var newGlyfData = new Uint8Array(oldGlyfDataLength);
|
||||
var startOffset = itemDecode(locaData, 0);
|
||||
var writeOffset = 0;
|
||||
var missingGlyphData = {};
|
||||
itemEncode(locaData, 0, writeOffset);
|
||||
var i, j;
|
||||
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
||||
|
@ -17083,6 +17093,10 @@ var Font = (function FontClosure() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (startOffset === endOffset) {
|
||||
missingGlyphData[i] = true;
|
||||
}
|
||||
|
||||
var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
|
||||
newGlyfData, writeOffset, hintsValid);
|
||||
writeOffset += newLength;
|
||||
|
@ -17099,7 +17113,7 @@ var Font = (function FontClosure() {
|
|||
itemEncode(locaData, j, simpleGlyph.length);
|
||||
}
|
||||
glyf.data = simpleGlyph;
|
||||
return;
|
||||
return missingGlyphData;
|
||||
}
|
||||
|
||||
if (dupFirstEntry) {
|
||||
|
@ -17116,6 +17130,7 @@ var Font = (function FontClosure() {
|
|||
} else {
|
||||
glyf.data = newGlyfData.subarray(0, writeOffset);
|
||||
}
|
||||
return missingGlyphData;
|
||||
}
|
||||
|
||||
function readPostScriptTable(post, properties, maxpNumGlyphs) {
|
||||
|
@ -17575,11 +17590,13 @@ var Font = (function FontClosure() {
|
|||
|
||||
sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);
|
||||
|
||||
var missingGlyphs = {};
|
||||
if (isTrueType) {
|
||||
var isGlyphLocationsLong = int16(tables.head.data[50],
|
||||
tables.head.data[51]);
|
||||
sanitizeGlyphLocations(tables.loca, tables.glyf, numGlyphs,
|
||||
isGlyphLocationsLong, hintsValid, dupFirstEntry);
|
||||
missingGlyphs = sanitizeGlyphLocations(tables.loca, tables.glyf,
|
||||
numGlyphs, isGlyphLocationsLong,
|
||||
hintsValid, dupFirstEntry);
|
||||
}
|
||||
|
||||
if (!tables.hhea) {
|
||||
|
@ -17601,19 +17618,33 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
}
|
||||
|
||||
var charCodeToGlyphId = [], charCode;
|
||||
var charCodeToGlyphId = [], charCode, toUnicode = properties.toUnicode;
|
||||
|
||||
function hasGlyph(glyphId, charCode) {
|
||||
if (!missingGlyphs[glyphId]) {
|
||||
return true;
|
||||
}
|
||||
if (charCode >= 0 && toUnicode.has(charCode)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (properties.type === 'CIDFontType2') {
|
||||
var cidToGidMap = properties.cidToGidMap || [];
|
||||
var cidToGidMapLength = cidToGidMap.length;
|
||||
var isCidToGidMapEmpty = cidToGidMap.length === 0;
|
||||
|
||||
properties.cMap.forEach(function(charCode, cid) {
|
||||
assert(cid <= 0xffff, 'Max size of CID is 65,535');
|
||||
var glyphId = -1;
|
||||
if (cidToGidMapLength === 0) {
|
||||
if (isCidToGidMapEmpty) {
|
||||
glyphId = charCode;
|
||||
} else if (cidToGidMap[cid] !== undefined) {
|
||||
glyphId = cidToGidMap[cid];
|
||||
}
|
||||
if (glyphId >= 0 && glyphId < numGlyphs) {
|
||||
|
||||
if (glyphId >= 0 && glyphId < numGlyphs &&
|
||||
hasGlyph(glyphId, charCode)) {
|
||||
charCodeToGlyphId[charCode] = glyphId;
|
||||
}
|
||||
});
|
||||
|
@ -17673,7 +17704,8 @@ var Font = (function FontClosure() {
|
|||
|
||||
var found = false;
|
||||
for (i = 0; i < cmapMappingsLength; ++i) {
|
||||
if (cmapMappings[i].charCode === unicodeOrCharCode) {
|
||||
if (cmapMappings[i].charCode === unicodeOrCharCode &&
|
||||
hasGlyph(cmapMappings[i].glyphId, unicodeOrCharCode)) {
|
||||
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
|
||||
found = true;
|
||||
break;
|
||||
|
@ -17683,11 +17715,17 @@ var Font = (function FontClosure() {
|
|||
// Try to map using the post table. There are currently no known
|
||||
// pdfs that this fixes.
|
||||
var glyphId = properties.glyphNames.indexOf(glyphName);
|
||||
if (glyphId > 0) {
|
||||
if (glyphId > 0 && hasGlyph(glyphId, -1)) {
|
||||
charCodeToGlyphId[charCode] = glyphId;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (cmapPlatformId === 0 && cmapEncodingId === 0) {
|
||||
// Default Unicode semantics, use the charcodes as is.
|
||||
for (i = 0; i < cmapMappingsLength; ++i) {
|
||||
charCodeToGlyphId[cmapMappings[i].charCode] =
|
||||
cmapMappings[i].glyphId;
|
||||
}
|
||||
} else {
|
||||
// For (3, 0) cmap tables:
|
||||
// The charcode key being stored in charCodeToGlyphId is the lower
|
||||
|
@ -32715,6 +32753,10 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||
|
||||
var gotEOL = false;
|
||||
|
||||
if (this.byteAlign) {
|
||||
this.inputBits &= ~7;
|
||||
}
|
||||
|
||||
if (!this.eoblock && this.row === this.rows - 1) {
|
||||
this.eof = true;
|
||||
} else {
|
||||
|
@ -32738,10 +32780,6 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.byteAlign && !gotEOL) {
|
||||
this.inputBits &= ~7;
|
||||
}
|
||||
|
||||
if (!this.eof && this.encoding > 0) {
|
||||
this.nextLine2D = !this.lookBits(1);
|
||||
this.eatBits(1);
|
||||
|
|
|
@ -5481,12 +5481,8 @@ var PDFViewerApplication = {
|
|||
get supportsPrinting() {
|
||||
var canvas = document.createElement('canvas');
|
||||
var value = 'mozPrintCallback' in canvas;
|
||||
// shadow
|
||||
Object.defineProperty(this, 'supportsPrinting', { value: value,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return value;
|
||||
|
||||
return PDFJS.shadow(this, 'supportsPrinting', value);
|
||||
},
|
||||
|
||||
get supportsFullscreen() {
|
||||
|
@ -5501,50 +5497,34 @@ var PDFViewerApplication = {
|
|||
support = false;
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'supportsFullscreen', { value: support,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return support;
|
||||
return PDFJS.shadow(this, 'supportsFullscreen', support);
|
||||
},
|
||||
|
||||
get supportsIntegratedFind() {
|
||||
var support = false;
|
||||
support = FirefoxCom.requestSync('supportsIntegratedFind');
|
||||
Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return support;
|
||||
|
||||
return PDFJS.shadow(this, 'supportsIntegratedFind', support);
|
||||
},
|
||||
|
||||
get supportsDocumentFonts() {
|
||||
var support = true;
|
||||
support = FirefoxCom.requestSync('supportsDocumentFonts');
|
||||
Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return support;
|
||||
|
||||
return PDFJS.shadow(this, 'supportsDocumentFonts', support);
|
||||
},
|
||||
|
||||
get supportsDocumentColors() {
|
||||
var support = true;
|
||||
support = FirefoxCom.requestSync('supportsDocumentColors');
|
||||
Object.defineProperty(this, 'supportsDocumentColors', { value: support,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return support;
|
||||
|
||||
return PDFJS.shadow(this, 'supportsDocumentColors', support);
|
||||
},
|
||||
|
||||
get loadingBar() {
|
||||
var bar = new ProgressBar('#loadingBar', {});
|
||||
Object.defineProperty(this, 'loadingBar', { value: bar,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false });
|
||||
return bar;
|
||||
|
||||
return PDFJS.shadow(this, 'loadingBar', bar);
|
||||
},
|
||||
|
||||
initPassiveLoading: function pdfViewInitPassiveLoading() {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
## LOCALIZATION NOTE(clientShortname2): This should not be localized and
|
||||
## should remain "Firefox Hello" for all locales.
|
||||
clientShortname2=Firefox Hello
|
||||
clientSuperShortname=Hello
|
||||
|
||||
rooms_tab_button_tooltip=Conversations
|
||||
contacts_tab_button_tooltip=Contacts
|
||||
|
@ -43,10 +44,11 @@ problem_accessing_account=There Was A Problem Accessing Your Account
|
|||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error for location
|
||||
retry_button=Retry
|
||||
|
||||
share_email_subject4={{clientShortname}} — Join the conversation
|
||||
share_email_subject5={{clientShortname2}} — Join the conversation
|
||||
## LOCALIZATION NOTE (share_email_body4): In this item, don't translate the
|
||||
## part between {{..}} and leave the \r\n\r\n part alone
|
||||
share_email_body4=Hello!\r\n\r\nJoin me for a video conversation using {{clientShortname}}:\r\n\r\nYou don't have to download or install anything. Just copy and paste this URL into your browser:\r\n\r\n{{callUrl}}\r\n\r\nIf you want, you can also learn more about {{clientShortname}} at {{learnMoreUrl}}\r\n\r\nTalk to you soon!
|
||||
## part between {{..}} and leave the \n\n part alone
|
||||
share_email_body5=Hello!\n\nJoin me for a video conversation on {{clientShortname2}}.\n\nIt's the easiest way to connect by video with anyone anywhere. With {{clientSuperShortname}}, you don't have to download or install anything. Just click or paste this link into your {{brandShortname}}, Opera, or Chrome browser to join the conversation:\n\n{{callUrl}}\n\nIf you'd like to learn more about {{clientSuperShortname}} and how you can start your own free video conversations, visit {{learnMoreUrl}}\n\nTalk to you soon!
|
||||
|
||||
share_button2=Email Link
|
||||
copy_url_button2=Copy Link
|
||||
copied_url_button=Copied!
|
||||
|
@ -167,6 +169,14 @@ audio_call_menu_button=Audio Conversation
|
|||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#contacts
|
||||
video_call_menu_button=Video Conversation
|
||||
|
||||
## LOCALIZATION NOTE(gravatars_promo_message): The {{learn_more}} part will be
|
||||
## replaced with a link with the text content of gravatars_promo_message_learnmore.
|
||||
gravatars_promo_message=You can automatically add profile icons to your contacts \
|
||||
by sharing their email addresses with Gravatar. {{learn_more}}.
|
||||
gravatars_promo_message_learnmore=Learn more
|
||||
gravatars_promo_button_nothanks=No Thanks
|
||||
gravatars_promo_button_use=Use Profile Icons
|
||||
|
||||
# Conversation Window Strings
|
||||
|
||||
initiate_call_button_label2=Ready to start your conversation?
|
||||
|
@ -185,6 +195,7 @@ mute_local_video_button_title=Mute your video
|
|||
unmute_local_video_button_title=Unmute your video
|
||||
active_screenshare_button_title=Stop sharing
|
||||
inactive_screenshare_button_title=Share your screen
|
||||
share_tabs_button_title=Share my Tabs
|
||||
share_windows_button_title=Share other Windows
|
||||
|
||||
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
|
||||
|
@ -312,3 +323,11 @@ rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
|
|||
rooms_room_joined_label=Someone has joined the conversation!
|
||||
rooms_room_join_label=Join the conversation
|
||||
rooms_signout_alert=Open conversations will be closed
|
||||
|
||||
# Infobar strings
|
||||
|
||||
infobar_screenshare_browser_message=Users in your conversation will now be able to see the contents of any tab you click on.
|
||||
infobar_button_gotit_label=Got it!
|
||||
infobar_button_gotit_accesskey=G
|
||||
infobar_menuitem_dontshowagain_label=Don't show this again
|
||||
infobar_menuitem_dontshowagain_accesskey=D
|
||||
|
|
|
@ -13,6 +13,10 @@ phishBeforeText=Selecting this option will send the address of web pages you are
|
|||
|
||||
labelDefaultFont=Default (%S)
|
||||
|
||||
veryLargeMinimumFontTitle=Large minimum font size
|
||||
veryLargeMinimumFontWarning=You have selected a very large minimum font size (more than 24 pixels). This may make it difficult or impossible to use some important configuration pages like this one.
|
||||
acceptVeryLargeMinimumFont=Keep my changes anyway
|
||||
|
||||
#### Permissions Manager
|
||||
|
||||
cookiepermissionstext=You can specify which websites are always or never allowed to use cookies. Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
|
||||
|
|
|
@ -23,5 +23,12 @@ cmd_clearHistory_accesskey=H
|
|||
cmd_showSuggestions=Show Suggestions
|
||||
cmd_showSuggestions_accesskey=S
|
||||
|
||||
# LOCALIZATION NOTE (cmd_addFoundEngine): %S is replaced by the name of
|
||||
# a search engine offered by a web page. Each engine is displayed as a
|
||||
# menuitem at the bottom of the search panel.
|
||||
cmd_addFoundEngine=Add "%S"
|
||||
|
||||
# LOCALIZATION NOTE (cmd_addFoundEngineMenu): When more than 5 engines
|
||||
# are offered by a web page, instead of listing all of them in the
|
||||
# search panel using the cmd_addFoundEngine string, they will be
|
||||
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
|
||||
cmd_addFoundEngineMenu=Add search engine
|
||||
|
|
|
@ -198,7 +198,9 @@ let AboutHome = {
|
|||
#endif
|
||||
// Trigger a search through nsISearchEngine.getSubmission()
|
||||
let submission = engine.getSubmission(data.searchTerms, null, "homepage");
|
||||
window.loadURI(submission.uri.spec, null, submission.postData);
|
||||
let where = data.useNewTab ? "tab" : "current";
|
||||
window.openUILinkIn(submission.uri.spec, where, false,
|
||||
submission.postData);
|
||||
|
||||
// Used for testing
|
||||
let mm = aMessage.target.messageManager;
|
||||
|
|
|
@ -599,6 +599,10 @@ this.BrowserUITelemetry = {
|
|||
this._countEvent(["click-builtin-item", source, "search-settings"]);
|
||||
},
|
||||
|
||||
countPanicEvent: function(timeId) {
|
||||
this._countEvent(["forget-button", timeId]);
|
||||
},
|
||||
|
||||
_logAwesomeBarSearchResult: function (url) {
|
||||
let spec = Services.search.parseSubmissionURL(url);
|
||||
if (spec.engine) {
|
||||
|
|
|
@ -210,6 +210,11 @@ this.ContentSearch = {
|
|||
let engine = Services.search.getEngineByName(data.engineName);
|
||||
let submission = engine.getSubmission(data.searchString, "", data.whence);
|
||||
let browser = msg.target;
|
||||
let newTab;
|
||||
if (data.useNewTab) {
|
||||
newTab = browser.getTabBrowser().addTab();
|
||||
browser = newTab.linkedBrowser;
|
||||
}
|
||||
try {
|
||||
browser.loadURIWithFlags(submission.uri.spec,
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null,
|
||||
|
@ -221,6 +226,9 @@ this.ContentSearch = {
|
|||
// method on it will throw.
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (data.useNewTab) {
|
||||
browser.getTabBrowser().selectedTab = newTab;
|
||||
}
|
||||
let win = browser.ownerDocument.defaultView;
|
||||
win.BrowserSearch.recordSearchInHealthReport(engine, data.whence,
|
||||
data.selection || null);
|
||||
|
|
|
@ -116,10 +116,13 @@ searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon
|
|||
|
||||
.search-panel-current-engine {
|
||||
border-top: none !important;
|
||||
border-bottom: 1px solid #ccc;
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
.search-panel-current-engine:not([showonlysettings]) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-panel-header {
|
||||
font-weight: normal;
|
||||
background-color: rgb(245, 245, 245);
|
||||
|
|
|
@ -139,10 +139,13 @@ searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon
|
|||
|
||||
.search-panel-current-engine {
|
||||
border-top: none !important;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.search-panel-current-engine:not([showonlysettings]) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-panel-header {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
|
@ -287,10 +290,6 @@ searchbar[oneoffui] .searchbar-engine-button {
|
|||
min-height: 32px;
|
||||
}
|
||||
|
||||
.search-setting-button[showonlysettings] {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-setting-button[selected] {
|
||||
background-color: #d3d3d3;
|
||||
border-top-color: #bdbebe;
|
||||
|
|
|
@ -128,10 +128,13 @@ searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon
|
|||
|
||||
.search-panel-current-engine {
|
||||
border-top: none !important;
|
||||
border-bottom: 1px solid #ccc;
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
.search-panel-current-engine:not([showonlysettings]) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-panel-header {
|
||||
font-weight: normal;
|
||||
background-color: rgb(245, 245, 245);
|
||||
|
|
|
@ -8535,6 +8535,7 @@ AC_SUBST(MOZ_WEBSMS_BACKEND)
|
|||
AC_SUBST(MOZ_ANDROID_BEAM)
|
||||
AC_SUBST(MOZ_LOCALE_SWITCHER)
|
||||
AC_SUBST(MOZ_DISABLE_GECKOVIEW)
|
||||
AC_SUBST(MOZ_ANDROID_GECKOLIBS_AAR)
|
||||
AC_SUBST(MOZ_ANDROID_READING_LIST_SERVICE)
|
||||
AC_SUBST(MOZ_ANDROID_SEARCH_ACTIVITY)
|
||||
AC_SUBST(MOZ_ANDROID_SHARE_OVERLAY)
|
||||
|
|
|
@ -1,12 +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/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
PATTERN_FILES = $(strip $(wildcard $(srcdir)/*/hyphenation/*.dic))
|
||||
|
||||
ifneq (,$(PATTERN_FILES))
|
||||
libs::
|
||||
$(INSTALL) $(PATTERN_FILES) $(FINAL_TARGET)/hyphenation
|
||||
endif
|
|
@ -4,3 +4,45 @@
|
|||
# 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/.
|
||||
|
||||
locales = [
|
||||
'af',
|
||||
'bg',
|
||||
'ca',
|
||||
'cy',
|
||||
'da',
|
||||
'de-1901',
|
||||
'de-1996',
|
||||
'de-CH',
|
||||
# 'en-US', # en-US is renamed -- see below.
|
||||
'eo',
|
||||
'es',
|
||||
'et',
|
||||
'fi',
|
||||
'fr',
|
||||
'gl',
|
||||
'hr',
|
||||
'hsb',
|
||||
'hu',
|
||||
'ia',
|
||||
'is',
|
||||
'it',
|
||||
'kmr',
|
||||
'la',
|
||||
'lt',
|
||||
'mn',
|
||||
'nb',
|
||||
'nl',
|
||||
'nn',
|
||||
'pl',
|
||||
'pt',
|
||||
'ru',
|
||||
'sh',
|
||||
'sl',
|
||||
'sv',
|
||||
'tr',
|
||||
'uk',
|
||||
]
|
||||
filename = '{locale}/hyphenation/hyph_{locale}.dic'
|
||||
FINAL_TARGET_FILES.hyphenation += [filename.format(locale=locale) for locale in locales]
|
||||
# en-US is a special case: the dic file is named like en_US.
|
||||
FINAL_TARGET_FILES.hyphenation += ['en-US/hyphenation/hyph_en_US.dic']
|
||||
|
|
|
@ -860,11 +860,5 @@ pref("reader.color_scheme", "auto");
|
|||
// Color scheme values available in reader mode UI.
|
||||
pref("reader.color_scheme.values", "[\"light\",\"dark\",\"auto\"]");
|
||||
|
||||
// The font type in reader (charis-sil, clear-sans)
|
||||
pref("reader.font_type", "clear-sans");
|
||||
|
||||
// Font type values available in reader mode UI.
|
||||
pref("reader.font_type.values", "[\"charis-sil\",\"clear-sans\"]");
|
||||
|
||||
// Whether to use a vertical or horizontal toolbar.
|
||||
pref("reader.toolbar.vertical", false);
|
||||
|
|
|
@ -154,6 +154,8 @@ public class BrowserApp extends GeckoApp
|
|||
LayoutInflater.Factory {
|
||||
private static final String LOGTAG = "GeckoBrowserApp";
|
||||
|
||||
private static final boolean ZOOMED_VIEW_ENABLED = AppConstants.NIGHTLY_BUILD;
|
||||
|
||||
private static final int TABS_ANIMATION_DURATION = 450;
|
||||
|
||||
private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
|
||||
|
@ -181,10 +183,10 @@ public class BrowserApp extends GeckoApp
|
|||
private HomePager mHomePager;
|
||||
private TabsPanel mTabsPanel;
|
||||
private ViewGroup mHomePagerContainer;
|
||||
protected Telemetry.Timer mAboutHomeStartupTimer;
|
||||
private ActionModeCompat mActionMode;
|
||||
private boolean mHideDynamicToolbarOnActionModeEnd;
|
||||
private TabHistoryController tabHistoryController;
|
||||
private ZoomedView mZoomedView;
|
||||
|
||||
private static final int GECKO_TOOLS_MENU = -1;
|
||||
private static final int ADDON_MENU_OFFSET = 1000;
|
||||
|
@ -657,8 +659,6 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME");
|
||||
|
||||
final Intent intent = getIntent();
|
||||
|
||||
// Note that we're calling GeckoProfile.get *before GeckoApp.onCreate*.
|
||||
|
@ -827,6 +827,11 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
// Set the maximum bits-per-pixel the favicon system cares about.
|
||||
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
|
||||
|
||||
if (ZOOMED_VIEW_ENABLED) {
|
||||
ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
|
||||
mZoomedView = (ZoomedView) stub.inflate();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupSystemUITinting() {
|
||||
|
@ -1301,6 +1306,9 @@ public class BrowserApp extends GeckoApp
|
|||
mReadingListHelper.uninit();
|
||||
mReadingListHelper = null;
|
||||
}
|
||||
if (mZoomedView != null) {
|
||||
mZoomedView.destroy();
|
||||
}
|
||||
|
||||
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
|
||||
"Menu:Open",
|
||||
|
@ -1378,16 +1386,6 @@ public class BrowserApp extends GeckoApp
|
|||
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadStartupTab(String url, int flags) {
|
||||
// We aren't showing about:home, so cancel the telemetry timer
|
||||
if (url != null || mShouldRestore) {
|
||||
mAboutHomeStartupTimer.cancel();
|
||||
}
|
||||
|
||||
super.loadStartupTab(url, flags);
|
||||
}
|
||||
|
||||
private void setToolbarMargin(int margin) {
|
||||
((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin;
|
||||
mGeckoLayout.requestLayout();
|
||||
|
|
|
@ -129,8 +129,6 @@ public abstract class GeckoApp
|
|||
private static final String LOGTAG = "GeckoApp";
|
||||
private static final int ONE_DAY_MS = 1000*60*60*24;
|
||||
|
||||
private static final boolean ZOOMED_VIEW_ENABLED = AppConstants.NIGHTLY_BUILD;
|
||||
|
||||
private static enum StartupAction {
|
||||
NORMAL, /* normal application start */
|
||||
URL, /* launched with a passed URL */
|
||||
|
@ -175,7 +173,6 @@ public abstract class GeckoApp
|
|||
private ContactService mContactService;
|
||||
private PromptService mPromptService;
|
||||
private TextSelection mTextSelection;
|
||||
private ZoomedView mZoomedView;
|
||||
|
||||
protected DoorHangerPopup mDoorHangerPopup;
|
||||
protected FormAssistPopup mFormAssistPopup;
|
||||
|
@ -1578,11 +1575,6 @@ public abstract class GeckoApp
|
|||
(TextSelectionHandle) findViewById(R.id.caret_handle),
|
||||
(TextSelectionHandle) findViewById(R.id.focus_handle));
|
||||
|
||||
if (ZOOMED_VIEW_ENABLED) {
|
||||
ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
|
||||
mZoomedView = (ZoomedView) stub.inflate();
|
||||
}
|
||||
|
||||
// Trigger the completion of the telemetry timer that wraps activity startup,
|
||||
// then grab the duration to give to FHR.
|
||||
mJavaUiStartupTimer.stop();
|
||||
|
@ -2049,9 +2041,6 @@ public abstract class GeckoApp
|
|||
mPromptService.destroy();
|
||||
if (mTextSelection != null)
|
||||
mTextSelection.destroy();
|
||||
if (mZoomedView != null) {
|
||||
mZoomedView.destroy();
|
||||
}
|
||||
NotificationHelper.destroy();
|
||||
IntentHelper.destroy();
|
||||
GeckoNetworkManager.destroy();
|
||||
|
|
|
@ -179,6 +179,10 @@ public class Tab {
|
|||
return mUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base domain of the loaded uri. Note that if the page is
|
||||
* a Reader mode uri, the base domain returned is that of the original uri.
|
||||
*/
|
||||
public String getBaseDomain() {
|
||||
return mBaseDomain;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
package org.mozilla.gecko.db;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.Build;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
|
||||
|
@ -15,6 +19,9 @@ import android.net.Uri;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DBUtils {
|
||||
private static final String LOGTAG = "GeckoDBUtils";
|
||||
|
@ -173,4 +180,175 @@ public class DBUtils {
|
|||
}
|
||||
return appendProfile(profile, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the following when no conflict action is specified.
|
||||
*/
|
||||
private static final int CONFLICT_NONE = 0;
|
||||
private static final String[] CONFLICT_VALUES = new String[] {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
|
||||
|
||||
/**
|
||||
* Convenience method for updating rows in the database.
|
||||
*
|
||||
* @param table the table to update in
|
||||
* @param values a map from column names to new column values. null is a
|
||||
* valid value that will be translated to NULL.
|
||||
* @param whereClause the optional WHERE clause to apply when updating.
|
||||
* Passing null will update all rows.
|
||||
* @param whereArgs You may include ?s in the where clause, which
|
||||
* will be replaced by the values from whereArgs. The values
|
||||
* will be bound as Strings.
|
||||
* @return the number of rows affected
|
||||
*/
|
||||
@RobocopTarget
|
||||
public static int updateArrays(SQLiteDatabase db, String table, ContentValues[] values, UpdateOperation[] ops, String whereClause, String[] whereArgs) {
|
||||
return updateArraysWithOnConflict(db, table, values, ops, whereClause, whereArgs, CONFLICT_NONE, true);
|
||||
}
|
||||
|
||||
public static void updateArraysBlindly(SQLiteDatabase db, String table, ContentValues[] values, UpdateOperation[] ops, String whereClause, String[] whereArgs) {
|
||||
updateArraysWithOnConflict(db, table, values, ops, whereClause, whereArgs, CONFLICT_NONE, false);
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public enum UpdateOperation {
|
||||
ASSIGN,
|
||||
BITWISE_OR,
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an evil reimplementation of SQLiteDatabase's methods to allow for
|
||||
* smarter updating.
|
||||
*
|
||||
* Each ContentValues has an associated enum that describes how to unify input values with the existing column values.
|
||||
*/
|
||||
private static int updateArraysWithOnConflict(SQLiteDatabase db, String table,
|
||||
ContentValues[] values,
|
||||
UpdateOperation[] ops,
|
||||
String whereClause,
|
||||
String[] whereArgs,
|
||||
int conflictAlgorithm,
|
||||
boolean returnChangedRows) {
|
||||
if (values == null || values.length == 0) {
|
||||
throw new IllegalArgumentException("Empty values");
|
||||
}
|
||||
|
||||
if (ops == null || ops.length != values.length) {
|
||||
throw new IllegalArgumentException("ops and values don't match");
|
||||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder(120);
|
||||
sql.append("UPDATE ");
|
||||
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
|
||||
sql.append(table);
|
||||
sql.append(" SET ");
|
||||
|
||||
// move all bind args to one array
|
||||
int setValuesSize = 0;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
setValuesSize += values[i].size();
|
||||
}
|
||||
|
||||
int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
|
||||
Object[] bindArgs = new Object[bindArgsSize];
|
||||
|
||||
int arg = 0;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
final ContentValues v = values[i];
|
||||
final UpdateOperation op = ops[i];
|
||||
|
||||
// Alas, code duplication.
|
||||
switch (op) {
|
||||
case ASSIGN:
|
||||
for (Map.Entry<String, Object> entry : v.valueSet()) {
|
||||
final String colName = entry.getKey();
|
||||
sql.append((arg > 0) ? "," : "");
|
||||
sql.append(colName);
|
||||
bindArgs[arg++] = entry.getValue();
|
||||
sql.append("= ?");
|
||||
}
|
||||
break;
|
||||
case BITWISE_OR:
|
||||
for (Map.Entry<String, Object> entry : v.valueSet()) {
|
||||
final String colName = entry.getKey();
|
||||
sql.append((arg > 0) ? "," : "");
|
||||
sql.append(colName);
|
||||
bindArgs[arg++] = entry.getValue();
|
||||
sql.append("= ? | ");
|
||||
sql.append(colName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whereArgs != null) {
|
||||
for (arg = setValuesSize; arg < bindArgsSize; arg++) {
|
||||
bindArgs[arg] = whereArgs[arg - setValuesSize];
|
||||
}
|
||||
}
|
||||
if (!TextUtils.isEmpty(whereClause)) {
|
||||
sql.append(" WHERE ");
|
||||
sql.append(whereClause);
|
||||
}
|
||||
|
||||
// What a huge pain in the ass, all because SQLiteDatabase doesn't expose .executeSql,
|
||||
// and we can't get a DB handle. Nor can we easily construct a statement with arguments
|
||||
// already bound.
|
||||
final SQLiteStatement statement = db.compileStatement(sql.toString());
|
||||
try {
|
||||
bindAllArgs(statement, bindArgs);
|
||||
if (!returnChangedRows) {
|
||||
statement.execute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (AppConstants.Versions.feature11Plus) {
|
||||
// This is a separate method so we can annotate it with @TargetApi.
|
||||
return executeStatementReturningChangedRows(statement);
|
||||
} else {
|
||||
statement.execute();
|
||||
final Cursor cursor = db.rawQuery("SELECT changes()", null);
|
||||
try {
|
||||
cursor.moveToFirst();
|
||||
return cursor.getInt(0);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
}
|
||||
} finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static int executeStatementReturningChangedRows(SQLiteStatement statement) {
|
||||
return statement.executeUpdateDelete();
|
||||
}
|
||||
|
||||
// All because {@link SQLiteProgram#bind(integer, Object)} is private.
|
||||
private static void bindAllArgs(SQLiteStatement statement, Object[] bindArgs) {
|
||||
if (bindArgs == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = bindArgs.length; i != 0; i--) {
|
||||
Object v = bindArgs[i - 1];
|
||||
if (v == null) {
|
||||
statement.bindNull(i);
|
||||
} else if (v instanceof String) {
|
||||
statement.bindString(i, (String) v);
|
||||
} else if (v instanceof Double) {
|
||||
statement.bindDouble(i, (Double) v);
|
||||
} else if (v instanceof Float) {
|
||||
statement.bindDouble(i, (Float) v);
|
||||
} else if (v instanceof Long) {
|
||||
statement.bindLong(i, (Long) v);
|
||||
} else if (v instanceof Integer) {
|
||||
statement.bindLong(i, (Integer) v);
|
||||
} else if (v instanceof Byte) {
|
||||
statement.bindLong(i, (Byte) v);
|
||||
} else if (v instanceof byte[]) {
|
||||
statement.bindBlob(i, (byte[]) v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,19 @@ public class PerProfileDatabases<T extends SQLiteOpenHelper> {
|
|||
private final String mDatabaseName;
|
||||
private final DatabaseHelperFactory<T> mHelperFactory;
|
||||
|
||||
// Only used during tests.
|
||||
public void shutdown() {
|
||||
synchronized (this) {
|
||||
for (T t : mStorages.values()) {
|
||||
try {
|
||||
t.close();
|
||||
} catch (Throwable e) {
|
||||
// Never mind.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface DatabaseHelperFactory<T> {
|
||||
public T makeDatabaseHelper(Context context, String databasePath);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,14 @@ public abstract class SharedBrowserDatabaseProvider extends AbstractPerProfileDa
|
|||
return databases;
|
||||
}
|
||||
|
||||
// Can't mark as @Override. Added in API 11.
|
||||
public void shutdown() {
|
||||
synchronized (SharedBrowserDatabaseProvider.class) {
|
||||
databases.shutdown();
|
||||
databases = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
// If necessary, do the shared DB work.
|
||||
|
@ -95,8 +103,8 @@ public abstract class SharedBrowserDatabaseProvider extends AbstractPerProfileDa
|
|||
// IDs of matching rows, then delete them in one go.
|
||||
final long now = System.currentTimeMillis();
|
||||
final String selection = SyncColumns.IS_DELETED + " = 1 AND " +
|
||||
SyncColumns.DATE_MODIFIED + " <= " +
|
||||
(now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
SyncColumns.DATE_MODIFIED + " <= " +
|
||||
(now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
|
||||
final String profile = fromUri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
final SQLiteDatabase db = getWritableDatabaseForProfile(profile, isTest(fromUri));
|
||||
|
|
|
@ -376,6 +376,9 @@ public class ShareDialog extends Locales.LocaleAwareActivity implements SendTabT
|
|||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// (bug 1132720) Hide the View so it doesn't flicker as the Activity closes.
|
||||
ShareDialog.this.setVisible(false);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,11 @@ skip-if = android_version == "10"
|
|||
[testClearPrivateData]
|
||||
# disabled on x86 and 2.3; bug 948591
|
||||
skip-if = android_version == "10" || processor == "x86"
|
||||
[testDBUtils]
|
||||
[testDistribution]
|
||||
[testDoorHanger]
|
||||
# disabled on 2.3; bug 1085609
|
||||
skip-if = android_version == "10"
|
||||
[testFilterOpenTab]
|
||||
[testFindInPage]
|
||||
# disabled on 2.3
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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.tests;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import org.mozilla.gecko.db.DBUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class testDBUtils extends BaseTest {
|
||||
public void testDBUtils() throws IOException {
|
||||
final File cacheDir = getInstrumentation().getContext().getCacheDir();
|
||||
final File dbFile = File.createTempFile("testDBUtils", ".db", cacheDir);
|
||||
final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
|
||||
try {
|
||||
mAsserter.ok(db != null, "Created DB.", null);
|
||||
db.execSQL("CREATE TABLE foo (x INTEGER NOT NULL DEFAULT 0, y TEXT)");
|
||||
final ContentValues v = new ContentValues();
|
||||
v.put("x", 5);
|
||||
v.put("y", "a");
|
||||
db.insert("foo", null, v);
|
||||
v.put("x", 2);
|
||||
v.putNull("y");
|
||||
db.insert("foo", null, v);
|
||||
v.put("x", 3);
|
||||
v.put("y", "z");
|
||||
db.insert("foo", null, v);
|
||||
|
||||
DBUtils.UpdateOperation[] ops = {DBUtils.UpdateOperation.BITWISE_OR, DBUtils.UpdateOperation.ASSIGN};
|
||||
ContentValues[] values = {new ContentValues(), new ContentValues()};
|
||||
values[0].put("x", 0xff);
|
||||
values[1].put("y", "hello");
|
||||
|
||||
final int updated = DBUtils.updateArrays(db, "foo", values, ops, "x >= 3", null);
|
||||
|
||||
mAsserter.ok(updated == 2, "Updated two rows.", null);
|
||||
final Cursor out = db.query("foo", new String[]{"x", "y"}, null, null, null, null, "x");
|
||||
try {
|
||||
mAsserter.ok(out.moveToNext(), "Has first result.", null);
|
||||
mAsserter.ok(2 == out.getInt(0), "1: First column was untouched.", null);
|
||||
mAsserter.ok(out.isNull(1), "1: Second column was untouched.", null);
|
||||
|
||||
mAsserter.ok(out.moveToNext(), "Has second result.", null);
|
||||
mAsserter.ok((0xff | 3) == out.getInt(0), "2: First column was ORed correctly.", null);
|
||||
mAsserter.ok("hello".equals(out.getString(1)), "2: Second column was assigned correctly.", null);
|
||||
|
||||
mAsserter.ok(out.moveToNext(), "Has third result.", null);
|
||||
mAsserter.ok((0xff | 5) == out.getInt(0), "3: First column was ORed correctly.", null);
|
||||
mAsserter.ok("hello".equals(out.getString(1)), "3: Second column was assigned correctly.", null);
|
||||
|
||||
mAsserter.ok(!out.moveToNext(), "No more results.", null);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
try {
|
||||
db.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
dbFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import org.mozilla.gecko.AppConstants.Versions;
|
|||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.ReaderModeUtils;
|
||||
import org.mozilla.gecko.SiteIdentity;
|
||||
import org.mozilla.gecko.SiteIdentity.SecurityMode;
|
||||
import org.mozilla.gecko.SiteIdentity.MixedMode;
|
||||
|
@ -191,7 +192,7 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout
|
|||
mSiteSecurityVisible = (mSiteSecurity.getVisibility() == View.VISIBLE);
|
||||
|
||||
mSiteIdentityPopup = new SiteIdentityPopup(mActivity);
|
||||
mSiteIdentityPopup.setAnchor(mTitle);
|
||||
mSiteIdentityPopup.setAnchor(mSiteSecurity);
|
||||
|
||||
mStop = (ImageButton) findViewById(R.id.stop);
|
||||
mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout);
|
||||
|
@ -360,11 +361,14 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout
|
|||
return;
|
||||
}
|
||||
|
||||
CharSequence title = url;
|
||||
String strippedURL = stripAboutReaderURL(url);
|
||||
|
||||
if (mPrefs.shouldTrimUrls()) {
|
||||
title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
|
||||
strippedURL = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(strippedURL));
|
||||
}
|
||||
|
||||
CharSequence title = strippedURL;
|
||||
|
||||
final String baseDomain = tab.getBaseDomain();
|
||||
if (!TextUtils.isEmpty(baseDomain)) {
|
||||
final SpannableStringBuilder builder = new SpannableStringBuilder(title);
|
||||
|
@ -382,6 +386,14 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout
|
|||
setTitle(title);
|
||||
}
|
||||
|
||||
private String stripAboutReaderURL(final String url) {
|
||||
if (!AboutPages.isAboutReader(url)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return ReaderModeUtils.getUrlFromAboutReader(url);
|
||||
}
|
||||
|
||||
private void updateFavicon(Tab tab) {
|
||||
if (HardwareUtils.isTablet()) {
|
||||
// We don't display favicons in the toolbar on tablet.
|
||||
|
|
|
@ -4430,7 +4430,11 @@ Tab.prototype = {
|
|||
this.clickToPlayPluginsActivated = false;
|
||||
// Borrowed from desktop Firefox: http://mxr.mozilla.org/mozilla-central/source/browser/base/content/urlbarBindings.xml#174
|
||||
let documentURI = contentWin.document.documentURIObject.spec
|
||||
let matchedURL = documentURI.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
|
||||
|
||||
// If reader mode, get the base domain for the original url.
|
||||
let strippedURI = this._stripAboutReaderURL(documentURI);
|
||||
|
||||
let matchedURL = strippedURI.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
|
||||
let baseDomain = "";
|
||||
if (matchedURL) {
|
||||
var domain = "";
|
||||
|
@ -4488,6 +4492,19 @@ Tab.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_stripAboutReaderURL: function (url) {
|
||||
if (!url.startsWith("about:reader")) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// From ReaderParent._getOriginalUrl (browser/modules/ReaderParent.jsm).
|
||||
let searchParams = new URLSearchParams(url.substring("about:reader?".length));
|
||||
if (!searchParams.has("url")) {
|
||||
return url;
|
||||
}
|
||||
return decodeURIComponent(searchParams.get("url"));
|
||||
},
|
||||
|
||||
// Properties used to cache security state used to update the UI
|
||||
_state: null,
|
||||
_hostChanged: false, // onLocationChange will flip this bit
|
||||
|
|
|
@ -15,4 +15,6 @@ STRIP_FLAGS="--strip-debug"
|
|||
export MOZILLA_OFFICIAL=1
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
MOZ_ANDROID_GECKOLIBS_AAR=1
|
||||
|
||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.mozilla.gecko.libs">
|
||||
|
||||
</manifest>
|
Двоичный файл не отображается.
|
@ -25,11 +25,11 @@ body {
|
|||
color: #eeeeee;
|
||||
}
|
||||
|
||||
.clear-sans {
|
||||
.sans-serif {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.charis-sil {
|
||||
.serif {
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
|
|
|
@ -4555,9 +4555,6 @@ pref("reader.color_scheme.values", "[\"light\",\"dark\",\"sepia\"]");
|
|||
// The font type in reader (sans-serif, serif)
|
||||
pref("reader.font_type", "sans-serif");
|
||||
|
||||
// Font type values available in reader mode UI.
|
||||
pref("reader.font_type.values", "[\"serif\",\"sans-serif\"]");
|
||||
|
||||
// Whether or not the user has interacted with the reader mode toolbar.
|
||||
// This is used to show a first-launch tip in reader mode.
|
||||
pref("reader.has_used_toolbar", false);
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
# 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/.
|
||||
|
||||
'''
|
||||
Script to produce an Android ARchive (.aar) containing the compiled
|
||||
Gecko library binaries. The AAR file is intended for use by local
|
||||
developers using Gradle.
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from mozbuild import util
|
||||
from mozpack.copier import Jarrer
|
||||
from mozpack.files import (
|
||||
File,
|
||||
FileFinder,
|
||||
)
|
||||
|
||||
MAVEN_POM_TEMPLATE = r'''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>{groupId}</groupId>
|
||||
<artifactId>{artifactId}</artifactId>
|
||||
<version>{version}</version>
|
||||
<packaging>{packaging}</packaging>
|
||||
</project>
|
||||
'''.lstrip()
|
||||
|
||||
IVY_XML_TEMPLATE = r'''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ivy-module version="2.0">
|
||||
<info organisation="{organisation}" module="{module}" revision="{revision}" status="integration" publication="{publication}"/>
|
||||
<configurations/>
|
||||
<publications>
|
||||
<artifact name="{name}" type="{type}" ext="{ext}"/>
|
||||
</publications>
|
||||
<dependencies/>
|
||||
</ivy-module>
|
||||
'''.lstrip()
|
||||
|
||||
def package_geckolibs_aar(topsrcdir, distdir, output_file):
|
||||
jarrer = Jarrer(optimize=False)
|
||||
|
||||
srcdir = os.path.join(topsrcdir, 'mobile', 'android', 'geckoview_library', 'geckolibs')
|
||||
jarrer.add('AndroidManifest.xml', File(os.path.join(srcdir, 'AndroidManifest.xml')))
|
||||
jarrer.add('classes.jar', File(os.path.join(srcdir, 'classes.jar')))
|
||||
|
||||
jni = FileFinder(os.path.join(distdir, 'fennec', 'lib'))
|
||||
for p, f in jni.find('**/*.so'):
|
||||
jarrer.add(os.path.join('jni', p), f)
|
||||
|
||||
# Include the buildinfo JSON as an asset, to give future consumers at least
|
||||
# a hope of determining where this AAR came from.
|
||||
json = FileFinder(distdir, ignore=['*.mozinfo.json'])
|
||||
for p, f in json.find('*.json'):
|
||||
jarrer.add(os.path.join('assets', p), f)
|
||||
|
||||
# This neatly ignores omni.ja.
|
||||
assets = FileFinder(os.path.join(distdir, 'fennec', 'assets'))
|
||||
for p, f in assets.find('**/*.so'):
|
||||
jarrer.add(os.path.join('assets', p), f)
|
||||
|
||||
jarrer.copy(output_file)
|
||||
return 0
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(dest='dir',
|
||||
metavar='DIR',
|
||||
help='Path to write geckolibs Android ARchive and metadata to.')
|
||||
parser.add_argument('--verbose', '-v', default=False, action='store_true',
|
||||
help='be verbose')
|
||||
parser.add_argument('--revision',
|
||||
help='Revision identifier to write.')
|
||||
parser.add_argument('--topsrcdir',
|
||||
help='Top source directory.')
|
||||
parser.add_argument('--distdir',
|
||||
help='Distribution directory (usually $OBJDIR/dist).')
|
||||
args = parser.parse_args(args)
|
||||
|
||||
paths_to_hash = []
|
||||
|
||||
aar = os.path.join(args.dir, 'geckolibs-{revision}.aar').format(revision=args.revision)
|
||||
paths_to_hash.append(aar)
|
||||
package_geckolibs_aar(args.topsrcdir, args.distdir, aar)
|
||||
|
||||
pom = os.path.join(args.dir, 'geckolibs-{revision}.pom').format(revision=args.revision)
|
||||
paths_to_hash.append(pom)
|
||||
with open(pom, 'wt') as f:
|
||||
f.write(MAVEN_POM_TEMPLATE.format(
|
||||
groupId='org.mozilla',
|
||||
artifactId='geckolibs',
|
||||
version=args.revision,
|
||||
packaging='aar',
|
||||
))
|
||||
|
||||
ivy = os.path.join(args.dir, 'ivy-geckolibs-{revision}.xml').format(revision=args.revision)
|
||||
paths_to_hash.append(ivy)
|
||||
with open(ivy, 'wt') as f:
|
||||
f.write(IVY_XML_TEMPLATE.format(
|
||||
organisation='org.mozilla',
|
||||
module='geckolibs',
|
||||
revision=args.revision,
|
||||
publication=args.revision, # A white lie.
|
||||
name='geckolibs',
|
||||
type='aar',
|
||||
ext='aar',
|
||||
))
|
||||
|
||||
for p in paths_to_hash:
|
||||
sha = "%s.sha1" % p
|
||||
with open(sha, 'wt') as f:
|
||||
f.write(util.hash_file(p, hasher=hashlib.sha1()))
|
||||
if args.verbose:
|
||||
print(p)
|
||||
print(sha)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
|
@ -31,12 +31,12 @@ if sys.version_info[0] == 3:
|
|||
else:
|
||||
str_type = basestring
|
||||
|
||||
def hash_file(path):
|
||||
def hash_file(path, hasher=None):
|
||||
"""Hashes a file specified by the path given and returns the hex digest."""
|
||||
|
||||
# If the hashing function changes, this may invalidate lots of cached data.
|
||||
# Don't change it lightly.
|
||||
h = hashlib.sha1()
|
||||
# If the default hashing function changes, this may invalidate
|
||||
# lots of cached data. Don't change it lightly.
|
||||
h = hasher or hashlib.sha1()
|
||||
|
||||
with open(path, 'rb') as fh:
|
||||
while True:
|
||||
|
|
|
@ -73,13 +73,17 @@ let AboutReader = function(mm, win) {
|
|||
this._setColorSchemePref(colorScheme);
|
||||
|
||||
let fontTypeSample = gStrings.GetStringFromName("aboutReader.fontTypeSample");
|
||||
let fontTypeValues = JSON.parse(Services.prefs.getCharPref("reader.font_type.values"));
|
||||
let fontTypeOptions = fontTypeValues.map((value) => {
|
||||
return { name: fontTypeSample,
|
||||
description: gStrings.GetStringFromName("aboutReader.fontType." + value),
|
||||
value: value,
|
||||
linkClass: value };
|
||||
});
|
||||
let fontTypeOptions = [
|
||||
{ name: fontTypeSample,
|
||||
description: gStrings.GetStringFromName("aboutReader.fontType.serif"),
|
||||
value: "serif",
|
||||
linkClass: "serif" },
|
||||
{ name: fontTypeSample,
|
||||
description: gStrings.GetStringFromName("aboutReader.fontType.sans-serif"),
|
||||
value: "sans-serif",
|
||||
linkClass: "sans-serif"
|
||||
},
|
||||
];
|
||||
|
||||
let fontType = Services.prefs.getCharPref("reader.font_type");
|
||||
this._setupSegmentedButton("font-type-buttons", fontTypeOptions, fontType, this._setFontType.bind(this));
|
||||
|
|
|
@ -4532,15 +4532,6 @@
|
|||
"description": "The way the GeckoApp was launched. (Normal, URL, Prefetch, Redirector)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_TIME_ABOUTHOME": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 100,
|
||||
"high": "10000",
|
||||
"n_buckets": 20,
|
||||
"description": "Time for the about:home page to be displayed (ms)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_STARTUP_TIME_GECKOREADY": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
|
|
|
@ -11,15 +11,10 @@ aboutReader.colorScheme.sepia=Sepia
|
|||
aboutReader.colorScheme.auto=Auto
|
||||
|
||||
# LOCALIZATION NOTE (aboutReader.fontType.serif, aboutReader.fontType.sans-serif):
|
||||
# These are the styles of typeface that are used on desktop.
|
||||
# These are the styles of typeface that are options in the reader view controls.
|
||||
aboutReader.fontType.serif=Serif
|
||||
aboutReader.fontType.sans-serif=Sans-serif
|
||||
|
||||
# LOCALIZATION NOTE (aboutReader.fontType.charis-sil, aboutReader.fontType.clear-sans):
|
||||
# These are the names of the fonts that are used on Android
|
||||
aboutReader.fontType.charis-sil=Charis SIL Compact
|
||||
aboutReader.fontType.clear-sans=Clear Sans
|
||||
|
||||
# LOCALIZATION NOTE (aboutReader.fontTypeSample): String used to sample font types.
|
||||
aboutReader.fontTypeSample=Aa
|
||||
|
||||
|
|
|
@ -362,6 +362,31 @@ INNER_MAKE_GECKOVIEW_LIBRARY=echo 'GeckoView library packaging is only enabled o
|
|||
INNER_MAKE_GECKOVIEW_EXAMPLE=echo 'GeckoView example packaging is only enabled on Nightly'
|
||||
endif
|
||||
|
||||
# Create geckolibs Android ARchive and metadata for download by local
|
||||
# developers using Gradle.
|
||||
ifdef MOZ_ANDROID_GECKOLIBS_AAR
|
||||
geckolibs-revision := $(BUILDID)
|
||||
|
||||
UPLOAD_EXTRA_FILES += \
|
||||
geckolibs-$(geckolibs-revision).aar \
|
||||
geckolibs-$(geckolibs-revision).aar.sha1 \
|
||||
geckolibs-$(geckolibs-revision).pom \
|
||||
geckolibs-$(geckolibs-revision).pom.sha1 \
|
||||
ivy-geckolibs-$(geckolibs-revision).xml \
|
||||
ivy-geckolibs-$(geckolibs-revision).xml.sha1 \
|
||||
$(NULL)
|
||||
|
||||
INNER_MAKE_GECKOLIBS_AAR= \
|
||||
$(PYTHON) -m mozbuild.action.package_geckolibs_aar \
|
||||
--verbose \
|
||||
--revision $(geckolibs-revision) \
|
||||
--topsrcdir '$(topsrcdir)' \
|
||||
--distdir '$(_ABS_DIST)' \
|
||||
'$(_ABS_DIST)'
|
||||
else
|
||||
INNER_MAKE_GECKOLIBS_AAR=echo 'Android geckolibs.aar packaging is disabled'
|
||||
endif
|
||||
|
||||
ifdef MOZ_OMX_PLUGIN
|
||||
DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so \
|
||||
libomxpluginhc.so libomxpluginkk.so
|
||||
|
@ -438,6 +463,7 @@ INNER_MAKE_PACKAGE = \
|
|||
$(RELEASE_JARSIGNER) $(_ABS_DIST)/gecko.apk && \
|
||||
$(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE) && \
|
||||
$(INNER_ROBOCOP_PACKAGE) && \
|
||||
$(INNER_MAKE_GECKOLIBS_AAR) && \
|
||||
$(INNER_MAKE_GECKOVIEW_LIBRARY) && \
|
||||
$(INNER_MAKE_GECKOVIEW_EXAMPLE)
|
||||
|
||||
|
|
|
@ -71,12 +71,17 @@ function moveToAlertPosition()
|
|||
sizeToContent();
|
||||
}
|
||||
|
||||
var xOffset = (opener.outerWidth - window.outerWidth) / 2;
|
||||
var yOffset = opener.outerHeight / 5;
|
||||
|
||||
var newX = opener.screenX + xOffset;
|
||||
var newY = opener.screenY + yOffset;
|
||||
|
||||
if (opener) {
|
||||
var xOffset = (opener.outerWidth - window.outerWidth) / 2;
|
||||
var yOffset = opener.outerHeight / 5;
|
||||
|
||||
var newX = opener.screenX + xOffset;
|
||||
var newY = opener.screenY + yOffset;
|
||||
} else {
|
||||
newX = (screen.availWidth - window.outerWidth) / 2;
|
||||
newY = (screen.availHeight - window.outerHeight) / 2;
|
||||
}
|
||||
|
||||
// ensure the window is fully onscreen (if smaller than the screen)
|
||||
if (newX < screen.availLeft)
|
||||
newX = screen.availLeft + 20;
|
||||
|
|
Загрузка…
Ссылка в новой задаче