1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsHTMLFrameSetElement.h"
39 : #include "jsapi.h"
40 :
41 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet)
42 :
43 :
44 0 : nsHTMLFrameSetElement::nsHTMLFrameSetElement(already_AddRefed<nsINodeInfo> aNodeInfo)
45 : : nsGenericHTMLElement(aNodeInfo), mNumRows(0), mNumCols(0),
46 0 : mCurrentRowColHint(NS_STYLE_HINT_REFLOW)
47 : {
48 0 : }
49 :
50 0 : nsHTMLFrameSetElement::~nsHTMLFrameSetElement()
51 : {
52 0 : }
53 :
54 :
55 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLFrameSetElement, nsGenericElement)
56 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLFrameSetElement, nsGenericElement)
57 :
58 :
59 0 : DOMCI_NODE_DATA(HTMLFrameSetElement, nsHTMLFrameSetElement)
60 :
61 : // QueryInterface implementation for nsHTMLFrameSetElement
62 0 : NS_INTERFACE_TABLE_HEAD(nsHTMLFrameSetElement)
63 0 : NS_HTML_CONTENT_INTERFACE_TABLE1(nsHTMLFrameSetElement,
64 : nsIDOMHTMLFrameSetElement)
65 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLFrameSetElement,
66 : nsGenericHTMLElement)
67 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLFrameSetElement)
68 :
69 :
70 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLFrameSetElement)
71 :
72 :
73 0 : NS_IMPL_STRING_ATTR(nsHTMLFrameSetElement, Cols, cols)
74 0 : NS_IMPL_STRING_ATTR(nsHTMLFrameSetElement, Rows, rows)
75 :
76 : nsresult
77 0 : nsHTMLFrameSetElement::SetAttr(PRInt32 aNameSpaceID,
78 : nsIAtom* aAttribute,
79 : nsIAtom* aPrefix,
80 : const nsAString& aValue,
81 : bool aNotify)
82 : {
83 : nsresult rv;
84 : /* The main goal here is to see whether the _number_ of rows or
85 : * columns has changed. If it has, we need to reframe; otherwise
86 : * we want to reflow. So we set mCurrentRowColHint here, then call
87 : * nsGenericHTMLElement::SetAttr, which will end up calling
88 : * GetAttributeChangeHint and notifying layout with that hint.
89 : * Once nsGenericHTMLElement::SetAttr returns, we want to go back to our
90 : * normal hint, which is NS_STYLE_HINT_REFLOW.
91 : */
92 0 : if (aAttribute == nsGkAtoms::rows && aNameSpaceID == kNameSpaceID_None) {
93 0 : PRInt32 oldRows = mNumRows;
94 0 : ParseRowCol(aValue, mNumRows, getter_Transfers(mRowSpecs));
95 :
96 0 : if (mNumRows != oldRows) {
97 0 : mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE;
98 0 : }
99 0 : } else if (aAttribute == nsGkAtoms::cols &&
100 : aNameSpaceID == kNameSpaceID_None) {
101 0 : PRInt32 oldCols = mNumCols;
102 0 : ParseRowCol(aValue, mNumCols, getter_Transfers(mColSpecs));
103 :
104 0 : if (mNumCols != oldCols) {
105 0 : mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE;
106 : }
107 : }
108 :
109 : rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, aPrefix,
110 0 : aValue, aNotify);
111 0 : mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
112 :
113 0 : return rv;
114 : }
115 :
116 : nsresult
117 0 : nsHTMLFrameSetElement::GetRowSpec(PRInt32 *aNumValues,
118 : const nsFramesetSpec** aSpecs)
119 : {
120 0 : NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
121 0 : NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
122 0 : *aNumValues = 0;
123 0 : *aSpecs = nsnull;
124 :
125 0 : if (!mRowSpecs) {
126 0 : const nsAttrValue* value = GetParsedAttr(nsGkAtoms::rows);
127 0 : if (value && value->Type() == nsAttrValue::eString) {
128 0 : nsresult rv = ParseRowCol(value->GetStringValue(), mNumRows,
129 0 : getter_Transfers(mRowSpecs));
130 0 : NS_ENSURE_SUCCESS(rv, rv);
131 : }
132 :
133 0 : if (!mRowSpecs) { // we may not have had an attr or had an empty attr
134 0 : mRowSpecs = new nsFramesetSpec[1];
135 0 : if (!mRowSpecs) {
136 0 : mNumRows = 0;
137 0 : return NS_ERROR_OUT_OF_MEMORY;
138 : }
139 0 : mNumRows = 1;
140 0 : mRowSpecs[0].mUnit = eFramesetUnit_Relative;
141 0 : mRowSpecs[0].mValue = 1;
142 : }
143 : }
144 :
145 0 : *aSpecs = mRowSpecs;
146 0 : *aNumValues = mNumRows;
147 0 : return NS_OK;
148 : }
149 :
150 : nsresult
151 0 : nsHTMLFrameSetElement::GetColSpec(PRInt32 *aNumValues,
152 : const nsFramesetSpec** aSpecs)
153 : {
154 0 : NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
155 0 : NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
156 0 : *aNumValues = 0;
157 0 : *aSpecs = nsnull;
158 :
159 0 : if (!mColSpecs) {
160 0 : const nsAttrValue* value = GetParsedAttr(nsGkAtoms::cols);
161 0 : if (value && value->Type() == nsAttrValue::eString) {
162 0 : nsresult rv = ParseRowCol(value->GetStringValue(), mNumCols,
163 0 : getter_Transfers(mColSpecs));
164 0 : NS_ENSURE_SUCCESS(rv, rv);
165 : }
166 :
167 0 : if (!mColSpecs) { // we may not have had an attr or had an empty attr
168 0 : mColSpecs = new nsFramesetSpec[1];
169 0 : if (!mColSpecs) {
170 0 : mNumCols = 0;
171 0 : return NS_ERROR_OUT_OF_MEMORY;
172 : }
173 0 : mNumCols = 1;
174 0 : mColSpecs[0].mUnit = eFramesetUnit_Relative;
175 0 : mColSpecs[0].mValue = 1;
176 : }
177 : }
178 :
179 0 : *aSpecs = mColSpecs;
180 0 : *aNumValues = mNumCols;
181 0 : return NS_OK;
182 : }
183 :
184 :
185 : bool
186 0 : nsHTMLFrameSetElement::ParseAttribute(PRInt32 aNamespaceID,
187 : nsIAtom* aAttribute,
188 : const nsAString& aValue,
189 : nsAttrValue& aResult)
190 : {
191 0 : if (aNamespaceID == kNameSpaceID_None) {
192 0 : if (aAttribute == nsGkAtoms::bordercolor) {
193 0 : return aResult.ParseColor(aValue);
194 : }
195 0 : if (aAttribute == nsGkAtoms::frameborder) {
196 0 : return nsGenericHTMLElement::ParseFrameborderValue(aValue, aResult);
197 : }
198 0 : if (aAttribute == nsGkAtoms::border) {
199 0 : return aResult.ParseIntWithBounds(aValue, 0, 100);
200 : }
201 : }
202 :
203 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
204 0 : aResult);
205 : }
206 :
207 : nsChangeHint
208 0 : nsHTMLFrameSetElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
209 : PRInt32 aModType) const
210 : {
211 : nsChangeHint retval =
212 0 : nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
213 0 : if (aAttribute == nsGkAtoms::rows ||
214 : aAttribute == nsGkAtoms::cols) {
215 0 : NS_UpdateHint(retval, mCurrentRowColHint);
216 : }
217 0 : return retval;
218 : }
219 :
220 : /**
221 : * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
222 : */
223 : nsresult
224 0 : nsHTMLFrameSetElement::ParseRowCol(const nsAString & aValue,
225 : PRInt32& aNumSpecs,
226 : nsFramesetSpec** aSpecs)
227 : {
228 0 : if (aValue.IsEmpty()) {
229 0 : aNumSpecs = 0;
230 0 : *aSpecs = nsnull;
231 0 : return NS_OK;
232 : }
233 :
234 : static const PRUnichar sAster('*');
235 : static const PRUnichar sPercent('%');
236 : static const PRUnichar sComma(',');
237 :
238 0 : nsAutoString spec(aValue);
239 : // remove whitespace (Bug 33699) and quotation marks (bug 224598)
240 : // also remove leading/trailing commas (bug 31482)
241 0 : spec.StripChars(" \n\r\t\"\'");
242 0 : spec.Trim(",");
243 :
244 : // Count the commas. Don't count more than X commas (bug 576447).
245 : PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT * sizeof(nsFramesetSpec) < (1 << 30));
246 0 : PRInt32 commaX = spec.FindChar(sComma);
247 0 : PRInt32 count = 1;
248 0 : while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) {
249 0 : count++;
250 0 : commaX = spec.FindChar(sComma, commaX + 1);
251 : }
252 :
253 0 : nsFramesetSpec* specs = new nsFramesetSpec[count];
254 0 : if (!specs) {
255 0 : *aSpecs = nsnull;
256 0 : aNumSpecs = 0;
257 0 : return NS_ERROR_OUT_OF_MEMORY;
258 : }
259 :
260 : // Pre-grab the compat mode; we may need it later in the loop.
261 0 : bool isInQuirks = InNavQuirksMode(OwnerDoc());
262 :
263 : // Parse each comma separated token
264 :
265 0 : PRInt32 start = 0;
266 0 : PRInt32 specLen = spec.Length();
267 :
268 0 : for (PRInt32 i = 0; i < count; i++) {
269 : // Find our comma
270 0 : commaX = spec.FindChar(sComma, start);
271 0 : NS_ASSERTION(i == count - 1 || commaX != kNotFound,
272 : "Failed to find comma, somehow");
273 0 : PRInt32 end = (commaX == kNotFound) ? specLen : commaX;
274 :
275 : // Note: If end == start then it means that the token has no
276 : // data in it other than a terminating comma (or the end of the spec).
277 : // So default to a fixed width of 0.
278 0 : specs[i].mUnit = eFramesetUnit_Fixed;
279 0 : specs[i].mValue = 0;
280 0 : if (end > start) {
281 0 : PRInt32 numberEnd = end;
282 0 : PRUnichar ch = spec.CharAt(numberEnd - 1);
283 0 : if (sAster == ch) {
284 0 : specs[i].mUnit = eFramesetUnit_Relative;
285 0 : numberEnd--;
286 0 : } else if (sPercent == ch) {
287 0 : specs[i].mUnit = eFramesetUnit_Percent;
288 0 : numberEnd--;
289 : // check for "*%"
290 0 : if (numberEnd > start) {
291 0 : ch = spec.CharAt(numberEnd - 1);
292 0 : if (sAster == ch) {
293 0 : specs[i].mUnit = eFramesetUnit_Relative;
294 0 : numberEnd--;
295 : }
296 : }
297 : }
298 :
299 : // Translate value to an integer
300 0 : nsAutoString token;
301 0 : spec.Mid(token, start, numberEnd - start);
302 :
303 : // Treat * as 1*
304 0 : if ((eFramesetUnit_Relative == specs[i].mUnit) &&
305 0 : (0 == token.Length())) {
306 0 : specs[i].mValue = 1;
307 : }
308 : else {
309 : // Otherwise just convert to integer.
310 : PRInt32 err;
311 0 : specs[i].mValue = token.ToInteger(&err);
312 0 : if (err) {
313 0 : specs[i].mValue = 0;
314 : }
315 : }
316 :
317 : // Treat 0* as 1* in quirks mode (bug 40383)
318 0 : if (isInQuirks) {
319 0 : if ((eFramesetUnit_Relative == specs[i].mUnit) &&
320 0 : (0 == specs[i].mValue)) {
321 0 : specs[i].mValue = 1;
322 : }
323 : }
324 :
325 : // Catch zero and negative frame sizes for Nav compatibility
326 : // Nav resized absolute and relative frames to "1" and
327 : // percent frames to an even percentage of the width
328 : //
329 : //if (isInQuirks && (specs[i].mValue <= 0)) {
330 : // if (eFramesetUnit_Percent == specs[i].mUnit) {
331 : // specs[i].mValue = 100 / count;
332 : // } else {
333 : // specs[i].mValue = 1;
334 : // }
335 : //} else {
336 :
337 : // In standards mode, just set negative sizes to zero
338 0 : if (specs[i].mValue < 0) {
339 0 : specs[i].mValue = 0;
340 : }
341 0 : start = end + 1;
342 : }
343 : }
344 :
345 0 : aNumSpecs = count;
346 : // Transfer ownership to caller here
347 0 : *aSpecs = specs;
348 :
349 0 : return NS_OK;
350 : }
351 :
352 : // Event listener stuff
353 : // FIXME (https://bugzilla.mozilla.org/show_bug.cgi?id=431767)
354 : // nsDocument::GetInnerWindow can return an outer window in some
355 : // cases. We don't want to stick an event listener on an outer
356 : // window, so bail if it does. See also similar code in
357 : // nsGenericHTMLElement::GetEventListenerManagerForAttr.
358 : #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */
359 : #define FORWARDED_EVENT(name_, id_, type_, struct_) \
360 : NS_IMETHODIMP nsHTMLFrameSetElement::GetOn##name_(JSContext *cx, \
361 : jsval *vp) { \
362 : /* XXXbz note to self: add tests for this! */ \
363 : nsPIDOMWindow* win = OwnerDoc()->GetInnerWindow(); \
364 : if (win && win->IsInnerWindow()) { \
365 : nsCOMPtr<nsIInlineEventHandlers> ev = do_QueryInterface(win); \
366 : return ev->GetOn##name_(cx, vp); \
367 : } \
368 : *vp = JSVAL_NULL; \
369 : return NS_OK; \
370 : } \
371 : NS_IMETHODIMP nsHTMLFrameSetElement::SetOn##name_(JSContext *cx, \
372 : const jsval &v) { \
373 : nsPIDOMWindow* win = OwnerDoc()->GetInnerWindow(); \
374 : if (win && win->IsInnerWindow()) { \
375 : nsCOMPtr<nsIInlineEventHandlers> ev = do_QueryInterface(win); \
376 : return ev->SetOn##name_(cx, v); \
377 : } \
378 : return NS_OK; \
379 : }
380 : #define WINDOW_EVENT(name_, id_, type_, struct_) \
381 : NS_IMETHODIMP nsHTMLFrameSetElement::GetOn##name_(JSContext *cx, \
382 : jsval *vp) { \
383 : /* XXXbz note to self: add tests for this! */ \
384 : nsPIDOMWindow* win = OwnerDoc()->GetInnerWindow(); \
385 : if (win && win->IsInnerWindow()) { \
386 : return win->GetOn##name_(cx, vp); \
387 : } \
388 : *vp = JSVAL_NULL; \
389 : return NS_OK; \
390 : } \
391 : NS_IMETHODIMP nsHTMLFrameSetElement::SetOn##name_(JSContext *cx, \
392 : const jsval &v) { \
393 : nsPIDOMWindow* win = OwnerDoc()->GetInnerWindow(); \
394 : if (win && win->IsInnerWindow()) { \
395 : return win->SetOn##name_(cx, v); \
396 : } \
397 : return NS_OK; \
398 : }
399 : #include "nsEventNameList.h"
400 : #undef WINDOW_EVENT
401 : #undef FORWARDED_EVENT
402 : #undef EVENT
|