merge fx-team to mozilla-central a=merge
@ -81,16 +81,14 @@ a {
|
||||
-moz-margin-end: 5px;
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
background-image: url("chrome://browser/skin/magnifier.png");
|
||||
background: url("chrome://browser/skin/magnifier.png") center center no-repeat;
|
||||
background-size: 26px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#searchIcon[active],
|
||||
#searchIcon:hover {
|
||||
background-color: #e9e9e9;
|
||||
border: 1px solid rgb(226, 227, 229);
|
||||
border-color: rgb(226, 227, 229);
|
||||
border-radius: 2.5px;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
||||
return this.toolbarButton = CustomizableUI.getWidget("loop-button").forWindow(window);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Promise}
|
||||
*/
|
||||
promiseDocumentVisible(aDocument) {
|
||||
if (!aDocument.hidden) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
aDocument.addEventListener("visibilitychange", function onVisibilityChanged() {
|
||||
aDocument.removeEventListener("visibilitychange", onVisibilityChanged);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the panel for Loop and sizes it appropriately.
|
||||
*
|
||||
@ -32,7 +48,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
||||
// Helper function to show a specific tab view in the panel.
|
||||
function showTab() {
|
||||
if (!tabId) {
|
||||
resolve();
|
||||
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -44,7 +60,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
||||
}
|
||||
}, win));
|
||||
win.dispatchEvent(ev);
|
||||
resolve();
|
||||
resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
|
||||
}
|
||||
|
||||
// If the panel has been opened and initialized before, we can skip waiting
|
||||
|
@ -12,6 +12,7 @@ let UITourListener = {
|
||||
return;
|
||||
}
|
||||
addMessageListener("UITour:SendPageCallback", this);
|
||||
addMessageListener("UITour:SendPageNotification", this);
|
||||
sendAsyncMessage("UITour:onPageEvent", {detail: event.detail, type: event.type});
|
||||
},
|
||||
|
||||
@ -68,14 +69,18 @@ let UITourListener = {
|
||||
receiveMessage: function(aMessage) {
|
||||
switch (aMessage.name) {
|
||||
case "UITour:SendPageCallback":
|
||||
this.sendPageCallback(aMessage.data);
|
||||
this.sendPageEvent("Response", aMessage.data);
|
||||
break;
|
||||
}
|
||||
case "UITour:SendPageNotification":
|
||||
this.sendPageEvent("Notification", aMessage.data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
sendPageCallback: function (detail) {
|
||||
sendPageEvent: function (type, detail) {
|
||||
let doc = content.document;
|
||||
let event = new doc.defaultView.CustomEvent("mozUITourResponse", {
|
||||
let eventName = "mozUITour" + type;
|
||||
let event = new doc.defaultView.CustomEvent(eventName, {
|
||||
bubbles: true,
|
||||
detail: Cu.cloneInto(detail, doc.defaultView)
|
||||
});
|
||||
|
@ -126,6 +126,8 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
[browser_autocomplete_autoselect.js]
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
[browser_autocomplete_oldschool_wrap.js]
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
[browser_backButtonFitts.js]
|
||||
skip-if = os != "win" || e10s # The Fitts Law back button is only supported on Windows (bug 571454) / e10s - Bug 1099154: test touches content (attempts to add an event listener directly to the contentWindow)
|
||||
[browser_blob-channelname.js]
|
||||
|
@ -0,0 +1,74 @@
|
||||
function repeat(limit, func) {
|
||||
for (let i = 0; i < limit; i++) {
|
||||
func(i);
|
||||
}
|
||||
}
|
||||
|
||||
function* promiseAutoComplete(inputText) {
|
||||
gURLBar.focus();
|
||||
gURLBar.value = inputText.slice(0, -1);
|
||||
EventUtils.synthesizeKey(inputText.slice(-1), {});
|
||||
yield promiseSearchComplete();
|
||||
}
|
||||
|
||||
function is_selected(index) {
|
||||
is(gURLBar.popup.richlistbox.selectedIndex, index, `Item ${index + 1} should be selected`);
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is *disabled*.
|
||||
if (Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
|
||||
registerCleanupFunction(promiseClearHistory);
|
||||
|
||||
let visits = [];
|
||||
repeat(10, i => {
|
||||
visits.push({
|
||||
uri: makeURI("http://example.com/autocomplete/?" + i),
|
||||
});
|
||||
});
|
||||
yield PlacesTestUtils.addVisits(visits);
|
||||
|
||||
yield promiseAutoComplete("example.com/autocomplete");
|
||||
|
||||
let popup = gURLBar.popup;
|
||||
let results = popup.richlistbox.children;
|
||||
is(results.length, 10, "Should get 11 results");
|
||||
is_selected(-1);
|
||||
|
||||
info("Key Down to select the next item");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is_selected(0);
|
||||
|
||||
info("Key Up to select the previous item");
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is_selected(-1);
|
||||
|
||||
info("Key Down to select the next item");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is_selected(0);
|
||||
|
||||
info("Key Down 11 times should wrap around all the way around");
|
||||
repeat(11, () => EventUtils.synthesizeKey("VK_DOWN", {}));
|
||||
is_selected(0);
|
||||
|
||||
info("Key Up 11 times should wrap around the other way");
|
||||
repeat(11, () => EventUtils.synthesizeKey("VK_UP", {}));
|
||||
is_selected(0);
|
||||
|
||||
info("Page Up will go up the list, but not wrap");
|
||||
repeat(4, () => EventUtils.synthesizeKey("VK_DOWN", {}));
|
||||
is_selected(4);
|
||||
EventUtils.synthesizeKey("VK_PAGE_UP", {})
|
||||
is_selected(0);
|
||||
|
||||
info("Page Up again will wrap around to the end of the list");
|
||||
EventUtils.synthesizeKey("VK_PAGE_UP", {})
|
||||
is_selected(-1);
|
||||
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promisePopupHidden(gURLBar.popup);
|
||||
});
|
@ -1,33 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let originalTab;
|
||||
let newTab;
|
||||
// gBrowser.selectedTab.lastAccessed and Date.now() called from this test can't
|
||||
// run concurrently, and therefore don't always match exactly.
|
||||
const CURRENT_TIME_TOLERANCE_MS = 15;
|
||||
|
||||
function isCurrent(tab, msg) {
|
||||
const tolerance = 5;
|
||||
const difference = Math.abs(Date.now() - tab.lastAccessed);
|
||||
ok(difference <= tolerance, msg + " (difference: " + difference + ")");
|
||||
const DIFF = Math.abs(Date.now() - tab.lastAccessed);
|
||||
ok(DIFF <= CURRENT_TIME_TOLERANCE_MS, msg + " (difference: " + DIFF + ")");
|
||||
}
|
||||
|
||||
function nextStep(fn) {
|
||||
setTimeout(fn, CURRENT_TIME_TOLERANCE_MS + 10);
|
||||
}
|
||||
|
||||
let originalTab;
|
||||
let newTab;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
setTimeout(step2, 10);
|
||||
nextStep(step2);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
isCurrent(originalTab, "selected tab has the current timestamp");
|
||||
newTab = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
setTimeout(step3, 10);
|
||||
nextStep(step3);
|
||||
}
|
||||
|
||||
function step3() {
|
||||
ok(newTab.lastAccessed < Date.now(), "new tab hasn't been selected so far");
|
||||
gBrowser.selectedTab = newTab;
|
||||
isCurrent(newTab, "new tab has the current timestamp after being selected");
|
||||
setTimeout(step4, 10);
|
||||
nextStep(step4);
|
||||
}
|
||||
|
||||
function step4() {
|
||||
|
@ -1224,8 +1224,9 @@
|
||||
createBundle("chrome://browser/locale/places/places.properties");
|
||||
</field>
|
||||
|
||||
<!-- Override this so that navigating between items results in an item
|
||||
always being selected. This is contrary to the normal behaviour where
|
||||
<!-- Override this so that when UnifiedComplete is enabled, navigating
|
||||
between items results in an item always being selected. This is
|
||||
contrary to the old behaviour (UnifiedComplete disabled) where
|
||||
if you navigate beyond either end of the list, no item will be
|
||||
selected. -->
|
||||
<method name="getNextIndex">
|
||||
@ -1239,10 +1240,23 @@
|
||||
|
||||
let newIndex = index + (reverse ? -1 : 1) * amount;
|
||||
|
||||
// We don't want to wrap if navigation in any direction by one item.
|
||||
// Otherwise we clamp to one end of the list.
|
||||
// We only want to wrap if navigation is in any direction by one item,
|
||||
// otherwise we clamp to one end of the list.
|
||||
// ie, hitting page-down will only cause is to wrap if we're already
|
||||
// at one end of the list.
|
||||
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
if (reverse && index == -1 || newIndex > maxRow && index != maxRow)
|
||||
newIndex = maxRow;
|
||||
else if (!reverse && index == -1 || newIndex < 0 && index != 0)
|
||||
newIndex = 0;
|
||||
|
||||
if (newIndex < 0 && index == 0 || newIndex > maxRow && index == maxRow)
|
||||
newIndex = -1;
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
if (newIndex < 0) {
|
||||
newIndex = index > 0 ? 0 : maxRow;
|
||||
} else if (newIndex > maxRow) {
|
||||
|
@ -345,8 +345,8 @@ let LoopRoomsInternal = {
|
||||
MozLoopService.hawkRequest(this.sessionType, url, "DELETE")
|
||||
.then(response => {
|
||||
this.rooms.delete(roomToken);
|
||||
eventEmitter.emit("delete", room);
|
||||
callback(null, room);
|
||||
// We'll emit an update when the push notification is received.
|
||||
}, error => callback(error)).catch(error => callback(error));
|
||||
},
|
||||
|
||||
|
@ -752,7 +752,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
}, this)
|
||||
),
|
||||
React.DOM.p(null,
|
||||
React.DOM.button({className: "btn btn-info",
|
||||
React.DOM.button({className: "btn btn-info new-room-button",
|
||||
onClick: this.handleCreateButtonClick,
|
||||
disabled: this._hasPendingOperation()},
|
||||
mozL10n.get("rooms_new_room_button_label")
|
||||
|
@ -752,7 +752,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
}, this)
|
||||
}</div>
|
||||
<p>
|
||||
<button className="btn btn-info"
|
||||
<button className="btn btn-info new-room-button"
|
||||
onClick={this.handleCreateButtonClick}
|
||||
disabled={this._hasPendingOperation()}>
|
||||
{mozL10n.get("rooms_new_room_button_label")}
|
||||
|
@ -41,7 +41,9 @@ loop.store.ActiveRoomStore = (function() {
|
||||
// The room is full
|
||||
FULL: "room-full",
|
||||
// The room conversation has ended
|
||||
ENDED: "room-ended"
|
||||
ENDED: "room-ended",
|
||||
// The window is closing
|
||||
CLOSING: "room-closing"
|
||||
};
|
||||
|
||||
/**
|
||||
@ -386,7 +388,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
* Handles the window being unloaded. Ensures the room is left.
|
||||
*/
|
||||
windowUnload: function() {
|
||||
this._leaveRoom();
|
||||
this._leaveRoom(ROOM_STATES.CLOSING);
|
||||
|
||||
// If we're closing the window, we can stop listening to updates.
|
||||
this._mozLoop.rooms.off("update:" + this.getStoreState().roomToken,
|
||||
|
@ -22,7 +22,18 @@ body,
|
||||
* Note: the is-standalone-room class is dynamically set by the StandaloneRoomView.
|
||||
*/
|
||||
.standalone.is-standalone-room {
|
||||
background-color: #000;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.standalone .beta-logo {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: transparent url(../shared/img/beta-ribbon.svg) no-repeat;
|
||||
background-size: 50px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.standalone-header {
|
||||
@ -247,6 +258,9 @@ p.standalone-btn-label {
|
||||
border-radius: 3px;
|
||||
z-index: 1002; /* ensures the form is always on top of the control bar */
|
||||
}
|
||||
.standalone .room-conversation-wrapper .ended-conversation .feedback {
|
||||
right: 35%;
|
||||
}
|
||||
|
||||
.standalone .ended-conversation .local-stream {
|
||||
/* Hide local media stream when feedback form is shown. */
|
||||
|
@ -351,6 +351,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "room-conversation-wrapper"},
|
||||
React.DOM.div({className: "beta-logo"}),
|
||||
StandaloneRoomHeader(null),
|
||||
StandaloneRoomInfoArea({roomState: this.state.roomState,
|
||||
failureReason: this.state.failureReason,
|
||||
|
@ -351,6 +351,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
<div className="room-conversation-wrapper">
|
||||
<div className="beta-logo" />
|
||||
<StandaloneRoomHeader />
|
||||
<StandaloneRoomInfoArea roomState={this.state.roomState}
|
||||
failureReason={this.state.failureReason}
|
||||
|
@ -676,10 +676,10 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
"fakeToken", "1627384950");
|
||||
});
|
||||
|
||||
it("should set the state to ENDED", function() {
|
||||
it("should set the state to CLOSING", function() {
|
||||
store.windowUnload();
|
||||
|
||||
expect(store._storeState.roomState).eql(ROOM_STATES.ENDED);
|
||||
expect(store._storeState.roomState).eql(ROOM_STATES.CLOSING);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -290,15 +290,6 @@ add_task(function* test_createRoom() {
|
||||
compareRooms(room, kCreateRoomProps);
|
||||
});
|
||||
|
||||
// Test if deleting a room works as expected.
|
||||
add_task(function* test_deleteRoom() {
|
||||
let roomToken = "QzBbvGmIZWU";
|
||||
let deletedRoom = yield LoopRooms.promise("delete", roomToken);
|
||||
Assert.equal(deletedRoom.roomToken, roomToken);
|
||||
let rooms = yield LoopRooms.promise("getAll");
|
||||
Assert.ok(!rooms.some((room) => room.roomToken == roomToken));
|
||||
});
|
||||
|
||||
// Test if opening a new room window works correctly.
|
||||
add_task(function* test_openRoom() {
|
||||
let openedUrl;
|
||||
@ -403,6 +394,16 @@ add_task(function* test_roomDeleteNotifications() {
|
||||
yield waitForCondition(() => gExpectedDeletes.length === 0);
|
||||
});
|
||||
|
||||
// Test if deleting a room works as expected.
|
||||
add_task(function* test_deleteRoom() {
|
||||
let roomToken = "QzBbvGmIZWU";
|
||||
gExpectedDeletes.push(roomToken);
|
||||
let deletedRoom = yield LoopRooms.promise("delete", roomToken);
|
||||
Assert.equal(deletedRoom.roomToken, roomToken);
|
||||
let rooms = yield LoopRooms.promise("getAll");
|
||||
Assert.ok(!rooms.some((room) => room.roomToken == roomToken));
|
||||
});
|
||||
|
||||
// Test if the event emitter implementation doesn't leak and is working as expected.
|
||||
add_task(function* () {
|
||||
Assert.strictEqual(gExpectedAdds.length, 0, "No room additions should be expected anymore");
|
||||
|
@ -621,6 +621,17 @@
|
||||
)
|
||||
),
|
||||
|
||||
Example({summary: "Standalone room conversation (feedback)"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
StandaloneRoomView({
|
||||
dispatcher: dispatcher,
|
||||
activeRoomStore: activeRoomStore,
|
||||
feedbackStore: feedbackStore,
|
||||
roomState: ROOM_STATES.ENDED,
|
||||
helper: {isFirefox: returnFalse}})
|
||||
)
|
||||
),
|
||||
|
||||
Example({summary: "Standalone room conversation (failed)"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
StandaloneRoomView({
|
||||
|
@ -621,6 +621,17 @@
|
||||
</div>
|
||||
</Example>
|
||||
|
||||
<Example summary="Standalone room conversation (feedback)">
|
||||
<div className="standalone">
|
||||
<StandaloneRoomView
|
||||
dispatcher={dispatcher}
|
||||
activeRoomStore={activeRoomStore}
|
||||
feedbackStore={feedbackStore}
|
||||
roomState={ROOM_STATES.ENDED}
|
||||
helper={{isFirefox: returnFalse}} />
|
||||
</div>
|
||||
</Example>
|
||||
|
||||
<Example summary="Standalone room conversation (failed)">
|
||||
<div className="standalone">
|
||||
<StandaloneRoomView
|
||||
|
@ -531,8 +531,8 @@ Settings.prototype = {
|
||||
yesNoToBoolean);
|
||||
this._set("Software\\Microsoft\\Internet Explorer\\Settings",
|
||||
"Always Use My Colors",
|
||||
"browser.display.use_document_colors",
|
||||
function (v) !Boolean(v));
|
||||
"browser.display.document_color_use",
|
||||
function (v) !Boolean(v) ? 0 : 2);
|
||||
this._set("Software\\Microsoft\\Internet Explorer\\Settings",
|
||||
"Always Use My Font Face",
|
||||
"browser.display.use_document_fonts",
|
||||
|
@ -1459,7 +1459,7 @@ BrowserGlue.prototype = {
|
||||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 26;
|
||||
const UI_VERSION = 27;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
@ -1769,6 +1769,15 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
if (currentUIVersion < 27) {
|
||||
// Fix up document color use:
|
||||
const kOldColorPref = "browser.display.use_document_colors";
|
||||
if (Services.prefs.prefHasUserValue(kOldColorPref) &&
|
||||
!Services.prefs.getBoolPref(kOldColorPref)) {
|
||||
Services.prefs.setIntPref("browser.display.document_color_use", 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
@ -28,7 +28,7 @@
|
||||
helpTopic="prefs-fonts-and-colors">
|
||||
|
||||
<preferences>
|
||||
<preference id="browser.display.use_document_colors" name="browser.display.use_document_colors" type="bool"/>
|
||||
<preference id="browser.display.document_color_use" name="browser.display.document_color_use" type="int"/>
|
||||
<preference id="browser.anchor_color" name="browser.anchor_color" type="string"/>
|
||||
<preference id="browser.visited_color" name="browser.visited_color" type="string"/>
|
||||
<preference id="browser.underline_anchors" name="browser.underline_anchors" type="bool"/>
|
||||
@ -80,10 +80,16 @@
|
||||
</hbox>
|
||||
</groupbox>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<checkbox id="useDocumentColors"
|
||||
label="&allowPagesToUse.label;" accesskey="&allowPagesToUse.accesskey;"
|
||||
preference="browser.display.use_document_colors" flex="1"/>
|
||||
</hbox>
|
||||
<vbox align="start">
|
||||
<label accesskey="&allowPagesToUseColors.accesskey;"
|
||||
control="useDocumentColors">&allowPagesToUseColors.label;</label>
|
||||
<menulist id="useDocumentColors" preference="browser.display.document_color_use">
|
||||
<menupopup>
|
||||
<menuitem label="&allowPagesToUseColors.automatic.label;" value="0" id="documentColorAutomatic"/>
|
||||
<menuitem label="&allowPagesToUseColors.always.label;" value="1" id="documentColorAlways"/>
|
||||
<menuitem label="&allowPagesToUseColors.never.label;" value="2" id="documentColorNever"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</vbox>
|
||||
</prefpane>
|
||||
</prefwindow>
|
||||
|
@ -6,8 +6,12 @@
|
||||
<!ENTITY window.width "38em">
|
||||
<!ENTITY window.macWidth "41em">
|
||||
|
||||
<!ENTITY allowPagesToUse.label "Allow pages to choose their own colors, instead of my selections above">
|
||||
<!ENTITY allowPagesToUse.accesskey "A">
|
||||
<!ENTITY allowPagesToUseColors.label "Allow pages to choose their own colors, instead of my selections above:">
|
||||
<!ENTITY allowPagesToUseColors.accesskey "A">
|
||||
|
||||
<!ENTITY allowPagesToUseColors.automatic.label "Automatic">
|
||||
<!ENTITY allowPagesToUseColors.always.label "Always">
|
||||
<!ENTITY allowPagesToUseColors.never.label "Never">
|
||||
|
||||
<!ENTITY color "Text and Background">
|
||||
<!ENTITY textColor.label "Text:">
|
||||
|
@ -120,6 +120,33 @@ this.UITour = {
|
||||
allowAdd: true,
|
||||
}],
|
||||
["loop", {query: "#loop-button"}],
|
||||
["loop-newRoom", {
|
||||
query: (aDocument) => {
|
||||
let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
|
||||
if (!loopBrowser) {
|
||||
return null;
|
||||
}
|
||||
return loopBrowser.contentDocument.querySelector(".new-room-button");
|
||||
},
|
||||
}],
|
||||
["loop-roomList", {
|
||||
query: (aDocument) => {
|
||||
let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
|
||||
if (!loopBrowser) {
|
||||
return null;
|
||||
}
|
||||
return loopBrowser.contentDocument.querySelector(".room-list");
|
||||
},
|
||||
}],
|
||||
["loop-signInUpLink", {
|
||||
query: (aDocument) => {
|
||||
let loopBrowser = aDocument.querySelector("#loop-notification-panel > #loop");
|
||||
if (!loopBrowser) {
|
||||
return null;
|
||||
}
|
||||
return loopBrowser.contentDocument.querySelector(".signin-link");
|
||||
},
|
||||
}],
|
||||
["privateWindow", {query: "#privatebrowsing-button"}],
|
||||
["quit", {query: "#PanelUI-quit"}],
|
||||
["search", {
|
||||
@ -358,7 +385,7 @@ this.UITour = {
|
||||
if (this.highlightEffects.indexOf(data.effect) !== -1) {
|
||||
effect = data.effect;
|
||||
}
|
||||
this.showHighlight(target, effect);
|
||||
this.showHighlight(window, target, effect);
|
||||
}).catch(log.error);
|
||||
break;
|
||||
}
|
||||
@ -414,7 +441,7 @@ this.UITour = {
|
||||
if (typeof data.targetCallbackID == "string")
|
||||
infoOptions.targetCallbackID = data.targetCallbackID;
|
||||
|
||||
this.showInfo(messageManager, target, data.title, data.text, iconURL, buttons, infoOptions);
|
||||
this.showInfo(window, messageManager, target, data.title, data.text, iconURL, buttons, infoOptions);
|
||||
}).catch(log.error);
|
||||
break;
|
||||
}
|
||||
@ -581,6 +608,12 @@ this.UITour = {
|
||||
}).then(null, Cu.reportError);
|
||||
break;
|
||||
}
|
||||
|
||||
case "ping": {
|
||||
if (typeof data.callbackID == "string")
|
||||
this.sendPageCallback(messageManager, data.callbackID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
|
||||
@ -735,10 +768,12 @@ this.UITour = {
|
||||
}
|
||||
|
||||
// Clean up panel listeners after we may have called hideMenu above.
|
||||
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hideAppMenuAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hideAppMenuAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("popuphidden", this.onPanelHidden);
|
||||
let loopPanel = aWindow.document.getElementById("loop-notification-panel");
|
||||
loopPanel.removeEventListener("popuphidden", this.onLoopPanelHidden);
|
||||
loopPanel.removeEventListener("popuphidden", this.onPanelHidden);
|
||||
loopPanel.removeEventListener("popuphiding", this.hideLoopPanelAnnotations);
|
||||
|
||||
this.endUrlbarCapture(aWindow);
|
||||
this.removePinnedTab(aWindow);
|
||||
@ -792,7 +827,9 @@ this.UITour = {
|
||||
|
||||
isElementVisible: function(aElement) {
|
||||
let targetStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement);
|
||||
return (targetStyle.display != "none" && targetStyle.visibility == "visible");
|
||||
return !aElement.ownerDocument.hidden &&
|
||||
targetStyle.display != "none" &&
|
||||
targetStyle.visibility == "visible";
|
||||
},
|
||||
|
||||
getTarget: function(aWindow, aTargetName, aSticky = false) {
|
||||
@ -953,27 +990,28 @@ this.UITour = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
|
||||
* are in a sub-frame so the defaultView is not the same as the chrome
|
||||
* window.
|
||||
* @param aTarget The element to highlight.
|
||||
* @param aEffect (optional) The effect to use from UITour.highlightEffects or "none".
|
||||
* @see UITour.highlightEffects
|
||||
*/
|
||||
showHighlight: function(aTarget, aEffect = "none") {
|
||||
let window = aTarget.node.ownerDocument.defaultView;
|
||||
|
||||
showHighlight: function(aChromeWindow, aTarget, aEffect = "none") {
|
||||
function showHighlightPanel() {
|
||||
if (aTarget.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX)) {
|
||||
// This won't affect normal higlights done via the panel, so we need to
|
||||
// manually hide those.
|
||||
this.hideHighlight(window);
|
||||
this.hideHighlight(aChromeWindow);
|
||||
aTarget.node.setAttribute("_moz-menuactive", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Conversely, highlights for search engines are highlighted via CSS
|
||||
// rather than a panel, so need to be manually removed.
|
||||
this._hideSearchEngineHighlight(window);
|
||||
this._hideSearchEngineHighlight(aChromeWindow);
|
||||
|
||||
let highlighter = aTarget.node.ownerDocument.getElementById("UITourHighlight");
|
||||
let highlighter = aChromeWindow.document.getElementById("UITourHighlight");
|
||||
|
||||
let effect = aEffect;
|
||||
if (effect == "random") {
|
||||
@ -985,7 +1023,7 @@ this.UITour = {
|
||||
}
|
||||
// Toggle the effect attribute to "none" and flush layout before setting it so the effect plays.
|
||||
highlighter.setAttribute("active", "none");
|
||||
aTarget.node.ownerDocument.defaultView.getComputedStyle(highlighter).animationName;
|
||||
aChromeWindow.getComputedStyle(highlighter).animationName;
|
||||
highlighter.setAttribute("active", effect);
|
||||
highlighter.parentElement.setAttribute("targetName", aTarget.targetName);
|
||||
highlighter.parentElement.hidden = false;
|
||||
@ -1025,7 +1063,7 @@ this.UITour = {
|
||||
}
|
||||
/* The "overlap" position anchors from the top-left but we want to centre highlights at their
|
||||
minimum size. */
|
||||
let highlightWindow = aTarget.node.ownerDocument.defaultView;
|
||||
let highlightWindow = aChromeWindow;
|
||||
let containerStyle = highlightWindow.getComputedStyle(highlighter.parentElement);
|
||||
let paddingTopPx = 0 - parseFloat(containerStyle.paddingTop);
|
||||
let paddingLeftPx = 0 - parseFloat(containerStyle.paddingLeft);
|
||||
@ -1046,7 +1084,7 @@ this.UITour = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._setAppMenuStateForAnnotation(aTarget.node.ownerDocument.defaultView, "highlight",
|
||||
this._setAppMenuStateForAnnotation(aChromeWindow, "highlight",
|
||||
this.targetIsInAppMenu(aTarget),
|
||||
showHighlightPanel.bind(this));
|
||||
},
|
||||
@ -1085,6 +1123,7 @@ this.UITour = {
|
||||
/**
|
||||
* Show an info panel.
|
||||
*
|
||||
* @param {ChromeWindow} aChromeWindow
|
||||
* @param {nsIMessageSender} aMessageManager
|
||||
* @param {Node} aAnchor
|
||||
* @param {String} [aTitle=""]
|
||||
@ -1094,12 +1133,12 @@ this.UITour = {
|
||||
* @param {Object} [aOptions={}]
|
||||
* @param {String} [aOptions.closeButtonCallbackID]
|
||||
*/
|
||||
showInfo: function(aMessageManager, aAnchor, aTitle = "", aDescription = "", aIconURL = "",
|
||||
showInfo: function(aChromeWindow, aMessageManager, aAnchor, aTitle = "", aDescription = "", aIconURL = "",
|
||||
aButtons = [], aOptions = {}) {
|
||||
function showInfoPanel(aAnchorEl) {
|
||||
aAnchorEl.focus();
|
||||
|
||||
let document = aAnchorEl.ownerDocument;
|
||||
let document = aChromeWindow.document;
|
||||
let tooltip = document.getElementById("UITourTooltip");
|
||||
let tooltipTitle = document.getElementById("UITourTooltipTitle");
|
||||
let tooltipDesc = document.getElementById("UITourTooltipDescription");
|
||||
@ -1187,15 +1226,17 @@ this.UITour = {
|
||||
}
|
||||
|
||||
// Prevent showing a panel at an undefined position.
|
||||
if (!this.isElementVisible(aAnchor.node))
|
||||
if (!this.isElementVisible(aAnchor.node)) {
|
||||
log.warn("showInfo: Not showing since the target isn't visible", aAnchor);
|
||||
return;
|
||||
}
|
||||
|
||||
// Due to a platform limitation, we can't anchor a panel to an element in a
|
||||
// <menupopup>. So we can't support showing info panels for search engines.
|
||||
if (aAnchor.targetName.startsWith(TARGET_SEARCHENGINE_PREFIX))
|
||||
return;
|
||||
|
||||
this._setAppMenuStateForAnnotation(aAnchor.node.ownerDocument.defaultView, "info",
|
||||
this._setAppMenuStateForAnnotation(aChromeWindow, "info",
|
||||
this.targetIsInAppMenu(aAnchor),
|
||||
showInfoPanel.bind(this, aAnchor.node));
|
||||
},
|
||||
@ -1235,8 +1276,9 @@ this.UITour = {
|
||||
if (aWindow.PanelUI.panel.state != "open") {
|
||||
this.recreatePopup(aWindow.PanelUI.panel);
|
||||
}
|
||||
aWindow.PanelUI.panel.addEventListener("popuphiding", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.addEventListener("ViewShowing", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.addEventListener("popuphiding", this.hideAppMenuAnnotations);
|
||||
aWindow.PanelUI.panel.addEventListener("ViewShowing", this.hideAppMenuAnnotations);
|
||||
aWindow.PanelUI.panel.addEventListener("popuphidden", this.onPanelHidden);
|
||||
if (aOpenCallback) {
|
||||
aWindow.PanelUI.panel.addEventListener("popupshown", onPopupShown);
|
||||
}
|
||||
@ -1254,6 +1296,7 @@ this.UITour = {
|
||||
panel.setAttribute("noautohide", true);
|
||||
if (panel.state != "open") {
|
||||
this.recreatePopup(panel);
|
||||
this.availableTargetsCache.clear();
|
||||
}
|
||||
|
||||
// An event object is expected but we don't want to toggle the panel with a click if the panel
|
||||
@ -1263,7 +1306,8 @@ this.UITour = {
|
||||
aOpenCallback();
|
||||
}
|
||||
});
|
||||
panel.addEventListener("popuphidden", this.onLoopPanelHidden);
|
||||
panel.addEventListener("popuphidden", this.onPanelHidden);
|
||||
panel.addEventListener("popuphiding", this.hideLoopPanelAnnotations);
|
||||
} else if (aMenuName == "searchEngines") {
|
||||
this.getTarget(aWindow, "searchProvider").then(target => {
|
||||
openMenuButton(target.node);
|
||||
@ -1278,9 +1322,7 @@ this.UITour = {
|
||||
}
|
||||
|
||||
if (aMenuName == "appMenu") {
|
||||
aWindow.PanelUI.panel.removeAttribute("noautohide");
|
||||
aWindow.PanelUI.hide();
|
||||
this.recreatePopup(aWindow.PanelUI.panel);
|
||||
} else if (aMenuName == "bookmarks") {
|
||||
let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
|
||||
closeMenuButton(menuBtn);
|
||||
@ -1293,7 +1335,7 @@ this.UITour = {
|
||||
}
|
||||
},
|
||||
|
||||
hidePanelAnnotations: function(aEvent) {
|
||||
hideAnnotationsForPanel: function(aEvent, aTargetPositionCallback) {
|
||||
let win = aEvent.target.ownerDocument.defaultView;
|
||||
let annotationElements = new Map([
|
||||
// [annotationElement (panel), method to hide the annotation]
|
||||
@ -1308,7 +1350,7 @@ this.UITour = {
|
||||
// changed since it may have just moved to somewhere outside of the app menu.
|
||||
if (annotationElement.getAttribute("targetName") != aTarget.targetName ||
|
||||
annotationElement.state == "closed" ||
|
||||
!UITour.targetIsInAppMenu(aTarget)) {
|
||||
!aTargetPositionCallback(aTarget)) {
|
||||
return;
|
||||
}
|
||||
hideMethod(win);
|
||||
@ -1318,7 +1360,18 @@ this.UITour = {
|
||||
UITour.appMenuOpenForAnnotation.clear();
|
||||
},
|
||||
|
||||
onLoopPanelHidden: function(aEvent) {
|
||||
hideAppMenuAnnotations: function(aEvent) {
|
||||
UITour.hideAnnotationsForPanel(aEvent, UITour.targetIsInAppMenu);
|
||||
},
|
||||
|
||||
hideLoopPanelAnnotations: function(aEvent) {
|
||||
UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
|
||||
// TODO: Bug 1104927 - Handle the conversation targets separately.
|
||||
return aTarget.targetName.startsWith("loop-");
|
||||
});
|
||||
},
|
||||
|
||||
onPanelHidden: function(aEvent) {
|
||||
aEvent.target.removeAttribute("noautohide");
|
||||
UITour.recreatePopup(aEvent.target);
|
||||
},
|
||||
@ -1575,7 +1628,29 @@ this.UITour = {
|
||||
reject("Search engine not available");
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
notify(eventName, params) {
|
||||
let winEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let window = winEnum.getNext();
|
||||
if (window.closed)
|
||||
continue;
|
||||
debugger;
|
||||
let originTabs = this.originTabs.get(window);
|
||||
if (!originTabs)
|
||||
continue;
|
||||
|
||||
for (let tab of originTabs) {
|
||||
let messageManager = tab.linkedBrowser.messageManager;
|
||||
let detail = {
|
||||
event: eventName,
|
||||
params: params,
|
||||
};
|
||||
messageManager.sendAsyncMessage("UITour:SendPageNotification", detail);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.UITour.init();
|
||||
|
@ -33,6 +33,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_modalDialog.js]
|
||||
run-if = os == "mac" # modal dialog disabling only working on OS X
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
||||
[browser_UITour_observe.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_panel_close_annotation.js]
|
||||
skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
|
||||
[browser_UITour_registerPageID.js]
|
||||
|
@ -50,6 +50,41 @@ let tests = [
|
||||
// Test that the open menu from above was torn down fully.
|
||||
checkLoopPanelIsHidden();
|
||||
}),
|
||||
function test_availableTargets(done) {
|
||||
gContentAPI.showMenu("loop");
|
||||
gContentAPI.getConfiguration("availableTargets", (data) => {
|
||||
for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
|
||||
isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
|
||||
}
|
||||
done();
|
||||
});
|
||||
},
|
||||
function test_hideMenuHidesAnnotations(done) {
|
||||
let infoPanel = document.getElementById("UITourTooltip");
|
||||
let highlightPanel = document.getElementById("UITourHighlightContainer");
|
||||
|
||||
gContentAPI.showMenu("loop", function menuCallback() {
|
||||
gContentAPI.showHighlight("loop-roomList");
|
||||
gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
|
||||
UITour.getTarget(window, "loop-newRoom").then((target) => {
|
||||
waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
|
||||
isnot(loopPanel.state, "closed", "Loop panel should still be open");
|
||||
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
|
||||
is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
|
||||
is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
|
||||
|
||||
info("Close the loop menu and make sure the annotations inside disappear");
|
||||
let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
|
||||
promisePanelElementHidden(window, highlightPanel)];
|
||||
gContentAPI.hideMenu("loop");
|
||||
yield Promise.all(hiddenPromises);
|
||||
isnot(infoPanel.state, "open", "Info panel should have automatically hid");
|
||||
isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
|
||||
done();
|
||||
}), "Info panel should be anchored to the new room button");
|
||||
});
|
||||
});
|
||||
},
|
||||
];
|
||||
|
||||
function checkLoopPanelIsHidden() {
|
||||
@ -61,7 +96,16 @@ function checkLoopPanelIsHidden() {
|
||||
|
||||
if (Services.prefs.getBoolPref("loop.enabled")) {
|
||||
loopButton = window.LoopUI.toolbarButton.node;
|
||||
// The targets to highlight only appear after getting started is launched.
|
||||
Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
|
||||
Services.prefs.setCharPref("loop.server", "http://localhost");
|
||||
Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("loop.gettingStarted.seen");
|
||||
Services.prefs.clearUserPref("loop.server");
|
||||
Services.prefs.clearUserPref("services.push.serverURL");
|
||||
|
||||
// Copied from browser/components/loop/test/mochitest/head.js
|
||||
// Remove the iframe after each test. This also avoids mochitest complaining
|
||||
// about leaks on shutdown as we intentionally hold the iframe open for the
|
||||
|
51
browser/modules/test/browser_UITour_observe.js
Normal file
@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
let gTestTab;
|
||||
let gContentAPI;
|
||||
let gContentWindow;
|
||||
|
||||
Components.utils.import("resource:///modules/UITour.jsm");
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
function test_no_params(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-1", "Correct event name");
|
||||
is(params, null, "No param object");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-1");
|
||||
});
|
||||
},
|
||||
function test_param_string(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-2", "Correct event name");
|
||||
is(params, "a param", "Correct param string");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-2", "a param");
|
||||
});
|
||||
},
|
||||
function test_param_object(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-3", "Correct event name");
|
||||
is(JSON.stringify(params), JSON.stringify({key: "something"}), "Correct param object");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-3", {key: "something"});
|
||||
});
|
||||
},
|
||||
];
|
@ -43,23 +43,54 @@ if (typeof Mozilla == 'undefined') {
|
||||
var id = _generateCallbackID();
|
||||
|
||||
function listener(event) {
|
||||
if (typeof event.detail != "object")
|
||||
if (typeof event.detail != 'object')
|
||||
return;
|
||||
if (event.detail.callbackID != id)
|
||||
return;
|
||||
|
||||
document.removeEventListener("mozUITourResponse", listener);
|
||||
document.removeEventListener('mozUITourResponse', listener);
|
||||
callback(event.detail.data);
|
||||
}
|
||||
document.addEventListener("mozUITourResponse", listener);
|
||||
document.addEventListener('mozUITourResponse', listener);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
var notificationListener = null;
|
||||
function _notificationListener(event) {
|
||||
if (typeof event.detail != 'object')
|
||||
return;
|
||||
if (typeof notificationListener != 'function')
|
||||
return;
|
||||
|
||||
notificationListener(event.detail.event, event.detail.params);
|
||||
}
|
||||
|
||||
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
|
||||
|
||||
Mozilla.UITour.CONFIGNAME_SYNC = "sync";
|
||||
Mozilla.UITour.CONFIGNAME_AVAILABLETARGETS = "availableTargets";
|
||||
Mozilla.UITour.CONFIGNAME_SYNC = 'sync';
|
||||
Mozilla.UITour.CONFIGNAME_AVAILABLETARGETS = 'availableTargets';
|
||||
|
||||
Mozilla.UITour.ping = function(callback) {
|
||||
var data = {};
|
||||
if (callback) {
|
||||
data.callbackID = _waitForCallback(callback);
|
||||
}
|
||||
_sendEvent('ping', data);
|
||||
};
|
||||
|
||||
Mozilla.UITour.observe = function(listener, callback) {
|
||||
notificationListener = listener;
|
||||
|
||||
if (listener) {
|
||||
document.addEventListener('mozUITourNotification',
|
||||
_notificationListener);
|
||||
Mozilla.UITour.ping(callback);
|
||||
} else {
|
||||
document.removeEventListener('mozUITourNotification',
|
||||
_notificationListener);
|
||||
}
|
||||
};
|
||||
|
||||
Mozilla.UITour.registerPageID = function(pageID) {
|
||||
_sendEvent('registerPageID', {
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 520 B |
@ -49,10 +49,11 @@ browser.jar:
|
||||
skin/classic/browser/menuPanel-exit.png
|
||||
skin/classic/browser/menuPanel-help.png
|
||||
skin/classic/browser/menuPanel-small.png
|
||||
skin/classic/browser/bad-content-blocked-16.png
|
||||
skin/classic/browser/bad-content-blocked-64.png
|
||||
skin/classic/browser/bad-content-unblocked-16.png
|
||||
skin/classic/browser/bad-content-unblocked-64.png
|
||||
skin/classic/browser/bad-content-blocked-16.png (../shared/bad-content-blocked-16.png)
|
||||
skin/classic/browser/bad-content-blocked-16@2x.png (../shared/bad-content-blocked-16@2x.png)
|
||||
skin/classic/browser/bad-content-blocked-64.png (../shared/bad-content-blocked-64.png)
|
||||
skin/classic/browser/bad-content-unblocked-16.png (../shared/bad-content-unblocked-16.png)
|
||||
skin/classic/browser/bad-content-unblocked-64.png (../shared/bad-content-unblocked-64.png)
|
||||
skin/classic/browser/monitor.png
|
||||
skin/classic/browser/monitor_16-10.png
|
||||
skin/classic/browser/notification-16.png
|
||||
|
Before Width: | Height: | Size: 346 B |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 520 B |
Before Width: | Height: | Size: 3.1 KiB |
@ -72,14 +72,14 @@ browser.jar:
|
||||
skin/classic/browser/menuPanel-help@2x.png
|
||||
skin/classic/browser/menuPanel-small.png
|
||||
skin/classic/browser/menuPanel-small@2x.png
|
||||
skin/classic/browser/bad-content-blocked-16.png
|
||||
skin/classic/browser/bad-content-blocked-16@2x.png
|
||||
skin/classic/browser/bad-content-blocked-64.png
|
||||
skin/classic/browser/bad-content-blocked-64@2x.png
|
||||
skin/classic/browser/bad-content-unblocked-16.png
|
||||
skin/classic/browser/bad-content-unblocked-16@2x.png
|
||||
skin/classic/browser/bad-content-unblocked-64.png
|
||||
skin/classic/browser/bad-content-unblocked-64@2x.png
|
||||
skin/classic/browser/bad-content-blocked-16.png (../shared/bad-content-blocked-16.png)
|
||||
skin/classic/browser/bad-content-blocked-16@2x.png (../shared/bad-content-blocked-16@2x.png)
|
||||
skin/classic/browser/bad-content-blocked-64.png (../shared/bad-content-blocked-64.png)
|
||||
skin/classic/browser/bad-content-blocked-64@2x.png (../shared/bad-content-blocked-64@2x.png)
|
||||
skin/classic/browser/bad-content-unblocked-16.png (../shared/bad-content-unblocked-16.png)
|
||||
skin/classic/browser/bad-content-unblocked-16@2x.png (../shared/bad-content-unblocked-16@2x.png)
|
||||
skin/classic/browser/bad-content-unblocked-64.png (../shared/bad-content-unblocked-64.png)
|
||||
skin/classic/browser/bad-content-unblocked-64@2x.png (../shared/bad-content-unblocked-64@2x.png)
|
||||
skin/classic/browser/panel-expander-closed.png
|
||||
skin/classic/browser/panel-expander-closed@2x.png
|
||||
skin/classic/browser/panel-expander-open.png
|
||||
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 831 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 346 B |
Before Width: | Height: | Size: 3.1 KiB |
@ -59,10 +59,11 @@ browser.jar:
|
||||
skin/classic/browser/Metro_Glyph.png (Metro_Glyph-aero.png)
|
||||
skin/classic/browser/Metro_Glyph-inverted.png
|
||||
skin/classic/browser/Metro_Glyph-menuPanel.png
|
||||
skin/classic/browser/bad-content-blocked-16.png
|
||||
skin/classic/browser/bad-content-blocked-64.png
|
||||
skin/classic/browser/bad-content-unblocked-16.png
|
||||
skin/classic/browser/bad-content-unblocked-64.png
|
||||
skin/classic/browser/bad-content-blocked-16.png (../shared/bad-content-blocked-16.png)
|
||||
skin/classic/browser/bad-content-blocked-16@2x.png (../shared/bad-content-blocked-16@2x.png)
|
||||
skin/classic/browser/bad-content-blocked-64.png (../shared/bad-content-blocked-64.png)
|
||||
skin/classic/browser/bad-content-unblocked-16.png (../shared/bad-content-unblocked-16.png)
|
||||
skin/classic/browser/bad-content-unblocked-64.png (../shared/bad-content-unblocked-64.png)
|
||||
skin/classic/browser/monitor.png
|
||||
skin/classic/browser/monitor_16-10.png
|
||||
skin/classic/browser/notification-16.png
|
||||
@ -502,10 +503,11 @@ browser.jar:
|
||||
skin/classic/aero/browser/Metro_Glyph.png (Metro_Glyph-aero.png)
|
||||
skin/classic/aero/browser/Metro_Glyph-inverted.png
|
||||
skin/classic/aero/browser/Metro_Glyph-menuPanel.png
|
||||
skin/classic/aero/browser/bad-content-blocked-16.png
|
||||
skin/classic/aero/browser/bad-content-blocked-64.png
|
||||
skin/classic/aero/browser/bad-content-unblocked-16.png
|
||||
skin/classic/aero/browser/bad-content-unblocked-64.png
|
||||
skin/classic/aero/browser/bad-content-blocked-16.png (../shared/bad-content-blocked-16.png)
|
||||
skin/classic/aero/browser/bad-content-blocked-16@2x.png (../shared/bad-content-blocked-16@2x.png)
|
||||
skin/classic/aero/browser/bad-content-blocked-64.png (../shared/bad-content-blocked-64.png)
|
||||
skin/classic/aero/browser/bad-content-unblocked-16.png (../shared/bad-content-unblocked-16.png)
|
||||
skin/classic/aero/browser/bad-content-unblocked-64.png (../shared/bad-content-unblocked-64.png)
|
||||
skin/classic/aero/browser/monitor.png
|
||||
skin/classic/aero/browser/monitor_16-10.png
|
||||
skin/classic/aero/browser/notification-16.png
|
||||
|
@ -614,6 +614,14 @@ nsPresContext::GetDocumentColorPreferences()
|
||||
int32_t useAccessibilityTheme = 0;
|
||||
bool usePrefColors = true;
|
||||
bool isChromeDocShell = false;
|
||||
static int32_t sDocumentColorsSetting;
|
||||
static bool sDocumentColorsSettingPrefCached = false;
|
||||
if (!sDocumentColorsSettingPrefCached) {
|
||||
sDocumentColorsSettingPrefCached = true;
|
||||
Preferences::AddIntVarCache(&sDocumentColorsSetting,
|
||||
"browser.display.document_color_use",
|
||||
0);
|
||||
}
|
||||
|
||||
nsIDocument* doc = mDocument->GetDisplayDocument();
|
||||
if (doc && doc->GetDocShell()) {
|
||||
@ -669,9 +677,21 @@ nsPresContext::GetDocumentColorPreferences()
|
||||
mBackgroundColor = NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF),
|
||||
mBackgroundColor);
|
||||
|
||||
mUseDocumentColors = !useAccessibilityTheme &&
|
||||
Preferences::GetBool("browser.display.use_document_colors",
|
||||
mUseDocumentColors);
|
||||
|
||||
// Now deal with the pref:
|
||||
// 0 = default: always, except in high contrast mode
|
||||
// 1 = always
|
||||
// 2 = never
|
||||
if (sDocumentColorsSetting == 1) {
|
||||
mUseDocumentColors = true;
|
||||
} else if (sDocumentColorsSetting == 2) {
|
||||
mUseDocumentColors = isChromeDocShell || mIsChromeOriginImage;
|
||||
} else {
|
||||
MOZ_ASSERT(!useAccessibilityTheme ||
|
||||
!(isChromeDocShell || mIsChromeOriginImage),
|
||||
"The accessibility theme should only be on for non-chrome");
|
||||
mUseDocumentColors = !useAccessibilityTheme;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -80,8 +80,7 @@ class ContainerLayer;
|
||||
|
||||
// supported values for cached bool types
|
||||
enum nsPresContext_CachedBoolPrefType {
|
||||
kPresContext_UseDocumentColors = 1,
|
||||
kPresContext_UseDocumentFonts,
|
||||
kPresContext_UseDocumentFonts = 1,
|
||||
kPresContext_UnderlineLinks
|
||||
};
|
||||
|
||||
@ -378,8 +377,6 @@ public:
|
||||
switch (aPrefType) {
|
||||
case kPresContext_UseDocumentFonts:
|
||||
return mUseDocumentFonts;
|
||||
case kPresContext_UseDocumentColors:
|
||||
return mUseDocumentColors;
|
||||
case kPresContext_UnderlineLinks:
|
||||
return mUnderlineLinks;
|
||||
default:
|
||||
@ -846,7 +843,9 @@ public:
|
||||
|
||||
// Is it OK to let the page specify colors and backgrounds?
|
||||
bool UseDocumentColors() const {
|
||||
return GetCachedBoolPref(kPresContext_UseDocumentColors) || IsChrome() || IsChromeOriginImage();
|
||||
MOZ_ASSERT(mUseDocumentColors || !(IsChrome() || IsChromeOriginImage()),
|
||||
"We should never have a chrome doc or image that can't use its colors.");
|
||||
return mUseDocumentColors;
|
||||
}
|
||||
|
||||
// Explicitly enable and disable paint flashing.
|
||||
|
@ -50,7 +50,7 @@ var cs5 = getComputedStyle(document.getElementById("five"), "");
|
||||
var cs6 = getComputedStyle(document.getElementById("six"), "");
|
||||
var cs7 = getComputedStyle(document.getElementById("seven"), "");
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [['browser.display.use_document_colors', true]]}, part1);
|
||||
SpecialPowers.pushPrefEnv({'set': [['browser.display.document_color_use', 1]]}, part1);
|
||||
|
||||
var transparentBackgroundColor;
|
||||
var inputBackgroundColor, inputColor, inputBorderTopColor;
|
||||
@ -121,7 +121,7 @@ function part1()
|
||||
inputBorderRightColor = cs4.borderRightColor;
|
||||
inputBorderLeftColor = cs4.borderLeftColor;
|
||||
inputBorderBottomColor = cs4.borderBottomColor;
|
||||
SpecialPowers.pushPrefEnv({'set': [['browser.display.use_document_colors', false]]}, part2);
|
||||
SpecialPowers.pushPrefEnv({'set': [['browser.display.document_color_use', 2]]}, part2);
|
||||
}
|
||||
|
||||
function part2()
|
||||
|
@ -531,7 +531,7 @@ public class BrowserApp extends GeckoApp
|
||||
public void run() {
|
||||
final TabHistoryFragment fragment = TabHistoryFragment.newInstance(historyPageList, toIndex);
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getInteger(R.integer.long_press_vibrate_msec));
|
||||
GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec));
|
||||
fragment.show(R.id.tab_history_panel, fragmentManager.beginTransaction(), TAB_HISTORY_FRAGMENT_TAG);
|
||||
}
|
||||
});
|
||||
|
@ -1416,10 +1416,19 @@ public class GeckoAppShell
|
||||
return (Vibrator) layerView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
// Helper method to convert integer array to long array.
|
||||
private static long[] convertIntToLongArray(int[] input) {
|
||||
long[] output = new long[input.length];
|
||||
for (int i = 0; i < input.length; i++) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// Vibrate only if haptic feedback is enabled.
|
||||
public static void vibrateOnHapticFeedbackEnabled(long milliseconds) {
|
||||
public static void vibrateOnHapticFeedbackEnabled(int[] milliseconds) {
|
||||
if (Settings.System.getInt(getContext().getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) > 0) {
|
||||
vibrate(milliseconds);
|
||||
vibrate(convertIntToLongArray(milliseconds), -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,8 @@ import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* A special drawable used with lightweight themes. This draws a color
|
||||
* (with an optional color-filter) and a bitmap (with a linear gradient
|
||||
* A special drawable used with lightweight themes. This draws a color
|
||||
* (with an optional color-filter) and a bitmap (with a linear gradient
|
||||
* to specify the alpha) in order.
|
||||
*/
|
||||
public class LightweightThemeDrawable extends Drawable {
|
||||
@ -81,6 +81,8 @@ public class LightweightThemeDrawable extends Drawable {
|
||||
/**
|
||||
* Creates a paint that paint a particular color.
|
||||
*
|
||||
* Note that the given color should include an alpha value.
|
||||
*
|
||||
* @param color The color to be painted.
|
||||
*/
|
||||
public void setColor(int color) {
|
||||
@ -91,6 +93,8 @@ public class LightweightThemeDrawable extends Drawable {
|
||||
/**
|
||||
* Creates a paint that paint a particular color, and a filter for the color.
|
||||
*
|
||||
* Note that the given color should include an alpha value.
|
||||
*
|
||||
* @param color The color to be painted.
|
||||
* @param filter The filter color to be applied using SRC_OVER mode.
|
||||
*/
|
||||
|
@ -12,8 +12,10 @@ import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.BrowserLocaleManager;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tab;
|
||||
@ -235,7 +237,9 @@ public class TopSitesPanel extends HomeFragment {
|
||||
// Record tile click events on non-private tabs.
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (!tab.isPrivate()) {
|
||||
mTilesRecorder.recordAction(tab, TilesRecorder.ACTION_CLICK, position, getTilesSnapshot());
|
||||
final Locale locale = Locale.getDefault();
|
||||
final String localeTag = BrowserLocaleManager.getLanguageTag(locale);
|
||||
mTilesRecorder.recordAction(tab, TilesRecorder.ACTION_CLICK, position, getTilesSnapshot(), localeTag);
|
||||
}
|
||||
|
||||
mUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
|
||||
|
@ -18,7 +18,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
|
||||
<TextView android:id="@+id/private_tabs_empty_header"
|
||||
style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/private_browsing_title"/>
|
||||
|
@ -6,6 +6,9 @@
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- The layout_height value is used in TabsPanel.getTabsLayoutContainerHeight
|
||||
and as an offset in PrivateTabsPanel: if you change it here,
|
||||
change it there! -->
|
||||
<RelativeLayout android:id="@+id/tabs_panel_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/browser_toolbar_height">
|
||||
|
@ -147,4 +147,11 @@
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</string-array>
|
||||
<!-- This value is similar to config_longPressVibePattern in android frameworks/base/core/res/res/values/config.xml-->
|
||||
<integer-array name="long_press_vibrate_msec">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>20</item>
|
||||
<item>21</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
|
@ -42,6 +42,7 @@
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_corner_radius">5dp</dimen>
|
||||
<dimen name="new_tablet_tab_strip_button_inset">5dp</dimen>
|
||||
<dimen name="new_tablet_private_tabs_panel_empty_width">300dp</dimen>
|
||||
<dimen name="forward_default_offset">-13dip</dimen>
|
||||
|
||||
<!-- Dimensions used by Favicons and FaviconView -->
|
||||
|
@ -8,6 +8,5 @@
|
||||
<integer name="number_of_top_sites">6</integer>
|
||||
<integer name="number_of_top_sites_cols">2</integer>
|
||||
<integer name="max_icon_grid_columns">4</integer>
|
||||
<integer name="long_press_vibrate_msec">100</integer>
|
||||
|
||||
</resources>
|
||||
|
@ -8,16 +8,20 @@ package org.mozilla.gecko.tabs;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.mozilla.gecko.BrowserLocaleManager;
|
||||
import org.mozilla.gecko.NewTabletUI;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.tabs.TabsPanel.CloseAllPanelView;
|
||||
import org.mozilla.gecko.tabs.TabsPanel.TabsLayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* A container that wraps the private tabs {@link android.widget.AdapterView} and empty
|
||||
@ -27,16 +31,28 @@ import android.widget.FrameLayout;
|
||||
*/
|
||||
class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
private TabsPanel tabsPanel;
|
||||
|
||||
private final TabsLayout tabsLayout;
|
||||
private final View emptyTabsHeader;
|
||||
private final LinearLayout emptyTabsFrame;
|
||||
|
||||
private final int emptyTabsFrameWidth;
|
||||
private final int emptyTabsFrameVerticalOffset;
|
||||
|
||||
public PrivateTabsPanel(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
final Resources res = getResources();
|
||||
emptyTabsFrameVerticalOffset = res.getDimensionPixelOffset(R.dimen.browser_toolbar_height);
|
||||
emptyTabsFrameWidth =
|
||||
res.getDimensionPixelSize(R.dimen.new_tablet_private_tabs_panel_empty_width);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.private_tabs_panel, this);
|
||||
tabsLayout = (TabsLayout) findViewById(R.id.private_tabs_layout);
|
||||
emptyTabsHeader = findViewById(R.id.private_tabs_empty_header);
|
||||
|
||||
final View emptyView = findViewById(R.id.private_tabs_empty);
|
||||
tabsLayout.setEmptyView(emptyView);
|
||||
emptyTabsFrame = (LinearLayout) findViewById(R.id.private_tabs_empty);
|
||||
tabsLayout.setEmptyView(emptyTabsFrame);
|
||||
|
||||
final View learnMore = findViewById(R.id.private_tabs_learn_more);
|
||||
learnMore.setOnClickListener(new OnClickListener() {
|
||||
@ -61,6 +77,8 @@ class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
updateStyleForNewTablet();
|
||||
|
||||
tabsLayout.show();
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
@ -80,4 +98,27 @@ class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
public void closeAll() {
|
||||
tabsLayout.closeAll();
|
||||
}
|
||||
|
||||
private void updateStyleForNewTablet() {
|
||||
if (!NewTabletUI.isEnabled(getContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Move this to styles when removing old tablet.
|
||||
// Delete the emptyTabsFrame & Header class vars too.
|
||||
emptyTabsFrame.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
final FrameLayout.LayoutParams lp =
|
||||
(FrameLayout.LayoutParams) emptyTabsFrame.getLayoutParams();
|
||||
lp.width = getResources().getDimensionPixelSize(
|
||||
R.dimen.new_tablet_private_tabs_panel_empty_width);
|
||||
lp.height = LayoutParams.WRAP_CONTENT;
|
||||
|
||||
// We want to center the content on the screen, not in the View,
|
||||
// so add padding to compensate for the header.
|
||||
lp.gravity = Gravity.CENTER;
|
||||
emptyTabsFrame.setPadding(0, 0, 0, emptyTabsFrameVerticalOffset);
|
||||
|
||||
emptyTabsHeader.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
@ -585,6 +585,28 @@ abstract class BaseTest extends BaseRobocopTest {
|
||||
mAsserter.ok(success, "Top site item was pinned: " + isPinned, null);
|
||||
}
|
||||
|
||||
public void pinTopSite(String gridItemTitle) {
|
||||
verifyPinned(false, gridItemTitle);
|
||||
mSolo.clickLongOnText(gridItemTitle);
|
||||
boolean dialogOpened = mSolo.waitForDialogToOpen();
|
||||
mAsserter.ok(dialogOpened, "Pin site dialog opened: " + gridItemTitle, null);
|
||||
boolean pinSiteFound = waitForText(StringHelper.CONTEXT_MENU_PIN_SITE);
|
||||
mAsserter.ok(pinSiteFound, "Found pin site menu item", null);
|
||||
mSolo.clickOnText(StringHelper.CONTEXT_MENU_PIN_SITE);
|
||||
verifyPinned(true, gridItemTitle);
|
||||
}
|
||||
|
||||
public void unpinTopSite(String gridItemTitle) {
|
||||
verifyPinned(true, gridItemTitle);
|
||||
mSolo.clickLongOnText(gridItemTitle);
|
||||
boolean dialogOpened = mSolo.waitForDialogToOpen();
|
||||
mAsserter.ok(dialogOpened, "Pin site dialog opened: " + gridItemTitle, null);
|
||||
boolean unpinSiteFound = waitForText(StringHelper.CONTEXT_MENU_UNPIN_SITE);
|
||||
mAsserter.ok(unpinSiteFound, "Found unpin site menu item", null);
|
||||
mSolo.clickOnText(StringHelper.CONTEXT_MENU_UNPIN_SITE);
|
||||
verifyPinned(false, gridItemTitle);
|
||||
}
|
||||
|
||||
// Used to perform clicks on pop-up buttons without having to close the virtual keyboard
|
||||
public void clickOnButton(String label) {
|
||||
final Button button = mSolo.getButton(label);
|
||||
|
@ -51,6 +51,7 @@ public class StringHelper {
|
||||
public static final String CONTEXT_MENU_EDIT_SITE_SETTINGS = "Edit Site Settings";
|
||||
public static final String CONTEXT_MENU_ADD_TO_HOME_SCREEN = "Add to Home Screen";
|
||||
public static final String CONTEXT_MENU_PIN_SITE = "Pin Site";
|
||||
public static final String CONTEXT_MENU_UNPIN_SITE = "Unpin Site";
|
||||
|
||||
// Context Menu menu items
|
||||
public static final String[] CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB = new String[] {
|
||||
|
@ -7,6 +7,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
import org.json.JSONArray;
|
||||
@ -15,6 +16,8 @@ import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.BrowserLocaleManager;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.SuggestedSites;
|
||||
@ -129,7 +132,14 @@ public class testDistribution extends ContentProviderTest {
|
||||
Distribution dist = initDistribution(mockPackagePath);
|
||||
SuggestedSites suggestedSites = new SuggestedSites(mActivity, dist);
|
||||
BrowserDB.setSuggestedSites(suggestedSites);
|
||||
checkTilesReporting();
|
||||
|
||||
// Test tiles uploading for an en-US OS locale with no app locale.
|
||||
setOSLocale(Locale.US);
|
||||
checkTilesReporting("en-US");
|
||||
|
||||
// Test tiles uploading for an es-MX OS locale with no app locale.
|
||||
setOSLocale(new Locale("es", "MX"));
|
||||
checkTilesReporting("es-MX");
|
||||
|
||||
// Pre-clear distribution pref, run basic preferences and en-US localized preferences Tests
|
||||
clearDistributionPref();
|
||||
@ -154,6 +164,11 @@ public class testDistribution extends ContentProviderTest {
|
||||
doTestInvalidReferrerIntent();
|
||||
}
|
||||
|
||||
private void setOSLocale(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
BrowserLocaleManager.storeAndNotifyOSLocale(GeckoSharedPrefs.forProfile(mActivity), locale);
|
||||
}
|
||||
|
||||
private void doReferrerTest(String ref, final TestableDistribution distribution, final Runnable distributionReady) throws InterruptedException {
|
||||
final Intent intent = new Intent(ACTION_INSTALL_REFERRER);
|
||||
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, CLASS_REFERRER_RECEIVER);
|
||||
@ -330,37 +345,9 @@ public class testDistribution extends ContentProviderTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the distribution locale preference for the test
|
||||
private void setTestLocale(String aLocale) {
|
||||
String prefUseragentLocale = "general.useragent.locale";
|
||||
|
||||
JSONObject jsonPref = new JSONObject();
|
||||
try {
|
||||
// Request the pref change to the locale.
|
||||
jsonPref.put("name", prefUseragentLocale);
|
||||
jsonPref.put("type", "string");
|
||||
jsonPref.put("value", aLocale);
|
||||
mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString());
|
||||
|
||||
// Wait for confirmation of the pref change.
|
||||
final String[] prefNames = { prefUseragentLocale };
|
||||
|
||||
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
|
||||
mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames);
|
||||
|
||||
JSONObject data = null;
|
||||
int requestId = -1;
|
||||
|
||||
// Wait until we get the correct "Preferences:Data" event
|
||||
while (requestId != PREF_REQUEST_ID) {
|
||||
data = new JSONObject(eventExpecter.blockForEventData());
|
||||
requestId = data.getInt("requestId");
|
||||
}
|
||||
eventExpecter.unregisterListener();
|
||||
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "exception setting test locale", e.toString());
|
||||
}
|
||||
// Sets the distribution locale preference for the test.
|
||||
private void setTestLocale(String locale) {
|
||||
BrowserLocaleManager.getInstance().setSelectedLocale(mActivity, locale);
|
||||
}
|
||||
|
||||
// Test localized distribution and preferences values stored in preferences.json
|
||||
@ -454,32 +441,31 @@ public class testDistribution extends ContentProviderTest {
|
||||
TestableDistribution.clearReferrerDescriptorForTesting();
|
||||
}
|
||||
|
||||
public void checkTilesReporting() throws JSONException {
|
||||
public void checkTilesReporting(String localeCode) throws JSONException {
|
||||
// Slight hack: Force top sites grid to reload.
|
||||
inputAndLoadUrl(StringHelper.ABOUT_BLANK_URL);
|
||||
inputAndLoadUrl(StringHelper.ABOUT_HOME_URL);
|
||||
|
||||
// Click the first tracking tile and verify the posted data.
|
||||
JSONObject response = clickTrackingTile(StringHelper.DISTRIBUTION1_LABEL);
|
||||
mAsserter.is(0, response.getInt("click"), "JSON click index matched");
|
||||
mAsserter.is("[{\"id\":123},{\"id\":456},{},{},{},{}]", response.getString("tiles"), "JSON tiles data matched");
|
||||
mAsserter.is(response.getInt("click"), 0, "JSON click index matched");
|
||||
mAsserter.is(response.getString("locale"), localeCode, "JSON locale code matched");
|
||||
mAsserter.is(response.getString("tiles"), "[{\"id\":123},{\"id\":456},{},{},{},{}]", "JSON tiles data matched");
|
||||
|
||||
inputAndLoadUrl(StringHelper.ABOUT_HOME_URL);
|
||||
|
||||
// Pin the second tracking tile.
|
||||
verifyPinned(false, StringHelper.DISTRIBUTION2_LABEL);
|
||||
mSolo.clickLongOnText(StringHelper.DISTRIBUTION2_LABEL);
|
||||
boolean dialogOpened = mSolo.waitForDialogToOpen();
|
||||
mAsserter.ok(dialogOpened, "Pin site dialog opened", null);
|
||||
boolean pinSiteFound = waitForText(StringHelper.CONTEXT_MENU_PIN_SITE);
|
||||
mAsserter.ok(pinSiteFound, "Found pin site menu item", null);
|
||||
mSolo.clickOnText(StringHelper.CONTEXT_MENU_PIN_SITE);
|
||||
verifyPinned(true, StringHelper.DISTRIBUTION2_LABEL);
|
||||
pinTopSite(StringHelper.DISTRIBUTION2_LABEL);
|
||||
|
||||
// Click the second tracking tile and verify the posted data.
|
||||
response = clickTrackingTile(StringHelper.DISTRIBUTION2_LABEL);
|
||||
mAsserter.is(1, response.getInt("click"), "JSON click index matched");
|
||||
mAsserter.is("[{\"id\":123},{\"id\":456,\"pin\":true},{},{},{},{}]", response.getString("tiles"), "JSON tiles data matched");
|
||||
mAsserter.is(response.getInt("click"), 1, "JSON click index matched");
|
||||
mAsserter.is(response.getString("tiles"), "[{\"id\":123},{\"id\":456,\"pin\":true},{},{},{},{}]", "JSON tiles data matched");
|
||||
|
||||
inputAndLoadUrl(StringHelper.ABOUT_HOME_URL);
|
||||
|
||||
// Unpin the second tracking tile.
|
||||
unpinTopSite(StringHelper.DISTRIBUTION2_LABEL);
|
||||
}
|
||||
|
||||
private JSONObject clickTrackingTile(String text) throws JSONException {
|
||||
|
@ -22,7 +22,7 @@ public class TilesRecorder {
|
||||
private static final String LOG_TAG = "GeckoTilesRecorder";
|
||||
private static final String EVENT_TILES_CLICK = "Tiles:Click";
|
||||
|
||||
public void recordAction(Tab tab, String action, int index, List<Tile> tiles) {
|
||||
public void recordAction(Tab tab, String action, int index, List<Tile> tiles, String locale) {
|
||||
final Tile clickedTile = tiles.get(index);
|
||||
|
||||
if (tab == null || clickedTile == null) {
|
||||
@ -67,6 +67,7 @@ public class TilesRecorder {
|
||||
final JSONObject payload = new JSONObject();
|
||||
payload.put(action, clickedTileIndex);
|
||||
payload.put("tiles", tilesJSON);
|
||||
payload.put("locale", locale);
|
||||
|
||||
final JSONObject data = new JSONObject();
|
||||
data.put("tabId", tab.getId());
|
||||
|
@ -152,4 +152,8 @@ public class ThemedEditText extends android.widget.EditText
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -152,4 +152,8 @@ public class ThemedImageButton extends android.widget.ImageButton
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -152,4 +152,8 @@ public class ThemedImageView extends android.widget.ImageView
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -147,4 +147,8 @@ public class ThemedLinearLayout extends android.widget.LinearLayout
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -152,4 +152,8 @@ public class ThemedRelativeLayout extends android.widget.RelativeLayout
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -147,4 +147,8 @@ public class ThemedTextSwitcher extends android.widget.TextSwitcher
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -152,4 +152,8 @@ public class ThemedTextView extends android.widget.TextView
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -152,4 +152,8 @@ public class ThemedView extends android.view.View
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -155,4 +155,8 @@ public class Themed@VIEW_NAME_SUFFIX@ extends @BASE_TYPE@
|
||||
public ColorDrawable getColorDrawable(int id) {
|
||||
return new ColorDrawable(getResources().getColor(id));
|
||||
}
|
||||
|
||||
protected LightweightTheme getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ this.WebappManager = {
|
||||
|
||||
try {
|
||||
yield DOMApplicationRegistry.startDownload(aData.manifestURL);
|
||||
} catch (ex if ex.message == "PACKAGE_UNCHANGED") {
|
||||
} catch (ex if ex == "PACKAGE_UNCHANGED") {
|
||||
debug("package unchanged");
|
||||
// If the package is unchanged, then there's nothing more to do.
|
||||
return;
|
||||
|
@ -177,7 +177,10 @@ pref("browser.sessionhistory.max_total_viewers", -1);
|
||||
pref("ui.use_native_colors", true);
|
||||
pref("ui.click_hold_context_menus", false);
|
||||
pref("browser.display.use_document_fonts", 1); // 0 = never, 1 = quick, 2 = always
|
||||
pref("browser.display.use_document_colors", true);
|
||||
// 0 = default: always, except in high contrast mode
|
||||
// 1 = always
|
||||
// 2 = never
|
||||
pref("browser.display.document_color_use", 0);
|
||||
pref("browser.display.use_system_colors", false);
|
||||
pref("browser.display.foreground_color", "#000000");
|
||||
pref("browser.display.background_color", "#FFFFFF");
|
||||
|
@ -310,10 +310,12 @@ AboutProtocolInstance.prototype = {
|
||||
|
||||
let AboutProtocolChild = {
|
||||
_classDescription: "Addon shim about: protocol handler",
|
||||
_classID: Components.ID("8d56a310-0c80-11e4-9191-0800200c9a66"),
|
||||
|
||||
init: function() {
|
||||
this._instances = {};
|
||||
// Maps contractIDs to instances
|
||||
this._instances = new Map();
|
||||
// Maps contractIDs to classIDs
|
||||
this._classIDs = new Map();
|
||||
NotificationTracker.watch("about-protocol", this);
|
||||
},
|
||||
|
||||
@ -322,11 +324,19 @@ let AboutProtocolChild = {
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
if (register) {
|
||||
let instance = new AboutProtocolInstance(contractID);
|
||||
this._instances[contractID] = instance;
|
||||
registrar.registerFactory(this._classID, this._classDescription, contractID, instance);
|
||||
let classID = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator)
|
||||
.generateUUID();
|
||||
|
||||
this._instances.set(contractID, instance);
|
||||
this._classIDs.set(contractID, classID);
|
||||
registrar.registerFactory(classID, this._classDescription, contractID, instance);
|
||||
} else {
|
||||
registrar.unregisterFactory(this._classID, this._instances[contractID]);
|
||||
delete this._instances[contractID];
|
||||
let instance = this._instances.get(contractID);
|
||||
let classID = this._classIDs.get(contractID);
|
||||
registrar.unregisterFactory(classID, instance);
|
||||
this._instances.delete(contractID);
|
||||
this._classIDs.delete(contractID);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -404,8 +404,13 @@ let Bookmarks = Object.freeze({
|
||||
let info = guidOrInfo;
|
||||
if (!info)
|
||||
throw new Error("Input should be a valid object");
|
||||
if (typeof(guidOrInfo) != "object") {
|
||||
if (typeof(guidOrInfo) != "object")
|
||||
info = { guid: guidOrInfo };
|
||||
|
||||
// Disallow removing the root folders.
|
||||
if ([this.rootGuid, this.menuGuid, this.toolbarGuid, this.unfiledGuid,
|
||||
this.tagsGuid].indexOf(info.guid) != -1) {
|
||||
throw new Error("It's not possible to remove Places root folders.");
|
||||
}
|
||||
|
||||
// Even if we ignore any other unneeded property, we still validate any
|
||||
@ -417,10 +422,6 @@ let Bookmarks = Object.freeze({
|
||||
if (!item)
|
||||
throw new Error("No bookmarks found for the provided GUID.");
|
||||
|
||||
// Disallow removing the root folders.
|
||||
if (!item._parentId || item._parentId == PlacesUtils.placesRootId)
|
||||
throw new Error("It's not possible to remove Places root folders.");
|
||||
|
||||
item = yield removeBookmark(item);
|
||||
|
||||
// Notify onItemRemoved to listeners.
|
||||
|
@ -44,19 +44,24 @@ add_task(function* remove_nonexistent_guid() {
|
||||
});
|
||||
|
||||
add_task(function* remove_roots_fail() {
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.remove(PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.ok(false, "Should have thrown");
|
||||
} catch (ex) {
|
||||
Assert.ok(/It's not possible to remove Places root folders/.test(ex));
|
||||
let guids = [PlacesUtils.bookmarks.rootGuid,
|
||||
PlacesUtils.bookmarks.unfiledGuid,
|
||||
PlacesUtils.bookmarks.menuGuid,
|
||||
PlacesUtils.bookmarks.toolbarGuid,
|
||||
PlacesUtils.bookmarks.tagsGuid];
|
||||
for (let guid of guids) {
|
||||
Assert.throws(() => PlacesUtils.bookmarks.remove(guid),
|
||||
/It's not possible to remove Places root folders/);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.remove(PlacesUtils.bookmarks.rootGuid);
|
||||
Assert.ok(false, "Should have thrown");
|
||||
} catch (ex) {
|
||||
Assert.ok(/It's not possible to remove Places root folders/.test(ex));
|
||||
}
|
||||
add_task(function* remove_normal_folder_undes_root_succeeds() {
|
||||
let folder = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.rootGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
checkBookmarkObject(folder);
|
||||
let removed_folder = yield PlacesUtils.bookmarks.remove(folder);
|
||||
Assert.deepEqual(folder, removed_folder);
|
||||
Assert.strictEqual((yield PlacesUtils.bookmarks.fetch(folder.guid)), null);
|
||||
});
|
||||
|
||||
add_task(function* remove_bookmark() {
|
||||
|