mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
3156cab275
@ -350,13 +350,14 @@ pref("browser.urlbar.match.title", "#");
|
||||
pref("browser.urlbar.match.url", "@");
|
||||
|
||||
// The default behavior for the urlbar can be configured to use any combination
|
||||
// of the restrict or match filters with each additional filter restricting
|
||||
// more (intersection). Add the following values to set the behavior as the
|
||||
// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
|
||||
// 64: javascript, 128: tabs
|
||||
// E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
|
||||
// 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
|
||||
pref("browser.urlbar.default.behavior", 0);
|
||||
// of the match filters with each additional filter adding more results (union).
|
||||
pref("browser.urlbar.suggest.history", true);
|
||||
pref("browser.urlbar.suggest.bookmark", true);
|
||||
pref("browser.urlbar.suggest.openpage", true);
|
||||
|
||||
// Restrictions to current suggestions can also be applied (intersection).
|
||||
// Typed suggestion works only if history is set to true.
|
||||
pref("browser.urlbar.suggest.history.onlyTyped", false);
|
||||
|
||||
pref("browser.urlbar.formatting.enabled", true);
|
||||
pref("browser.urlbar.trimURLs", true);
|
||||
@ -1262,7 +1263,6 @@ pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
|
||||
pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
|
||||
pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
|
||||
pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
|
||||
pref("services.sync.prefs.sync.browser.urlbar.default.behavior", true);
|
||||
pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
|
||||
pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
|
||||
pref("services.sync.prefs.sync.dom.disable_window_flip", true);
|
||||
|
@ -43,10 +43,12 @@ let gCustomize = {
|
||||
return Promise.resolve(nodes);
|
||||
}
|
||||
|
||||
panel.hidden = false;
|
||||
panel.openPopup(button);
|
||||
button.setAttribute("active", true);
|
||||
panel.addEventListener("popuphidden", function onHidden() {
|
||||
panel.removeEventListener("popuphidden", onHidden);
|
||||
panel.hidden = true;
|
||||
button.removeAttribute("active");
|
||||
});
|
||||
|
||||
|
@ -20,6 +20,7 @@ let gIntro = {
|
||||
}
|
||||
|
||||
this._nodes.panel.addEventListener("popupshowing", e => this._setUpPanel());
|
||||
this._nodes.panel.addEventListener("popuphidden", e => this._hidePanel());
|
||||
this._nodes.what.addEventListener("click", e => this.showPanel());
|
||||
},
|
||||
|
||||
@ -32,6 +33,7 @@ let gIntro = {
|
||||
|
||||
showPanel: function() {
|
||||
// Point the panel at the 'what' link
|
||||
this._nodes.panel.hidden = false;
|
||||
this._nodes.panel.openPopup(this._nodes.what);
|
||||
},
|
||||
|
||||
@ -48,4 +50,8 @@ let gIntro = {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_hidePanel: function() {
|
||||
this._nodes.panel.hidden = true;
|
||||
}
|
||||
};
|
||||
|
@ -21,19 +21,19 @@
|
||||
title="&newtab.pageTitle;">
|
||||
|
||||
<xul:panel id="newtab-intro-panel" orient="vertical" type="arrow"
|
||||
noautohide="true">
|
||||
noautohide="true" hidden="true">
|
||||
<h1>&newtab.intro.header;</h1>
|
||||
</xul:panel>
|
||||
|
||||
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
|
||||
noautohide="true">
|
||||
noautohide="true" hidden="true">
|
||||
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
|
||||
<xul:label>&cmd_engineManager.label;</xul:label>
|
||||
</xul:hbox>
|
||||
</xul:panel>
|
||||
|
||||
<xul:panel id="newtab-customize-panel" orient="vertical" type="arrow"
|
||||
noautohide="true">
|
||||
noautohide="true" hidden="true">
|
||||
<xul:hbox id="newtab-customize-enhanced" class="newtab-customize-panel-item">
|
||||
<xul:label>&newtab.customize.enhanced;</xul:label>
|
||||
</xul:hbox>
|
||||
|
@ -4,6 +4,9 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#endif
|
||||
|
||||
// The amount of time we wait while coalescing updates for hidden pages.
|
||||
const SCHEDULE_UPDATE_TIMEOUT_MS = 1000;
|
||||
|
||||
/**
|
||||
* This singleton represents the whole 'New Tab Page' and takes care of
|
||||
* initializing all its components.
|
||||
@ -69,16 +72,39 @@ let gPage = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the whole page and the grid when the storage has changed.
|
||||
* @param aOnlyIfHidden If true, the page is updated only if it's hidden in
|
||||
* the preloader.
|
||||
* Updates the page's grid right away for visible pages. If the page is
|
||||
* currently hidden, i.e. in a background tab or in the preloader, then we
|
||||
* batch multiple update requests and refresh the grid once after a short
|
||||
* delay. Accepts a single parameter the specifies the reason for requesting
|
||||
* a page update. The page may decide to delay or prevent a requested updated
|
||||
* based on the given reason.
|
||||
*/
|
||||
update: function Page_update(aOnlyIfHidden=false) {
|
||||
let skipUpdate = aOnlyIfHidden && !document.hidden;
|
||||
// The grid might not be ready yet as we initialize it asynchronously.
|
||||
if (gGrid.ready && !skipUpdate) {
|
||||
gGrid.refresh();
|
||||
update(reason = "") {
|
||||
// Update immediately if we're visible.
|
||||
if (!document.hidden) {
|
||||
// Ignore updates where reason=links-changed as those signal that the
|
||||
// provider's set of links changed. We don't want to update visible pages
|
||||
// in that case, it is ok to wait until the user opens the next tab.
|
||||
if (reason != "links-changed" && gGrid.ready) {
|
||||
gGrid.refresh();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail out if we scheduled before.
|
||||
if (this._scheduleUpdateTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._scheduleUpdateTimeout = setTimeout(() => {
|
||||
// Refresh if the grid is ready.
|
||||
if (gGrid.ready) {
|
||||
gGrid.refresh();
|
||||
}
|
||||
|
||||
this._scheduleUpdateTimeout = null;
|
||||
}, SCHEDULE_UPDATE_TIMEOUT_MS);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -170,6 +196,15 @@ let gPage = {
|
||||
}
|
||||
break;
|
||||
case "visibilitychange":
|
||||
// Cancel any delayed updates for hidden pages now that we're visible.
|
||||
if (this._scheduleUpdateTimeout) {
|
||||
clearTimeout(this._scheduleUpdateTimeout);
|
||||
this._scheduleUpdateTimeout = null;
|
||||
|
||||
// An update was pending so force an update now.
|
||||
this.update();
|
||||
}
|
||||
|
||||
setTimeout(() => this.onPageFirstVisible());
|
||||
removeEventListener("visibilitychange", this);
|
||||
break;
|
||||
|
@ -21,10 +21,12 @@ let gSearch = {
|
||||
showPanel: function () {
|
||||
let panel = this._nodes.panel;
|
||||
let logo = this._nodes.logo;
|
||||
panel.hidden = false;
|
||||
panel.openPopup(logo);
|
||||
logo.setAttribute("active", "true");
|
||||
panel.addEventListener("popuphidden", function onHidden() {
|
||||
panel.removeEventListener("popuphidden", onHidden);
|
||||
panel.hidden = true;
|
||||
logo.removeAttribute("active");
|
||||
});
|
||||
},
|
||||
|
@ -44,6 +44,10 @@ function runTests() {
|
||||
"New page grid is updated correctly.");
|
||||
|
||||
gBrowser.removeTab(newTab);
|
||||
|
||||
// Wait until the original tab is visible again.
|
||||
let doc = existingTab.linkedBrowser.contentDocument;
|
||||
yield waitForCondition(() => !doc.hidden).then(TestRunner.next);
|
||||
}
|
||||
|
||||
gBrowser.removeTab(existingTab);
|
||||
|
@ -354,8 +354,8 @@ let checkCurrentEngine = Task.async(function* ({name: basename, logoPrefix1x, lo
|
||||
|
||||
ok(base64.startsWith(expectedLogoPrefix), "Checking image prefix.");
|
||||
|
||||
logo.click();
|
||||
let panel = searchPanel();
|
||||
panel.openPopup(logo);
|
||||
yield promisePanelShown(panel);
|
||||
|
||||
panel.hidePopup();
|
||||
|
@ -1,44 +1,32 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Checks that newtab is updated as its links change.
|
||||
*/
|
||||
|
||||
function runTests() {
|
||||
// First, start with an empty page. setLinks will trigger a hidden page
|
||||
// update because it calls clearHistory. We need to wait for that update to
|
||||
// happen so that the next time we wait for a page update below, we catch the
|
||||
// right update and not the one triggered by setLinks.
|
||||
//
|
||||
// Why this weird way of yielding? First, these two functions don't return
|
||||
// promises, they call TestRunner.next when done. Second, the point at which
|
||||
// setLinks is done is independent of when the page update will happen, so
|
||||
// calling whenPagesUpdated cannot wait until that time.
|
||||
setLinks([]);
|
||||
whenPagesUpdated(null, true);
|
||||
yield null;
|
||||
yield null;
|
||||
yield whenPagesUpdatedAnd(resolve => setLinks([], resolve));
|
||||
|
||||
// Strategy: Add some visits, open a new page, check the grid, repeat.
|
||||
fillHistory([link(1)]);
|
||||
yield whenPagesUpdated(null, true);
|
||||
yield fillHistoryAndWaitForPageUpdate([1]);
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("1,,,,,,,,");
|
||||
|
||||
fillHistory([link(2)]);
|
||||
yield whenPagesUpdated(null, true);
|
||||
yield fillHistoryAndWaitForPageUpdate([2]);
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("2,1,,,,,,,");
|
||||
|
||||
fillHistory([link(1)]);
|
||||
yield whenPagesUpdated(null, true);
|
||||
yield fillHistoryAndWaitForPageUpdate([1]);
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("1,2,,,,,,,");
|
||||
|
||||
// Wait for fillHistory to add all links before waiting for an update
|
||||
yield fillHistory([link(2), link(3), link(4)], TestRunner.next);
|
||||
yield whenPagesUpdated(null, true);
|
||||
yield fillHistoryAndWaitForPageUpdate([2, 3, 4]);
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("2,1,3,4,,,,,");
|
||||
|
||||
@ -46,6 +34,16 @@ function runTests() {
|
||||
is(getCell(1).site.link.type, "history", "added link is history");
|
||||
}
|
||||
|
||||
function fillHistoryAndWaitForPageUpdate(links) {
|
||||
return whenPagesUpdatedAnd(resolve => fillHistory(links.map(link), resolve));
|
||||
}
|
||||
|
||||
function whenPagesUpdatedAnd(promiseConstructor) {
|
||||
let promise1 = new Promise(whenPagesUpdated);
|
||||
let promise2 = new Promise(promiseConstructor);
|
||||
return Promise.all([promise1, promise2]).then(TestRunner.next);
|
||||
}
|
||||
|
||||
function link(id) {
|
||||
return { url: "http://example" + id + ".com/", title: "site#" + id };
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ function getCell(aIndex) {
|
||||
* {url: "http://example2.com/", title: "site#2"},
|
||||
* {url: "http://example3.com/", title: "site#3"}]
|
||||
*/
|
||||
function setLinks(aLinks) {
|
||||
function setLinks(aLinks, aCallback = TestRunner.next) {
|
||||
let links = aLinks;
|
||||
|
||||
if (typeof links == "string") {
|
||||
@ -233,7 +233,7 @@ function setLinks(aLinks) {
|
||||
fillHistory(links, function () {
|
||||
NewTabUtils.links.populateCache(function () {
|
||||
NewTabUtils.allPages.update();
|
||||
TestRunner.next();
|
||||
aCallback();
|
||||
}, true);
|
||||
});
|
||||
});
|
||||
@ -249,7 +249,7 @@ function clearHistory(aCallback) {
|
||||
PlacesUtils.history.removeAllPages();
|
||||
}
|
||||
|
||||
function fillHistory(aLinks, aCallback) {
|
||||
function fillHistory(aLinks, aCallback = TestRunner.next) {
|
||||
let numLinks = aLinks.length;
|
||||
if (!numLinks) {
|
||||
if (aCallback)
|
||||
@ -323,6 +323,33 @@ function restore() {
|
||||
NewTabUtils.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until a given condition becomes true.
|
||||
*/
|
||||
function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let tries = 0;
|
||||
|
||||
function tryNow() {
|
||||
tries++;
|
||||
|
||||
if (aConditionFn()) {
|
||||
resolve();
|
||||
} else if (tries < aMaxTries) {
|
||||
tryAgain();
|
||||
} else {
|
||||
reject("Condition timed out: " + aConditionFn.toSource());
|
||||
}
|
||||
}
|
||||
|
||||
function tryAgain() {
|
||||
setTimeout(tryNow, aCheckInterval);
|
||||
}
|
||||
|
||||
tryAgain();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab containing 'about:newtab'.
|
||||
*/
|
||||
@ -349,7 +376,7 @@ function addNewTabPageTabPromise() {
|
||||
|
||||
// The new tab page might have been preloaded in the background.
|
||||
if (browser.contentDocument.readyState == "complete") {
|
||||
whenNewTabLoaded();
|
||||
waitForCondition(() => !browser.contentDocument.hidden).then(whenNewTabLoaded);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
@ -617,18 +644,14 @@ function createDragEvent(aEventType, aData) {
|
||||
/**
|
||||
* Resumes testing when all pages have been updated.
|
||||
* @param aCallback Called when done. If not specified, TestRunner.next is used.
|
||||
* @param aOnlyIfHidden If true, this resumes testing only when an update that
|
||||
* applies to pre-loaded, hidden pages is observed. If
|
||||
* false, this resumes testing when any update is observed.
|
||||
*/
|
||||
function whenPagesUpdated(aCallback, aOnlyIfHidden=false) {
|
||||
function whenPagesUpdated(aCallback = TestRunner.next) {
|
||||
let page = {
|
||||
observe: _ => _,
|
||||
update: function (onlyIfHidden=false) {
|
||||
if (onlyIfHidden == aOnlyIfHidden) {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
executeSoon(aCallback || TestRunner.next);
|
||||
}
|
||||
|
||||
update() {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -743,16 +743,19 @@ html, .fx-embedded, #main,
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.standalone .room-inner-action-area {
|
||||
.standalone .room-inner-info-area {
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
left: 0;
|
||||
right: 25%;
|
||||
z-index: 1000;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.standalone .room-inner-action-area button {
|
||||
position: absolute;
|
||||
.standalone .room-inner-info-area button {
|
||||
border-radius: 3px;
|
||||
font-size: 1.2em;
|
||||
padding: .2em 1.2em;
|
||||
|
@ -129,19 +129,33 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
|
||||
},
|
||||
|
||||
_renderActionButtons: function() {
|
||||
_renderContextualRoomInfo: function() {
|
||||
switch(this.state.roomState) {
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.READY: {
|
||||
// Join button
|
||||
return (
|
||||
React.DOM.div({className: "room-inner-info-area"},
|
||||
React.DOM.button({className: "btn btn-join btn-info", onClick: this.joinRoom},
|
||||
mozL10n.get("rooms_room_join_label")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED: {
|
||||
// Empty room message
|
||||
return (
|
||||
React.DOM.div({className: "room-inner-info-area"},
|
||||
React.DOM.p({className: "empty-room-message"},
|
||||
mozL10n.get("rooms_only_occupant_label")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
// XXX Render "Start your own" button when room is over capacity (see
|
||||
// bug 1074709)
|
||||
if (this.state.roomState === ROOM_STATES.INIT ||
|
||||
this.state.roomState === ROOM_STATES.READY) {
|
||||
return (
|
||||
React.DOM.div({className: "room-inner-action-area"},
|
||||
React.DOM.button({className: "btn btn-join btn-info", onClick: this.joinRoom},
|
||||
mozL10n.get("rooms_room_join_label")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -154,7 +168,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "room-conversation-wrapper"},
|
||||
this._renderActionButtons(),
|
||||
this._renderContextualRoomInfo(),
|
||||
React.DOM.div({className: "video-layout-wrapper"},
|
||||
React.DOM.div({className: "conversation room-conversation"},
|
||||
React.DOM.h2({className: "room-name"}, this.state.roomName),
|
||||
|
@ -129,19 +129,33 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
|
||||
},
|
||||
|
||||
_renderActionButtons: function() {
|
||||
_renderContextualRoomInfo: function() {
|
||||
switch(this.state.roomState) {
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.READY: {
|
||||
// Join button
|
||||
return (
|
||||
<div className="room-inner-info-area">
|
||||
<button className="btn btn-join btn-info" onClick={this.joinRoom}>
|
||||
{mozL10n.get("rooms_room_join_label")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED: {
|
||||
// Empty room message
|
||||
return (
|
||||
<div className="room-inner-info-area">
|
||||
<p className="empty-room-message">
|
||||
{mozL10n.get("rooms_only_occupant_label")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
// XXX Render "Start your own" button when room is over capacity (see
|
||||
// bug 1074709)
|
||||
if (this.state.roomState === ROOM_STATES.INIT ||
|
||||
this.state.roomState === ROOM_STATES.READY) {
|
||||
return (
|
||||
<div className="room-inner-action-area">
|
||||
<button className="btn btn-join btn-info" onClick={this.joinRoom}>
|
||||
{mozL10n.get("rooms_room_join_label")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -154,7 +168,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
|
||||
return (
|
||||
<div className="room-conversation-wrapper">
|
||||
{this._renderActionButtons()}
|
||||
{this._renderContextualRoomInfo()}
|
||||
<div className="video-layout-wrapper">
|
||||
<div className="conversation room-conversation">
|
||||
<h2 className="room-name">{this.state.roomName}</h2>
|
||||
|
@ -102,6 +102,32 @@ describe("loop.standaloneRoomViews", function() {
|
||||
view = mountTestComponent();
|
||||
});
|
||||
|
||||
describe("Empty room message", function() {
|
||||
it("should display an empty room message on JOINED",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.not.eql(null);
|
||||
});
|
||||
|
||||
it("should display an empty room message on SESSION_CONNECTED",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.not.eql(null);
|
||||
});
|
||||
|
||||
it("shouldn't display an empty room message on HAS_PARTICIPANTS",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.eql(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Join button", function() {
|
||||
function getJoinButton(view) {
|
||||
return view.getDOMNode().querySelector(".btn-join");
|
||||
|
@ -569,6 +569,15 @@
|
||||
)
|
||||
),
|
||||
|
||||
Example({summary: "Standalone room conversation (joined)"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
StandaloneRoomView({
|
||||
dispatcher: dispatcher,
|
||||
activeRoomStore: activeRoomStore,
|
||||
roomState: ROOM_STATES.JOINED})
|
||||
)
|
||||
),
|
||||
|
||||
Example({summary: "Standalone room conversation (has-participants)"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
StandaloneRoomView({
|
||||
|
@ -569,6 +569,15 @@
|
||||
</div>
|
||||
</Example>
|
||||
|
||||
<Example summary="Standalone room conversation (joined)">
|
||||
<div className="standalone">
|
||||
<StandaloneRoomView
|
||||
dispatcher={dispatcher}
|
||||
activeRoomStore={activeRoomStore}
|
||||
roomState={ROOM_STATES.JOINED} />
|
||||
</div>
|
||||
</Example>
|
||||
|
||||
<Example summary="Standalone room conversation (has-participants)">
|
||||
<div className="standalone">
|
||||
<StandaloneRoomView
|
||||
|
@ -230,7 +230,7 @@ BrowserGlue.prototype = {
|
||||
},
|
||||
#endif
|
||||
|
||||
// nsIObserver implementation
|
||||
// nsIObserver implementation
|
||||
observe: function BG_observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "prefservice:after-app-defaults":
|
||||
@ -1459,7 +1459,7 @@ BrowserGlue.prototype = {
|
||||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 25;
|
||||
const UI_VERSION = 26;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
@ -1735,6 +1735,40 @@ BrowserGlue.prototype = {
|
||||
catch (ex) {}
|
||||
}
|
||||
|
||||
if (currentUIVersion < 26) {
|
||||
// Refactor urlbar suggestion preferences to make it extendable and
|
||||
// allow new suggestion types (e.g: search suggestions).
|
||||
let types = ["history", "bookmark", "openpage"];
|
||||
let defaultBehavior = 0;
|
||||
try {
|
||||
defaultBehavior = Services.prefs.getIntPref("browser.urlbar.default.behavior");
|
||||
} catch (ex) {}
|
||||
try {
|
||||
let autocompleteEnabled = Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled");
|
||||
if (!autocompleteEnabled) {
|
||||
defaultBehavior = -1;
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
||||
// If the default behavior is:
|
||||
// -1 - all new "...suggest.*" preferences will be false
|
||||
// 0 - all new "...suggest.*" preferences will use the default values
|
||||
// > 0 - all new "...suggest.*" preferences will be inherited
|
||||
for (let type of types) {
|
||||
let prefValue = defaultBehavior == 0;
|
||||
if (defaultBehavior > 0) {
|
||||
prefValue = !!(defaultBehavior & Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
|
||||
}
|
||||
Services.prefs.setBoolPref("browser.urlbar.suggest." + type, prefValue);
|
||||
}
|
||||
|
||||
// Typed behavior will be used only for results from history.
|
||||
if (defaultBehavior != -1 &&
|
||||
!!(defaultBehavior & Ci.mozIPlacesAutoComplete["BEHAVIOR_TYPED"])) {
|
||||
Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
@ -0,0 +1,150 @@
|
||||
/* 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/. */
|
||||
|
||||
const UI_VERSION = 26;
|
||||
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
|
||||
const TOPICDATA_BROWSERGLUE_TEST = "force-ui-migration";
|
||||
const DEFAULT_BEHAVIOR_PREF = "browser.urlbar.default.behavior";
|
||||
const AUTOCOMPLETE_PREF = "browser.urlbar.autocomplete.enabled";
|
||||
|
||||
let gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
|
||||
.getService(Ci.nsIObserver);
|
||||
let gGetBoolPref = Services.prefs.getBoolPref;
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
function cleanup() {
|
||||
let prefix = "browser.urlbar.suggest.";
|
||||
for (let type of ["history", "bookmark", "openpage", "history.onlyTyped"]) {
|
||||
Services.prefs.clearUserPref(prefix + type);
|
||||
}
|
||||
Services.prefs.clearUserPref("browser.migration.version");
|
||||
Services.prefs.clearUserPref(AUTOCOMPLETE_PREF);
|
||||
};
|
||||
|
||||
function setupBehaviorAndMigrate(aDefaultBehavior, aAutocompleteEnabled = true) {
|
||||
cleanup();
|
||||
// Migrate browser.urlbar.default.behavior preference.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION - 1);
|
||||
Services.prefs.setIntPref(DEFAULT_BEHAVIOR_PREF, aDefaultBehavior);
|
||||
Services.prefs.setBoolPref(AUTOCOMPLETE_PREF, aAutocompleteEnabled);
|
||||
// Simulate a migration.
|
||||
gBrowserGlue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_BROWSERGLUE_TEST);
|
||||
};
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 0");
|
||||
setupBehaviorAndMigrate(0);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
|
||||
"Bookmark preference should be true.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
|
||||
"Openpage preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false.");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 1");
|
||||
setupBehaviorAndMigrate(1);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
|
||||
"Bookmark preference should be false.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 2");
|
||||
setupBehaviorAndMigrate(2);
|
||||
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
|
||||
"History preference should be false.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
|
||||
"Bookmark preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 3");
|
||||
setupBehaviorAndMigrate(3);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
|
||||
"Bookmark preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 19");
|
||||
setupBehaviorAndMigrate(19);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
|
||||
"Bookmark preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 33");
|
||||
setupBehaviorAndMigrate(33);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
|
||||
"Bookmark preference should be false.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"),
|
||||
"Typed preference should be true");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 129");
|
||||
setupBehaviorAndMigrate(129);
|
||||
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
|
||||
"History preference should be true.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
|
||||
"Bookmark preference should be false.");
|
||||
Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
|
||||
"Openpage preference should be true");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
do_log_info("Migrate default.behavior = 0, autocomplete.enabled = false");
|
||||
setupBehaviorAndMigrate(0, false);
|
||||
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
|
||||
"History preference should be false.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
|
||||
"Bookmark preference should be false.");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
|
||||
"Openpage preference should be false");
|
||||
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
|
||||
"Typed preference should be false");
|
||||
});
|
@ -19,6 +19,7 @@ support-files =
|
||||
[test_browserGlue_prefs.js]
|
||||
[test_browserGlue_restore.js]
|
||||
[test_browserGlue_smartBookmarks.js]
|
||||
[test_browserGlue_urlbar_defaultbehavior_migration.js]
|
||||
[test_clearHistory_shutdown.js]
|
||||
[test_leftpane_corruption_handling.js]
|
||||
[test_PUIU_makeTransaction.js]
|
||||
|
@ -32,6 +32,25 @@ var gPrivacyPane = {
|
||||
},
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize autocomplete to ensure prefs are in sync.
|
||||
*/
|
||||
_initAutocomplete: function () {
|
||||
let unifiedCompletePref = false;
|
||||
try {
|
||||
unifiedCompletePref =
|
||||
Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
} catch (ex) {}
|
||||
|
||||
if (unifiedCompletePref) {
|
||||
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
|
||||
.getService(Components.interfaces.mozIPlacesAutoComplete);
|
||||
} else {
|
||||
Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
|
||||
.getService(Components.interfaces.mozIPlacesAutoComplete);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up the UI for the number of days of history to keep, and updates the
|
||||
* label of the "Clear Now..." button.
|
||||
@ -52,11 +71,8 @@ var gPrivacyPane = {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
this._initTrackingProtection();
|
||||
#endif
|
||||
this._initAutocomplete();
|
||||
|
||||
setEventListener("browser.urlbar.default.behavior", "change",
|
||||
document.getElementById('browser.urlbar.autocomplete.enabled')
|
||||
.updateElements
|
||||
);
|
||||
setEventListener("privacy.sanitize.sanitizeOnShutdown", "change",
|
||||
gPrivacyPane._updateSanitizeSettingsButton);
|
||||
setEventListener("browser.privatebrowsing.autostart", "change",
|
||||
@ -203,13 +219,8 @@ var gPrivacyPane = {
|
||||
// select the remember forms history option
|
||||
document.getElementById("browser.formfill.enable").value = true;
|
||||
|
||||
#ifdef RELEASE_BUILD
|
||||
// select the allow cookies option
|
||||
document.getElementById("network.cookie.cookieBehavior").value = 0;
|
||||
#else
|
||||
// select the limit cookies option
|
||||
document.getElementById("network.cookie.cookieBehavior").value = 3;
|
||||
#endif
|
||||
// select the cookie lifetime policy option
|
||||
document.getElementById("network.cookie.lifetimePolicy").value = 0;
|
||||
|
||||
@ -333,40 +344,16 @@ var gPrivacyPane = {
|
||||
// HISTORY
|
||||
|
||||
/**
|
||||
* Read the location bar enabled and suggestion prefs
|
||||
* @return Int value for suggestion menulist
|
||||
* Update browser.urlbar.autocomplete.enabled when a
|
||||
* browser.urlbar.suggest.* pref is changed from the ui.
|
||||
*/
|
||||
readSuggestionPref: function PPP_readSuggestionPref()
|
||||
{
|
||||
let getVal = function(aPref)
|
||||
document.getElementById("browser.urlbar." + aPref).value;
|
||||
|
||||
// Suggest nothing if autocomplete is not enabled
|
||||
if (!getVal("autocomplete.enabled"))
|
||||
return -1;
|
||||
|
||||
// Bottom 2 bits of default.behavior specify history/bookmark
|
||||
return getVal("default.behavior") & 3;
|
||||
},
|
||||
|
||||
/**
|
||||
* Write the location bar enabled and suggestion prefs when necessary
|
||||
* @return Bool value for enabled pref
|
||||
*/
|
||||
writeSuggestionPref: function PPP_writeSuggestionPref()
|
||||
{
|
||||
let menuVal = document.getElementById("locationBarSuggestion").value;
|
||||
let enabled = menuVal != -1;
|
||||
|
||||
// Only update default.behavior if we're giving suggestions
|
||||
if (enabled) {
|
||||
// Put the selected menu item's value directly into the bottom 2 bits
|
||||
let behavior = document.getElementById("browser.urlbar.default.behavior");
|
||||
behavior.value = behavior.value >> 2 << 2 | menuVal;
|
||||
writeSuggestionPref: function () {
|
||||
let getVal = (aPref) => {
|
||||
return document.getElementById("browser.urlbar.suggest." + aPref).value;
|
||||
}
|
||||
|
||||
// Always update the enabled pref
|
||||
return enabled;
|
||||
// autocomplete.enabled is true if any of the suggestions is true
|
||||
let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v);
|
||||
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
|
||||
},
|
||||
|
||||
/*
|
||||
@ -429,19 +416,11 @@ var gPrivacyPane = {
|
||||
var accept = document.getElementById("acceptCookies");
|
||||
var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");
|
||||
|
||||
#ifdef RELEASE_BUILD
|
||||
// if we're enabling cookies, automatically select 'accept third party always'
|
||||
if (accept.checked)
|
||||
acceptThirdPartyMenu.selectedIndex = 0;
|
||||
|
||||
return accept.checked ? 0 : 2;
|
||||
#else
|
||||
// if we're enabling cookies, automatically select 'accept third party from visited'
|
||||
if (accept.checked)
|
||||
acceptThirdPartyMenu.selectedIndex = 1;
|
||||
|
||||
return accept.checked ? 3 : 2;
|
||||
#endif
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -29,9 +29,15 @@
|
||||
<preference id="browser.urlbar.autocomplete.enabled"
|
||||
name="browser.urlbar.autocomplete.enabled"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.default.behavior"
|
||||
name="browser.urlbar.default.behavior"
|
||||
type="int"/>
|
||||
<preference id="browser.urlbar.suggest.bookmark"
|
||||
name="browser.urlbar.suggest.bookmark"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.suggest.history"
|
||||
name="browser.urlbar.suggest.history"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.suggest.openpage"
|
||||
name="browser.urlbar.suggest.openpage"
|
||||
type="bool"/>
|
||||
|
||||
<!-- History -->
|
||||
<preference id="places.history.enabled"
|
||||
@ -231,23 +237,21 @@
|
||||
</groupbox>
|
||||
|
||||
<!-- Location Bar -->
|
||||
<groupbox id="locationBarGroup" data-category="panePrivacy" hidden="true">
|
||||
<groupbox id="locationBarGroup"
|
||||
data-category="panePrivacy"
|
||||
hidden="true">
|
||||
<caption><label>&locationBar.label;</label></caption>
|
||||
<hbox align="center">
|
||||
<label id="locationBarSuggestionLabel"
|
||||
control="locationBarSuggestion"
|
||||
accesskey="&locbar.pre.accessKey;">&locbar.pre.label;</label>
|
||||
<menulist id="locationBarSuggestion"
|
||||
preference="browser.urlbar.autocomplete.enabled"
|
||||
onsyncfrompreference="return gPrivacyPane.readSuggestionPref();"
|
||||
onsynctopreference="return gPrivacyPane.writeSuggestionPref();">
|
||||
<menupopup>
|
||||
<menuitem label="&locbar.both.label;" value="0"/>
|
||||
<menuitem label="&locbar.history.label;" value="1"/>
|
||||
<menuitem label="&locbar.bookmarks.label;" value="2"/>
|
||||
<menuitem label="&locbar.nothing.label;" value="-1"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<label>&locbar.post.label;</label>
|
||||
</hbox>
|
||||
<label id="locationBarSuggestionLabel">&locbar.pre.label;</label>
|
||||
<checkbox id="historySuggestion" label="&locbar.history.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.history.accesskey;"
|
||||
preference="browser.urlbar.suggest.history"/>
|
||||
<checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.bookmarks.accesskey;"
|
||||
preference="browser.urlbar.suggest.bookmark"/>
|
||||
<checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.openpage.accesskey;"
|
||||
preference="browser.urlbar.suggest.openpage"/>
|
||||
</groupbox>
|
||||
|
@ -17,14 +17,10 @@ function test() {
|
||||
test_custom_retention("acceptCookies", "remember"),
|
||||
test_custom_retention("acceptCookies", "custom")
|
||||
],
|
||||
(runtime.isReleaseBuild ? [
|
||||
[
|
||||
test_custom_retention("acceptThirdPartyMenu", "remember", "visited"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "always")
|
||||
]
|
||||
: [
|
||||
test_custom_retention("acceptThirdPartyMenu", "remember", "always"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "visited")
|
||||
]), [
|
||||
], [
|
||||
test_custom_retention("keepCookiesUntil", "remember", 1),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 2),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 0),
|
||||
|
@ -13,13 +13,13 @@ function test() {
|
||||
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
|
||||
|
||||
run_test_subset([
|
||||
test_locbar_suggestion_retention(-1, undefined),
|
||||
test_locbar_suggestion_retention(1, -1),
|
||||
test_locbar_suggestion_retention(2, 1),
|
||||
test_locbar_suggestion_retention(0, 2),
|
||||
test_locbar_suggestion_retention(0, 0),
|
||||
test_locbar_suggestion_retention("history", true),
|
||||
test_locbar_suggestion_retention("bookmark", true),
|
||||
test_locbar_suggestion_retention("openpage", false),
|
||||
test_locbar_suggestion_retention("history", true),
|
||||
test_locbar_suggestion_retention("history", false),
|
||||
|
||||
// reset all preferences to their default values once we're done
|
||||
reset_preferences
|
||||
]);
|
||||
}
|
||||
}
|
@ -301,18 +301,14 @@ function test_custom_retention(controlToChange, expect, valueIncrement) {
|
||||
};
|
||||
}
|
||||
|
||||
function test_locbar_suggestion_retention(mode, expect) {
|
||||
function test_locbar_suggestion_retention(suggestion, autocomplete) {
|
||||
return function(win) {
|
||||
let locbarsuggest = win.document.getElementById("locationBarSuggestion");
|
||||
ok(locbarsuggest, "location bar suggestion menulist should exist");
|
||||
let elem = win.document.getElementById(suggestion + "Suggestion");
|
||||
ok(elem, "Suggest " + suggestion + " checkbox should exist.");
|
||||
elem.click();
|
||||
|
||||
if (expect !== undefined) {
|
||||
is(locbarsuggest.value, expect,
|
||||
"location bar suggestion is expected to remain " + expect);
|
||||
}
|
||||
|
||||
locbarsuggest.value = mode;
|
||||
controlChanged(locbarsuggest);
|
||||
is(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), autocomplete,
|
||||
"browser.urlbar.autocomplete.enabled pref should be " + autocomplete);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,25 @@ var gPrivacyPane = {
|
||||
},
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize autocomplete to ensure prefs are in sync.
|
||||
*/
|
||||
_initAutocomplete: function () {
|
||||
let unifiedCompletePref = false;
|
||||
try {
|
||||
unifiedCompletePref =
|
||||
Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
} catch (ex) {}
|
||||
|
||||
if (unifiedCompletePref) {
|
||||
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
|
||||
.getService(Components.interfaces.mozIPlacesAutoComplete);
|
||||
} else {
|
||||
Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
|
||||
.getService(Components.interfaces.mozIPlacesAutoComplete);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up the UI for the number of days of history to keep, and updates the
|
||||
* label of the "Clear Now..." button.
|
||||
@ -49,6 +68,7 @@ var gPrivacyPane = {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
this._initTrackingProtection();
|
||||
#endif
|
||||
this._initAutocomplete();
|
||||
},
|
||||
|
||||
// HISTORY MODE
|
||||
@ -165,13 +185,8 @@ var gPrivacyPane = {
|
||||
// select the remember forms history option
|
||||
document.getElementById("browser.formfill.enable").value = true;
|
||||
|
||||
#ifdef RELEASE_BUILD
|
||||
// select the accept cookies option
|
||||
document.getElementById("network.cookie.cookieBehavior").value = 0;
|
||||
#else
|
||||
// select the limit cookies option
|
||||
document.getElementById("network.cookie.cookieBehavior").value = 3;
|
||||
#endif
|
||||
// select the cookie lifetime policy option
|
||||
document.getElementById("network.cookie.lifetimePolicy").value = 0;
|
||||
|
||||
@ -296,40 +311,16 @@ var gPrivacyPane = {
|
||||
// HISTORY
|
||||
|
||||
/**
|
||||
* Read the location bar enabled and suggestion prefs
|
||||
* @return Int value for suggestion menulist
|
||||
* Update browser.urlbar.autocomplete.enabled when a
|
||||
* browser.urlbar.suggest.* pref is changed from the ui.
|
||||
*/
|
||||
readSuggestionPref: function PPP_readSuggestionPref()
|
||||
{
|
||||
let getVal = function(aPref)
|
||||
document.getElementById("browser.urlbar." + aPref).value;
|
||||
|
||||
// Suggest nothing if autocomplete is not enabled
|
||||
if (!getVal("autocomplete.enabled"))
|
||||
return -1;
|
||||
|
||||
// Bottom 2 bits of default.behavior specify history/bookmark
|
||||
return getVal("default.behavior") & 3;
|
||||
},
|
||||
|
||||
/**
|
||||
* Write the location bar enabled and suggestion prefs when necessary
|
||||
* @return Bool value for enabled pref
|
||||
*/
|
||||
writeSuggestionPref: function PPP_writeSuggestionPref()
|
||||
{
|
||||
let menuVal = document.getElementById("locationBarSuggestion").value;
|
||||
let enabled = menuVal != -1;
|
||||
|
||||
// Only update default.behavior if we're giving suggestions
|
||||
if (enabled) {
|
||||
// Put the selected menu item's value directly into the bottom 2 bits
|
||||
let behavior = document.getElementById("browser.urlbar.default.behavior");
|
||||
behavior.value = behavior.value >> 2 << 2 | menuVal;
|
||||
writeSuggestionPref: function PPP_writeSuggestionPref() {
|
||||
let getVal = (aPref) => {
|
||||
return document.getElementById("browser.urlbar.suggest." + aPref).value;
|
||||
}
|
||||
|
||||
// Always update the enabled pref
|
||||
return enabled;
|
||||
// autocomplete.enabled is true if any of the suggestions is true
|
||||
let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v);
|
||||
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
|
||||
},
|
||||
|
||||
/*
|
||||
@ -392,19 +383,11 @@ var gPrivacyPane = {
|
||||
var accept = document.getElementById("acceptCookies");
|
||||
var acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu");
|
||||
|
||||
#ifdef RELEASE_BUILD
|
||||
// if we're enabling cookies, automatically select 'accept third party always'
|
||||
if (accept.checked)
|
||||
acceptThirdPartyMenu.selectedIndex = 0;
|
||||
|
||||
return accept.checked ? 0 : 2;
|
||||
#else
|
||||
// if we're enabling cookies, automatically select 'accept third party from visited'
|
||||
if (accept.checked)
|
||||
acceptThirdPartyMenu.selectedIndex = 1;
|
||||
|
||||
return accept.checked ? 3 : 2;
|
||||
#endif
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -42,10 +42,15 @@
|
||||
<preference id="browser.urlbar.autocomplete.enabled"
|
||||
name="browser.urlbar.autocomplete.enabled"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.default.behavior"
|
||||
name="browser.urlbar.default.behavior"
|
||||
type="int"
|
||||
onchange="document.getElementById('browser.urlbar.autocomplete.enabled').updateElements();"/>
|
||||
<preference id="browser.urlbar.suggest.bookmark"
|
||||
name="browser.urlbar.suggest.bookmark"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.suggest.history"
|
||||
name="browser.urlbar.suggest.history"
|
||||
type="bool"/>
|
||||
<preference id="browser.urlbar.suggest.openpage"
|
||||
name="browser.urlbar.suggest.openpage"
|
||||
type="bool"/>
|
||||
|
||||
<!-- History -->
|
||||
<preference id="places.history.enabled"
|
||||
@ -254,23 +259,22 @@
|
||||
<groupbox id="locationBarGroup">
|
||||
<caption label="&locationBar.label;"/>
|
||||
|
||||
<hbox align="center">
|
||||
<label id="locationBarSuggestionLabel"
|
||||
control="locationBarSuggestion"
|
||||
accesskey="&locbar.pre.accessKey;">&locbar.pre.label;</label>
|
||||
<menulist id="locationBarSuggestion"
|
||||
preference="browser.urlbar.autocomplete.enabled"
|
||||
onsyncfrompreference="return gPrivacyPane.readSuggestionPref();"
|
||||
onsynctopreference="return gPrivacyPane.writeSuggestionPref();">
|
||||
<menupopup>
|
||||
<menuitem label="&locbar.both.label;" value="0"/>
|
||||
<menuitem label="&locbar.history.label;" value="1"/>
|
||||
<menuitem label="&locbar.bookmarks.label;" value="2"/>
|
||||
<menuitem label="&locbar.nothing.label;" value="-1"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<label>&locbar.post.label;</label>
|
||||
</hbox>
|
||||
<label id="locationBarSuggestionLabel">&locbar.pre.label;</label>
|
||||
|
||||
<vbox id="tabPrefsBox" align="start" flex="1">
|
||||
<checkbox id="historySuggestion" label="&locbar.history.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.history.accesskey;"
|
||||
preference="browser.urlbar.suggest.history"/>
|
||||
<checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.bookmarks.accesskey;"
|
||||
preference="browser.urlbar.suggest.bookmark"/>
|
||||
<checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
|
||||
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
|
||||
accesskey="&locbar.openpage.accesskey;"
|
||||
preference="browser.urlbar.suggest.openpage"/>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
</prefpane>
|
||||
|
@ -19,5 +19,4 @@ skip-if = !healthreport || (os == 'linux' && debug)
|
||||
[browser_privacypane_4.js]
|
||||
skip-if = e10s # leaks windows
|
||||
[browser_privacypane_5.js]
|
||||
skip-if = e10s # leaks windows
|
||||
[browser_privacypane_8.js]
|
||||
|
@ -18,14 +18,10 @@ function test() {
|
||||
test_custom_retention("acceptCookies", "remember"),
|
||||
test_custom_retention("acceptCookies", "custom")
|
||||
],
|
||||
(runtime.isReleaseBuild ? [
|
||||
[
|
||||
test_custom_retention("acceptThirdPartyMenu", "remember", "visited"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "always")
|
||||
]
|
||||
: [
|
||||
test_custom_retention("acceptThirdPartyMenu", "remember", "always"),
|
||||
test_custom_retention("acceptThirdPartyMenu", "custom", "visited")
|
||||
]), [
|
||||
], [
|
||||
test_custom_retention("keepCookiesUntil", "remember", 1),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 2),
|
||||
test_custom_retention("keepCookiesUntil", "custom", 0),
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* 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() {
|
||||
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
@ -14,13 +13,13 @@ function test() {
|
||||
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
|
||||
|
||||
run_test_subset([
|
||||
test_locbar_suggestion_retention(-1, undefined),
|
||||
test_locbar_suggestion_retention(1, -1),
|
||||
test_locbar_suggestion_retention(2, 1),
|
||||
test_locbar_suggestion_retention(0, 2),
|
||||
test_locbar_suggestion_retention(0, 0),
|
||||
test_locbar_suggestion_retention("history", true),
|
||||
test_locbar_suggestion_retention("bookmark", true),
|
||||
test_locbar_suggestion_retention("openpage", false),
|
||||
test_locbar_suggestion_retention("history", true),
|
||||
test_locbar_suggestion_retention("history", false),
|
||||
|
||||
// reset all preferences to their default values once we're done
|
||||
reset_preferences
|
||||
]);
|
||||
}
|
||||
}
|
@ -310,18 +310,14 @@ function test_custom_retention(controlToChange, expect, valueIncrement) {
|
||||
};
|
||||
}
|
||||
|
||||
function test_locbar_suggestion_retention(mode, expect) {
|
||||
function test_locbar_suggestion_retention(suggestion, autocomplete) {
|
||||
return function(win) {
|
||||
let locbarsuggest = win.document.getElementById("locationBarSuggestion");
|
||||
ok(locbarsuggest, "location bar suggestion menulist should exist");
|
||||
let elem = win.document.getElementById(suggestion + "Suggestion");
|
||||
ok(elem, "Suggest " + suggestion + " checkbox should exist.");
|
||||
elem.click();
|
||||
|
||||
if (expect !== undefined) {
|
||||
is(locbarsuggest.value, expect,
|
||||
"location bar suggestion is expected to remain " + expect);
|
||||
}
|
||||
|
||||
locbarsuggest.value = mode;
|
||||
controlChanged(locbarsuggest);
|
||||
is(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), autocomplete,
|
||||
"browser.urlbar.autocomplete.enabled pref should be " + autocomplete);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,58 @@
|
||||
/* 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/. */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://browser/locale/aboutPrivateBrowsing.properties");
|
||||
|
||||
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
document.title = stringBundle.GetStringFromName("title.normal");
|
||||
setFavIcon("chrome://global/skin/icons/question-16.png");
|
||||
} else {
|
||||
document.title = stringBundle.GetStringFromName("title");
|
||||
setFavIcon("chrome://browser/skin/Privacy-16.png");
|
||||
}
|
||||
|
||||
var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
|
||||
function setFavIcon(url) {
|
||||
var icon = document.createElement("link");
|
||||
icon.setAttribute("rel", "icon");
|
||||
icon.setAttribute("type", "image/png");
|
||||
icon.setAttribute("href", url);
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
head.insertBefore(icon, head.firstChild);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
document.body.setAttribute("class", "normal");
|
||||
}
|
||||
|
||||
// Set up the help link
|
||||
let learnMoreURL = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
|
||||
.getService(Ci.nsIURLFormatter)
|
||||
.formatURLPref("app.support.baseURL");
|
||||
let learnMore = document.getElementById("learnMore");
|
||||
if (learnMore) {
|
||||
learnMore.setAttribute("href", learnMoreURL + "private-browsing");
|
||||
}
|
||||
|
||||
let startPrivateBrowsing = document.getElementById("startPrivateBrowsing");
|
||||
if (startPrivateBrowsing) {
|
||||
startPrivateBrowsing.addEventListener("command", openPrivateWindow);
|
||||
}
|
||||
}, false);
|
||||
|
||||
function openPrivateWindow() {
|
||||
mainWindow.OpenBrowserWindow({private: true});
|
||||
}
|
@ -20,56 +20,7 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<link rel="stylesheet" href="chrome://browser/content/aboutPrivateBrowsing.css" type="text/css" media="all"/>
|
||||
<script type="application/javascript;version=1.7"><![CDATA[
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
document.title = "]]>&aboutPrivateBrowsing.title.normal;<![CDATA[";
|
||||
setFavIcon("chrome://global/skin/icons/question-16.png");
|
||||
} else {
|
||||
#ifndef XP_MACOSX
|
||||
document.title = "]]>&aboutPrivateBrowsing.title;<![CDATA[";
|
||||
#endif
|
||||
setFavIcon("chrome://browser/skin/Privacy-16.png");
|
||||
}
|
||||
|
||||
var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
|
||||
function setFavIcon(url) {
|
||||
var icon = document.createElement("link");
|
||||
icon.setAttribute("rel", "icon");
|
||||
icon.setAttribute("type", "image/png");
|
||||
icon.setAttribute("href", url);
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
head.insertBefore(icon, head.firstChild);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
document.body.setAttribute("class", "normal");
|
||||
}
|
||||
|
||||
// Set up the help link
|
||||
let learnMoreURL = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
|
||||
.getService(Ci.nsIURLFormatter)
|
||||
.formatURLPref("app.support.baseURL");
|
||||
let learnMore = document.getElementById("learnMore");
|
||||
if (learnMore) {
|
||||
learnMore.setAttribute("href", learnMoreURL + "private-browsing");
|
||||
}
|
||||
}, false);
|
||||
|
||||
function openPrivateWindow() {
|
||||
mainWindow.OpenBrowserWindow({private: true});
|
||||
}
|
||||
]]></script>
|
||||
<script type="application/javascript;version=1.7" src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;" class="private">
|
||||
@ -85,10 +36,10 @@
|
||||
<p class="notPrivateText showNormal">&aboutPrivateBrowsing.notPrivate;</p>
|
||||
|
||||
<button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="startPrivateBrowsing"
|
||||
class="openPrivate showNormal"
|
||||
label="&privatebrowsingpage.openPrivateWindow.label;"
|
||||
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"
|
||||
oncommand="openPrivateWindow();"/>
|
||||
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"/>
|
||||
<div class="showPrivate">
|
||||
<p class="moreInfoText">&aboutPrivateBrowsing.moreInfo;</p>
|
||||
<p><a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a></p>
|
||||
|
@ -4,4 +4,5 @@
|
||||
|
||||
browser.jar:
|
||||
* content/browser/aboutPrivateBrowsing.css (content/aboutPrivateBrowsing.css)
|
||||
* content/browser/aboutPrivateBrowsing.xhtml (content/aboutPrivateBrowsing.xhtml)
|
||||
content/browser/aboutPrivateBrowsing.xhtml (content/aboutPrivateBrowsing.xhtml)
|
||||
* content/browser/aboutPrivateBrowsing.js (content/aboutPrivateBrowsing.js)
|
@ -27,7 +27,7 @@ function test() {
|
||||
about_pb_title = "Open a private window?";
|
||||
pb_page_with_title = test_title + " - (Private Browsing)";
|
||||
pb_page_without_title = app_name + " - (Private Browsing)";
|
||||
pb_about_pb_title = pb_page_without_title;
|
||||
pb_about_pb_title = "You're browsing privately - (Private Browsing)";
|
||||
}
|
||||
else {
|
||||
page_with_title = test_title + " - " + app_name;
|
||||
|
@ -539,12 +539,12 @@ StyleEditorUI.prototype = {
|
||||
editor.removeAllUnusedRegions();
|
||||
|
||||
if (data.reports.length > 0) {
|
||||
// So there is some coverage markup, but can we apply it?
|
||||
let text = editor.sourceEditor.getText() + "\r";
|
||||
// If the CSS text contains a '}' with some non-whitespace
|
||||
// after then we assume this is compressed CSS and stop
|
||||
// marking-up.
|
||||
if (!/}\s*\S+\s*\r/.test(text)) {
|
||||
// Only apply if this file isn't compressed. We detect a
|
||||
// compressed file if there are more rules than lines.
|
||||
let text = editor.sourceEditor.getText();
|
||||
let lineCount = text.split("\n").length;
|
||||
let ruleCount = editor.styleSheet.ruleCount;
|
||||
if (lineCount >= ruleCount) {
|
||||
editor.addUnusedRegions(data.reports);
|
||||
}
|
||||
else {
|
||||
|
@ -0,0 +1,2 @@
|
||||
title=You're browsing privately
|
||||
title.normal=Open a private window?
|
@ -16,12 +16,12 @@
|
||||
<!ENTITY locationBar.label "Location Bar">
|
||||
|
||||
<!ENTITY locbar.pre.label "When using the location bar, suggest:">
|
||||
<!ENTITY locbar.pre.accessKey "u">
|
||||
<!ENTITY locbar.post.label "">
|
||||
<!ENTITY locbar.both.label "History and Bookmarks">
|
||||
<!ENTITY locbar.history.label "History">
|
||||
<!ENTITY locbar.history.accesskey "i">
|
||||
<!ENTITY locbar.bookmarks.label "Bookmarks">
|
||||
<!ENTITY locbar.nothing.label "Nothing">
|
||||
<!ENTITY locbar.bookmarks.accesskey "d">
|
||||
<!ENTITY locbar.openpage.label "Open tabs">
|
||||
<!ENTITY locbar.openpage.accesskey "g">
|
||||
|
||||
<!ENTITY acceptCookies.label "Accept cookies from sites">
|
||||
<!ENTITY acceptCookies.accesskey "A">
|
||||
|
@ -10,6 +10,7 @@
|
||||
locale/browser/aboutCertError.dtd (%chrome/browser/aboutCertError.dtd)
|
||||
locale/browser/aboutDialog.dtd (%chrome/browser/aboutDialog.dtd)
|
||||
locale/browser/aboutPrivateBrowsing.dtd (%chrome/browser/aboutPrivateBrowsing.dtd)
|
||||
locale/browser/aboutPrivateBrowsing.properties (%chrome/browser/aboutPrivateBrowsing.properties)
|
||||
locale/browser/aboutRobots.dtd (%chrome/browser/aboutRobots.dtd)
|
||||
locale/browser/aboutHome.dtd (%chrome/browser/aboutHome.dtd)
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
|
@ -305,14 +305,21 @@ pref("browser.search.official", true);
|
||||
// enable xul error pages
|
||||
pref("browser.xul.error_pages.enabled", true);
|
||||
|
||||
// Specify emptyRestriction = 0 so that bookmarks appear in the list by default
|
||||
pref("browser.urlbar.default.behavior", 0);
|
||||
pref("browser.urlbar.default.behavior.emptyRestriction", 0);
|
||||
|
||||
// Let the faviconservice know that we display favicons as 25x25px so that it
|
||||
// uses the right size when optimizing favicons
|
||||
pref("places.favicons.optimizeToDimension", 25);
|
||||
|
||||
// The default behavior for the urlbar can be configured to use any combination
|
||||
// of the match filters with each additional filter adding more results (union).
|
||||
pref("browser.urlbar.suggest.history", true);
|
||||
pref("browser.urlbar.suggest.bookmark", true);
|
||||
pref("browser.urlbar.suggest.openpage", true);
|
||||
pref("browser.urlbar.suggest.search", true);
|
||||
|
||||
// Restrictions to current suggestions can also be applied (intersection).
|
||||
// Typed suggestion works only if history is set to true.
|
||||
pref("browser.urlbar.suggest.history.onlyTyped", false);
|
||||
|
||||
// various and sundry awesomebar prefs (should remove/re-evaluate
|
||||
// these once bug 447900 is fixed)
|
||||
pref("browser.urlbar.trimURLs", true);
|
||||
|
@ -300,6 +300,12 @@ HiddenBrowser.prototype = {
|
||||
this._applySize();
|
||||
doc.getElementById("win").appendChild(this._browser);
|
||||
|
||||
// The browser might not have a docShell here if the HostFrame was
|
||||
// destroyed while the promise was resolved. Simply bail out.
|
||||
if (!this._browser.docShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the docShell be inactive so that document.hidden=true.
|
||||
this._browser.docShell.isActive = false;
|
||||
|
||||
|
@ -14768,6 +14768,25 @@ class CGEventMethod(CGNativeMember):
|
||||
target += ".SetValue()"
|
||||
source += ".Value()"
|
||||
members += sequenceCopy % (target, source)
|
||||
elif m.type.isSpiderMonkeyInterface():
|
||||
srcname = "%s.%s" % (self.args[1].name, name)
|
||||
if m.type.nullable():
|
||||
members += fill(
|
||||
"""
|
||||
if (${srcname}.IsNull()) {
|
||||
e->${varname} = nullptr;
|
||||
} else {
|
||||
e->${varname} = ${srcname}.Value().Obj();
|
||||
}
|
||||
""",
|
||||
varname=name,
|
||||
srcname=srcname);
|
||||
else:
|
||||
members += fill(
|
||||
"""
|
||||
e->${varname}.set(${srcname}.Obj());
|
||||
""",
|
||||
varname=name, srcname=srcname);
|
||||
else:
|
||||
members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
|
||||
if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
|
||||
|
@ -1357,6 +1357,25 @@ CanvasRenderingContext2D::ClearTarget()
|
||||
state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
|
||||
state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
|
||||
state->shadowColor = NS_RGBA(0,0,0,0);
|
||||
|
||||
// For vertical writing-mode, unless text-orientation is sideways,
|
||||
// we'll modify the initial value of textBaseline to 'middle'.
|
||||
nsRefPtr<nsStyleContext> canvasStyle;
|
||||
if (mCanvasElement && mCanvasElement->IsInDoc()) {
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
if (presShell) {
|
||||
canvasStyle =
|
||||
nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
|
||||
nullptr,
|
||||
presShell);
|
||||
if (canvasStyle) {
|
||||
WritingMode wm(canvasStyle);
|
||||
if (wm.IsVertical() && !wm.IsSideways()) {
|
||||
state->textBaseline = TextBaseline::MIDDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -3149,6 +3168,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
|
||||
gfxPoint point = mPt;
|
||||
bool rtl = mTextRun->IsRightToLeft();
|
||||
bool verticalRun = mTextRun->IsVertical();
|
||||
bool centerBaseline = mTextRun->UseCenterBaseline();
|
||||
|
||||
gfxFloat& inlineCoord = verticalRun ? point.y : point.x;
|
||||
inlineCoord += xOffset;
|
||||
@ -3217,20 +3237,27 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
|
||||
if (runs[c].mOrientation ==
|
||||
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT) {
|
||||
sidewaysRestore.Init(mCtx->mTarget);
|
||||
// TODO: The baseline adjustment here is kinda ad-hoc; eventually
|
||||
// perhaps we should check for horizontal and vertical baseline data
|
||||
// in the font, and adjust accordingly.
|
||||
// (The same will be true for HTML text layout.)
|
||||
const gfxFont::Metrics& metrics = mTextRun->GetFontGroup()->
|
||||
GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
|
||||
mCtx->mTarget->SetTransform(mCtx->mTarget->GetTransform().Copy().
|
||||
|
||||
gfx::Matrix mat = mCtx->mTarget->GetTransform().Copy().
|
||||
PreTranslate(baselineOrigin). // translate origin for rotation
|
||||
PreRotate(gfx::Float(M_PI / 2.0)). // turn 90deg clockwise
|
||||
PreTranslate(-baselineOrigin). // undo the translation
|
||||
PreTranslate(Point(0, (metrics.emAscent - metrics.emDescent) / 2)));
|
||||
// and offset the (alphabetic) baseline of the
|
||||
PreTranslate(-baselineOrigin); // undo the translation
|
||||
|
||||
if (centerBaseline) {
|
||||
// TODO: The baseline adjustment here is kinda ad hoc; eventually
|
||||
// perhaps we should check for horizontal and vertical baseline data
|
||||
// in the font, and adjust accordingly.
|
||||
// (The same will be true for HTML text layout.)
|
||||
float offset = (metrics.emAscent - metrics.emDescent) / 2;
|
||||
mat = mat.PreTranslate(Point(0, offset));
|
||||
// offset the (alphabetic) baseline of the
|
||||
// horizontally-shaped text from the (centered)
|
||||
// default baseline used for vertical
|
||||
}
|
||||
|
||||
mCtx->mTarget->SetTransform(mat);
|
||||
}
|
||||
|
||||
RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
|
||||
@ -3522,39 +3549,45 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
|
||||
|
||||
processor.mPt.x -= anchorX * totalWidth;
|
||||
|
||||
// offset pt.y based on text baseline
|
||||
// offset pt.y (or pt.x, for vertical text) based on text baseline
|
||||
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
|
||||
const gfxFont::Metrics& fontMetrics =
|
||||
processor.mFontgrp->GetFirstValidFont()->GetMetrics(
|
||||
((processor.mTextRunFlags & gfxTextRunFactory::TEXT_ORIENT_MASK) ==
|
||||
gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL)
|
||||
? gfxFont::eHorizontal : gfxFont::eVertical);
|
||||
processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
|
||||
|
||||
gfxFloat anchorY;
|
||||
gfxFloat baselineAnchor;
|
||||
|
||||
switch (state.textBaseline)
|
||||
{
|
||||
case TextBaseline::HANGING:
|
||||
// fall through; best we can do with the information available
|
||||
case TextBaseline::TOP:
|
||||
anchorY = fontMetrics.emAscent;
|
||||
baselineAnchor = fontMetrics.emAscent;
|
||||
break;
|
||||
case TextBaseline::MIDDLE:
|
||||
anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
|
||||
baselineAnchor = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
|
||||
break;
|
||||
case TextBaseline::IDEOGRAPHIC:
|
||||
// fall through; best we can do with the information available
|
||||
case TextBaseline::ALPHABETIC:
|
||||
anchorY = 0;
|
||||
baselineAnchor = 0;
|
||||
break;
|
||||
case TextBaseline::BOTTOM:
|
||||
anchorY = -fontMetrics.emDescent;
|
||||
baselineAnchor = -fontMetrics.emDescent;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected TextBaseline");
|
||||
}
|
||||
|
||||
processor.mPt.y += anchorY;
|
||||
if (processor.mTextRun->IsVertical()) {
|
||||
if (processor.mTextRun->UseCenterBaseline()) {
|
||||
// Adjust to account for mTextRun being shaped using center baseline
|
||||
// rather than alphabetic.
|
||||
baselineAnchor -= (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
|
||||
}
|
||||
processor.mPt.x -= baselineAnchor;
|
||||
} else {
|
||||
processor.mPt.y += baselineAnchor;
|
||||
}
|
||||
|
||||
// correct bounding box to get it to be the correct size/position
|
||||
processor.mBoundingBox.width = totalWidth;
|
||||
|
@ -1793,28 +1793,31 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
|
||||
}
|
||||
#endif
|
||||
OutputMediaStream* out = mOutputStreams.AppendElement();
|
||||
#ifdef DEBUG
|
||||
// Estimate hints based on the type of the media element
|
||||
// under the preference media.capturestream_hints for the
|
||||
// debug builds only. This allows WebRTC Peer Connection
|
||||
// to behave appropriately when media streams generated
|
||||
// via mozCaptureStream*() are added to the Peer Connection.
|
||||
// This functionality is planned to be used as part of Audio
|
||||
// Quality Performance testing for WebRTC.
|
||||
// Bug932845: Revisit this once hints mechanism is dealt with
|
||||
// holistically.
|
||||
uint8_t hints = 0;
|
||||
if (Preferences::GetBool("media.capturestream_hints.enabled")) {
|
||||
if (IsVideo() && GetVideoFrameContainer()) {
|
||||
hints = DOMMediaStream::HINT_CONTENTS_VIDEO | DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||
} else {
|
||||
hints = DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||
hints = (mHasAudio? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
|
||||
(mHasVideo? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
// Estimate hints based on the type of the media element
|
||||
// under the preference media.capturestream_hints for the
|
||||
// debug builds only. This allows WebRTC Peer Connection
|
||||
// to behave appropriately when media streams generated
|
||||
// via mozCaptureStream*() are added to the Peer Connection.
|
||||
// This functionality is planned to be used as part of Audio
|
||||
// Quality Performance testing for WebRTC.
|
||||
// Bug932845: Revisit this once hints mechanism is dealt with
|
||||
// holistically.
|
||||
if (Preferences::GetBool("media.capturestream_hints.enabled")) {
|
||||
if (IsVideo() && GetVideoFrameContainer()) {
|
||||
hints = DOMMediaStream::HINT_CONTENTS_VIDEO | DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||
} else {
|
||||
hints = DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
out->mStream = DOMMediaStream::CreateTrackUnionStream(window, hints);
|
||||
#else
|
||||
out->mStream = DOMMediaStream::CreateTrackUnionStream(window);
|
||||
#endif
|
||||
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
|
||||
out->mStream->CombineWithPrincipal(principal);
|
||||
out->mFinishWhenEnded = aFinishWhenEnded;
|
||||
|
@ -8734,16 +8734,15 @@ FileManager::Invalidate()
|
||||
{
|
||||
public:
|
||||
static PLDHashOperator
|
||||
CopyToTArray(const uint64_t& aKey, FileInfo* aValue, void* aUserArg)
|
||||
ClearDBRefs(const uint64_t& aKey, FileInfo*& aValue, void* aUserArg)
|
||||
{
|
||||
MOZ_ASSERT(aValue);
|
||||
|
||||
auto* array = static_cast<FallibleTArray<FileInfo*>*>(aUserArg);
|
||||
MOZ_ASSERT(array);
|
||||
if (aValue->LockedClearDBRefs()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(array->AppendElement(aValue));
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
};
|
||||
|
||||
@ -8752,26 +8751,12 @@ FileManager::Invalidate()
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
FallibleTArray<FileInfo*> fileInfos;
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
mInvalidated = true;
|
||||
MOZ_ASSERT(!mInvalidated);
|
||||
mInvalidated = true;
|
||||
|
||||
if (NS_WARN_IF(!fileInfos.SetCapacity(mFileInfos.Count()))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mFileInfos.EnumerateRead(Helper::CopyToTArray, &fileInfos);
|
||||
}
|
||||
|
||||
for (uint32_t count = fileInfos.Length(), index = 0; index < count; index++) {
|
||||
FileInfo* fileInfo = fileInfos[index];
|
||||
MOZ_ASSERT(fileInfo);
|
||||
|
||||
fileInfo->ClearDBRefs();
|
||||
}
|
||||
mFileInfos.Enumerate(Helper::ClearDBRefs, nullptr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -130,15 +130,13 @@ FileInfo::GetReferences(int32_t* aRefCnt,
|
||||
|
||||
void
|
||||
FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
|
||||
int32_t aDelta,
|
||||
bool aClear)
|
||||
int32_t aDelta)
|
||||
{
|
||||
// XXX This can go away once DOM objects no longer hold FileInfo objects...
|
||||
// Looking at you, IDBMutableFile...
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
MOZ_ASSERT(&aRefCount == &mRefCnt);
|
||||
MOZ_ASSERT(aDelta == 1 || aDelta == -1);
|
||||
MOZ_ASSERT(!aClear);
|
||||
|
||||
if (aDelta > 0) {
|
||||
++aRefCount;
|
||||
@ -158,7 +156,7 @@ FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
aRefCount = aClear ? 0 : aRefCount + aDelta;
|
||||
aRefCount = aRefCount + aDelta;
|
||||
|
||||
if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
|
||||
return;
|
||||
@ -176,6 +174,29 @@ FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool
|
||||
FileInfo::LockedClearDBRefs()
|
||||
{
|
||||
MOZ_ASSERT(!IndexedDatabaseManager::IsClosed());
|
||||
|
||||
IndexedDatabaseManager::FileMutex().AssertCurrentThreadOwns();
|
||||
|
||||
mDBRefCnt = 0;
|
||||
|
||||
if (mRefCnt || mSliceRefCnt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In this case, we are not responsible for removing the file info from the
|
||||
// hashtable. It's up to FileManager which is the only caller of this method.
|
||||
|
||||
MOZ_ASSERT(mFileManager->Invalidated());
|
||||
|
||||
delete this;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
FileInfo::Cleanup()
|
||||
{
|
||||
|
@ -50,12 +50,6 @@ public:
|
||||
UpdateReferences(mDBRefCnt, aDelta);
|
||||
}
|
||||
|
||||
void
|
||||
ClearDBRefs()
|
||||
{
|
||||
UpdateReferences(mDBRefCnt, 0, true);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateSliceRefs(int32_t aDelta)
|
||||
{
|
||||
@ -80,8 +74,10 @@ protected:
|
||||
private:
|
||||
void
|
||||
UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
|
||||
int32_t aDelta,
|
||||
bool aClear = false);
|
||||
int32_t aDelta);
|
||||
|
||||
bool
|
||||
LockedClearDBRefs();
|
||||
|
||||
void
|
||||
Cleanup();
|
||||
|
@ -158,6 +158,10 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
// let InterleaveTrackData downmix pcm data.
|
||||
mChannels = aChannels > MAX_CHANNELS ? MAX_CHANNELS : aChannels;
|
||||
|
||||
// Reject non-audio sample rates.
|
||||
NS_ENSURE_TRUE(aSamplingRate >= 8000, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aSamplingRate <= 192000, NS_ERROR_INVALID_ARG);
|
||||
|
||||
// According to www.opus-codec.org, creating an opus encoder requires the
|
||||
// sampling rate of source signal be one of 8000, 12000, 16000, 24000, or
|
||||
// 48000. If this constraint is not satisfied, we resample the input to 48kHz.
|
||||
|
@ -49,10 +49,10 @@ VorbisTrackEncoder::~VorbisTrackEncoder()
|
||||
nsresult
|
||||
VorbisTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
{
|
||||
if (aChannels <= 0 || aChannels > 8) {
|
||||
VORBISLOG("aChannels <= 0 || aChannels > 8");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
NS_ENSURE_TRUE(aChannels > 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aChannels <= 8, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aSamplingRate >= 8000, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aSamplingRate <= 192000, NS_ERROR_INVALID_ARG);
|
||||
|
||||
// This monitor is used to wake up other methods that are waiting for encoder
|
||||
// to be completely initialized.
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "MP4Reader.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mp4_demuxer/Adts.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "nsIThread.h"
|
||||
@ -33,10 +32,11 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
, mCallback(aCallback)
|
||||
, mConverter(nullptr)
|
||||
, mStream(nullptr)
|
||||
, mCurrentAudioTimestamp(0)
|
||||
, mCurrentAudioTimestamp(-1)
|
||||
, mNextAudioTimestamp(-1)
|
||||
, mSamplePosition(0)
|
||||
, mHaveOutput(false)
|
||||
, mFlushed(false)
|
||||
, mSizeDecoded(0)
|
||||
, mLastError(noErr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleATDecoder);
|
||||
LOG("Creating Apple AudioToolbox decoder");
|
||||
@ -238,8 +238,7 @@ AppleATDecoder::SampleCallback(uint32_t aNumBytes,
|
||||
// Pick a multiple of the frame size close to a power of two
|
||||
// for efficient allocation.
|
||||
const uint32_t MAX_AUDIO_FRAMES = 128;
|
||||
const uint32_t decodedSize = MAX_AUDIO_FRAMES * mConfig.channel_count *
|
||||
sizeof(AudioDataValue);
|
||||
const uint32_t maxDecodedSamples = MAX_AUDIO_FRAMES * mConfig.channel_count;
|
||||
|
||||
// Descriptions for _decompressed_ audio packets. ignored.
|
||||
nsAutoArrayPtr<AudioStreamPacketDescription>
|
||||
@ -251,14 +250,15 @@ AppleATDecoder::SampleCallback(uint32_t aNumBytes,
|
||||
PassthroughUserData userData =
|
||||
{ this, aNumPackets, aNumBytes, aData, aPackets, false };
|
||||
|
||||
do {
|
||||
// Decompressed audio buffer
|
||||
nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
|
||||
// Decompressed audio buffer
|
||||
nsAutoArrayPtr<AudioDataValue> decoded(new AudioDataValue[maxDecodedSamples]);
|
||||
|
||||
do {
|
||||
AudioBufferList decBuffer;
|
||||
decBuffer.mNumberBuffers = 1;
|
||||
decBuffer.mBuffers[0].mNumberChannels = mOutputFormat.mChannelsPerFrame;
|
||||
decBuffer.mBuffers[0].mDataByteSize = decodedSize;
|
||||
decBuffer.mBuffers[0].mDataByteSize =
|
||||
maxDecodedSamples * sizeof(AudioDataValue);
|
||||
decBuffer.mBuffers[0].mData = decoded.get();
|
||||
|
||||
// in: the max number of packets we can handle from the decoder.
|
||||
@ -274,50 +274,28 @@ AppleATDecoder::SampleCallback(uint32_t aNumBytes,
|
||||
|
||||
if (rv && rv != kNeedMoreData) {
|
||||
LOG("Error decoding audio stream: %d\n", rv);
|
||||
mCallback->Error();
|
||||
break;
|
||||
}
|
||||
LOG("%d frames decoded", numFrames);
|
||||
|
||||
// If we decoded zero frames then AudioConverterFillComplexBuffer is out
|
||||
// of data to provide. We drained its internal buffer completely on the
|
||||
// last pass.
|
||||
if (numFrames == 0 && rv == kNeedMoreData) {
|
||||
LOG("FillComplexBuffer out of data exactly\n");
|
||||
mCallback->InputExhausted();
|
||||
mLastError = rv;
|
||||
break;
|
||||
}
|
||||
|
||||
const int rate = mOutputFormat.mSampleRate;
|
||||
const int channels = mOutputFormat.mChannelsPerFrame;
|
||||
|
||||
int64_t time = mCurrentAudioTimestamp;
|
||||
int64_t duration = FramesToUsecs(numFrames, rate).value();
|
||||
|
||||
LOG("pushed audio at time %lfs; duration %lfs\n",
|
||||
(double)time / USECS_PER_S, (double)duration / USECS_PER_S);
|
||||
|
||||
AudioData* audio = new AudioData(mSamplePosition,
|
||||
time, duration, numFrames,
|
||||
reinterpret_cast<AudioDataValue*>(decoded.forget()),
|
||||
channels, rate);
|
||||
mCallback->Output(audio);
|
||||
mHaveOutput = true;
|
||||
mOutputData.AppendElements(decoded.get(),
|
||||
numFrames * mConfig.channel_count);
|
||||
|
||||
if (rv == kNeedMoreData) {
|
||||
// No error; we just need more data.
|
||||
LOG("FillComplexBuffer out of data\n");
|
||||
mCallback->InputExhausted();
|
||||
break;
|
||||
}
|
||||
LOG("%d frames decoded", numFrames);
|
||||
} while (true);
|
||||
|
||||
mSizeDecoded += aNumBytes;
|
||||
}
|
||||
|
||||
void
|
||||
AppleATDecoder::SetupDecoder()
|
||||
{
|
||||
LOG("Setting up Apple AudioToolbox decoder.");
|
||||
mHaveOutput = false;
|
||||
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
nsresult rv = AppleUtils::GetRichestDecodableFormat(mStream, inputFormat);
|
||||
@ -368,23 +346,84 @@ AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Push the sample to the AudioFileStream for parsing.
|
||||
mSamplePosition = aSample->byte_offset;
|
||||
mCurrentAudioTimestamp = aSample->composition_timestamp;
|
||||
uint32_t flags = mFlushed ? kAudioFileStreamParseFlag_Discontinuity : 0;
|
||||
|
||||
const Microseconds fuzz = 5;
|
||||
CheckedInt<Microseconds> upperFuzz = mNextAudioTimestamp + fuzz;
|
||||
CheckedInt<Microseconds> lowerFuzz = mNextAudioTimestamp - fuzz;
|
||||
bool discontinuity =
|
||||
!mNextAudioTimestamp.isValid() || mNextAudioTimestamp.value() < 0 ||
|
||||
!upperFuzz.isValid() || lowerFuzz.value() < 0 ||
|
||||
upperFuzz.value() < aSample->composition_timestamp ||
|
||||
lowerFuzz.value() > aSample->composition_timestamp;
|
||||
|
||||
if (discontinuity) {
|
||||
LOG("Discontinuity detected, expected %lld got %lld\n",
|
||||
mNextAudioTimestamp.value(), aSample->composition_timestamp);
|
||||
mCurrentAudioTimestamp = aSample->composition_timestamp;
|
||||
mSamplePosition = aSample->byte_offset;
|
||||
}
|
||||
|
||||
uint32_t flags = discontinuity ? kAudioFileStreamParseFlag_Discontinuity : 0;
|
||||
|
||||
OSStatus rv = AudioFileStreamParseBytes(mStream,
|
||||
aSample->size,
|
||||
aSample->data,
|
||||
flags);
|
||||
|
||||
if (!mOutputData.IsEmpty()) {
|
||||
int rate = mOutputFormat.mSampleRate;
|
||||
int channels = mOutputFormat.mChannelsPerFrame;
|
||||
size_t numFrames = mOutputData.Length() / channels;
|
||||
CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
|
||||
if (!duration.isValid()) {
|
||||
NS_ERROR("Invalid count of accumulated audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("pushed audio at time %lfs; duration %lfs\n",
|
||||
(double)mCurrentAudioTimestamp.value() / USECS_PER_S,
|
||||
(double)duration.value() / USECS_PER_S);
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue>
|
||||
data(new AudioDataValue[mOutputData.Length()]);
|
||||
PodCopy(data.get(), &mOutputData[0], mOutputData.Length());
|
||||
mOutputData.Clear();
|
||||
AudioData* audio = new AudioData(mSamplePosition,
|
||||
mCurrentAudioTimestamp.value(),
|
||||
duration.value(),
|
||||
numFrames,
|
||||
data.forget(),
|
||||
channels,
|
||||
rate);
|
||||
mCallback->Output(audio);
|
||||
mCurrentAudioTimestamp += duration.value();
|
||||
if (!mCurrentAudioTimestamp.isValid()) {
|
||||
NS_ERROR("Invalid count of accumulated audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
mSamplePosition += mSizeDecoded;
|
||||
mSizeDecoded = 0;
|
||||
}
|
||||
|
||||
// This is the timestamp of the next sample we should be receiving
|
||||
mNextAudioTimestamp =
|
||||
CheckedInt<Microseconds>(aSample->composition_timestamp) + aSample->duration;
|
||||
|
||||
if (rv != noErr) {
|
||||
LOG("Error %d parsing audio data", rv);
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
if (mLastError != noErr) {
|
||||
LOG("Error %d during decoding", mLastError);
|
||||
mCallback->Error();
|
||||
mLastError = noErr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes we need multiple input samples before AudioToolbox
|
||||
// starts decoding. If we haven't seen any output yet, ask for
|
||||
// more data here.
|
||||
if (!mHaveOutput) {
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
@ -392,7 +431,9 @@ AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
|
||||
void
|
||||
AppleATDecoder::SignalFlush()
|
||||
{
|
||||
mFlushed = true;
|
||||
mOutputData.Clear();
|
||||
mNextAudioTimestamp = -1;
|
||||
mSizeDecoded = 0;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -49,12 +49,20 @@ private:
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
AudioConverterRef mConverter;
|
||||
AudioFileStreamID mStream;
|
||||
Microseconds mCurrentAudioTimestamp;
|
||||
// Timestamp of the next audio frame going to be output by the decoder.
|
||||
CheckedInt<Microseconds> mCurrentAudioTimestamp;
|
||||
// Estimated timestamp of the next compressed audio packet to be supplied by
|
||||
// the MP4 demuxer.
|
||||
CheckedInt<Microseconds> mNextAudioTimestamp;
|
||||
int64_t mSamplePosition;
|
||||
bool mHaveOutput;
|
||||
bool mFlushed;
|
||||
// Compressed data size that has been processed by the decoder since the last
|
||||
// output.
|
||||
int64_t mSizeDecoded;
|
||||
AudioStreamBasicDescription mOutputFormat;
|
||||
AudioFileTypeID mFileType;
|
||||
// Array containing the queued decoded audio frames, about to be output.
|
||||
nsTArray<AudioDataValue> mOutputData;
|
||||
OSStatus mLastError;
|
||||
|
||||
void SetupDecoder();
|
||||
void SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample);
|
||||
|
@ -18,6 +18,7 @@ namespace mozilla
|
||||
{
|
||||
|
||||
bool FFmpegDataDecoder<LIBAV_VER>::sFFmpegInitDone = false;
|
||||
StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
|
||||
|
||||
FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(MediaTaskQueue* aTaskQueue,
|
||||
AVCodecID aCodecID)
|
||||
@ -58,6 +59,8 @@ ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
|
||||
nsresult
|
||||
FFmpegDataDecoder<LIBAV_VER>::Init()
|
||||
{
|
||||
StaticMutexAutoLock mon(sMonitor);
|
||||
|
||||
FFMPEG_LOG("Initialising FFmpeg decoder.");
|
||||
|
||||
if (!sFFmpegInitDone) {
|
||||
@ -130,6 +133,8 @@ FFmpegDataDecoder<LIBAV_VER>::Flush()
|
||||
nsresult
|
||||
FFmpegDataDecoder<LIBAV_VER>::Shutdown()
|
||||
{
|
||||
StaticMutexAutoLock mon(sMonitor);
|
||||
|
||||
if (sFFmpegInitDone) {
|
||||
avcodec_close(mCodecContext);
|
||||
av_freep(&mCodecContext);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "FFmpegLibs.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
@ -44,6 +45,7 @@ protected:
|
||||
|
||||
private:
|
||||
static bool sFFmpegInitDone;
|
||||
static StaticMutex sMonitor;
|
||||
|
||||
AVCodecID mCodecID;
|
||||
};
|
||||
|
@ -66,6 +66,15 @@ TEST(Media, OpusEncoder_Init)
|
||||
// Expect false with 0 or negative sampling rate of input signal.
|
||||
EXPECT_FALSE(TestOpusInit(1, 0));
|
||||
EXPECT_FALSE(TestOpusInit(1, -1));
|
||||
|
||||
// Verify sample rate bounds checking.
|
||||
EXPECT_FALSE(TestOpusInit(2, 2000));
|
||||
EXPECT_FALSE(TestOpusInit(2, 4000));
|
||||
EXPECT_FALSE(TestOpusInit(2, 7999));
|
||||
EXPECT_TRUE(TestOpusInit(2, 8000));
|
||||
EXPECT_TRUE(TestOpusInit(2, 192000));
|
||||
EXPECT_FALSE(TestOpusInit(2, 192001));
|
||||
EXPECT_FALSE(TestOpusInit(2, 200000));
|
||||
}
|
||||
|
||||
TEST(Media, OpusEncoder_Resample)
|
||||
|
@ -130,6 +130,9 @@ TEST(VorbisTrackEncoder, Init)
|
||||
// Sample rate and channel range test.
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
EXPECT_FALSE(TestVorbisInit(i, -1));
|
||||
EXPECT_FALSE(TestVorbisInit(i, 2000));
|
||||
EXPECT_FALSE(TestVorbisInit(i, 4000));
|
||||
EXPECT_FALSE(TestVorbisInit(i, 7999));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 8000));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 11000));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 16000));
|
||||
@ -138,6 +141,8 @@ TEST(VorbisTrackEncoder, Init)
|
||||
EXPECT_TRUE(TestVorbisInit(i, 44100));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 48000));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 96000));
|
||||
EXPECT_TRUE(TestVorbisInit(i, 192000));
|
||||
EXPECT_FALSE(TestVorbisInit(i, 192001));
|
||||
EXPECT_FALSE(TestVorbisInit(i, 200000 + 1));
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ runWithMSE(function (ms, v) {
|
||||
|
||||
v.addEventListener("loadedmetadata", function () {
|
||||
ok(true, "Got loadedmetadata event");
|
||||
is(v.videoWidth, 320, "videoWidth has correct initial value");
|
||||
is(v.videoHeight, 240, "videoHeight has correct initial value");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
@ -89,6 +89,8 @@ skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
|
||||
skip-if = toolkit == 'gonk' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
|
||||
[test_peerConnection_bug1042791.html]
|
||||
skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403
|
||||
[test_peerConnection_capturedVideo.html]
|
||||
skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
|
||||
[test_peerConnection_close.html]
|
||||
skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
|
||||
[test_peerConnection_errorCallbacks.html]
|
||||
|
@ -2354,10 +2354,11 @@ PeerConnectionWrapper.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
verifySdp : function PCW_verifySdp(desc, expectedType, constraints,
|
||||
offerOptions, trickleIceCallback) {
|
||||
verifySdp : function PCW_verifySdp(desc, expectedType, offerConstraintsList,
|
||||
answerConstraintsList, offerOptions, trickleIceCallback) {
|
||||
info("Examining this SessionDescription: " + JSON.stringify(desc));
|
||||
info("constraints: " + JSON.stringify(constraints));
|
||||
info("offerConstraintsList: " + JSON.stringify(offerConstraintsList));
|
||||
info("answerConstraintsList: " + JSON.stringify(answerConstraintsList));
|
||||
info("offerOptions: " + JSON.stringify(offerOptions));
|
||||
ok(desc, "SessionDescription is not null");
|
||||
is(desc.type, expectedType, "SessionDescription type is " + expectedType);
|
||||
@ -2376,11 +2377,11 @@ PeerConnectionWrapper.prototype = {
|
||||
}
|
||||
//TODO: how can we check for absence/presence of m=application?
|
||||
|
||||
//TODO: how to handle media contraints + offer options
|
||||
var audioTracks = this.countAudioTracksInMediaConstraint(constraints);
|
||||
if (constraints.length === 0) {
|
||||
audioTracks = this.audioInOfferOptions(offerOptions);
|
||||
}
|
||||
var audioTracks =
|
||||
Math.max(this.countAudioTracksInMediaConstraint(offerConstraintsList),
|
||||
this.countAudioTracksInMediaConstraint(answerConstraintsList)) ||
|
||||
this.audioInOfferOptions(offerOptions);
|
||||
|
||||
info("expected audio tracks: " + audioTracks);
|
||||
if (audioTracks == 0) {
|
||||
ok(!desc.sdp.contains("m=audio"), "audio m-line is absent from SDP");
|
||||
@ -2393,11 +2394,11 @@ PeerConnectionWrapper.prototype = {
|
||||
|
||||
}
|
||||
|
||||
//TODO: how to handle media contraints + offer options
|
||||
var videoTracks = this.countVideoTracksInMediaConstraint(constraints);
|
||||
if (constraints.length === 0) {
|
||||
videoTracks = this.videoInOfferOptions(offerOptions);
|
||||
}
|
||||
var videoTracks =
|
||||
Math.max(this.countVideoTracksInMediaConstraint(offerConstraintsList),
|
||||
this.countVideoTracksInMediaConstraint(answerConstraintsList)) ||
|
||||
this.videoInOfferOptions(offerOptions);
|
||||
|
||||
info("expected video tracks: " + videoTracks);
|
||||
if (videoTracks == 0) {
|
||||
ok(!desc.sdp.contains("m=video"), "video m-line is absent from SDP");
|
||||
|
@ -220,7 +220,7 @@ var commandsPeerConnection = [
|
||||
'PC_LOCAL_SANE_LOCAL_SDP',
|
||||
function (test) {
|
||||
test.pcLocal.verifySdp(test._local_offer, "offer",
|
||||
test._offer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function(trickle) {
|
||||
test.pcLocal.localRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -231,7 +231,7 @@ var commandsPeerConnection = [
|
||||
'PC_REMOTE_SANE_REMOTE_SDP',
|
||||
function (test) {
|
||||
test.pcRemote.verifySdp(test._local_offer, "offer",
|
||||
test._offer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcRemote.remoteRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -344,7 +344,7 @@ var commandsPeerConnection = [
|
||||
'PC_REMOTE_SANE_LOCAL_SDP',
|
||||
function (test) {
|
||||
test.pcRemote.verifySdp(test._remote_answer, "answer",
|
||||
test._answer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcRemote.localRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -355,7 +355,7 @@ var commandsPeerConnection = [
|
||||
'PC_LOCAL_SANE_REMOTE_SDP',
|
||||
function (test) {
|
||||
test.pcLocal.verifySdp(test._remote_answer, "answer",
|
||||
test._answer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcLocal.remoteRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -837,7 +837,7 @@ var commandsDataChannel = [
|
||||
'PC_LOCAL_SANE_LOCAL_SDP',
|
||||
function (test) {
|
||||
test.pcLocal.verifySdp(test._local_offer, "offer",
|
||||
test._offer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function(trickle) {
|
||||
test.pcLocal.localRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -848,7 +848,7 @@ var commandsDataChannel = [
|
||||
'PC_REMOTE_SANE_REMOTE_SDP',
|
||||
function (test) {
|
||||
test.pcRemote.verifySdp(test._local_offer, "offer",
|
||||
test._offer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcRemote.remoteRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -941,7 +941,7 @@ var commandsDataChannel = [
|
||||
'PC_REMOTE_SANE_LOCAL_SDP',
|
||||
function (test) {
|
||||
test.pcRemote.verifySdp(test._remote_answer, "answer",
|
||||
test._answer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcRemote.localRequiresTrickleIce = trickle;
|
||||
});
|
||||
@ -952,7 +952,7 @@ var commandsDataChannel = [
|
||||
'PC_LOCAL_SANE_REMOTE_SDP',
|
||||
function (test) {
|
||||
test.pcLocal.verifySdp(test._remote_answer, "answer",
|
||||
test._answer_constraints, test._offer_options,
|
||||
test._offer_constraints, test._answer_constraints, test._offer_options,
|
||||
function (trickle) {
|
||||
test.pcLocal.remoteRequiresTrickleIce = trickle;
|
||||
});
|
||||
|
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="head.js"></script>
|
||||
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
<script type="application/javascript" src="templates.js"></script>
|
||||
<script type="application/javascript" src="turnConfig.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video id="v1" src="../../test/vp9cake.webm" height="120" width="160" autoplay muted></video>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
createHTML({
|
||||
bug: "1081409",
|
||||
title: "Captured video-only over peer connection",
|
||||
visible: true
|
||||
});
|
||||
|
||||
var domLoaded = new Promise(r => addEventListener("DOMContentLoaded", e => r()));
|
||||
var test;
|
||||
var stream;
|
||||
var waitUntil = func => new Promise(resolve => {
|
||||
var ival = setInterval(() => func() && resolve(clearInterval(ival)), 200);
|
||||
});
|
||||
|
||||
runNetworkTest(function() {
|
||||
test = new PeerConnectionTest();
|
||||
test.setOfferOptions({ offerToReceiveVideo: false,
|
||||
offerToReceiveAudio: false });
|
||||
test.chain.insertAfter("PC_LOCAL_GUM", [["PC_LOCAL_CAPTUREVIDEO", function (test) {
|
||||
domLoaded
|
||||
.then(() => waitUntil(() => v1.videoWidth > 0)) // TODO: Bug 1096723
|
||||
.then(function() {
|
||||
stream = v1.mozCaptureStreamUntilEnded();
|
||||
is(stream.getTracks().length, 2, "Captured stream has 2 tracks");
|
||||
stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream));
|
||||
test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests
|
||||
test.next();
|
||||
})
|
||||
.catch(function(reason) {
|
||||
ok(false, "unexpected failure: " + reason);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
]]);
|
||||
test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -227,8 +227,14 @@ InternalSetAudioRoutesICS(SwitchState aState)
|
||||
AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
|
||||
sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
|
||||
} else if (aState == SWITCH_STATE_OFF) {
|
||||
AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(sHeadsetState),
|
||||
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
|
||||
if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
|
||||
AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
|
||||
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
|
||||
}
|
||||
if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
|
||||
AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
|
||||
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
|
||||
}
|
||||
sHeadsetState = 0;
|
||||
}
|
||||
}
|
||||
|
@ -1902,22 +1902,28 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
|
||||
gfxPoint p(aPt->x * aRunParams.devPerApp,
|
||||
aPt->y * aRunParams.devPerApp);
|
||||
const Metrics& metrics = GetMetrics(eHorizontal);
|
||||
// Adjust the matrix to draw the (horizontally-shaped) textrun with
|
||||
// 90-degree CW rotation, and adjust position so that the rotated
|
||||
// horizontal text (which uses a standard alphabetic baseline) will
|
||||
// Get a matrix we can use to draw the (horizontally-shaped) textrun
|
||||
// with 90-degree CW rotation.
|
||||
gfxMatrix mat = aRunParams.context->CurrentMatrix().
|
||||
Translate(p). // translate origin for rotation
|
||||
Rotate(M_PI / 2.0). // turn 90deg clockwise
|
||||
Translate(-p); // undo the translation
|
||||
|
||||
// If we're drawing rotated horizontal text for an element styled
|
||||
// text-orientation:mixed, the dominant baseline will be vertical-
|
||||
// centered. So in this case, we need to adjust the position so that
|
||||
// the rotated horizontal text (which uses an alphabetic baseline) will
|
||||
// look OK when juxtaposed with upright glyphs (rendered on a centered
|
||||
// vertical baseline). The adjustment here is somewhat ad hoc; we
|
||||
// should eventually look for baseline tables[1] in the fonts and use
|
||||
// those if available.
|
||||
// [1] http://www.microsoft.com/typography/otspec/base.htm
|
||||
aRunParams.context->SetMatrix(aRunParams.context->CurrentMatrix().
|
||||
Translate(p). // translate origin for rotation
|
||||
Rotate(M_PI / 2.0). // turn 90deg clockwise
|
||||
Translate(-p). // undo the translation
|
||||
Translate(gfxPoint(0, (metrics.emAscent - metrics.emDescent) / 2)));
|
||||
// and offset the (alphabetic) baseline of the
|
||||
// horizontally-shaped text from the (centered)
|
||||
// default baseline used for vertical
|
||||
// [1] See http://www.microsoft.com/typography/otspec/base.htm
|
||||
if (aTextRun->UseCenterBaseline()) {
|
||||
gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
|
||||
mat.Translate(baseAdj);
|
||||
}
|
||||
|
||||
aRunParams.context->SetMatrix(mat);
|
||||
}
|
||||
|
||||
nsAutoPtr<gfxTextContextPaint> contextPaint;
|
||||
@ -2138,15 +2144,33 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
// Current position in appunits
|
||||
gfxFont::Orientation orientation =
|
||||
aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT
|
||||
? gfxFont::eVertical : gfxFont::eHorizontal;
|
||||
? eVertical : eHorizontal;
|
||||
const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
|
||||
|
||||
gfxFloat baselineOffset = 0;
|
||||
if (aTextRun->UseCenterBaseline() && orientation == eHorizontal) {
|
||||
// For a horizontal font being used in vertical writing mode with
|
||||
// text-orientation:mixed, the overall metrics we're accumulating
|
||||
// will be aimed at a center baseline. But this font's metrics were
|
||||
// based on the alphabetic baseline. So we compute a baseline offset
|
||||
// that will be applied to ascent/descent values and glyph rects
|
||||
// to effectively shift them relative to the baseline.
|
||||
// XXX Eventually we should probably use the BASE table, if present.
|
||||
// But it usually isn't, so we need an ad hoc adjustment for now.
|
||||
baselineOffset = appUnitsPerDevUnit *
|
||||
(fontMetrics.emAscent - fontMetrics.emDescent) / 2;
|
||||
}
|
||||
|
||||
RunMetrics metrics;
|
||||
metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
|
||||
metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
|
||||
metrics.mAscent = fontMetrics.maxAscent * appUnitsPerDevUnit;
|
||||
metrics.mDescent = fontMetrics.maxDescent * appUnitsPerDevUnit;
|
||||
|
||||
if (aStart == aEnd) {
|
||||
// exit now before we look at aSpacing[0], which is undefined
|
||||
metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
|
||||
metrics.mAscent -= baselineOffset;
|
||||
metrics.mDescent += baselineOffset;
|
||||
metrics.mBoundingBox = gfxRect(0, -metrics.mAscent,
|
||||
0, metrics.mAscent + metrics.mDescent);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@ -2247,6 +2271,12 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
metrics.mBoundingBox -= gfxPoint(x, 0);
|
||||
}
|
||||
|
||||
if (baselineOffset != 0) {
|
||||
metrics.mAscent -= baselineOffset;
|
||||
metrics.mDescent += baselineOffset;
|
||||
metrics.mBoundingBox.y += baselineOffset;
|
||||
}
|
||||
|
||||
metrics.mAdvanceWidth = x*direction;
|
||||
return metrics;
|
||||
}
|
||||
|
@ -945,6 +945,12 @@ public:
|
||||
gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL;
|
||||
}
|
||||
|
||||
bool UseCenterBaseline() const {
|
||||
uint32_t orient = GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK;
|
||||
return orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED ||
|
||||
orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
|
||||
}
|
||||
|
||||
bool IsRightToLeft() const {
|
||||
return (GetFlags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
|
||||
}
|
||||
|
@ -161,7 +161,11 @@ CPUInfo::SetSSEVersion()
|
||||
);
|
||||
# else
|
||||
// On x86, preserve ebx. The compiler needs it for PIC mode.
|
||||
// Some older processors don't fill the ecx register with cpuid, so clobber
|
||||
// it before calling cpuid, so that there's no risk of picking random bits
|
||||
// indicating SSE3/SSE4 are present.
|
||||
asm (
|
||||
"xor %%ecx, %%ecx;"
|
||||
"movl $0x1, %%eax;"
|
||||
"pushl %%ebx;"
|
||||
"cpuid;"
|
||||
|
@ -1453,9 +1453,12 @@ RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
|
||||
// Until we get rid of these phases in bug 960465, we need to skip
|
||||
// animation restyles during the non-animation phase, and post
|
||||
// animation restyles so that we restyle those elements again in the
|
||||
// animation phase.
|
||||
// animation phase. Furthermore, we need to add
|
||||
// eRestyle_ChangeAnimationPhaseDescendants so that we actually honor
|
||||
// these booleans in all cases.
|
||||
mSkipAnimationRules = true;
|
||||
mPostAnimationRestyles = true;
|
||||
aRestyleHint |= eRestyle_ChangeAnimationPhaseDescendants;
|
||||
|
||||
DoRebuildAllStyleData(mPendingRestyles, aExtraHint, aRestyleHint);
|
||||
|
||||
@ -1495,8 +1498,12 @@ RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
|
||||
// different styles). If we use up the hint for one of the
|
||||
// ancestors that we hit first, then we'll fail to do the restyling
|
||||
// we need to do.
|
||||
aRestyleTracker.AddPendingRestyle(mPresContext->Document()->GetRootElement(),
|
||||
aRestyleHint, nsChangeHint(0));
|
||||
Element* root = mPresContext->Document()->GetRootElement();
|
||||
if (root) {
|
||||
// If the root element is gone, dropping the hint on the floor
|
||||
// should be fine.
|
||||
aRestyleTracker.AddPendingRestyle(root, aRestyleHint, nsChangeHint(0));
|
||||
}
|
||||
aRestyleHint = nsRestyleHint(0);
|
||||
}
|
||||
|
||||
@ -2515,11 +2522,13 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
|
||||
}
|
||||
}
|
||||
|
||||
// If we are restyling this frame with eRestyle_Self, we restyle
|
||||
// children with nsRestyleHint(0). But we pass the eRestyle_ForceDescendants
|
||||
// flag down too.
|
||||
// If we are restyling this frame with eRestyle_Self or weaker hints,
|
||||
// we restyle children with nsRestyleHint(0). But we pass the
|
||||
// eRestyle_ChangeAnimationPhaseDescendants and eRestyle_ForceDescendants
|
||||
// flags down too.
|
||||
nsRestyleHint childRestyleHint =
|
||||
nsRestyleHint(aRestyleHint & (eRestyle_Subtree |
|
||||
eRestyle_ChangeAnimationPhaseDescendants |
|
||||
eRestyle_ForceDescendants));
|
||||
|
||||
nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
|
||||
@ -3736,7 +3745,9 @@ RestyleManager::RestyleHintToString(nsRestyleHint aHint)
|
||||
bool any = false;
|
||||
const char* names[] = { "Self", "Subtree", "LaterSiblings", "CSSTransitions",
|
||||
"CSSAnimations", "SVGAttrAnimations", "StyleAttribute",
|
||||
"ChangeAnimationPhase", "Force", "ForceDescendants" };
|
||||
"ChangeAnimationPhase",
|
||||
"ChangeAnimationPhaseDescendants",
|
||||
"Force", "ForceDescendants" };
|
||||
uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
|
||||
uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
|
||||
for (uint32_t i = 0; i < ArrayLength(names); i++) {
|
||||
|
@ -4162,8 +4162,12 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
|
||||
return;
|
||||
}
|
||||
|
||||
// The y position should be set to the middle of the line.
|
||||
rect.y += lineThickness / 2;
|
||||
// The block-direction position should be set to the middle of the line.
|
||||
if (aVertical) {
|
||||
rect.x -= lineThickness / 2;
|
||||
} else {
|
||||
rect.y += lineThickness / 2;
|
||||
}
|
||||
|
||||
switch (aStyle) {
|
||||
case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
|
||||
@ -4360,12 +4364,18 @@ nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
|
||||
|
||||
gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0);
|
||||
|
||||
// The y position should be set to the middle of the line.
|
||||
rect.y += lineThickness / 2;
|
||||
|
||||
aGfxContext->Rectangle
|
||||
(gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineThickness / 2)),
|
||||
gfxSize(rect.Width(), lineThickness)));
|
||||
// The block-direction position should be set to the middle of the line.
|
||||
if (aVertical) {
|
||||
rect.x -= lineThickness / 2;
|
||||
aGfxContext->Rectangle
|
||||
(gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(lineThickness / 2, 0.0)),
|
||||
gfxSize(lineThickness, rect.Height())));
|
||||
} else {
|
||||
rect.y += lineThickness / 2;
|
||||
aGfxContext->Rectangle
|
||||
(gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineThickness / 2)),
|
||||
gfxSize(rect.Width(), lineThickness)));
|
||||
}
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
@ -309,9 +309,14 @@ nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
|
||||
descent = fm->MaxDescent();
|
||||
}
|
||||
nscoord height = ascent + descent;
|
||||
bool vertical = aFrame->GetWritingMode().IsVertical();
|
||||
WritingMode wm = aFrame->GetWritingMode();
|
||||
bool vertical = wm.IsVertical();
|
||||
if (vertical) {
|
||||
framePos.x = baseline - ascent;
|
||||
if (wm.IsLineInverted()) {
|
||||
framePos.x = baseline - descent;
|
||||
} else {
|
||||
framePos.x = baseline - ascent;
|
||||
}
|
||||
} else {
|
||||
framePos.y = baseline - ascent;
|
||||
}
|
||||
|
@ -346,16 +346,20 @@ enum nsRestyleHint {
|
||||
// FIXME: Remove this as part of bug 960465.
|
||||
eRestyle_ChangeAnimationPhase = (1 << 7),
|
||||
|
||||
// Same as the previous, except this applies to the entire subtree.
|
||||
// FIXME: Remove this as part of bug 960465.
|
||||
eRestyle_ChangeAnimationPhaseDescendants = (1 << 8),
|
||||
|
||||
// Continue the restyling process to the current frame's children even
|
||||
// if this frame's restyling resulted in no style changes.
|
||||
eRestyle_Force = (1<<8),
|
||||
eRestyle_Force = (1<<9),
|
||||
|
||||
// Continue the restyling process to all of the current frame's
|
||||
// descendants, even if any frame's restyling resulted in no style
|
||||
// changes. (Implies eRestyle_Force.) Note that this is weaker than
|
||||
// eRestyle_Subtree, which makes us rerun selector matching on all
|
||||
// descendants rather than just continuing the restyling process.
|
||||
eRestyle_ForceDescendants = (1<<9),
|
||||
eRestyle_ForceDescendants = (1<<10),
|
||||
};
|
||||
|
||||
// The functions below need an integral type to cast to to avoid
|
||||
|
@ -3468,10 +3468,11 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
|
||||
if (aInflation != 1.0f) {
|
||||
font.size = NSToCoordRound(font.size * aInflation);
|
||||
}
|
||||
WritingMode wm(aStyleContext->StyleVisibility());
|
||||
WritingMode wm(aStyleContext);
|
||||
return pc->DeviceContext()->GetMetricsFor(
|
||||
font, aStyleContext->StyleFont()->mLanguage,
|
||||
wm.IsVertical() ? gfxFont::eVertical : gfxFont::eHorizontal,
|
||||
wm.IsVertical() && !wm.IsSideways()
|
||||
? gfxFont::eVertical : gfxFont::eHorizontal,
|
||||
fs, tp, *aFontMetrics);
|
||||
}
|
||||
|
||||
@ -4868,9 +4869,11 @@ nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
|
||||
|
||||
/* static */ nscoord
|
||||
nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
|
||||
nscoord aLineHeight)
|
||||
nscoord aLineHeight,
|
||||
bool aIsInverted)
|
||||
{
|
||||
nscoord fontAscent = aFontMetrics->MaxAscent();
|
||||
nscoord fontAscent = aIsInverted ? aFontMetrics->MaxDescent()
|
||||
: aFontMetrics->MaxAscent();
|
||||
nscoord fontHeight = aFontMetrics->MaxHeight();
|
||||
|
||||
nscoord leading = aLineHeight - fontHeight;
|
||||
@ -5846,7 +5849,7 @@ nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WritingMode wm(aStyleContext->StyleVisibility());
|
||||
WritingMode wm(aStyleContext);
|
||||
if (wm.IsVertical()) {
|
||||
switch (aStyleText->mTextOrientation) {
|
||||
case NS_STYLE_TEXT_ORIENTATION_MIXED:
|
||||
@ -7323,7 +7326,8 @@ nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
|
||||
// The height of our box is the sum of our font size plus the top
|
||||
// and bottom border and padding. The height of children do not
|
||||
// affect our height.
|
||||
aMetrics.SetBlockStartAscent(fm->MaxAscent());
|
||||
aMetrics.SetBlockStartAscent(aLineWM.IsLineInverted() ? fm->MaxDescent()
|
||||
: fm->MaxAscent());
|
||||
aMetrics.BSize(aLineWM) = fm->MaxHeight();
|
||||
} else {
|
||||
NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
|
||||
|
@ -1391,11 +1391,15 @@ public:
|
||||
/**
|
||||
* Gets the baseline to vertically center text from a font within a
|
||||
* line of specified height.
|
||||
* aIsInverted: true if the text is inverted relative to the block
|
||||
* direction, so that the block-dir "ascent" corresponds to font
|
||||
* descent. (Applies to sideways text in vertical-lr mode.)
|
||||
*
|
||||
* Returns the baseline position relative to the top of the line.
|
||||
*/
|
||||
static nscoord GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
|
||||
nscoord aLineHeight);
|
||||
nscoord aLineHeight,
|
||||
bool aIsInverted);
|
||||
|
||||
/**
|
||||
* Derive a baseline of |aFrame| (measured from its top border edge)
|
||||
|
@ -519,7 +519,8 @@ nsTextControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
inflation);
|
||||
// now adjust for our borders and padding
|
||||
aDesiredSize.SetBlockStartAscent(
|
||||
nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight) +
|
||||
nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight,
|
||||
wm.IsLineInverted()) +
|
||||
aReflowState.ComputedLogicalBorderPadding().BStart(wm));
|
||||
|
||||
// overflow handling
|
||||
|
@ -7,7 +7,7 @@
|
||||
#define WritingModes_h_
|
||||
|
||||
#include "nsRect.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "nsStyleContext.h"
|
||||
|
||||
// If WRITING_MODE_VERTICAL_ENABLED is defined, we will attempt to support
|
||||
// the vertical writing-mode values; if it is not defined, then
|
||||
@ -168,12 +168,17 @@ public:
|
||||
/**
|
||||
* Return true if LTR. (Convenience method)
|
||||
*/
|
||||
bool IsBidiLTR() const { return eBidiLTR == (mWritingMode & eBidiMask); }
|
||||
bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
|
||||
|
||||
/**
|
||||
* True if vertical-mode block direction is LR (convenience method).
|
||||
*/
|
||||
bool IsVerticalLR() const { return eBlockLR == (mWritingMode & eBlockMask); }
|
||||
bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
|
||||
|
||||
/**
|
||||
* True if vertical-mode block direction is RL (convenience method).
|
||||
*/
|
||||
bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); }
|
||||
|
||||
/**
|
||||
* True if vertical writing mode, i.e. when
|
||||
@ -208,6 +213,20 @@ public:
|
||||
return IsLineInverted() ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the text-orientation will force all text to be rendered sideways
|
||||
* in vertical lines, in which case we should prefer an alphabetic baseline;
|
||||
* otherwise, the default is centered.
|
||||
* Note that some glyph runs may be rendered sideways even if this is false,
|
||||
* due to text-orientation:mixed resolution, but in that case the dominant
|
||||
* baseline remains centered.
|
||||
*/
|
||||
#ifdef WRITING_MODE_VERTICAL_ENABLED
|
||||
bool IsSideways() const { return !!(mWritingMode & eSidewaysMask); }
|
||||
#else
|
||||
bool IsSideways() const { return false; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default constructor gives us a horizontal, LTR writing mode.
|
||||
* XXX We will probably eliminate this and require explicit initialization
|
||||
@ -220,25 +239,49 @@ public:
|
||||
/**
|
||||
* Construct writing mode based on a style context
|
||||
*/
|
||||
explicit WritingMode(const nsStyleVisibility* aStyleVisibility)
|
||||
explicit WritingMode(nsStyleContext* aStyleContext)
|
||||
{
|
||||
NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
|
||||
NS_ASSERTION(aStyleContext, "we need an nsStyleContext here");
|
||||
|
||||
const nsStyleVisibility* styleVisibility = aStyleContext->StyleVisibility();
|
||||
|
||||
#ifdef WRITING_MODE_VERTICAL_ENABLED
|
||||
switch (aStyleVisibility->mWritingMode) {
|
||||
switch (styleVisibility->mWritingMode) {
|
||||
case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
|
||||
mWritingMode = 0;
|
||||
break;
|
||||
|
||||
case NS_STYLE_WRITING_MODE_VERTICAL_LR:
|
||||
{
|
||||
mWritingMode = eBlockFlowMask |
|
||||
eLineOrientMask | //XXX needs update when text-orientation added
|
||||
eLineOrientMask |
|
||||
eOrientationMask;
|
||||
uint8_t textOrientation = aStyleContext->StyleText()->mTextOrientation;
|
||||
#if 0 // not yet implemented
|
||||
if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_LEFT) {
|
||||
mWritingMode &= ~eLineOrientMask;
|
||||
}
|
||||
#endif
|
||||
if (textOrientation >= NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_RIGHT) {
|
||||
mWritingMode |= eSidewaysMask;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NS_STYLE_WRITING_MODE_VERTICAL_RL:
|
||||
{
|
||||
mWritingMode = eOrientationMask;
|
||||
uint8_t textOrientation = aStyleContext->StyleText()->mTextOrientation;
|
||||
#if 0 // not yet implemented
|
||||
if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_LEFT) {
|
||||
mWritingMode |= eLineOrientMask;
|
||||
}
|
||||
#endif
|
||||
if (textOrientation >= NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_RIGHT) {
|
||||
mWritingMode |= eSidewaysMask;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("unknown writing mode!");
|
||||
@ -249,7 +292,7 @@ public:
|
||||
mWritingMode = 0;
|
||||
#endif
|
||||
|
||||
if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
|
||||
if (NS_STYLE_DIRECTION_RTL == styleVisibility->mDirection) {
|
||||
mWritingMode |= eInlineFlowMask | //XXX needs update when text-orientation added
|
||||
eBidiMask;
|
||||
}
|
||||
@ -325,6 +368,10 @@ private:
|
||||
// Note: We have one excess bit of info; WritingMode can pack into 4 bits.
|
||||
// But since we have space, we're caching interesting things for fast access.
|
||||
|
||||
eSidewaysMask = 0x20, // true means text-orientation is sideways-*,
|
||||
// which means we'll use alphabetic instead of
|
||||
// centered default baseline for vertical text
|
||||
|
||||
// Masks for output enums
|
||||
eInlineMask = 0x03,
|
||||
eBlockMask = 0x05
|
||||
|
@ -122,7 +122,8 @@ BRFrame::Reflow(nsPresContext* aPresContext,
|
||||
if (fm) {
|
||||
nscoord logicalHeight = aReflowState.CalcLineHeight();
|
||||
finalSize.BSize(wm) = logicalHeight;
|
||||
aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline(fm, logicalHeight));
|
||||
aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline(
|
||||
fm, logicalHeight, wm.IsLineInverted()));
|
||||
}
|
||||
else {
|
||||
aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0);
|
||||
|
@ -515,7 +515,9 @@ nsBlockFrame::GetCaretBaseline() const
|
||||
nscoord lineHeight =
|
||||
nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
|
||||
contentRect.height, inflation);
|
||||
return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight) + bp.top;
|
||||
const WritingMode wm = GetWritingMode();
|
||||
return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
|
||||
wm.IsLineInverted()) + bp.top;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@ -1231,6 +1233,42 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
|
||||
// Compute our final size
|
||||
nscoord blockEndEdgeOfChildren;
|
||||
ComputeFinalSize(*reflowState, state, aMetrics, &blockEndEdgeOfChildren);
|
||||
|
||||
// If the block direction is right-to-left, we need to update the bounds of
|
||||
// lines that were placed relative to mContainerWidth during reflow, as
|
||||
// we typically do not know the true container width (block-dir size of the
|
||||
// finished paragraph/block) until we've reflowed all its children. So we
|
||||
// use a "fake" mContainerWidth during reflow (see nsBlockReflowState's
|
||||
// constructor) and then fix up the positions of the lines here, once the
|
||||
// final block size is known.
|
||||
//
|
||||
// Note that writing-mode:vertical-rl is the only case where the block
|
||||
// logical direction progresses in a negative physical direction, and
|
||||
// therefore block-dir coordinate conversion depends on knowing the width
|
||||
// of the coordinate space in order to translate between the logical and
|
||||
// physical origins.
|
||||
if (wm.GetBlockDir() == WritingMode::BlockDir::eBlockRL) {
|
||||
nscoord deltaX = aMetrics.Width() - state.mContainerWidth;
|
||||
if (deltaX) {
|
||||
for (line_iterator line = begin_lines(), end = end_lines();
|
||||
line != end; line++) {
|
||||
SlideLine(state, line, -deltaX);
|
||||
}
|
||||
for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
|
||||
nsPoint physicalDelta(deltaX, 0);
|
||||
f->MovePositionBy(physicalDelta);
|
||||
}
|
||||
nsFrameList* bulletList = GetOutsideBulletList();
|
||||
if (bulletList) {
|
||||
nsPoint physicalDelta(deltaX, 0);
|
||||
for (nsIFrame* f = bulletList->FirstChild(); f;
|
||||
f = f->GetNextSibling()) {
|
||||
f->MovePositionBy(physicalDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRect areaBounds = nsRect(0, 0, aMetrics.Width(), aMetrics.Height());
|
||||
ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay,
|
||||
blockEndEdgeOfChildren, aMetrics.mOverflowAreas);
|
||||
@ -2516,7 +2554,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
nsLayoutUtils::FontSizeInflationFor(this));
|
||||
|
||||
nscoord minAscent =
|
||||
nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight);
|
||||
nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight,
|
||||
wm.IsLineInverted());
|
||||
nscoord minDescent = aState.mMinLineHeight - minAscent;
|
||||
|
||||
aState.mBCoord += std::max(minAscent, metrics.BlockStartAscent()) +
|
||||
@ -2731,12 +2770,12 @@ nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
|
||||
|
||||
void
|
||||
nsBlockFrame::SlideLine(nsBlockReflowState& aState,
|
||||
nsLineBox* aLine, nscoord aDY)
|
||||
nsLineBox* aLine, nscoord aDeltaBCoord)
|
||||
{
|
||||
NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
|
||||
NS_PRECONDITION(aDeltaBCoord != 0, "why slide a line nowhere?");
|
||||
|
||||
// Adjust line state
|
||||
aLine->SlideBy(aDY, aState.mContainerWidth);
|
||||
aLine->SlideBy(aDeltaBCoord, aState.mContainerWidth);
|
||||
|
||||
// Adjust the frames in the line
|
||||
nsIFrame* kid = aLine->mFirstChild;
|
||||
@ -2744,23 +2783,26 @@ nsBlockFrame::SlideLine(nsBlockReflowState& aState,
|
||||
return;
|
||||
}
|
||||
|
||||
WritingMode wm = GetWritingMode();
|
||||
LogicalPoint translation(wm, 0, aDeltaBCoord);
|
||||
|
||||
if (aLine->IsBlock()) {
|
||||
if (aDY) {
|
||||
kid->MovePositionBy(nsPoint(0, aDY));
|
||||
if (aDeltaBCoord) {
|
||||
kid->MovePositionBy(wm, translation);
|
||||
}
|
||||
|
||||
// Make sure the frame's view and any child views are updated
|
||||
nsContainerFrame::PlaceFrameView(kid);
|
||||
}
|
||||
else {
|
||||
// Adjust the Y coordinate of the frames in the line.
|
||||
// Note: we need to re-position views even if aDY is 0, because
|
||||
// Adjust the block-dir coordinate of the frames in the line.
|
||||
// Note: we need to re-position views even if aDeltaBCoord is 0, because
|
||||
// one of our parent frames may have moved and so the view's position
|
||||
// relative to its parent may have changed
|
||||
// relative to its parent may have changed.
|
||||
int32_t n = aLine->GetChildCount();
|
||||
while (--n >= 0) {
|
||||
if (aDY) {
|
||||
kid->MovePositionBy(nsPoint(0, aDY));
|
||||
if (aDeltaBCoord) {
|
||||
kid->MovePositionBy(wm, translation);
|
||||
}
|
||||
// Make sure the frame's view and any child views are updated
|
||||
nsContainerFrame::PlaceFrameView(kid);
|
||||
|
@ -408,11 +408,11 @@ protected:
|
||||
mState |= aFlags;
|
||||
}
|
||||
|
||||
/** move the frames contained by aLine by aDY
|
||||
/** move the frames contained by aLine by aDeltaBCoord
|
||||
* if aLine is a block, its child floats are added to the state manager
|
||||
*/
|
||||
void SlideLine(nsBlockReflowState& aState,
|
||||
nsLineBox* aLine, nscoord aDY);
|
||||
nsLineBox* aLine, nscoord aDeltaBCoord);
|
||||
|
||||
void ComputeFinalSize(const nsHTMLReflowState& aReflowState,
|
||||
nsBlockReflowState& aState,
|
||||
|
@ -54,8 +54,19 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
|
||||
aFrame->GetLogicalSkipSides(&aReflowState);
|
||||
mBorderPadding.ApplySkipSides(logicalSkipSides);
|
||||
|
||||
// Note that mContainerWidth is the physical width!
|
||||
mContainerWidth = aReflowState.ComputedWidth() + mBorderPadding.LeftRight(wm);
|
||||
// Note that mContainerWidth is the physical width, needed to convert
|
||||
// logical block-coordinates in vertical-rl writing mode (measured from a
|
||||
// RHS origin) to physical coordinates within the containing block.
|
||||
// If aReflowState doesn't have a constrained ComputedWidth(), we set it to
|
||||
// zero, which means lines will be positioned (physically) incorrectly;
|
||||
// we will fix them up at the end of nsBlockFrame::Reflow, after we know
|
||||
// the total block-size of the frame.
|
||||
mContainerWidth = aReflowState.ComputedWidth();
|
||||
if (mContainerWidth == NS_UNCONSTRAINEDSIZE) {
|
||||
mContainerWidth = 0;
|
||||
}
|
||||
|
||||
mContainerWidth += mBorderPadding.LeftRight(wm);
|
||||
|
||||
if ((aBStartMarginRoot && !logicalSkipSides.BStart()) ||
|
||||
0 != mBorderPadding.BStart(wm)) {
|
||||
|
@ -203,6 +203,8 @@ public:
|
||||
mozilla::WritingMode wm = mReflowState.GetWritingMode();
|
||||
return mContentArea.Size(wm).ConvertTo(aWM, wm);
|
||||
}
|
||||
|
||||
// Physical width. Use only for physical <-> logical coordinate conversion.
|
||||
nscoord mContainerWidth;
|
||||
|
||||
// Continuation out-of-flow float frames that need to move to our
|
||||
|
@ -1339,7 +1339,12 @@ nsFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
||||
{
|
||||
NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
|
||||
"frame must not be dirty");
|
||||
// Default to the bottom margin edge, per CSS2.1's definition of the
|
||||
// Baseline for inverted line content is the top (block-start) margin edge,
|
||||
// as the frame is in effect "flipped" for alignment purposes.
|
||||
if (aWritingMode.IsLineInverted()) {
|
||||
return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
|
||||
}
|
||||
// Otherwise, the bottom margin edge, per CSS2.1's definition of the
|
||||
// 'baseline' value of 'vertical-align'.
|
||||
return BSize(aWritingMode) +
|
||||
GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
|
||||
|
@ -608,7 +608,7 @@ public:
|
||||
* The frame's writing-mode, used for logical layout computations.
|
||||
*/
|
||||
mozilla::WritingMode GetWritingMode() const {
|
||||
return mozilla::WritingMode(StyleVisibility());
|
||||
return mozilla::WritingMode(StyleContext());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -744,6 +744,15 @@ public:
|
||||
*/
|
||||
void MovePositionBy(const nsPoint& aTranslation);
|
||||
|
||||
/**
|
||||
* As above, using a logical-point delta in a given writing mode.
|
||||
*/
|
||||
void MovePositionBy(mozilla::WritingMode aWritingMode,
|
||||
const mozilla::LogicalPoint& aTranslation)
|
||||
{
|
||||
MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return frame's position without relative positioning
|
||||
*/
|
||||
|
@ -473,8 +473,10 @@ public:
|
||||
mContainerWidth = aContainerWidth;
|
||||
mBounds.BStart(mWritingMode) += aDBCoord;
|
||||
if (mData) {
|
||||
nsPoint physicalDelta = mozilla::LogicalPoint(mWritingMode, 0, aDBCoord).
|
||||
GetPhysicalPoint(mWritingMode, 0);
|
||||
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
|
||||
mData->mOverflowAreas.Overflow(otype).y += aDBCoord;
|
||||
mData->mOverflowAreas.Overflow(otype) += physicalDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -585,9 +587,13 @@ public:
|
||||
nsIFrame* mFirstChild;
|
||||
|
||||
mozilla::WritingMode mWritingMode;
|
||||
|
||||
// Physical width. Use only for physical <-> logical coordinate conversion.
|
||||
nscoord mContainerWidth;
|
||||
|
||||
private:
|
||||
mozilla::LogicalRect mBounds;
|
||||
|
||||
public:
|
||||
const mozilla::LogicalRect& GetBounds() { return mBounds; }
|
||||
nsRect GetPhysicalBounds() const
|
||||
|
@ -608,8 +608,8 @@ nsLineLayout::NewPerFrameData(nsIFrame* aFrame)
|
||||
WritingMode frameWM = aFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
pfd->mBounds = LogicalRect(lineWM);
|
||||
pfd->mMargin = LogicalMargin(frameWM);
|
||||
pfd->mBorderPadding = LogicalMargin(frameWM);
|
||||
pfd->mMargin = LogicalMargin(lineWM);
|
||||
pfd->mBorderPadding = LogicalMargin(lineWM);
|
||||
pfd->mOffsets = LogicalMargin(frameWM);
|
||||
|
||||
pfd->mJustificationInfo = JustificationInfo();
|
||||
@ -809,9 +809,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
}
|
||||
WritingMode stateWM = reflowState.GetWritingMode();
|
||||
pfd->mMargin =
|
||||
reflowState.ComputedLogicalMargin().ConvertTo(frameWM, stateWM);
|
||||
reflowState.ComputedLogicalMargin().ConvertTo(lineWM, stateWM);
|
||||
pfd->mBorderPadding =
|
||||
reflowState.ComputedLogicalBorderPadding().ConvertTo(frameWM, stateWM);
|
||||
reflowState.ComputedLogicalBorderPadding().ConvertTo(lineWM, stateWM);
|
||||
pfd->SetFlag(PFD_RELATIVEPOS,
|
||||
reflowState.mStyleDisplay->IsRelativelyPositionedStyle());
|
||||
if (pfd->GetFlag(PFD_RELATIVEPOS)) {
|
||||
@ -1086,7 +1086,7 @@ nsLineLayout::AllowForStartMargin(PerFrameData* pfd,
|
||||
"How'd we get a floated inline frame? "
|
||||
"The frame ctor should've dealt with this.");
|
||||
|
||||
WritingMode frameWM = pfd->mFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
|
||||
// Only apply start-margin on the first-in flow for inline frames,
|
||||
// and make sure to not apply it to any inline other than the first
|
||||
@ -1101,7 +1101,7 @@ nsLineLayout::AllowForStartMargin(PerFrameData* pfd,
|
||||
NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
|
||||
// Zero this out so that when we compute the max-element-width of
|
||||
// the frame we will properly avoid adding in the starting margin.
|
||||
pfd->mMargin.IStart(frameWM) = 0;
|
||||
pfd->mMargin.IStart(lineWM) = 0;
|
||||
} else {
|
||||
NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.AvailableISize(),
|
||||
"have unconstrained inline-size; this should only result "
|
||||
@ -1112,7 +1112,9 @@ nsLineLayout::AllowForStartMargin(PerFrameData* pfd,
|
||||
// in the reflow state), adjust available inline-size to account for the
|
||||
// start margin. The end margin will be accounted for when we
|
||||
// finish flowing the frame.
|
||||
aReflowState.AvailableISize() -= pfd->mMargin.IStart(frameWM);
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
aReflowState.AvailableISize() -=
|
||||
pfd->mMargin.ConvertTo(wm, lineWM).IStart(wm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1151,7 +1153,6 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
|
||||
|
||||
*aOptionalBreakAfterFits = true;
|
||||
|
||||
WritingMode frameWM = pfd->mFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
/*
|
||||
* We want to only apply the end margin if we're the last continuation and
|
||||
@ -1176,14 +1177,12 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
|
||||
!pfd->GetFlag(PFD_ISLETTERFRAME) &&
|
||||
pfd->mFrame->StyleBorder()->mBoxDecorationBreak ==
|
||||
NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
|
||||
pfd->mMargin.IEnd(frameWM) = 0;
|
||||
pfd->mMargin.IEnd(lineWM) = 0;
|
||||
}
|
||||
|
||||
// Convert the frame's margins to the line's writing mode and apply
|
||||
// the start margin to the frame bounds.
|
||||
LogicalMargin usedMargins = pfd->mMargin.ConvertTo(lineWM, frameWM);
|
||||
nscoord startMargin = usedMargins.IStart(lineWM);
|
||||
nscoord endMargin = usedMargins.IEnd(lineWM);
|
||||
// Apply the start margin to the frame bounds.
|
||||
nscoord startMargin = pfd->mMargin.IStart(lineWM);
|
||||
nscoord endMargin = pfd->mMargin.IEnd(lineWM);
|
||||
|
||||
pfd->mBounds.IStart(lineWM) += startMargin;
|
||||
|
||||
@ -1310,7 +1309,6 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
|
||||
void
|
||||
nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics)
|
||||
{
|
||||
WritingMode frameWM = pfd->mFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
|
||||
// Record ascent and update max-ascent and max-descent values
|
||||
@ -1322,7 +1320,7 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics)
|
||||
|
||||
// Advance to next inline coordinate
|
||||
mCurrentSpan->mICoord = pfd->mBounds.IEnd(lineWM) +
|
||||
pfd->mMargin.ConvertTo(lineWM, frameWM).IEnd(lineWM);
|
||||
pfd->mMargin.IEnd(lineWM);
|
||||
|
||||
// Count the number of non-placeholder frames on the line...
|
||||
if (pfd->mFrame->GetType() == nsGkAtoms::placeholderFrame) {
|
||||
@ -1509,7 +1507,6 @@ nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(0xFF != pfd->mBlockDirAlign, "umr");
|
||||
#endif
|
||||
WritingMode frameWM = pfd->mFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
nscoord containerWidth = ContainerWidthForSpan(psd);
|
||||
switch (pfd->mBlockDirAlign) {
|
||||
@ -1519,7 +1516,7 @@ nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
|
||||
}
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) =
|
||||
-aDistanceFromStart + pfd->mMargin.BStart(frameWM);
|
||||
-aDistanceFromStart + pfd->mMargin.BStart(lineWM);
|
||||
}
|
||||
pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerWidth);
|
||||
#ifdef NOISY_BLOCKDIR_ALIGN
|
||||
@ -1527,7 +1524,7 @@ nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
|
||||
nsFrame::ListTag(stdout, pfd->mFrame);
|
||||
printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
|
||||
pfd->mBounds.BStart(lineWM), aDistanceFromStart,
|
||||
span ? pfd->mBorderPadding.BStart(frameWM) : 0,
|
||||
span ? pfd->mBorderPadding.BStart(lineWM) : 0,
|
||||
span ? span->mBStartLeading : 0);
|
||||
#endif
|
||||
break;
|
||||
@ -1539,7 +1536,7 @@ nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
|
||||
}
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) = -aDistanceFromStart + aLineBSize -
|
||||
pfd->mMargin.BEnd(frameWM) - pfd->mBounds.BSize(lineWM);
|
||||
pfd->mMargin.BEnd(lineWM) - pfd->mBounds.BSize(lineWM);
|
||||
}
|
||||
pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerWidth);
|
||||
#ifdef NOISY_BLOCKDIR_ALIGN
|
||||
@ -1598,7 +1595,6 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
// - it has a prev-in-flow
|
||||
// - it has no next in flow
|
||||
// - it's zero sized
|
||||
WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode();
|
||||
WritingMode lineWM = mRootSpan->mWritingMode;
|
||||
bool emptyContinuation = psd != mRootSpan &&
|
||||
spanFrame->GetPrevInFlow() && !spanFrame->GetNextInFlow() &&
|
||||
@ -1616,14 +1612,14 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
if (psd != mRootSpan) {
|
||||
WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode();
|
||||
printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
|
||||
spanFramePFD->mBorderPadding.Top(frameWM),
|
||||
spanFramePFD->mBorderPadding.Right(frameWM),
|
||||
spanFramePFD->mBorderPadding.Bottom(frameWM),
|
||||
spanFramePFD->mBorderPadding.Left(frameWM),
|
||||
spanFramePFD->mMargin.Top(frameWM),
|
||||
spanFramePFD->mMargin.Right(frameWM),
|
||||
spanFramePFD->mMargin.Bottom(frameWM),
|
||||
spanFramePFD->mMargin.Left(frameWM));
|
||||
spanFramePFD->mBorderPadding.Top(lineWM),
|
||||
spanFramePFD->mBorderPadding.Right(lineWM),
|
||||
spanFramePFD->mBorderPadding.Bottom(lineWM),
|
||||
spanFramePFD->mBorderPadding.Left(lineWM),
|
||||
spanFramePFD->mMargin.Top(lineWM),
|
||||
spanFramePFD->mMargin.Right(lineWM),
|
||||
spanFramePFD->mMargin.Bottom(lineWM),
|
||||
spanFramePFD->mMargin.Left(lineWM));
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
@ -1725,8 +1721,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
mBlockReflowState->ComputedHeight(),
|
||||
inflation);
|
||||
nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
|
||||
spanFramePFD->mBorderPadding.BStart(frameWM) -
|
||||
spanFramePFD->mBorderPadding.BEnd(frameWM);
|
||||
spanFramePFD->mBorderPadding.BStartEnd(lineWM);
|
||||
|
||||
// Special-case for a ::first-letter frame, set the line height to
|
||||
// the frame block size if the user has left line-height == normal
|
||||
@ -1759,7 +1754,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
// If there are child frames in this span that stick out of this area
|
||||
// then the minBCoord and maxBCoord are updated by the amount of logical
|
||||
// blockSize that is outside this range.
|
||||
minBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) -
|
||||
minBCoord = spanFramePFD->mBorderPadding.BStart(lineWM) -
|
||||
psd->mBStartLeading;
|
||||
maxBCoord = minBCoord + psd->mLogicalBSize;
|
||||
}
|
||||
@ -1776,8 +1771,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
|
||||
baselineBCoord, psd->mLogicalBSize, psd->mBStartLeading,
|
||||
spanFramePFD->mBounds.BSize(lineWM),
|
||||
spanFramePFD->mBorderPadding.Top(frameWM),
|
||||
spanFramePFD->mBorderPadding.Bottom(frameWM),
|
||||
spanFramePFD->mBorderPadding.Top(lineWM),
|
||||
spanFramePFD->mBorderPadding.Bottom(lineWM),
|
||||
zeroEffectiveSpanBox ? "yes" : "no");
|
||||
#endif
|
||||
}
|
||||
@ -1787,7 +1782,6 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
PerFrameData* pfd = psd->mFirstFrame;
|
||||
while (nullptr != pfd) {
|
||||
nsIFrame* frame = pfd->mFrame;
|
||||
WritingMode frameWM = frame->GetWritingMode();
|
||||
|
||||
// sanity check (see bug 105168, non-reproducible crashes from null frame)
|
||||
NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad");
|
||||
@ -1807,7 +1801,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
// For other elements the logical block size is the same as the
|
||||
// frame's block size plus its margins.
|
||||
logicalBSize = pfd->mBounds.BSize(lineWM) +
|
||||
pfd->mMargin.BStartEnd(frameWM);
|
||||
pfd->mMargin.BStartEnd(lineWM);
|
||||
if (logicalBSize < 0 &&
|
||||
mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
|
||||
pfd->mAscent -= logicalBSize;
|
||||
@ -1915,7 +1909,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord -
|
||||
(parentXHeight + logicalBSize)/2 +
|
||||
pfd->mMargin.BStart(frameWM);
|
||||
pfd->mMargin.BStart(lineWM);
|
||||
}
|
||||
pfd->mBlockDirAlign = VALIGN_OTHER;
|
||||
break;
|
||||
@ -1930,11 +1924,11 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
nscoord parentAscent = fm->MaxAscent();
|
||||
if (frameSpan) {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent -
|
||||
pfd->mBorderPadding.BStart(frameWM) + frameSpan->mBStartLeading;
|
||||
pfd->mBorderPadding.BStart(lineWM) + frameSpan->mBStartLeading;
|
||||
}
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent +
|
||||
pfd->mMargin.BStart(frameWM);
|
||||
pfd->mMargin.BStart(lineWM);
|
||||
}
|
||||
pfd->mBlockDirAlign = VALIGN_OTHER;
|
||||
break;
|
||||
@ -1948,13 +1942,13 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
if (frameSpan) {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
|
||||
pfd->mBounds.BSize(lineWM) +
|
||||
pfd->mBorderPadding.BEnd(frameWM) -
|
||||
pfd->mBorderPadding.BEnd(lineWM) -
|
||||
frameSpan->mBEndLeading;
|
||||
}
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
|
||||
pfd->mBounds.BSize(lineWM) -
|
||||
pfd->mMargin.BEnd(frameWM);
|
||||
pfd->mMargin.BEnd(lineWM);
|
||||
}
|
||||
pfd->mBlockDirAlign = VALIGN_OTHER;
|
||||
break;
|
||||
@ -1969,7 +1963,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
}
|
||||
else {
|
||||
pfd->mBounds.BStart(lineWM) = baselineBCoord - logicalBSize/2 +
|
||||
pfd->mMargin.BStart(frameWM);
|
||||
pfd->mMargin.BStart(lineWM);
|
||||
}
|
||||
pfd->mBlockDirAlign = VALIGN_OTHER;
|
||||
break;
|
||||
@ -2035,7 +2029,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
}
|
||||
else {
|
||||
blockStart = pfd->mBounds.BStart(lineWM) -
|
||||
pfd->mMargin.BStart(frameWM);
|
||||
pfd->mMargin.BStart(lineWM);
|
||||
blockEnd = blockStart + logicalBSize;
|
||||
}
|
||||
if (!preMode &&
|
||||
@ -2054,8 +2048,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
#ifdef NOISY_BLOCKDIR_ALIGN
|
||||
printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n",
|
||||
pfd->mAscent, pfd->mBounds.BSize(lineWM),
|
||||
pfd->mBorderPadding.Top(frameWM),
|
||||
pfd->mBorderPadding.Bottom(frameWM),
|
||||
pfd->mBorderPadding.Top(lineWM),
|
||||
pfd->mBorderPadding.Bottom(lineWM),
|
||||
logicalBSize,
|
||||
frameSpan ? frameSpan->mBStartLeading : 0,
|
||||
pfd->mBounds.BStart(lineWM), minBCoord, maxBCoord);
|
||||
@ -2104,7 +2098,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
#endif
|
||||
nscoord minimumLineBSize = mMinLineBSize;
|
||||
nscoord blockStart =
|
||||
-nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize);
|
||||
-nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize,
|
||||
lineWM.IsLineInverted());
|
||||
nscoord blockEnd = blockStart + minimumLineBSize;
|
||||
|
||||
if (blockStart < minBCoord) minBCoord = blockStart;
|
||||
@ -2146,8 +2141,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
spanFramePFD->mAscent,
|
||||
psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
|
||||
#endif
|
||||
nscoord goodMinBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) -
|
||||
psd->mBStartLeading;
|
||||
nscoord goodMinBCoord =
|
||||
spanFramePFD->mBorderPadding.BStart(lineWM) - psd->mBStartLeading;
|
||||
nscoord goodMaxBCoord = goodMinBCoord + psd->mLogicalBSize;
|
||||
|
||||
// For cases like the one in bug 714519 (text-decoration placement
|
||||
|
@ -385,9 +385,9 @@ protected:
|
||||
nsOverflowAreas mOverflowAreas;
|
||||
|
||||
// From reflow-state
|
||||
mozilla::LogicalMargin mMargin;
|
||||
mozilla::LogicalMargin mBorderPadding;
|
||||
mozilla::LogicalMargin mOffsets;
|
||||
mozilla::LogicalMargin mMargin; // in *line* writing mode
|
||||
mozilla::LogicalMargin mBorderPadding; // in *line* writing mode
|
||||
mozilla::LogicalMargin mOffsets; // in *frame* writing mode
|
||||
|
||||
// state for text justification
|
||||
mozilla::JustificationInfo mJustificationInfo;
|
||||
@ -537,6 +537,7 @@ protected:
|
||||
// frame, if any
|
||||
nscoord mTrimmableISize;
|
||||
|
||||
// Physical width. Use only for physical <-> logical coordinate conversion.
|
||||
nscoord mContainerWidth;
|
||||
|
||||
bool mFirstLetterStyleOK : 1;
|
||||
|
@ -1790,13 +1790,13 @@ GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextF
|
||||
}
|
||||
|
||||
static gfxFont::Metrics
|
||||
GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVertical)
|
||||
GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVerticalMetrics)
|
||||
{
|
||||
if (!aFontGroup)
|
||||
return gfxFont::Metrics();
|
||||
gfxFont* font = aFontGroup->GetFirstValidFont();
|
||||
return font->GetMetrics(aVertical ? gfxFont::eVertical :
|
||||
gfxFont::eHorizontal);
|
||||
return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical
|
||||
: gfxFont::eHorizontal);
|
||||
}
|
||||
|
||||
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
|
||||
@ -3161,7 +3161,7 @@ ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun)
|
||||
// textruns do
|
||||
gfxFloat spaceWidthAppUnits =
|
||||
NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
|
||||
aTextRun->IsVertical()).spaceWidth *
|
||||
aTextRun->UseCenterBaseline()).spaceWidth *
|
||||
aTextRun->GetAppUnitsPerDevUnit());
|
||||
return textStyle->mTabSize * spaceWidthAppUnits;
|
||||
}
|
||||
@ -4768,15 +4768,16 @@ nsTextFrame::GetTextDecorations(
|
||||
bool useOverride = false;
|
||||
nscolor overrideColor = NS_RGBA(0, 0, 0, 0);
|
||||
|
||||
// frameTopOffset represents the offset to f's top from our baseline in our
|
||||
// frameBStartOffset represents the offset to f's BStart from our baseline in our
|
||||
// coordinate space
|
||||
// baselineOffset represents the offset from our baseline to f's baseline or
|
||||
// the nearest block's baseline, in our coordinate space, whichever is closest
|
||||
// during the particular iteration
|
||||
nscoord frameTopOffset = mAscent,
|
||||
nscoord frameBStartOffset = mAscent,
|
||||
baselineOffset = 0;
|
||||
|
||||
bool nearestBlockFound = false;
|
||||
bool vertical = GetWritingMode().IsVertical();
|
||||
|
||||
for (nsIFrame* f = this, *fChild = nullptr;
|
||||
f;
|
||||
@ -4819,18 +4820,20 @@ nsTextFrame::GetTextDecorations(
|
||||
const nscoord lineBaselineOffset = LazyGetLineBaselineOffset(fChild,
|
||||
fBlock);
|
||||
|
||||
baselineOffset =
|
||||
frameTopOffset - fChild->GetNormalPosition().y - lineBaselineOffset;
|
||||
baselineOffset = frameBStartOffset - lineBaselineOffset -
|
||||
(vertical ? fChild->GetNormalPosition().x
|
||||
: fChild->GetNormalPosition().y);
|
||||
}
|
||||
}
|
||||
else if (!nearestBlockFound) {
|
||||
// use a dummy WritingMode, because nsTextFrame::GetLogicalBaseLine
|
||||
// doesn't use it anyway
|
||||
baselineOffset = frameTopOffset - f->GetLogicalBaseline(WritingMode());
|
||||
baselineOffset = frameBStartOffset - f->GetLogicalBaseline(WritingMode());
|
||||
}
|
||||
|
||||
nearestBlockFound = nearestBlockFound || firstBlock;
|
||||
frameTopOffset += f->GetNormalPosition().y;
|
||||
frameBStartOffset +=
|
||||
vertical ? f->GetNormalPosition().x : f->GetNormalPosition().y;
|
||||
|
||||
const uint8_t style = styleText->GetDecorationStyle();
|
||||
if (textDecorations) {
|
||||
@ -4915,7 +4918,9 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
nsRect shadowRect =
|
||||
nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
|
||||
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
|
||||
bool vertical = mTextRun->IsVertical();
|
||||
bool verticalRun = mTextRun->IsVertical();
|
||||
bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
|
||||
bool inverted = GetWritingMode().IsLineInverted();
|
||||
|
||||
if (IsFloatingFirstLetterChild()) {
|
||||
// The underline/overline drawable area must be contained in the overflow
|
||||
@ -4932,11 +4937,13 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
nsFontMetrics* fontMetrics = aProvider.GetFontMetrics();
|
||||
nscoord underlineOffset, underlineSize;
|
||||
fontMetrics->GetUnderline(underlineOffset, underlineSize);
|
||||
nscoord maxAscent = fontMetrics->MaxAscent();
|
||||
nscoord maxAscent = inverted ? fontMetrics->MaxDescent()
|
||||
: fontMetrics->MaxAscent();
|
||||
|
||||
gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
|
||||
gfxFloat gfxWidth =
|
||||
(vertical ? aVisualOverflowRect->height : aVisualOverflowRect->width) /
|
||||
(verticalRun ? aVisualOverflowRect->height
|
||||
: aVisualOverflowRect->width) /
|
||||
appUnitsPerDevUnit;
|
||||
gfxFloat gfxAscent = gfxFloat(mAscent) / appUnitsPerDevUnit;
|
||||
gfxFloat gfxMaxAscent = maxAscent / appUnitsPerDevUnit;
|
||||
@ -4945,11 +4952,11 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
nsRect underlineRect =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext,
|
||||
gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxUnderlineOffset,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, vertical);
|
||||
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, verticalRun);
|
||||
nsRect overlineRect =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext,
|
||||
gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxMaxAscent,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, vertical);
|
||||
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, verticalRun);
|
||||
|
||||
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
|
||||
aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
|
||||
@ -4969,11 +4976,16 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
nscoord inflationMinFontSize =
|
||||
nsLayoutUtils::InflationMinFontSizeFor(aBlock);
|
||||
|
||||
const nscoord measure = vertical ? GetSize().height : GetSize().width;
|
||||
const nscoord measure = verticalRun ? GetSize().height : GetSize().width;
|
||||
const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
|
||||
gfxWidth = measure / appUnitsPerDevUnit,
|
||||
ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
|
||||
nscoord top(nscoord_MAX), bottom(nscoord_MIN);
|
||||
gfxWidth = measure / appUnitsPerDevUnit;
|
||||
gfxFloat ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
|
||||
const WritingMode wm = GetWritingMode();
|
||||
if (wm.IsVerticalRL()) {
|
||||
ascent = -ascent;
|
||||
}
|
||||
|
||||
nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN);
|
||||
// Below we loop through all text decorations and compute the rectangle
|
||||
// containing all of them, in this frame's coordinate space
|
||||
for (uint32_t i = 0; i < textDecs.mUnderlines.Length(); ++i) {
|
||||
@ -4990,18 +5002,23 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
const nsRect decorationRect =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext,
|
||||
gfxSize(gfxWidth, metrics.underlineSize),
|
||||
ascent, metrics.underlineOffset,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle,
|
||||
vertical) +
|
||||
verticalRun) +
|
||||
nsPoint(0, -dec.mBaselineOffset);
|
||||
|
||||
top = std::min(decorationRect.y, top);
|
||||
bottom = std::max(decorationRect.YMost(), bottom);
|
||||
if (verticalRun) {
|
||||
topOrLeft = std::min(decorationRect.x, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
|
||||
} else {
|
||||
topOrLeft = std::min(decorationRect.y, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < textDecs.mOverlines.Length(); ++i) {
|
||||
const LineDecoration& dec = textDecs.mOverlines[i];
|
||||
@ -5017,18 +5034,23 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
const nsRect decorationRect =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext,
|
||||
gfxSize(gfxWidth, metrics.underlineSize),
|
||||
ascent, metrics.maxAscent,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle,
|
||||
vertical) +
|
||||
verticalRun) +
|
||||
nsPoint(0, -dec.mBaselineOffset);
|
||||
|
||||
top = std::min(decorationRect.y, top);
|
||||
bottom = std::max(decorationRect.YMost(), bottom);
|
||||
if (verticalRun) {
|
||||
topOrLeft = std::min(decorationRect.x, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
|
||||
} else {
|
||||
topOrLeft = std::min(decorationRect.y, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < textDecs.mStrikes.Length(); ++i) {
|
||||
const LineDecoration& dec = textDecs.mStrikes[i];
|
||||
@ -5044,23 +5066,29 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
const nsRect decorationRect =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext,
|
||||
gfxSize(gfxWidth, metrics.strikeoutSize),
|
||||
ascent, metrics.strikeoutOffset,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle,
|
||||
vertical) +
|
||||
verticalRun) +
|
||||
nsPoint(0, -dec.mBaselineOffset);
|
||||
top = std::min(decorationRect.y, top);
|
||||
bottom = std::max(decorationRect.YMost(), bottom);
|
||||
|
||||
if (verticalRun) {
|
||||
topOrLeft = std::min(decorationRect.x, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
|
||||
} else {
|
||||
topOrLeft = std::min(decorationRect.y, topOrLeft);
|
||||
bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
|
||||
}
|
||||
}
|
||||
|
||||
aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
|
||||
vertical ?
|
||||
nsRect(top, 0, bottom - top, measure) :
|
||||
nsRect(0, top, measure, bottom - top));
|
||||
aVisualOverflowRect->UnionRect(
|
||||
*aVisualOverflowRect,
|
||||
verticalRun ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
|
||||
: nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
|
||||
}
|
||||
}
|
||||
// When this frame is not selected, the text-decoration area must be in
|
||||
@ -5720,19 +5748,20 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
}
|
||||
|
||||
gfxFont* firstFont = aProvider.GetFontGroup()->GetFirstValidFont();
|
||||
bool vertical = mTextRun->IsVertical();
|
||||
bool verticalRun = mTextRun->IsVertical();
|
||||
bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
|
||||
gfxFont::Metrics
|
||||
decorationMetrics(firstFont->GetMetrics(vertical ?
|
||||
decorationMetrics(firstFont->GetMetrics(useVerticalMetrics ?
|
||||
gfxFont::eVertical : gfxFont::eHorizontal));
|
||||
if (!vertical) {
|
||||
if (!useVerticalMetrics) {
|
||||
// The potential adjustment from using gfxFontGroup::GetUnderlineOffset
|
||||
// is only valid for horizontal text.
|
||||
// is only valid for horizontal font metrics.
|
||||
decorationMetrics.underlineOffset =
|
||||
aProvider.GetFontGroup()->GetUnderlineOffset();
|
||||
}
|
||||
|
||||
gfxFloat startIOffset =
|
||||
vertical ? aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x;
|
||||
verticalRun ? aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x;
|
||||
SelectionIterator iterator(selectedChars, aContentOffset, aContentLength,
|
||||
aProvider, mTextRun, startIOffset);
|
||||
gfxFloat iOffset, hyphenWidth;
|
||||
@ -5740,7 +5769,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
int32_t app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
|
||||
// XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
|
||||
gfxPoint pt;
|
||||
if (vertical) {
|
||||
if (verticalRun) {
|
||||
pt.x = (aTextBaselinePt.x - mAscent) / app;
|
||||
} else {
|
||||
pt.y = (aTextBaselinePt.y - mAscent) / app;
|
||||
@ -5754,7 +5783,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
gfxFloat advance = hyphenWidth +
|
||||
mTextRun->GetAdvanceWidth(offset, length, &aProvider);
|
||||
if (type == aSelectionType) {
|
||||
if (vertical) {
|
||||
if (verticalRun) {
|
||||
pt.y = (aFramePt.y + iOffset -
|
||||
(mTextRun->IsRightToLeft() ? advance : 0)) / app;
|
||||
} else {
|
||||
@ -5766,7 +5795,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType, this,
|
||||
aTextPaintStyle, selectedStyle, pt, xInFrame,
|
||||
width, mAscent / app, decorationMetrics,
|
||||
aCallbacks, vertical);
|
||||
aCallbacks, verticalRun);
|
||||
}
|
||||
iterator.UpdateWithAdvance(advance);
|
||||
}
|
||||
@ -6010,13 +6039,15 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
|
||||
|
||||
gfxContext* ctx = aRenderingContext->ThebesContext();
|
||||
const bool rtl = mTextRun->IsRightToLeft();
|
||||
const bool vertical = mTextRun->IsVertical();
|
||||
const bool verticalRun = mTextRun->IsVertical();
|
||||
WritingMode wm = GetWritingMode();
|
||||
const nscoord frameWidth = GetSize().width;
|
||||
gfxPoint framePt(aPt.x, aPt.y);
|
||||
gfxPoint textBaselinePt;
|
||||
if (vertical) {
|
||||
textBaselinePt = gfxPoint(aPt.x + mAscent,
|
||||
rtl ? gfxFloat(aPt.y + GetSize().height) : aPt.y);
|
||||
if (verticalRun) {
|
||||
textBaselinePt = // XXX sideways-left will need different handling here
|
||||
gfxPoint(aPt.x + (wm.IsVerticalLR() ? mAscent : frameWidth - mAscent),
|
||||
rtl ? aPt.y + GetSize().height : aPt.y);
|
||||
} else {
|
||||
textBaselinePt = gfxPoint(rtl ? gfxFloat(aPt.x + frameWidth) : framePt.x,
|
||||
nsLayoutUtils::GetSnappedBaselineY(this, ctx, aPt.y, mAscent));
|
||||
@ -6028,7 +6059,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
|
||||
&startOffset, &maxLength, &snappedLeftEdge, &snappedRightEdge)) {
|
||||
return;
|
||||
}
|
||||
if (vertical) {
|
||||
if (verticalRun) {
|
||||
textBaselinePt.y += rtl ? -snappedRightEdge : snappedLeftEdge;
|
||||
} else {
|
||||
textBaselinePt.x += rtl ? -snappedRightEdge : snappedLeftEdge;
|
||||
@ -6153,17 +6184,43 @@ nsTextFrame::DrawTextRunAndDecorations(
|
||||
nsTextFrame::DrawPathCallbacks* aCallbacks)
|
||||
{
|
||||
const gfxFloat app = aTextStyle.PresContext()->AppUnitsPerDevPixel();
|
||||
bool vertical = mTextRun->IsVertical();
|
||||
bool verticalRun = mTextRun->IsVertical();
|
||||
bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
|
||||
|
||||
// XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
|
||||
nscoord x = NSToCoordRound(aFramePt.x);
|
||||
nscoord width = vertical ? GetRect().height : GetRect().width;
|
||||
aClipEdges.Intersect(&x, &width);
|
||||
nscoord y = NSToCoordRound(aFramePt.y);
|
||||
|
||||
gfxPoint decPt(x / app, 0);
|
||||
gfxSize decSize(width / app, 0);
|
||||
const gfxFloat ascent = gfxFloat(mAscent) / app;
|
||||
const gfxFloat frameTop = aFramePt.y;
|
||||
// 'measure' here is textrun-relative, so for a horizontal run it's the
|
||||
// width, while for a vertical run it's the height of the decoration
|
||||
const nsSize frameSize = GetSize();
|
||||
nscoord measure = verticalRun ? frameSize.height : frameSize.width;
|
||||
|
||||
// XXX todo: probably should have a vertical version of this...
|
||||
if (!verticalRun) {
|
||||
aClipEdges.Intersect(&x, &measure);
|
||||
}
|
||||
|
||||
// decPt is the physical point where the decoration is to be drawn,
|
||||
// relative to the frame; one of its coordinates will be updated below.
|
||||
gfxPoint decPt(x / app, y / app);
|
||||
gfxFloat& bCoord = verticalRun ? decPt.x : decPt.y;
|
||||
|
||||
// decSize is a textrun-relative size, so its 'width' field is actually
|
||||
// the run-relative measure, and 'height' will be the line thickness
|
||||
gfxSize decSize(measure / app, 0);
|
||||
gfxFloat ascent = gfxFloat(mAscent) / app;
|
||||
|
||||
// The starting edge of the frame in block direction
|
||||
gfxFloat frameBStart = verticalRun ? aFramePt.x : aFramePt.y;
|
||||
|
||||
// In vertical-rl mode, block coordinates are measured from the right,
|
||||
// so we need to adjust here.
|
||||
const WritingMode wm = GetWritingMode();
|
||||
if (wm.IsVerticalRL()) {
|
||||
frameBStart += frameSize.width;
|
||||
ascent = -ascent;
|
||||
}
|
||||
|
||||
gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
|
||||
aDirtyRect.Width() / app, aDirtyRect.Height() / app);
|
||||
@ -6182,15 +6239,15 @@ nsTextFrame::DrawTextRunAndDecorations(
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
decSize.height = metrics.underlineSize;
|
||||
decPt.y = (frameTop - dec.mBaselineOffset) / app;
|
||||
bCoord = (frameBStart - dec.mBaselineOffset) / app;
|
||||
|
||||
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
|
||||
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
|
||||
metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
dec.mStyle, eNormalDecoration, aCallbacks, vertical);
|
||||
dec.mStyle, eNormalDecoration, aCallbacks, verticalRun);
|
||||
}
|
||||
// Overlines
|
||||
for (uint32_t i = aDecorations.mOverlines.Length(); i-- > 0; ) {
|
||||
@ -6203,15 +6260,15 @@ nsTextFrame::DrawTextRunAndDecorations(
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
decSize.height = metrics.underlineSize;
|
||||
decPt.y = (frameTop - dec.mBaselineOffset) / app;
|
||||
bCoord = (frameBStart - dec.mBaselineOffset) / app;
|
||||
|
||||
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
|
||||
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
|
||||
metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
|
||||
eNormalDecoration, aCallbacks, vertical);
|
||||
eNormalDecoration, aCallbacks, verticalRun);
|
||||
}
|
||||
|
||||
// CSS 2.1 mandates that text be painted after over/underlines, and *then*
|
||||
@ -6230,15 +6287,15 @@ nsTextFrame::DrawTextRunAndDecorations(
|
||||
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
|
||||
const gfxFont::Metrics metrics =
|
||||
GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
|
||||
vertical);
|
||||
useVerticalMetrics);
|
||||
|
||||
decSize.height = metrics.strikeoutSize;
|
||||
decPt.y = (frameTop - dec.mBaselineOffset) / app;
|
||||
bCoord = (frameBStart - dec.mBaselineOffset) / app;
|
||||
|
||||
PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
|
||||
aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
|
||||
metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
|
||||
dec.mStyle, eNormalDecoration, aCallbacks, vertical);
|
||||
dec.mStyle, eNormalDecoration, aCallbacks, verticalRun);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6437,9 +6494,12 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
|
||||
GetFontSizeInflation());
|
||||
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
|
||||
gfxFont* firstFont = fontGroup->GetFirstValidFont();
|
||||
bool vertical = GetWritingMode().IsVertical();
|
||||
WritingMode wm = GetWritingMode();
|
||||
bool verticalRun = wm.IsVertical();
|
||||
bool useVerticalMetrics = verticalRun && !wm.IsSideways();
|
||||
const gfxFont::Metrics& metrics =
|
||||
firstFont->GetMetrics(vertical ? gfxFont::eVertical : gfxFont::eHorizontal);
|
||||
firstFont->GetMetrics(useVerticalMetrics ? gfxFont::eVertical
|
||||
: gfxFont::eHorizontal);
|
||||
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
||||
gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
|
||||
gfxFloat descentLimit =
|
||||
@ -6484,7 +6544,7 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
|
||||
decorationArea =
|
||||
nsCSSRendering::GetTextDecorationRect(aPresContext, size,
|
||||
ascent, underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
style, vertical, descentLimit);
|
||||
style, verticalRun, descentLimit);
|
||||
aRect.UnionRect(aRect, decorationArea);
|
||||
}
|
||||
DestroySelectionDetails(details);
|
||||
@ -7596,7 +7656,11 @@ nsTextFrame::ComputeTightBounds(gfxContext* aContext) const
|
||||
aContext, &provider);
|
||||
// mAscent should be the same as metrics.mAscent, but it's what we use to
|
||||
// paint so that's the one we'll use.
|
||||
nsRect boundingBox = RoundOut(metrics.mBoundingBox) + nsPoint(0, mAscent);
|
||||
nsRect boundingBox = RoundOut(metrics.mBoundingBox);
|
||||
if (GetWritingMode().IsLineInverted()) {
|
||||
boundingBox.y = -boundingBox.YMost();
|
||||
}
|
||||
boundingBox += nsPoint(0, mAscent);
|
||||
if (mTextRun->IsVertical()) {
|
||||
// Swap line-relative textMetrics dimensions to physical coordinates.
|
||||
Swap(boundingBox.x, boundingBox.y);
|
||||
@ -8281,9 +8345,15 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
finalSize.BSize(wm) = 0;
|
||||
} else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
|
||||
// Use actual text metrics for floating first letter frame.
|
||||
aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mAscent));
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() +
|
||||
NSToCoordCeil(textMetrics.mDescent);
|
||||
if (wm.IsLineInverted()) {
|
||||
aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mDescent));
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() +
|
||||
NSToCoordCeil(textMetrics.mAscent);
|
||||
} else {
|
||||
aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mAscent));
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() +
|
||||
NSToCoordCeil(textMetrics.mDescent);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, ascent should contain the overline drawable area.
|
||||
// And also descent should contain the underline drawable area.
|
||||
@ -8291,9 +8361,15 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
nsFontMetrics* fm = provider.GetFontMetrics();
|
||||
nscoord fontAscent = fm->MaxAscent();
|
||||
nscoord fontDescent = fm->MaxDescent();
|
||||
aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
|
||||
nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
|
||||
if (wm.IsLineInverted()) {
|
||||
aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent));
|
||||
nscoord descent = std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent);
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
|
||||
} else {
|
||||
aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
|
||||
nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
|
||||
finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
|
||||
}
|
||||
}
|
||||
aMetrics.SetSize(wm, finalSize);
|
||||
|
||||
@ -8306,13 +8382,17 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
mAscent = aMetrics.BlockStartAscent();
|
||||
|
||||
// Handle text that runs outside its normal bounds.
|
||||
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox) + nsPoint(0, mAscent);
|
||||
aMetrics.SetOverflowAreasToDesiredBounds();
|
||||
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
|
||||
if (wm.IsLineInverted()) {
|
||||
boundingBox.y = -boundingBox.YMost();
|
||||
}
|
||||
boundingBox += nsPoint(0, mAscent);
|
||||
if (mTextRun->IsVertical()) {
|
||||
// Swap line-relative textMetrics dimensions to physical coordinates.
|
||||
Swap(boundingBox.x, boundingBox.y);
|
||||
Swap(boundingBox.width, boundingBox.height);
|
||||
}
|
||||
aMetrics.SetOverflowAreasToDesiredBounds();
|
||||
aMetrics.VisualOverflow().UnionRect(aMetrics.VisualOverflow(), boundingBox);
|
||||
|
||||
// When we have text decorations, we don't need to compute their overflow now
|
||||
@ -8528,18 +8608,23 @@ nsTextFrame::RecomputeOverflow(const nsHTMLReflowState& aBlockReflowState)
|
||||
ComputeTransformedLength(provider),
|
||||
gfxFont::LOOSE_INK_EXTENTS, nullptr,
|
||||
&provider);
|
||||
nsRect &vis = result.VisualOverflow();
|
||||
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox) + nsPoint(0, mAscent);
|
||||
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
|
||||
if (GetWritingMode().IsLineInverted()) {
|
||||
boundingBox.y = -boundingBox.YMost();
|
||||
}
|
||||
boundingBox += nsPoint(0, mAscent);
|
||||
if (mTextRun->IsVertical()) {
|
||||
// Swap line-relative textMetrics dimensions to physical coordinates.
|
||||
Swap(boundingBox.x, boundingBox.y);
|
||||
Swap(boundingBox.width, boundingBox.height);
|
||||
}
|
||||
nsRect &vis = result.VisualOverflow();
|
||||
vis.UnionRect(vis, boundingBox);
|
||||
UnionAdditionalOverflow(PresContext(), aBlockReflowState.frame, provider,
|
||||
&vis, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
static char16_t TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
|
||||
uint32_t aSkippedOffset, char16_t aChar)
|
||||
{
|
||||
|
24
layout/reftests/writing-mode/1088025-1-ref.html
Normal file
24
layout/reftests/writing-mode/1088025-1-ref.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1083892</title>
|
||||
<style>
|
||||
div {
|
||||
width:300px;
|
||||
height:300px;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
writing-mode:vertical-rl;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
This is the <b><i>first</i> paragraph</b>. It's long enough to wrap onto multiple lines.<br>
|
||||
<b>Paragraph <i>two</i></b>.<br>
|
||||
<b><i>Third and final</i> paragraph</b> of this simple testcase. That's all, folks!
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
27
layout/reftests/writing-mode/1088025-1.html
Normal file
27
layout/reftests/writing-mode/1088025-1.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1083892</title>
|
||||
<style>
|
||||
div {
|
||||
width:300px;
|
||||
height:300px;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
writing-mode:vertical-rl;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<p>This is the <b><i>first</i> paragraph</b>. It's long enough to wrap onto multiple lines.
|
||||
<p><b>Paragraph <i>two</i></b>.
|
||||
<p><b><i>Third and final</i> paragraph</b> of this simple testcase. That's all, folks!
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
30
layout/reftests/writing-mode/1089388-1-ref.html
Normal file
30
layout/reftests/writing-mode/1089388-1-ref.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background: #ddd;
|
||||
margin: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="v-lr">
|
||||
First part of the block.
|
||||
<i id="test">New text inserted by script, to cause a reflow that slides the following lines.</i>
|
||||
We will insert enough new content that it wraps onto additional lines.
|
||||
<br><br>
|
||||
Here is some more text that follows a forced break.
|
||||
Observe what happens to it when text is added earlier.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
39
layout/reftests/writing-mode/1089388-1.html
Normal file
39
layout/reftests/writing-mode/1089388-1.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background: #ddd;
|
||||
margin: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function doTest() {
|
||||
document.getElementById("test").textContent =
|
||||
"New text inserted by script, to cause a reflow that slides the following lines.";
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="doTest()">
|
||||
|
||||
<div class="v-lr">
|
||||
First part of the block.
|
||||
<i id="test"></i>
|
||||
We will insert enough new content that it wraps onto additional lines.
|
||||
<br><br>
|
||||
Here is some more text that follows a forced break.
|
||||
Observe what happens to it when text is added earlier.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
30
layout/reftests/writing-mode/1089388-2-ref.html
Normal file
30
layout/reftests/writing-mode/1089388-2-ref.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background: #ddd;
|
||||
margin: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="v-rl">
|
||||
First part of the block.
|
||||
<i id="test">New text inserted by script, to cause a reflow that slides the following lines.</i>
|
||||
We will insert enough new content that it wraps onto additional lines.
|
||||
<br><br>
|
||||
Here is some more text that follows a forced break.
|
||||
Observe what happens to it when text is added earlier.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
39
layout/reftests/writing-mode/1089388-2.html
Normal file
39
layout/reftests/writing-mode/1089388-2.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background: #ddd;
|
||||
margin: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function doTest() {
|
||||
document.getElementById("test").textContent =
|
||||
"New text inserted by script, to cause a reflow that slides the following lines.";
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="doTest()">
|
||||
|
||||
<div class="v-rl">
|
||||
First part of the block.
|
||||
<i id="test"></i>
|
||||
We will insert enough new content that it wraps onto additional lines.
|
||||
<br><br>
|
||||
Here is some more text that follows a forced break.
|
||||
Observe what happens to it when text is added earlier.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
37
layout/reftests/writing-mode/1090159-1-ref.html
Normal file
37
layout/reftests/writing-mode/1090159-1-ref.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.h { writing-mode:horizontal-tb; }
|
||||
.v-lr { writing-mode:vertical-lr; text-orientation:sideways-right; }
|
||||
.v-rl { writing-mode:vertical-rl; text-orientation:sideways-right; }
|
||||
|
||||
div {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border: 1px solid red;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
font: 16px monospace;
|
||||
line-height: 32px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="h">
|
||||
你好吗? hello
|
||||
</div>
|
||||
|
||||
<div class="v-lr">
|
||||
你好吗? hello
|
||||
</div>
|
||||
|
||||
<div class="v-rl">
|
||||
你好吗? hello
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
37
layout/reftests/writing-mode/1090159-1.html
Normal file
37
layout/reftests/writing-mode/1090159-1.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.h { writing-mode:horizontal-tb; }
|
||||
.v-lr { writing-mode:vertical-lr; text-orientation:sideways-right; }
|
||||
.v-rl { writing-mode:vertical-rl; text-orientation:sideways-right; }
|
||||
|
||||
div {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border: 1px solid red;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
font: 16px monospace;
|
||||
line-height: 32px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="h">
|
||||
你好吗? <span>hello</span>
|
||||
</div>
|
||||
|
||||
<div class="v-lr">
|
||||
你好吗? <span>hello</span>
|
||||
</div>
|
||||
|
||||
<div class="v-rl">
|
||||
你好吗? <span>hello</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
41
layout/reftests/writing-mode/1090168-1-notref.html
Normal file
41
layout/reftests/writing-mode/1090168-1-notref.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with orientation:sideways-right
|
||||
// test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:sideways-right', 0);
|
||||
|
||||
// Reference: horizontal text with 90° rotation
|
||||
// test(100, 50, 'Hello', 'writing-mode:horizontal-tb', Math.PI/2);
|
||||
|
||||
// Non-reference: vertical text with orientation:mixed
|
||||
test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:mixed', 0);
|
||||
|
||||
</script>
|
41
layout/reftests/writing-mode/1090168-1-ref.html
Normal file
41
layout/reftests/writing-mode/1090168-1-ref.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with orientation:sideways-right
|
||||
// test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:sideways-right', 0);
|
||||
|
||||
// Reference: horizontal text with 90° rotation
|
||||
test(100, 50, 'Hello', 'writing-mode:horizontal-tb', Math.PI/2);
|
||||
|
||||
// Non-reference: vertical text with orientation:mixed
|
||||
// test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:mixed', 0);
|
||||
|
||||
</script>
|
41
layout/reftests/writing-mode/1090168-1.html
Normal file
41
layout/reftests/writing-mode/1090168-1.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with orientation:sideways-right
|
||||
test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:sideways-right', 0);
|
||||
|
||||
// Reference: horizontal text with 90° rotation
|
||||
// test(100, 50, 'Hello', 'writing-mode:horizontal-tb', Math.PI/2);
|
||||
|
||||
// Non-reference: vertical text with orientation:mixed
|
||||
// test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:mixed', 0);
|
||||
|
||||
</script>
|
41
layout/reftests/writing-mode/1090168-2-ref.html
Normal file
41
layout/reftests/writing-mode/1090168-2-ref.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation, baseline) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
if (baseline != '') {
|
||||
ctx.textBaseline = baseline;
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with orientation:mixed
|
||||
// test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:mixed', 0, '');
|
||||
|
||||
// Reference: horizontal text with 90° rotation and textBaseline=middle
|
||||
test(100, 50, 'Hello', 'writing-mode:horizontal-tb', Math.PI/2, 'middle');
|
||||
|
||||
</script>
|
41
layout/reftests/writing-mode/1090168-2.html
Normal file
41
layout/reftests/writing-mode/1090168-2.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation, baseline) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
if (baseline != '') {
|
||||
ctx.textBaseline = baseline;
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with orientation:mixed
|
||||
test(100, 50, 'Hello', 'writing-mode:vertical-lr;text-orientation:mixed', 0, '');
|
||||
|
||||
// Reference: horizontal text with 90° rotation and textBaseline=middle
|
||||
// test(100, 50, 'Hello', 'writing-mode:horizontal-tb', Math.PI/2, 'middle');
|
||||
|
||||
</script>
|
47
layout/reftests/writing-mode/1090168-3-ref.html
Normal file
47
layout/reftests/writing-mode/1090168-3-ref.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation, baseline) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
if (baseline != '') {
|
||||
ctx.textBaseline = baseline;
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with various textBaselines
|
||||
// test(100, 50, 'Top', 'writing-mode:vertical-lr', 0, 'top');
|
||||
// test(150, 50, 'Middle', 'writing-mode:vertical-lr', 0, 'middle');
|
||||
// test(200, 50, 'Alphabetic', 'writing-mode:vertical-lr', 0, 'alphabetic');
|
||||
// test(250, 50, 'Bottom', 'writing-mode:vertical-lr', 0, 'bottom');
|
||||
|
||||
// Reference: horizontal text with 90° rotation and the same baselines
|
||||
test(100, 50, 'Top', 'writing-mode:horizontal-tb', Math.PI/2, 'top');
|
||||
test(150, 50, 'Middle', 'writing-mode:horizontal-tb', Math.PI/2, 'middle');
|
||||
test(200, 50, 'Alphabetic', 'writing-mode:horizontal-tb', Math.PI/2, 'alphabetic');
|
||||
test(250, 50, 'Bottom', 'writing-mode:horizontal-tb', Math.PI/2, 'bottom');
|
||||
|
||||
</script>
|
47
layout/reftests/writing-mode/1090168-3.html
Normal file
47
layout/reftests/writing-mode/1090168-3.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var testFont = '40px sans-serif';
|
||||
|
||||
function test(x, y, text, style, rotation, baseline) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
canvas.style.cssText = 'position:absolute;' + style;
|
||||
document.getElementsByTagName('body')[0].appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 20, y); ctx.lineTo(x + 20, y);
|
||||
ctx.moveTo(x, y - 20); ctx.lineTo(x, y + 20);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.font = testFont;
|
||||
if (rotation != 0) {
|
||||
ctx.translate(x,y);
|
||||
ctx.rotate(rotation);
|
||||
ctx.translate(-x,-y);
|
||||
}
|
||||
if (baseline != '') {
|
||||
ctx.textBaseline = baseline;
|
||||
}
|
||||
ctx.fillText(text, x, y);
|
||||
}
|
||||
|
||||
// Testcase: vertical text with various textBaselines
|
||||
test(100, 50, 'Top', 'writing-mode:vertical-lr', 0, 'top');
|
||||
test(150, 50, 'Middle', 'writing-mode:vertical-lr', 0, 'middle');
|
||||
test(200, 50, 'Alphabetic', 'writing-mode:vertical-lr', 0, 'alphabetic');
|
||||
test(250, 50, 'Bottom', 'writing-mode:vertical-lr', 0, 'bottom');
|
||||
|
||||
// Reference: horizontal text with 90° rotation and the same baselines
|
||||
// test(100, 50, 'Top', 'writing-mode:horizontal-tb', Math.PI/2, 'top');
|
||||
// test(150, 50, 'Middle', 'writing-mode:horizontal-tb', Math.PI/2, 'middle');
|
||||
// test(200, 50, 'Alphabetic', 'writing-mode:horizontal-tb', Math.PI/2, 'alphabetic');
|
||||
// test(250, 50, 'Bottom', 'writing-mode:horizontal-tb', Math.PI/2, 'bottom');
|
||||
|
||||
</script>
|
35
layout/reftests/writing-mode/1091058-1-ref.html
Normal file
35
layout/reftests/writing-mode/1091058-1-ref.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.h { writing-mode:horizontal-tb; }
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border: 1px solid red;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="h">
|
||||
<u>方ABC方方</u><i><u>abc</u></i><u>方方方</u><b><u>xyz</u></b><u>方</u>
|
||||
</div>
|
||||
|
||||
<div class="v-lr">
|
||||
<u>方ABC方方</u><i><u>abc</u></i><u>方方方</u><b><u>xyz</u></b><u>方</u>
|
||||
</div>
|
||||
|
||||
<div class="v-rl">
|
||||
<u>方ABC方方</u><i><u>abc</u></i><u>方方方</u><b><u>xyz</u></b><u>方</u>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
35
layout/reftests/writing-mode/1091058-1.html
Normal file
35
layout/reftests/writing-mode/1091058-1.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.h { writing-mode:horizontal-tb; }
|
||||
.v-lr { writing-mode:vertical-lr; }
|
||||
.v-rl { writing-mode:vertical-rl; }
|
||||
|
||||
div {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border: 1px solid red;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="h">
|
||||
<u>方ABC方方<i>abc</i>方方方<b>xyz</b>方</u>
|
||||
</div>
|
||||
|
||||
<div class="v-lr">
|
||||
<u>方ABC方方<i>abc</i>方方方<b>xyz</b>方</u>
|
||||
</div>
|
||||
|
||||
<div class="v-rl">
|
||||
<u>方ABC方方<i>abc</i>方方方<b>xyz</b>方</u>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
43
layout/reftests/writing-mode/1094914-1-ref.html
Normal file
43
layout/reftests/writing-mode/1094914-1-ref.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#outer {
|
||||
margin: 0px;
|
||||
background: #ddd;
|
||||
font-size: 0;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#test1 {
|
||||
writing-mode: horizontal-tb;
|
||||
|
||||
height: 50px;
|
||||
width: 100px;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
margin-left: 50px;
|
||||
margin-right: 0px;
|
||||
|
||||
border-top: 10px solid blue;
|
||||
border-left: 20px solid red;
|
||||
border-right: 30px solid green;
|
||||
border-bottom: 40px solid gray;
|
||||
|
||||
display: inline-block;
|
||||
background: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="outer">
|
||||
<div id="test1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user