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
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/
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
15 * The Original Code is Mozilla Communicator client code.
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.
23 * Pierre Phaneuf <pp@ludusdesign.com>
24 * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or 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.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsIAppShellService.h"
42 #include "nsISupportsArray.h"
43 #include "nsIComponentManager.h"
45 #include "nsNetUtil.h"
46 #include "nsIServiceManager.h"
47 #include "nsIObserverService.h"
48 #include "nsIObserver.h"
49 #include "nsIXPConnect.h"
50 #include "nsIJSContextStack.h"
51 #include "nsIPrefBranch.h"
52 #include "nsIPrefService.h"
54 #include "nsIWindowMediator.h"
55 #include "nsIWindowWatcher.h"
56 #include "nsPIWindowWatcher.h"
57 #include "nsIDOMWindowInternal.h"
58 #include "nsWebShellWindow.h"
60 #include "nsIEnumerator.h"
62 #include "nsITimelineService.h"
65 #include "nsWidgetsCID.h"
66 #include "nsIRequestObserver.h"
68 /* For implementing GetHiddenWindowAndJSContext */
69 #include "nsIScriptGlobalObject.h"
70 #include "nsIScriptContext.h"
73 #include "nsAppShellService.h"
74 #include "nsISupportsPrimitives.h"
75 #include "nsIPlatformCharset.h"
76 #include "nsICharsetConverterManager.h"
77 #include "nsIUnicodeDecoder.h"
78 #include "nsIChromeRegistry.h"
80 // Default URL for the hidden window, can be overridden by a pref on Mac
81 #define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
85 nsAppShellService::nsAppShellService() :
86 mXPCOMWillShutDown(PR_FALSE
),
87 mXPCOMShuttingDown(PR_FALSE
),
89 mApplicationProvidedHiddenWindow(PR_FALSE
)
91 nsCOMPtr
<nsIObserverService
> obs
92 (do_GetService("@mozilla.org/observer-service;1"));
95 obs
->AddObserver(this, "xpcom-will-shutdown", PR_FALSE
);
96 obs
->AddObserver(this, "xpcom-shutdown", PR_FALSE
);
100 nsAppShellService::~nsAppShellService()
106 * Implement the nsISupports methods...
108 NS_IMPL_ISUPPORTS2(nsAppShellService
,
113 nsAppShellService::SetXPConnectSafeContext()
117 nsCOMPtr
<nsIThreadJSContextStack
> cxstack
=
118 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv
);
119 NS_ENSURE_SUCCESS(rv
, rv
);
121 nsCOMPtr
<nsIDOMWindowInternal
> junk
;
123 rv
= GetHiddenWindowAndJSContext(getter_AddRefs(junk
), &cx
);
124 NS_ENSURE_SUCCESS(rv
, rv
);
126 return cxstack
->SetSafeJSContext(cx
);
129 nsresult
nsAppShellService::ClearXPConnectSafeContext()
133 nsCOMPtr
<nsIThreadJSContextStack
> cxstack
=
134 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv
);
136 NS_ERROR("XPConnect ContextStack gone before XPCOM shutdown?");
140 nsCOMPtr
<nsIDOMWindowInternal
> junk
;
142 rv
= GetHiddenWindowAndJSContext(getter_AddRefs(junk
), &cx
);
143 NS_ENSURE_SUCCESS(rv
, rv
);
146 rv
= cxstack
->GetSafeJSContext(&safe_cx
);
147 NS_ENSURE_SUCCESS(rv
, rv
);
150 rv
= cxstack
->SetSafeJSContext(nsnull
);
156 nsAppShellService::CreateHiddenWindow(nsIAppShell
* aAppShell
)
159 PRInt32 initialHeight
= 100, initialWidth
= 100;
162 PRUint32 chromeMask
= 0;
163 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
164 nsCOMPtr
<nsIPrefService
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
165 prefs
->GetBranch(nsnull
, getter_AddRefs(prefBranch
));
166 nsXPIDLCString prefVal
;
167 rv
= prefBranch
->GetCharPref("browser.hiddenWindowChromeURL", getter_Copies(prefVal
));
168 const char* hiddenWindowURL
= prefVal
.get() ? prefVal
.get() : DEFAULT_HIDDENWINDOW_URL
;
169 mApplicationProvidedHiddenWindow
= prefVal
.get() ? PR_TRUE
: PR_FALSE
;
171 static const char hiddenWindowURL
[] = DEFAULT_HIDDENWINDOW_URL
;
172 PRUint32 chromeMask
= nsIWebBrowserChrome::CHROME_ALL
;
175 nsCOMPtr
<nsIURI
> url
;
176 rv
= NS_NewURI(getter_AddRefs(url
), hiddenWindowURL
);
177 NS_ENSURE_SUCCESS(rv
, rv
);
179 nsRefPtr
<nsWebShellWindow
> newWindow
;
180 rv
= JustCreateTopWindow(nsnull
, url
,
181 chromeMask
, initialWidth
, initialHeight
,
182 PR_TRUE
, aAppShell
, getter_AddRefs(newWindow
));
183 NS_ENSURE_SUCCESS(rv
, rv
);
185 mHiddenWindow
.swap(newWindow
);
188 // hide the hidden window by launching it into outer space. This
189 // way, we can keep it visible and let the OS send it activates
190 // to keep menus happy. This will cause it to show up in window
191 // lists under osx, but I think that's ok.
192 mHiddenWindow
->SetPosition ( -32000, -32000 );
193 mHiddenWindow
->SetVisibility ( PR_TRUE
);
196 // Set XPConnect's fallback JSContext (used for JS Components)
197 // to the DOM JSContext for this thread, so that DOM-to-XPConnect
198 // conversions get the JSContext private magic they need to
200 SetXPConnectSafeContext();
202 // RegisterTopLevelWindow(newWindow); -- Mac only
208 nsAppShellService::DestroyHiddenWindow()
211 ClearXPConnectSafeContext();
212 mHiddenWindow
->Destroy();
214 mHiddenWindow
= nsnull
;
221 * Create a new top level window and display the given URL within it...
224 nsAppShellService::CreateTopLevelWindow(nsIXULWindow
*aParent
,
226 PRUint32 aChromeMask
,
227 PRInt32 aInitialWidth
,
228 PRInt32 aInitialHeight
,
229 nsIAppShell
* aAppShell
,
230 nsIXULWindow
**aResult
)
235 nsWebShellWindow
*newWindow
= nsnull
;
236 rv
= JustCreateTopWindow(aParent
, aUrl
,
237 aChromeMask
, aInitialWidth
, aInitialHeight
,
238 PR_FALSE
, aAppShell
, &newWindow
); // addrefs
240 *aResult
= newWindow
; // transfer ref
242 if (NS_SUCCEEDED(rv
)) {
243 // the addref resulting from this is the owning addref for this window
244 RegisterTopLevelWindow(*aResult
);
245 nsCOMPtr
<nsIXULWindow
> parent
;
246 if (aChromeMask
& nsIWebBrowserChrome::CHROME_DEPENDENT
)
248 (*aResult
)->SetZLevel(CalculateWindowZLevel(parent
, aChromeMask
));
255 nsAppShellService::CalculateWindowZLevel(nsIXULWindow
*aParent
,
256 PRUint32 aChromeMask
)
260 zLevel
= nsIXULWindow::normalZ
;
261 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_RAISED
)
262 zLevel
= nsIXULWindow::raisedZ
;
263 else if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_LOWERED
)
264 zLevel
= nsIXULWindow::loweredZ
;
267 /* Platforms on which modal windows are always application-modal, not
268 window-modal (that's just the Mac, right?) want modal windows to
269 be stacked on top of everyone else.
271 On Mac OS X, bind modality to parent window instead of app (ala Mac OS 9)
273 PRUint32 modalDepMask
= nsIWebBrowserChrome::CHROME_MODAL
|
274 nsIWebBrowserChrome::CHROME_DEPENDENT
;
275 if (aParent
&& (aChromeMask
& modalDepMask
)) {
276 aParent
->GetZLevel(&zLevel
);
279 /* Platforms with native support for dependent windows (that's everyone
280 but pre-Mac OS X, right?) know how to stack dependent windows. On these
281 platforms, give the dependent window the same level as its parent,
282 so we won't try to override the normal platform behaviour. */
283 if ((aChromeMask
& nsIWebBrowserChrome::CHROME_DEPENDENT
) && aParent
)
284 aParent
->GetZLevel(&zLevel
);
291 * Checks to see if any existing window is currently in fullscreen mode.
294 CheckForFullscreenWindow()
296 nsCOMPtr
<nsIWindowMediator
> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
300 nsCOMPtr
<nsISimpleEnumerator
> windowList
;
301 wm
->GetXULWindowEnumerator(nsnull
, getter_AddRefs(windowList
));
306 PRBool more
= PR_FALSE
;
307 windowList
->HasMoreElements(&more
);
311 nsCOMPtr
<nsISupports
> supportsWindow
;
312 windowList
->GetNext(getter_AddRefs(supportsWindow
));
313 nsCOMPtr
<nsIBaseWindow
> baseWin(do_QueryInterface(supportsWindow
));
316 nsCOMPtr
<nsIWidget
> widget
;
317 baseWin
->GetMainWidget(getter_AddRefs(widget
));
318 if (widget
&& NS_SUCCEEDED(widget
->GetSizeMode(&sizeMode
)) &&
319 sizeMode
== nsSizeMode_Fullscreen
) {
328 * Just do the window-making part of CreateTopLevelWindow
331 nsAppShellService::JustCreateTopWindow(nsIXULWindow
*aParent
,
333 PRUint32 aChromeMask
,
334 PRInt32 aInitialWidth
,
335 PRInt32 aInitialHeight
,
336 PRBool aIsHiddenWindow
,
337 nsIAppShell
* aAppShell
,
338 nsWebShellWindow
**aResult
)
341 NS_ENSURE_STATE(!mXPCOMWillShutDown
);
343 nsCOMPtr
<nsIXULWindow
> parent
;
344 if (aChromeMask
& nsIWebBrowserChrome::CHROME_DEPENDENT
)
347 nsRefPtr
<nsWebShellWindow
> window
= new nsWebShellWindow(aChromeMask
);
348 NS_ENSURE_TRUE(window
, NS_ERROR_OUT_OF_MEMORY
);
351 // If the parent is currently fullscreen, tell the child to ignore persisted
352 // full screen states. This way new browser windows open on top of fullscreen
354 if (window
&& CheckForFullscreenWindow())
355 window
->IgnoreXULSizeMode(PR_TRUE
);
358 nsWidgetInitData widgetInitData
;
361 widgetInitData
.mWindowType
= eWindowType_invisible
;
363 widgetInitData
.mWindowType
= aChromeMask
& nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
?
364 eWindowType_dialog
: eWindowType_toplevel
;
366 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_POPUP
)
367 widgetInitData
.mWindowType
= eWindowType_popup
;
370 // Mac OS X sheet support
371 // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from
372 // nsGlobalWindow::ShowModalDialog() be dialogs (not sheets), while modal
373 // windows opened from nsPromptService::DoDialog() still are sheets. This
374 // fixes bmo bug 395465 (see nsCocoaWindow::StandardCreate() and
375 // nsCocoaWindow::SetModal()).
376 PRUint32 sheetMask
= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
|
377 nsIWebBrowserChrome::CHROME_MODAL
|
378 nsIWebBrowserChrome::CHROME_OPENAS_CHROME
;
379 if (parent
&& ((aChromeMask
& sheetMask
) == sheetMask
))
380 widgetInitData
.mWindowType
= eWindowType_sheet
;
384 if (widgetInitData
.mWindowType
== eWindowType_toplevel
||
385 widgetInitData
.mWindowType
== eWindowType_dialog
)
386 widgetInitData
.clipChildren
= PR_TRUE
;
389 widgetInitData
.mContentType
= eContentTypeUI
;
391 // note default chrome overrides other OS chrome settings, but
392 // not internal chrome
393 if (aChromeMask
& nsIWebBrowserChrome::CHROME_DEFAULT
)
394 widgetInitData
.mBorderStyle
= eBorderStyle_default
;
395 else if ((aChromeMask
& nsIWebBrowserChrome::CHROME_ALL
) == nsIWebBrowserChrome::CHROME_ALL
)
396 widgetInitData
.mBorderStyle
= eBorderStyle_all
;
398 widgetInitData
.mBorderStyle
= eBorderStyle_none
; // assumes none == 0x00
399 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_BORDERS
)
400 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_border
);
401 if (aChromeMask
& nsIWebBrowserChrome::CHROME_TITLEBAR
)
402 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_title
);
403 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_CLOSE
)
404 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_close
);
405 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_RESIZE
) {
406 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_resizeh
);
407 // only resizable windows get the maximize button (but not dialogs)
408 if (!(aChromeMask
& nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
))
409 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_maximize
);
411 // all windows (except dialogs) get minimize buttons and the system menu
412 if (!(aChromeMask
& nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
))
413 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_minimize
| eBorderStyle_menu
);
414 // but anyone can explicitly ask for a minimize button
415 if (aChromeMask
& nsIWebBrowserChrome::CHROME_WINDOW_MIN
) {
416 widgetInitData
.mBorderStyle
= static_cast<enum nsBorderStyle
>(widgetInitData
.mBorderStyle
| eBorderStyle_minimize
);
420 if (aInitialWidth
== nsIAppShellService::SIZE_TO_CONTENT
||
421 aInitialHeight
== nsIAppShellService::SIZE_TO_CONTENT
) {
424 window
->SetIntrinsicallySized(PR_TRUE
);
427 PRBool center
= aChromeMask
& nsIWebBrowserChrome::CHROME_CENTER_SCREEN
;
429 nsCOMPtr
<nsIXULChromeRegistry
> reg
=
430 mozilla::services::GetXULChromeRegistryService();
432 nsCAutoString package
;
433 package
.AssignLiteral("global");
434 PRBool isRTL
= PR_FALSE
;
435 reg
->IsLocaleRTL(package
, &isRTL
);
436 widgetInitData
.mRTL
= isRTL
;
439 nsresult rv
= window
->Initialize(parent
, center
? aParent
: nsnull
,
441 aInitialWidth
, aInitialHeight
,
442 aIsHiddenWindow
, widgetInitData
);
444 NS_ENSURE_SUCCESS(rv
, rv
);
446 window
.swap(*aResult
); // transfer reference
448 parent
->AddChildWindow(*aResult
);
451 rv
= (*aResult
)->Center(parent
, parent
? PR_FALSE
: PR_TRUE
, PR_FALSE
);
457 nsAppShellService::GetHiddenWindow(nsIXULWindow
**aWindow
)
459 NS_ENSURE_ARG_POINTER(aWindow
);
461 *aWindow
= mHiddenWindow
;
462 NS_IF_ADDREF(*aWindow
);
463 return *aWindow
? NS_OK
: NS_ERROR_FAILURE
;
467 nsAppShellService::GetHiddenDOMWindow(nsIDOMWindowInternal
**aWindow
)
470 nsCOMPtr
<nsIDocShell
> docShell
;
471 NS_ENSURE_TRUE(mHiddenWindow
, NS_ERROR_FAILURE
);
473 rv
= mHiddenWindow
->GetDocShell(getter_AddRefs(docShell
));
474 NS_ENSURE_SUCCESS(rv
, rv
);
476 nsCOMPtr
<nsIDOMWindowInternal
> hiddenDOMWindow(do_GetInterface(docShell
, &rv
));
477 NS_ENSURE_SUCCESS(rv
, rv
);
479 *aWindow
= hiddenDOMWindow
;
480 NS_IF_ADDREF(*aWindow
);
485 nsAppShellService::GetHiddenWindowAndJSContext(nsIDOMWindowInternal
**aWindow
,
486 JSContext
**aJSContext
)
489 if ( aWindow
&& aJSContext
) {
491 *aJSContext
= nsnull
;
493 if ( mHiddenWindow
) {
494 // Convert hidden window to nsIDOMWindowInternal and extract its JSContext.
496 // 1. Get doc for hidden window.
497 nsCOMPtr
<nsIDocShell
> docShell
;
498 rv
= mHiddenWindow
->GetDocShell(getter_AddRefs(docShell
));
499 if (NS_FAILED(rv
)) break;
501 // 2. Convert that to an nsIDOMWindowInternal.
502 nsCOMPtr
<nsIDOMWindowInternal
> hiddenDOMWindow(do_GetInterface(docShell
));
503 if(!hiddenDOMWindow
) break;
505 // 3. Get script global object for the window.
506 nsCOMPtr
<nsIScriptGlobalObject
> sgo
;
507 sgo
= do_QueryInterface( hiddenDOMWindow
);
508 if (!sgo
) { rv
= NS_ERROR_FAILURE
; break; }
510 // 4. Get script context from that.
511 nsIScriptContext
*scriptContext
= sgo
->GetContext();
512 if (!scriptContext
) { rv
= NS_ERROR_FAILURE
; break; }
514 // 5. Get JSContext from the script context.
515 JSContext
*jsContext
= (JSContext
*)scriptContext
->GetNativeContext();
516 if (!jsContext
) { rv
= NS_ERROR_FAILURE
; break; }
518 // Now, give results to caller.
519 *aWindow
= hiddenDOMWindow
.get();
520 NS_IF_ADDREF( *aWindow
);
521 *aJSContext
= jsContext
;
524 rv
= NS_ERROR_FAILURE
;
527 rv
= NS_ERROR_NULL_POINTER
;
533 nsAppShellService::GetApplicationProvidedHiddenWindow(PRBool
* aAPHW
)
535 *aAPHW
= mApplicationProvidedHiddenWindow
;
540 * Register a new top level window (created elsewhere)
543 nsAppShellService::RegisterTopLevelWindow(nsIXULWindow
* aWindow
)
545 // tell the window mediator about the new window
546 nsCOMPtr
<nsIWindowMediator
> mediator
547 ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
) );
548 NS_ASSERTION(mediator
, "Couldn't get window mediator.");
551 mediator
->RegisterWindow(aWindow
);
553 // tell the window watcher about the new window
554 nsCOMPtr
<nsPIWindowWatcher
> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID
) );
555 NS_ASSERTION(wwatcher
, "No windowwatcher?");
557 nsCOMPtr
<nsIDocShell
> docShell
;
558 aWindow
->GetDocShell(getter_AddRefs(docShell
));
559 NS_ASSERTION(docShell
, "Window has no docshell");
561 nsCOMPtr
<nsIDOMWindow
> domWindow(do_GetInterface(docShell
));
562 NS_ASSERTION(domWindow
, "Couldn't get DOM window.");
564 wwatcher
->AddWindow(domWindow
, 0);
568 // an ongoing attempt to quit is stopped by a newly opened window
569 nsCOMPtr
<nsIObserverService
> obssvc
=
570 do_GetService("@mozilla.org/observer-service;1");
571 NS_ASSERTION(obssvc
, "Couldn't get observer service.");
574 obssvc
->NotifyObservers(aWindow
, "xul-window-registered", nsnull
);
581 nsAppShellService::UnregisterTopLevelWindow(nsIXULWindow
* aWindow
)
583 if (mXPCOMShuttingDown
) {
584 /* return an error code in order to:
585 - avoid doing anything with other member variables while we are in
587 - notify the caller not to release the AppShellService after
588 unregistering the window
589 (we don't want to be deleted twice consecutively to
590 mHiddenWindow->Destroy() in our destructor)
592 return NS_ERROR_FAILURE
;
595 NS_ENSURE_ARG_POINTER(aWindow
);
597 if (aWindow
== mHiddenWindow
) {
598 // CreateHiddenWindow() does not register the window, so we're done.
602 // tell the window mediator
603 nsCOMPtr
<nsIWindowMediator
> mediator
604 ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
) );
605 NS_ASSERTION(mediator
, "Couldn't get window mediator. Doing xpcom shutdown?");
608 mediator
->UnregisterWindow(aWindow
);
610 // tell the window watcher
611 nsCOMPtr
<nsPIWindowWatcher
> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID
) );
612 NS_ASSERTION(wwatcher
, "Couldn't get windowwatcher, doing xpcom shutdown?");
614 nsCOMPtr
<nsIDocShell
> docShell
;
615 aWindow
->GetDocShell(getter_AddRefs(docShell
));
617 nsCOMPtr
<nsIDOMWindow
> domWindow(do_GetInterface(docShell
));
619 wwatcher
->RemoveWindow(domWindow
);
628 nsAppShellService::Observe(nsISupports
* aSubject
, const char *aTopic
,
629 const PRUnichar
*aData
)
631 if (!strcmp(aTopic
, "xpcom-will-shutdown")) {
632 mXPCOMWillShutDown
= PR_TRUE
;
633 } else if (!strcmp(aTopic
, "xpcom-shutdown")) {
634 mXPCOMShuttingDown
= PR_TRUE
;
636 ClearXPConnectSafeContext();
637 mHiddenWindow
->Destroy();
640 NS_ERROR("Unexpected observer topic!");