import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html class="reftest-print"><head>
<meta charset="utf-8">
<title>Testcase for bug 1102791</title>
<style type="text/css">
html,body {
color:black; background-color:white; font-size:16px; padding:0; margin:0;
}
button {
position: absolute;
-moz-appearance: none;
background: transparent;
padding: 0;
border-style:none;
}
button::before {
position: absolute;
content: "::before";
width: 10px;
height: 200em;
border: 1px solid black;
}
</style>
</head>
<body>
<button></button>
</body>
</html>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<style>
input { -moz-appearance: textfield; }
</style>
<script>
function boom()
{
window.getComputedStyle(x, "::-moz-number-spin-down").getPropertyValue("color");
}
</script>
<body onload="boom();">
<input type="number" id="x">
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html class="reftest-print">
<head>
<meta charset="UTF-8">
<style type="text/css">
#menu { position: fixed; left: 0px; top: 0px; }
</style>
</head>
<body>
<svg id="canvas" width="2427" height="2295.5" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
<div id="menu">
<input id="chooseSize" type="range">
</div>
</body>
</html>

View file

@ -0,0 +1,27 @@
<style type="text/css">
optgroup {overflow-x: hidden;}
</style>
<select>
<optgroup label="optgroup">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
<option>11</option>
<option>12</option>
<option>13</option>
<option>14</option>
<option>15</option>
<option>16</option>
<option>17</option>
<option>18</option>
<option>19</option>
<option>20</option>
</optgroup>
</select>

View file

@ -0,0 +1,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<select>
<optgroup style="display: list-item; list-style: inside;"></optgroup>
</select>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html class="reftest-print"><head>
<meta charset="utf-8">
<title>Testcase for bug 1279354</title>
</head>
<body>
<div style="position:fixed"><progress></progress></div>
1
<br style="page-break-after: always">
2
<br style="page-break-after: always">
3
<br style="page-break-after: always">
</body>
</html>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><title>Killer</title></head>
<body>
<form style="overflow: auto;">
<select style="position: fixed;">
<option>First</option>
<option>Second</option>
<option>Third</option>
</select>
</form>
</body>
</html>

View file

@ -0,0 +1,8 @@
<html>
<body>
<fieldset style="width: 300px; position: fixed">
<legend>Crash test</legend>
<div style="float: right; background-color: orange; border: 1px solid black">Hello, my name is Inigo Montoya.</div> hello world content is the best content around, I love hello world content to death, especially when it wraps; that just gives me the chills. Anything less than hello world content is uncivilized.
</fieldset>
</body>
</html>

View file

@ -0,0 +1,24 @@
<html>
<head></head>
<body>
<form method="post" action="#" enctype="multipart/form-data" name="content">
<div id="sshot" style="position:absolute; left:0; top:70; width:600;
visibility:visible">
<input type="file" name="sshot-1" size="20">
</div>
<div id="comment" style="position:absolute; left:0; top:70; width:600;
visibility:hidden"></div>
<script language="JavaScript">
<!--
document.documentElement.offsetHeight;
document.getElementById('sshot').style.display = 'none';
document.getElementById('comment').style.display = 'none';
// -->
</script>
</form>
</body></html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<HTML>
<BODY>
<EMBED>
<TABLE>
<TR>
<TD STYLE="FONT: 10px Arial;">
<A STYLE="FONT: 11px Arial;">1</A>
</TD>
</TR>
</TABLE>
<DIV STYLE="POSITION: absolute;">
<FORM>
<SELECT STYLE="FONT: 11px Arial;">
</FORM>
</DIV>
</BODY>
</HTML>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>Crash Testcase</title>
<script type="text/javascript">
function crash()
{
var inp = document.getElementById("theinp");
inp.type = "file";
}
</script>
</head>
<body onload="crash();">
<input id="theinp" type="text">
</body>
</html>

View file

@ -0,0 +1,7 @@
<HTML>
<HEAD>
</HEAD>
<BODY>
<FIELDSET STYLE="float:right; text-indent:999px;">Test</FIELDSET>
</BODY>
</HTML>

View file

@ -0,0 +1,9 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<select><span style="position: absolute;" /></select>
</body>
</html>

View file

@ -0,0 +1,19 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script><![CDATA[
function init()
{
var select = document.getElementsByTagName("select")[0];
select.parentNode.removeChild(select);
}
window.addEventListener("load", init, 0);
]]></script>
</head>
<body><select><input/></select></body>
</html>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace">
<head>
<title>Settings - Unclassified NewsBoard</title>
</head>
<body>
<select name="Language">
<option value="" style="clear: right;">
Automatic </option>
<option value="de" style="clear: right;">
<span alt="" style="float: right; margin-top: 1px;"> Deutsch</span> <small>(de)</small> </option>
<option value="en" selected="selected" style="clear: right;">
<span alt="" style="float: right; margin-top: 1px;"> English</span> <small>(en)</small> </option>
</select>
</body>
</html>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Testcase, bug 317502</title>
</head>
<body onload="document.getElementsByTagName('input')[0].style.width='200px'">
<input type="file" style="position: fixed" />
</body>
</html>

View file

@ -0,0 +1,17 @@
<html><head>
<title>
Testcase bug 321894 - ASSERTION: RemovedAsPrimaryFrame called after PreDestroy: 'PR_FALSE'
</title>
</head>
<body>
This shouldn't assert in a debug build
<span>
<script>var x=document.body.offsetHeight;</script>
<div>
<span style="float:left;">
<input type="text">
</span>
</div>
</span>
</body>
</html>

View file

@ -0,0 +1,21 @@
<html>
<head>
<style>
select {
visibility: collapse;
}
body {
display: -moz-grid-group;
}
</style>
</head>
<body>
<select><option>a</option></select>
</body>
</html>

View file

@ -0,0 +1,4 @@
<select style="position: absolute;">
<meta>
<option style="position: absolute;">
<body style="display: block;">

View file

@ -0,0 +1,10 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<body>
<xul:hbox>
<input type="file" />
</xul:hbox>
</body>
</html>

View file

@ -0,0 +1,2 @@
<div style="display: -moz-grid-line;">
<input type="file">

View file

@ -0,0 +1,5 @@
<html><head></head><body>
<span style="display: -moz-box;"><input type="file"><textarea></textarea></span>
</body></html>

View file

@ -0,0 +1,11 @@
<html>
<head>
</head>
<body onload='var yar=document.getElementById("yar"); yar.parentNode.removeChild(yar); document.getElementById("foo").removeAttribute("multiple");'>
<div id="yar">Clicking the button below should not trigger an assertion</div>
<select id="foo" multiple><option>A</option></select>
</body>
</html>

View file

@ -0,0 +1,32 @@
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
<head>
<script>
function boom()
{
var fieldset = document.getElementById("fieldset");
var h3 = document.getElementById("h3");
var legend = document.getElementById("legend");
fieldset.appendChild(legend);
fieldset.appendChild(h3);
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="setTimeout(boom, 30);">
<legend id="legend">legend</legend>
<h3 id="h3">H3</h3>
<select><option>option<fieldset id="fieldset"></fieldset></option></select>
</body>
</html>

View file

@ -0,0 +1,37 @@
<html class="reftest-wait">
<head>
<style>
#opt1 { display: table-footer-group; }
#opt1 { visibility: collapse; }
#opt1 { overflow: -moz-scrollbars-vertical; }
#opt2 { display: table-cell; }
#opt2 { clear: left; }
select { width: 1px; }
</style>
<script>
function boom()
{
document.getElementById("opt2").style.clear = "none";
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="setTimeout(boom, 30);">
<select multiple>
<option id="opt1">A</option>
<option id="opt2">B</option>
</select>
</body>
</html>

View file

@ -0,0 +1,30 @@
<html class="reftest-wait">
<head>
<script>
function boom()
{
document.getElementById("M").style.width = "6em";
document.documentElement.removeAttribute("class");
}
</script>
<body onload="setTimeout(boom, 30);">
<div style="position:absolute; top: 50px; left: 50px; border: 2px solid orange;">
<select>
<option id="M">M</option>
</select>
<br>
<select style="width: 200%">
<option style="visibility: collapse; overflow: auto; display: table-footer-group;">X</option>
</select>
</div>
</body>
</html>

View file

@ -0,0 +1,28 @@
<html class="reftest-wait">
<head>
<script>
var HTML_NS = "http://www.w3.org/1999/xhtml";
function boom1()
{
var newSpan = document.createElementNS(HTML_NS, "span");
document.body.appendChild(newSpan);
setTimeout(boom2, 30);
}
function boom2()
{
var newDiv = document.createElementNS(HTML_NS, "div");
document.getElementById("s").appendChild(newDiv);
document.documentElement.removeAttribute("class");
}
</script>
</head>
<body onload="setTimeout(boom1, 30);">
<p>العربي</p>
<span id="s"></span><input><select></select><isindex><span></span>
</body>
</html>

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Untitled</title>
</head>
<body>
Focus the input of the isindex, then do a reload, Mozilla should not crash
<isindex autofocus>
</label>
</body>
</html>

View file

@ -0,0 +1,40 @@
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
<head>
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="foo">
<content>
<children xmlns="http://www.mozilla.org/xbl" />
</content>
</binding>
</bindings>
<script>
function boom()
{
document.getElementById("div").style.MozBinding = "url('#foo')";
var opt1 = document.getElementById("opt1");
opt1.removeChild(opt1.firstChild);
document.getElementById("textarea").value += " ";
document.documentElement.removeAttribute("class")
}
</script>
</head>
<body onload="setTimeout(boom, 30);">
<div id="div">
<textarea rows="3" cols="5" id="textarea"></textarea>
</div>
<select>
<option id="opt1">opt1</option>
</select>
</body>
</html>

View file

@ -0,0 +1,4 @@
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="a">
<content><children/></content>
</binding></bindings>

View file

@ -0,0 +1,17 @@
<html><head>
<title>Testcase bug - Crash [@ nsFileControlFrame::CreateAnonymousContent] when removing stylesheet with binding and removing file input</title>
<style>
body {-moz-binding:url("375299-binding.xml#a"); }
</style>
</head><body onload="doe()">
<input type="file">
<script>
function doe() {
var x=document.getElementsByTagName('input')[0];
x.parentNode.removeChild(x);
var y=document.getElementsByTagName('style')[0];
y.parentNode.removeChild(y);
}
</script>
</body></html>

View file

@ -0,0 +1,19 @@
<html>
<head>
<title>Testcase bug - Crash [@ nsEventListenerManager::FixContextMenuEvent] when firing contextmenu event in display: none iframe</title>
</head>
<body>
This page should not crash Mozilla
<iframe style="display: none;"></iframe>
<script>
function docontextmenu(i){
var doc = window.frames[0].document;
var ev = doc.createEvent ('MouseEvents');
ev.initMouseEvent('contextmenu', true,true, window, 3,5, 5, 400, 400, 0, 0, 0,0,0,null);
doc.dispatchEvent(ev);
}
setTimeout(docontextmenu,0);
</script>
</body>
</html>

View file

@ -0,0 +1,16 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body style="direction: rtl;">
<form style="display: -moz-deck; width: 1em;">
<fieldset>
<legend>Your name</legend>
<input type="text" name="name" size="20"/>
</fieldset>
<fieldset>
<legend>Your E-mail address</legend>
<input type="text" name="email" size="25"/>
</fieldset>
</form>
</body>
</html>

View file

@ -0,0 +1,11 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body onload="document.getElementById('t').style.display = 'block';">
<table border="1" id="t">
<tr>
<td><select><option>foopy</option></select></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<body>
<div>
<xul:textbox><select><option>foo</option></select></xul:textbox>
</div>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<div style="position: absolute">
<input type="file" style="position: absolute; -moz-appearance: menulist;">
</div>
</body>
</html>

View file

@ -0,0 +1,20 @@
<html>
<body>
<table>
<tr>
<td>
<select>
<option style="padding: 200%;">
</select>
</td>
</tr>
<tr>
<td>
<div style="padding: 200%"><span style="float: left; border: 1px solid red;"></span></div>
</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,14 @@
<html>
<head></head>
<body>
<div style="position: absolute"><input id="input" type="file" style="top: 100%"></div>
<script>
document.body.offsetHeight;
document.getElementById("input").style.position = "absolute";
</script>
</body>
</html>

View file

@ -0,0 +1,22 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
function boom()
{
var newText = document.createTextNode('x');
document.getElementById("a").parentNode.appendChild(newText);
}
</script>
</head>
<body onload="boom()">
<table border="1">
<tr>
<td>1</td>
<td id="a" style="padding-left: 10%"><select style="padding-left: 20%; bottom: 10%;"><option style="padding: 0 0 10% 20%;">pppp</option></select></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,25 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
function boom()
{
var newDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
newDiv.style.width = "100px";
newDiv.style.height = "8px";
newDiv.style.background = "lightgreen";
document.getElementById("tr").appendChild(newDiv);
}
</script>
</head>
<body onload="boom()">
<table border="1">
<tr id="tr">
<td></td>
<td style="padding-left: 10%"><select style="padding-left: 20%; bottom: 10%;"><option style="padding: 0 0 10% 20%; width: 25px;"></option></select></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,13 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<select style="float: right; clear: right;">
<option><a style="float: right;"></a></option>
<option style="margin: 100%;"></option>
<option><div style="clear: both"></div></option>
</select>
</body>
</html>

View file

@ -0,0 +1,22 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<div style="overflow: hidden;">
<div style="float: right;"><span>XXX</span></div>
<select style="float: right; clear: right;">
<option><a style="float: right;"></a></option>
<option style="margin: 100%;"></option>
<option><div style="clear: both"></div></option>
</select>
<dl style="float: left;"></dl>
<div>
<div style="float: left;"></div>
<div style="display: block; clear: both"></div>
<dl style="float: left;"></dl>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,50 @@
<style>
select::first-letter, option::first-letter { color:green; }
.block select, .block option{ display:block; }
.scroll select, .scroll option{ display:block; overflow:hidden; }
</style>
<select></select>
<select>F</select>
<select>Ff</select>
<select><option>F</select>
<select><option>Ff</select>
<div class="block">
<select></select>
<select>F</select>
<select>Ff</select>
<select><option>F</select>
<select><option>Ff</select>
</div>
<div class="scroll">
<select></select>
<select>F</select>
<select>Ff</select>
<select><option>F</select>
<select><option>Ff</select>
</div>
<select size=2></select>
<select size=2>F</select>
<select size=2>Ff</select>
<select size=2><option>F</select>
<select size=2><option>Ff</select>
<div class="block">
<select size=2></select>
<select size=2>F</select>
<select size=2>Ff</select>
<select size=2><option>F</select>
<select size=2><option>Ff</select>
</div>
<div class="scroll">
<select size=2></select>
<select size=2>F</select>
<select size=2>Ff</select>
<select size=2><option>F</select>
<select size=2><option>Ff</select>
</div>

View file

@ -0,0 +1,2 @@
<body style="text-indent: 99999999999999999999px;">
<fieldset>

View file

@ -0,0 +1,22 @@
<html>
<head>
<script>
function boom()
{
document.getElementById("ce").contentEditable = true;
document.documentElement.offsetHeight;
document.getElementById("finput").setAttribute("type", "");
}
</script>
</head>
<body onload="boom();">
<input id="finput" type="file" disabled>
<span id="ce"></span>
</body>
</html>

View file

@ -0,0 +1,5 @@
<html>
<body>
<input type="file" style="position: fixed; height: 0; width: 10px; white-space: normal;">
</body>
</html>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<fieldset>
<legend style="padding: 0pt 100%;"></legend>
</fieldset>
</body>
</html>

View file

@ -0,0 +1 @@
<div style="display: inline-block; -moz-column-count: 2;">text<input type="reset" style="float: right;">

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<div style="-moz-column-width: 1px;"><select style="height: 12em; display: block;"></select></div>
<div style="-moz-column-width: 1px;"><select multiple style="height: 12em; display: block;"></select></div>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function boom()
{
window.addEventListener("DOMCharacterDataModified", function(){}, false);
document.body.appendChild(document.createElement("isindex"));
}
</script>
</head>
<body onload="boom();"></body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function boom()
{
window.addEventListener("DOMAttrModified", function(){}, false);
document.body.appendChild(document.createElement("input"));
}
</script>
</head>
<body onload="boom();"></body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function boom()
{
window.addEventListener("DOMAttrModified", function(){}, false);
document.body.appendChild(document.createElement("isindex"));
}
</script>
</head>
<body onload="boom();"></body>
</html>

View file

@ -0,0 +1,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<div>
<xul:textbox onDOMAttrModified="this.parentNode.removeChild(this)">
<mathml:mathml/>
</xul:textbox>
</div>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<select style="height: 3401091640591pc"></select>
</body>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html><body onload="document.body.style.display = 'table';" style="-moz-column-count: 1; white-space: pre-line; -moz-appearance: resizer; height: 23698324514pc;">
<input></body></html>

View file

@ -0,0 +1,20 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
<![CDATA[
function boom()
{
var option = document.getElementsByTagName("option")[0];
var b = option.firstChild;
option.appendChild(document.createTextNode("\u064A"));
document.documentElement.offsetHeight;
option.removeChild(b);
}
]]>
</script>
</head>
<body onload="boom();"><select><option>B </option></select></body>
</html>

View file

@ -0,0 +1,15 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
function boom()
{
var xt = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 'textbox');
document.body.appendChild(xt);
xt.setAttribute('disabled', "true");
xt.setAttribute('value', "foo");
}
</script>
</head>
<body onload="boom();">
</body>
</html>

View file

@ -0,0 +1,17 @@
<html class="reftest-print">
<head>
<style>
h2 { page-break-before: always; }
#reviewer { position: fixed;}
</style>
</head>
<body>
<h2>Crash Test Case</h2>
<div>
<input id='reviewer'>
</div>
</body>
</html>

View file

@ -0,0 +1,4 @@
<html class="reftest-print" xmlns="http://www.w3.org/1999/xhtml" style=" float: left; white-space: pre; ">
<div style="page-break-before: always;"></div>
<textarea style="position: fixed;"></textarea>
</html>

View file

@ -0,0 +1,20 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
<![CDATA[
function boom()
{
document.getElementById("i").selectionEnd;
document.getElementById("a").appendChild(document.getElementById("b"));
}
]]>
</script>
</head>
<body onload="boom();">
<mrow xmlns="http://www.w3.org/1998/Math/MathML" id="a"><mrow id="b"/><input xmlns="http://www.w3.org/1999/xhtml" id="i" /></mrow>
</body>
</html>

View file

@ -0,0 +1,26 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
<![CDATA[
function remove(n)
{
n.parentNode.removeChild(n);
}
function boom()
{
document.documentElement.offsetHeight;
remove(document.getElementById("s"));
remove(document.getElementById("e"));
document.documentElement.offsetHeight;
}
]]>
</script>
</head>
<body onload="boom();">
<embed id="e" src="data:text/html,A"></embed><span id="s"><div></div></span><isindex/>
</body>
</html>

View file

@ -0,0 +1,14 @@
<html>
<head>
<title>Untitled</title>
</head>
<body>
<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%0A%3Ctextarea%3E%3C/textarea%3E%0A%0A%0A%3Cstyle%3E%0A@font-face%20%7B%0A%20%20%20%20%20%20font-family%3A%20%22cutabovetherest%22%3B%0A%20%20%20%20%20%20src%3A%20url%28%22http%3A//www.webpagepublicity.com/free-fonts/a/A%2520Cut%2520Above%2520The%2520Rest.ttf%22%29%3B%0A%7D%20%20%20%20%0A%0A%3C/style%3E%0A%0A%3Coptgroup%20contenteditable%3D%22true%22%20style%3D%22display%3A%20inline%3B%22%3E%3C/optgroup%3E%0A%0A%3C/body%3E%3C/html%3E"></iframe>
<script>
</script>
</body>
</html>

View file

@ -0,0 +1,4 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<style>html::before { content:"b"; }</style>
<input contenteditable="true" spellcheck="true"/>
</html>

View file

@ -0,0 +1,3 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<span style="mask: url(682684-binding.xml#a);"/>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html><body>
<div style="-moz-column-count: 2;"><fieldset style="transform-style: preserve-3d;"><fieldset style="white-space: pre-line; position: fixed;"><div style="position: fixed;">
</div></fieldset></fieldset></div>
</body></html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<input min="-10" max="10" step="0.1" type="range" value="-5"/>
<input max="50" min="10" step="2" type="range" value="7"/>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body onload="c.removeAttribute('type'); c.removeAttribute('value');">
<input id="c" type="color">
</body>
</html>

View file

@ -0,0 +1,5 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<input type="number"> </input>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html class="reftest-print">
<head>
<meta charset="utf-8">
<style type="text/css">
@page { size:5in 3in; margin:0in; }
div { height: 2.5in; }
select { height: 0.5in; display:block; padding:20px; page-break-inside:initial; }
</style>
</head>
<body>
<div></div>
<select>
<option>Text</option>
</select>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<style>
#d { overflow:scroll; width:200px; height:200px; background:yellow; }
#d2 { right:0; width:100px; height:100px; background:purple; }
</style>
<fieldset id="d">
<div id="d2"></div>
</fieldset>
<script>
d.getBoundingClientRect();
d.style.transform = "translateX(100px)";
d.getBoundingClientRect();
d2.style.position = "absolute";
</script>

View file

@ -0,0 +1,5 @@
<!DOCTYPE HTML>
<html class="reftest-print"><body><div style="position: fixed;">
<div style="page-break-after: always"></div>
<select style="display:flex; position: fixed;"><option>A</select>
</div></body></html>

View file

@ -0,0 +1,69 @@
load 166750-1.html
load 200347-1.html
load 203041-1.html
load 213390-1.html
load 258101-1.html
load 266225-1.html
load 310426-1.xhtml
load 310520-1.xhtml
load 315752-1.xhtml
load 317502-1.xhtml
load 321894.html
load 323499-1.html
load 343510-1.html
load 363696-1.xul
load 363696-2.html
load 363696-3.html
load 366205-1.html
load 366537-1.xhtml
load 367587-1.html
load 370703-1.html
load 370940-1.html
load 370967.html
load 373586-1.xhtml
load 375299.html
load 378369.html
asserts(4-10) load 378413-1.xhtml # bug 424225, bug 402850?
load 380116-1.xhtml
load 382212-1.xhtml
load 382610-1.html
load 383887-1.html
load 386554-1.html
load 388374-1.xhtml
load 388374-2.html
load 393656-1.xhtml
load 393656-2.xhtml
load 399262.html
load 402852-1.html
load 403148-1.html
load 404118-1.html
load 404123-1.html
load 407066.html
load 451316.html
load 455451-1.html
load 457537-1.html
load 457537-2.html
load 478219-1.xhtml
load 498698-1.html
load 513113-1.html
load 538062-1.xhtml
load 570624-1.html
asserts(1) load 578604-1.html # bug 584564
asserts(4-7) load 590302-1.xhtml # bug 584564
load 626014.xhtml
load 639733.xhtml
load 669767.html
load 682684.xhtml
load 865602.html
load 893332-1.html
load 944198.html
load 949891.xhtml
load 959311.html
load 960277-2.html
load 997709-1.html
load 1102791.html
load 1140216.html
load 1182414.html
load 1212688.html
load 1228670.xhtml
load 1279354.html

57
layout/forms/moz.build Normal file
View file

@ -0,0 +1,57 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'Layout: Form Controls')
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
EXPORTS += [
'nsIComboboxControlFrame.h',
'nsIFormControlFrame.h',
'nsIListControlFrame.h',
'nsISelectControlFrame.h',
'nsITextControlFrame.h',
]
UNIFIED_SOURCES += [
'nsButtonFrameRenderer.cpp',
'nsColorControlFrame.cpp',
'nsComboboxControlFrame.cpp',
'nsDateTimeControlFrame.cpp',
'nsFieldSetFrame.cpp',
'nsFileControlFrame.cpp',
'nsFormControlFrame.cpp',
'nsGfxButtonControlFrame.cpp',
'nsGfxCheckboxControlFrame.cpp',
'nsGfxRadioControlFrame.cpp',
'nsHTMLButtonControlFrame.cpp',
'nsImageControlFrame.cpp',
'nsLegendFrame.cpp',
'nsListControlFrame.cpp',
'nsMeterFrame.cpp',
'nsNumberControlFrame.cpp',
'nsProgressFrame.cpp',
'nsRangeFrame.cpp',
'nsSelectsAreaFrame.cpp',
'nsTextControlFrame.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../../editor/txmgr',
'../base',
'../generic',
'../style',
'../xul',
'/dom/base',
'/dom/html',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,516 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsButtonFrameRenderer.h"
#include "nsCSSRendering.h"
#include "nsPresContext.h"
#include "nsGkAtoms.h"
#include "nsCSSPseudoElements.h"
#include "nsNameSpaceManager.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "nsDisplayList.h"
#include "nsITheme.h"
#include "nsFrame.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/Element.h"
#define ACTIVE "active"
#define HOVER "hover"
#define FOCUS "focus"
using namespace mozilla;
using namespace mozilla::image;
nsButtonFrameRenderer::nsButtonFrameRenderer()
{
MOZ_COUNT_CTOR(nsButtonFrameRenderer);
}
nsButtonFrameRenderer::~nsButtonFrameRenderer()
{
MOZ_COUNT_DTOR(nsButtonFrameRenderer);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
}
void
nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
{
mFrame = aFrame;
ReResolveStyles(aPresContext);
}
nsIFrame*
nsButtonFrameRenderer::GetFrame()
{
return mFrame;
}
void
nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify)
{
if (aDisabled)
mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
notify);
else
mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify);
}
bool
nsButtonFrameRenderer::isDisabled()
{
return mFrame->GetContent()->AsElement()->
State().HasState(NS_EVENT_STATE_DISABLED);
}
class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
public:
nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonBoxShadowOuter() {
MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
}
#endif
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) override;
NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
private:
nsButtonFrameRenderer* mBFR;
};
nsRect
nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
*aSnap = false;
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
void
nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) {
nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsRect buttonRect;
mBFR->GetButtonRect(frameRect, buttonRect);
nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
buttonRect, mVisibleRect);
}
class nsDisplayButtonBorder : public nsDisplayItem {
public:
nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonBorder);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonBorder() {
MOZ_COUNT_DTOR(nsDisplayButtonBorder);
}
#endif
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) override {
aOutFrames->AppendElement(mFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) override;
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
private:
nsButtonFrameRenderer* mBFR;
};
nsDisplayItemGeometry*
nsDisplayButtonBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayButtonBorder::ComputeInvalidationRegion(
nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion)
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
NS_ASSERTION(mFrame, "No frame?");
nsPresContext* pc = mFrame->PresContext();
nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
// draw the border and background inside the focus and outline borders
DrawResult result =
mBFR->PaintBorder(aBuilder, pc, *aCtx, mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
nsRect
nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
*aSnap = false;
return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize())
: mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
class nsDisplayButtonForeground : public nsDisplayItem {
public:
nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonForeground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonForeground() {
MOZ_COUNT_DTOR(nsDisplayButtonForeground);
}
#endif
nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
private:
nsButtonFrameRenderer* mBFR;
};
nsDisplayItemGeometry*
nsDisplayButtonForeground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayButtonForeground::ComputeInvalidationRegion(
nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion)
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
nsPresContext *presContext = mFrame->PresContext();
const nsStyleDisplay *disp = mFrame->StyleDisplay();
if (!mFrame->IsThemed(disp) ||
!presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
// Draw the focus and outline borders.
DrawResult result =
mBFR->PaintOutlineAndFocusBorders(aBuilder, presContext, *aCtx,
mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
}
nsresult
nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
nsDisplayList* aBackground,
nsDisplayList* aForeground)
{
if (mFrame->StyleEffects()->mBoxShadow) {
aBackground->AppendNewToTop(new (aBuilder)
nsDisplayButtonBoxShadowOuter(aBuilder, this));
}
nsRect buttonRect;
GetButtonRect(mFrame->GetRectRelativeToSelf(), buttonRect);
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, mFrame, buttonRect, aBackground);
aBackground->AppendNewToTop(new (aBuilder)
nsDisplayButtonBorder(aBuilder, this));
// Only display focus rings if we actually have them. Since at most one
// button would normally display a focus ring, most buttons won't have them.
if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) ||
(mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) {
aForeground->AppendNewToTop(new (aBuilder)
nsDisplayButtonForeground(aBuilder, this));
}
return NS_OK;
}
DrawResult
nsButtonFrameRenderer::PaintOutlineAndFocusBorders(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// once we have all that we'll draw the focus if we have it. We will
// need to draw 2 focuses, the inner and the outer. This is so we
// can do any kind of look and feel. Some buttons have focus on the
// outside like mac and motif. While others like windows have it
// inside (dotted line). Usually only one will be specifed. But I
// guess you could have both if you wanted to.
nsRect rect;
PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
DrawResult result = DrawResult::SUCCESS;
if (mOuterFocusStyle) {
// ---------- paint the outer focus border -------------
GetButtonOuterFocusRect(aRect, rect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, rect, mOuterFocusStyle, flags);
}
if (mInnerFocusStyle) {
// ---------- paint the inner focus border -------------
GetButtonInnerFocusRect(aRect, rect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, rect, mInnerFocusStyle, flags);
}
return result;
}
DrawResult
nsButtonFrameRenderer::PaintBorder(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// get the button rect this is inside the focus and outline rects
nsRect buttonRect;
GetButtonRect(aRect, buttonRect);
nsStyleContext* context = mFrame->StyleContext();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
mFrame, buttonRect);
DrawResult result =
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, buttonRect, context, borderFlags);
return result;
}
void
nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
{
focusRect = aRect;
}
void
nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r)
{
r = aRect;
r.Deflate(GetButtonOuterFocusBorderAndPadding());
}
void
nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect)
{
GetButtonRect(aRect, focusRect);
focusRect.Deflate(GetButtonBorderAndPadding());
focusRect.Deflate(GetButtonInnerFocusMargin());
}
nsMargin
nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding()
{
nsMargin result(0,0,0,0);
if (mOuterFocusStyle) {
mOuterFocusStyle->StylePadding()->GetPadding(result);
result += mOuterFocusStyle->StyleBorder()->GetComputedBorder();
}
return result;
}
nsMargin
nsButtonFrameRenderer::GetButtonBorderAndPadding()
{
return mFrame->GetUsedBorderAndPadding();
}
/**
* Gets the size of the buttons border this is the union of the normal and disabled borders.
*/
nsMargin
nsButtonFrameRenderer::GetButtonInnerFocusMargin()
{
nsMargin innerFocusMargin(0,0,0,0);
if (mInnerFocusStyle) {
const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin();
margin->GetMargin(innerFocusMargin);
}
return innerFocusMargin;
}
nsMargin
nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding()
{
nsMargin result(0,0,0,0);
if (mInnerFocusStyle) {
mInnerFocusStyle->StylePadding()->GetPadding(result);
result += mInnerFocusStyle->StyleBorder()->GetComputedBorder();
}
return result;
}
// gets all the focus borders and padding that will be added to the regular border
nsMargin
nsButtonFrameRenderer::GetAddedButtonBorderAndPadding()
{
return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding();
}
/**
* Call this when styles change
*/
void
nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext)
{
// get all the styles
nsStyleContext* context = mFrame->StyleContext();
StyleSetHandle styleSet = aPresContext->StyleSet();
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
// style for the inner such as a dotted line (Windows)
mInnerFocusStyle =
styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
CSSPseudoElementType::mozFocusInner,
context);
// style for outer focus like a ridged border (MAC).
mOuterFocusStyle =
styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
CSSPseudoElementType::mozFocusOuter,
context);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameAddRef();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameAddRef();
}
#endif
}
nsStyleContext*
nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const
{
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
return mInnerFocusStyle;
case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
return mOuterFocusStyle;
default:
return nullptr;
}
}
void
nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext)
{
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
#endif
mInnerFocusStyle = aStyleContext;
break;
case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
#ifdef DEBUG
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
mOuterFocusStyle = aStyleContext;
break;
}
#ifdef DEBUG
aStyleContext->FrameAddRef();
#endif
}

View file

@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsButtonFrameRenderer_h___
#define nsButtonFrameRenderer_h___
#include "imgIContainer.h"
#include "nsMargin.h"
class nsIFrame;
class nsFrame;
class nsDisplayList;
class nsDisplayListBuilder;
class nsPresContext;
class nsRenderingContext;
struct nsRect;
class nsStyleContext;
#define NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX 0
#define NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX 1
#define NS_BUTTON_RENDERER_LAST_CONTEXT_INDEX NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX
class nsButtonFrameRenderer {
typedef mozilla::image::DrawResult DrawResult;
public:
nsButtonFrameRenderer();
~nsButtonFrameRenderer();
/**
* Create display list items for the button
*/
nsresult DisplayButton(nsDisplayListBuilder* aBuilder,
nsDisplayList* aBackground, nsDisplayList* aForeground);
DrawResult PaintOutlineAndFocusBorders(nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect);
DrawResult PaintBorder(nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect);
void SetFrame(nsFrame* aFrame, nsPresContext* aPresContext);
void SetDisabled(bool aDisabled, bool notify);
bool isActive();
bool isDisabled();
void GetButtonOuterFocusRect(const nsRect& aRect, nsRect& aResult);
void GetButtonRect(const nsRect& aRect, nsRect& aResult);
void GetButtonInnerFocusRect(const nsRect& aRect, nsRect& aResult);
nsMargin GetButtonOuterFocusBorderAndPadding();
nsMargin GetButtonBorderAndPadding();
nsMargin GetButtonInnerFocusMargin();
nsMargin GetButtonInnerFocusBorderAndPadding();
nsMargin GetAddedButtonBorderAndPadding();
nsStyleContext* GetStyleContext(int32_t aIndex) const;
void SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext);
void ReResolveStyles(nsPresContext* aPresContext);
nsIFrame* GetFrame();
protected:
private:
// cached styles for focus and outline.
RefPtr<nsStyleContext> mInnerFocusStyle;
RefPtr<nsStyleContext> mOuterFocusStyle;
nsFrame* mFrame;
};
#endif

View file

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsColorControlFrame.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentList.h"
#include "nsContentUtils.h"
#include "nsCSSPseudoElements.h"
#include "nsFormControlFrame.h"
#include "nsGkAtoms.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMNode.h"
#include "nsIFormControl.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "nsIDocument.h"
using mozilla::dom::Element;
nsColorControlFrame::nsColorControlFrame(nsStyleContext* aContext)
: nsHTMLButtonControlFrame(aContext)
{
}
nsIFrame*
NS_NewColorControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsColorControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsColorControlFrame)
NS_QUERYFRAME_HEAD(nsColorControlFrame)
NS_QUERYFRAME_ENTRY(nsColorControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame)
void nsColorControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
nsContentUtils::DestroyAnonymousContent(&mColorContent);
nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot);
}
nsIAtom*
nsColorControlFrame::GetType() const
{
return nsGkAtoms::colorControlFrame;
}
#ifdef DEBUG_FRAME_DUMP
nsresult
nsColorControlFrame::GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("ColorControl"), aResult);
}
#endif
// Create the color area for the button.
// The frame will be generated by the frame constructor.
nsresult
nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
mColorContent = doc->CreateHTMLElement(nsGkAtoms::div);
// Mark the element to be native anonymous before setting any attributes.
mColorContent->SetIsNativeAnonymousRoot();
nsresult rv = UpdateColor();
NS_ENSURE_SUCCESS(rv, rv);
CSSPseudoElementType pseudoType = CSSPseudoElementType::mozColorSwatch;
RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()->
ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
StyleContext(), mColorContent->AsElement());
if (!aElements.AppendElement(ContentInfo(mColorContent, newStyleContext))) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
void
nsColorControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter)
{
if (mColorContent) {
aElements.AppendElement(mColorContent);
}
}
nsresult
nsColorControlFrame::UpdateColor()
{
// Get the color from the "value" property of our content; it will return the
// default color (through the sanitization algorithm) if there is none.
nsAutoString color;
nsCOMPtr<nsIDOMHTMLInputElement> elt = do_QueryInterface(mContent);
elt->GetValue(color);
MOZ_ASSERT(!color.IsEmpty(),
"Content node's GetValue() should return a valid color string "
"(the default color, in case no valid color is set)");
// Set the background-color style property of the swatch element to this color
return mColorContent->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
NS_LITERAL_STRING("background-color:") + color, true);
}
nsresult
nsColorControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
NS_ASSERTION(mColorContent, "The color div must exist");
// If the value attribute is set, update the color box, but only if we're
// still a color control, which might not be the case if the type attribute
// was removed/changed.
nsCOMPtr<nsIFormControl> fctrl = do_QueryInterface(GetContent());
if (fctrl->GetType() == NS_FORM_INPUT_COLOR &&
aNameSpaceID == kNameSpaceID_None && nsGkAtoms::value == aAttribute) {
UpdateColor();
}
return nsHTMLButtonControlFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
nsContainerFrame*
nsColorControlFrame::GetContentInsertionFrame()
{
return this;
}
Element*
nsColorControlFrame::GetPseudoElement(CSSPseudoElementType aType)
{
if (aType == CSSPseudoElementType::mozColorSwatch) {
return mColorContent;
}
return nsContainerFrame::GetPseudoElement(aType);
}

View file

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsColorControlFrame_h___
#define nsColorControlFrame_h___
#include "nsCOMPtr.h"
#include "nsHTMLButtonControlFrame.h"
#include "nsIAnonymousContentCreator.h"
namespace mozilla {
enum class CSSPseudoElementType : uint8_t;
} // namespace mozilla
// Class which implements the input type=color
class nsColorControlFrame final : public nsHTMLButtonControlFrame,
public nsIAnonymousContentCreator
{
typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
typedef mozilla::dom::Element Element;
public:
friend nsIFrame* NS_NewColorControlFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
NS_DECL_QUERYFRAME_TARGET(nsColorControlFrame)
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
virtual nsIAtom* GetType() const override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
// nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
// nsIFrame
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
virtual bool IsLeaf() const override { return true; }
virtual nsContainerFrame* GetContentInsertionFrame() override;
virtual Element* GetPseudoElement(CSSPseudoElementType aType) override;
// Refresh the color swatch, using associated input's value
nsresult UpdateColor();
private:
explicit nsColorControlFrame(nsStyleContext* aContext);
nsCOMPtr<Element> mColorContent;
};
#endif // nsColorControlFrame_h___

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsComboboxControlFrame_h___
#define nsComboboxControlFrame_h___
#ifdef DEBUG_evaughan
//#define DEBUG_rods
#endif
#ifdef DEBUG_rods
//#define DO_REFLOW_DEBUG
//#define DO_REFLOW_COUNTER
//#define DO_UNCONSTRAINED_CHECK
//#define DO_PIXELS
//#define DO_NEW_REFLOW
#endif
//Mark used to indicate when onchange has been fired for current combobox item
#define NS_SKIP_NOTIFY_INDEX -2
#include "mozilla/Attributes.h"
#include "nsBlockFrame.h"
#include "nsIFormControlFrame.h"
#include "nsIComboboxControlFrame.h"
#include "nsIAnonymousContentCreator.h"
#include "nsISelectControlFrame.h"
#include "nsIRollupListener.h"
#include "nsIStatefulFrame.h"
#include "nsThreadUtils.h"
class nsStyleContext;
class nsIListControlFrame;
class nsComboboxDisplayFrame;
class nsIDOMEventListener;
class nsIScrollableFrame;
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
} // namespace mozilla
class nsComboboxControlFrame final : public nsBlockFrame,
public nsIFormControlFrame,
public nsIComboboxControlFrame,
public nsIAnonymousContentCreator,
public nsISelectControlFrame,
public nsIRollupListener,
public nsIStatefulFrame
{
typedef mozilla::gfx::DrawTarget DrawTarget;
public:
friend nsContainerFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext,
nsFrameState aFlags);
friend class nsComboboxDisplayFrame;
explicit nsComboboxControlFrame(nsStyleContext* aContext);
~nsComboboxControlFrame();
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
// nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
virtual nsIFrame* CreateFrameFor(nsIContent* aContent) override;
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
virtual void Reflow(nsPresContext* aCX,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);
// XXXbz this is only needed to prevent the quirk percent height stuff from
// leaking out of the combobox. We may be able to get rid of this as more
// things move to IsFrameOfType.
virtual nsIAtom* GetType() const override;
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsBlockFrame::IsFrameOfType(aFlags &
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
}
virtual nsIScrollableFrame* GetScrollTargetFrame() override {
return do_QueryFrame(mDropdownFrame);
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
virtual void SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) override;
virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
virtual nsContainerFrame* GetContentInsertionFrame() override;
// nsIFormControlFrame
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
/**
* Inform the control that it got (or lost) focus.
* If it lost focus, the dropdown menu will be rolled up if needed,
* and FireOnChange() will be called.
* @param aOn true if got focus, false if lost focus.
* @param aRepaint if true then force repaint (NOTE: we always force repaint currently)
* @note This method might destroy |this|.
*/
virtual void SetFocus(bool aOn, bool aRepaint) override;
//nsIComboboxControlFrame
virtual bool IsDroppedDown() override { return mDroppedDown; }
/**
* @note This method might destroy |this|.
*/
virtual void ShowDropDown(bool aDoDropDown) override;
virtual nsIFrame* GetDropDown() override;
virtual void SetDropDown(nsIFrame* aDropDownFrame) override;
/**
* @note This method might destroy |this|.
*/
virtual void RollupFromList() override;
/**
* Return the available space before and after this frame for
* placing the drop-down list, and the current 2D translation.
* Note that either or both can be less than or equal to zero,
* if both are then the drop-down should be closed.
*/
void GetAvailableDropdownSpace(mozilla::WritingMode aWM,
nscoord* aBefore,
nscoord* aAfter,
mozilla::LogicalPoint* aTranslation);
virtual int32_t GetIndexOfDisplayArea() override;
/**
* @note This method might destroy |this|.
*/
NS_IMETHOD RedisplaySelectedText() override;
virtual int32_t UpdateRecentIndex(int32_t aIndex) override;
virtual void OnContentReset() override;
bool IsOpenInParentProcess() override
{
return mIsOpenInParentProcess;
}
void SetOpenInParentProcess(bool aVal) override
{
mIsOpenInParentProcess = aVal;
}
// nsISelectControlFrame
NS_IMETHOD AddOption(int32_t index) override;
NS_IMETHOD RemoveOption(int32_t index) override;
NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
//nsIRollupListener
/**
* Hide the dropdown menu and stop capturing mouse events.
* @note This method might destroy |this|.
*/
virtual bool Rollup(uint32_t aCount, bool aFlush,
const nsIntPoint* pos, nsIContent** aLastRolledUp) override;
virtual void NotifyGeometryChange() override;
/**
* A combobox should roll up if a mousewheel event happens outside of
* the popup area.
*/
virtual bool ShouldRollupOnMouseWheelEvent() override
{ return true; }
virtual bool ShouldConsumeOnMouseWheelEvent() override
{ return false; }
/**
* A combobox should not roll up if activated by a mouse activate message
* (eg. X-mouse).
*/
virtual bool ShouldRollupOnMouseActivate() override
{ return false; }
virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) override
{ return 0; }
virtual nsIWidget* GetRollupWidget() override;
//nsIStatefulFrame
NS_IMETHOD SaveState(nsPresState** aState) override;
NS_IMETHOD RestoreState(nsPresState* aState) override;
NS_IMETHOD GenerateStateKey(nsIContent* aContent,
nsIDocument* aDocument,
nsACString& aKey) override;
static bool ToolkitHasNativePopup();
protected:
friend class RedisplayTextEvent;
friend class nsAsyncResize;
friend class nsResizeDropdownAtFinalPosition;
// Utilities
void ReflowDropdown(nsPresContext* aPresContext,
const ReflowInput& aReflowInput);
enum DropDownPositionState {
// can't show the dropdown at its current position
eDropDownPositionSuppressed,
// a resize reflow is pending, don't show it yet
eDropDownPositionPendingResize,
// the dropdown has its final size and position and can be displayed here
eDropDownPositionFinal
};
DropDownPositionState AbsolutelyPositionDropDown();
// Helper for GetMinISize/GetPrefISize
nscoord GetIntrinsicISize(nsRenderingContext* aRenderingContext,
nsLayoutUtils::IntrinsicISizeType aType);
class RedisplayTextEvent : public mozilla::Runnable {
public:
NS_DECL_NSIRUNNABLE
explicit RedisplayTextEvent(nsComboboxControlFrame *c) : mControlFrame(c) {}
void Revoke() { mControlFrame = nullptr; }
private:
nsComboboxControlFrame *mControlFrame;
};
/**
* Show or hide the dropdown list.
* @note This method might destroy |this|.
*/
void ShowPopup(bool aShowPopup);
/**
* Show or hide the dropdown list.
* @param aShowList true to show, false to hide the dropdown.
* @note This method might destroy |this|.
* @return false if this frame is destroyed, true if still alive.
*/
bool ShowList(bool aShowList);
void CheckFireOnChange();
void FireValueChangeEvent();
nsresult RedisplayText(int32_t aIndex);
void HandleRedisplayTextEvent();
void ActuallyDisplayText(bool aNotify);
private:
// If our total transform to the root frame of the root document is only a 2d
// translation then return that translation, otherwise returns (0,0).
nsPoint GetCSSTransformTranslation();
protected:
nsFrameList mPopupFrames; // additional named child list
nsCOMPtr<nsIContent> mDisplayContent; // Anonymous content used to display the current selection
nsCOMPtr<nsIContent> mButtonContent; // Anonymous content for the button
nsContainerFrame* mDisplayFrame; // frame to display selection
nsIFrame* mButtonFrame; // button frame
nsIFrame* mDropdownFrame; // dropdown list frame
nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame
// The inline size of our display area. Used by that frame's reflow
// to size to the full inline size except the drop-marker.
nscoord mDisplayISize;
nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
int32_t mRecentSelectedIndex;
int32_t mDisplayedIndex;
nsString mDisplayedOptionText;
// make someone to listen to the button. If its programmatically pressed by someone like Accessibility
// then open or close the combo box.
nsCOMPtr<nsIDOMEventListener> mButtonListener;
// The last y-positions used for estimating available space before and
// after for the dropdown list in GetAvailableDropdownSpace. These are
// reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the
// dropdown at its actual position. The GetAvailableDropdownSpace call
// from nsListControlFrame::ReflowAsDropdown use the last position.
nscoord mLastDropDownBeforeScreenBCoord;
nscoord mLastDropDownAfterScreenBCoord;
// Current state of the dropdown list, true is dropped down.
bool mDroppedDown;
// See comment in HandleRedisplayTextEvent().
bool mInRedisplayText;
// Acting on ShowDropDown(true) is delayed until we're focused.
bool mDelayedShowDropDown;
bool mIsOpenInParentProcess;
// static class data member for Bug 32920
// only one control can be focused at a time
static nsComboboxControlFrame* sFocused;
#ifdef DO_REFLOW_COUNTER
int32_t mReflowId;
#endif
};
#endif

View file

@ -0,0 +1,414 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/**
* This frame type is used for input type=date, time, month, week, and
* datetime-local.
*/
#include "nsDateTimeControlFrame.h"
#include "nsContentUtils.h"
#include "nsFormControlFrame.h"
#include "nsGkAtoms.h"
#include "nsContentUtils.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentList.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "nsNodeInfoManager.h"
#include "nsIDateTimeInputArea.h"
#include "nsIObserverService.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMMutationEvent.h"
#include "jsapi.h"
#include "nsJSUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
nsIFrame*
NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsDateTimeControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsDateTimeControlFrame)
NS_QUERYFRAME_HEAD(nsDateTimeControlFrame)
NS_QUERYFRAME_ENTRY(nsDateTimeControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
nsDateTimeControlFrame::nsDateTimeControlFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
{
}
void
nsDateTimeControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsContentUtils::DestroyAnonymousContent(&mInputAreaContent);
nsContainerFrame::DestroyFrom(aDestructRoot);
}
void
nsDateTimeControlFrame::UpdateInputBoxValue()
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->NotifyInputElementValueChanged();
}
}
void
nsDateTimeControlFrame::SetValueFromPicker(const DateTimeValue& aValue)
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
AutoJSAPI api;
if (!api.Init(mContent->OwnerDoc()->GetScopeObject())) {
return;
}
JSObject* wrapper = mContent->GetWrapper();
if (!wrapper) {
return;
}
JSObject* scope = xpc::GetXBLScope(api.cx(), wrapper);
AutoJSAPI jsapi;
if (!scope || !jsapi.Init(scope)) {
return;
}
JS::Rooted<JS::Value> jsValue(jsapi.cx());
if (!ToJSValue(jsapi.cx(), aValue, &jsValue)) {
return;
}
inputAreaContent->SetValueFromPicker(jsValue);
}
}
void
nsDateTimeControlFrame::SetPickerState(bool aOpen)
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->SetPickerState(aOpen);
}
}
void
nsDateTimeControlFrame::HandleFocusEvent()
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->FocusInnerTextBox();
}
}
void
nsDateTimeControlFrame::HandleBlurEvent()
{
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
inputAreaContent->BlurInnerTextBox();
}
}
nscoord
nsDateTimeControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
{
nscoord result;
DISPLAY_MIN_WIDTH(this, result);
nsIFrame* kid = mFrames.FirstChild();
if (kid) { // display:none?
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
kid,
nsLayoutUtils::MIN_ISIZE);
} else {
result = 0;
}
return result;
}
nscoord
nsDateTimeControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
{
nscoord result;
DISPLAY_PREF_WIDTH(this, result);
nsIFrame* kid = mFrames.FirstChild();
if (kid) { // display:none?
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
kid,
nsLayoutUtils::PREF_ISIZE);
} else {
result = 0;
}
return result;
}
void
nsDateTimeControlFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsDateTimeControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsDateTimeControlFrame::Reflow: availSize=%d,%d",
aReflowInput.AvailableWidth(),
aReflowInput.AvailableHeight()));
NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
const WritingMode myWM = aReflowInput.GetWritingMode();
// The ISize of our content box, which is the available ISize
// for our anonymous content:
const nscoord contentBoxISize = aReflowInput.ComputedISize();
nscoord contentBoxBSize = aReflowInput.ComputedBSize();
// Figure out our border-box sizes as well (by adding borderPadding to
// content-box sizes):
const nscoord borderBoxISize = contentBoxISize +
aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
nscoord borderBoxBSize;
if (contentBoxBSize != NS_INTRINSICSIZE) {
borderBoxBSize = contentBoxBSize +
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
} // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.
nsIFrame* inputAreaFrame = mFrames.FirstChild();
if (!inputAreaFrame) { // display:none?
if (contentBoxBSize == NS_INTRINSICSIZE) {
contentBoxBSize = 0;
borderBoxBSize =
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
}
} else {
NS_ASSERTION(inputAreaFrame->GetContent() == mInputAreaContent,
"What is this child doing here?");
ReflowOutput childDesiredSize(aReflowInput);
WritingMode wm = inputAreaFrame->GetWritingMode();
LogicalSize availSize = aReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
ReflowInput childReflowOuput(aPresContext, aReflowInput,
inputAreaFrame, availSize);
// Convert input area margin into my own writing-mode (in case it differs):
LogicalMargin childMargin =
childReflowOuput.ComputedLogicalMargin().ConvertTo(myWM, wm);
// offsets of input area frame within this frame:
LogicalPoint
childOffset(myWM,
aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
childMargin.IStart(myWM),
aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
childMargin.BStart(myWM));
nsReflowStatus childStatus;
// We initially reflow the child with a dummy containerSize; positioning
// will be fixed later.
const nsSize dummyContainerSize;
ReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
childReflowOuput, myWM, childOffset, dummyContainerSize, 0,
childStatus);
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
"We gave our child unconstrained available block-size, "
"so it should be complete");
nscoord childMarginBoxBSize =
childDesiredSize.BSize(myWM) + childMargin.BStartEnd(myWM);
if (contentBoxBSize == NS_INTRINSICSIZE) {
// We are intrinsically sized -- we should shrinkwrap the input area's
// block-size:
contentBoxBSize = childMarginBoxBSize;
// Make sure we obey min/max-bsize in the case when we're doing intrinsic
// sizing (we get it for free when we have a non-intrinsic
// aReflowInput.ComputedBSize()). Note that we do this before
// adjusting for borderpadding, since ComputedMaxBSize and
// ComputedMinBSize are content heights.
contentBoxBSize =
NS_CSS_MINMAX(contentBoxBSize,
aReflowInput.ComputedMinBSize(),
aReflowInput.ComputedMaxBSize());
borderBoxBSize = contentBoxBSize +
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
}
// Center child in block axis
nscoord extraSpace = contentBoxBSize - childMarginBoxBSize;
childOffset.B(myWM) += std::max(0, extraSpace / 2);
// Needed in FinishReflowChild, for logical-to-physical conversion:
nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
GetPhysicalSize(myWM);
// Place the child
FinishReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
&childReflowOuput, myWM, childOffset, borderBoxSize, 0);
nsSize contentBoxSize =
LogicalSize(myWM, contentBoxISize, contentBoxBSize).
GetPhysicalSize(myWM);
aDesiredSize.SetBlockStartAscent(
childDesiredSize.BlockStartAscent() +
inputAreaFrame->BStart(aReflowInput.GetWritingMode(),
contentBoxSize));
}
LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
aDesiredSize.SetSize(myWM, logicalDesiredSize);
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (inputAreaFrame) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inputAreaFrame);
}
FinishAndStoreOverflow(&aDesiredSize);
aStatus = NS_FRAME_COMPLETE;
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("exit nsDateTimeControlFrame::Reflow: size=%d,%d",
aDesiredSize.Width(), aDesiredSize.Height()));
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
nsresult
nsDateTimeControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
// Set up "datetimebox" XUL element which will be XBL-bound to the
// actual controls.
nsNodeInfoManager* nodeInfoManager =
mContent->GetComposedDoc()->NodeInfoManager();
RefPtr<NodeInfo> nodeInfo =
nodeInfoManager->GetNodeInfo(nsGkAtoms::datetimebox, nullptr,
kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
NS_TrustedNewXULElement(getter_AddRefs(mInputAreaContent), nodeInfo.forget());
aElements.AppendElement(mInputAreaContent);
// Propogate our tabindex.
nsAutoString tabIndexStr;
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex,
tabIndexStr, false);
}
// Propagate our readonly state.
nsAutoString readonly;
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly,
false);
}
SyncDisabledState();
return NS_OK;
}
void
nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter)
{
if (mInputAreaContent) {
aElements.AppendElement(mInputAreaContent);
}
}
void
nsDateTimeControlFrame::SyncDisabledState()
{
EventStates eventStates = mContent->AsElement()->State();
if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
EmptyString(), true);
} else {
mInputAreaContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
}
}
nsresult
nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
// nsGkAtoms::disabled is handled by SyncDisabledState
if (aNameSpaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::value ||
aAttribute == nsGkAtoms::readonly ||
aAttribute == nsGkAtoms::tabindex) {
MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
// If script changed the <input>'s type before setting these attributes
// then we don't need to do anything since we are going to be reframed.
if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME) {
if (aAttribute == nsGkAtoms::value) {
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
do_QueryInterface(mInputAreaContent);
if (inputAreaContent) {
nsContentUtils::AddScriptRunner(NewRunnableMethod(inputAreaContent,
&nsIDateTimeInputArea::NotifyInputElementValueChanged));
}
} else {
if (aModType == nsIDOMMutationEvent::REMOVAL) {
mInputAreaContent->UnsetAttr(aNameSpaceID, aAttribute, true);
} else {
MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
aModType == nsIDOMMutationEvent::MODIFICATION);
nsAutoString value;
mContent->GetAttr(aNameSpaceID, aAttribute, value);
mInputAreaContent->SetAttr(aNameSpaceID, aAttribute, value, true);
}
}
}
}
}
return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
void
nsDateTimeControlFrame::ContentStatesChanged(EventStates aStates)
{
if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
}
}
nsIAtom*
nsDateTimeControlFrame::GetType() const
{
return nsGkAtoms::dateTimeControlFrame;
}

View file

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/**
* This frame type is used for input type=date, time, month, week, and
* datetime-local.
*
* NOTE: some of the above-mentioned input types are still to-be-implemented.
* See nsCSSFrameConstructor::FindInputData, as well as bug 1286182 (date),
* bug 1306215 (month), bug 1306216 (week) and bug 1306217 (datetime-local).
*/
#ifndef nsDateTimeControlFrame_h__
#define nsDateTimeControlFrame_h__
#include "mozilla/Attributes.h"
#include "nsContainerFrame.h"
#include "nsIAnonymousContentCreator.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
struct DateTimeValue;
} // namespace dom
} // namespace mozilla
class nsDateTimeControlFrame final : public nsContainerFrame,
public nsIAnonymousContentCreator
{
typedef mozilla::dom::DateTimeValue DateTimeValue;
explicit nsDateTimeControlFrame(nsStyleContext* aContext);
public:
friend nsIFrame* NS_NewDateTimeControlFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
void ContentStatesChanged(mozilla::EventStates aStates) override;
void DestroyFrom(nsIFrame* aDestructRoot) override;
NS_DECL_QUERYFRAME_TARGET(nsDateTimeControlFrame)
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
#ifdef DEBUG_FRAME_DUMP
nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("DateTimeControl"), aResult);
}
#endif
nsIAtom* GetType() const override;
bool IsFrameOfType(uint32_t aFlags) const override
{
return nsContainerFrame::IsFrameOfType(aFlags &
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
}
// Reflow
nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
void Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowState,
nsReflowStatus& aStatus) override;
// nsIAnonymousContentCreator
nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute,
int32_t aModType) override;
void UpdateInputBoxValue();
void SetValueFromPicker(const DateTimeValue& aValue);
void HandleFocusEvent();
void HandleBlurEvent();
void SetPickerState(bool aOpen);
private:
class SyncDisabledStateEvent;
friend class SyncDisabledStateEvent;
class SyncDisabledStateEvent : public mozilla::Runnable
{
public:
explicit SyncDisabledStateEvent(nsDateTimeControlFrame* aFrame)
: mFrame(aFrame)
{}
NS_IMETHOD Run() override
{
nsDateTimeControlFrame* frame =
static_cast<nsDateTimeControlFrame*>(mFrame.GetFrame());
NS_ENSURE_STATE(frame);
frame->SyncDisabledState();
return NS_OK;
}
private:
nsWeakFrame mFrame;
};
/**
* Sync the disabled state of the anonymous children up with our content's.
*/
void SyncDisabledState();
// Anonymous child which is bound via XBL to an element that wraps the input
// area and reset button.
nsCOMPtr<nsIContent> mInputAreaContent;
};
#endif // nsDateTimeControlFrame_h__

View file

@ -0,0 +1,703 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsFieldSetFrame.h"
#include "mozilla/gfx/2D.h"
#include "nsCSSAnonBoxes.h"
#include "nsLayoutUtils.h"
#include "nsLegendFrame.h"
#include "nsCSSRendering.h"
#include <algorithm>
#include "nsIFrame.h"
#include "nsPresContext.h"
#include "mozilla/RestyleManager.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsDisplayList.h"
#include "nsRenderingContext.h"
#include "nsIScrollableFrame.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::layout;
nsContainerFrame*
NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsFieldSetFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame)
nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
, mLegendRect(GetWritingMode())
{
mLegendSpace = 0;
}
nsIAtom*
nsFieldSetFrame::GetType() const
{
return nsGkAtoms::fieldSetFrame;
}
nsRect
nsFieldSetFrame::VisualBorderRectRelativeToSelf() const
{
WritingMode wm = GetWritingMode();
css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide);
LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
nsSize containerSize = r.Size(wm).GetPhysicalSize(wm);
if (legendBorder < mLegendRect.BSize(wm)) {
nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2;
r.BStart(wm) += off;
r.BSize(wm) -= off;
}
return r.GetPhysicalRect(wm, containerSize);
}
nsIFrame*
nsFieldSetFrame::GetInner() const
{
nsIFrame* last = mFrames.LastChild();
if (last &&
last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) {
return last;
}
MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
return nullptr;
}
nsIFrame*
nsFieldSetFrame::GetLegend() const
{
if (mFrames.FirstChild() == GetInner()) {
MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
return nullptr;
}
MOZ_ASSERT(mFrames.FirstChild() &&
mFrames.FirstChild()->GetContentInsertionFrame()->GetType() ==
nsGkAtoms::legendFrame);
return mFrames.FirstChild();
}
class nsDisplayFieldSetBorderBackground : public nsDisplayItem {
public:
nsDisplayFieldSetBorderBackground(nsDisplayListBuilder* aBuilder,
nsFieldSetFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame) {
MOZ_COUNT_CTOR(nsDisplayFieldSetBorderBackground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayFieldSetBorderBackground() {
MOZ_COUNT_DTOR(nsDisplayFieldSetBorderBackground);
}
#endif
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) override;
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
NS_DISPLAY_DECL_NAME("FieldSetBorderBackground", TYPE_FIELDSET_BORDER_BACKGROUND)
};
void nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
{
// aPt is guaranteed to be in this item's bounds. We do the hit test based on the
// frame bounds even though our background doesn't cover the whole frame.
// It's not clear whether this is correct.
aOutFrames->AppendElement(mFrame);
}
void
nsDisplayFieldSetBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
DrawResult result = static_cast<nsFieldSetFrame*>(mFrame)->
PaintBorder(aBuilder, *aCtx, ToReferenceFrame(), mVisibleRect);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
nsDisplayItemGeometry*
nsDisplayFieldSetBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayFieldSetBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion)
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void
nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) {
// Paint our background and border in a special way.
// REVIEW: We don't really need to check frame emptiness here; if it's empty,
// the background/border display item won't do anything, and if it isn't empty,
// we need to paint the outline
if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
IsVisibleForPainting(aBuilder)) {
if (StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayBoxShadowOuter(aBuilder, this));
}
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, this, VisualBorderRectRelativeToSelf(),
aLists.BorderBackground(),
/* aAllowWillPaintBorderOptimization = */ false);
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayFieldSetBorderBackground(aBuilder, this));
DisplayOutlineUnconditional(aBuilder, aLists);
DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame");
}
if (GetPrevInFlow()) {
DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
}
nsDisplayListCollection contentDisplayItems;
if (nsIFrame* inner = GetInner()) {
// Collect the inner frame's display items into their own collection.
// We need to be calling BuildDisplayList on it before the legend in
// case it contains out-of-flow frames whose placeholders are in the
// legend. However, we want the inner frame's display items to be
// after the legend's display items in z-order, so we need to save them
// and append them later.
BuildDisplayListForChild(aBuilder, inner, aDirtyRect, contentDisplayItems);
}
if (nsIFrame* legend = GetLegend()) {
// The legend's background goes on our BlockBorderBackgrounds list because
// it's a block child.
nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
BuildDisplayListForChild(aBuilder, legend, aDirtyRect, set);
}
// Put the inner frame's display items on the master list. Note that this
// moves its border/background display items to our BorderBackground() list,
// which isn't really correct, but it's OK because the inner frame is
// anonymous and can't have its own border and background.
contentDisplayItems.MoveTo(aLists);
}
DrawResult
nsFieldSetFrame::PaintBorder(
nsDisplayListBuilder* aBuilder,
nsRenderingContext& aRenderingContext,
nsPoint aPt,
const nsRect& aDirtyRect)
{
// if the border is smaller than the legend. Move the border down
// to be centered on the legend.
// FIXME: This means border-radius clamping is incorrect; we should
// override nsIFrame::GetBorderRadii.
WritingMode wm = GetWritingMode();
nsRect rect = VisualBorderRectRelativeToSelf();
nscoord off = wm.IsVertical() ? rect.x : rect.y;
rect += aPt;
nsPresContext* presContext = PresContext();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
DrawResult result = DrawResult::SUCCESS;
nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
this, rect);
if (nsIFrame* legend = GetLegend()) {
css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
nscoord legendBorderWidth =
StyleBorder()->GetComputedBorderWidth(legendSide);
// Use the rect of the legend frame, not mLegendRect, so we draw our
// border under the legend's inline-start and -end margins.
LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size());
// Compute clipRect using logical coordinates, so that the legend space
// will be clipped out of the appropriate physical side depending on mode.
LogicalRect clipRect = LogicalRect(wm, rect, rect.Size());
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
gfxContext* gfx = aRenderingContext.ThebesContext();
int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
// draw inline-start portion of the block-start side of the border
clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);
gfx->Restore();
// draw inline-end portion of the block-start side of the border
clipRect = LogicalRect(wm, rect, rect.Size());
clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm);
clipRect.IStart(wm) = legendRect.IEnd(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);
gfx->Restore();
// draw remainder of the border (omitting the block-start side)
clipRect = LogicalRect(wm, rect, rect.Size());
clipRect.BStart(wm) += legendBorderWidth;
clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth);
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);
gfx->Restore();
} else {
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, nsRect(aPt, mRect.Size()),
mStyleContext, borderFlags);
}
return result;
}
nscoord
nsFieldSetFrame::GetIntrinsicISize(nsRenderingContext* aRenderingContext,
nsLayoutUtils::IntrinsicISizeType aType)
{
nscoord legendWidth = 0;
nscoord contentWidth = 0;
if (nsIFrame* legend = GetLegend()) {
legendWidth =
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, legend, aType);
}
if (nsIFrame* inner = GetInner()) {
// Ignore padding on the inner, since the padding will be applied to the
// outer instead, and the padding computed for the inner is wrong
// for percentage padding.
contentWidth =
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType,
nsLayoutUtils::IGNORE_PADDING);
}
return std::max(legendWidth, contentWidth);
}
nscoord
nsFieldSetFrame::GetMinISize(nsRenderingContext* aRenderingContext)
{
nscoord result = 0;
DISPLAY_MIN_WIDTH(this, result);
result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
return result;
}
nscoord
nsFieldSetFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
{
nscoord result = 0;
DISPLAY_PREF_WIDTH(this, result);
result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
return result;
}
/* virtual */
LogicalSize
nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext,
WritingMode aWM,
const LogicalSize& aCBSize,
nscoord aAvailableISize,
const LogicalSize& aMargin,
const LogicalSize& aBorder,
const LogicalSize& aPadding,
ComputeSizeFlags aFlags)
{
LogicalSize result =
nsContainerFrame::ComputeSize(aRenderingContext, aWM,
aCBSize, aAvailableISize,
aMargin, aBorder, aPadding, aFlags);
// XXX The code below doesn't make sense if the caller's writing mode
// is orthogonal to this frame's. Not sure yet what should happen then;
// for now, just bail out.
if (aWM.IsVertical() != GetWritingMode().IsVertical()) {
return result;
}
// Fieldsets never shrink below their min width.
// If we're a container for font size inflation, then shrink
// wrapping inside of us should not apply font size inflation.
AutoMaybeDisableFontInflation an(this);
nscoord minISize = GetMinISize(aRenderingContext);
if (minISize > result.ISize(aWM)) {
result.ISize(aWM) = minISize;
}
return result;
}
void
nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
NS_PRECONDITION(aReflowInput.ComputedISize() != NS_INTRINSICSIZE,
"Should have a precomputed inline-size!");
// Initialize OUT parameter
aStatus = NS_FRAME_COMPLETE;
nsOverflowAreas ocBounds;
nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
if (GetPrevInFlow()) {
ReflowOverflowContainerChildren(aPresContext, aReflowInput, ocBounds, 0,
ocStatus);
}
//------------ Handle Incremental Reflow -----------------
bool reflowInner;
bool reflowLegend;
nsIFrame* legend = GetLegend();
nsIFrame* inner = GetInner();
if (aReflowInput.ShouldReflowAllKids()) {
reflowInner = inner != nullptr;
reflowLegend = legend != nullptr;
} else {
reflowInner = inner && NS_SUBTREE_DIRTY(inner);
reflowLegend = legend && NS_SUBTREE_DIRTY(legend);
}
// We don't allow fieldsets to break vertically. If we did, we'd
// need logic here to push and pull overflow frames.
// Since we're not applying our padding in this frame, we need to add it here
// to compute the available width for our children.
WritingMode wm = GetWritingMode();
WritingMode innerWM = inner ? inner->GetWritingMode() : wm;
WritingMode legendWM = legend ? legend->GetWritingMode() : wm;
LogicalSize innerAvailSize = aReflowInput.ComputedSizeWithPadding(innerWM);
LogicalSize legendAvailSize = aReflowInput.ComputedSizeWithPadding(legendWM);
innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
NS_UNCONSTRAINEDSIZE;
NS_ASSERTION(!inner ||
nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
inner,
nsLayoutUtils::MIN_ISIZE) <=
innerAvailSize.ISize(innerWM),
"Bogus availSize.ISize; should be bigger");
NS_ASSERTION(!legend ||
nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
legend,
nsLayoutUtils::MIN_ISIZE) <=
legendAvailSize.ISize(legendWM),
"Bogus availSize.ISize; should be bigger");
// get our border and padding
LogicalMargin border = aReflowInput.ComputedLogicalBorderPadding() -
aReflowInput.ComputedLogicalPadding();
// Figure out how big the legend is if there is one.
// get the legend's margin
LogicalMargin legendMargin(wm);
// reflow the legend only if needed
Maybe<ReflowInput> legendReflowInput;
if (legend) {
legendReflowInput.emplace(aPresContext, aReflowInput, legend,
legendAvailSize);
}
if (reflowLegend) {
ReflowOutput legendDesiredSize(aReflowInput);
// We'll move the legend to its proper place later, so the position
// and containerSize passed here are unimportant.
const nsSize dummyContainerSize;
ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowInput,
wm, LogicalPoint(wm), dummyContainerSize,
NS_FRAME_NO_MOVE_FRAME, aStatus);
#ifdef NOISY_REFLOW
printf(" returned (%d, %d)\n",
legendDesiredSize.Width(), legendDesiredSize.Height());
#endif
// figure out the legend's rectangle
legendMargin = legend->GetLogicalUsedMargin(wm);
mLegendRect =
LogicalRect(wm, 0, 0,
legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm),
legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm));
nscoord oldSpace = mLegendSpace;
mLegendSpace = 0;
if (mLegendRect.BSize(wm) > border.BStart(wm)) {
// center the border on the legend
mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm);
} else {
mLegendRect.BStart(wm) =
(border.BStart(wm) - mLegendRect.BSize(wm)) / 2;
}
// if the legend space changes then we need to reflow the
// content area as well.
if (mLegendSpace != oldSpace && inner) {
reflowInner = true;
}
FinishReflowChild(legend, aPresContext, legendDesiredSize,
legendReflowInput.ptr(), wm, LogicalPoint(wm),
dummyContainerSize, NS_FRAME_NO_MOVE_FRAME);
} else if (!legend) {
mLegendRect.SetEmpty();
mLegendSpace = 0;
} else {
// mLegendSpace and mLegendRect haven't changed, but we need
// the used margin when placing the legend.
legendMargin = legend->GetLogicalUsedMargin(wm);
}
// This containerSize is incomplete as yet: it does not include the size
// of the |inner| frame itself.
nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) +
border.Size(wm)).GetPhysicalSize(wm);
// reflow the content frame only if needed
if (reflowInner) {
ReflowInput kidReflowInput(aPresContext, aReflowInput, inner,
innerAvailSize, nullptr,
ReflowInput::CALLER_WILL_INIT);
// Override computed padding, in case it's percentage padding
kidReflowInput.Init(aPresContext, nullptr, nullptr,
&aReflowInput.ComputedPhysicalPadding());
// Our child is "height:100%" but we actually want its height to be reduced
// by the amount of content-height the legend is eating up, unless our
// height is unconstrained (in which case the child's will be too).
if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
kidReflowInput.SetComputedBSize(
std::max(0, aReflowInput.ComputedBSize() - mLegendSpace));
}
if (aReflowInput.ComputedMinBSize() > 0) {
kidReflowInput.ComputedMinBSize() =
std::max(0, aReflowInput.ComputedMinBSize() - mLegendSpace);
}
if (aReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
kidReflowInput.ComputedMaxBSize() =
std::max(0, aReflowInput.ComputedMaxBSize() - mLegendSpace);
}
ReflowOutput kidDesiredSize(kidReflowInput,
aDesiredSize.mFlags);
// Reflow the frame
NS_ASSERTION(kidReflowInput.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
"Margins on anonymous fieldset child not supported!");
LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace);
// We don't know the correct containerSize until we have reflowed |inner|,
// so we use a dummy value for now; FinishReflowChild will fix the position
// if necessary.
const nsSize dummyContainerSize;
ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowInput,
wm, pt, dummyContainerSize, 0, aStatus);
// Update containerSize to account for size of the inner frame, so that
// FinishReflowChild can position it correctly.
containerSize += kidDesiredSize.PhysicalSize();
FinishReflowChild(inner, aPresContext, kidDesiredSize,
&kidReflowInput, wm, pt, containerSize, 0);
NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
} else if (inner) {
// |inner| didn't need to be reflowed but we do need to include its size
// in containerSize.
containerSize += inner->GetSize();
}
LogicalRect contentRect(wm);
if (inner) {
// We don't support margins on inner, so our content rect is just the
// inner's border-box. (We don't really care about container size at this
// point, as we'll figure out the actual positioning later.)
contentRect = inner->GetLogicalRect(wm, containerSize);
}
// Our content rect must fill up the available width
LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm);
if (availSize.ISize(wm) > contentRect.ISize(wm)) {
contentRect.ISize(wm) = innerAvailSize.ISize(wm);
}
if (legend) {
// The legend is positioned inline-wards within the inner's content rect
// (so that padding on the fieldset affects the legend position).
LogicalRect innerContentRect = contentRect;
innerContentRect.Deflate(wm, aReflowInput.ComputedLogicalPadding());
// If the inner content rect is larger than the legend, we can align the
// legend.
if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
// NOTE legend @align values are: left/right/center/top/bottom.
// GetLogicalAlign converts left/right to start/end for the given WM.
// @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign
int32_t align = static_cast<nsLegendFrame*>
(legend->GetContentInsertionFrame())->GetLogicalAlign(wm);
switch (align) {
case NS_STYLE_TEXT_ALIGN_END:
mLegendRect.IStart(wm) =
innerContentRect.IEnd(wm) - mLegendRect.ISize(wm);
break;
case NS_STYLE_TEXT_ALIGN_CENTER:
// Note: rounding removed; there doesn't seem to be any need
mLegendRect.IStart(wm) = innerContentRect.IStart(wm) +
(innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2;
break;
case NS_STYLE_TEXT_ALIGN_START:
case NS_STYLE_VERTICAL_ALIGN_TOP:
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
break;
default:
MOZ_ASSERT_UNREACHABLE("unexpected GetLogicalAlign value");
}
} else {
// otherwise make place for the legend
mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
innerContentRect.ISize(wm) = mLegendRect.ISize(wm);
contentRect.ISize(wm) = mLegendRect.ISize(wm) +
aReflowInput.ComputedLogicalPadding().IStartEnd(wm);
}
// place the legend
LogicalRect actualLegendRect = mLegendRect;
actualLegendRect.Deflate(wm, legendMargin);
LogicalPoint actualLegendPos(actualLegendRect.Origin(wm));
// Note that legend's writing mode may be different from the fieldset's,
// so we need to convert offsets before applying them to it (bug 1134534).
LogicalMargin offsets =
legendReflowInput->ComputedLogicalOffsets().
ConvertTo(wm, legendReflowInput->GetWritingMode());
ReflowInput::ApplyRelativePositioning(legend, wm, offsets,
&actualLegendPos,
containerSize);
legend->SetPosition(wm, actualLegendPos, containerSize);
nsContainerFrame::PositionFrameView(legend);
nsContainerFrame::PositionChildViews(legend);
}
// Return our size and our result.
LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm),
mLegendSpace + border.BStartEnd(wm) +
(inner ? inner->BSize(wm) : 0));
aDesiredSize.SetSize(wm, finalSize);
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (legend) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend);
}
if (inner) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner);
}
// Merge overflow container bounds and status.
aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
NS_MergeReflowStatusInto(&aStatus, ocStatus);
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
InvalidateFrame();
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
#ifdef DEBUG
void
nsFieldSetFrame::SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList)
{
nsContainerFrame::SetInitialChildList(aListID, aChildList);
MOZ_ASSERT(aListID != kPrincipalList || GetInner(),
"Setting principal child list should populate our inner frame");
}
void
nsFieldSetFrame::AppendFrames(ChildListID aListID,
nsFrameList& aFrameList)
{
MOZ_CRASH("nsFieldSetFrame::AppendFrames not supported");
}
void
nsFieldSetFrame::InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList)
{
MOZ_CRASH("nsFieldSetFrame::InsertFrames not supported");
}
void
nsFieldSetFrame::RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame)
{
MOZ_CRASH("nsFieldSetFrame::RemoveFrame not supported");
}
#endif
#ifdef ACCESSIBILITY
a11y::AccType
nsFieldSetFrame::AccessibleType()
{
return a11y::eHTMLGroupboxType;
}
#endif
nscoord
nsFieldSetFrame::GetLogicalBaseline(WritingMode aWritingMode) const
{
nsIFrame* inner = GetInner();
return inner->BStart(aWritingMode, GetParent()->GetSize()) +
inner->GetLogicalBaseline(aWritingMode);
}

View file

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsFieldSetFrame_h___
#define nsFieldSetFrame_h___
#include "mozilla/Attributes.h"
#include "imgIContainer.h"
#include "nsContainerFrame.h"
class nsFieldSetFrame final : public nsContainerFrame
{
typedef mozilla::image::DrawResult DrawResult;
public:
NS_DECL_FRAMEARENA_HELPERS
explicit nsFieldSetFrame(nsStyleContext* aContext);
nscoord
GetIntrinsicISize(nsRenderingContext* aRenderingContext,
nsLayoutUtils::IntrinsicISizeType);
virtual nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
virtual nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
virtual mozilla::LogicalSize
ComputeSize(nsRenderingContext *aRenderingContext,
mozilla::WritingMode aWritingMode,
const mozilla::LogicalSize& aCBSize,
nscoord aAvailableISize,
const mozilla::LogicalSize& aMargin,
const mozilla::LogicalSize& aBorder,
const mozilla::LogicalSize& aPadding,
ComputeSizeFlags aFlags) override;
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
/**
* The area to paint box-shadows around. It's the border rect except
* when there's a <legend> we offset the y-position to the center of it.
*/
virtual nsRect VisualBorderRectRelativeToSelf() const override;
virtual void Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
DrawResult PaintBorder(nsDisplayListBuilder* aBuilder,
nsRenderingContext& aRenderingContext,
nsPoint aPt, const nsRect& aDirtyRect);
#ifdef DEBUG
virtual void SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) override;
virtual void AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) override;
virtual void InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList) override;
virtual void RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame) override;
#endif
virtual nsIAtom* GetType() const override;
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsContainerFrame::IsFrameOfType(aFlags &
~nsIFrame::eCanContainOverflowContainers);
}
virtual nsIScrollableFrame* GetScrollTargetFrame() override
{
return do_QueryFrame(GetInner());
}
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("FieldSet"), aResult);
}
#endif
/**
* Return the anonymous frame that contains all descendants except
* the legend frame. This is currently always a block frame with
* pseudo nsCSSAnonBoxes::fieldsetContent -- this may change in the
* future when we add support for CSS overflow for <fieldset>.
*/
nsIFrame* GetInner() const;
/**
* Return the frame that represents the legend if any. This may be
* a nsLegendFrame or a nsHTMLScrollFrame with the nsLegendFrame as the
* scrolled frame (aka content insertion frame).
*/
nsIFrame* GetLegend() const;
protected:
mozilla::LogicalRect mLegendRect;
nscoord mLegendSpace;
};
#endif // nsFieldSetFrame_h___

View file

@ -0,0 +1,506 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsFileControlFrame.h"
#include "nsGkAtoms.h"
#include "nsCOMPtr.h"
#include "nsIDocument.h"
#include "mozilla/dom/NodeInfo.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/HTMLButtonElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/Preferences.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileList.h"
#include "nsIDOMDragEvent.h"
#include "nsIDOMFileList.h"
#include "nsContentList.h"
#include "nsIDOMMutationEvent.h"
#include "nsTextNode.h"
using namespace mozilla;
using namespace mozilla::dom;
nsIFrame*
NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsFileControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext)
: nsBlockFrame(aContext)
{
AddStateBits(NS_BLOCK_FLOAT_MGR);
}
void
nsFileControlFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
mMouseListener = new DnDListener(this);
}
void
nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
ENSURE_TRUE(mContent);
// Remove the events.
if (mContent) {
mContent->RemoveSystemEventListener(NS_LITERAL_STRING("drop"),
mMouseListener, false);
mContent->RemoveSystemEventListener(NS_LITERAL_STRING("dragover"),
mMouseListener, false);
}
nsContentUtils::DestroyAnonymousContent(&mTextContent);
nsContentUtils::DestroyAnonymousContent(&mBrowseFilesOrDirs);
mMouseListener->ForgetFrame();
nsBlockFrame::DestroyFrom(aDestructRoot);
}
static already_AddRefed<Element>
MakeAnonButton(nsIDocument* aDoc, const char* labelKey,
HTMLInputElement* aInputElement,
const nsAString& aAccessKey)
{
RefPtr<Element> button = aDoc->CreateHTMLElement(nsGkAtoms::button);
// NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
// attribute.
button->SetIsNativeAnonymousRoot();
button->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
NS_LITERAL_STRING("button"), false);
// Set the file picking button text depending on the current locale.
nsXPIDLString buttonTxt;
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
labelKey, buttonTxt);
// Set the browse button text. It's a bit of a pain to do because we want to
// make sure we are not notifying.
RefPtr<nsTextNode> textContent =
new nsTextNode(button->NodeInfo()->NodeInfoManager());
textContent->SetText(buttonTxt, false);
nsresult rv = button->AppendChildTo(textContent, false);
if (NS_FAILED(rv)) {
return nullptr;
}
// Make sure access key and tab order for the element actually redirect to the
// file picking button.
RefPtr<HTMLButtonElement> buttonElement =
HTMLButtonElement::FromContentOrNull(button);
if (!aAccessKey.IsEmpty()) {
buttonElement->SetAccessKey(aAccessKey);
}
// Both elements are given the same tab index so that the user can tab
// to the file control at the correct index, and then between the two
// buttons.
int32_t tabIndex;
aInputElement->GetTabIndex(&tabIndex);
buttonElement->SetTabIndex(tabIndex);
return button.forget();
}
nsresult
nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
RefPtr<HTMLInputElement> fileContent = HTMLInputElement::FromContentOrNull(mContent);
// The access key is transferred to the "Choose files..." button only. In
// effect that access key allows access to the control via that button, then
// the user can tab between the two buttons.
nsAutoString accessKey;
fileContent->GetAccessKey(accessKey);
mBrowseFilesOrDirs = MakeAnonButton(doc, "Browse", fileContent, accessKey);
if (!mBrowseFilesOrDirs || !aElements.AppendElement(mBrowseFilesOrDirs)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Create and setup the text showing the selected files.
RefPtr<NodeInfo> nodeInfo;
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr,
kNameSpaceID_XUL,
nsIDOMNode::ELEMENT_NODE);
NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget());
// NOTE: SetIsNativeAnonymousRoot() has to be called before setting any
// attribute.
mTextContent->SetIsNativeAnonymousRoot();
mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop,
NS_LITERAL_STRING("center"), false);
// Update the displayed text to reflect the current element's value.
nsAutoString value;
HTMLInputElement::FromContent(mContent)->GetDisplayFileName(value);
UpdateDisplayedValue(value, false);
if (!aElements.AppendElement(mTextContent)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// We should be able to interact with the element by doing drag and drop.
mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
mMouseListener, false);
mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
mMouseListener, false);
SyncDisabledState();
return NS_OK;
}
void
nsFileControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter)
{
if (mBrowseFilesOrDirs) {
aElements.AppendElement(mBrowseFilesOrDirs);
}
if (mTextContent) {
aElements.AppendElement(mTextContent);
}
}
NS_QUERYFRAME_HEAD(nsFileControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
void
nsFileControlFrame::SetFocus(bool aOn, bool aRepaint)
{
}
static void
AppendBlobImplAsDirectory(nsTArray<OwningFileOrDirectory>& aArray,
BlobImpl* aBlobImpl,
nsIContent* aContent)
{
MOZ_ASSERT(aBlobImpl);
MOZ_ASSERT(aBlobImpl->IsDirectory());
nsAutoString fullpath;
ErrorResult err;
aBlobImpl->GetMozFullPath(fullpath, err);
if (err.Failed()) {
err.SuppressException();
return;
}
nsCOMPtr<nsIFile> file;
NS_ConvertUTF16toUTF8 path(fullpath);
nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsPIDOMWindowInner* inner = aContent->OwnerDoc()->GetInnerWindow();
if (!inner || !inner->IsCurrentInnerWindow()) {
return;
}
RefPtr<Directory> directory =
Directory::Create(inner, file);
MOZ_ASSERT(directory);
OwningFileOrDirectory* element = aArray.AppendElement();
element->SetAsDirectory() = directory;
}
/**
* This is called when we receive a drop or a dragover.
*/
NS_IMETHODIMP
nsFileControlFrame::DnDListener::HandleEvent(nsIDOMEvent* aEvent)
{
NS_ASSERTION(mFrame, "We should have been unregistered");
bool defaultPrevented = false;
aEvent->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
return NS_OK;
}
nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
if (!dragEvent) {
return NS_OK;
}
nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
dragEvent->GetDataTransfer(getter_AddRefs(dataTransfer));
if (!IsValidDropData(dataTransfer)) {
return NS_OK;
}
nsCOMPtr<nsIContent> content = mFrame->GetContent();
bool supportsMultiple = content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
if (!CanDropTheseFiles(dataTransfer, supportsMultiple)) {
dataTransfer->SetDropEffect(NS_LITERAL_STRING("none"));
aEvent->StopPropagation();
return NS_OK;
}
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("dragover")) {
// Prevent default if we can accept this drag data
aEvent->PreventDefault();
return NS_OK;
}
if (eventType.EqualsLiteral("drop")) {
aEvent->StopPropagation();
aEvent->PreventDefault();
NS_ASSERTION(content, "The frame has no content???");
HTMLInputElement* inputElement = HTMLInputElement::FromContent(content);
NS_ASSERTION(inputElement, "No input element for this file upload control frame!");
nsCOMPtr<nsIDOMFileList> fileList;
dataTransfer->GetFiles(getter_AddRefs(fileList));
RefPtr<BlobImpl> webkitDir;
nsresult rv =
GetBlobImplForWebkitDirectory(fileList, getter_AddRefs(webkitDir));
NS_ENSURE_SUCCESS(rv, NS_OK);
nsTArray<OwningFileOrDirectory> array;
if (webkitDir) {
AppendBlobImplAsDirectory(array, webkitDir, content);
inputElement->MozSetDndFilesAndDirectories(array);
} else {
bool blinkFileSystemEnabled =
Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false);
if (blinkFileSystemEnabled) {
FileList* files = static_cast<FileList*>(fileList.get());
if (files) {
for (uint32_t i = 0; i < files->Length(); ++i) {
File* file = files->Item(i);
if (file) {
if (file->Impl() && file->Impl()->IsDirectory()) {
AppendBlobImplAsDirectory(array, file->Impl(), content);
} else {
OwningFileOrDirectory* element = array.AppendElement();
element->SetAsFile() = file;
}
}
}
}
}
// This is rather ugly. Pass the directories as Files using SetFiles,
// but then if blink filesystem API is enabled, it wants
// FileOrDirectory array.
inputElement->SetFiles(fileList, true);
if (blinkFileSystemEnabled) {
inputElement->UpdateEntries(array);
}
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
NS_LITERAL_STRING("input"), true,
false);
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
NS_LITERAL_STRING("change"), true,
false);
}
}
return NS_OK;
}
nsresult
nsFileControlFrame::DnDListener::GetBlobImplForWebkitDirectory(nsIDOMFileList* aFileList,
BlobImpl** aBlobImpl)
{
*aBlobImpl = nullptr;
HTMLInputElement* inputElement =
HTMLInputElement::FromContent(mFrame->GetContent());
bool webkitDirPicker =
Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
inputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory);
if (!webkitDirPicker) {
return NS_OK;
}
if (!aFileList) {
return NS_ERROR_FAILURE;
}
FileList* files = static_cast<FileList*>(aFileList);
// webkitdirectory doesn't care about the length of the file list but
// only about the first item on it.
uint32_t len = files->Length();
if (len) {
File* file = files->Item(0);
if (file) {
BlobImpl* impl = file->Impl();
if (impl && impl->IsDirectory()) {
RefPtr<BlobImpl> retVal = impl;
retVal.swap(*aBlobImpl);
return NS_OK;
}
}
}
return NS_ERROR_FAILURE;
}
bool
nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDataTransfer* aDOMDataTransfer)
{
nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
NS_ENSURE_TRUE(dataTransfer, false);
// We only support dropping files onto a file upload control
nsTArray<nsString> types;
dataTransfer->GetTypes(types, *nsContentUtils::GetSystemPrincipal());
return types.Contains(NS_LITERAL_STRING("Files"));
}
bool
nsFileControlFrame::DnDListener::CanDropTheseFiles(nsIDOMDataTransfer* aDOMDataTransfer,
bool aSupportsMultiple)
{
nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDOMDataTransfer);
NS_ENSURE_TRUE(dataTransfer, false);
nsCOMPtr<nsIDOMFileList> fileList;
dataTransfer->GetFiles(getter_AddRefs(fileList));
RefPtr<BlobImpl> webkitDir;
nsresult rv =
GetBlobImplForWebkitDirectory(fileList, getter_AddRefs(webkitDir));
// Just check if either there isn't webkitdirectory attribute, or
// fileList has a directory which can be dropped to the element.
// No need to use webkitDir for anything here.
NS_ENSURE_SUCCESS(rv, false);
uint32_t listLength = 0;
if (fileList) {
fileList->GetLength(&listLength);
}
return listLength <= 1 || aSupportsMultiple;
}
nscoord
nsFileControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
{
nscoord result;
DISPLAY_MIN_WIDTH(this, result);
// Our min width is our pref width
result = GetPrefISize(aRenderingContext);
return result;
}
void
nsFileControlFrame::SyncDisabledState()
{
EventStates eventStates = mContent->AsElement()->State();
if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
mBrowseFilesOrDirs->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
EmptyString(), true);
} else {
mBrowseFilesOrDirs->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
}
}
nsresult
nsFileControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
if (aModType == nsIDOMMutationEvent::REMOVAL) {
mBrowseFilesOrDirs->UnsetAttr(aNameSpaceID, aAttribute, true);
} else {
nsAutoString value;
mContent->GetAttr(aNameSpaceID, aAttribute, value);
mBrowseFilesOrDirs->SetAttr(aNameSpaceID, aAttribute, value, true);
}
}
return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
void
nsFileControlFrame::ContentStatesChanged(EventStates aStates)
{
if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
}
}
#ifdef DEBUG_FRAME_DUMP
nsresult
nsFileControlFrame::GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
}
#endif
void
nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
{
mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, aNotify);
}
nsresult
nsFileControlFrame::SetFormProperty(nsIAtom* aName,
const nsAString& aValue)
{
if (nsGkAtoms::value == aName) {
UpdateDisplayedValue(aValue, true);
}
return NS_OK;
}
void
nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
}
#ifdef ACCESSIBILITY
a11y::AccType
nsFileControlFrame::AccessibleType()
{
return a11y::eHTMLFileInputType;
}
#endif
////////////////////////////////////////////////////////////
// Mouse listener implementation
NS_IMPL_ISUPPORTS(nsFileControlFrame::MouseListener,
nsIDOMEventListener)

View file

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsFileControlFrame_h___
#define nsFileControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsBlockFrame.h"
#include "nsIFormControlFrame.h"
#include "nsIDOMEventListener.h"
#include "nsIAnonymousContentCreator.h"
#include "nsCOMPtr.h"
class nsIDOMDataTransfer;
class nsIDOMFileList;
namespace mozilla {
namespace dom {
class BlobImpl;
} // namespace dom
} // namespace mozilla
class nsFileControlFrame : public nsBlockFrame,
public nsIFormControlFrame,
public nsIAnonymousContentCreator
{
public:
explicit nsFileControlFrame(nsStyleContext* aContext);
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
// nsIFormControlFrame
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
virtual void SetFocus(bool aOn, bool aRepaint) override;
virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
virtual void ContentStatesChanged(mozilla::EventStates aStates) override;
virtual bool IsLeaf() const override
{
return true;
}
// nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
typedef bool (*AcceptAttrCallback)(const nsAString&, void*);
protected:
class MouseListener;
friend class MouseListener;
class MouseListener : public nsIDOMEventListener {
public:
NS_DECL_ISUPPORTS
explicit MouseListener(nsFileControlFrame* aFrame)
: mFrame(aFrame)
{}
void ForgetFrame() {
mFrame = nullptr;
}
protected:
virtual ~MouseListener() {}
nsFileControlFrame* mFrame;
};
class SyncDisabledStateEvent;
friend class SyncDisabledStateEvent;
class SyncDisabledStateEvent : public mozilla::Runnable
{
public:
explicit SyncDisabledStateEvent(nsFileControlFrame* aFrame)
: mFrame(aFrame)
{}
NS_IMETHOD Run() override {
nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mFrame.GetFrame());
NS_ENSURE_STATE(frame);
frame->SyncDisabledState();
return NS_OK;
}
private:
nsWeakFrame mFrame;
};
class DnDListener: public MouseListener {
public:
explicit DnDListener(nsFileControlFrame* aFrame)
: MouseListener(aFrame)
{}
NS_DECL_NSIDOMEVENTLISTENER
nsresult GetBlobImplForWebkitDirectory(nsIDOMFileList* aFileList,
mozilla::dom::BlobImpl** aBlobImpl);
bool IsValidDropData(nsIDOMDataTransfer* aDOMDataTransfer);
bool CanDropTheseFiles(nsIDOMDataTransfer* aDOMDataTransfer, bool aSupportsMultiple);
};
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsBlockFrame::IsFrameOfType(aFlags &
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
}
/**
* The text box input.
* @see nsFileControlFrame::CreateAnonymousContent
*/
nsCOMPtr<nsIContent> mTextContent;
/**
* The button to open a file or directory picker.
* @see nsFileControlFrame::CreateAnonymousContent
*/
nsCOMPtr<nsIContent> mBrowseFilesOrDirs;
/**
* Drag and drop mouse listener.
* This makes sure we don't get used after destruction.
*/
RefPtr<DnDListener> mMouseListener;
protected:
/**
* Sync the disabled state of the content with anonymous children.
*/
void SyncDisabledState();
/**
* Updates the displayed value by using aValue.
*/
void UpdateDisplayedValue(const nsAString& aValue, bool aNotify);
};
#endif // nsFileControlFrame_h___

View file

@ -0,0 +1,225 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsFormControlFrame.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
#include "nsIDOMHTMLInputElement.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/LookAndFeel.h"
#include "nsDeviceContext.h"
#include "nsIContent.h"
using namespace mozilla;
//#define FCF_NOISY
nsFormControlFrame::nsFormControlFrame(nsStyleContext* aContext)
: nsAtomicContainerFrame(aContext)
{
}
nsFormControlFrame::~nsFormControlFrame()
{
}
nsIAtom*
nsFormControlFrame::GetType() const
{
return nsGkAtoms::formControlFrame;
}
void
nsFormControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
// Unregister the access key registered in reflow
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
}
NS_QUERYFRAME_HEAD(nsFormControlFrame)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
/* virtual */ nscoord
nsFormControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
{
nscoord result;
DISPLAY_MIN_WIDTH(this, result);
result = GetIntrinsicISize();
return result;
}
/* virtual */ nscoord
nsFormControlFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
{
nscoord result;
DISPLAY_PREF_WIDTH(this, result);
result = GetIntrinsicISize();
return result;
}
/* virtual */
LogicalSize
nsFormControlFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
WritingMode aWM,
const LogicalSize& aCBSize,
nscoord aAvailableISize,
const LogicalSize& aMargin,
const LogicalSize& aBorder,
const LogicalSize& aPadding,
ComputeSizeFlags aFlags)
{
const WritingMode wm = GetWritingMode();
LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
return result.ConvertTo(aWM, wm);
}
nscoord
nsFormControlFrame::GetIntrinsicISize()
{
// Provide a reasonable default for sites that use an "auto" height.
// Note that if you change this, you should change the values in forms.css
// as well. This is the 13px default width minus the 2px default border.
return nsPresContext::CSSPixelsToAppUnits(13 - 2 * 2);
}
nscoord
nsFormControlFrame::GetIntrinsicBSize()
{
// Provide a reasonable default for sites that use an "auto" height.
// Note that if you change this, you should change the values in forms.css
// as well. This is the 13px default width minus the 2px default border.
return nsPresContext::CSSPixelsToAppUnits(13 - 2 * 2);
}
nscoord
nsFormControlFrame::GetLogicalBaseline(WritingMode aWritingMode) const
{
NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
"frame must not be dirty");
// Treat radio buttons and checkboxes as having an intrinsic baseline
// at the block-end of the control (use the block-end content edge rather
// than the margin edge).
// For "inverted" lines (typically in writing-mode:vertical-lr), use the
// block-start end instead.
return aWritingMode.IsLineInverted()
? GetLogicalUsedBorderAndPadding(aWritingMode).BStart(aWritingMode)
: BSize(aWritingMode) -
GetLogicalUsedBorderAndPadding(aWritingMode).BEnd(aWritingMode);
}
void
nsFormControlFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsFormControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsFormControlFrame::Reflow: aMaxSize=%d,%d",
aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
if (mState & NS_FRAME_FIRST_REFLOW) {
RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
}
aStatus = NS_FRAME_COMPLETE;
aDesiredSize.SetSize(aReflowInput.GetWritingMode(),
aReflowInput.ComputedSizeWithBorderPadding());
if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
float inflation = nsLayoutUtils::FontSizeInflationFor(this);
aDesiredSize.Width() *= inflation;
aDesiredSize.Height() *= inflation;
}
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("exit nsFormControlFrame::Reflow: size=%d,%d",
aDesiredSize.Width(), aDesiredSize.Height()));
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
aDesiredSize.SetOverflowAreasToDesiredBounds();
FinishAndStoreOverflow(&aDesiredSize);
}
nsresult
nsFormControlFrame::RegUnRegAccessKey(nsIFrame * aFrame, bool aDoReg)
{
NS_ENSURE_ARG_POINTER(aFrame);
nsPresContext* presContext = aFrame->PresContext();
NS_ASSERTION(presContext, "aPresContext is NULL in RegUnRegAccessKey!");
nsAutoString accessKey;
nsIContent* content = aFrame->GetContent();
content->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
if (!accessKey.IsEmpty()) {
EventStateManager* stateManager = presContext->EventStateManager();
if (aDoReg) {
stateManager->RegisterAccessKey(content, (uint32_t)accessKey.First());
} else {
stateManager->UnregisterAccessKey(content, (uint32_t)accessKey.First());
}
return NS_OK;
}
return NS_ERROR_FAILURE;
}
void
nsFormControlFrame::SetFocus(bool aOn, bool aRepaint)
{
}
nsresult
nsFormControlFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
// Check for user-input:none style
const nsStyleUserInterface* uiStyle = StyleUserInterface();
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
return NS_OK;
}
void
nsFormControlFrame::GetCurrentCheckState(bool *aState)
{
nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(mContent);
if (inputElement) {
inputElement->GetChecked(aState);
}
}
nsresult
nsFormControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
{
return NS_OK;
}
// static
nsRect
nsFormControlFrame::GetUsableScreenRect(nsPresContext* aPresContext)
{
nsRect screen;
nsDeviceContext *context = aPresContext->DeviceContext();
int32_t dropdownCanOverlapOSBar =
LookAndFeel::GetInt(LookAndFeel::eIntID_MenusCanOverlapOSBar, 0);
if ( dropdownCanOverlapOSBar )
context->GetRect(screen);
else
context->GetClientRect(screen);
return screen;
}

View file

@ -0,0 +1,129 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsFormControlFrame_h___
#define nsFormControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsIFormControlFrame.h"
#include "nsAtomicContainerFrame.h"
#include "nsDisplayList.h"
/**
* nsFormControlFrame is the base class for radio buttons and
* checkboxes. It also has two static methods (RegUnRegAccessKey and
* GetScreenHeight) that are used by other form controls.
*/
class nsFormControlFrame : public nsAtomicContainerFrame,
public nsIFormControlFrame
{
public:
/**
* Main constructor
* @param aContent the content representing this frame
* @param aParentFrame the parent frame
*/
explicit nsFormControlFrame(nsStyleContext*);
virtual nsIAtom* GetType() const override;
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsAtomicContainerFrame::IsFrameOfType(aFlags &
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
}
NS_DECL_QUERYFRAME
NS_DECL_ABSTRACT_FRAME(nsFormControlFrame)
// nsIFrame replacements
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override {
DO_GLOBAL_REFLOW_COUNT_DSP("nsFormControlFrame");
DisplayBorderBackgroundOutline(aBuilder, aLists);
}
/**
* Both GetMinISize and GetPrefISize will return whatever GetIntrinsicISize
* returns.
*/
virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
/**
* Our auto size is just intrinsic width and intrinsic height.
*/
virtual mozilla::LogicalSize
ComputeAutoSize(nsRenderingContext* aRenderingContext,
mozilla::WritingMode aWM,
const mozilla::LogicalSize& aCBSize,
nscoord aAvailableISize,
const mozilla::LogicalSize& aMargin,
const mozilla::LogicalSize& aBorder,
const mozilla::LogicalSize& aPadding,
ComputeSizeFlags aFlags) override;
/**
* Respond to a gui event
* @see nsIFrame::HandleEvent
*/
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode)
const override;
/**
* Respond to the request to resize and/or reflow
* @see nsIFrame::Reflow
*/
virtual void Reflow(nsPresContext* aCX,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
// new behavior
virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
// nsIFormControlFrame
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
// AccessKey Helper function
static nsresult RegUnRegAccessKey(nsIFrame * aFrame, bool aDoReg);
/**
* Returns the usable screen rect in app units, eg the rect where we can
* draw dropdowns.
*/
static nsRect GetUsableScreenRect(nsPresContext* aPresContext);
protected:
virtual ~nsFormControlFrame();
nscoord GetIntrinsicISize();
nscoord GetIntrinsicBSize();
//
//-------------------------------------------------------------------------------------
// Utility methods for managing checkboxes and radiobuttons
//-------------------------------------------------------------------------------------
//
/**
* Get the state of the checked attribute.
* @param aState set to true if the checked attribute is set,
* false if the checked attribute has been removed
*/
void GetCurrentCheckState(bool* aState);
};
#endif

View file

@ -0,0 +1,237 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsGfxButtonControlFrame.h"
#include "nsIFormControl.h"
#include "nsGkAtoms.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "nsContentUtils.h"
// MouseEvent suppression in PP
#include "nsContentList.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsTextNode.h"
using namespace mozilla;
nsGfxButtonControlFrame::nsGfxButtonControlFrame(nsStyleContext* aContext):
nsHTMLButtonControlFrame(aContext)
{
}
nsContainerFrame*
NS_NewGfxButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsGfxButtonControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsGfxButtonControlFrame)
void nsGfxButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsContentUtils::DestroyAnonymousContent(&mTextContent);
nsHTMLButtonControlFrame::DestroyFrom(aDestructRoot);
}
nsIAtom*
nsGfxButtonControlFrame::GetType() const
{
return nsGkAtoms::gfxButtonControlFrame;
}
#ifdef DEBUG_FRAME_DUMP
nsresult
nsGfxButtonControlFrame::GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("ButtonControl"), aResult);
}
#endif
// Create the text content used as label for the button.
// The frame will be generated by the frame constructor.
nsresult
nsGfxButtonControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
nsXPIDLString label;
GetLabel(label);
// Add a child text content node for the label
mTextContent = new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
// set the value of the text node and add it to the child list
mTextContent->SetText(label, false);
aElements.AppendElement(mTextContent);
return NS_OK;
}
void
nsGfxButtonControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter)
{
if (mTextContent) {
aElements.AppendElement(mTextContent);
}
}
// Create the text content used as label for the button.
// The frame will be generated by the frame constructor.
nsIFrame*
nsGfxButtonControlFrame::CreateFrameFor(nsIContent* aContent)
{
nsIFrame * newFrame = nullptr;
if (aContent == mTextContent) {
nsContainerFrame* parentFrame = do_QueryFrame(mFrames.FirstChild());
nsPresContext* presContext = PresContext();
RefPtr<nsStyleContext> textStyleContext;
textStyleContext = presContext->StyleSet()->
ResolveStyleForText(mTextContent, mStyleContext);
newFrame = NS_NewTextFrame(presContext->PresShell(), textStyleContext);
// initialize the text frame
newFrame->Init(mTextContent, parentFrame, nullptr);
mTextContent->SetPrimaryFrame(newFrame);
}
return newFrame;
}
NS_QUERYFRAME_HEAD(nsGfxButtonControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_TAIL_INHERITING(nsHTMLButtonControlFrame)
// Initially we hardcoded the default strings here.
// Next, we used html.css to store the default label for various types
// of buttons. (nsGfxButtonControlFrame::DoNavQuirksReflow rev 1.20)
// However, since html.css is not internationalized, we now grab the default
// label from a string bundle as is done for all other UI strings.
// See bug 16999 for further details.
nsresult
nsGfxButtonControlFrame::GetDefaultLabel(nsXPIDLString& aString) const
{
nsCOMPtr<nsIFormControl> form = do_QueryInterface(mContent);
NS_ENSURE_TRUE(form, NS_ERROR_UNEXPECTED);
int32_t type = form->GetType();
const char *prop;
if (type == NS_FORM_INPUT_RESET) {
prop = "Reset";
}
else if (type == NS_FORM_INPUT_SUBMIT) {
prop = "Submit";
}
else {
aString.Truncate();
return NS_OK;
}
return nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
prop, aString);
}
nsresult
nsGfxButtonControlFrame::GetLabel(nsXPIDLString& aLabel)
{
// Get the text from the "value" property on our content if there is
// one; otherwise set it to a default value (localized).
nsresult rv;
nsCOMPtr<nsIDOMHTMLInputElement> elt = do_QueryInterface(mContent);
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value) && elt) {
rv = elt->GetValue(aLabel);
} else {
// Generate localized label.
// We can't make any assumption as to what the default would be
// because the value is localized for non-english platforms, thus
// it might not be the string "Reset", "Submit Query", or "Browse..."
rv = GetDefaultLabel(aLabel);
}
NS_ENSURE_SUCCESS(rv, rv);
// Compress whitespace out of label if needed.
if (!StyleText()->WhiteSpaceIsSignificant()) {
aLabel.CompressWhitespace();
} else if (aLabel.Length() > 2 && aLabel.First() == ' ' &&
aLabel.CharAt(aLabel.Length() - 1) == ' ') {
// This is a bit of a hack. The reason this is here is as follows: we now
// have default padding on our buttons to make them non-ugly.
// Unfortunately, IE-windows does not have such padding, so people will
// stick values like " ok " (with the spaces) in the buttons in an attempt
// to make them look decent. Unfortunately, if they do this the button
// looks way too big in Mozilla. Worse yet, if they do this _and_ set a
// fixed width for the button we run into trouble because our focus-rect
// border/padding and outer border take up 10px of the horizontal button
// space or so; the result is that the text is misaligned, even with the
// recentering we do in nsHTMLButtonControlFrame::Reflow. So to solve
// this, even if the whitespace is significant, single leading and trailing
// _spaces_ (and not other whitespace) are removed. The proper solution,
// of course, is to not have the focus rect painting taking up 6px of
// horizontal space. We should do that instead (via XBL form controls or
// changing the renderer) and remove this.
aLabel.Cut(0, 1);
aLabel.Truncate(aLabel.Length() - 1);
}
return NS_OK;
}
nsresult
nsGfxButtonControlFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
nsresult rv = NS_OK;
// If the value attribute is set, update the text of the label
if (nsGkAtoms::value == aAttribute) {
if (mTextContent && mContent) {
nsXPIDLString label;
rv = GetLabel(label);
NS_ENSURE_SUCCESS(rv, rv);
mTextContent->SetText(label, true);
} else {
rv = NS_ERROR_UNEXPECTED;
}
// defer to HTMLButtonControlFrame
} else {
rv = nsHTMLButtonControlFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
return rv;
}
bool
nsGfxButtonControlFrame::IsLeaf() const
{
return true;
}
nsContainerFrame*
nsGfxButtonControlFrame::GetContentInsertionFrame()
{
return this;
}
nsresult
nsGfxButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
// Override the HandleEvent to prevent the nsFrame::HandleEvent
// from being called. The nsFrame::HandleEvent causes the button label
// to be selected (Drawn with an XOR rectangle over the label)
// do we have user-input style?
const nsStyleUserInterface* uiStyle = StyleUserInterface();
if (uiStyle->mUserInput == StyleUserInput::None ||
uiStyle->mUserInput == StyleUserInput::Disabled) {
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
return NS_OK;
}

View file

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsGfxButtonControlFrame_h___
#define nsGfxButtonControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsHTMLButtonControlFrame.h"
#include "nsCOMPtr.h"
#include "nsIAnonymousContentCreator.h"
// Class which implements the input[type=button, reset, submit] and
// browse button for input[type=file].
// The label for button is specified through generated content
// in the ua.css file.
class nsGfxButtonControlFrame : public nsHTMLButtonControlFrame,
public nsIAnonymousContentCreator
{
public:
NS_DECL_FRAMEARENA_HELPERS
explicit nsGfxButtonControlFrame(nsStyleContext* aContext);
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
virtual nsIAtom* GetType() const override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
NS_DECL_QUERYFRAME
// nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
virtual nsIFrame* CreateFrameFor(nsIContent* aContent) override;
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
virtual bool IsLeaf() const override;
virtual nsContainerFrame* GetContentInsertionFrame() override;
protected:
nsresult GetDefaultLabel(nsXPIDLString& aLabel) const;
nsresult GetLabel(nsXPIDLString& aLabel);
virtual bool IsInput() override { return true; }
private:
nsCOMPtr<nsIContent> mTextContent;
};
#endif

View file

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsGfxCheckboxControlFrame.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "nsIContent.h"
#include "nsCOMPtr.h"
#include "nsLayoutUtils.h"
#include "nsRenderingContext.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsDisplayList.h"
#include <algorithm>
using namespace mozilla;
using namespace mozilla::gfx;
static void
PaintCheckMark(nsIFrame* aFrame,
DrawTarget* aDrawTarget,
const nsRect& aDirtyRect,
nsPoint aPt)
{
nsRect rect(aPt, aFrame->GetSize());
rect.Deflate(aFrame->GetUsedBorderAndPadding());
// Points come from the coordinates on a 7X7 unit box centered at 0,0
const int32_t checkPolygonX[] = { -3, -1, 3, 3, -1, -3 };
const int32_t checkPolygonY[] = { -1, 1, -3, -1, 3, 1 };
const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(int32_t);
const int32_t checkSize = 9; // 2 units of padding on either side
// of the 7x7 unit checkmark
// Scale the checkmark based on the smallest dimension
nscoord paintScale = std::min(rect.width, rect.height) / checkSize;
nsPoint paintCenter(rect.x + rect.width / 2,
rect.y + rect.height / 2);
RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
nsPoint p = paintCenter + nsPoint(checkPolygonX[0] * paintScale,
checkPolygonY[0] * paintScale);
int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
builder->MoveTo(NSPointToPoint(p, appUnitsPerDevPixel));
for (int32_t polyIndex = 1; polyIndex < checkNumPoints; polyIndex++) {
p = paintCenter + nsPoint(checkPolygonX[polyIndex] * paintScale,
checkPolygonY[polyIndex] * paintScale);
builder->LineTo(NSPointToPoint(p, appUnitsPerDevPixel));
}
RefPtr<Path> path = builder->Finish();
aDrawTarget->Fill(path,
ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
}
static void
PaintIndeterminateMark(nsIFrame* aFrame,
DrawTarget* aDrawTarget,
const nsRect& aDirtyRect,
nsPoint aPt)
{
int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
nsRect rect(aPt, aFrame->GetSize());
rect.Deflate(aFrame->GetUsedBorderAndPadding());
rect.y += (rect.height - rect.height/4) / 2;
rect.height /= 4;
Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *aDrawTarget);
aDrawTarget->FillRect(
devPxRect, ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
}
//------------------------------------------------------------
nsIFrame*
NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext)
{
return new (aPresShell) nsGfxCheckboxControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsGfxCheckboxControlFrame)
//------------------------------------------------------------
// Initialize GFX-rendered state
nsGfxCheckboxControlFrame::nsGfxCheckboxControlFrame(nsStyleContext* aContext)
: nsFormControlFrame(aContext)
{
}
nsGfxCheckboxControlFrame::~nsGfxCheckboxControlFrame()
{
}
#ifdef ACCESSIBILITY
a11y::AccType
nsGfxCheckboxControlFrame::AccessibleType()
{
return a11y::eHTMLCheckboxType;
}
#endif
//------------------------------------------------------------
void
nsGfxCheckboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
// Get current checked state through content model.
if ((!IsChecked() && !IsIndeterminate()) || !IsVisibleForPainting(aBuilder))
return; // we're not checked or not visible, nothing to paint.
if (IsThemed())
return; // No need to paint the checkmark. The theme will do it.
aLists.Content()->AppendNewToTop(new (aBuilder)
nsDisplayGeneric(aBuilder, this,
IsIndeterminate()
? PaintIndeterminateMark : PaintCheckMark,
"CheckedCheckbox",
nsDisplayItem::TYPE_CHECKED_CHECKBOX));
}
//------------------------------------------------------------
bool
nsGfxCheckboxControlFrame::IsChecked()
{
nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
bool retval = false;
elem->GetChecked(&retval);
return retval;
}
bool
nsGfxCheckboxControlFrame::IsIndeterminate()
{
nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
bool retval = false;
elem->GetIndeterminate(&retval);
return retval;
}

View file

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsGfxCheckboxControlFrame_h___
#define nsGfxCheckboxControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsFormControlFrame.h"
class nsGfxCheckboxControlFrame : public nsFormControlFrame
{
public:
NS_DECL_FRAMEARENA_HELPERS
explicit nsGfxCheckboxControlFrame(nsStyleContext* aContext);
virtual ~nsGfxCheckboxControlFrame();
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("CheckboxControl"), aResult);
}
#endif
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
protected:
bool IsChecked();
bool IsIndeterminate();
};
#endif

View file

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsGfxRadioControlFrame.h"
#include "gfx2DGlue.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/PathHelpers.h"
#include "nsLayoutUtils.h"
#include "nsRenderingContext.h"
#include "nsDisplayList.h"
using namespace mozilla;
using namespace mozilla::gfx;
nsIFrame*
NS_NewGfxRadioControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsGfxRadioControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsGfxRadioControlFrame)
nsGfxRadioControlFrame::nsGfxRadioControlFrame(nsStyleContext* aContext):
nsFormControlFrame(aContext)
{
}
nsGfxRadioControlFrame::~nsGfxRadioControlFrame()
{
}
#ifdef ACCESSIBILITY
a11y::AccType
nsGfxRadioControlFrame::AccessibleType()
{
return a11y::eHTMLRadioButtonType;
}
#endif
//--------------------------------------------------------------
// Draw the dot for a non-native radio button in the checked state.
static void
PaintCheckedRadioButton(nsIFrame* aFrame,
DrawTarget* aDrawTarget,
const nsRect& aDirtyRect,
nsPoint aPt)
{
// The dot is an ellipse 2px on all sides smaller than the content-box,
// drawn in the foreground color.
nsRect rect(aPt, aFrame->GetSize());
rect.Deflate(aFrame->GetUsedBorderAndPadding());
rect.Deflate(nsPresContext::CSSPixelsToAppUnits(2),
nsPresContext::CSSPixelsToAppUnits(2));
Rect devPxRect =
ToRect(nsLayoutUtils::RectToGfxRect(rect,
aFrame->PresContext()->AppUnitsPerDevPixel()));
ColorPattern color(ToDeviceColor(aFrame->StyleColor()->mColor));
RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
RefPtr<Path> ellipse = builder->Finish();
aDrawTarget->Fill(ellipse, color);
}
void
nsGfxRadioControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
if (!IsVisibleForPainting(aBuilder))
return;
if (IsThemed())
return; // The theme will paint the check, if any.
bool checked = true;
GetCurrentCheckState(&checked); // Get check state from the content model
if (!checked)
return;
aLists.Content()->AppendNewToTop(new (aBuilder)
nsDisplayGeneric(aBuilder, this, PaintCheckedRadioButton,
"CheckedRadioButton",
nsDisplayItem::TYPE_CHECKED_RADIOBUTTON));
}

View file

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsGfxRadioControlFrame_h___
#define nsGfxRadioControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsFormControlFrame.h"
// nsGfxRadioControlFrame
class nsGfxRadioControlFrame : public nsFormControlFrame
{
public:
explicit nsGfxRadioControlFrame(nsStyleContext* aContext);
~nsGfxRadioControlFrame();
NS_DECL_FRAMEARENA_HELPERS
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
};
#endif

View file

@ -0,0 +1,465 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsHTMLButtonControlFrame.h"
#include "nsContainerFrame.h"
#include "nsIFormControlFrame.h"
#include "nsPresContext.h"
#include "nsGkAtoms.h"
#include "nsButtonFrameRenderer.h"
#include "nsCSSAnonBoxes.h"
#include "nsFormControlFrame.h"
#include "nsNameSpaceManager.h"
#include "nsDisplayList.h"
#include <algorithm>
using namespace mozilla;
nsContainerFrame*
NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsHTMLButtonControlFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLButtonControlFrame)
nsHTMLButtonControlFrame::nsHTMLButtonControlFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
{
}
nsHTMLButtonControlFrame::~nsHTMLButtonControlFrame()
{
}
void
nsHTMLButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
nsContainerFrame::DestroyFrom(aDestructRoot);
}
void
nsHTMLButtonControlFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
mRenderer.SetFrame(this, PresContext());
}
NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
#ifdef ACCESSIBILITY
a11y::AccType
nsHTMLButtonControlFrame::AccessibleType()
{
return a11y::eHTMLButtonType;
}
#endif
nsIAtom*
nsHTMLButtonControlFrame::GetType() const
{
return nsGkAtoms::HTMLButtonControlFrame;
}
void
nsHTMLButtonControlFrame::SetFocus(bool aOn, bool aRepaint)
{
}
nsresult
nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
// if disabled do nothing
if (mRenderer.isDisabled()) {
return NS_OK;
}
// mouse clicks are handled by content
// we don't want our children to get any events. So just pass it to frame.
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
bool
nsHTMLButtonControlFrame::ShouldClipPaintingToBorderBox()
{
return IsInput() || StyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE;
}
void
nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
// Clip to our border area for event hit testing.
Maybe<DisplayListClipState::AutoSaveRestore> eventClipState;
const bool isForEventDelivery = aBuilder->IsForEventDelivery();
if (isForEventDelivery) {
eventClipState.emplace(aBuilder);
nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
nscoord radii[8];
bool hasRadii = GetBorderRadii(radii);
eventClipState->ClipContainingBlockDescendants(rect, hasRadii ? radii : nullptr);
}
nsDisplayList onTop;
if (IsVisibleForPainting(aBuilder)) {
mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
}
nsDisplayListCollection set;
// Do not allow the child subtree to receive events.
if (!isForEventDelivery) {
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (ShouldClipPaintingToBorderBox()) {
nsMargin border = StyleBorder()->GetComputedBorder();
nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
rect.Deflate(border);
nscoord radii[8];
bool hasRadii = GetPaddingBoxBorderRadii(radii);
clipState.ClipContainingBlockDescendants(rect, hasRadii ? radii : nullptr);
}
BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
// That should put the display items in set.Content()
}
// Put the foreground outline and focus rects on top of the children
set.Content()->AppendToTop(&onTop);
set.MoveTo(aLists);
DisplayOutline(aBuilder, aLists);
// to draw border when selected in editor
DisplaySelectionOverlay(aBuilder, aLists.Content());
}
nscoord
nsHTMLButtonControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
{
nscoord result;
DISPLAY_MIN_WIDTH(this, result);
nsIFrame* kid = mFrames.FirstChild();
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
kid,
nsLayoutUtils::MIN_ISIZE);
result += GetWritingMode().IsVertical()
? mRenderer.GetAddedButtonBorderAndPadding().TopBottom()
: mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
return result;
}
nscoord
nsHTMLButtonControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
{
nscoord result;
DISPLAY_PREF_WIDTH(this, result);
nsIFrame* kid = mFrames.FirstChild();
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
kid,
nsLayoutUtils::PREF_ISIZE);
result += GetWritingMode().IsVertical()
? mRenderer.GetAddedButtonBorderAndPadding().TopBottom()
: mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
return result;
}
void
nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus)
{
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
NS_PRECONDITION(aReflowInput.ComputedISize() != NS_INTRINSICSIZE,
"Should have real computed inline-size by now");
if (mState & NS_FRAME_FIRST_REFLOW) {
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
}
// Reflow the child
nsIFrame* firstKid = mFrames.FirstChild();
MOZ_ASSERT(firstKid, "Button should have a child frame for its contents");
MOZ_ASSERT(!firstKid->GetNextSibling(),
"Button should have exactly one child frame");
MOZ_ASSERT(firstKid->StyleContext()->GetPseudo() ==
nsCSSAnonBoxes::buttonContent,
"Button's child frame has unexpected pseudo type!");
// XXXbz Eventually we may want to check-and-bail if
// !aReflowInput.ShouldReflowAllKids() &&
// !NS_SUBTREE_DIRTY(firstKid).
// We'd need to cache our ascent for that, of course.
// Reflow the contents of the button.
// (This populates our aDesiredSize, too.)
ReflowButtonContents(aPresContext, aDesiredSize,
aReflowInput, firstKid);
if (!ShouldClipPaintingToBorderBox()) {
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, firstKid);
}
// else, we ignore child overflow -- anything that overflows beyond our
// own border-box will get clipped when painting.
aStatus = NS_FRAME_COMPLETE;
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
aReflowInput, aStatus);
// We're always complete and we don't support overflow containers
// so we shouldn't have a next-in-flow ever.
aStatus = NS_FRAME_COMPLETE;
MOZ_ASSERT(!GetNextInFlow());
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
// Helper-function that lets us clone the button's reflow state, but with its
// ComputedWidth and ComputedHeight reduced by the amount of renderer-specific
// focus border and padding that we're using. (This lets us provide a more
// appropriate content-box size for descendents' percent sizes to resolve
// against.)
static ReflowInput
CloneReflowInputWithReducedContentBox(
const ReflowInput& aButtonReflowInput,
const nsMargin& aFocusPadding)
{
nscoord adjustedWidth =
aButtonReflowInput.ComputedWidth() - aFocusPadding.LeftRight();
adjustedWidth = std::max(0, adjustedWidth);
// (Only adjust height if it's an actual length.)
nscoord adjustedHeight = aButtonReflowInput.ComputedHeight();
if (adjustedHeight != NS_INTRINSICSIZE) {
adjustedHeight -= aFocusPadding.TopBottom();
adjustedHeight = std::max(0, adjustedHeight);
}
ReflowInput clone(aButtonReflowInput);
clone.SetComputedWidth(adjustedWidth);
clone.SetComputedHeight(adjustedHeight);
return clone;
}
void
nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
ReflowOutput& aButtonDesiredSize,
const ReflowInput& aButtonReflowInput,
nsIFrame* aFirstKid)
{
WritingMode wm = GetWritingMode();
LogicalSize availSize = aButtonReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_INTRINSICSIZE;
// Buttons have some bonus renderer-determined border/padding,
// which occupies part of the button's content-box area:
LogicalMargin focusPadding =
LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding());
// See whether out availSize's inline-size is big enough. If it's
// smaller than our intrinsic min iSize, that means that the kid
// wouldn't really fit. In that case, we overflow into our internal
// focuspadding (which other browsers don't have) so that there's a
// little more space for it.
// Note that GetMinISize includes the focusPadding.
nscoord IOverflow = GetMinISize(aButtonReflowInput.mRenderingContext) -
aButtonReflowInput.ComputedISize();
nscoord IFocusPadding = focusPadding.IStartEnd(wm);
nscoord focusPaddingReduction = std::min(IFocusPadding,
std::max(IOverflow, 0));
if (focusPaddingReduction > 0) {
nscoord startReduction = focusPadding.IStart(wm);
if (focusPaddingReduction != IFocusPadding) {
startReduction = NSToCoordRound(startReduction *
(float(focusPaddingReduction) /
float(IFocusPadding)));
}
focusPadding.IStart(wm) -= startReduction;
focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction;
}
// shorthand for a value we need to use in a bunch of places
const LogicalMargin& clbp = aButtonReflowInput.ComputedLogicalBorderPadding();
// Indent the child inside us by the focus border. We must do this separate
// from the regular border.
availSize.ISize(wm) -= focusPadding.IStartEnd(wm);
LogicalPoint childPos(wm);
childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm);
availSize.ISize(wm) = std::max(availSize.ISize(wm), 0);
// Give child a clone of the button's reflow state, with height/width reduced
// by focusPadding, so that descendants with height:100% don't protrude.
ReflowInput adjustedButtonReflowInput =
CloneReflowInputWithReducedContentBox(aButtonReflowInput,
focusPadding.GetPhysicalMargin(wm));
ReflowInput contentsReflowInput(aPresContext,
adjustedButtonReflowInput,
aFirstKid, availSize);
nsReflowStatus contentsReflowStatus;
ReflowOutput contentsDesiredSize(aButtonReflowInput);
childPos.B(wm) = 0; // This will be set properly later, after reflowing the
// child to determine its size.
// We just pass a dummy containerSize here, as the child will be
// repositioned later by FinishReflowChild.
nsSize dummyContainerSize;
ReflowChild(aFirstKid, aPresContext,
contentsDesiredSize, contentsReflowInput,
wm, childPos, dummyContainerSize, 0, contentsReflowStatus);
MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
"We gave button-contents frame unconstrained available height, "
"so it should be complete");
// Compute the button's content-box size:
LogicalSize buttonContentBox(wm);
if (aButtonReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
// Button has a fixed block-size -- that's its content-box bSize.
buttonContentBox.BSize(wm) = aButtonReflowInput.ComputedBSize();
} else {
// Button is intrinsically sized -- it should shrinkwrap the
// button-contents' bSize, plus any focus-padding space:
buttonContentBox.BSize(wm) =
contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm);
// Make sure we obey min/max-bSize in the case when we're doing intrinsic
// sizing (we get it for free when we have a non-intrinsic
// aButtonReflowInput.ComputedBSize()). Note that we do this before
// adjusting for borderpadding, since mComputedMaxBSize and
// mComputedMinBSize are content bSizes.
buttonContentBox.BSize(wm) =
NS_CSS_MINMAX(buttonContentBox.BSize(wm),
aButtonReflowInput.ComputedMinBSize(),
aButtonReflowInput.ComputedMaxBSize());
}
if (aButtonReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
buttonContentBox.ISize(wm) = aButtonReflowInput.ComputedISize();
} else {
buttonContentBox.ISize(wm) =
contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm);
buttonContentBox.ISize(wm) =
NS_CSS_MINMAX(buttonContentBox.ISize(wm),
aButtonReflowInput.ComputedMinISize(),
aButtonReflowInput.ComputedMaxISize());
}
// Center child in the block-direction in the button
// (technically, inside of the button's focus-padding area)
nscoord extraSpace =
buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) -
contentsDesiredSize.BSize(wm);
childPos.B(wm) = std::max(0, extraSpace / 2);
// Adjust childPos.B() to be in terms of the button's frame-rect, instead of
// its focus-padding rect:
childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm);
nsSize containerSize =
(buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm);
// Place the child
FinishReflowChild(aFirstKid, aPresContext,
contentsDesiredSize, &contentsReflowInput,
wm, childPos, containerSize, 0);
// Make sure we have a useful 'ascent' value for the child
if (contentsDesiredSize.BlockStartAscent() ==
ReflowOutput::ASK_FOR_BASELINE) {
WritingMode wm = aButtonReflowInput.GetWritingMode();
contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
}
// OK, we're done with the child frame.
// Use what we learned to populate the button frame's reflow metrics.
// * Button's height & width are content-box size + border-box contribution:
aButtonDesiredSize.SetSize(wm,
LogicalSize(wm, aButtonReflowInput.ComputedISize() + clbp.IStartEnd(wm),
buttonContentBox.BSize(wm) + clbp.BStartEnd(wm)));
// * Button's ascent is its child's ascent, plus the child's block-offset
// within our frame... unless it's orthogonal, in which case we'll use the
// contents inline-size as an approximation for now.
// XXX is there a better strategy? should we include border-padding?
if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) {
aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm));
} else {
aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() +
childPos.B(wm));
}
aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
}
nsresult nsHTMLButtonControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
{
if (nsGkAtoms::value == aName) {
return mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value,
aValue, true);
}
return NS_OK;
}
nsStyleContext*
nsHTMLButtonControlFrame::GetAdditionalStyleContext(int32_t aIndex) const
{
return mRenderer.GetStyleContext(aIndex);
}
void
nsHTMLButtonControlFrame::SetAdditionalStyleContext(int32_t aIndex,
nsStyleContext* aStyleContext)
{
mRenderer.SetStyleContext(aIndex, aStyleContext);
}
#ifdef DEBUG
void
nsHTMLButtonControlFrame::AppendFrames(ChildListID aListID,
nsFrameList& aFrameList)
{
MOZ_CRASH("unsupported operation");
}
void
nsHTMLButtonControlFrame::InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList)
{
MOZ_CRASH("unsupported operation");
}
void
nsHTMLButtonControlFrame::RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame)
{
MOZ_CRASH("unsupported operation");
}
#endif

View file

@ -0,0 +1,115 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsHTMLButtonControlFrame_h___
#define nsHTMLButtonControlFrame_h___
#include "mozilla/Attributes.h"
#include "nsContainerFrame.h"
#include "nsIFormControlFrame.h"
#include "nsButtonFrameRenderer.h"
class nsRenderingContext;
class nsPresContext;
class nsHTMLButtonControlFrame : public nsContainerFrame,
public nsIFormControlFrame
{
public:
explicit nsHTMLButtonControlFrame(nsStyleContext* aContext);
~nsHTMLButtonControlFrame();
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
virtual void Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual nsStyleContext* GetAdditionalStyleContext(int32_t aIndex) const override;
virtual void SetAdditionalStyleContext(int32_t aIndex,
nsStyleContext* aStyleContext) override;
#ifdef DEBUG
virtual void AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) override;
virtual void InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList) override;
virtual void RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame) override;
#endif
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
virtual nsIAtom* GetType() const override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("HTMLButtonControl"), aResult);
}
#endif
virtual bool HonorPrintBackgroundSettings() override { return false; }
// nsIFormControlFrame
void SetFocus(bool aOn, bool aRepaint) override;
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
// Inserted child content gets its frames parented by our child block
virtual nsContainerFrame* GetContentInsertionFrame() override {
return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
}
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsContainerFrame::IsFrameOfType(aFlags &
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
}
protected:
virtual bool IsInput() { return false; }
// Indicates whether we should clip our children's painting to our
// border-box (either because of "overflow" or because of legacy reasons
// about how <input>-flavored buttons work).
bool ShouldClipPaintingToBorderBox();
// Reflows the button's sole child frame, and computes the desired size
// of the button itself from the results.
void ReflowButtonContents(nsPresContext* aPresContext,
ReflowOutput& aButtonDesiredSize,
const ReflowInput& aButtonReflowInput,
nsIFrame* aFirstKid);
nsButtonFrameRenderer mRenderer;
};
#endif

View file

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsIComboboxControlFrame_h___
#define nsIComboboxControlFrame_h___
#include "nsQueryFrame.h"
/**
* nsIComboboxControlFrame is the interface for comboboxes.
*/
class nsIComboboxControlFrame : public nsQueryFrame
{
public:
NS_DECL_QUERYFRAME_TARGET(nsIComboboxControlFrame)
/**
* Indicates whether the list is dropped down
*/
virtual bool IsDroppedDown() = 0;
virtual bool IsOpenInParentProcess() = 0;
virtual void SetOpenInParentProcess(bool aVal) = 0;
bool IsDroppedDownOrHasParentPopup() { return IsDroppedDown() || IsOpenInParentProcess(); }
/**
* Shows or hides the drop down
*/
virtual void ShowDropDown(bool aDoDropDown) = 0;
/**
* Gets the Drop Down List
*/
virtual nsIFrame* GetDropDown() = 0;
/**
* Sets the Drop Down List
*/
virtual void SetDropDown(nsIFrame* aDropDownFrame) = 0;
/**
* Tells the combobox to roll up
*/
virtual void RollupFromList() = 0;
/**
* Redisplay the selected text (will do nothing if text has not changed).
* This method might destroy this frame or any others that happen to be
* around. It might even run script.
*/
NS_IMETHOD RedisplaySelectedText() = 0;
/**
* Method for the listbox to set and get the recent index
*/
virtual int32_t UpdateRecentIndex(int32_t aIndex) = 0;
/**
* Notification that the content has been reset
*/
virtual void OnContentReset() = 0;
/**
* This returns the index of the item that is currently being displayed
* in the display area. It may differ from what the currently Selected index
* is in in the dropdown.
*
* Detailed explanation:
* When the dropdown is dropped down via a mouse click and the user moves the mouse
* up and down without clicking, the currently selected item is being tracking inside
* the dropdown, but the combobox is not being updated. When the user selects items
* with the arrow keys, the combobox is being updated. So when the user clicks outside
* the dropdown and it needs to roll up it has to decide whether to keep the current
* selection or not. This method is used to get the current index in the combobox to
* compare it to the current index in the dropdown to see if the combox has been updated
* and that way it knows whether to "cancel" the current selection residing in the
* dropdown. Or whether to leave the selection alone.
*/
virtual int32_t GetIndexOfDisplayArea() = 0;
};
#endif

View file

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsIFormControlFrame_h___
#define nsIFormControlFrame_h___
#include "nsQueryFrame.h"
class nsAString;
class nsIAtom;
/**
* nsIFormControlFrame is the common interface for frames of form controls. It
* provides a uniform way of creating widgets, resizing, and painting.
* @see nsLeafFrame and its base classes for more info
*/
class nsIFormControlFrame : public nsQueryFrame
{
public:
NS_DECL_QUERYFRAME_TARGET(nsIFormControlFrame)
/**
*
* @param aOn
* @param aRepaint
*/
virtual void SetFocus(bool aOn = true, bool aRepaint = false) = 0;
/**
* Set a property on the form control frame.
*
* @param aName name of the property to set
* @param aValue value of the property
* @returns NS_OK if the property name is valid, otherwise an error code
*/
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) = 0;
};
#endif

View file

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsIListControlFrame_h___
#define nsIListControlFrame_h___
#include "nsQueryFrame.h"
class nsAString;
namespace mozilla {
namespace dom {
class HTMLOptionElement;
} // namespace dom
} // namespace mozilla
/**
* nsIListControlFrame is the interface for frame-based listboxes.
*/
class nsIListControlFrame : public nsQueryFrame
{
public:
NS_DECL_QUERYFRAME_TARGET(nsIListControlFrame)
/**
* Sets the ComboBoxFrame
*
*/
virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) = 0;
/**
* Get the display string for an item
*/
virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) = 0;
/**
* Get the Selected Item's index
*
*/
virtual int32_t GetSelectedIndex() = 0;
/**
* Return current option. The current option is the option displaying
* the focus ring when the listbox is focused.
*/
virtual mozilla::dom::HTMLOptionElement* GetCurrentOption() = 0;
/**
* Initiates mouse capture for the listbox
*
*/
virtual void CaptureMouseEvents(bool aGrabMouseEvents) = 0;
/**
* Returns the block size of a single row in the list. This is the
* maximum of the block sizes of all the options/optgroups.
*/
virtual nscoord GetBSizeOfARow() = 0;
/**
* Returns the number of options in the listbox
*/
virtual uint32_t GetNumberOfOptions() = 0;
/**
* Called by combobox when it's about to drop down
*/
virtual void AboutToDropDown() = 0;
/**
* Called by combobox when it's about to roll up
*/
virtual void AboutToRollup() = 0;
/**
* Fire on input and on change (used by combobox)
*/
virtual void FireOnInputAndOnChange() = 0;
/**
* Tell the selected list to roll up and ensure that the proper index is
* selected, possibly firing onChange if the index has changed
*
* @param aIndex the index to actually select
*/
virtual void ComboboxFinish(int32_t aIndex) = 0;
/**
* Notification that the content has been reset
*/
virtual void OnContentReset() = 0;
};
#endif

View file

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsISelectControlFrame_h___
#define nsISelectControlFrame_h___
#include "nsQueryFrame.h"
/**
* nsISelectControlFrame is the interface for combo boxes and listboxes
*/
class nsISelectControlFrame : public nsQueryFrame
{
public:
NS_DECL_QUERYFRAME_TARGET(nsISelectControlFrame)
/**
* Adds an option to the list at index
*/
NS_IMETHOD AddOption(int32_t index) = 0;
/**
* Removes the option at index. The caller must have a live script
* blocker while calling this method.
*/
NS_IMETHOD RemoveOption(int32_t index) = 0;
/**
* Sets whether the parser is done adding children
* @param aIsDone whether the parser is done adding children
*/
NS_IMETHOD DoneAddingChildren(bool aIsDone) = 0;
/**
* Notify the frame when an option is selected
*/
NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) = 0;
/**
* Notify the frame when selectedIndex was changed. This might
* destroy the frame.
*/
NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) = 0;
};
#endif

View file

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef nsITextControlFrame_h___
#define nsITextControlFrame_h___
#include "nsIFormControlFrame.h"
class nsIEditor;
class nsISelectionController;
class nsFrameSelection;
class nsITextControlFrame : public nsIFormControlFrame
{
public:
NS_DECL_QUERYFRAME_TARGET(nsITextControlFrame)
enum SelectionDirection {
eNone,
eForward,
eBackward
};
NS_IMETHOD GetEditor(nsIEditor **aEditor) = 0;
NS_IMETHOD SetSelectionStart(int32_t aSelectionStart) = 0;
NS_IMETHOD SetSelectionEnd(int32_t aSelectionEnd) = 0;
NS_IMETHOD SetSelectionRange(int32_t aSelectionStart,
int32_t aSelectionEnd,
SelectionDirection aDirection = eNone) = 0;
NS_IMETHOD GetSelectionRange(int32_t* aSelectionStart,
int32_t* aSelectionEnd,
SelectionDirection* aDirection = nullptr) = 0;
NS_IMETHOD GetOwnedSelectionController(nsISelectionController** aSelCon) = 0;
virtual nsFrameSelection* GetOwnedFrameSelection() = 0;
virtual nsresult GetPhonetic(nsAString& aPhonetic) = 0;
/**
* Ensure editor is initialized with the proper flags and the default value.
* @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created
* @throws various and sundry other things
*/
virtual nsresult EnsureEditorInitialized() = 0;
virtual nsresult ScrollSelectionIntoView() = 0;
};
#endif

Some files were not shown because too many files have changed in this diff Show more