зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
Коммит
ee57c0cd8a
|
@ -311,6 +311,11 @@ add_task(async function test_new_tab_restore_settings() {
|
|||
is(gBrowser.currentURI.spec, "about:newtab",
|
||||
"The user has been redirected to about:newtab");
|
||||
|
||||
// Wait for the next event tick to make sure the remaining part of the test
|
||||
// is not executed inside tabbrowser's onLocationChange.
|
||||
// See bug 1416153 for more details.
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
// Reopen a browser tab and verify that there's no doorhanger.
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
let newTabOpened = waitForNewTab();
|
||||
|
@ -431,6 +436,11 @@ add_task(async function test_new_tab_restore_settings_multiple() {
|
|||
is(gBrowser.currentURI.spec, "about:newtab",
|
||||
"The user is now on the original New Tab URL since all extensions are disabled");
|
||||
|
||||
// Wait for the next event tick to make sure the remaining part of the test
|
||||
// is not executed inside tabbrowser's onLocationChange.
|
||||
// See bug 1416153 for more details.
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
// Reopen a browser tab and verify that there's no doorhanger.
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
let newTabOpened = waitForNewTab();
|
||||
|
|
|
@ -137,15 +137,11 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
|
|||
// problem. We do the work here since this is only run when
|
||||
// requested by devtools, and slowness here will not affect
|
||||
// normal browsing.
|
||||
nsTArray<nsString> possiblyDuplicateLineNames(
|
||||
const nsTArray<nsString>& possiblyDuplicateLineNames(
|
||||
aLineInfo->mNames.SafeElementAt(i, nsTArray<nsString>()));
|
||||
|
||||
// Add the possiblyDuplicateLineNames one at a time to filter
|
||||
// out the duplicates.
|
||||
nsTArray<nsString> lineNames;
|
||||
for (const auto& name : possiblyDuplicateLineNames) {
|
||||
AddLineNameIfNotPresent(lineNames, name);
|
||||
}
|
||||
AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames);
|
||||
|
||||
// Add in names from grid areas where this line is used as a boundary.
|
||||
for (auto area : aAreas) {
|
||||
|
@ -187,6 +183,16 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
|
|||
lineNames);
|
||||
}
|
||||
|
||||
// If this line is the one that ends a repeat, then add
|
||||
// in the mNamesFollowingRepeat names from aLineInfo.
|
||||
if (numRepeatTracks > 0 &&
|
||||
i == (aTrackInfo->mRepeatFirstTrack +
|
||||
aTrackInfo->mNumLeadingImplicitTracks +
|
||||
numRepeatTracks - numAddedLines)) {
|
||||
AddLineNamesIfNotPresent(lineNames,
|
||||
aLineInfo->mNamesFollowingRepeat);
|
||||
}
|
||||
|
||||
RefPtr<GridLine> line = new GridLine(this);
|
||||
mLines.AppendElement(line);
|
||||
MOZ_ASSERT(line1Index > 0, "line1Index must be positive.");
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
[chrome/test_grid_lines.html]
|
||||
[chrome/test_grid_line_numbers.html]
|
||||
[chrome/test_grid_object.html]
|
||||
[chrome/test_grid_repeats.html]
|
||||
[chrome/test_grid_repeat_auto_fit.html]
|
||||
[chrome/test_grid_repeat_auto_fill.html]
|
||||
[chrome/test_grid_tracks.html]
|
||||
|
|
|
@ -0,0 +1,614 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
width: 600px;
|
||||
grid-gap: 0px;
|
||||
grid-auto-column: 50px;
|
||||
background-color: #f00;
|
||||
}
|
||||
.grid1 {
|
||||
grid-template-columns: 50px 0px repeat(auto-fill, 100px);
|
||||
}
|
||||
.grid2 {
|
||||
grid-template-columns: 50px 0px [real-before] repeat(auto-fill, [before] 100px [after]) [real-after] 0px [final];
|
||||
}
|
||||
.grid3 {
|
||||
grid-template-columns: repeat(3, 66px) [real-before] repeat(auto-fill, [before] 100px [after]) [real-after];
|
||||
}
|
||||
.grid4 {
|
||||
grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px);
|
||||
}
|
||||
.grid5 {
|
||||
grid-template-columns: [real-before] repeat(auto-fill, [before] 100px [after]) [real-after];
|
||||
}
|
||||
.grid6 {
|
||||
grid-template-columns: [first] 0px [real-before] repeat(auto-fill, [before] 100px [after]) [real-after];
|
||||
}
|
||||
.grid7 {
|
||||
grid-template-columns: [real-before before] repeat(auto-fill, [before] 100px [after]) [after real-after] 0px [final];
|
||||
}
|
||||
.grid8 {
|
||||
grid-template-columns: [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after];
|
||||
}
|
||||
.grid9 {
|
||||
grid-template-columns: [real-before] repeat(auto-fill, 100px [after]) [real-after];
|
||||
}
|
||||
.grid10 {
|
||||
grid-template-columns: [real-before] repeat(auto-fill, [before] 100px) [real-after];
|
||||
}
|
||||
.grid11 {
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after];
|
||||
}
|
||||
.grid12 {
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after] 10px;
|
||||
}
|
||||
.grid13 {
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid14 {
|
||||
grid-template-columns: [real-before] repeat(auto-fill, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid15 {
|
||||
grid-template-columns: repeat(auto-fill, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid16 {
|
||||
grid-template-columns: repeat(auto-fill, [before] 1000px [after]) [real-after] 10px;
|
||||
}
|
||||
.grid17 {
|
||||
grid-template-columns: repeat(auto-fill, [before] 1000px [after]) [real-after] 10px [final];
|
||||
}
|
||||
.grid18 {
|
||||
grid-template-columns: repeat(auto-fill, [before] 1000px [after]) 10px [final];
|
||||
}
|
||||
.grid19 {
|
||||
grid-template-columns: repeat(auto-fill, [before] 1000px);
|
||||
}
|
||||
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.a {
|
||||
grid-column: auto;
|
||||
}
|
||||
.b {
|
||||
grid-column: 4;
|
||||
}
|
||||
.c {
|
||||
grid-column: 6;
|
||||
}
|
||||
.d {
|
||||
grid-column: 7;
|
||||
}
|
||||
.e {
|
||||
grid-column: 5;
|
||||
}
|
||||
.f {
|
||||
grid-column: -9;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testLines(elementName, grid, expectedValues) {
|
||||
is(grid.cols.lines.length, expectedValues.length, elementName + " has expected number of lines.");
|
||||
|
||||
for (let i = 0; i < grid.cols.lines.length; i++) {
|
||||
// 'number' is optional.
|
||||
if (typeof(expectedValues[i].number) != "undefined") {
|
||||
is(grid.cols.lines[i].number, expectedValues[i].number, elementName + " line " + (i + 1) + " has expected number.");
|
||||
} else {
|
||||
// If 'number' is omitted, assume that first line is line 1 and increase from there.
|
||||
is(grid.cols.lines[i].number, (i + 1), elementName + " line " + (i + 1) + " has expected number.");
|
||||
}
|
||||
|
||||
// 'negativeNumber' is optional.
|
||||
if (typeof(expectedValues[i].negativeNumber) != "undefined") {
|
||||
// Check for the supplied number.
|
||||
is(grid.cols.lines[i].negativeNumber, expectedValues[i].negativeNumber, elementName + " line " + (i + 1) + " has expected negativeNumber.");
|
||||
}
|
||||
|
||||
// 'start' is optional.
|
||||
if (typeof(expectedValues[i].start) != "undefined") {
|
||||
is(grid.cols.lines[i].start, expectedValues[i].start, elementName + " line " + (i + 1) + " has expected start.");
|
||||
}
|
||||
|
||||
// 'breadth' is optional.
|
||||
if (typeof(expectedValues[i].breadth) != "undefined") {
|
||||
is(grid.cols.lines[i].breadth, 0, elementName + " line " + (i + 1) + " has zero breadth.");
|
||||
}
|
||||
|
||||
// 'names' is optional.
|
||||
if (typeof(expectedValues[i].names) != "undefined") {
|
||||
is(grid.cols.lines[i].names + "", expectedValues[i].names, elementName + " line " + (i + 1) + " has expected names.");
|
||||
}
|
||||
|
||||
// 'todo_names' is optional.
|
||||
if (typeof(expectedValues[i].todo_names) != "undefined") {
|
||||
todo_is(grid.cols.lines[i].names + "", expectedValues[i].todo_names, elementName + " line " + (i + 1) + " has expected names.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
let wrapper;
|
||||
let grid;
|
||||
let expectedValues;
|
||||
|
||||
wrapper = document.getElementById("wrapper1");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test auto-fill count
|
||||
is(grid.cols.tracks.length, 7, "Grid column track array reports removed auto-fill columns.");
|
||||
|
||||
// test resolved value of grid-template-columns
|
||||
let templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns;
|
||||
is(templateColumnsText, "50px 0px 100px 100px 100px 100px 100px",
|
||||
"Resolved value of grid-template-columns is as expected.");
|
||||
|
||||
// test starts, breadths, and states
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"breadth": 50,
|
||||
"state": "static" },
|
||||
{ "start": 50,
|
||||
"breadth": 0,
|
||||
"state": "static" },
|
||||
{ "start": 50,
|
||||
"breadth": 100,
|
||||
"state": "repeat" },
|
||||
{ "start": 150,
|
||||
"breadth": 100,
|
||||
"state": "repeat" },
|
||||
{ "start": 250,
|
||||
"breadth": 100,
|
||||
"state": "repeat" },
|
||||
{ "start": 350,
|
||||
"breadth": 100,
|
||||
"state": "repeat" },
|
||||
{ "start": 450,
|
||||
"breadth": 100,
|
||||
"state": "repeat" },
|
||||
];
|
||||
for (let i = 0; i < grid.cols.tracks.length; i++) {
|
||||
is(grid.cols.tracks[i].start, expectedValues[i].start, "Column " + (i + 1) + " has expected start.");
|
||||
is(grid.cols.tracks[i].breadth, expectedValues[i].breadth, "Column " + (i + 1) + " has expected breadth.");
|
||||
is(grid.cols.tracks[i].state, expectedValues[i].state, "Column " + (i + 1) + " has expected state.");
|
||||
}
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper2");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test auto-fill count
|
||||
is(grid.cols.lines.length, 9, "Grid column line array reports removed auto-fill columns.");
|
||||
|
||||
// test resolved value of grid-template-columns
|
||||
templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns;
|
||||
is(templateColumnsText, "50px 0px [real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after] 0px [final]",
|
||||
"Resolved value of grid-template-columns is as expected.");
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 50,
|
||||
"names": "" },
|
||||
{ "start": 50,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 150,
|
||||
"names": "after,before" },
|
||||
{ "start": 250,
|
||||
"names": "after,before" },
|
||||
{ "start": 350,
|
||||
"names": "after,before" },
|
||||
{ "start": 450,
|
||||
"names": "after,before" },
|
||||
{ "start": 550,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 550,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper2", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper3");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test resolved value of grid-template-columns
|
||||
templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns;
|
||||
is(templateColumnsText, "66px 66px 66px [real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after]",
|
||||
"Resolved value of grid-template-columns is as expected.");
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper4");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test auto-fill count of tracks
|
||||
is(grid.cols.tracks.length, 10, "Grid column track array respects auto-fill columns.");
|
||||
|
||||
if (grid.cols.tracks.length == 10) {
|
||||
// test for static and repeat
|
||||
is(grid.cols.tracks[1].state, "static", "Grid column track 2 is marked as static.");
|
||||
is(grid.cols.tracks[2].state, "repeat", "Grid column track 3 is marked as repeat.");
|
||||
}
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper5");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test resolved value of grid-template-columns
|
||||
templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns;
|
||||
is(templateColumnsText, "[real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after]", "Resolved value of grid-template-columns is as expected.");
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper6");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "first" },
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 100,
|
||||
"names": "after,before" },
|
||||
{ "start": 200,
|
||||
"names": "after,before" },
|
||||
{ "start": 300,
|
||||
"names": "after,before" },
|
||||
{ "start": 400,
|
||||
"names": "after,before" },
|
||||
{ "start": 500,
|
||||
"names": "after,before" },
|
||||
{ "start": 600,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper6", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper7");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 100,
|
||||
"names": "after,before" },
|
||||
{ "start": 200,
|
||||
"names": "after,before" },
|
||||
{ "start": 300,
|
||||
"names": "after,before" },
|
||||
{ "start": 400,
|
||||
"names": "after,before" },
|
||||
{ "start": 500,
|
||||
"names": "after,before" },
|
||||
{ "start": 600,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 600,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper7", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper8");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 1000,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper8", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper8b");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 1,
|
||||
"names": "real-before,before" },
|
||||
{ "number": 2,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper8b", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper9");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before" },
|
||||
{ "start": 100,
|
||||
"names": "after" },
|
||||
{ "start": 200,
|
||||
"names": "after" },
|
||||
{ "start": 300,
|
||||
"names": "after" },
|
||||
{ "start": 400,
|
||||
"names": "after" },
|
||||
{ "start": 500,
|
||||
"names": "after" },
|
||||
{ "start": 600,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper9", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper10");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 100,
|
||||
"names": "before" },
|
||||
{ "start": 200,
|
||||
"names": "before" },
|
||||
{ "start": 300,
|
||||
"names": "before" },
|
||||
{ "start": 400,
|
||||
"names": "before" },
|
||||
{ "start": 500,
|
||||
"names": "before" },
|
||||
{ "start": 600,
|
||||
"names": "real-after" },
|
||||
];
|
||||
testLines("wrapper10", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper11");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 1010,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper11", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper12");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 1010,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 1020,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper12", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper13");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 1010,
|
||||
"names": "after" },
|
||||
{ "start": 1020,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper13", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper14");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 1000,
|
||||
"names": "after" },
|
||||
{ "start": 1010,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper14", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper15");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 1000,
|
||||
"names": "after" },
|
||||
{ "start": 1010,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper15", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper16");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 1000,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 1010,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper16", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper17");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 1000,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 1010,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper17", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper18");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 1000,
|
||||
"names": "after" },
|
||||
{ "start": 1010,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper18", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper19");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 1000,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper19", grid, expectedValues);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper1" class="wrapper grid1">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper2" class="wrapper grid2">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxC" class="box c">C</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper3" class="wrapper grid3">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxC" class="box c">C</div>
|
||||
<div id="boxD" class="box d">D</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper4" class="wrapper grid4">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper5" class="wrapper grid5">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper6" class="wrapper grid6">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper7" class="wrapper grid7">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper8" class="wrapper grid8">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper8b" class="wrapper grid8">
|
||||
<div id="boxF" class="box f">F</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper9" class="wrapper grid9">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxE" class="box e">E</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper10" class="wrapper grid10">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxE" class="box e">E</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper11" class="wrapper grid11">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper12" class="wrapper grid12">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper13" class="wrapper grid13">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper14" class="wrapper grid14">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper15" class="wrapper grid15">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper16" class="wrapper grid16">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper17" class="wrapper grid17">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper18" class="wrapper grid18">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper19" class="wrapper grid19">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -46,7 +46,31 @@ body {
|
|||
grid-template-columns: [real-before] repeat(auto-fit, [before] 100px) [real-after];
|
||||
}
|
||||
.grid11 {
|
||||
grid-template-columns: repeat(auto-fit, 100px);
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) [real-after];
|
||||
}
|
||||
.grid12 {
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) [real-after] 10px;
|
||||
}
|
||||
.grid13 {
|
||||
grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid14 {
|
||||
grid-template-columns: [real-before] repeat(auto-fit, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid15 {
|
||||
grid-template-columns: repeat(auto-fit, [before] 1000px [after]) 10px;
|
||||
}
|
||||
.grid16 {
|
||||
grid-template-columns: repeat(auto-fit, [before] 1000px [after]) [real-after] 10px;
|
||||
}
|
||||
.grid17 {
|
||||
grid-template-columns: repeat(auto-fit, [before] 1000px [after]) [real-after] 10px [final];
|
||||
}
|
||||
.grid18 {
|
||||
grid-template-columns: repeat(auto-fit, [before] 1000px [after]) 10px [final];
|
||||
}
|
||||
.grid19 {
|
||||
grid-template-columns: repeat(auto-fit, [before] 1000px);
|
||||
}
|
||||
|
||||
.box {
|
||||
|
@ -287,13 +311,33 @@ function runTests() {
|
|||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"todo_names": "real-before,before" },
|
||||
"names": "real-before,before" },
|
||||
{ "start": 0,
|
||||
"todo_names": "after,real-after" },
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper8", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper8b");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 0 },
|
||||
{ "number": 1,
|
||||
"names": "real-before,before" },
|
||||
{ "number": 2,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper8b", grid, expectedValues);
|
||||
|
||||
|
||||
wrapper = document.getElementById("wrapper9");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
|
@ -339,6 +383,134 @@ function runTests() {
|
|||
];
|
||||
testLines("wrapper10", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper11");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 10,
|
||||
"names": "after,real-after" },
|
||||
];
|
||||
testLines("wrapper11", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper12");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 10,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 20,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper12", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper13");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
{ "start": 10,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 10,
|
||||
"names": "after" },
|
||||
{ "start": 20,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper13", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper14");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "real-before,before" },
|
||||
{ "start": 0,
|
||||
"names": "after" },
|
||||
{ "start": 10,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper14", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper15");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 0,
|
||||
"names": "after" },
|
||||
{ "start": 10,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper15", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper16");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 0,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 10,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper16", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper17");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 0,
|
||||
"names": "after,real-after" },
|
||||
{ "start": 10,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper17", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper18");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 0,
|
||||
"names": "after" },
|
||||
{ "start": 10,
|
||||
"names": "final" },
|
||||
];
|
||||
testLines("wrapper18", grid, expectedValues);
|
||||
|
||||
wrapper = document.getElementById("wrapper19");
|
||||
grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test starts and names
|
||||
expectedValues = [
|
||||
{ "start": 0,
|
||||
"names": "before" },
|
||||
{ "start": 0,
|
||||
"names": "" },
|
||||
];
|
||||
testLines("wrapper19", grid, expectedValues);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
|
@ -385,6 +557,11 @@ function runTests() {
|
|||
<div id="wrapper8" class="wrapper grid8">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper8b" class="wrapper grid8">
|
||||
<div id="boxF" class="box f">F</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper9" class="wrapper grid9">
|
||||
<div id="boxB" class="box b">B</div>
|
||||
|
@ -397,5 +574,41 @@ function runTests() {
|
|||
<div id="boxE" class="box e">E</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper11" class="wrapper grid11">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper12" class="wrapper grid12">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper13" class="wrapper grid13">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper14" class="wrapper grid14">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper15" class="wrapper grid15">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper16" class="wrapper grid16">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper17" class="wrapper grid17">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper18" class="wrapper grid18">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="wrapper19" class="wrapper grid19">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1491,23 +1491,17 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
PRTime beginTime = PR_Now();
|
||||
|
||||
nsAutoString wordText;
|
||||
RefPtr<nsRange> wordRange;
|
||||
NodeOffsetRange wordNodeOffsetRange;
|
||||
bool dontCheckWord;
|
||||
while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText,
|
||||
getter_AddRefs(wordRange),
|
||||
while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText, &wordNodeOffsetRange,
|
||||
&dontCheckWord)) &&
|
||||
wordRange) {
|
||||
!wordNodeOffsetRange.Empty()) {
|
||||
|
||||
// get the range for the current word.
|
||||
nsINode *beginNode;
|
||||
nsINode *endNode;
|
||||
int32_t beginOffset, endOffset;
|
||||
|
||||
ErrorResult erv;
|
||||
beginNode = wordRange->GetStartContainer(erv);
|
||||
endNode = wordRange->GetEndContainer(erv);
|
||||
beginOffset = wordRange->GetStartOffset(erv);
|
||||
endOffset = wordRange->GetEndOffset(erv);
|
||||
nsINode* beginNode = wordNodeOffsetRange.Begin().mNode;
|
||||
nsINode* endNode = wordNodeOffsetRange.End().mNode;
|
||||
int32_t beginOffset = wordNodeOffsetRange.Begin().mOffset;
|
||||
int32_t endOffset = wordNodeOffsetRange.End().mOffset;
|
||||
|
||||
// see if we've done enough words in this round and run out of time.
|
||||
if (wordsChecked >= INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT &&
|
||||
|
@ -1535,6 +1529,7 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
printf("\n");
|
||||
#endif
|
||||
|
||||
ErrorResult erv;
|
||||
// see if there is a spellcheck range that already intersects the word
|
||||
// and remove it. We only need to remove old ranges, so don't bother if
|
||||
// there were no ranges when we started out.
|
||||
|
@ -1582,11 +1577,14 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
continue;
|
||||
|
||||
wordsChecked++;
|
||||
|
||||
if (isMisspelled) {
|
||||
// misspelled words count extra toward the max
|
||||
RefPtr<nsRange> wordRange;
|
||||
// If we somehow can't make a range for this word, just ignore it.
|
||||
if(NS_SUCCEEDED(aWordUtil.MakeRange(wordNodeOffsetRange.Begin(),
|
||||
wordNodeOffsetRange.End(),
|
||||
getter_AddRefs(wordRange)))) {
|
||||
AddRange(aSpellCheckSelection, wordRange);
|
||||
|
||||
aStatus->mWordCount++;
|
||||
if (aStatus->mWordCount >= mMaxMisspellingsPerCheck ||
|
||||
SpellCheckSelectionIsFull()) {
|
||||
|
@ -1594,6 +1592,7 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -231,6 +231,14 @@ mozInlineSpellWordUtil::MakeRangeForWord(const RealWord& aWord, nsRange** aRange
|
|||
NodeOffset end = MapSoftTextOffsetToDOMPosition(aWord.EndOffset(), HINT_END);
|
||||
return MakeRange(begin, end, aRange);
|
||||
}
|
||||
void
|
||||
mozInlineSpellWordUtil::MakeNodeOffsetRangeForWord(const RealWord& aWord,
|
||||
NodeOffsetRange* aNodeOffsetRange)
|
||||
{
|
||||
NodeOffset begin = MapSoftTextOffsetToDOMPosition(aWord.mSoftTextOffset, HINT_BEGIN);
|
||||
NodeOffset end = MapSoftTextOffsetToDOMPosition(aWord.EndOffset(), HINT_END);
|
||||
*aNodeOffsetRange = NodeOffsetRange(begin, end);
|
||||
}
|
||||
|
||||
// mozInlineSpellWordUtil::GetRangeForWord
|
||||
|
||||
|
@ -289,7 +297,8 @@ NormalizeWord(const nsAString& aInput, int32_t aPos, int32_t aLen, nsAString& aO
|
|||
// range unless the word was misspelled. This may or may not be possible.
|
||||
|
||||
nsresult
|
||||
mozInlineSpellWordUtil::GetNextWord(nsAString& aText, nsRange** aRange,
|
||||
mozInlineSpellWordUtil::GetNextWord(nsAString& aText,
|
||||
NodeOffsetRange* aNodeOffsetRange,
|
||||
bool* aSkipChecking)
|
||||
{
|
||||
#ifdef DEBUG_SPELLCHECK
|
||||
|
@ -299,14 +308,13 @@ mozInlineSpellWordUtil::GetNextWord(nsAString& aText, nsRange** aRange,
|
|||
if (mNextWordIndex < 0 ||
|
||||
mNextWordIndex >= int32_t(mRealWords.Length())) {
|
||||
mNextWordIndex = -1;
|
||||
*aRange = nullptr;
|
||||
*aNodeOffsetRange = NodeOffsetRange();
|
||||
*aSkipChecking = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const RealWord& word = mRealWords[mNextWordIndex];
|
||||
nsresult rv = MakeRangeForWord(word, aRange);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MakeNodeOffsetRangeForWord(word, aNodeOffsetRange);
|
||||
++mNextWordIndex;
|
||||
*aSkipChecking = !word.mCheckableWord;
|
||||
::NormalizeWord(mSoftText, word.mSoftTextOffset, word.mLength, aText);
|
||||
|
@ -965,7 +973,7 @@ FindLastNongreaterOffset(const nsTArray<T>& aContainer, int32_t aSoftTextOffset,
|
|||
|
||||
} // namespace
|
||||
|
||||
mozInlineSpellWordUtil::NodeOffset
|
||||
NodeOffset
|
||||
mozInlineSpellWordUtil::MapSoftTextOffsetToDOMPosition(int32_t aSoftTextOffset,
|
||||
DOMMapHint aHint)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,53 @@ namespace mozilla {
|
|||
class TextEditor;
|
||||
} // namespace mozilla
|
||||
|
||||
struct NodeOffset
|
||||
{
|
||||
nsINode* mNode;
|
||||
int32_t mOffset;
|
||||
|
||||
NodeOffset(): mNode(nullptr), mOffset(0) {}
|
||||
NodeOffset(nsINode* aNode, int32_t aOffset)
|
||||
: mNode(aNode), mOffset(aOffset) {}
|
||||
|
||||
bool operator==(const NodeOffset& aOther) const
|
||||
{
|
||||
return mNode == aOther.mNode && mOffset == aOther.mOffset;
|
||||
}
|
||||
|
||||
bool operator!=(const NodeOffset& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
|
||||
class NodeOffsetRange
|
||||
{
|
||||
private:
|
||||
NodeOffset mBegin;
|
||||
NodeOffset mEnd;
|
||||
bool mEmpty;
|
||||
public:
|
||||
NodeOffsetRange() : mEmpty(true) {}
|
||||
NodeOffsetRange(NodeOffset b, NodeOffset e)
|
||||
: mBegin(b), mEnd(e), mEmpty(false) {}
|
||||
|
||||
NodeOffset Begin()
|
||||
{
|
||||
return mBegin;
|
||||
}
|
||||
|
||||
NodeOffset End()
|
||||
{
|
||||
return mEnd;
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
return mEmpty;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class extracts text from the DOM and builds it into a single string.
|
||||
* The string includes whitespace breaks whereever non-inline elements begin
|
||||
|
@ -44,22 +91,6 @@ class TextEditor;
|
|||
class mozInlineSpellWordUtil
|
||||
{
|
||||
public:
|
||||
struct NodeOffset {
|
||||
nsINode* mNode;
|
||||
int32_t mOffset;
|
||||
|
||||
NodeOffset(nsINode* aNode, int32_t aOffset) :
|
||||
mNode(aNode), mOffset(aOffset) {}
|
||||
|
||||
bool operator==(const NodeOffset& aOther) const {
|
||||
return mNode == aOther.mNode && mOffset == aOther.mOffset;
|
||||
}
|
||||
|
||||
bool operator!=(const NodeOffset& aOther) const {
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
|
||||
mozInlineSpellWordUtil()
|
||||
: mRootNode(nullptr),
|
||||
mSoftBegin(nullptr, 0), mSoftEnd(nullptr, 0),
|
||||
|
@ -85,11 +116,15 @@ public:
|
|||
nsresult GetRangeForWord(nsIDOMNode* aWordNode, int32_t aWordOffset,
|
||||
nsRange** aRange);
|
||||
|
||||
// Convenience functions, object must be initialized
|
||||
nsresult MakeRange(NodeOffset aBegin, NodeOffset aEnd, nsRange** aRange);
|
||||
|
||||
// Moves to the the next word in the range, and retrieves it's text and range.
|
||||
// An empty word and a nullptr range are returned when we are done checking.
|
||||
// aSkipChecking will be set if the word is "special" and shouldn't be
|
||||
// checked (e.g., an email address).
|
||||
nsresult GetNextWord(nsAString& aText, nsRange** aRange,
|
||||
nsresult GetNextWord(nsAString& aText,
|
||||
NodeOffsetRange* aNodeOffsetRange,
|
||||
bool* aSkipChecking);
|
||||
|
||||
// Call to normalize some punctuation. This function takes an autostring
|
||||
|
@ -175,9 +210,9 @@ private:
|
|||
|
||||
nsresult SplitDOMWord(int32_t aStart, int32_t aEnd);
|
||||
|
||||
// Convenience functions, object must be initialized
|
||||
nsresult MakeRange(NodeOffset aBegin, NodeOffset aEnd, nsRange** aRange);
|
||||
nsresult MakeRangeForWord(const RealWord& aWord, nsRange** aRange);
|
||||
void MakeNodeOffsetRangeForWord(const RealWord& aWord,
|
||||
NodeOffsetRange* aNodeOffsetRange);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -397,7 +397,7 @@ ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
|
|||
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
|
||||
aInstanceData.SetupPattern(pattern);
|
||||
|
||||
cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern);
|
||||
cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern, nullptr, 0);
|
||||
if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
|
||||
gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
|
||||
FcPatternDestroy(pattern);
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include FT_IMAGE_H
|
||||
#include FT_BITMAP_H
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#if HAVE_FT_GLYPHSLOT_EMBOLDEN
|
||||
#include FT_SYNTHESIS_H
|
||||
#endif
|
||||
|
@ -164,6 +165,10 @@ struct _cairo_ft_unscaled_font {
|
|||
char *filename;
|
||||
int id;
|
||||
|
||||
/* For variation fonts, the variation coordinates to apply to each axis. */
|
||||
const FT_Fixed *var_coords;
|
||||
int num_var_coords;
|
||||
|
||||
/* We temporarily scale the unscaled font as needed */
|
||||
cairo_bool_t have_scale;
|
||||
cairo_matrix_t current_scale;
|
||||
|
@ -360,6 +365,8 @@ _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key,
|
|||
cairo_bool_t from_face,
|
||||
char *filename,
|
||||
int id,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords,
|
||||
FT_Face face)
|
||||
{
|
||||
unsigned long hash;
|
||||
|
@ -368,12 +375,16 @@ _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key,
|
|||
key->filename = filename;
|
||||
key->id = id;
|
||||
key->face = face;
|
||||
key->var_coords = var_coords;
|
||||
key->num_var_coords = num_var_coords;
|
||||
|
||||
hash = _cairo_hash_string (filename);
|
||||
/* the constants are just arbitrary primes */
|
||||
hash += ((unsigned long) id) * 1607;
|
||||
hash += ((unsigned long) face) * 2137;
|
||||
|
||||
hash = _cairo_hash_bytes (hash, var_coords, num_var_coords * sizeof(FT_Fixed));
|
||||
|
||||
key->base.hash_entry.hash = hash;
|
||||
}
|
||||
|
||||
|
@ -403,6 +414,8 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
|
|||
cairo_bool_t from_face,
|
||||
const char *filename,
|
||||
int id,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords,
|
||||
FT_Face face)
|
||||
{
|
||||
_cairo_unscaled_font_init (&unscaled->base,
|
||||
|
@ -410,7 +423,7 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
|
|||
|
||||
if (from_face) {
|
||||
unscaled->from_face = TRUE;
|
||||
_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
|
||||
_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, var_coords, num_var_coords, face);
|
||||
} else {
|
||||
char *filename_copy;
|
||||
|
||||
|
@ -421,7 +434,7 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
|
|||
if (unlikely (filename_copy == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL);
|
||||
_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, var_coords, num_var_coords, NULL);
|
||||
}
|
||||
|
||||
unscaled->have_scale = FALSE;
|
||||
|
@ -454,6 +467,11 @@ _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled)
|
|||
unscaled->filename = NULL;
|
||||
}
|
||||
|
||||
if (unscaled->var_coords) {
|
||||
free (unscaled->var_coords);
|
||||
unscaled->var_coords = NULL;
|
||||
}
|
||||
|
||||
CAIRO_MUTEX_FINI (unscaled->mutex);
|
||||
}
|
||||
|
||||
|
@ -465,12 +483,17 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a,
|
|||
const cairo_ft_unscaled_font_t *unscaled_b = key_b;
|
||||
|
||||
if (unscaled_a->id == unscaled_b->id &&
|
||||
unscaled_a->from_face == unscaled_b->from_face)
|
||||
unscaled_a->from_face == unscaled_b->from_face &&
|
||||
unscaled_a->num_var_coords == unscaled_b->num_var_coords)
|
||||
{
|
||||
if (unscaled_a->from_face)
|
||||
return unscaled_a->face == unscaled_b->face;
|
||||
|
||||
if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
|
||||
if (unscaled_a->num_var_coords > 0 &&
|
||||
(memcmp (unscaled_a->var_coords, unscaled_b->var_coords,
|
||||
unscaled_a->num_var_coords * sizeof(FT_Fixed)) != 0))
|
||||
return FALSE;
|
||||
else if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
|
||||
return TRUE;
|
||||
else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL)
|
||||
return FALSE;
|
||||
|
@ -489,17 +512,20 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
|||
char *filename,
|
||||
int id,
|
||||
FT_Face font_face,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
cairo_ft_unscaled_font_t key, *unscaled;
|
||||
cairo_ft_unscaled_font_map_t *font_map;
|
||||
FT_Fixed* new_var_coords = NULL;
|
||||
cairo_status_t status;
|
||||
|
||||
font_map = _cairo_ft_unscaled_font_map_lock ();
|
||||
if (unlikely (font_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
_cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face);
|
||||
_cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, var_coords, num_var_coords, font_face);
|
||||
|
||||
/* Return existing unscaled font if it exists in the hash table. */
|
||||
unscaled = _cairo_hash_table_lookup (font_map->hash_table,
|
||||
|
@ -516,7 +542,17 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
|||
goto UNWIND_FONT_MAP_LOCK;
|
||||
}
|
||||
|
||||
status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face);
|
||||
/* If we have variation coordinate data, make a copy to save in the unscaled_font */
|
||||
if (var_coords && num_var_coords) {
|
||||
new_var_coords = malloc (num_var_coords * sizeof(FT_Fixed));
|
||||
if (unlikely (!new_var_coords)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto UNWIND_VAR_COORDS;
|
||||
}
|
||||
memcpy (new_var_coords, var_coords, num_var_coords * sizeof(FT_Fixed));
|
||||
}
|
||||
|
||||
status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, new_var_coords, num_var_coords, font_face);
|
||||
if (unlikely (status))
|
||||
goto UNWIND_UNSCALED_MALLOC;
|
||||
|
||||
|
@ -535,6 +571,8 @@ UNWIND_UNSCALED_FONT_INIT:
|
|||
_cairo_ft_unscaled_font_fini (unscaled);
|
||||
UNWIND_UNSCALED_MALLOC:
|
||||
free (unscaled);
|
||||
UNWIND_VAR_COORDS:
|
||||
free (new_var_coords);
|
||||
UNWIND_FONT_MAP_LOCK:
|
||||
_cairo_ft_unscaled_font_map_unlock ();
|
||||
return status;
|
||||
|
@ -544,6 +582,7 @@ UNWIND_FONT_MAP_LOCK:
|
|||
#if CAIRO_HAS_FC_FONT
|
||||
static cairo_status_t
|
||||
_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
|
||||
const FT_Fixed *var_coords, int num_var_coords,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
FT_Face font_face = NULL;
|
||||
|
@ -576,15 +615,17 @@ _cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
|
|||
DONE:
|
||||
return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
|
||||
filename, id, font_face,
|
||||
var_coords, num_var_coords,
|
||||
out);
|
||||
}
|
||||
#endif
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_ft_unscaled_font_create_from_face (FT_Face face,
|
||||
_cairo_ft_unscaled_font_create_from_face (FT_Face face, const FT_Fixed *var_coords, int num_var_coords,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
|
||||
return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face,
|
||||
var_coords, num_var_coords, out);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -684,6 +725,19 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (unscaled->var_coords) {
|
||||
typedef FT_UInt (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
|
||||
static SetCoordsFunc setCoords;
|
||||
static cairo_bool_t firstTime = TRUE;
|
||||
if (firstTime) {
|
||||
firstTime = FALSE;
|
||||
(SetCoordsFunc)dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
|
||||
}
|
||||
if (setCoords) {
|
||||
(*setCoords)(face, unscaled->num_var_coords, unscaled->var_coords);
|
||||
}
|
||||
}
|
||||
|
||||
unscaled->face = face;
|
||||
|
||||
font_map->num_open_faces++;
|
||||
|
@ -3066,7 +3120,7 @@ _cairo_ft_resolve_pattern (FcPattern *pattern,
|
|||
goto FREE_PATTERN;
|
||||
}
|
||||
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (resolved, NULL, 0, &unscaled);
|
||||
if (unlikely (status || unscaled == NULL)) {
|
||||
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
|
||||
goto FREE_RESOLVED;
|
||||
|
@ -3123,14 +3177,17 @@ FREE_PATTERN:
|
|||
* cairo_font_face_destroy() when you are done using it.
|
||||
**/
|
||||
cairo_font_face_t *
|
||||
cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
|
||||
cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
|
||||
const FT_Fixed *var_coords, int num_var_coords)
|
||||
{
|
||||
cairo_ft_unscaled_font_t *unscaled;
|
||||
cairo_font_face_t *font_face;
|
||||
cairo_ft_options_t ft_options;
|
||||
cairo_status_t status;
|
||||
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (pattern,
|
||||
var_coords, num_var_coords,
|
||||
&unscaled);
|
||||
if (unlikely (status))
|
||||
return (cairo_font_face_t *) &_cairo_font_face_nil;
|
||||
if (unlikely (unscaled == NULL)) {
|
||||
|
@ -3200,14 +3257,18 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
|
|||
**/
|
||||
cairo_font_face_t *
|
||||
cairo_ft_font_face_create_for_ft_face (FT_Face face,
|
||||
int load_flags)
|
||||
int load_flags,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords)
|
||||
{
|
||||
cairo_ft_unscaled_font_t *unscaled;
|
||||
cairo_font_face_t *font_face;
|
||||
cairo_ft_options_t ft_options;
|
||||
cairo_status_t status;
|
||||
|
||||
status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
|
||||
status = _cairo_ft_unscaled_font_create_from_face (face,
|
||||
var_coords, num_var_coords,
|
||||
&unscaled);
|
||||
if (unlikely (status))
|
||||
return (cairo_font_face_t *)&_cairo_font_face_nil;
|
||||
|
||||
|
|
|
@ -54,7 +54,9 @@ CAIRO_BEGIN_DECLS
|
|||
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_ft_font_face_create_for_ft_face (FT_Face face,
|
||||
int load_flags);
|
||||
int load_flags,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords);
|
||||
|
||||
cairo_public FT_Face
|
||||
cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font);
|
||||
|
@ -65,7 +67,9 @@ cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font);
|
|||
#if CAIRO_HAS_FC_FONT
|
||||
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
|
||||
cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
|
||||
const FT_Fixed *var_coords,
|
||||
int num_var_coords);
|
||||
|
||||
cairo_public void
|
||||
cairo_ft_font_options_substitute (const cairo_font_options_t *options,
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#include "gfxFontConstants.h"
|
||||
#include "gfxFontUtils.h"
|
||||
#include <algorithm>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include FT_TRUETYPE_TAGS_H
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
#include FT_ADVANCES_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
|
||||
#ifndef FT_FACE_FLAG_COLOR
|
||||
#define FT_FACE_FLAG_COLOR ( 1L << 14 )
|
||||
|
@ -23,9 +26,11 @@ using namespace mozilla::gfx;
|
|||
gfxFT2FontBase::gfxFT2FontBase(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
|
||||
cairo_scaled_font_t *aScaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle)
|
||||
const gfxFontStyle *aFontStyle,
|
||||
bool aEmbolden)
|
||||
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont)
|
||||
, mSpaceGlyph(0)
|
||||
, mEmbolden(aEmbolden)
|
||||
{
|
||||
cairo_scaled_font_reference(mScaledFont);
|
||||
|
||||
|
@ -158,6 +163,22 @@ gfxFT2FontBase::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
|
|||
return gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get glyph id and width for a simple character.
|
||||
* The return value is the glyph id of that glyph or zero if no such glyph
|
||||
* exists. aWidth is only set when this returns a non-zero glyph id.
|
||||
* This is just for use during initialization, and doesn't use the width cache.
|
||||
*/
|
||||
uint32_t
|
||||
gfxFT2FontBase::GetCharWidth(char aChar, gfxFloat* aWidth)
|
||||
{
|
||||
FT_UInt gid = GetGlyph(aChar);
|
||||
if (gid) {
|
||||
*aWidth = FLOAT_FROM_16_16(GetFTGlyphAdvance(gid));
|
||||
}
|
||||
return gid;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFT2FontBase::InitMetrics()
|
||||
{
|
||||
|
@ -202,6 +223,23 @@ gfxFT2FontBase::InitMetrics()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!mStyle.variationSettings.IsEmpty()) {
|
||||
SetupVarCoords(face, mStyle.variationSettings, &mCoords);
|
||||
if (!mCoords.IsEmpty()) {
|
||||
typedef FT_UInt (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
|
||||
static SetCoordsFunc setCoords;
|
||||
static bool firstTime = true;
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
setCoords = (SetCoordsFunc)
|
||||
dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
|
||||
}
|
||||
if (setCoords) {
|
||||
(*setCoords)(face, mCoords.Length(), mCoords.Elements());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FT_Size_Metrics& ftMetrics = face->size->metrics;
|
||||
|
||||
mMetrics.maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
|
||||
|
@ -354,16 +392,16 @@ gfxFT2FontBase::InitMetrics()
|
|||
// necessary without recursively locking.
|
||||
cairo_ft_scaled_font_unlock_face(GetCairoScaledFont());
|
||||
|
||||
cairo_text_extents_t extents;
|
||||
mSpaceGlyph = GetCharExtents(' ', &extents);
|
||||
gfxFloat width;
|
||||
mSpaceGlyph = GetCharWidth(' ', &width);
|
||||
if (mSpaceGlyph) {
|
||||
mMetrics.spaceWidth = extents.x_advance;
|
||||
mMetrics.spaceWidth = width;
|
||||
} else {
|
||||
mMetrics.spaceWidth = mMetrics.maxAdvance; // guess
|
||||
}
|
||||
|
||||
if (GetCharExtents('0', &extents)) {
|
||||
mMetrics.zeroOrAveCharWidth = extents.x_advance;
|
||||
if (GetCharWidth('0', &width)) {
|
||||
mMetrics.zeroOrAveCharWidth = width;
|
||||
} else {
|
||||
mMetrics.zeroOrAveCharWidth = 0.0;
|
||||
}
|
||||
|
@ -372,6 +410,7 @@ gfxFT2FontBase::InitMetrics()
|
|||
// hinting, but maybe the x extents are not quite right in some fancy
|
||||
// script fonts. CSS 2.1 suggests possibly using the height of an "o",
|
||||
// which would have a more consistent glyph across fonts.
|
||||
cairo_text_extents_t extents;
|
||||
if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
|
||||
mMetrics.xHeight = -extents.y_bearing;
|
||||
mMetrics.aveCharWidth =
|
||||
|
@ -468,13 +507,68 @@ gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector)
|
|||
return GetGlyph(unicode);
|
||||
}
|
||||
|
||||
FT_Fixed
|
||||
gfxFT2FontBase::GetFTGlyphAdvance(uint16_t aGID)
|
||||
{
|
||||
gfxFT2LockedFace face(this);
|
||||
int32_t flags =
|
||||
gfxPlatform::GetPlatform()->FontHintingEnabled()
|
||||
? FT_LOAD_ADVANCE_ONLY
|
||||
: FT_LOAD_ADVANCE_ONLY | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
|
||||
FT_Fixed advance = 0;
|
||||
// FT_Get_Advance is not reliable with variations until FreeType 2.8.2,
|
||||
// so fall back to calling FT_Load_Glyph and reading the glyph slot's
|
||||
// linearHoriAdvance.
|
||||
// See https://savannah.nongnu.org/bugs/index.php?52683.
|
||||
static uint32_t sFTVersion = 0;
|
||||
if (!sFTVersion) {
|
||||
FT_Int major, minor, patch;
|
||||
FT_Library_Version(face.get()->glyph->library, &major, &minor, &patch);
|
||||
sFTVersion = (major << 16) | (minor << 8) | patch;
|
||||
}
|
||||
if (sFTVersion < 0x020802 &&
|
||||
(face.get()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
|
||||
mozilla::DebugOnly<FT_Error> ftError =
|
||||
FT_Load_Glyph(face.get(), aGID, flags);
|
||||
MOZ_ASSERT(!ftError);
|
||||
advance = face.get()->glyph->linearHoriAdvance;
|
||||
} else {
|
||||
mozilla::DebugOnly<FT_Error> ftError =
|
||||
FT_Get_Advance(face.get(), aGID, flags, &advance);
|
||||
MOZ_ASSERT(!ftError);
|
||||
}
|
||||
|
||||
// If freetype emboldening is being used, and it's not a zero-width glyph,
|
||||
// adjust the advance to account for the increased width.
|
||||
if (mEmbolden && advance > 0) {
|
||||
// This is the embolden "strength" used by FT_GlyphSlot_Embolden,
|
||||
// converted from 26.6 to 16.16
|
||||
FT_Fixed strength = 1024 *
|
||||
FT_MulFix(face.get()->units_per_EM,
|
||||
face.get()->size->metrics.y_scale) / 24;
|
||||
advance += strength;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
int32_t
|
||||
gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
|
||||
{
|
||||
cairo_text_extents_t extents;
|
||||
GetGlyphExtents(aGID, &extents);
|
||||
// convert to 16.16 fixed point
|
||||
return NS_lround(0x10000 * extents.x_advance);
|
||||
if (!mGlyphWidths) {
|
||||
mGlyphWidths =
|
||||
mozilla::MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
|
||||
}
|
||||
|
||||
int32_t width;
|
||||
if (mGlyphWidths->Get(aGID, &width)) {
|
||||
return width;
|
||||
}
|
||||
|
||||
width = GetFTGlyphAdvance(aGID);
|
||||
mGlyphWidths->Put(aGID, width);
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -515,3 +609,40 @@ gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
|
|||
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For variation fonts, figure out the variation coordinates to be applied
|
||||
// for each axis, in freetype's order (which may not match the order of
|
||||
// axes in mStyle.variationSettings, so we need to search by axis tag).
|
||||
/*static*/
|
||||
void
|
||||
gfxFT2FontBase::SetupVarCoords(FT_Face aFace,
|
||||
const nsTArray<gfxFontVariation>& aVariations,
|
||||
nsTArray<FT_Fixed>* aCoords)
|
||||
{
|
||||
aCoords->TruncateLength(0);
|
||||
if (aFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
typedef FT_UInt (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
||||
static GetVarFunc getVar;
|
||||
static bool firstTime = true;
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
|
||||
}
|
||||
FT_MM_Var* ftVar;
|
||||
if (getVar && FT_Err_Ok == (*getVar)(aFace, &ftVar)) {
|
||||
for (unsigned i = 0; i < ftVar->num_axis; ++i) {
|
||||
aCoords->AppendElement(ftVar->axis[i].def);
|
||||
for (const auto& v : aVariations) {
|
||||
if (ftVar->axis[i].tag == v.mTag) {
|
||||
FT_Fixed val = v.mValue * 0x10000;
|
||||
val = std::min(val, ftVar->axis[i].maximum);
|
||||
val = std::max(val, ftVar->axis[i].minimum);
|
||||
(*aCoords)[i] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(ftVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,16 @@
|
|||
#include "gfxFont.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/UnscaledFontFreeType.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
class gfxFT2FontBase : public gfxFont {
|
||||
public:
|
||||
gfxFT2FontBase(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
|
||||
cairo_scaled_font_t *aScaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle);
|
||||
const gfxFontStyle *aFontStyle,
|
||||
bool aEmbolden);
|
||||
virtual ~gfxFT2FontBase();
|
||||
|
||||
uint32_t GetGlyph(uint32_t aCharCode);
|
||||
|
@ -35,8 +38,14 @@ public:
|
|||
|
||||
virtual FontType GetType() const override { return FONT_TYPE_FT2; }
|
||||
|
||||
static void SetupVarCoords(FT_Face aFace,
|
||||
const nsTArray<gfxFontVariation>& aVariations,
|
||||
nsTArray<FT_Fixed>* aCoords);
|
||||
|
||||
private:
|
||||
uint32_t GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
|
||||
uint32_t GetCharWidth(char aChar, gfxFloat* aWidth);
|
||||
FT_Fixed GetFTGlyphAdvance(uint16_t aGID);
|
||||
void InitMetrics();
|
||||
|
||||
protected:
|
||||
|
@ -44,6 +53,16 @@ protected:
|
|||
|
||||
uint32_t mSpaceGlyph;
|
||||
Metrics mMetrics;
|
||||
bool mEmbolden;
|
||||
|
||||
// For variation/multiple-master fonts, this will be an array of the values
|
||||
// for each axis, as specified by mStyle.variationSettings (or the font's
|
||||
// default for axes not present in variationSettings). Values here are in
|
||||
// freetype's 16.16 fixed-point format, and clamped to the valid min/max
|
||||
// range reported by the face.
|
||||
nsTArray<FT_Fixed> mCoords;
|
||||
|
||||
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,int32_t>> mGlyphWidths;
|
||||
};
|
||||
|
||||
#endif /* GFX_FT2FONTBASE_H */
|
||||
|
|
|
@ -390,7 +390,7 @@ FT2FontEntry::CreateFontEntry(FT_Face aFace,
|
|||
int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
|
||||
FT_LOAD_DEFAULT :
|
||||
(FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
|
||||
fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
|
||||
fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags, nullptr, 0);
|
||||
FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
|
||||
cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
|
||||
userFontData, FTFontDestroyFunc);
|
||||
|
@ -434,7 +434,7 @@ FT2FontEntry::CairoFontFace()
|
|||
int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
|
||||
FT_LOAD_DEFAULT :
|
||||
(FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
|
||||
mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
|
||||
mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags, nullptr, 0);
|
||||
FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
|
||||
cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
|
||||
userFontData, FTFontDestroyFunc);
|
||||
|
|
|
@ -166,10 +166,11 @@ gfxFT2Font::gfxFT2Font(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
|
|||
FT2FontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle,
|
||||
bool aNeedsBold)
|
||||
: gfxFT2FontBase(aUnscaledFont, aCairoFont, aFontEntry, aFontStyle)
|
||||
: gfxFT2FontBase(aUnscaledFont, aCairoFont, aFontEntry, aFontStyle, false)
|
||||
, mCharGlyphCache(32)
|
||||
{
|
||||
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
|
||||
// TODO: use FreeType emboldening instead of multi-strike?
|
||||
mApplySyntheticBold = aNeedsBold;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
|||
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
|
||||
mFTFace(nullptr), mFTFaceInitialized(false),
|
||||
mIgnoreFcCharmap(aIgnoreFcCharmap),
|
||||
mAspect(0.0), mFontData(nullptr)
|
||||
mAspect(0.0), mFontData(nullptr), mLength(0)
|
||||
{
|
||||
// italic
|
||||
int slant;
|
||||
|
@ -264,22 +264,9 @@ gfxFontconfigFontEntry::Clone() const
|
|||
return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
|
||||
}
|
||||
|
||||
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle,
|
||||
const uint8_t *aData,
|
||||
FT_Face aFace)
|
||||
: gfxFontEntry(aFaceName),
|
||||
mFTFace(aFace), mFTFaceInitialized(true),
|
||||
mIgnoreFcCharmap(true),
|
||||
mAspect(0.0), mFontData(aData)
|
||||
static FcPattern*
|
||||
CreatePatternForFace(FT_Face aFace)
|
||||
{
|
||||
mWeight = aWeight;
|
||||
mStyle = aStyle;
|
||||
mStretch = aStretch;
|
||||
mIsDataUserFont = true;
|
||||
|
||||
// Use fontconfig to fill out the pattern from the FTFace.
|
||||
// The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
|
||||
// least). The dummy file passed here is removed below.
|
||||
|
@ -290,17 +277,54 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
|||
// "blanks", effectively assuming that, if the font has a blank glyph,
|
||||
// then the author intends any associated character to be rendered
|
||||
// blank.
|
||||
mFontPattern = FcFreeTypeQueryFace(mFTFace, ToFcChar8Ptr(""), 0, nullptr);
|
||||
FcPattern* pattern =
|
||||
FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr);
|
||||
// given that we have a FT_Face, not really sure this is possible...
|
||||
if (!mFontPattern) {
|
||||
mFontPattern = FcPatternCreate();
|
||||
if (!pattern) {
|
||||
pattern = FcPatternCreate();
|
||||
}
|
||||
FcPatternDel(mFontPattern, FC_FILE);
|
||||
FcPatternDel(mFontPattern, FC_INDEX);
|
||||
FcPatternDel(pattern, FC_FILE);
|
||||
FcPatternDel(pattern, FC_INDEX);
|
||||
|
||||
// Make a new pattern and store the face in it so that cairo uses
|
||||
// that when creating a cairo font face.
|
||||
FcPatternAddFTFace(mFontPattern, FC_FT_FACE, mFTFace);
|
||||
FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
static FT_Face
|
||||
CreateFaceForPattern(FcPattern* aPattern)
|
||||
{
|
||||
FcChar8 *filename;
|
||||
if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
|
||||
return nullptr;
|
||||
}
|
||||
int index;
|
||||
if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
|
||||
index = 0; // default to 0 if not found in pattern
|
||||
}
|
||||
return Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
|
||||
}
|
||||
|
||||
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle,
|
||||
const uint8_t *aData,
|
||||
uint32_t aLength,
|
||||
FT_Face aFace)
|
||||
: gfxFontEntry(aFaceName),
|
||||
mFTFace(aFace), mFTFaceInitialized(true),
|
||||
mIgnoreFcCharmap(true),
|
||||
mAspect(0.0), mFontData(aData), mLength(aLength)
|
||||
{
|
||||
mWeight = aWeight;
|
||||
mStyle = aStyle;
|
||||
mStretch = aStretch;
|
||||
mIsDataUserFont = true;
|
||||
|
||||
mFontPattern = CreatePatternForFace(mFTFace);
|
||||
|
||||
mUserFontData = new FTUserFontData(mFTFace, mFontData);
|
||||
}
|
||||
|
@ -312,7 +336,7 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
|||
uint8_t aStyle)
|
||||
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
|
||||
mFTFace(nullptr), mFTFaceInitialized(false),
|
||||
mAspect(0.0), mFontData(nullptr)
|
||||
mAspect(0.0), mFontData(nullptr), mLength(0)
|
||||
{
|
||||
mWeight = aWeight;
|
||||
mStyle = aStyle;
|
||||
|
@ -694,8 +718,19 @@ gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
|
|||
FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
|
||||
}
|
||||
|
||||
AutoTArray<FT_Fixed,8> coords;
|
||||
if (!aStyle->variationSettings.IsEmpty()) {
|
||||
FT_Face ftFace = GetFTFace();
|
||||
if (ftFace) {
|
||||
gfxFT2FontBase::SetupVarCoords(ftFace, aStyle->variationSettings,
|
||||
&coords);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_font_face_t *face =
|
||||
cairo_ft_font_face_create_for_pattern(aRenderPattern);
|
||||
cairo_ft_font_face_create_for_pattern(aRenderPattern,
|
||||
coords.Elements(),
|
||||
coords.Length());
|
||||
|
||||
if (mFontData) {
|
||||
// for data fonts, add the face/data pointer to the cairo font face
|
||||
|
@ -887,9 +922,40 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
|
|||
double size = ChooseFontSize(this, *aFontStyle);
|
||||
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
|
||||
|
||||
FT_Face face = mFTFace;
|
||||
FcPattern* fontPattern = mFontPattern;
|
||||
if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
// For variation fonts, we create a new FT_Face and FcPattern here
|
||||
// so that variation coordinates from the style can be applied
|
||||
// without affecting other font instances created from the same
|
||||
// entry (font resource).
|
||||
if (mFontData) {
|
||||
// For user fonts: create a new FT_Face from the font data, and then
|
||||
// make a pattern from that.
|
||||
face = Factory::NewFTFaceFromData(nullptr, mFontData, mLength, 0);
|
||||
fontPattern = CreatePatternForFace(face);
|
||||
} else {
|
||||
// For system fonts: create a new FT_Face and store it in a copy of
|
||||
// the original mFontPattern.
|
||||
fontPattern = FcPatternDuplicate(mFontPattern);
|
||||
face = CreateFaceForPattern(fontPattern);
|
||||
if (face) {
|
||||
FcPatternAddFTFace(fontPattern, FC_FT_FACE, face);
|
||||
} else {
|
||||
// I don't think CreateFaceForPattern above should ever fail,
|
||||
// but just in case let's fall back here.
|
||||
face = mFTFace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PreparePattern(pattern, aFontStyle->printerFont);
|
||||
nsAutoRef<FcPattern> renderPattern
|
||||
(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
|
||||
(FcFontRenderPrepare(nullptr, pattern, fontPattern));
|
||||
if (fontPattern != mFontPattern) {
|
||||
// Discard temporary pattern used for variation support
|
||||
FcPatternDestroy(fontPattern);
|
||||
}
|
||||
if (!renderPattern) {
|
||||
NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
|
||||
return nullptr;
|
||||
|
@ -914,7 +980,7 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
|
|||
if (!unscaledFont) {
|
||||
unscaledFont =
|
||||
mFontData ?
|
||||
new UnscaledFontFontconfig(mFTFace) :
|
||||
new UnscaledFontFontconfig(face) :
|
||||
new UnscaledFontFontconfig(ToCharPtr(file), index);
|
||||
mUnscaledFontCache.Add(unscaledFont);
|
||||
}
|
||||
|
@ -928,6 +994,16 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
|
|||
return newFont;
|
||||
}
|
||||
|
||||
FT_Face
|
||||
gfxFontconfigFontEntry::GetFTFace()
|
||||
{
|
||||
if (!mFTFaceInitialized) {
|
||||
mFTFaceInitialized = true;
|
||||
mFTFace = CreateFaceForPattern(mFontPattern);
|
||||
}
|
||||
return mFTFace;
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
|
||||
nsTArray<uint8_t>& aBuffer)
|
||||
|
@ -935,34 +1011,19 @@ gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
|
|||
NS_ASSERTION(!mIsDataUserFont,
|
||||
"data fonts should be reading tables directly from memory");
|
||||
|
||||
if (!mFTFaceInitialized) {
|
||||
mFTFaceInitialized = true;
|
||||
FcChar8 *filename;
|
||||
if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
int index;
|
||||
if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
|
||||
index = 0; // default to 0 if not found in pattern
|
||||
}
|
||||
mFTFace = Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
|
||||
if (!mFTFace) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mFTFace) {
|
||||
FT_Face face = GetFTFace();
|
||||
if (!face) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
FT_ULong length = 0;
|
||||
if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
|
||||
if (FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &length) != 0) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (!aBuffer.SetLength(length, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
|
||||
if (FT_Load_Sfnt_Table(face, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
|
||||
aBuffer.Clear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -1229,7 +1290,7 @@ gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnsc
|
|||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle,
|
||||
bool aNeedsBold)
|
||||
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
|
||||
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle, aNeedsBold)
|
||||
, mPattern(aPattern)
|
||||
{
|
||||
mAdjustedSize = aAdjustedSize;
|
||||
|
@ -1691,7 +1752,7 @@ gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
|
|||
}
|
||||
|
||||
return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
|
||||
aStyle, aFontData, face);
|
||||
aStyle, aFontData, aLength, face);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
int16_t aStretch,
|
||||
uint8_t aStyle,
|
||||
const uint8_t *aData,
|
||||
uint32_t aLength,
|
||||
FT_Face aFace);
|
||||
|
||||
// used for @font-face local system fonts with explicit patterns
|
||||
|
@ -112,6 +113,8 @@ public:
|
|||
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
|
||||
bool TestCharacterMap(uint32_t aCh) override;
|
||||
|
||||
FT_Face GetFTFace();
|
||||
|
||||
hb_blob_t* GetFontTable(uint32_t aTableTag) override;
|
||||
|
||||
void ForgetHBFace() override;
|
||||
|
@ -161,6 +164,7 @@ protected:
|
|||
|
||||
// data font
|
||||
const uint8_t* mFontData;
|
||||
uint32_t mLength;
|
||||
|
||||
class UnscaledFontCache
|
||||
{
|
||||
|
|
|
@ -126,16 +126,16 @@ function array2ContentBail2(i) {
|
|||
return a.length;
|
||||
}
|
||||
|
||||
// We don't handle COW array writes
|
||||
function arrayWrite1(i) {
|
||||
var a = [1, 2];
|
||||
a[0] = 42;
|
||||
assertEq(a[0], 42);
|
||||
assertEq(a[1], 2);
|
||||
assertRecoveredOnBailout(a, false);
|
||||
assertRecoveredOnBailout(a, true);
|
||||
return a.length;
|
||||
}
|
||||
|
||||
// We don't handle length sets yet.
|
||||
function arrayWrite2(i) {
|
||||
var a = [1, 2];
|
||||
a.length = 1;
|
||||
|
@ -145,6 +145,44 @@ function arrayWrite2(i) {
|
|||
return a.length;
|
||||
}
|
||||
|
||||
function arrayWrite3(i) {
|
||||
var a = [1, 2, 0];
|
||||
if (i % 2 === 1)
|
||||
a[0] = 2;
|
||||
assertEq(a[0], 1 + (i % 2));
|
||||
assertRecoveredOnBailout(a, true);
|
||||
if (i % 2 === 1)
|
||||
bailout();
|
||||
assertEq(a[0], 1 + (i % 2));
|
||||
return a.length;
|
||||
}
|
||||
|
||||
function arrayWrite4(i) {
|
||||
var a = [1, 2, 0];
|
||||
for (var x = 0; x < 2; x++) {
|
||||
if (x % 2 === 1)
|
||||
bailout();
|
||||
else
|
||||
a[0] = a[0] + 1;
|
||||
}
|
||||
assertEq(a[0], 2);
|
||||
assertEq(a[1], 2);
|
||||
assertRecoveredOnBailout(a, true);
|
||||
return a.length;
|
||||
}
|
||||
|
||||
function arrayWriteDoubles(i) {
|
||||
var a = [0, 0, 0];
|
||||
a[0] = 3.14;
|
||||
// MConvertElementsToDoubles is only used for loads inside a loop.
|
||||
for (var x = 0; x < 2; x++) {
|
||||
assertEq(a[0], 3.14);
|
||||
assertEq(a[1], 0);
|
||||
}
|
||||
assertRecoveredOnBailout(a, true);
|
||||
return a.length;
|
||||
}
|
||||
|
||||
// Check escape analysis in case of holes.
|
||||
function arrayHole0(i) {
|
||||
var a = [1,,3];
|
||||
|
@ -200,6 +238,9 @@ for (var i = 0; i < 100; i++) {
|
|||
array2ContentBail2(i);
|
||||
arrayWrite1(i);
|
||||
arrayWrite2(i);
|
||||
arrayWrite3(i);
|
||||
arrayWrite4(i);
|
||||
arrayWriteDoubles(i);
|
||||
arrayHole0(i);
|
||||
arrayAlloc(i);
|
||||
}
|
||||
|
|
|
@ -1762,7 +1762,10 @@ RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const
|
|||
uint32_t initLength = iter.read().toInt32();
|
||||
|
||||
if (!object->denseElementsAreCopyOnWrite()) {
|
||||
MOZ_ASSERT(object->getDenseInitializedLength() == 0,
|
||||
"initDenseElement call below relies on this");
|
||||
object->setDenseInitializedLength(initLength);
|
||||
|
||||
for (size_t index = 0; index < numElements(); index++) {
|
||||
Value val = iter.read();
|
||||
|
||||
|
@ -1774,12 +1777,16 @@ RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const
|
|||
object->initDenseElement(index, val);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(object->getDenseInitializedLength() == numElements());
|
||||
MOZ_ASSERT(initLength == numElements());
|
||||
MOZ_RELEASE_ASSERT(object->getDenseInitializedLength() == numElements());
|
||||
MOZ_RELEASE_ASSERT(initLength == numElements());
|
||||
|
||||
for (size_t index = 0; index < numElements(); index++) {
|
||||
Value val = iter.read();
|
||||
MOZ_RELEASE_ASSERT(object->getDenseElement(index) == val);
|
||||
if (object->getDenseElement(index) == val)
|
||||
continue;
|
||||
if (!object->maybeCopyElementsForWrite(cx))
|
||||
return false;
|
||||
object->setDenseElement(index, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -875,8 +875,10 @@ IndexOf(MDefinition* ins, int32_t* res)
|
|||
// Returns False if the elements is not escaped and if it is optimizable by
|
||||
// ScalarReplacementOfArray.
|
||||
static bool
|
||||
IsElementEscaped(MElements* def, uint32_t arraySize, bool copyOnWrite)
|
||||
IsElementEscaped(MDefinition* def, uint32_t arraySize)
|
||||
{
|
||||
MOZ_ASSERT(def->isElements() || def->isConvertElementsToDoubles());
|
||||
|
||||
JitSpewDef(JitSpew_Escape, "Check elements\n", def);
|
||||
JitSpewIndent spewIndent(JitSpew_Escape);
|
||||
|
||||
|
@ -919,11 +921,6 @@ IsElementEscaped(MElements* def, uint32_t arraySize, bool copyOnWrite)
|
|||
case MDefinition::Opcode::StoreElement: {
|
||||
MOZ_ASSERT(access->toStoreElement()->elements() == def);
|
||||
|
||||
if (copyOnWrite) {
|
||||
JitSpewDef(JitSpew_Escape, "write to COW\n", access);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we need hole checks, then the array cannot be escaped
|
||||
// as the array might refer to the prototype chain to look
|
||||
// for properties, thus it might do additional side-effects
|
||||
|
@ -956,11 +953,6 @@ IsElementEscaped(MElements* def, uint32_t arraySize, bool copyOnWrite)
|
|||
}
|
||||
|
||||
case MDefinition::Opcode::SetInitializedLength:
|
||||
if (copyOnWrite) {
|
||||
JitSpewDef(JitSpew_Escape, "write to COW\n", access);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(access->toSetInitializedLength()->elements() == def);
|
||||
break;
|
||||
|
||||
|
@ -972,6 +964,14 @@ IsElementEscaped(MElements* def, uint32_t arraySize, bool copyOnWrite)
|
|||
MOZ_ASSERT(access->toArrayLength()->elements() == def);
|
||||
break;
|
||||
|
||||
case MDefinition::Opcode::ConvertElementsToDoubles:
|
||||
MOZ_ASSERT(access->toConvertElementsToDoubles()->elements() == def);
|
||||
if (IsElementEscaped(access, arraySize)) {
|
||||
JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", access);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
JitSpewDef(JitSpew_Escape, "is escaped by\n", access);
|
||||
return true;
|
||||
|
@ -993,24 +993,26 @@ IsOptimizableArrayInstruction(MInstruction* ins)
|
|||
// For the moment, this code is dumb as it only supports arrays which are not
|
||||
// changing length, with only access with known constants.
|
||||
static bool
|
||||
IsArrayEscaped(MInstruction* ins)
|
||||
IsArrayEscaped(MInstruction* ins, MInstruction* newArray)
|
||||
{
|
||||
MOZ_ASSERT(ins->type() == MIRType::Object);
|
||||
MOZ_ASSERT(IsOptimizableArrayInstruction(ins));
|
||||
MOZ_ASSERT(IsOptimizableArrayInstruction(ins) ||
|
||||
ins->isMaybeCopyElementsForWrite());
|
||||
MOZ_ASSERT(IsOptimizableArrayInstruction(newArray));
|
||||
|
||||
JitSpewDef(JitSpew_Escape, "Check array\n", ins);
|
||||
JitSpewIndent spewIndent(JitSpew_Escape);
|
||||
|
||||
uint32_t length;
|
||||
if (ins->isNewArray()) {
|
||||
if (!ins->toNewArray()->templateObject()) {
|
||||
if (newArray->isNewArray()) {
|
||||
if (!newArray->toNewArray()->templateObject()) {
|
||||
JitSpew(JitSpew_Escape, "No template object defined.");
|
||||
return true;
|
||||
}
|
||||
|
||||
length = ins->toNewArray()->length();
|
||||
length = newArray->toNewArray()->length();
|
||||
} else {
|
||||
length = ins->toNewArrayCopyOnWrite()->templateObject()->length();
|
||||
length = newArray->toNewArrayCopyOnWrite()->templateObject()->length();
|
||||
}
|
||||
|
||||
if (length >= 16) {
|
||||
|
@ -1037,7 +1039,7 @@ IsArrayEscaped(MInstruction* ins)
|
|||
case MDefinition::Opcode::Elements: {
|
||||
MElements *elem = def->toElements();
|
||||
MOZ_ASSERT(elem->object() == ins);
|
||||
if (IsElementEscaped(elem, length, ins->isNewArrayCopyOnWrite())) {
|
||||
if (IsElementEscaped(elem, length)) {
|
||||
JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", elem);
|
||||
return true;
|
||||
}
|
||||
|
@ -1045,6 +1047,16 @@ IsArrayEscaped(MInstruction* ins)
|
|||
break;
|
||||
}
|
||||
|
||||
case MDefinition::Opcode::MaybeCopyElementsForWrite: {
|
||||
MMaybeCopyElementsForWrite* copied = def->toMaybeCopyElementsForWrite();
|
||||
MOZ_ASSERT(copied->object() == ins);
|
||||
if (IsArrayEscaped(copied, ins)) {
|
||||
JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", copied);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// This instruction is a no-op used to verify that scalar replacement
|
||||
// is working as expected in jit-test.
|
||||
case MDefinition::Opcode::AssertRecoveredOnBailout:
|
||||
|
@ -1114,6 +1126,8 @@ class ArrayMemoryView : public MDefinitionVisitorDefaultNoop
|
|||
void visitSetInitializedLength(MSetInitializedLength* ins);
|
||||
void visitInitializedLength(MInitializedLength* ins);
|
||||
void visitArrayLength(MArrayLength* ins);
|
||||
void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
|
||||
void visitConvertElementsToDoubles(MConvertElementsToDoubles* ins);
|
||||
};
|
||||
|
||||
const char* ArrayMemoryView::phaseName = "Scalar Replacement of Array";
|
||||
|
@ -1406,6 +1420,47 @@ ArrayMemoryView::visitArrayLength(MArrayLength* ins)
|
|||
discardInstruction(ins, elements);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayMemoryView::visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->numOperands() == 1);
|
||||
MOZ_ASSERT(ins->type() == MIRType::Object);
|
||||
|
||||
// Skip guards on other objects.
|
||||
if (ins->object() != arr_)
|
||||
return;
|
||||
|
||||
// Nothing to do here: RArrayState::recover will copy the elements if
|
||||
// needed.
|
||||
|
||||
// Replace the guard with the array.
|
||||
ins->replaceAllUsesWith(arr_);
|
||||
|
||||
// Remove original instruction.
|
||||
ins->block()->discard(ins);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayMemoryView::visitConvertElementsToDoubles(MConvertElementsToDoubles* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->numOperands() == 1);
|
||||
MOZ_ASSERT(ins->type() == MIRType::Elements);
|
||||
|
||||
// Skip other array objects.
|
||||
MDefinition* elements = ins->elements();
|
||||
if (!isArrayStateElements(elements))
|
||||
return;
|
||||
|
||||
// We don't have to do anything else here: MConvertElementsToDoubles just
|
||||
// exists to allow MLoadELement to use masm.loadDouble (without checking
|
||||
// for int32 elements), but since we're using scalar replacement for the
|
||||
// elements that doesn't matter.
|
||||
ins->replaceAllUsesWith(elements);
|
||||
|
||||
// Remove original instruction.
|
||||
ins->block()->discard(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
ScalarReplacement(MIRGenerator* mir, MIRGraph& graph)
|
||||
{
|
||||
|
@ -1428,7 +1483,7 @@ ScalarReplacement(MIRGenerator* mir, MIRGraph& graph)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (IsOptimizableArrayInstruction(*ins) && !IsArrayEscaped(*ins)) {
|
||||
if (IsOptimizableArrayInstruction(*ins) && !IsArrayEscaped(*ins, *ins)) {
|
||||
ArrayMemoryView view(graph.alloc(), *ins);
|
||||
if (!replaceArray.run(view))
|
||||
return false;
|
||||
|
|
|
@ -1633,12 +1633,6 @@ struct nsGridContainerFrame::Tracks
|
|||
if (aIndex < lineNameLists.Length()) {
|
||||
lineNames.AppendElements(lineNameLists[aIndex]);
|
||||
}
|
||||
if (aIndex == repeatAutoEnd) {
|
||||
uint32_t i = aIndex + 1;
|
||||
if (i < lineNameLists.Length()) {
|
||||
lineNames.AppendElements(lineNameLists[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aIndex <= repeatAutoEnd && aIndex > repeatAutoStart) {
|
||||
lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListAfter);
|
||||
|
@ -1646,7 +1640,7 @@ struct nsGridContainerFrame::Tracks
|
|||
if (aIndex < repeatAutoEnd && aIndex >= repeatAutoStart) {
|
||||
lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListBefore);
|
||||
}
|
||||
if (aIndex >= repeatAutoEnd && aIndex > repeatAutoStart) {
|
||||
if (aIndex > repeatAutoEnd && aIndex > repeatAutoStart) {
|
||||
uint32_t i = aIndex - repeatEndDelta;
|
||||
if (i < lineNameLists.Length()) {
|
||||
lineNames.AppendElements(lineNameLists[i]);
|
||||
|
@ -6241,10 +6235,22 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
columnLineNames.AppendElement(explicitNames);
|
||||
}
|
||||
// Get the explicit names that follow a repeat auto declaration.
|
||||
nsTArray<nsString> colNamesFollowingRepeat;
|
||||
if (gridColTemplate.HasRepeatAuto()) {
|
||||
// The line name list after the repeatAutoIndex holds the line names
|
||||
// for the first explicit line after the repeat auto declaration.
|
||||
uint32_t repeatAutoEnd = gridColTemplate.mRepeatAutoIndex + 1;
|
||||
MOZ_ASSERT(repeatAutoEnd < gridColTemplate.mLineNameLists.Length());
|
||||
colNamesFollowingRepeat.AppendElements(
|
||||
gridColTemplate.mLineNameLists[repeatAutoEnd]);
|
||||
}
|
||||
|
||||
ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
|
||||
Move(columnLineNames),
|
||||
gridColTemplate.mRepeatAutoLineNameListBefore,
|
||||
gridColTemplate.mRepeatAutoLineNameListAfter);
|
||||
gridColTemplate.mRepeatAutoLineNameListAfter,
|
||||
Move(colNamesFollowingRepeat));
|
||||
SetProperty(GridColumnLineInfo(), columnLineInfo);
|
||||
|
||||
// Generate row lines next.
|
||||
|
@ -6262,10 +6268,22 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
rowLineNames.AppendElement(explicitNames);
|
||||
}
|
||||
// Get the explicit names that follow a repeat auto declaration.
|
||||
nsTArray<nsString> rowNamesFollowingRepeat;
|
||||
if (gridRowTemplate.HasRepeatAuto()) {
|
||||
// The line name list after the repeatAutoIndex holds the line names
|
||||
// for the first explicit line after the repeat auto declaration.
|
||||
uint32_t repeatAutoEnd = gridRowTemplate.mRepeatAutoIndex + 1;
|
||||
MOZ_ASSERT(repeatAutoEnd < gridRowTemplate.mLineNameLists.Length());
|
||||
rowNamesFollowingRepeat.AppendElements(
|
||||
gridRowTemplate.mLineNameLists[repeatAutoEnd]);
|
||||
}
|
||||
|
||||
ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
|
||||
Move(rowLineNames),
|
||||
gridRowTemplate.mRepeatAutoLineNameListBefore,
|
||||
gridRowTemplate.mRepeatAutoLineNameListAfter);
|
||||
gridRowTemplate.mRepeatAutoLineNameListAfter,
|
||||
Move(rowNamesFollowingRepeat));
|
||||
SetProperty(GridRowLineInfo(), rowLineInfo);
|
||||
|
||||
// Generate area info for explicit areas. Implicit areas are handled
|
||||
|
|
|
@ -70,14 +70,17 @@ struct ComputedGridLineInfo
|
|||
{
|
||||
explicit ComputedGridLineInfo(nsTArray<nsTArray<nsString>>&& aNames,
|
||||
const nsTArray<nsString>& aNamesBefore,
|
||||
const nsTArray<nsString>& aNamesAfter)
|
||||
const nsTArray<nsString>& aNamesAfter,
|
||||
nsTArray<nsString>&& aNamesFollowingRepeat)
|
||||
: mNames(aNames)
|
||||
, mNamesBefore(aNamesBefore)
|
||||
, mNamesAfter(aNamesAfter)
|
||||
, mNamesFollowingRepeat(aNamesFollowingRepeat)
|
||||
{}
|
||||
nsTArray<nsTArray<nsString>> mNames;
|
||||
nsTArray<nsString> mNamesBefore;
|
||||
nsTArray<nsString> mNamesAfter;
|
||||
nsTArray<nsString> mNamesFollowingRepeat;
|
||||
};
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
{
|
||||
return mPtr;
|
||||
}
|
||||
MOZ_IMPLICIT operator bool() const
|
||||
explicit operator bool() const
|
||||
{
|
||||
return mPtr != nullptr;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,14 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "weaveXPCService", function() {
|
||||
try {
|
||||
return Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
} catch (ex) {
|
||||
// The app didn't build Sync.
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "Weave", () => {
|
||||
|
@ -116,7 +121,7 @@ this.PlacesRemoteTabsAutocompleteProvider = {
|
|||
// a promise that resolves with an array of matching remote tabs.
|
||||
getMatches(searchString) {
|
||||
// If Sync isn't configured we bail early.
|
||||
if (!weaveXPCService.ready || !weaveXPCService.enabled) {
|
||||
if (!weaveXPCService || !weaveXPCService.ready || !weaveXPCService.enabled) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче