Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / search / search_tab_helper.cc
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/search/search_tab_helper.h"
6
7 #include <set>
8
9 #include "base/memory/scoped_ptr.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/history/most_visited_tiles_experiment.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search/instant_service.h"
17 #include "chrome/browser/search/instant_service_factory.h"
18 #include "chrome/browser/search/search.h"
19 #include "chrome/browser/signin/signin_manager_factory.h"
20 #include "chrome/browser/ui/app_list/app_list_util.h"
21 #include "chrome/browser/ui/browser_navigator.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/omnibox/location_bar.h"
24 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
25 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
26 #include "chrome/browser/ui/omnibox/omnibox_view.h"
27 #include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
28 #include "chrome/browser/ui/search/search_tab_helper_delegate.h"
29 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
30 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
31 #include "chrome/common/url_constants.h"
32 #include "components/signin/core/browser/signin_manager.h"
33 #include "content/public/browser/navigation_controller.h"
34 #include "content/public/browser/navigation_details.h"
35 #include "content/public/browser/navigation_entry.h"
36 #include "content/public/browser/navigation_type.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/user_metrics.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/common/page_transition_types.h"
43 #include "content/public/common/referrer.h"
44 #include "grit/generated_resources.h"
45 #include "net/base/net_errors.h"
46 #include "ui/base/l10n/l10n_util.h"
47 #include "url/gurl.h"
48
49 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);
50
51 namespace {
52
53 // For reporting Cacheable NTP navigations.
54 enum CacheableNTPLoad {
55   CACHEABLE_NTP_LOAD_FAILED = 0,
56   CACHEABLE_NTP_LOAD_SUCCEEDED = 1,
57   CACHEABLE_NTP_LOAD_MAX = 2
58 };
59
60 void RecordCacheableNTPLoadHistogram(bool succeeded) {
61   UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
62                             succeeded ? CACHEABLE_NTP_LOAD_SUCCEEDED :
63                                 CACHEABLE_NTP_LOAD_FAILED,
64                             CACHEABLE_NTP_LOAD_MAX);
65 }
66
67 bool IsCacheableNTP(const content::WebContents* contents) {
68   const content::NavigationEntry* entry =
69       contents->GetController().GetLastCommittedEntry();
70   return chrome::NavEntryIsInstantNTP(contents, entry) &&
71       entry->GetURL() != GURL(chrome::kChromeSearchLocalNtpUrl);
72 }
73
74 bool IsNTP(const content::WebContents* contents) {
75   // We can't use WebContents::GetURL() because that uses the active entry,
76   // whereas we want the visible entry.
77   const content::NavigationEntry* entry =
78       contents->GetController().GetVisibleEntry();
79   if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL))
80     return true;
81
82   return chrome::IsInstantNTP(contents);
83 }
84
85 bool IsSearchResults(const content::WebContents* contents) {
86   return !chrome::GetSearchTerms(contents).empty();
87 }
88
89 bool IsLocal(const content::WebContents* contents) {
90   if (!contents)
91     return false;
92   const content::NavigationEntry* entry =
93       contents->GetController().GetVisibleEntry();
94   return entry && entry->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl);
95 }
96
97 // Returns true if |contents| are rendered inside an Instant process.
98 bool InInstantProcess(Profile* profile,
99                       const content::WebContents* contents) {
100   if (!profile || !contents)
101     return false;
102
103   InstantService* instant_service =
104       InstantServiceFactory::GetForProfile(profile);
105   return instant_service &&
106       instant_service->IsInstantProcess(
107           contents->GetRenderProcessHost()->GetID());
108 }
109
110 // Called when an NTP finishes loading. If the load start time was noted,
111 // calculates and logs the total load time.
112 void RecordNewTabLoadTime(content::WebContents* contents) {
113   CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
114   if (core_tab_helper->new_tab_start_time().is_null())
115     return;
116
117   base::TimeDelta duration =
118       base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
119   UMA_HISTOGRAM_TIMES("Tab.NewTabOnload", duration);
120   core_tab_helper->set_new_tab_start_time(base::TimeTicks());
121 }
122
123 }  // namespace
124
125 SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
126     : WebContentsObserver(web_contents),
127       is_search_enabled_(chrome::IsInstantExtendedAPIEnabled()),
128       web_contents_(web_contents),
129       ipc_router_(web_contents, this,
130                   make_scoped_ptr(new SearchIPCRouterPolicyImpl(web_contents))
131                       .PassAs<SearchIPCRouter::Policy>()),
132       instant_service_(NULL),
133       delegate_(NULL) {
134   if (!is_search_enabled_)
135     return;
136
137   instant_service_ =
138       InstantServiceFactory::GetForProfile(
139           Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
140   if (instant_service_)
141     instant_service_->AddObserver(this);
142 }
143
144 SearchTabHelper::~SearchTabHelper() {
145   if (instant_service_)
146     instant_service_->RemoveObserver(this);
147 }
148
149 void SearchTabHelper::InitForPreloadedNTP() {
150   UpdateMode(true, true);
151 }
152
153 void SearchTabHelper::OmniboxInputStateChanged() {
154   if (!is_search_enabled_)
155     return;
156
157   UpdateMode(false, false);
158 }
159
160 void SearchTabHelper::OmniboxFocusChanged(OmniboxFocusState state,
161                                           OmniboxFocusChangeReason reason) {
162   content::NotificationService::current()->Notify(
163       chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
164       content::Source<SearchTabHelper>(this),
165       content::NotificationService::NoDetails());
166
167   ipc_router_.OmniboxFocusChanged(state, reason);
168
169   // Don't send oninputstart/oninputend updates in response to focus changes
170   // if there's a navigation in progress. This prevents Chrome from sending
171   // a spurious oninputend when the user accepts a match in the omnibox.
172   if (web_contents_->GetController().GetPendingEntry() == NULL) {
173     ipc_router_.SetInputInProgress(IsInputInProgress());
174   }
175 }
176
177 void SearchTabHelper::NavigationEntryUpdated() {
178   if (!is_search_enabled_)
179     return;
180
181   UpdateMode(false, false);
182 }
183
184 void SearchTabHelper::InstantSupportChanged(bool instant_support) {
185   if (!is_search_enabled_)
186     return;
187
188   InstantSupportState new_state = instant_support ? INSTANT_SUPPORT_YES :
189       INSTANT_SUPPORT_NO;
190
191   model_.SetInstantSupportState(new_state);
192
193   content::NavigationEntry* entry =
194       web_contents_->GetController().GetLastCommittedEntry();
195   if (entry) {
196     chrome::SetInstantSupportStateInNavigationEntry(new_state, entry);
197     if (delegate_ && !instant_support)
198       delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
199   }
200 }
201
202 bool SearchTabHelper::SupportsInstant() const {
203   return model_.instant_support() == INSTANT_SUPPORT_YES;
204 }
205
206 void SearchTabHelper::SetSuggestionToPrefetch(
207     const InstantSuggestion& suggestion) {
208   ipc_router_.SetSuggestionToPrefetch(suggestion);
209 }
210
211 void SearchTabHelper::Submit(const base::string16& text) {
212   ipc_router_.Submit(text);
213 }
214
215 void SearchTabHelper::OnTabActivated() {
216   ipc_router_.OnTabActivated();
217 }
218
219 void SearchTabHelper::OnTabDeactivated() {
220   ipc_router_.OnTabDeactivated();
221 }
222
223 void SearchTabHelper::ToggleVoiceSearch() {
224   ipc_router_.ToggleVoiceSearch();
225 }
226
227 bool SearchTabHelper::IsSearchResultsPage() {
228   return model_.mode().is_origin_search();
229 }
230
231 void SearchTabHelper::RenderViewCreated(
232     content::RenderViewHost* render_view_host) {
233   ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
234 }
235
236 void SearchTabHelper::DidStartNavigationToPendingEntry(
237     const GURL& url,
238     content::NavigationController::ReloadType /* reload_type */) {
239   if (chrome::IsNTPURL(url, profile())) {
240     // Set the title on any pending entry corresponding to the NTP. This
241     // prevents any flickering of the tab title.
242     content::NavigationEntry* entry =
243         web_contents_->GetController().GetPendingEntry();
244     if (entry)
245       entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
246   }
247 }
248
249 void SearchTabHelper::DidNavigateMainFrame(
250     const content::LoadCommittedDetails& details,
251     const content::FrameNavigateParams& params) {
252   if (IsCacheableNTP(web_contents_)) {
253     if (details.http_status_code == 204 || details.http_status_code >= 400) {
254       RedirectToLocalNTP();
255       RecordCacheableNTPLoadHistogram(false);
256       return;
257     }
258     RecordCacheableNTPLoadHistogram(true);
259   }
260
261   // Always set the title on the new tab page to be the one from our UI
262   // resources. Normally, we set the title when we begin a NTP load, but it can
263   // get reset in several places (like when you press Reload). This check
264   // ensures that the title is properly set to the string defined by the Chrome
265   // UI language (rather than the server language) in all cases.
266   //
267   // We only override the title when it's nonempty to allow the page to set the
268   // title if it really wants. An empty title means to use the default. There's
269   // also a race condition between this code and the page's SetTitle call which
270   // this rule avoids.
271   content::NavigationEntry* entry =
272       web_contents_->GetController().GetLastCommittedEntry();
273   if (entry && entry->GetTitle().empty() &&
274       (entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL) ||
275        chrome::NavEntryIsInstantNTP(web_contents_, entry))) {
276     entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
277   }
278 }
279
280 void SearchTabHelper::DidFailProvisionalLoad(
281     int64 /* frame_id */,
282     const base::string16& /* frame_unique_name */,
283     bool is_main_frame,
284     const GURL& validated_url,
285     int error_code,
286     const base::string16& /* error_description */,
287     content::RenderViewHost* /* render_view_host */) {
288   // If error_code is ERR_ABORTED means that the user has canceled this
289   // navigation so it shouldn't be redirected.
290   if (is_main_frame &&
291       error_code != net::ERR_ABORTED &&
292       validated_url != GURL(chrome::kChromeSearchLocalNtpUrl) &&
293       chrome::IsNTPURL(validated_url, profile())) {
294     RedirectToLocalNTP();
295     RecordCacheableNTPLoadHistogram(false);
296   }
297 }
298
299 void SearchTabHelper::DidFinishLoad(
300     int64 /* frame_id */,
301     const GURL&  /* validated_url */,
302     bool is_main_frame,
303     content::RenderViewHost* /* render_view_host */) {
304   if (is_main_frame) {
305     if (chrome::IsInstantNTP(web_contents_))
306       RecordNewTabLoadTime(web_contents_);
307
308     DetermineIfPageSupportsInstant();
309   }
310 }
311
312 void SearchTabHelper::NavigationEntryCommitted(
313     const content::LoadCommittedDetails& load_details) {
314   if (!is_search_enabled_)
315     return;
316
317   if (!load_details.is_main_frame)
318     return;
319
320   if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
321                                                profile())) {
322     InstantService* instant_service =
323         InstantServiceFactory::GetForProfile(profile());
324     ipc_router_.SetOmniboxStartMargin(instant_service->omnibox_start_margin());
325     ipc_router_.SetDisplayInstantResults();
326   }
327
328   UpdateMode(true, false);
329
330   content::NavigationEntry* entry =
331       web_contents_->GetController().GetVisibleEntry();
332   DCHECK(entry);
333
334   // Already determined the instant support state for this page, do not reset
335   // the instant support state.
336   //
337   // When we get a navigation entry committed event, there seem to be two ways
338   // to tell whether the navigation was "in-page". Ideally, when
339   // LoadCommittedDetails::is_in_page is true, we should have
340   // LoadCommittedDetails::type to be NAVIGATION_TYPE_IN_PAGE. Unfortunately,
341   // they are different in some cases. To workaround this bug, we are checking
342   // (is_in_page || type == NAVIGATION_TYPE_IN_PAGE). Please refer to
343   // crbug.com/251330 for more details.
344   if (load_details.is_in_page ||
345       load_details.type == content::NAVIGATION_TYPE_IN_PAGE) {
346     // When an "in-page" navigation happens, we will not receive a
347     // DidFinishLoad() event. Therefore, we will not determine the Instant
348     // support for the navigated page. So, copy over the Instant support from
349     // the previous entry. If the page does not support Instant, update the
350     // location bar from here to turn off search terms replacement.
351     chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
352                                                     entry);
353     if (delegate_ && model_.instant_support() == INSTANT_SUPPORT_NO)
354       delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
355     return;
356   }
357
358   model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
359   model_.SetVoiceSearchSupported(false);
360   chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
361                                                   entry);
362 }
363
364 void SearchTabHelper::OnInstantSupportDetermined(bool supports_instant) {
365   InstantSupportChanged(supports_instant);
366 }
367
368 void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
369   model_.SetVoiceSearchSupported(supports_voice_search);
370 }
371
372 void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
373   ipc_router_.SendThemeBackgroundInfo(theme_info);
374 }
375
376 void SearchTabHelper::MostVisitedItemsChanged(
377     const std::vector<InstantMostVisitedItem>& items) {
378   std::vector<InstantMostVisitedItem> items_copy(items);
379   MaybeRemoveMostVisitedItems(&items_copy);
380   ipc_router_.SendMostVisitedItems(items_copy);
381 }
382
383 void SearchTabHelper::OmniboxStartMarginChanged(int omnibox_start_margin) {
384   ipc_router_.SetOmniboxStartMargin(omnibox_start_margin);
385 }
386
387 void SearchTabHelper::MaybeRemoveMostVisitedItems(
388     std::vector<InstantMostVisitedItem>* items) {
389   if (!delegate_)
390     return;
391
392   if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
393     return;
394
395   history::MostVisitedTilesExperiment::RemoveItemsMatchingOpenTabs(
396       delegate_->GetOpenUrls(), items);
397 }
398
399 void SearchTabHelper::FocusOmnibox(OmniboxFocusState state) {
400 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
401 #if !defined(OS_ANDROID)
402   OmniboxView* omnibox = GetOmniboxView();
403   if (!omnibox)
404     return;
405
406   // Do not add a default case in the switch block for the following reasons:
407   // (1) Explicitly handle the new states. If new states are added in the
408   // OmniboxFocusState, the compiler will warn the developer to handle the new
409   // states.
410   // (2) An attacker may control the renderer and sends the browser process a
411   // malformed IPC. This function responds to the invalid |state| values by
412   // doing nothing instead of crashing the browser process (intentional no-op).
413   switch (state) {
414     case OMNIBOX_FOCUS_VISIBLE:
415       omnibox->SetFocus();
416       omnibox->model()->SetCaretVisibility(true);
417       break;
418     case OMNIBOX_FOCUS_INVISIBLE:
419       omnibox->SetFocus();
420       omnibox->model()->SetCaretVisibility(false);
421       // If the user clicked on the fakebox, any text already in the omnibox
422       // should get cleared when they start typing. Selecting all the existing
423       // text is a convenient way to accomplish this. It also gives a slight
424       // visual cue to users who really understand selection state about what
425       // will happen if they start typing.
426       omnibox->SelectAll(false);
427       omnibox->ShowImeIfNeeded();
428       break;
429     case OMNIBOX_FOCUS_NONE:
430       // Remove focus only if the popup is closed. This will prevent someone
431       // from changing the omnibox value and closing the popup without user
432       // interaction.
433       if (!omnibox->model()->popup_model()->IsOpen())
434         web_contents()->Focus();
435       break;
436   }
437 #endif
438 }
439
440 void SearchTabHelper::NavigateToURL(const GURL& url,
441                                     WindowOpenDisposition disposition,
442                                     bool is_most_visited_item_url) {
443   if (is_most_visited_item_url) {
444     content::RecordAction(
445         base::UserMetricsAction("InstantExtended.MostVisitedClicked"));
446   }
447
448   if (delegate_)
449     delegate_->NavigateOnThumbnailClick(url, disposition, web_contents_);
450 }
451
452 void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
453   DCHECK(!url.is_empty());
454   if (instant_service_)
455     instant_service_->DeleteMostVisitedItem(url);
456 }
457
458 void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
459   DCHECK(!url.is_empty());
460   if (instant_service_)
461     instant_service_->UndoMostVisitedDeletion(url);
462 }
463
464 void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
465   if (instant_service_)
466     instant_service_->UndoAllMostVisitedDeletions();
467 }
468
469 void SearchTabHelper::OnLogEvent(NTPLoggingEventType event) {
470 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
471 #if !defined(OS_ANDROID)
472   NTPUserDataLogger::GetOrCreateFromWebContents(
473       web_contents())->LogEvent(event);
474 #endif
475 }
476
477 void SearchTabHelper::OnLogMostVisitedImpression(
478     int position, const base::string16& provider) {
479 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
480 #if !defined(OS_ANDROID)
481   NTPUserDataLogger::GetOrCreateFromWebContents(
482       web_contents())->LogMostVisitedImpression(position, provider);
483 #endif
484 }
485
486 void SearchTabHelper::OnLogMostVisitedNavigation(
487     int position, const base::string16& provider) {
488 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
489 #if !defined(OS_ANDROID)
490   NTPUserDataLogger::GetOrCreateFromWebContents(
491       web_contents())->LogMostVisitedNavigation(position, provider);
492 #endif
493 }
494
495 void SearchTabHelper::PasteIntoOmnibox(const base::string16& text) {
496 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
497 #if !defined(OS_ANDROID)
498   OmniboxView* omnibox = GetOmniboxView();
499   if (!omnibox)
500     return;
501   // The first case is for right click to paste, where the text is retrieved
502   // from the clipboard already sanitized. The second case is needed to handle
503   // drag-and-drop value and it has to be sanitazed before setting it into the
504   // omnibox.
505   base::string16 text_to_paste = text.empty() ? omnibox->GetClipboardText() :
506       omnibox->SanitizeTextForPaste(text);
507
508   if (text_to_paste.empty())
509     return;
510
511   if (!omnibox->model()->has_focus())
512     omnibox->SetFocus();
513
514   omnibox->OnBeforePossibleChange();
515   omnibox->model()->OnPaste();
516   omnibox->SetUserText(text_to_paste);
517   omnibox->OnAfterPossibleChange();
518 #endif
519 }
520
521 void SearchTabHelper::OnChromeIdentityCheck(const base::string16& identity) {
522   SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile());
523   if (manager) {
524     const base::string16 username =
525         base::UTF8ToUTF16(manager->GetAuthenticatedUsername());
526     ipc_router_.SendChromeIdentityCheckResult(identity,
527                                               identity == username);
528   }
529 }
530
531 void SearchTabHelper::UpdateMode(bool update_origin, bool is_preloaded_ntp) {
532   SearchMode::Type type = SearchMode::MODE_DEFAULT;
533   SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT;
534   if (IsNTP(web_contents_) || is_preloaded_ntp) {
535     type = SearchMode::MODE_NTP;
536     origin = SearchMode::ORIGIN_NTP;
537   } else if (IsSearchResults(web_contents_)) {
538     type = SearchMode::MODE_SEARCH_RESULTS;
539     origin = SearchMode::ORIGIN_SEARCH;
540   }
541   if (!update_origin)
542     origin = model_.mode().origin;
543
544   OmniboxView* omnibox = GetOmniboxView();
545   if (omnibox && omnibox->model()->user_input_in_progress())
546     type = SearchMode::MODE_SEARCH_SUGGESTIONS;
547
548   SearchMode old_mode(model_.mode());
549   model_.SetMode(SearchMode(type, origin));
550   if (old_mode.is_ntp() != model_.mode().is_ntp()) {
551     ipc_router_.SetInputInProgress(IsInputInProgress());
552   }
553 }
554
555 void SearchTabHelper::DetermineIfPageSupportsInstant() {
556   if (!InInstantProcess(profile(), web_contents_)) {
557     // The page is not in the Instant process. This page does not support
558     // instant. If we send an IPC message to a page that is not in the Instant
559     // process, it will never receive it and will never respond. Therefore,
560     // return immediately.
561     InstantSupportChanged(false);
562   } else if (IsLocal(web_contents_)) {
563     // Local pages always support Instant.
564     InstantSupportChanged(true);
565   } else {
566     ipc_router_.DetermineIfPageSupportsInstant();
567   }
568 }
569
570 Profile* SearchTabHelper::profile() const {
571   return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
572 }
573
574 void SearchTabHelper::RedirectToLocalNTP() {
575   // Extra parentheses to declare a variable.
576   content::NavigationController::LoadURLParams load_params(
577       (GURL(chrome::kChromeSearchLocalNtpUrl)));
578   load_params.referrer = content::Referrer();
579   load_params.transition_type = content::PAGE_TRANSITION_SERVER_REDIRECT;
580   // Don't push a history entry.
581   load_params.should_replace_current_entry = true;
582   web_contents_->GetController().LoadURLWithParams(load_params);
583 }
584
585 bool SearchTabHelper::IsInputInProgress() const {
586   OmniboxView* omnibox = GetOmniboxView();
587   return !model_.mode().is_ntp() && omnibox &&
588       omnibox->model()->focus_state() == OMNIBOX_FOCUS_VISIBLE;
589 }
590
591 OmniboxView* SearchTabHelper::GetOmniboxView() const {
592   return delegate_ ? delegate_->GetOmniboxView() : NULL;
593 }