1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : // vim:cindent:tabstop=4:expandtab:shiftwidth=4:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is layout debugging code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 2002
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org> (original author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsLayoutDebuggingTools.h"
41 :
42 : #include "nsIDocShell.h"
43 : #include "nsIDocShellTreeNode.h"
44 : #include "nsIDocShellTreeItem.h"
45 : #include "nsPIDOMWindow.h"
46 : #include "nsIContentViewer.h"
47 :
48 : #include "nsIServiceManager.h"
49 : #include "nsIAtom.h"
50 : #include "nsQuickSort.h"
51 :
52 : #include "nsIContent.h"
53 : #include "nsIDocument.h"
54 : #include "nsIDOMDocument.h"
55 :
56 : #include "nsIPresShell.h"
57 : #include "nsIViewManager.h"
58 : #include "nsIFrame.h"
59 :
60 : #include "nsILayoutDebugger.h"
61 : #include "nsLayoutCID.h"
62 : static NS_DEFINE_CID(kLayoutDebuggerCID, NS_LAYOUT_DEBUGGER_CID);
63 :
64 : #include "nsISelectionController.h"
65 : #include "mozilla/dom/Element.h"
66 : #include "mozilla/Preferences.h"
67 :
68 : using namespace mozilla;
69 :
70 : static already_AddRefed<nsIContentViewer>
71 0 : doc_viewer(nsIDocShell *aDocShell)
72 : {
73 0 : if (!aDocShell)
74 0 : return nsnull;
75 0 : nsIContentViewer *result = nsnull;
76 0 : aDocShell->GetContentViewer(&result);
77 0 : return result;
78 : }
79 :
80 : static already_AddRefed<nsIPresShell>
81 0 : pres_shell(nsIDocShell *aDocShell)
82 : {
83 0 : nsCOMPtr<nsIContentViewer> cv = doc_viewer(aDocShell);
84 0 : if (!cv)
85 0 : return nsnull;
86 0 : nsCOMPtr<nsIPresShell> result;
87 0 : cv->GetPresShell(getter_AddRefs(result));
88 0 : return result.forget();
89 : }
90 :
91 : static nsIViewManager*
92 0 : view_manager(nsIDocShell *aDocShell)
93 : {
94 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
95 0 : if (!shell)
96 0 : return nsnull;
97 0 : return shell->GetViewManager();
98 : }
99 :
100 : #ifdef DEBUG
101 : static already_AddRefed<nsIDocument>
102 0 : document(nsIDocShell *aDocShell)
103 : {
104 0 : nsCOMPtr<nsIContentViewer> cv(doc_viewer(aDocShell));
105 0 : if (!cv)
106 0 : return nsnull;
107 0 : nsCOMPtr<nsIDOMDocument> domDoc;
108 0 : cv->GetDOMDocument(getter_AddRefs(domDoc));
109 0 : if (!domDoc)
110 0 : return nsnull;
111 0 : nsIDocument *result = nsnull;
112 0 : CallQueryInterface(domDoc, &result);
113 0 : return result;
114 : }
115 : #endif
116 :
117 0 : nsLayoutDebuggingTools::nsLayoutDebuggingTools()
118 : : mPaintFlashing(false),
119 : mPaintDumping(false),
120 : mInvalidateDumping(false),
121 : mEventDumping(false),
122 : mMotionEventDumping(false),
123 : mCrossingEventDumping(false),
124 0 : mReflowCounts(false)
125 : {
126 0 : NewURILoaded();
127 0 : }
128 :
129 0 : nsLayoutDebuggingTools::~nsLayoutDebuggingTools()
130 : {
131 0 : }
132 :
133 0 : NS_IMPL_ISUPPORTS1(nsLayoutDebuggingTools, nsILayoutDebuggingTools)
134 :
135 : NS_IMETHODIMP
136 0 : nsLayoutDebuggingTools::Init(nsIDOMWindow *aWin)
137 : {
138 0 : if (!Preferences::GetService()) {
139 0 : return NS_ERROR_UNEXPECTED;
140 : }
141 :
142 : {
143 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWin);
144 0 : if (!window)
145 0 : return NS_ERROR_UNEXPECTED;
146 0 : mDocShell = window->GetDocShell();
147 : }
148 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_UNEXPECTED);
149 :
150 : mPaintFlashing =
151 0 : Preferences::GetBool("nglayout.debug.paint_flashing", mPaintFlashing);
152 : mPaintDumping =
153 0 : Preferences::GetBool("nglayout.debug.paint_dumping", mPaintDumping);
154 : mInvalidateDumping =
155 0 : Preferences::GetBool("nglayout.debug.invalidate_dumping", mInvalidateDumping);
156 : mEventDumping =
157 0 : Preferences::GetBool("nglayout.debug.event_dumping", mEventDumping);
158 : mMotionEventDumping =
159 : Preferences::GetBool("nglayout.debug.motion_event_dumping",
160 0 : mMotionEventDumping);
161 : mCrossingEventDumping =
162 : Preferences::GetBool("nglayout.debug.crossing_event_dumping",
163 0 : mCrossingEventDumping);
164 : mReflowCounts =
165 0 : Preferences::GetBool("layout.reflow.showframecounts", mReflowCounts);
166 :
167 : {
168 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
169 0 : if (ld) {
170 0 : ld->GetShowFrameBorders(&mVisualDebugging);
171 0 : ld->GetShowEventTargetFrameBorder(&mVisualEventDebugging);
172 : }
173 : }
174 :
175 0 : return NS_OK;
176 : }
177 :
178 : NS_IMETHODIMP
179 0 : nsLayoutDebuggingTools::NewURILoaded()
180 : {
181 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
182 : // Reset all the state that should be reset between pages.
183 :
184 : // XXX Some of these should instead be transferred between pages!
185 0 : mEditorMode = false;
186 0 : mVisualDebugging = false;
187 0 : mVisualEventDebugging = false;
188 :
189 0 : mReflowCounts = false;
190 :
191 0 : ForceRefresh();
192 0 : return NS_OK;
193 : }
194 :
195 : NS_IMETHODIMP
196 0 : nsLayoutDebuggingTools::GetVisualDebugging(bool *aVisualDebugging)
197 : {
198 0 : *aVisualDebugging = mVisualDebugging;
199 0 : return NS_OK;
200 : }
201 :
202 : NS_IMETHODIMP
203 0 : nsLayoutDebuggingTools::SetVisualDebugging(bool aVisualDebugging)
204 : {
205 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
206 0 : if (!ld)
207 0 : return NS_ERROR_UNEXPECTED;
208 0 : mVisualDebugging = aVisualDebugging;
209 0 : ld->SetShowFrameBorders(aVisualDebugging);
210 0 : ForceRefresh();
211 0 : return NS_OK;
212 : }
213 :
214 : NS_IMETHODIMP
215 0 : nsLayoutDebuggingTools::GetVisualEventDebugging(bool *aVisualEventDebugging)
216 : {
217 0 : *aVisualEventDebugging = mVisualEventDebugging;
218 0 : return NS_OK;
219 : }
220 :
221 : NS_IMETHODIMP
222 0 : nsLayoutDebuggingTools::SetVisualEventDebugging(bool aVisualEventDebugging)
223 : {
224 0 : nsCOMPtr<nsILayoutDebugger> ld = do_GetService(kLayoutDebuggerCID);
225 0 : if (!ld)
226 0 : return NS_ERROR_UNEXPECTED;
227 0 : mVisualEventDebugging = aVisualEventDebugging;
228 0 : ld->SetShowEventTargetFrameBorder(aVisualEventDebugging);
229 0 : ForceRefresh();
230 0 : return NS_OK;
231 : }
232 :
233 : NS_IMETHODIMP
234 0 : nsLayoutDebuggingTools::GetPaintFlashing(bool *aPaintFlashing)
235 : {
236 0 : *aPaintFlashing = mPaintFlashing;
237 0 : return NS_OK;
238 : }
239 :
240 : NS_IMETHODIMP
241 0 : nsLayoutDebuggingTools::SetPaintFlashing(bool aPaintFlashing)
242 : {
243 0 : mPaintFlashing = aPaintFlashing;
244 0 : return SetBoolPrefAndRefresh("nglayout.debug.paint_flashing", mPaintFlashing);
245 : }
246 :
247 : NS_IMETHODIMP
248 0 : nsLayoutDebuggingTools::GetPaintDumping(bool *aPaintDumping)
249 : {
250 0 : *aPaintDumping = mPaintDumping;
251 0 : return NS_OK;
252 : }
253 :
254 : NS_IMETHODIMP
255 0 : nsLayoutDebuggingTools::SetPaintDumping(bool aPaintDumping)
256 : {
257 0 : mPaintDumping = aPaintDumping;
258 0 : return SetBoolPrefAndRefresh("nglayout.debug.paint_dumping", mPaintDumping);
259 : }
260 :
261 : NS_IMETHODIMP
262 0 : nsLayoutDebuggingTools::GetInvalidateDumping(bool *aInvalidateDumping)
263 : {
264 0 : *aInvalidateDumping = mInvalidateDumping;
265 0 : return NS_OK;
266 : }
267 :
268 : NS_IMETHODIMP
269 0 : nsLayoutDebuggingTools::SetInvalidateDumping(bool aInvalidateDumping)
270 : {
271 0 : mInvalidateDumping = aInvalidateDumping;
272 0 : return SetBoolPrefAndRefresh("nglayout.debug.invalidate_dumping", mInvalidateDumping);
273 : }
274 :
275 : NS_IMETHODIMP
276 0 : nsLayoutDebuggingTools::GetEventDumping(bool *aEventDumping)
277 : {
278 0 : *aEventDumping = mEventDumping;
279 0 : return NS_OK;
280 : }
281 :
282 : NS_IMETHODIMP
283 0 : nsLayoutDebuggingTools::SetEventDumping(bool aEventDumping)
284 : {
285 0 : mEventDumping = aEventDumping;
286 0 : return SetBoolPrefAndRefresh("nglayout.debug.event_dumping", mEventDumping);
287 : }
288 :
289 : NS_IMETHODIMP
290 0 : nsLayoutDebuggingTools::GetMotionEventDumping(bool *aMotionEventDumping)
291 : {
292 0 : *aMotionEventDumping = mMotionEventDumping;
293 0 : return NS_OK;
294 : }
295 :
296 : NS_IMETHODIMP
297 0 : nsLayoutDebuggingTools::SetMotionEventDumping(bool aMotionEventDumping)
298 : {
299 0 : mMotionEventDumping = aMotionEventDumping;
300 0 : return SetBoolPrefAndRefresh("nglayout.debug.motion_event_dumping", mMotionEventDumping);
301 : }
302 :
303 : NS_IMETHODIMP
304 0 : nsLayoutDebuggingTools::GetCrossingEventDumping(bool *aCrossingEventDumping)
305 : {
306 0 : *aCrossingEventDumping = mCrossingEventDumping;
307 0 : return NS_OK;
308 : }
309 :
310 : NS_IMETHODIMP
311 0 : nsLayoutDebuggingTools::SetCrossingEventDumping(bool aCrossingEventDumping)
312 : {
313 0 : mCrossingEventDumping = aCrossingEventDumping;
314 0 : return SetBoolPrefAndRefresh("nglayout.debug.crossing_event_dumping", mCrossingEventDumping);
315 : }
316 :
317 : NS_IMETHODIMP
318 0 : nsLayoutDebuggingTools::GetReflowCounts(bool* aShow)
319 : {
320 0 : *aShow = mReflowCounts;
321 0 : return NS_OK;
322 : }
323 :
324 : NS_IMETHODIMP
325 0 : nsLayoutDebuggingTools::SetReflowCounts(bool aShow)
326 : {
327 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
328 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
329 0 : if (shell) {
330 : #ifdef MOZ_REFLOW_PERF
331 0 : shell->SetPaintFrameCount(aShow);
332 0 : SetBoolPrefAndRefresh("layout.reflow.showframecounts", aShow);
333 0 : mReflowCounts = aShow;
334 : #else
335 : printf("************************************************\n");
336 : printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
337 : printf("************************************************\n");
338 : #endif
339 : }
340 0 : return NS_OK;
341 : }
342 :
343 0 : static void DumpAWebShell(nsIDocShellTreeItem* aShellItem, FILE* out, PRInt32 aIndent)
344 : {
345 0 : nsXPIDLString name;
346 0 : nsCOMPtr<nsIDocShellTreeItem> parent;
347 : PRInt32 i, n;
348 :
349 0 : for (i = aIndent; --i >= 0; )
350 0 : fprintf(out, " ");
351 :
352 0 : fprintf(out, "%p '", static_cast<void*>(aShellItem));
353 0 : aShellItem->GetName(getter_Copies(name));
354 0 : aShellItem->GetSameTypeParent(getter_AddRefs(parent));
355 0 : fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
356 0 : fprintf(out, "' parent=%p <\n", static_cast<void*>(parent));
357 :
358 0 : ++aIndent;
359 0 : nsCOMPtr<nsIDocShellTreeNode> shellAsNode(do_QueryInterface(aShellItem));
360 0 : shellAsNode->GetChildCount(&n);
361 0 : for (i = 0; i < n; ++i) {
362 0 : nsCOMPtr<nsIDocShellTreeItem> child;
363 0 : shellAsNode->GetChildAt(i, getter_AddRefs(child));
364 0 : if (child) {
365 0 : DumpAWebShell(child, out, aIndent);
366 : }
367 : }
368 0 : --aIndent;
369 0 : for (i = aIndent; --i >= 0; )
370 0 : fprintf(out, " ");
371 0 : fputs(">\n", out);
372 0 : }
373 :
374 : NS_IMETHODIMP
375 0 : nsLayoutDebuggingTools::DumpWebShells()
376 : {
377 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
378 0 : nsCOMPtr<nsIDocShellTreeItem> shellAsItem(do_QueryInterface(mDocShell));
379 0 : DumpAWebShell(shellAsItem, stdout, 0);
380 0 : return NS_OK;
381 : }
382 :
383 : static
384 : void
385 0 : DumpContentRecur(nsIDocShell* aDocShell, FILE* out)
386 : {
387 : #ifdef DEBUG
388 0 : if (nsnull != aDocShell) {
389 0 : fprintf(out, "docshell=%p \n", static_cast<void*>(aDocShell));
390 0 : nsCOMPtr<nsIDocument> doc(document(aDocShell));
391 0 : if (doc) {
392 0 : dom::Element *root = doc->GetRootElement();
393 0 : if (root) {
394 0 : root->List(out);
395 : }
396 : }
397 : else {
398 0 : fputs("no document\n", out);
399 : }
400 : // dump the frames of the sub documents
401 : PRInt32 i, n;
402 0 : nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
403 0 : docShellAsNode->GetChildCount(&n);
404 0 : for (i = 0; i < n; ++i) {
405 0 : nsCOMPtr<nsIDocShellTreeItem> child;
406 0 : docShellAsNode->GetChildAt(i, getter_AddRefs(child));
407 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
408 0 : if (child) {
409 0 : DumpContentRecur(childAsShell, out);
410 : }
411 : }
412 : }
413 : #endif
414 0 : }
415 :
416 : NS_IMETHODIMP
417 0 : nsLayoutDebuggingTools::DumpContent()
418 : {
419 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
420 0 : DumpContentRecur(mDocShell, stdout);
421 0 : return NS_OK;
422 : }
423 :
424 : static void
425 0 : DumpFramesRecur(nsIDocShell* aDocShell, FILE* out)
426 : {
427 : #ifdef DEBUG
428 0 : fprintf(out, "webshell=%p \n", static_cast<void*>(aDocShell));
429 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
430 0 : if (shell) {
431 0 : nsIFrame* root = shell->GetRootFrame();
432 0 : if (root) {
433 0 : root->List(out, 0);
434 : }
435 : }
436 : else {
437 0 : fputs("null pres shell\n", out);
438 : }
439 :
440 : // dump the frames of the sub documents
441 : PRInt32 i, n;
442 0 : nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
443 0 : docShellAsNode->GetChildCount(&n);
444 0 : for (i = 0; i < n; ++i) {
445 0 : nsCOMPtr<nsIDocShellTreeItem> child;
446 0 : docShellAsNode->GetChildAt(i, getter_AddRefs(child));
447 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
448 0 : if (childAsShell) {
449 0 : DumpFramesRecur(childAsShell, out);
450 : }
451 : }
452 : #endif
453 0 : }
454 :
455 : NS_IMETHODIMP
456 0 : nsLayoutDebuggingTools::DumpFrames()
457 : {
458 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
459 0 : DumpFramesRecur(mDocShell, stdout);
460 0 : return NS_OK;
461 : }
462 :
463 : static
464 : void
465 0 : DumpViewsRecur(nsIDocShell* aDocShell, FILE* out)
466 : {
467 : #ifdef DEBUG
468 0 : fprintf(out, "docshell=%p \n", static_cast<void*>(aDocShell));
469 0 : nsCOMPtr<nsIViewManager> vm(view_manager(aDocShell));
470 0 : if (vm) {
471 0 : nsIView* root = vm->GetRootView();
472 0 : if (root) {
473 0 : root->List(out);
474 : }
475 : }
476 : else {
477 0 : fputs("null view manager\n", out);
478 : }
479 :
480 : // dump the views of the sub documents
481 : PRInt32 i, n;
482 0 : nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(aDocShell));
483 0 : docShellAsNode->GetChildCount(&n);
484 0 : for (i = 0; i < n; i++) {
485 0 : nsCOMPtr<nsIDocShellTreeItem> child;
486 0 : docShellAsNode->GetChildAt(i, getter_AddRefs(child));
487 0 : nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
488 0 : if (childAsShell) {
489 0 : DumpViewsRecur(childAsShell, out);
490 : }
491 : }
492 : #endif // DEBUG
493 0 : }
494 :
495 : NS_IMETHODIMP
496 0 : nsLayoutDebuggingTools::DumpViews()
497 : {
498 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
499 0 : DumpViewsRecur(mDocShell, stdout);
500 0 : return NS_OK;
501 : }
502 :
503 : NS_IMETHODIMP
504 0 : nsLayoutDebuggingTools::DumpStyleSheets()
505 : {
506 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
507 : #ifdef DEBUG
508 0 : FILE *out = stdout;
509 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
510 0 : if (shell)
511 0 : shell->ListStyleSheets(out);
512 : else
513 0 : fputs("null pres shell\n", out);
514 : #endif
515 0 : return NS_OK;
516 : }
517 :
518 : NS_IMETHODIMP
519 0 : nsLayoutDebuggingTools::DumpStyleContexts()
520 : {
521 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
522 : #ifdef DEBUG
523 0 : FILE *out = stdout;
524 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
525 0 : if (shell) {
526 0 : nsIFrame* root = shell->GetRootFrame();
527 0 : if (!root) {
528 0 : fputs("null root frame\n", out);
529 : } else {
530 0 : shell->ListStyleContexts(root, out);
531 : }
532 : } else {
533 0 : fputs("null pres shell\n", out);
534 : }
535 : #endif
536 0 : return NS_OK;
537 : }
538 :
539 : NS_IMETHODIMP
540 0 : nsLayoutDebuggingTools::DumpReflowStats()
541 : {
542 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
543 : #ifdef DEBUG
544 0 : nsCOMPtr<nsIPresShell> shell(pres_shell(mDocShell));
545 0 : if (shell) {
546 : #ifdef MOZ_REFLOW_PERF
547 0 : shell->DumpReflows();
548 : #else
549 : printf("************************************************\n");
550 : printf("Sorry, you have not built with MOZ_REFLOW_PERF=1\n");
551 : printf("************************************************\n");
552 : #endif
553 : }
554 : #endif
555 0 : return NS_OK;
556 : }
557 :
558 0 : void nsLayoutDebuggingTools::ForceRefresh()
559 : {
560 0 : nsCOMPtr<nsIViewManager> vm(view_manager(mDocShell));
561 0 : if (!vm)
562 : return;
563 0 : nsIView* root = vm->GetRootView();
564 0 : if (root) {
565 0 : vm->InvalidateView(root);
566 : }
567 : }
568 :
569 : nsresult
570 0 : nsLayoutDebuggingTools::SetBoolPrefAndRefresh(const char * aPrefName,
571 : bool aNewVal)
572 : {
573 0 : NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
574 :
575 0 : nsIPrefService* prefService = Preferences::GetService();
576 0 : NS_ENSURE_TRUE(prefService && aPrefName, NS_OK);
577 :
578 0 : Preferences::SetBool(aPrefName, aNewVal);
579 0 : prefService->SavePrefFile(nsnull);
580 :
581 0 : ForceRefresh();
582 :
583 0 : return NS_OK;
584 : }
|