mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Merge inbound to mozilla-central. a=merge
This commit is contained in:
commit
e8d8a7f9a3
@ -1477,7 +1477,7 @@ var gBrowserInit = {
|
||||
|
||||
// Setup click-and-hold gestures access to the session history
|
||||
// menus if global click-and-hold isn't turned on
|
||||
if (!getBoolPref("ui.click_hold_context_menus", false))
|
||||
if (!Services.prefs.getBoolPref("ui.click_hold_context_menus", false))
|
||||
SetClickAndHoldHandlers();
|
||||
|
||||
PlacesToolbarHelper.init();
|
||||
@ -2148,7 +2148,7 @@ function BrowserGoHome(aEvent) {
|
||||
case "tabshifted":
|
||||
case "tab":
|
||||
urls = homePage.split("|");
|
||||
var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
|
||||
var loadInBackground = Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground", false);
|
||||
// The homepage observer event should only be triggered when the homepage opens
|
||||
// in the foreground. This is mostly to support the homepage changed by extension
|
||||
// doorhanger which doesn't currently support background pages. This may change in
|
||||
|
@ -5,7 +5,6 @@
|
||||
const gTests = [
|
||||
test_eventMatchesKey,
|
||||
test_getTopWin,
|
||||
test_getBoolPref,
|
||||
test_openNewTabWith,
|
||||
test_openUILink,
|
||||
];
|
||||
@ -81,14 +80,6 @@ function test_getTopWin() {
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
|
||||
function test_getBoolPref() {
|
||||
is(getBoolPref("browser.search.openintab", false), false, "getBoolPref");
|
||||
is(getBoolPref("this.pref.doesnt.exist", true), true, "getBoolPref fallback");
|
||||
is(getBoolPref("this.pref.doesnt.exist", false), false, "getBoolPref fallback #2");
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
function test_openNewTabWith() {
|
||||
openNewTabWith("http://example.com/", null, {triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})});
|
||||
let tab = gBrowser.selectedTab = gBrowser.tabs[1];
|
||||
|
@ -62,14 +62,6 @@ function getTopWin(skipPopups) {
|
||||
});
|
||||
}
|
||||
|
||||
function getBoolPref(prefname, def) {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(prefname);
|
||||
} catch (er) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
function doGetProtocolFlags(aURI) {
|
||||
let handler = Services.io.getProtocolHandler(aURI.scheme);
|
||||
// see DoGetProtocolFlags in nsIProtocolHandler.idl
|
||||
@ -163,7 +155,7 @@ function whereToOpenLink(e, ignoreButton, ignoreAlt) {
|
||||
|
||||
// ignoreButton allows "middle-click paste" to use function without always opening in a new window.
|
||||
var middle = !ignoreButton && e.button == 1;
|
||||
var middleUsesTabs = getBoolPref("browser.tabs.opentabfor.middleclick", true);
|
||||
var middleUsesTabs = Services.prefs.getBoolPref("browser.tabs.opentabfor.middleclick", true);
|
||||
|
||||
// Don't do anything special with right-mouse clicks. They're probably clicks on context menu items.
|
||||
|
||||
@ -171,7 +163,7 @@ function whereToOpenLink(e, ignoreButton, ignoreAlt) {
|
||||
if (metaKey || (middle && middleUsesTabs))
|
||||
return shift ? "tabshifted" : "tab";
|
||||
|
||||
if (alt && getBoolPref("browser.altClickSave", false))
|
||||
if (alt && Services.prefs.getBoolPref("browser.altClickSave", false))
|
||||
return "save";
|
||||
|
||||
if (shift || (middle && !middleUsesTabs))
|
||||
@ -472,7 +464,7 @@ function openLinkIn(url, where, params) {
|
||||
loadInBackground = aInBackground;
|
||||
if (loadInBackground == null) {
|
||||
loadInBackground =
|
||||
aFromChrome ? false : getBoolPref("browser.tabs.loadInBackground");
|
||||
aFromChrome ? false : Services.prefs.getBoolPref("browser.tabs.loadInBackground");
|
||||
}
|
||||
}
|
||||
|
||||
@ -777,7 +769,7 @@ function getShellService() {
|
||||
|
||||
function isBidiEnabled() {
|
||||
// first check the pref.
|
||||
if (getBoolPref("bidi.browser.ui", false))
|
||||
if (Services.prefs.getBoolPref("bidi.browser.ui", false))
|
||||
return true;
|
||||
|
||||
// now see if the app locale is an RTL one.
|
||||
@ -977,7 +969,7 @@ function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
|
||||
function openPrefsHelp() {
|
||||
// non-instant apply prefwindows are usually modal, so we can't open in the topmost window,
|
||||
// since its probably behind the window.
|
||||
var instantApply = getBoolPref("browser.preferences.instantApply");
|
||||
var instantApply = Services.prefs.getBoolPref("browser.preferences.instantApply");
|
||||
|
||||
var helpTopic = document.documentElement.getAttribute("helpTopic");
|
||||
openHelpLink(helpTopic, !instantApply);
|
||||
|
@ -381,8 +381,8 @@ HistoryListener.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
OnHistoryGotoIndex(index, gotoURI) { return true; },
|
||||
OnHistoryPurge(numEntries) { return true; },
|
||||
OnHistoryGotoIndex(index, gotoURI) {},
|
||||
OnHistoryPurge(numEntries) {},
|
||||
OnHistoryReplaceEntry(index) {},
|
||||
|
||||
// This will be called for a pending tab when loadURI(uri) is called where
|
||||
@ -417,14 +417,6 @@ HistoryListener.prototype = {
|
||||
// Cancel the load.
|
||||
return false;
|
||||
},
|
||||
|
||||
OnLengthChanged(aCount) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
},
|
||||
|
||||
OnIndexChanged(aIndex) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -301,12 +301,10 @@ class SessionHistoryListener extends Handler {
|
||||
OnHistoryGotoIndex(index, gotoURI) {
|
||||
// We ought to collect the previously current entry as well, see bug 1350567.
|
||||
this.collectFrom(kLastIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnHistoryPurge(numEntries) {
|
||||
this.collect();
|
||||
return true;
|
||||
}
|
||||
|
||||
OnHistoryReload(reloadURI, reloadFlags) {
|
||||
@ -317,14 +315,6 @@ class SessionHistoryListener extends Handler {
|
||||
OnHistoryReplaceEntry(index) {
|
||||
this.collect();
|
||||
}
|
||||
|
||||
OnLengthChanged(aCount) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
}
|
||||
|
||||
OnIndexChanged(aIndex) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
}
|
||||
}
|
||||
SessionHistoryListener.prototype.QueryInterface =
|
||||
ChromeUtils.generateQI([Ci.nsISHistoryListener,
|
||||
|
@ -19,12 +19,10 @@ var historyListener = {
|
||||
|
||||
OnHistoryGotoIndex() {
|
||||
sendAsyncMessage("ss-test:OnHistoryGotoIndex");
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryPurge() {
|
||||
sendAsyncMessage("ss-test:OnHistoryPurge");
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryReload() {
|
||||
|
@ -1,22 +1,20 @@
|
||||
{
|
||||
"llvm_revision": "338614",
|
||||
"llvm_revision": "341816",
|
||||
"stages": "3",
|
||||
"build_libcxx": false,
|
||||
"build_type": "Release",
|
||||
"assertions": false,
|
||||
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
|
||||
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
|
||||
"lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
|
||||
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
|
||||
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
|
||||
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc3",
|
||||
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc3",
|
||||
"lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/rc3",
|
||||
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc3",
|
||||
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc3",
|
||||
"python_path": "c:/mozilla-build/python/python.exe",
|
||||
"cc": "cl.exe",
|
||||
"cxx": "cl.exe",
|
||||
"ml": "ml64.exe",
|
||||
"patches": [
|
||||
"workaround-issue38586.patch",
|
||||
"r339636.patch",
|
||||
"r341035.patch",
|
||||
"loosen-msvc-detection.patch"
|
||||
]
|
||||
}
|
||||
|
@ -1,395 +0,0 @@
|
||||
Index: lld/COFF/Driver.cpp
|
||||
===================================================================
|
||||
--- a/lld/COFF/Driver.cpp (revision 341034)
|
||||
+++ b/lld/COFF/Driver.cpp (revision 341035)
|
||||
@@ -116,6 +116,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
+// Symbol names are mangled by prepending "_" on x86.
|
||||
+static StringRef mangle(StringRef Sym) {
|
||||
+ assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
|
||||
+ if (Config->Machine == I386)
|
||||
+ return Saver.save("_" + Sym);
|
||||
+ return Sym;
|
||||
+}
|
||||
+
|
||||
+static bool findUnderscoreMangle(StringRef Sym) {
|
||||
+ StringRef Entry = Symtab->findMangle(mangle(Sym));
|
||||
+ return !Entry.empty() && !isa<Undefined>(Symtab->find(Entry));
|
||||
+}
|
||||
+
|
||||
MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
|
||||
MemoryBufferRef MBRef = *MB;
|
||||
make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership
|
||||
@@ -407,14 +420,6 @@
|
||||
return B;
|
||||
}
|
||||
|
||||
-// Symbol names are mangled by appending "_" prefix on x86.
|
||||
-StringRef LinkerDriver::mangle(StringRef Sym) {
|
||||
- assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
|
||||
- if (Config->Machine == I386)
|
||||
- return Saver.save("_" + Sym);
|
||||
- return Sym;
|
||||
-}
|
||||
-
|
||||
// Windows specific -- find default entry point name.
|
||||
//
|
||||
// There are four different entry point functions for Windows executables,
|
||||
@@ -421,31 +426,23 @@
|
||||
// each of which corresponds to a user-defined "main" function. This function
|
||||
// infers an entry point from a user-defined "main" function.
|
||||
StringRef LinkerDriver::findDefaultEntry() {
|
||||
+ assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
|
||||
+ "must handle /subsystem before calling this");
|
||||
+
|
||||
// As a special case, if /nodefaultlib is given, we directly look for an
|
||||
// entry point. This is because, if no default library is linked, users
|
||||
// need to define an entry point instead of a "main".
|
||||
- if (Config->NoDefaultLibAll) {
|
||||
- for (StringRef S : {"mainCRTStartup", "wmainCRTStartup",
|
||||
- "WinMainCRTStartup", "wWinMainCRTStartup"}) {
|
||||
- StringRef Entry = Symtab->findMangle(S);
|
||||
- if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
|
||||
- return mangle(S);
|
||||
- }
|
||||
- return "";
|
||||
+ bool FindMain = !Config->NoDefaultLibAll;
|
||||
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
|
||||
+ if (findUnderscoreMangle(FindMain ? "WinMain" : "WinMainCRTStartup"))
|
||||
+ return mangle("WinMainCRTStartup");
|
||||
+ if (findUnderscoreMangle(FindMain ? "wWinMain" : "wWinMainCRTStartup"))
|
||||
+ return mangle("wWinMainCRTStartup");
|
||||
}
|
||||
-
|
||||
- // User-defined main functions and their corresponding entry points.
|
||||
- static const char *Entries[][2] = {
|
||||
- {"main", "mainCRTStartup"},
|
||||
- {"wmain", "wmainCRTStartup"},
|
||||
- {"WinMain", "WinMainCRTStartup"},
|
||||
- {"wWinMain", "wWinMainCRTStartup"},
|
||||
- };
|
||||
- for (auto E : Entries) {
|
||||
- StringRef Entry = Symtab->findMangle(mangle(E[0]));
|
||||
- if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
|
||||
- return mangle(E[1]);
|
||||
- }
|
||||
+ if (findUnderscoreMangle(FindMain ? "main" : "mainCRTStartup"))
|
||||
+ return mangle("mainCRTStartup");
|
||||
+ if (findUnderscoreMangle(FindMain ? "wmain" : "wmainCRTStartup"))
|
||||
+ return mangle("wmainCRTStartup");
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -452,9 +449,9 @@
|
||||
WindowsSubsystem LinkerDriver::inferSubsystem() {
|
||||
if (Config->DLL)
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
- if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain"))
|
||||
+ if (findUnderscoreMangle("main") || findUnderscoreMangle("wmain"))
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
||||
- if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain"))
|
||||
+ if (findUnderscoreMangle("WinMain") || findUnderscoreMangle("wWinMain"))
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
return IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
}
|
||||
@@ -1335,25 +1332,6 @@
|
||||
error("/dynamicbase:no is not compatible with " +
|
||||
machineToStr(Config->Machine));
|
||||
|
||||
- // Handle /entry and /dll
|
||||
- if (auto *Arg = Args.getLastArg(OPT_entry)) {
|
||||
- Config->Entry = addUndefined(mangle(Arg->getValue()));
|
||||
- } else if (!Config->Entry && !Config->NoEntry) {
|
||||
- if (Args.hasArg(OPT_dll)) {
|
||||
- StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
|
||||
- : "_DllMainCRTStartup";
|
||||
- Config->Entry = addUndefined(S);
|
||||
- } else {
|
||||
- // Windows specific -- If entry point name is not given, we need to
|
||||
- // infer that from user-defined entry name.
|
||||
- StringRef S = findDefaultEntry();
|
||||
- if (S.empty())
|
||||
- fatal("entry point must be defined");
|
||||
- Config->Entry = addUndefined(S);
|
||||
- log("Entry name inferred: " + S);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
// Handle /export
|
||||
for (auto *Arg : Args.filtered(OPT_export)) {
|
||||
Export E = parseExport(Arg->getValue());
|
||||
@@ -1379,6 +1357,34 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+ // Windows specific -- if no /subsystem is given, we need to infer
|
||||
+ // that from entry point name. Must happen before /entry handling,
|
||||
+ // and after the early return when just writing an import library.
|
||||
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
|
||||
+ Config->Subsystem = inferSubsystem();
|
||||
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
|
||||
+ fatal("subsystem must be defined");
|
||||
+ }
|
||||
+
|
||||
+ // Handle /entry and /dll
|
||||
+ if (auto *Arg = Args.getLastArg(OPT_entry)) {
|
||||
+ Config->Entry = addUndefined(mangle(Arg->getValue()));
|
||||
+ } else if (!Config->Entry && !Config->NoEntry) {
|
||||
+ if (Args.hasArg(OPT_dll)) {
|
||||
+ StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
|
||||
+ : "_DllMainCRTStartup";
|
||||
+ Config->Entry = addUndefined(S);
|
||||
+ } else {
|
||||
+ // Windows specific -- If entry point name is not given, we need to
|
||||
+ // infer that from user-defined entry name.
|
||||
+ StringRef S = findDefaultEntry();
|
||||
+ if (S.empty())
|
||||
+ fatal("entry point must be defined");
|
||||
+ Config->Entry = addUndefined(S);
|
||||
+ log("Entry name inferred: " + S);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Handle /delayload
|
||||
for (auto *Arg : Args.filtered(OPT_delayload)) {
|
||||
Config->DelayLoads.insert(StringRef(Arg->getValue()).lower());
|
||||
@@ -1491,14 +1497,6 @@
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
- // Windows specific -- if no /subsystem is given, we need to infer
|
||||
- // that from entry point name.
|
||||
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
|
||||
- Config->Subsystem = inferSubsystem();
|
||||
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
|
||||
- fatal("subsystem must be defined");
|
||||
- }
|
||||
-
|
||||
// Handle /safeseh.
|
||||
if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
|
||||
for (ObjFile *File : ObjFile::Instances)
|
||||
Index: lld/COFF/Driver.h
|
||||
===================================================================
|
||||
--- a/lld/COFF/Driver.h (revision 341034)
|
||||
+++ b/lld/COFF/Driver.h (revision 341035)
|
||||
@@ -103,7 +103,6 @@
|
||||
std::set<std::string> VisitedLibs;
|
||||
|
||||
Symbol *addUndefined(StringRef Sym);
|
||||
- StringRef mangle(StringRef Sym);
|
||||
|
||||
// Windows specific -- "main" is not the only main function in Windows.
|
||||
// You can choose one from these four -- {w,}{WinMain,main}.
|
||||
Index: lld/test/COFF/entry-inference332.test
|
||||
===================================================================
|
||||
--- a/lld/test/COFF/entry-inference332.test (nonexistent)
|
||||
+++ b/lld/test/COFF/entry-inference332.test (revision 341035)
|
||||
@@ -0,0 +1,39 @@
|
||||
+# RUN: sed -e s/ENTRYNAME/_mainCRTStartup/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /subsystem:console /out:%t.exe %t.obj /verbose /nodefaultlib > %t.log 2>&1
|
||||
+# RUN: FileCheck %s < %t.log
|
||||
+
|
||||
+# RUN: sed -e s/ENTRYNAME/?mainCRTStartup@@YAHXZ/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /subsystem:console /out:%t.exe %t.obj /verbose /nodefaultlib > %t.log 2>&1
|
||||
+# RUN: FileCheck %s < %t.log
|
||||
+
|
||||
+# CHECK: Entry name inferred: _mainCRTStartup
|
||||
+
|
||||
+--- !COFF
|
||||
+header:
|
||||
+ Machine: IMAGE_FILE_MACHINE_I386
|
||||
+ Characteristics: []
|
||||
+sections:
|
||||
+ - Name: .text
|
||||
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
|
||||
+ Alignment: 4
|
||||
+ SectionData: B82A000000C3
|
||||
+symbols:
|
||||
+ - Name: .text
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
+ SectionDefinition:
|
||||
+ Length: 6
|
||||
+ NumberOfRelocations: 0
|
||||
+ NumberOfLinenumbers: 0
|
||||
+ CheckSum: 0
|
||||
+ Number: 0
|
||||
+ - Name: "ENTRYNAME"
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+...
|
||||
Index: lld/test/COFF/entry-inference3.test
|
||||
===================================================================
|
||||
--- a/lld/test/COFF/entry-inference3.test (revision 341034)
|
||||
+++ b/lld/test/COFF/entry-inference3.test (revision 341035)
|
||||
@@ -1,7 +1,11 @@
|
||||
-# RUN: yaml2obj < %s > %t.obj
|
||||
-# RUN: not lld-link /out:%t.exe %t.obj /verbose /nodefaultlib > %t.log 2>&1
|
||||
+# RUN: sed -e s/ENTRYNAME/mainCRTStartup/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /subsystem:console /out:%t.exe %t.obj /verbose /nodefaultlib > %t.log 2>&1
|
||||
# RUN: FileCheck %s < %t.log
|
||||
|
||||
+# RUN: sed -e s/ENTRYNAME/?mainCRTStartup@@YAHXZ/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /subsystem:console /out:%t.exe %t.obj /verbose /nodefaultlib > %t.log 2>&1
|
||||
+# RUN: FileCheck %s < %t.log
|
||||
+
|
||||
# CHECK: Entry name inferred: mainCRTStartup
|
||||
|
||||
--- !COFF
|
||||
@@ -26,7 +30,7 @@
|
||||
NumberOfLinenumbers: 0
|
||||
CheckSum: 0
|
||||
Number: 0
|
||||
- - Name: mainCRTStartup
|
||||
+ - Name: "ENTRYNAME"
|
||||
Value: 0
|
||||
SectionNumber: 1
|
||||
SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
Index: lld/test/COFF/subsystem-inference32.test
|
||||
===================================================================
|
||||
--- a/lld/test/COFF/subsystem-inference32.test (nonexistent)
|
||||
+++ b/lld/test/COFF/subsystem-inference32.test (revision 341035)
|
||||
@@ -0,0 +1,74 @@
|
||||
+# RUN: sed -e s/ENTRYNAME/_main/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /out:%t.exe %t.obj
|
||||
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=MAIN %s
|
||||
+
|
||||
+# RUN: sed s/ENTRYNAME/_wmain/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /out:%t.exe %t.obj
|
||||
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=WMAIN %s
|
||||
+
|
||||
+# RUN: sed s/ENTRYNAME/_WinMain@16/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /out:%t.exe %t.obj
|
||||
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=WINMAIN %s
|
||||
+
|
||||
+# RUN: sed s/ENTRYNAME/_wWinMain@16/ %s | yaml2obj > %t.obj
|
||||
+# RUN: lld-link /out:%t.exe %t.obj
|
||||
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=WWINMAIN %s
|
||||
+
|
||||
+# MAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||
+# WMAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||
+# WINMAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI
|
||||
+# WWINMAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI
|
||||
+
|
||||
+--- !COFF
|
||||
+header:
|
||||
+ Machine: IMAGE_FILE_MACHINE_I386
|
||||
+ Characteristics: []
|
||||
+sections:
|
||||
+ - Name: .text
|
||||
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
|
||||
+ Alignment: 4
|
||||
+ SectionData: B82A000000C3
|
||||
+symbols:
|
||||
+ - Name: .text
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
+ SectionDefinition:
|
||||
+ Length: 6
|
||||
+ NumberOfRelocations: 0
|
||||
+ NumberOfLinenumbers: 0
|
||||
+ CheckSum: 0
|
||||
+ Number: 0
|
||||
+ - Name: ENTRYNAME
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+ - Name: _mainCRTStartup
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+ - Name: _wmainCRTStartup
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+ - Name: _WinMainCRTStartup
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+ - Name: _wWinMainCRTStartup
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+...
|
||||
Index: lld/test/COFF/entry-inference4.test
|
||||
===================================================================
|
||||
--- a/lld/test/COFF/entry-inference4.test (nonexistent)
|
||||
+++ b/lld/test/COFF/entry-inference4.test (revision 341035)
|
||||
@@ -0,0 +1,56 @@
|
||||
+# RUN: sed 's/ENTRY1/WinMain/;s/ENTRY2/main/' %s | yaml2obj > %t.obj
|
||||
+# RUN: not lld-link /subsystem:windows /out:%t.exe %t.obj > %t.log 2>&1
|
||||
+# RUN: FileCheck -check-prefix=WINMAIN %s < %t.log
|
||||
+
|
||||
+# RUN: sed 's/ENTRY1/wWinMain/;s/ENTRY2/main/' %s | yaml2obj > %t.obj
|
||||
+# RUN: not lld-link /subsystem:windows /out:%t.exe %t.obj > %t.log 2>&1
|
||||
+# RUN: FileCheck -check-prefix=WWINMAIN %s < %t.log
|
||||
+
|
||||
+# RUN: sed 's/ENTRY1/WinMain/;s/ENTRY2/main/' %s | yaml2obj > %t.obj
|
||||
+# RUN: not lld-link /subsystem:console /out:%t.exe %t.obj > %t.log 2>&1
|
||||
+# RUN: FileCheck -check-prefix=MAIN %s < %t.log
|
||||
+
|
||||
+# RUN: sed 's/ENTRY1/WinMain/;s/ENTRY2/wmain/' %s | yaml2obj > %t.obj
|
||||
+# RUN: not lld-link /subsystem:console /out:%t.exe %t.obj > %t.log 2>&1
|
||||
+# RUN: FileCheck -check-prefix=WMAIN %s < %t.log
|
||||
+
|
||||
+# MAIN: error: <root>: undefined symbol: mainCRTStartup
|
||||
+# WMAIN: error: <root>: undefined symbol: wmainCRTStartup
|
||||
+# WINMAIN: error: <root>: undefined symbol: WinMainCRTStartup
|
||||
+# WWINMAIN: error: <root>: undefined symbol: wWinMainCRTStartup
|
||||
+
|
||||
+--- !COFF
|
||||
+header:
|
||||
+ Machine: IMAGE_FILE_MACHINE_AMD64
|
||||
+ Characteristics: []
|
||||
+sections:
|
||||
+ - Name: .text
|
||||
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
|
||||
+ Alignment: 4
|
||||
+ SectionData: B82A000000C3
|
||||
+symbols:
|
||||
+ - Name: .text
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_STATIC
|
||||
+ SectionDefinition:
|
||||
+ Length: 6
|
||||
+ NumberOfRelocations: 0
|
||||
+ NumberOfLinenumbers: 0
|
||||
+ CheckSum: 0
|
||||
+ Number: 0
|
||||
+ - Name: ENTRY1
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+ - Name: ENTRY2
|
||||
+ Value: 0
|
||||
+ SectionNumber: 1
|
||||
+ SimpleType: IMAGE_SYM_TYPE_NULL
|
||||
+ ComplexType: IMAGE_SYM_DTYPE_NULL
|
||||
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
|
||||
+...
|
@ -10,19 +10,21 @@ interface nsIURI;
|
||||
|
||||
/**
|
||||
* nsISHistoryListener defines the interface one can implement to receive
|
||||
* notifications about activities in session history and to be able to
|
||||
* cancel them.
|
||||
* notifications about activities in session history and (for reloads) to be
|
||||
* able to cancel them.
|
||||
*
|
||||
* A session history listener will be notified when pages are added, removed
|
||||
* and loaded from session history. It can prevent any action (except adding
|
||||
* a new session history entry) from happening by returning false from the
|
||||
* corresponding callback method.
|
||||
* and loaded from session history. In the case of reloads, it can prevent them
|
||||
* from happening by returning false from the corresponding callback method.
|
||||
*
|
||||
* A session history listener can be registered on a particular nsISHistory
|
||||
* instance via the nsISHistory::addSHistoryListener() method.
|
||||
*
|
||||
* Listener methods should not alter the session history. Things are likely to
|
||||
* go haywire if they do.
|
||||
*/
|
||||
[scriptable, uuid(125c0833-746a-400e-9b89-d2d18545c08a)]
|
||||
interface nsISHistoryListener : nsISupports
|
||||
interface nsISHistoryListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when a new document is added to session history. New documents are
|
||||
@ -30,71 +32,58 @@ interface nsISHistoryListener : nsISupports
|
||||
* or content area, for example via nsIWebNavigation::loadURI()
|
||||
*
|
||||
* @param aNewURI The URI of the document to be added to session history.
|
||||
* @param aOldIndex The index of the current history item before the operation.
|
||||
* @param aOldIndex The index of the current history item before the
|
||||
* operation.
|
||||
*/
|
||||
void OnHistoryNewEntry(in nsIURI aNewURI, in long aOldIndex);
|
||||
void OnHistoryNewEntry(in nsIURI aNewURI, in long aOldIndex);
|
||||
|
||||
/**
|
||||
* Called when the current document is reloaded, for example due to a
|
||||
/**
|
||||
* Called before the current document is reloaded, for example due to a
|
||||
* nsIWebNavigation::reload() call.
|
||||
*
|
||||
* @param aReloadURI The URI of the document to be reloaded.
|
||||
* @param aReloadFlags Flags that indicate how the document is to be
|
||||
* @param aReloadFlags Flags that indicate how the document is to be
|
||||
* refreshed. See constants on the nsIWebNavigation
|
||||
* interface.
|
||||
* @return Whether the operation can proceed.
|
||||
*
|
||||
* @see nsIWebNavigation
|
||||
*/
|
||||
boolean OnHistoryReload(in nsIURI aReloadURI, in unsigned long aReloadFlags);
|
||||
boolean OnHistoryReload(in nsIURI aReloadURI, in unsigned long aReloadFlags);
|
||||
|
||||
/**
|
||||
* Called when navigating to a session history entry by index, for example,
|
||||
* Called before navigating to a session history entry by index, for example,
|
||||
* when nsIWebNavigation::gotoIndex() is called.
|
||||
*
|
||||
* @param aIndex The index in session history of the entry to be loaded.
|
||||
* @param aIndex The index in session history of the entry to be
|
||||
* loaded.
|
||||
* @param aGotoURI The URI of the session history entry to be loaded.
|
||||
* It could be null in case of a grouped session history
|
||||
* navigation since we have no URI information of entries
|
||||
* existing in other partial histories.
|
||||
* @return Whether the operation can proceed.
|
||||
*/
|
||||
boolean OnHistoryGotoIndex(in long aIndex, in nsIURI aGotoURI);
|
||||
void OnHistoryGotoIndex(in long aIndex, in nsIURI aGotoURI);
|
||||
|
||||
/**
|
||||
* Called when entries are removed from session history. Entries can be
|
||||
* Called before entries are removed from session history. Entries can be
|
||||
* removed from session history for various reasons, for example to control
|
||||
* the memory usage of the browser, to prevent users from loading documents
|
||||
* from history, to erase evidence of prior page loads, etc.
|
||||
*
|
||||
* To purge documents from session history call nsISHistory::PurgeHistory()
|
||||
* To purge documents from session history call nsISHistory::PurgeHistory().
|
||||
*
|
||||
* @param aNumEntries The number of entries to be removed from session history.
|
||||
* @return Whether the operation can proceed.
|
||||
* @param aNumEntries The number of entries to be removed from session
|
||||
* history.
|
||||
*/
|
||||
boolean OnHistoryPurge(in long aNumEntries);
|
||||
void OnHistoryPurge(in long aNumEntries);
|
||||
|
||||
/**
|
||||
* Called when an entry is replaced in the session history. Entries are
|
||||
* Called before an entry is replaced in the session history. Entries are
|
||||
* replaced when navigating away from non-persistent history entries (such as
|
||||
* about pages) and when history.replaceState is called.
|
||||
*
|
||||
* @param aIndex The index in session history of the entry being
|
||||
* replaced
|
||||
* replaced.
|
||||
*/
|
||||
void OnHistoryReplaceEntry(in long aIndex);
|
||||
|
||||
/**
|
||||
* Called when nsISHistory::count has been updated. Unlike OnHistoryNewEntry
|
||||
* and OnHistoryPurge which happen before the modifications are actually done
|
||||
* and maybe cancellable, this function is called after these modifications.
|
||||
*/
|
||||
void OnLengthChanged(in long aCount);
|
||||
|
||||
/**
|
||||
* Called when nsISHistory::index has been updated. Unlike the other methods
|
||||
* on this interface, which happen before the modifications are actually done
|
||||
* and maybe cancellable, this function is called after these modifications.
|
||||
*/
|
||||
void OnIndexChanged(in long aIndex);
|
||||
void OnHistoryReplaceEntry(in long aIndex);
|
||||
};
|
||||
|
@ -641,9 +641,6 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist)
|
||||
mEntries.AppendElement(aSHEntry);
|
||||
mIndex++;
|
||||
|
||||
NOTIFY_LISTENERS(OnLengthChanged, (Length()));
|
||||
NOTIFY_LISTENERS(OnIndexChanged, (mIndex));
|
||||
|
||||
// Purge History list if it is too long
|
||||
if (gHistoryMaxSize >= 0 && Length() > gHistoryMaxSize) {
|
||||
PurgeHistory(Length() - gHistoryMaxSize);
|
||||
@ -677,8 +674,6 @@ nsSHistory::SetIndex(int32_t aIndex)
|
||||
}
|
||||
|
||||
mIndex = aIndex;
|
||||
NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -796,14 +791,7 @@ nsSHistory::PurgeHistory(int32_t aNumEntries)
|
||||
|
||||
aNumEntries = std::min(aNumEntries, Length());
|
||||
|
||||
bool purgeHistory = true;
|
||||
NOTIFY_LISTENERS_CANCELABLE(OnHistoryPurge, purgeHistory,
|
||||
(aNumEntries, &purgeHistory));
|
||||
|
||||
if (!purgeHistory) {
|
||||
// Listener asked us not to purge
|
||||
return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
|
||||
}
|
||||
NOTIFY_LISTENERS(OnHistoryPurge, (aNumEntries));
|
||||
|
||||
// Remove the first `aNumEntries` entries.
|
||||
mEntries.RemoveElementsAt(0, aNumEntries);
|
||||
@ -814,9 +802,6 @@ nsSHistory::PurgeHistory(int32_t aNumEntries)
|
||||
mRequestedIndex -= aNumEntries;
|
||||
mRequestedIndex = std::max(mRequestedIndex, -1);
|
||||
|
||||
NOTIFY_LISTENERS(OnLengthChanged, (Length()));
|
||||
NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
|
||||
|
||||
if (mRootDocShell) {
|
||||
mRootDocShell->HistoryPurged(aNumEntries);
|
||||
}
|
||||
@ -952,14 +937,9 @@ NS_IMETHODIMP
|
||||
nsSHistory::ReloadCurrentEntry()
|
||||
{
|
||||
// Notify listeners
|
||||
bool canNavigate = true;
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
GetCurrentURI(getter_AddRefs(currentURI));
|
||||
NOTIFY_LISTENERS_CANCELABLE(OnHistoryGotoIndex, canNavigate,
|
||||
(mIndex, currentURI, &canNavigate));
|
||||
if (!canNavigate) {
|
||||
return NS_OK;
|
||||
}
|
||||
NOTIFY_LISTENERS(OnHistoryGotoIndex, (mIndex, currentURI));
|
||||
|
||||
return LoadEntry(mIndex, LOAD_HISTORY, HIST_CMD_RELOAD);
|
||||
}
|
||||
@ -1368,7 +1348,6 @@ nsSHistory::RemoveDuplicate(int32_t aIndex, bool aKeepNext)
|
||||
// Adjust our indices to reflect the removed entry.
|
||||
if (mIndex > aIndex) {
|
||||
mIndex = mIndex - 1;
|
||||
NOTIFY_LISTENERS(OnIndexChanged, (mIndex));
|
||||
}
|
||||
|
||||
// NB: If the entry we are removing is the entry currently
|
||||
@ -1386,7 +1365,6 @@ nsSHistory::RemoveDuplicate(int32_t aIndex, bool aKeepNext)
|
||||
if (mRequestedIndex > aIndex || (mRequestedIndex == aIndex && !aKeepNext)) {
|
||||
mRequestedIndex = mRequestedIndex - 1;
|
||||
}
|
||||
NOTIFY_LISTENERS(OnLengthChanged, (Length()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -1451,7 +1429,6 @@ nsSHistory::UpdateIndex()
|
||||
// Update the actual index with the right value.
|
||||
if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
|
||||
mIndex = mRequestedIndex;
|
||||
NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
|
||||
}
|
||||
|
||||
mRequestedIndex = -1;
|
||||
@ -1529,18 +1506,9 @@ nsSHistory::LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd)
|
||||
MOZ_ASSERT((prevEntry && nextEntry && nextURI), "prevEntry, nextEntry and nextURI can't be null");
|
||||
|
||||
// Send appropriate listener notifications.
|
||||
bool canNavigate = true;
|
||||
if (aHistCmd == HIST_CMD_GOTOINDEX) {
|
||||
// We are going somewhere else. This is not reload either
|
||||
NOTIFY_LISTENERS_CANCELABLE(OnHistoryGotoIndex, canNavigate,
|
||||
(aIndex, nextURI, &canNavigate));
|
||||
}
|
||||
|
||||
if (!canNavigate) {
|
||||
// If the listener asked us not to proceed with
|
||||
// the operation, simply return.
|
||||
mRequestedIndex = -1;
|
||||
return NS_OK; // XXX Maybe I can return some other error code?
|
||||
NOTIFY_LISTENERS(OnHistoryGotoIndex, (aIndex, nextURI));
|
||||
}
|
||||
|
||||
if (mRequestedIndex == mIndex) {
|
||||
|
@ -34,19 +34,16 @@ add_task(async function test() {
|
||||
delete content._testListener;
|
||||
content.setTimeout(() => { content.location.reload(); }, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryReload: () => true,
|
||||
OnHistoryGotoIndex: () => true,
|
||||
OnHistoryPurge: () => true,
|
||||
OnHistoryGotoIndex: () => {},
|
||||
OnHistoryPurge: () => {},
|
||||
OnHistoryReplaceEntry: () => {
|
||||
// The initial load of about:blank causes a transient entry to be
|
||||
// created, so our first navigation to a real page is a replace
|
||||
// instead of a new entry.
|
||||
++count;
|
||||
return true;
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsISHistoryListener,
|
||||
|
@ -13,12 +13,10 @@ SHistoryListener.prototype = {
|
||||
|
||||
OnHistoryGotoIndex: function (aIndex, aGotoURI) {
|
||||
this.last = "gotoindex";
|
||||
return this.retval;
|
||||
},
|
||||
|
||||
OnHistoryPurge: function (aNumEntries) {
|
||||
this.last = "purge";
|
||||
return this.retval;
|
||||
},
|
||||
|
||||
OnHistoryReload: function (aReloadURI, aReloadFlags) {
|
||||
|
@ -180,8 +180,12 @@ nsXBLResourceLoader::StyleSheetLoaded(StyleSheet* aSheet,
|
||||
|
||||
if (mPendingSheets == 0) {
|
||||
// All stylesheets are loaded.
|
||||
mResources->ComputeServoStyles(
|
||||
*mBoundDocument->GetShell()->StyleSet());
|
||||
|
||||
// Our document might have been undisplayed after this sheet load
|
||||
// was started, so check before building the XBL cascade data.
|
||||
if (nsIPresShell* shell = mBoundDocument->GetShell()) {
|
||||
mResources->ComputeServoStyles(*shell->StyleSet());
|
||||
}
|
||||
|
||||
// XXX Check for mPendingScripts when scripts also come online.
|
||||
if (!mInLoadResourcesFunc)
|
||||
|
@ -233,7 +233,7 @@ public:
|
||||
// weight - [100, 900] (multiples of 100)
|
||||
// stretch = [FontStretch::UltraCondensed(), FontStretch::UltraExpanded()]
|
||||
// italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL
|
||||
// language override = result of calling nsRuleNode::ParseFontLanguageOverride
|
||||
// language override = result of calling nsLayoutUtils::ParseFontLanguageOverride
|
||||
// TODO: support for unicode ranges not yet implemented
|
||||
virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
|
||||
const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
|
||||
|
@ -61,9 +61,10 @@ varying vec2 vPos;
|
||||
#define BORDER_STYLE_INSET 8
|
||||
#define BORDER_STYLE_OUTSET 9
|
||||
|
||||
#define CLIP_NONE 0
|
||||
#define CLIP_DASH 1
|
||||
#define CLIP_DOT 2
|
||||
#define CLIP_NONE 0
|
||||
#define CLIP_DASH_CORNER 1
|
||||
#define CLIP_DASH_EDGE 2
|
||||
#define CLIP_DOT 3
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
@ -356,7 +357,21 @@ void main(void) {
|
||||
d = distance(vClipParams1.xy, vPos) - vClipParams1.z;
|
||||
break;
|
||||
}
|
||||
case CLIP_DASH: {
|
||||
case CLIP_DASH_EDGE: {
|
||||
bool is_vertical = vClipParams1.x == 0.;
|
||||
float half_dash = is_vertical ? vClipParams1.y : vClipParams1.x;
|
||||
// We want to draw something like:
|
||||
// +---+---+---+---+
|
||||
// |xxx| | |xxx|
|
||||
// +---+---+---+---+
|
||||
float pos = is_vertical ? vPos.y : vPos.x;
|
||||
bool in_dash = pos < half_dash || pos > 3.0 * half_dash;
|
||||
if (!in_dash) {
|
||||
d = 1.;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLIP_DASH_CORNER: {
|
||||
// Get SDF for the two line/tangent clip lines,
|
||||
// do SDF subtract to get clip distance.
|
||||
float d0 = distance_to_line(vClipParams1.xy,
|
||||
|
@ -235,16 +235,19 @@ impl BorderSideHelpers for BorderSide {
|
||||
/// The kind of border corner clip.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Debug, Clone, PartialEq)]
|
||||
pub enum BorderCornerClipKind {
|
||||
Dash = 1,
|
||||
Dot = 2,
|
||||
pub enum BorderClipKind {
|
||||
DashCorner = 1,
|
||||
DashEdge = 2,
|
||||
Dot = 3,
|
||||
}
|
||||
|
||||
/// The source data for a border corner clip mask.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BorderCornerClipSource {
|
||||
pub max_clip_count: usize,
|
||||
kind: BorderCornerClipKind,
|
||||
struct BorderCornerClipSource {
|
||||
// FIXME(emilio): the `max_clip_count` name makes no sense for dashed
|
||||
// borders now that it represents half-dashes.
|
||||
max_clip_count: usize,
|
||||
kind: BorderClipKind,
|
||||
widths: DeviceSize,
|
||||
radius: DeviceSize,
|
||||
ellipse: Ellipse<DevicePixel>,
|
||||
@ -254,7 +257,7 @@ impl BorderCornerClipSource {
|
||||
pub fn new(
|
||||
corner_radius: DeviceSize,
|
||||
widths: DeviceSize,
|
||||
kind: BorderCornerClipKind,
|
||||
kind: BorderClipKind,
|
||||
) -> BorderCornerClipSource {
|
||||
// Work out a dash length (and therefore dash count)
|
||||
// based on the width of the border edges. The "correct"
|
||||
@ -265,24 +268,21 @@ impl BorderCornerClipSource {
|
||||
// uses for dash length.
|
||||
|
||||
let (ellipse, max_clip_count) = match kind {
|
||||
BorderCornerClipKind::Dash => {
|
||||
BorderClipKind::DashEdge => unreachable!("not for corners"),
|
||||
BorderClipKind::DashCorner => {
|
||||
let ellipse = Ellipse::new(corner_radius);
|
||||
|
||||
// The desired dash length is ~3x the border width.
|
||||
let average_border_width = 0.5 * (widths.width + widths.height);
|
||||
let desired_dash_arc_length = average_border_width * 3.0;
|
||||
|
||||
// Get the ideal number of dashes for that arc length.
|
||||
// This is scaled by 0.5 since there is an on/off length
|
||||
// for each dash.
|
||||
let desired_count = 0.5 * ellipse.total_arc_length / desired_dash_arc_length;
|
||||
let (_half_dash, num_half_dashes) =
|
||||
compute_half_dash(average_border_width, ellipse.total_arc_length);
|
||||
|
||||
// Round that up to the nearest integer, so that the dash length
|
||||
// doesn't exceed the ratio above. Add one extra dash to cover
|
||||
// the last half-dash of the arc.
|
||||
(ellipse, desired_count.ceil() as usize)
|
||||
(ellipse, num_half_dashes as usize)
|
||||
}
|
||||
BorderCornerClipKind::Dot => {
|
||||
BorderClipKind::Dot => {
|
||||
let mut corner_radius = corner_radius;
|
||||
if corner_radius.width < (widths.width / 2.0) {
|
||||
corner_radius.width = 0.0;
|
||||
@ -334,6 +334,10 @@ impl BorderCornerClipSource {
|
||||
pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> {
|
||||
let mut dot_dash_data = Vec::new();
|
||||
|
||||
if self.max_clip_count == 0 {
|
||||
return dot_dash_data;
|
||||
}
|
||||
|
||||
let outer_scale = match segment {
|
||||
BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
|
||||
BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
|
||||
@ -353,29 +357,32 @@ impl BorderCornerClipSource {
|
||||
let max_clip_count = self.max_clip_count.min(MAX_DASH_COUNT);
|
||||
|
||||
match self.kind {
|
||||
BorderCornerClipKind::Dash => {
|
||||
// Get the correct dash arc length.
|
||||
let dash_arc_length =
|
||||
0.5 * self.ellipse.total_arc_length / max_clip_count as f32;
|
||||
// Start the first dash at one quarter the length of a single dash
|
||||
// along the arc line. This is arbitrary but looks reasonable in
|
||||
// most cases. We need to spend some time working on a more
|
||||
// sophisticated dash placement algorithm that takes into account
|
||||
// the offset of the dashes along edge segments.
|
||||
let mut current_arc_length = 0.25 * dash_arc_length;
|
||||
dot_dash_data.reserve(max_clip_count);
|
||||
for _ in 0 .. max_clip_count {
|
||||
let arc_length0 = current_arc_length;
|
||||
current_arc_length += dash_arc_length;
|
||||
BorderClipKind::DashEdge => unreachable!("not for corners"),
|
||||
BorderClipKind::DashCorner => {
|
||||
// Get the correct half-dash arc length.
|
||||
let half_dash_arc_length =
|
||||
self.ellipse.total_arc_length / max_clip_count as f32;
|
||||
let dash_length = 2. * half_dash_arc_length;
|
||||
|
||||
let arc_length1 = current_arc_length;
|
||||
current_arc_length += dash_arc_length;
|
||||
let mut current_length = 0.;
|
||||
|
||||
dot_dash_data.reserve(max_clip_count / 4 + 1);
|
||||
for i in 0 .. (max_clip_count / 4 + 1) {
|
||||
let arc_length0 = current_length;
|
||||
current_length += if i == 0 {
|
||||
half_dash_arc_length
|
||||
} else {
|
||||
dash_length
|
||||
};
|
||||
|
||||
let arc_length1 = current_length;
|
||||
current_length += dash_length;
|
||||
|
||||
let alpha = self.ellipse.find_angle_for_arc_length(arc_length0);
|
||||
let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
|
||||
let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
|
||||
|
||||
let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
|
||||
let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
|
||||
let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
|
||||
let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
|
||||
|
||||
let point0 = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (self.radius.width - point0.x),
|
||||
@ -409,14 +416,14 @@ impl BorderCornerClipSource {
|
||||
]);
|
||||
}
|
||||
}
|
||||
BorderCornerClipKind::Dot if max_clip_count == 1 => {
|
||||
BorderClipKind::Dot if max_clip_count == 1 => {
|
||||
let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5);
|
||||
dot_dash_data.push([
|
||||
self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0.,
|
||||
0., 0., 0., 0.,
|
||||
]);
|
||||
}
|
||||
BorderCornerClipKind::Dot => {
|
||||
BorderClipKind::Dot => {
|
||||
let mut forward_dots = Vec::with_capacity(max_clip_count / 2 + 1);
|
||||
let mut back_dots = Vec::with_capacity(max_clip_count / 2 + 1);
|
||||
let mut leftover_arc_length = 0.0;
|
||||
@ -539,13 +546,14 @@ pub struct BorderRenderTaskInfo {
|
||||
pub size: DeviceIntSize,
|
||||
}
|
||||
|
||||
// Information needed to place and draw a border edge.
|
||||
/// Information needed to place and draw a border edge.
|
||||
#[derive(Debug)]
|
||||
struct EdgeInfo {
|
||||
// Offset in local space to place the edge from origin.
|
||||
/// Offset in local space to place the edge from origin.
|
||||
local_offset: f32,
|
||||
// Size of the edge in local space.
|
||||
/// Size of the edge in local space.
|
||||
local_size: f32,
|
||||
// Size in device pixels needed in the render task.
|
||||
/// Size in device pixels needed in the render task.
|
||||
device_size: f32,
|
||||
}
|
||||
|
||||
@ -563,6 +571,30 @@ impl EdgeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Given a side width and the available space, compute the half-dash (half of
|
||||
// the 'on' segment) and the count of them for a given segment.
|
||||
fn compute_half_dash(side_width: f32, total_size: f32) -> (f32, u32) {
|
||||
let half_dash = side_width * 1.5;
|
||||
let num_half_dashes = (total_size / half_dash).ceil() as u32;
|
||||
|
||||
if num_half_dashes == 0 {
|
||||
return (0., 0);
|
||||
}
|
||||
|
||||
// TODO(emilio): Gecko has some other heuristics here to start with a full
|
||||
// dash when the border side is zero, for example. We might consider those
|
||||
// in the future.
|
||||
let num_half_dashes = if num_half_dashes % 4 != 0 {
|
||||
num_half_dashes + 4 - num_half_dashes % 4
|
||||
} else {
|
||||
num_half_dashes
|
||||
};
|
||||
|
||||
let half_dash = total_size / num_half_dashes as f32;
|
||||
(half_dash, num_half_dashes)
|
||||
}
|
||||
|
||||
|
||||
// Get the needed size in device pixels for an edge,
|
||||
// based on the border style of that edge. This is used
|
||||
// to determine how big the render task should be.
|
||||
@ -579,14 +611,11 @@ fn get_edge_info(
|
||||
|
||||
match style {
|
||||
BorderStyle::Dashed => {
|
||||
let dash_size = 3.0 * side_width;
|
||||
let approx_dash_count = (avail_size - dash_size) / dash_size;
|
||||
let dash_count = 1.0 + 2.0 * (approx_dash_count / 2.0).floor();
|
||||
let used_size = dash_count * dash_size;
|
||||
let extra_space = avail_size - used_size;
|
||||
let device_size = 2.0 * dash_size * scale;
|
||||
let offset = (extra_space * 0.5).round();
|
||||
EdgeInfo::new(offset, used_size, device_size)
|
||||
// Basically, two times the dash size.
|
||||
let (half_dash, _num_half_dashes) =
|
||||
compute_half_dash(side_width, avail_size);
|
||||
let device_size = (2.0 * 2.0 * half_dash * scale).round();
|
||||
EdgeInfo::new(0., avail_size, device_size)
|
||||
}
|
||||
BorderStyle::Dotted => {
|
||||
let dot_and_space_size = 2.0 * side_width;
|
||||
@ -960,7 +989,7 @@ fn add_brush_segment(
|
||||
brush_segments.push(
|
||||
BrushSegment::new(
|
||||
image_rect,
|
||||
true,
|
||||
/* may_need_clip_mask = */ true,
|
||||
edge_flags,
|
||||
[
|
||||
task_rect.origin.x,
|
||||
@ -1014,8 +1043,8 @@ fn add_segment(
|
||||
}
|
||||
|
||||
let clip_kind = match style0 {
|
||||
BorderStyle::Dashed => Some(BorderCornerClipKind::Dash),
|
||||
BorderStyle::Dotted => Some(BorderCornerClipKind::Dot),
|
||||
BorderStyle::Dashed => Some(BorderClipKind::DashCorner),
|
||||
BorderStyle::Dotted => Some(BorderClipKind::Dot),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@ -1031,12 +1060,16 @@ fn add_segment(
|
||||
// so that we don't allocate a Vec here.
|
||||
let clip_list = clip_source.write(segment);
|
||||
|
||||
for params in clip_list {
|
||||
instances.push(BorderInstance {
|
||||
flags: base_flags | ((clip_kind as i32) << 24),
|
||||
clip_params: params,
|
||||
..base_instance
|
||||
});
|
||||
if clip_list.is_empty() {
|
||||
instances.push(base_instance);
|
||||
} else {
|
||||
for params in clip_list {
|
||||
instances.push(BorderInstance {
|
||||
flags: base_flags | ((clip_kind as i32) << 24),
|
||||
clip_params: params,
|
||||
..base_instance
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -1053,32 +1086,19 @@ fn add_segment(
|
||||
|
||||
match style0 {
|
||||
BorderStyle::Dashed => {
|
||||
let rect = if is_vertical {
|
||||
let half_dash_size = task_rect.size.height * 0.5;
|
||||
let y0 = task_rect.origin.y;
|
||||
let y1 = y0 + half_dash_size.round();
|
||||
|
||||
DeviceRect::from_floats(
|
||||
task_rect.origin.x,
|
||||
y0,
|
||||
task_rect.origin.x + task_rect.size.width,
|
||||
y1,
|
||||
)
|
||||
let (x, y) = if is_vertical {
|
||||
let half_dash_size = task_rect.size.height * 0.25;
|
||||
(0., half_dash_size)
|
||||
} else {
|
||||
let half_dash_size = task_rect.size.width * 0.5;
|
||||
let x0 = task_rect.origin.x;
|
||||
let x1 = x0 + half_dash_size.round();
|
||||
|
||||
DeviceRect::from_floats(
|
||||
x0,
|
||||
task_rect.origin.y,
|
||||
x1,
|
||||
task_rect.origin.y + task_rect.size.height,
|
||||
)
|
||||
let half_dash_size = task_rect.size.width * 0.25;
|
||||
(half_dash_size, 0.)
|
||||
};
|
||||
|
||||
instances.push(BorderInstance {
|
||||
local_rect: rect,
|
||||
flags: base_flags | ((BorderClipKind::DashEdge as i32) << 24),
|
||||
clip_params: [
|
||||
x, y, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
],
|
||||
..base_instance
|
||||
});
|
||||
}
|
||||
@ -1094,7 +1114,7 @@ fn add_segment(
|
||||
};
|
||||
|
||||
instances.push(BorderInstance {
|
||||
flags: base_flags | ((BorderCornerClipKind::Dot as i32) << 24),
|
||||
flags: base_flags | ((BorderClipKind::Dot as i32) << 24),
|
||||
clip_params: [
|
||||
x, y, r, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
],
|
||||
|
@ -328,6 +328,9 @@ pub struct ClipChainInstance {
|
||||
pub local_clip_rect: LayoutRect,
|
||||
pub has_non_root_coord_system: bool,
|
||||
pub has_non_local_clips: bool,
|
||||
// If true, this clip chain requires allocation
|
||||
// of a clip mask.
|
||||
pub needs_mask: bool,
|
||||
// Combined clip rect in picture space (may
|
||||
// be more conservative that local_clip_rect).
|
||||
pub pic_clip_rect: PictureRect,
|
||||
@ -524,6 +527,7 @@ impl ClipStore {
|
||||
let first_clip_node_index = self.clip_node_indices.len() as u32;
|
||||
let mut has_non_root_coord_system = false;
|
||||
let mut has_non_local_clips = false;
|
||||
let mut needs_mask = false;
|
||||
|
||||
// For each potential clip node
|
||||
for node_info in self.clip_node_info.drain(..) {
|
||||
@ -580,6 +584,26 @@ impl ClipStore {
|
||||
}
|
||||
};
|
||||
|
||||
// As a special case, a partial accept of a clip rect that is
|
||||
// in the same coordinate system as the primitive doesn't need
|
||||
// a clip mask. Instead, it can be handled by the primitive
|
||||
// vertex shader as part of the local clip rect. This is an
|
||||
// important optimization for reducing the number of clip
|
||||
// masks that are allocated on common pages.
|
||||
needs_mask |= match node.item {
|
||||
ClipItem::Rectangle(_, ClipMode::ClipOut) |
|
||||
ClipItem::RoundedRectangle(..) |
|
||||
ClipItem::Image(..) |
|
||||
ClipItem::BoxShadow(..) |
|
||||
ClipItem::LineDecoration(..) => {
|
||||
true
|
||||
}
|
||||
|
||||
ClipItem::Rectangle(_, ClipMode::Clip) => {
|
||||
!flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM)
|
||||
}
|
||||
};
|
||||
|
||||
// Store this in the index buffer for this clip chain instance.
|
||||
self.clip_node_indices
|
||||
.push(ClipNodeInstance::new(node_info.node_index, flags));
|
||||
@ -602,6 +626,7 @@ impl ClipStore {
|
||||
has_non_local_clips,
|
||||
local_clip_rect,
|
||||
pic_clip_rect,
|
||||
needs_mask,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -107,9 +107,6 @@ pub struct DisplayListFlattener<'a> {
|
||||
/// A stack of stacking context properties.
|
||||
sc_stack: Vec<FlattenedStackingContext>,
|
||||
|
||||
/// A stack of the current pictures.
|
||||
picture_stack: Vec<PrimitiveIndex>,
|
||||
|
||||
/// A stack of the currently active shadows
|
||||
shadow_stack: Vec<(Shadow, PrimitiveIndex)>,
|
||||
|
||||
@ -164,7 +161,6 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
id_to_index_mapper: ClipIdToIndexMapper::default(),
|
||||
hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
|
||||
scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
|
||||
picture_stack: Vec::new(),
|
||||
shadow_stack: Vec::new(),
|
||||
sc_stack: Vec::new(),
|
||||
next_picture_id: old_builder.next_picture_id,
|
||||
@ -181,7 +177,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
|
||||
flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
|
||||
|
||||
debug_assert!(flattener.picture_stack.is_empty());
|
||||
debug_assert!(flattener.sc_stack.is_empty());
|
||||
|
||||
new_scene.root_pipeline_id = Some(root_pipeline_id);
|
||||
new_scene.pipeline_epochs = scene.pipeline_epochs.clone();
|
||||
@ -818,7 +814,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
prim_index: PrimitiveIndex,
|
||||
) {
|
||||
// Add primitive to the top-most Picture on the stack.
|
||||
let pic_prim_index = *self.picture_stack.last().unwrap();
|
||||
let pic_prim_index = self.sc_stack.last().unwrap().leaf_prim_index;
|
||||
let pic = self.prim_store.get_pic_mut(pic_prim_index);
|
||||
pic.add_primitive(prim_index);
|
||||
}
|
||||
@ -922,51 +918,6 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
// to correctly handle some CSS cases (see #1957).
|
||||
let max_clip = LayoutRect::max_rect();
|
||||
|
||||
// If there is no root picture, create one for the main framebuffer.
|
||||
if self.sc_stack.is_empty() {
|
||||
// Should be no pictures at all if the stack is empty...
|
||||
debug_assert!(self.prim_store.primitives.is_empty());
|
||||
debug_assert_eq!(transform_style, TransformStyle::Flat);
|
||||
|
||||
// This picture stores primitive runs for items on the
|
||||
// main framebuffer.
|
||||
let picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
None,
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
ClipChainId::NONE,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(BrushPrimitive::new_picture(picture)),
|
||||
);
|
||||
|
||||
self.picture_stack.push(prim_index);
|
||||
} else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
|
||||
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
|
||||
// the stacking context needs to be isolated to blend correctly as per
|
||||
// the CSS spec.
|
||||
// TODO(gw): The way we detect not being the primary framebuffer (len > 2)
|
||||
// is hacky and depends on how we create a root stacking context
|
||||
// during flattening.
|
||||
let parent_prim_index = *self.picture_stack.last().unwrap();
|
||||
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
|
||||
// If not already isolated for some other reason,
|
||||
// make this picture as isolated.
|
||||
if parent_pic.requested_composite_mode.is_none() {
|
||||
parent_pic.requested_composite_mode = Some(PictureCompositeMode::Blit);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the transform-style of the parent stacking context,
|
||||
// which determines if we *might* need to draw this on
|
||||
// an intermediate surface for plane splitting purposes.
|
||||
@ -992,126 +943,13 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
participating_in_3d_context &&
|
||||
parent_transform_style == TransformStyle::Flat;
|
||||
|
||||
let rendering_context_3d_prim_index = if establishes_3d_context {
|
||||
// If establishing a 3d context, we need to add a picture
|
||||
// that will be the container for all the planes and any
|
||||
// un-transformed content.
|
||||
let picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
None,
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let prim = BrushPrimitive::new_picture(picture);
|
||||
|
||||
let prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(prim),
|
||||
);
|
||||
|
||||
let parent_prim_index = *self.picture_stack.last().unwrap();
|
||||
|
||||
let pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
pic.add_primitive(prim_index);
|
||||
|
||||
self.picture_stack.push(prim_index);
|
||||
|
||||
Some(prim_index)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut parent_prim_index = if !establishes_3d_context && participating_in_3d_context {
|
||||
// If we're in a 3D context, we will parent the picture
|
||||
// to the first stacking context we find that is a
|
||||
// 3D rendering context container. This follows the spec
|
||||
// by hoisting these items out into the same 3D context
|
||||
// for plane splitting.
|
||||
self.sc_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|sc| sc.rendering_context_3d_prim_index.is_some())
|
||||
.map(|sc| sc.rendering_context_3d_prim_index.unwrap())
|
||||
.unwrap()
|
||||
} else {
|
||||
*self.picture_stack.last().unwrap()
|
||||
};
|
||||
|
||||
// Same for mix-blend-mode.
|
||||
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
|
||||
let picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let src_prim = BrushPrimitive::new_picture(picture);
|
||||
|
||||
let src_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(src_prim),
|
||||
);
|
||||
|
||||
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
parent_prim_index = src_prim_index;
|
||||
parent_pic.add_primitive(src_prim_index);
|
||||
|
||||
self.picture_stack.push(src_prim_index);
|
||||
}
|
||||
|
||||
// For each filter, create a new image with that composite mode.
|
||||
for filter in composite_ops.filters.iter().rev() {
|
||||
let picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
Some(PictureCompositeMode::Filter(*filter)),
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let src_prim = BrushPrimitive::new_picture(picture);
|
||||
let src_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(src_prim),
|
||||
);
|
||||
|
||||
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
parent_prim_index = src_prim_index;
|
||||
|
||||
parent_pic.add_primitive(src_prim_index);
|
||||
|
||||
self.picture_stack.push(src_prim_index);
|
||||
}
|
||||
|
||||
// By default, this picture will be collapsed into
|
||||
// the owning target.
|
||||
let mut composite_mode = None;
|
||||
let mut frame_output_pipeline_id = None;
|
||||
|
||||
// If this stacking context if the root of a pipeline, and the caller
|
||||
// If this stacking context is the root of a pipeline, and the caller
|
||||
// has requested it as an output frame, create a render task to isolate it.
|
||||
let mut frame_output_pipeline_id = None;
|
||||
if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
|
||||
composite_mode = Some(PictureCompositeMode::Blit);
|
||||
frame_output_pipeline_id = Some(pipeline_id);
|
||||
@ -1133,7 +971,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
}
|
||||
|
||||
// Add picture for this actual stacking context contents to render into.
|
||||
let picture = PicturePrimitive::new_image(
|
||||
let leaf_picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
composite_mode,
|
||||
participating_in_3d_context,
|
||||
@ -1143,34 +981,112 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
);
|
||||
|
||||
// Create a brush primitive that draws this picture.
|
||||
let sc_prim = BrushPrimitive::new_picture(picture);
|
||||
let leaf_prim = BrushPrimitive::new_picture(leaf_picture);
|
||||
|
||||
// Add the brush to the parent picture.
|
||||
let sc_prim_index = self.prim_store.add_primitive(
|
||||
let leaf_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(sc_prim),
|
||||
PrimitiveContainer::Brush(leaf_prim),
|
||||
);
|
||||
|
||||
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
parent_pic.add_primitive(sc_prim_index);
|
||||
// Create a chain of pictures based on presence of filters,
|
||||
// mix-blend-mode and/or 3d rendering context containers.
|
||||
let mut current_prim_index = leaf_prim_index;
|
||||
|
||||
// Add this as the top-most picture for primitives to be added to.
|
||||
self.picture_stack.push(sc_prim_index);
|
||||
// For each filter, create a new image with that composite mode.
|
||||
for filter in &composite_ops.filters {
|
||||
let mut filter_picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
Some(PictureCompositeMode::Filter(*filter)),
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
filter_picture.add_primitive(current_prim_index);
|
||||
let filter_prim = BrushPrimitive::new_picture(filter_picture);
|
||||
|
||||
current_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(filter_prim),
|
||||
);
|
||||
}
|
||||
|
||||
// Same for mix-blend-mode.
|
||||
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
|
||||
let mut blend_picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
blend_picture.add_primitive(current_prim_index);
|
||||
let blend_prim = BrushPrimitive::new_picture(blend_picture);
|
||||
|
||||
current_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(blend_prim),
|
||||
);
|
||||
}
|
||||
|
||||
if establishes_3d_context {
|
||||
// If establishing a 3d context, we need to add a picture
|
||||
// that will be the container for all the planes and any
|
||||
// un-transformed content.
|
||||
let mut container_picture = PicturePrimitive::new_image(
|
||||
self.get_next_picture_id(),
|
||||
None,
|
||||
false,
|
||||
pipeline_id,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
container_picture.add_primitive(current_prim_index);
|
||||
let container_prim = BrushPrimitive::new_picture(container_picture);
|
||||
|
||||
current_prim_index = self.prim_store.add_primitive(
|
||||
&LayoutRect::zero(),
|
||||
&max_clip,
|
||||
true,
|
||||
clip_chain_id,
|
||||
spatial_node_index,
|
||||
None,
|
||||
PrimitiveContainer::Brush(container_prim),
|
||||
);
|
||||
}
|
||||
|
||||
// Push the SC onto the stack, so we know how to handle things in
|
||||
// pop_stacking_context.
|
||||
let sc = FlattenedStackingContext {
|
||||
composite_ops,
|
||||
is_backface_visible,
|
||||
pipeline_id,
|
||||
transform_style,
|
||||
rendering_context_3d_prim_index,
|
||||
establishes_3d_context,
|
||||
participating_in_3d_context,
|
||||
leaf_prim_index,
|
||||
root_prim_index: current_prim_index,
|
||||
glyph_raster_space,
|
||||
has_mix_blend_mode: composite_ops.mix_blend_mode.is_some(),
|
||||
};
|
||||
|
||||
self.sc_stack.push(sc);
|
||||
@ -1179,30 +1095,47 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
pub fn pop_stacking_context(&mut self) {
|
||||
let sc = self.sc_stack.pop().unwrap();
|
||||
|
||||
// Always pop at least the main picture for this stacking context.
|
||||
let mut pop_count = 1;
|
||||
|
||||
// Remove the picture for any filter/mix-blend-mode effects.
|
||||
pop_count += sc.composite_ops.count();
|
||||
|
||||
// Remove the 3d context container if created
|
||||
if sc.rendering_context_3d_prim_index.is_some() {
|
||||
pop_count += 1;
|
||||
}
|
||||
|
||||
for _ in 0 .. pop_count {
|
||||
let prim_index = self
|
||||
.picture_stack
|
||||
.pop()
|
||||
.expect("bug: mismatched picture stack");
|
||||
// Run the optimize pass on each picture in the chain,
|
||||
// to see if we can collapse opacity and avoid drawing
|
||||
// to an off-screen surface.
|
||||
for i in sc.leaf_prim_index.0 .. sc.root_prim_index.0 + 1 {
|
||||
let prim_index = PrimitiveIndex(i);
|
||||
self.prim_store.optimize_picture_if_possible(prim_index);
|
||||
}
|
||||
|
||||
// By the time the stacking context stack is empty, we should
|
||||
// also have cleared the picture stack.
|
||||
if self.sc_stack.is_empty() {
|
||||
self.picture_stack.pop().expect("bug: picture stack invalid");
|
||||
debug_assert!(self.picture_stack.is_empty());
|
||||
// This must be the root stacking context
|
||||
return;
|
||||
}
|
||||
|
||||
let parent_prim_index = if !sc.establishes_3d_context && sc.participating_in_3d_context {
|
||||
// If we're in a 3D context, we will parent the picture
|
||||
// to the first stacking context we find that is a
|
||||
// 3D rendering context container. This follows the spec
|
||||
// by hoisting these items out into the same 3D context
|
||||
// for plane splitting.
|
||||
self.sc_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|sc| sc.establishes_3d_context)
|
||||
.map(|sc| sc.root_prim_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
self.sc_stack.last().unwrap().leaf_prim_index
|
||||
};
|
||||
|
||||
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
|
||||
parent_pic.add_primitive(sc.root_prim_index);
|
||||
|
||||
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
|
||||
// the stacking context needs to be isolated to blend correctly as per
|
||||
// the CSS spec.
|
||||
// If not already isolated for some other reason,
|
||||
// make this picture as isolated.
|
||||
if sc.has_mix_blend_mode &&
|
||||
self.sc_stack.len() > 2 &&
|
||||
parent_pic.requested_composite_mode.is_none() {
|
||||
parent_pic.requested_composite_mode = Some(PictureCompositeMode::Blit);
|
||||
}
|
||||
|
||||
assert!(
|
||||
@ -2006,9 +1939,6 @@ struct FlattenedStackingContext {
|
||||
/// Pipeline this stacking context belongs to.
|
||||
pipeline_id: PipelineId,
|
||||
|
||||
/// Filters / mix-blend-mode effects
|
||||
composite_ops: CompositeOps,
|
||||
|
||||
/// If true, visible when backface is visible.
|
||||
is_backface_visible: bool,
|
||||
|
||||
@ -2019,10 +1949,14 @@ struct FlattenedStackingContext {
|
||||
/// CSS transform-style property.
|
||||
transform_style: TransformStyle,
|
||||
|
||||
/// If Some(..), this stacking context establishes a new
|
||||
/// 3d rendering context, and the value is the picture
|
||||
// index of the 3d context container.
|
||||
rendering_context_3d_prim_index: Option<PrimitiveIndex>,
|
||||
root_prim_index: PrimitiveIndex,
|
||||
leaf_prim_index: PrimitiveIndex,
|
||||
|
||||
/// If true, this stacking context establishes a new
|
||||
/// 3d rendering context.
|
||||
establishes_3d_context: bool,
|
||||
participating_in_3d_context: bool,
|
||||
has_mix_blend_mode: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -2245,7 +2245,7 @@ impl Primitive {
|
||||
|
||||
match segment_clip_chain {
|
||||
Some(segment_clip_chain) => {
|
||||
if segment_clip_chain.clips_range.count == 0 ||
|
||||
if !segment_clip_chain.needs_mask ||
|
||||
(!segment.may_need_clip_mask && !segment_clip_chain.has_non_local_clips) {
|
||||
segment.clip_task_id = BrushSegmentTaskId::Opaque;
|
||||
continue;
|
||||
@ -2794,7 +2794,7 @@ impl Primitive {
|
||||
return;
|
||||
}
|
||||
|
||||
if clip_chain.clips_range.count > 0 {
|
||||
if clip_chain.needs_mask {
|
||||
if let Some((device_rect, _, _)) = get_raster_rects(
|
||||
clip_chain.pic_clip_rect,
|
||||
&pic_state.map_pic_to_raster,
|
||||
|
@ -753,7 +753,31 @@ impl RenderBackend {
|
||||
window_size: doc.view.window_size,
|
||||
};
|
||||
tx.send(captured).unwrap();
|
||||
|
||||
// notify the active recorder
|
||||
if let Some(ref mut r) = self.recorder {
|
||||
let pipeline_id = doc.scene.root_pipeline_id.unwrap();
|
||||
let epoch = doc.scene.pipeline_epochs[&pipeline_id];
|
||||
let pipeline = &doc.scene.pipelines[&pipeline_id];
|
||||
let scene_msg = SceneMsg::SetDisplayList {
|
||||
list_descriptor: pipeline.display_list.descriptor().clone(),
|
||||
epoch,
|
||||
pipeline_id,
|
||||
background: pipeline.background_color,
|
||||
viewport_size: pipeline.viewport_size,
|
||||
content_size: pipeline.content_size,
|
||||
preserve_frame_state: false,
|
||||
};
|
||||
let txn = TransactionMsg::scene_message(scene_msg);
|
||||
r.write_msg(*frame_counter, &ApiMsg::UpdateDocument(*id, txn));
|
||||
r.write_payload(*frame_counter, &Payload::construct_data(
|
||||
epoch,
|
||||
pipeline_id,
|
||||
pipeline.display_list.data(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Note: we can't pass `LoadCapture` here since it needs to arrive
|
||||
// before the `PublishDocument` messages sent by `load_capture`.
|
||||
return true;
|
||||
|
@ -2048,10 +2048,7 @@ impl Renderer {
|
||||
);
|
||||
|
||||
for alpha_batch_container in &target.alpha_batch_containers {
|
||||
for batch in alpha_batch_container
|
||||
.opaque_batches
|
||||
.iter()
|
||||
.rev() {
|
||||
for batch in alpha_batch_container.opaque_batches.iter().rev() {
|
||||
debug_target.add(
|
||||
debug_server::BatchKind::Opaque,
|
||||
batch.key.kind.debug_name(),
|
||||
@ -2059,8 +2056,7 @@ impl Renderer {
|
||||
);
|
||||
}
|
||||
|
||||
for batch in &alpha_batch_container
|
||||
.alpha_batches {
|
||||
for batch in &alpha_batch_container.alpha_batches {
|
||||
debug_target.add(
|
||||
debug_server::BatchKind::Alpha,
|
||||
batch.key.kind.debug_name(),
|
||||
|
@ -382,7 +382,7 @@ impl TransactionMsg {
|
||||
}
|
||||
|
||||
// TODO: We only need this for a few RenderApi methods which we should remove.
|
||||
fn frame_message(msg: FrameMsg) -> Self {
|
||||
pub fn frame_message(msg: FrameMsg) -> Self {
|
||||
TransactionMsg {
|
||||
scene_ops: Vec::new(),
|
||||
frame_ops: vec![msg],
|
||||
@ -393,7 +393,7 @@ impl TransactionMsg {
|
||||
}
|
||||
}
|
||||
|
||||
fn scene_message(msg: SceneMsg) -> Self {
|
||||
pub fn scene_message(msg: SceneMsg) -> Self {
|
||||
TransactionMsg {
|
||||
scene_ops: vec![msg],
|
||||
frame_ops: Vec::new(),
|
||||
|
@ -22,23 +22,28 @@ pub struct Payload {
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
/// Convert the payload to a raw byte vector, in order for it to be
|
||||
/// efficiently shared via shmem, for example.
|
||||
/// This is a helper static method working on a slice.
|
||||
pub fn construct_data(epoch: Epoch, pipeline_id: PipelineId, dl_data: &[u8]) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(
|
||||
mem::size_of::<u32>() + 2 * mem::size_of::<u32>() + mem::size_of::<u64>() + dl_data.len(),
|
||||
);
|
||||
data.write_u32::<LittleEndian>(epoch.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(pipeline_id.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(pipeline_id.1).unwrap();
|
||||
data.write_u64::<LittleEndian>(dl_data.len() as u64)
|
||||
.unwrap();
|
||||
data.extend_from_slice(dl_data);
|
||||
data
|
||||
}
|
||||
/// Convert the payload to a raw byte vector, in order for it to be
|
||||
/// efficiently shared via shmem, for example.
|
||||
///
|
||||
/// TODO(emilio, #1049): Consider moving the IPC boundary to the
|
||||
/// constellation in Servo and remove this complexity from WR.
|
||||
pub fn to_data(&self) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(
|
||||
mem::size_of::<u32>() + 2 * mem::size_of::<u32>() + mem::size_of::<u64>() +
|
||||
self.display_list_data.len(),
|
||||
);
|
||||
data.write_u32::<LittleEndian>(self.epoch.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(self.pipeline_id.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(self.pipeline_id.1).unwrap();
|
||||
data.write_u64::<LittleEndian>(self.display_list_data.len() as u64)
|
||||
.unwrap();
|
||||
data.extend_from_slice(&self.display_list_data);
|
||||
data
|
||||
Self::construct_data(self.epoch, self.pipeline_id, &self.display_list_data)
|
||||
}
|
||||
|
||||
/// Deserializes the given payload from a raw byte vector.
|
||||
|
@ -1 +1 @@
|
||||
04d63e7d73b9661d9eb934a0933c8f9751a9a3db
|
||||
02f14d0f333ef125d1abff7b1146039a0ba75f43
|
||||
|
@ -1,38 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* Linked list node for undisplayed element */
|
||||
|
||||
#ifndef mozilla_UndisplayedNode_h
|
||||
#define mozilla_UndisplayedNode_h
|
||||
|
||||
#include "nsIContent.h"
|
||||
#include "mozilla/ComputedStyle.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Node in a linked list, containing the style for an element that
|
||||
* does not have a frame but whose parent does have a frame.
|
||||
*/
|
||||
struct UndisplayedNode : public LinkedListElement<UndisplayedNode>
|
||||
{
|
||||
UndisplayedNode(nsIContent* aContent, ComputedStyle* aStyle)
|
||||
: mContent(aContent)
|
||||
, mStyle(aStyle)
|
||||
{
|
||||
MOZ_COUNT_CTOR(mozilla::UndisplayedNode);
|
||||
}
|
||||
|
||||
~UndisplayedNode() { MOZ_COUNT_DTOR(mozilla::UndisplayedNode); }
|
||||
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
RefPtr<ComputedStyle> mStyle;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_UndisplayedNode_h
|
@ -83,7 +83,6 @@ EXPORTS.mozilla += [
|
||||
'ScrollStyles.h',
|
||||
'ShapeUtils.h',
|
||||
'StaticPresData.h',
|
||||
'UndisplayedNode.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -3270,7 +3270,7 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
|
||||
break;
|
||||
default: {
|
||||
MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
|
||||
"bug in nsRuleNode::ComputeDisplayData?");
|
||||
"bug in StyleAdjuster::adjust_for_fieldset_content?");
|
||||
|
||||
contentFrame = NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
|
||||
contentFrameTop =
|
||||
@ -4897,9 +4897,10 @@ nsCSSFrameConstructor::FindMathMLData(const Element& aElement,
|
||||
|
||||
// Handle <math> specially, because it sometimes produces inlines
|
||||
if (tag == nsGkAtoms::math) {
|
||||
// This needs to match the test in EnsureBlockDisplay in
|
||||
// nsRuleNode.cpp. Though the behavior here for the display:table
|
||||
// case is pretty weird...
|
||||
// The IsBlockOutsideStyle() check must match what
|
||||
// specified::Display::equivalent_block_display is checking for
|
||||
// already-block-outside things. Though the behavior here for the
|
||||
// display:table case is pretty weird...
|
||||
if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
|
||||
static const FrameConstructionData sBlockMathData =
|
||||
FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "mozilla/PresState.h"
|
||||
#include "mozilla/ComputedStyle.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/UndisplayedNode.h"
|
||||
#include "nsIDocument.h"
|
||||
|
||||
#include "nsError.h"
|
||||
|
@ -12,12 +12,7 @@
|
||||
PRES_ARENA_OBJECT(GeckoComputedStyle)
|
||||
|
||||
PRES_ARENA_OBJECT(nsLineBox)
|
||||
PRES_ARENA_OBJECT(nsRuleNode)
|
||||
PRES_ARENA_OBJECT(DisplayItemData)
|
||||
PRES_ARENA_OBJECT(nsInheritedStyleData)
|
||||
PRES_ARENA_OBJECT(nsResetStyleData)
|
||||
PRES_ARENA_OBJECT(nsConditionalResetStyleData)
|
||||
PRES_ARENA_OBJECT(nsConditionalResetStyleDataEntry)
|
||||
PRES_ARENA_OBJECT(nsFrameList)
|
||||
PRES_ARENA_OBJECT(CustomCounterStyle)
|
||||
PRES_ARENA_OBJECT(DependentBuiltinCounterStyle)
|
||||
|
@ -837,13 +837,6 @@ ReflowInput::InitFrameType(LayoutFrameType aFrameType)
|
||||
const nsStyleDisplay *disp = mStyleDisplay;
|
||||
nsCSSFrameType frameType;
|
||||
|
||||
// Section 9.7 of the CSS2 spec indicates that absolute position
|
||||
// takes precedence over float which takes precedence over display.
|
||||
// XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
|
||||
// Make sure the frame was actually moved out of the flow, and don't
|
||||
// just assume what the style says, because we might not have had a
|
||||
// useful float/absolute containing block
|
||||
|
||||
DISPLAY_INIT_TYPE(mFrame, this);
|
||||
|
||||
if (aFrameType == LayoutFrameType::Table) {
|
||||
|
@ -46,22 +46,20 @@ enum class CSSPseudoElementType : uint8_t;
|
||||
class ComputedStyle;
|
||||
|
||||
/**
|
||||
* A ComputedStyle represents the computed style data for an element. The
|
||||
* computed style data are stored in a set of structs (see nsStyleStruct.h) that
|
||||
* are cached either on the ComputedStyle or in the rule tree (see nsRuleNode.h
|
||||
* for a description of this caching and how the cached structs are shared).
|
||||
* A ComputedStyle represents the computed style data for an element.
|
||||
*
|
||||
* Since the data in |nsIStyleRule|s and |nsRuleNode|s are immutable (with a few
|
||||
* exceptions, like system color changes), the data in an ComputedStyle are also
|
||||
* immutable (with the additional exception of GetUniqueStyleData). When style
|
||||
* data change, ElementRestyler::Restyle creates a new ComputedStyle.
|
||||
* The computed style data are stored in a set of reference counted structs
|
||||
* (see nsStyleStruct.h) that are stored directly on the ComputedStyle.
|
||||
*
|
||||
* Style structs are immutable once they have been produced, so when any change
|
||||
* is made that needs a restyle, we create a new ComputedStyle.
|
||||
*
|
||||
* ComputedStyles are reference counted. References are generally held by:
|
||||
* 1. the |nsIFrame|s that are using the ComputedStyle and
|
||||
* 2. any *child* ComputedStyle (this might be the reverse of
|
||||
* expectation, but it makes sense in this case)
|
||||
*
|
||||
* FIXME(emilio): This comment is somewhat outdated now.
|
||||
* 1. nsIFrame::mComputedStyle, for every frame
|
||||
* 2. Element::mServoData, for every element not inside a display:none subtree
|
||||
* 3. nsComputedDOMStyle, when created for elements in display:none subtrees
|
||||
* 4. media_queries::Device, which holds the initial value of every property
|
||||
*/
|
||||
|
||||
enum class ComputedStyleBit : uint8_t
|
||||
|
@ -146,22 +146,11 @@ ProcessTranslatePart(const nsCSSValue& aValue,
|
||||
percent = aValue.GetPercentValue();
|
||||
} else if (aValue.GetUnit() == eCSSUnit_Pixel ||
|
||||
aValue.GetUnit() == eCSSUnit_Number) {
|
||||
// Handle this here (even though nsRuleNode::CalcLength handles it
|
||||
// fine) so that callers are allowed to pass a null ComputedStyle
|
||||
// and pres context to SetToTransformFunction if they know (as
|
||||
// StyleAnimationValue does) that all lengths within the transform
|
||||
// function have already been computed to pixels and percents.
|
||||
//
|
||||
// Raw numbers are treated as being pixels.
|
||||
//
|
||||
// Don't convert to aValue to AppUnits here to avoid precision issues.
|
||||
return aValue.GetFloatValue();
|
||||
} else if (aValue.IsCalcUnit()) {
|
||||
// Servo backend. We can retrieve the Calc value directly because it has
|
||||
// been computed from Servo side and set by nsCSSValue::SetCalcValue().
|
||||
// We don't use nsRuleNode::SpecifiedCalcToComputedCalc() because it
|
||||
// asserts for null context and we always pass null context for Servo
|
||||
// backend.
|
||||
// We can retrieve the Calc value directly because it has been computed
|
||||
// from the Servo side and set by nsCSSValue::SetCalcValue().
|
||||
nsStyleCoord::CalcValue calc = aValue.GetCalcValue();
|
||||
percent = calc.mPercent;
|
||||
offset = calc.mLength;
|
||||
|
@ -11,7 +11,7 @@
|
||||
@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
|
||||
|
||||
*|*::-moz-fieldset-content {
|
||||
display: block; /* nsRuleNode::ComputeDisplayData overrules this in some cases */
|
||||
display: block; /* StyleAdjuster::adjust_for_fieldset_content overrides this in some cases */
|
||||
unicode-bidi: inherit;
|
||||
text-overflow: inherit;
|
||||
overflow: inherit;
|
||||
|
@ -20,18 +20,19 @@
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/**
|
||||
* The purpose of this test is to test that nsRuleNode::Compute*Data
|
||||
* functions are written correctly. In particular, in these functions,
|
||||
* when the specified value of a property has unit eCSSUnit_Null,
|
||||
* touching the computed data is forbidden. This is because we
|
||||
* sometimes stop walking up the rule tree when we find computed data
|
||||
* for an initial subsequence of our rules (i.e., an ancestor rule node)
|
||||
* that we can use as a starting point (aStartStruct) for the
|
||||
* computation for the current rule node.
|
||||
* The purpose of this test was to test that in the old style system
|
||||
* the nsRuleNode::Compute*Data functions were written correctly.
|
||||
* In particular, in these functions, when the specified value of a
|
||||
* property had unit eCSSUnit_Null, touching the computed data is
|
||||
* forbidden. This is because we sometimes would stop walking up the
|
||||
* rule tree when we find computed data for an initial subsequence of
|
||||
* our rules (i.e., an ancestor rule node) that we can use as a starting
|
||||
* point (aStartStruct) for the computation for the current rule node.
|
||||
*
|
||||
* If one of these tests fails, you should look for a case where the
|
||||
* property's code in nsRuleNode::Compute*Data touches the computed
|
||||
* value when the specified value has eCSSUnit_Null, and fix it.
|
||||
* However, we don't cache style structs in the rule tree in the current
|
||||
* style system code, and property cascading no longer relies on hand
|
||||
* written functions, so this particular failure mode isn't as likely to
|
||||
* happen.
|
||||
*
|
||||
* The test works by maintaining one style rule that has every CSS
|
||||
* property specified, and a second style rule that has different values
|
||||
|
@ -102,14 +102,20 @@ function test_property(property)
|
||||
isnot(other_computed_f, initial_computed_f,
|
||||
"should be testing with values that compute to different things " +
|
||||
"for '" + property + "'");
|
||||
// It's important (given the current design of nsRuleNode) that we're
|
||||
// modifying the most specific rule that matches the element, and that
|
||||
// we've already requested style while that rule was empty. This
|
||||
// means we'll have a cached aStartStruct from the parent in the rule
|
||||
// tree (caching the "other" value), so we'll make sure we don't get
|
||||
// the initial value from the luck of default-initialization.
|
||||
// This means that it's important that we set the prereqs on
|
||||
// It used to be important for values that are supposed to compute to the
|
||||
// initial value (given the design of the old rule tree, nsRuleNode) that
|
||||
// we're modifying the most specific rule that matches the element, and
|
||||
// that we've already requested style while that rule was empty. This
|
||||
// means we'd have a cached aStartStruct from the parent in the rule
|
||||
// tree (caching the "other" value), so we'd make sure we don't get the
|
||||
// initial value from the luck of default-initialization. This means
|
||||
// that it would've been important that we set the prereqs on
|
||||
// gRule1.style rather than on gElement.style.
|
||||
//
|
||||
// However, the rule tree no longer stores cached structs, and we only
|
||||
// temporarily cache reset structs during a single restyle. So the
|
||||
// particular failure mode this was designed to test for isn't as
|
||||
// likely to eventuate.
|
||||
gRule2.style.setProperty(property, keyword, "");
|
||||
var initial_val_computed_n = get_computed_value(getComputedStyle(gElementN, ""), property);
|
||||
var initial_val_computed_f = get_computed_value(getComputedStyle(gElementF, ""), property);
|
||||
|
@ -156,15 +156,20 @@ function test_value(property, val, is_initial)
|
||||
"should be testing with values that compute to different things " +
|
||||
"for '" + property + "'");
|
||||
}
|
||||
// It's important for values that are supposed to compute to the
|
||||
// initial value (given the current design of nsRuleNode) that we're
|
||||
// modifying the most specific rule that matches the element, and that
|
||||
// we've already requested style while that rule was empty. This
|
||||
// means we'll have a cached aStartStruct from the parent in the rule
|
||||
// tree (caching the "other" value), so we'll make sure we don't get
|
||||
// the initial value from the luck of default-initialization.
|
||||
// This means that it's important that we set the prereqs on
|
||||
// It used to be important for values that are supposed to compute to the
|
||||
// initial value (given the design of the old rule tree, nsRuleNode) that
|
||||
// we're modifying the most specific rule that matches the element, and
|
||||
// that we've already requested style while that rule was empty. This
|
||||
// means we'd have a cached aStartStruct from the parent in the rule
|
||||
// tree (caching the "other" value), so we'd make sure we don't get the
|
||||
// initial value from the luck of default-initialization. This means
|
||||
// that it would've been important that we set the prereqs on
|
||||
// gRule1.style rather than on gElement.style.
|
||||
//
|
||||
// However, the rule tree no longer stores cached structs, and we only
|
||||
// temporarily cache reset structs during a single restyle. So the
|
||||
// particular failure mode this was designed to test for isn't as
|
||||
// likely to eventuate.
|
||||
gRule2.style.setProperty(property, val, "");
|
||||
var val_computed_n = get_computed_value(getComputedStyle(gElementN, ""), property);
|
||||
var val_computed_f = get_computed_value(getComputedStyle(gElementF, ""), property);
|
||||
|
@ -4677,26 +4677,16 @@ Tab.prototype = {
|
||||
|
||||
OnHistoryGotoIndex: function(index, gotoURI) {
|
||||
Services.obs.notifyObservers(this.browser, "Content:HistoryChange");
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryPurge: function(numEntries) {
|
||||
Services.obs.notifyObservers(this.browser, "Content:HistoryChange");
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryReplaceEntry: function(index) {
|
||||
Services.obs.notifyObservers(this.browser, "Content:HistoryChange");
|
||||
},
|
||||
|
||||
OnLengthChanged: function(aCount) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
},
|
||||
|
||||
OnIndexChanged: function(aIndex) {
|
||||
// Ignore, the method is implemented so that XPConnect doesn't throw!
|
||||
},
|
||||
|
||||
UpdateMediaPlaybackRelatedObserver: function(active) {
|
||||
// Media control is only used for the tab which has playing media, so we
|
||||
// only need to register observer after having the active media. And the
|
||||
|
@ -2403,7 +2403,8 @@ fn static_assert() {
|
||||
/// Calculates the constrained and unconstrained font sizes to be inherited
|
||||
/// from the parent.
|
||||
///
|
||||
/// See ComputeScriptLevelSize in Gecko's nsRuleNode.cpp
|
||||
/// This is a port of Gecko's old ComputeScriptLevelSize function:
|
||||
/// https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3197-3254
|
||||
///
|
||||
/// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is
|
||||
/// +1, for example, it will inherit as the script size multiplier times
|
||||
@ -2511,17 +2512,18 @@ fn static_assert() {
|
||||
= self.calculate_script_level_size(parent, device);
|
||||
if adjusted_size.0 != parent.gecko.mSize ||
|
||||
adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize {
|
||||
// This is incorrect. When there is both a keyword size being inherited
|
||||
// and a scriptlevel change, we must handle the keyword size the same
|
||||
// way we handle em units. This complicates things because we now have
|
||||
// to keep track of the adjusted and unadjusted ratios in the kw font size.
|
||||
// This only affects the use case of a generic font being used in MathML.
|
||||
// FIXME(Manishearth): This is incorrect. When there is both a
|
||||
// keyword size being inherited and a scriptlevel change, we must
|
||||
// handle the keyword size the same way we handle em units. This
|
||||
// complicates things because we now have to keep track of the
|
||||
// adjusted and unadjusted ratios in the kw font size. This only
|
||||
// affects the use case of a generic font being used in MathML.
|
||||
//
|
||||
// If we were to fix this I would prefer doing it by removing the
|
||||
// ruletree walk on the Gecko side in nsRuleNode::SetGenericFont
|
||||
// and instead using extra bookkeeping in the mSize and mScriptUnconstrainedSize
|
||||
// values, and reusing those instead of font_size_keyword.
|
||||
|
||||
// If we were to fix this I would prefer doing it not doing
|
||||
// something like the ruletree walk that Gecko used to do in
|
||||
// nsRuleNode::SetGenericFont and instead using extra bookkeeping in
|
||||
// the mSize and mScriptUnconstrainedSize values, and reusing those
|
||||
// instead of font_size_keyword.
|
||||
|
||||
// In the case that MathML has given us an adjusted size, apply it.
|
||||
// Keep track of the unconstrained adjusted size.
|
||||
|
@ -1200,9 +1200,6 @@ impl StrongRuleNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `nsRuleNode::HasAuthorSpecifiedRules` for Servo rule
|
||||
/// nodes.
|
||||
///
|
||||
/// Returns true if any properties specified by `rule_type_mask` was set by
|
||||
/// an author rule.
|
||||
#[cfg(feature = "gecko")]
|
||||
|
@ -746,7 +746,12 @@ impl ToComputedValue for KeywordSize {
|
||||
fn to_computed_value(&self, cx: &Context) -> NonNegativeLength {
|
||||
use context::QuirksMode;
|
||||
use values::specified::length::au_to_int_px;
|
||||
// Data from nsRuleNode.cpp in Gecko
|
||||
|
||||
// The tables in this function are originally from
|
||||
// nsRuleNode::CalcFontPointSize in Gecko:
|
||||
//
|
||||
// https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3262-3336
|
||||
|
||||
// Mapping from base size and HTML size to pixels
|
||||
// The first index is (base_size - 9), the second is the
|
||||
// HTML size. "0" is CSS keyword xx-small, not HTML size 0,
|
||||
@ -765,9 +770,6 @@ impl ToComputedValue for KeywordSize {
|
||||
[9, 10, 13, 16, 18, 24, 32, 48],
|
||||
];
|
||||
|
||||
// Data from nsRuleNode.cpp in Gecko
|
||||
// (https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3303)
|
||||
//
|
||||
// This table gives us compatibility with WinNav4 for the default fonts only.
|
||||
// In WinNav4, the default fonts were:
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user