/* -*- Mode: C++; tab-width: 4; 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 "LayerManagerD3D10.h" #include "MetroWidget.h" #include "MetroApp.h" #include "mozilla/Preferences.h" #include "nsToolkit.h" #include "KeyboardLayout.h" #include "MetroUtils.h" #include "WinUtils.h" #include "nsToolkitCompsCID.h" #include "nsIAppStartup.h" #include "../resource.h" #include "nsIWidgetListener.h" #include "nsIPresShell.h" #include "nsPrintfCString.h" #include "nsWindowDefs.h" #include "FrameworkView.h" #include "nsTextStore.h" #include "Layers.h" #include "BasicLayers.h" #include "Windows.Graphics.Display.h" using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace mozilla; using namespace mozilla::widget; using namespace mozilla::layers; using namespace mozilla::widget::winrt; using namespace ABI::Windows::ApplicationModel; using namespace ABI::Windows::ApplicationModel::Core; using namespace ABI::Windows::ApplicationModel::Activation; using namespace ABI::Windows::UI::Input; using namespace ABI::Windows::Devices::Input; using namespace ABI::Windows::UI::Core; using namespace ABI::Windows::System; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Graphics::Display; #ifdef PR_LOGGING extern PRLogModuleInfo* gWindowsLog; #endif static uint32_t gInstanceCount = 0; const PRUnichar* kMetroSubclassThisProp = L"MetroSubclassThisProp"; static const UINT sDefaultBrowserMsgID = RegisterWindowMessageW(L"DefaultBrowserClosing"); namespace { void SendInputs(uint32_t aModifiers, INPUT* aExtraInputs, uint32_t aExtraInputsLen) { // keySequence holds the virtual key values of each of the keys we intend // to press nsAutoTArray keySequence; for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) { const uint32_t* map = sModifierKeyMap[i]; if (aModifiers & map[0]) { keySequence.AppendElement(KeyPair(map[1], map[2])); } } uint32_t const len = keySequence.Length() * 2 + aExtraInputsLen; // The `inputs` array is a sequence of input events that will happen // serially. We set the array up so that each modifier key is pressed // down, then the additional input events happen, // then each modifier key is released in reverse order of when // it was pressed down. We pass this array to `SendInput`. // // inputs[0]: modifier key (e.g. shift, ctrl, etc) down // ... ... // inputs[keySequence.Length()-1]: modifier key (e.g. shift, ctrl, etc) down // inputs[keySequence.Length()]: aExtraInputs[0] // inputs[keySequence.Length()+1]: aExtraInputs[1] // ... ... // inputs[keySequence.Length() + aExtraInputsLen - 1]: aExtraInputs[aExtraInputsLen - 1] // inputs[keySequence.Length() + aExtraInputsLen]: modifier key (e.g. shift, ctrl, etc) up // ... ... // inputs[len-1]: modifier key (e.g. shift, ctrl, etc) up INPUT* inputs = new INPUT[len]; memset(inputs, 0, len * sizeof(INPUT)); for (uint32_t i = 0; i < keySequence.Length(); ++i) { inputs[i].type = inputs[len-i-1].type = INPUT_KEYBOARD; inputs[i].ki.wVk = inputs[len-i-1].ki.wVk = keySequence[i].mSpecific ? keySequence[i].mSpecific : keySequence[i].mGeneral; inputs[len-i-1].ki.dwFlags |= KEYEVENTF_KEYUP; } for (uint32_t i = 0; i < aExtraInputsLen; i++) { inputs[keySequence.Length()+i] = aExtraInputs[i]; } Log(L" Sending inputs"); for (uint32_t i = 0; i < len; i++) { if (inputs[i].type == INPUT_KEYBOARD) { Log(L" Key press: 0x%x %s", inputs[i].ki.wVk, inputs[i].ki.dwFlags & KEYEVENTF_KEYUP ? L"UP" : L"DOWN"); } else if(inputs[i].type == INPUT_MOUSE) { Log(L" Mouse input: 0x%x 0x%x", inputs[i].mi.dwFlags, inputs[i].mi.mouseData); } else { Log(L" Unknown input type!"); } } ::SendInput(len, inputs, sizeof(INPUT)); delete[] inputs; // The inputs have been sent, and the WM_* messages they generate are // waiting to be processed by our event loop. Now we manually pump // those messages so that, upon our return, all the inputs have been // processed. Log(L" Inputs sent. Waiting for input messages to clear"); MSG msg; while (WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (nsTextStore::ProcessRawKeyMessage(msg)) { continue; // the message is consumed by TSF } ::TranslateMessage(&msg); ::DispatchMessage(&msg); Log(L" Dispatched 0x%x 0x%x 0x%x", msg.message, msg.wParam, msg.lParam); } Log(L" No more input messages"); } } NS_IMPL_ISUPPORTS_INHERITED0(MetroWidget, nsBaseWidget) MetroWidget::MetroWidget() : mTransparencyMode(eTransparencyOpaque), mWnd(NULL), mMetroWndProc(NULL), mTempBasicLayerInUse(false), nsWindowBase() { // Global initialization if (!gInstanceCount) { UserActivity(); nsTextStore::Initialize(); } // !gInstanceCount gInstanceCount++; } MetroWidget::~MetroWidget() { LogThis(); gInstanceCount--; // Global shutdown if (!gInstanceCount) { nsTextStore::Terminate(); } // !gInstanceCount } static bool gTopLevelAssigned = false; NS_IMETHODIMP MetroWidget::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, nsDeviceContext *aContext, nsWidgetInitData *aInitData) { LogFunction(); nsWidgetInitData defaultInitData; if (!aInitData) aInitData = &defaultInitData; mWindowType = aInitData->mWindowType; // Ensure that the toolkit is created. nsToolkit::GetToolkit(); BaseCreate(aParent, aRect, aContext, aInitData); if (mWindowType != eWindowType_toplevel) { switch(mWindowType) { case eWindowType_dialog: Log(L"eWindowType_dialog window requested, returning failure."); break; case eWindowType_child: Log(L"eWindowType_child window requested, returning failure."); break; case eWindowType_popup: Log(L"eWindowType_popup window requested, returning failure."); break; case eWindowType_plugin: Log(L"eWindowType_plugin window requested, returning failure."); break; // we should support toolkit's eWindowType_invisible at some point. case eWindowType_invisible: Log(L"eWindowType_invisible window requested, this doesn't actually exist!"); return NS_OK; } NS_WARNING("Invalid window type requested."); return NS_ERROR_FAILURE; } if (gTopLevelAssigned) { // Need to accept so that the mochitest-chrome test harness window // can be created. NS_WARNING("New eWindowType_toplevel window requested after FrameworkView widget created."); NS_WARNING("Widget created but the physical window does not exist! Fix me!"); return NS_OK; } // the main widget gets created first gTopLevelAssigned = true; MetroApp::SetBaseWidget(this); if (mWidgetListener) { mWidgetListener->WindowActivated(); } return NS_OK; } void MetroWidget::SetView(FrameworkView* aView) { mView = aView; // If we've already set this up, it points to a useless // layer manager, so reset it. mLayerManager = nullptr; } NS_IMETHODIMP MetroWidget::Destroy() { if (mOnDestroyCalled) return NS_OK; Log(L"[%X] %s mWnd=%X type=%d", this, __WFUNCTION__, mWnd, mWindowType); mOnDestroyCalled = true; RemoveSubclass(); mView = nullptr; mIdleService = nullptr; return NS_OK; } NS_IMETHODIMP MetroWidget::SetParent(nsIWidget *aNewParent) { return NS_OK; } NS_IMETHODIMP MetroWidget::Show(bool bState) { return NS_OK; } NS_IMETHODIMP MetroWidget::IsVisible(bool & aState) { aState = mView->IsVisible(); return NS_OK; } bool MetroWidget::IsVisible() const { if (!mView) return false; return mView->IsVisible(); } NS_IMETHODIMP MetroWidget::IsEnabled(bool *aState) { *aState = mView->IsEnabled(); return NS_OK; } bool MetroWidget::IsEnabled() const { if (!mView) return false; return mView->IsEnabled(); } NS_IMETHODIMP MetroWidget::Enable(bool bState) { return NS_OK; } NS_IMETHODIMP MetroWidget::GetBounds(nsIntRect &aRect) { if (mView) { mView->GetBounds(aRect); } else { nsIntRect rect(0,0,0,0); aRect = rect; } return NS_OK; } NS_IMETHODIMP MetroWidget::GetScreenBounds(nsIntRect &aRect) { if (mView) { mView->GetBounds(aRect); } else { nsIntRect rect(0,0,0,0); aRect = rect; } return NS_OK; } NS_IMETHODIMP MetroWidget::GetClientBounds(nsIntRect &aRect) { if (mView) { mView->GetBounds(aRect); } else { nsIntRect rect(0,0,0,0); aRect = rect; } return NS_OK; } NS_IMETHODIMP MetroWidget::SetCursor(nsCursor aCursor) { if (!mView) return NS_ERROR_FAILURE; switch (aCursor) { case eCursor_select: mView->SetCursor(CoreCursorType::CoreCursorType_IBeam); break; case eCursor_wait: mView->SetCursor(CoreCursorType::CoreCursorType_Wait); break; case eCursor_hyperlink: mView->SetCursor(CoreCursorType::CoreCursorType_Hand); break; case eCursor_standard: mView->SetCursor(CoreCursorType::CoreCursorType_Arrow); break; case eCursor_n_resize: case eCursor_s_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNorthSouth); break; case eCursor_w_resize: case eCursor_e_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeWestEast); break; case eCursor_nw_resize: case eCursor_se_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNorthwestSoutheast); break; case eCursor_ne_resize: case eCursor_sw_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNortheastSouthwest); break; case eCursor_crosshair: mView->SetCursor(CoreCursorType::CoreCursorType_Cross); break; case eCursor_move: mView->SetCursor(CoreCursorType::CoreCursorType_SizeAll); break; case eCursor_help: mView->SetCursor(CoreCursorType::CoreCursorType_Help); break; // CSS3 custom cursors case eCursor_copy: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_COPY); break; case eCursor_alias: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_ALIAS); break; case eCursor_cell: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_CELL); break; case eCursor_grab: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_GRAB); break; case eCursor_grabbing: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_GRABBING); break; case eCursor_spinning: mView->SetCursor(CoreCursorType::CoreCursorType_Wait); break; case eCursor_context_menu: mView->SetCursor(CoreCursorType::CoreCursorType_Arrow); break; case eCursor_zoom_in: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_ZOOMIN); break; case eCursor_zoom_out: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_ZOOMOUT); break; case eCursor_not_allowed: case eCursor_no_drop: mView->SetCursor(CoreCursorType::CoreCursorType_UniversalNo); break; case eCursor_col_resize: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_COLRESIZE); break; case eCursor_row_resize: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_ROWRESIZE); break; case eCursor_vertical_text: mView->SetCursor(CoreCursorType::CoreCursorType_Custom, IDC_VERTICALTEXT); break; case eCursor_all_scroll: mView->SetCursor(CoreCursorType::CoreCursorType_SizeAll); break; case eCursor_nesw_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNortheastSouthwest); break; case eCursor_nwse_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNorthwestSoutheast); break; case eCursor_ns_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeNorthSouth); break; case eCursor_ew_resize: mView->SetCursor(CoreCursorType::CoreCursorType_SizeWestEast); break; case eCursor_none: mView->ClearCursor(); break; default: NS_WARNING("Invalid cursor type"); break; } return NS_OK; } nsresult MetroWidget::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, uint32_t aModifierFlags, const nsAString& aCharacters, const nsAString& aUnmodifiedCharacters) { Log(L"ENTERED SynthesizeNativeKeyEvent"); // According to MSDN, valid virtual-key codes are in the range 1 to 254. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646271%28v=vs.85%29.aspx NS_ENSURE_ARG_RANGE(aNativeKeyCode, 1, 254); // Store a list of all loaded keyboard layouts int32_t const numKeyboardLayouts = GetKeyboardLayoutList(0, NULL); if (numKeyboardLayouts == 0) { return NS_ERROR_FAILURE; } HKL* keyboardLayoutList = new HKL[numKeyboardLayouts]; GetKeyboardLayoutList(numKeyboardLayouts, keyboardLayoutList); // Store the current keyboard layout HKL const oldKeyboardLayout = ::GetKeyboardLayout(0); Log(L" Current keyboard layout: %08x", oldKeyboardLayout); Log(L" Loading keyboard layout: %08x", aNativeKeyboardLayout); // Load the requested keyboard layout nsPrintfCString layoutName("%08x", aNativeKeyboardLayout); HKL const newKeyboardLayout = ::LoadKeyboardLayoutA(layoutName.get(), KLF_REPLACELANG); Log(L" ::LoadKeyboardLayoutA returned %08x", newKeyboardLayout); // We have a list of all keyboard layouts that were loaded before we called // ::LoadKeyboardLayout. Now, we loop through that list to determine which // of these cases we've hit: // A) The layout we loaded was already loaded // B) The layout we loaded was not already loaded, and it replaced // another layout in the list // C) The layout we loaded was not already loaded, and it has not // replaced another layout bool haveLoaded = true; bool haveActivated = false; bool haveReplaced = false; if (GetKeyboardLayoutList(0, NULL) == numKeyboardLayouts) { haveReplaced = true; for (int32_t i = 0; i < numKeyboardLayouts; i++) { if (keyboardLayoutList[i] == newKeyboardLayout) { Log(L" %08x found in list of loaded keyboard layouts", newKeyboardLayout); haveLoaded = false; haveReplaced = false; break; } } } // If the requested keyboard layout was already active when this function // was called, then we don't need to activate our keyboard layout if (oldKeyboardLayout != newKeyboardLayout) { Log(L" %08x != %08x", oldKeyboardLayout, newKeyboardLayout); haveActivated = true; Log(L" Activating keyboard layout: %08x", newKeyboardLayout); HKL ret = ::ActivateKeyboardLayout(newKeyboardLayout, KLF_SETFORPROCESS); Log(L" ::ActivateKeyboardLayout returned %08x", ret); } INPUT inputs[2]; memset(&inputs, 0, 2*sizeof(INPUT)); inputs[0].type = inputs[1].type = INPUT_KEYBOARD; inputs[0].ki.wVk = inputs[1].ki.wVk = aNativeKeyCode; inputs[1].ki.dwFlags |= KEYEVENTF_KEYUP; SendInputs(aModifierFlags, inputs, 2); // Now that all the events have been processed, we can set the keyboard // layout list back to its original state. If we didn't activate a // keyboard (meaning that the requested keyboard was already the active // keyboard), we don't have to do anything. if (haveActivated) { // If we replaced a keyboard in the layout list, let's be safe and reload // all the keyboards that were in the original list. if (haveReplaced) { Log(L" Loading all previous layouts"); for (int32_t i = 0; i < numKeyboardLayouts; i++) { nsPrintfCString layoutName("%08x", keyboardLayoutList[i]); HKL ret = ::LoadKeyboardLayoutA(layoutName.get(), KLF_REPLACELANG); Log(L" ::LoadKeyboardLayoutA returned %08x", ret); } } // Any keyboards that were in the keyboard layout list when we entered // this function should be loaded, so let's go ahead and activate the // keyboard layout that was active when we entered. Log(L" Activating previous layout %08x", oldKeyboardLayout); HKL ret = ::ActivateKeyboardLayout(oldKeyboardLayout, KLF_SETFORPROCESS); Log(L" ::ActivateKeyboardLayout returned %08x", ret); // If we loaded a keyboard that was not already loaded, and that didn't // replace another keyboard in the keyboard layout list, let's unload it. if (haveLoaded && !haveReplaced) { Log(L" Unloading keyboard layout %08x", newKeyboardLayout); ::UnloadKeyboardLayout(newKeyboardLayout); } } delete[] keyboardLayoutList; Log(L"EXITING SynthesizeNativeKeyEvent"); return NS_OK; } nsresult MetroWidget::SynthesizeNativeMouseEvent(nsIntPoint aPoint, uint32_t aNativeMessage, uint32_t aModifierFlags) { Log(L"ENTERED SynthesizeNativeMouseEvent"); INPUT inputs[2]; memset(inputs, 0, 2*sizeof(INPUT)); inputs[0].type = inputs[1].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; // Inexplicably, the x and y coordinates that we want to move the mouse to // are specified as values in the range (0, 65535). (0,0) represents the // top left of the primary monitor and (65535, 65535) represents the // bottom right of the primary monitor. inputs[0].mi.dx = (aPoint.x * 65535) / ::GetSystemMetrics(SM_CXSCREEN); inputs[0].mi.dy = (aPoint.y * 65535) / ::GetSystemMetrics(SM_CYSCREEN); inputs[1].mi.dwFlags = aNativeMessage; SendInputs(aModifierFlags, inputs, 2); Log(L"Exiting SynthesizeNativeMouseEvent"); return NS_OK; } nsresult MetroWidget::SynthesizeNativeMouseScrollEvent(nsIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags, uint32_t aAdditionalFlags) { Log(L"ENTERED SynthesizeNativeMouseScrollEvent"); int32_t mouseData = 0; if (aNativeMessage == MOUSEEVENTF_WHEEL) { mouseData = static_cast(aDeltaY); Log(L" Vertical scroll, delta %d", mouseData); } else if (aNativeMessage == MOUSEEVENTF_HWHEEL) { mouseData = static_cast(aDeltaX); Log(L" Horizontal scroll, delta %d", mouseData); } else { Log(L"ERROR Unrecognized scroll event"); return NS_ERROR_INVALID_ARG; } INPUT inputs[2]; memset(inputs, 0, 2*sizeof(INPUT)); inputs[0].type = inputs[1].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; // Inexplicably, the x and y coordinates that we want to move the mouse to // are specified as values in the range (0, 65535). (0,0) represents the // top left of the primary monitor and (65535, 65535) represents the // bottom right of the primary monitor. inputs[0].mi.dx = (aPoint.x * 65535) / ::GetSystemMetrics(SM_CXSCREEN); inputs[0].mi.dy = (aPoint.y * 65535) / ::GetSystemMetrics(SM_CYSCREEN); inputs[1].mi.dwFlags = aNativeMessage; inputs[1].mi.mouseData = mouseData; SendInputs(aModifierFlags, inputs, 2); Log(L"EXITING SynthesizeNativeMouseScrollEvent"); return NS_OK; } static void CloseGesture() { Log(L"shuting down due to close gesture.\n"); nsCOMPtr appStartup = do_GetService(NS_APPSTARTUP_CONTRACTID); if (appStartup) { appStartup->Quit(nsIAppStartup::eForceQuit); } } // static LRESULT CALLBACK MetroWidget::StaticWindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam) { MetroWidget* self = reinterpret_cast( GetProp(aWnd, kMetroSubclassThisProp)); if (!self) { NS_NOTREACHED("Missing 'this' prop on subclassed metro window, this is bad."); return 0; } return self->WindowProcedure(aWnd, aMsg, aWParam, aLParam); } LRESULT MetroWidget::WindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam) { if(sDefaultBrowserMsgID == aMsg) { CloseGesture(); } // Indicates if we should hand messages to the default windows // procedure for processing. bool processDefault = true; // The result returned if we do not do default processing. LRESULT processResult = 0; switch (aMsg) { case WM_PAINT: { HRGN rgn = CreateRectRgn(0, 0, 0, 0); GetUpdateRgn(mWnd, rgn, false); nsIntRegion region = WinUtils::ConvertHRGNToRegion(rgn); DeleteObject(rgn); if (region.IsEmpty()) break; mView->Render(region); break; } case WM_POWERBROADCAST: { switch (aWParam) { case PBT_APMSUSPEND: MetroApp::PostSleepWakeNotification(true); break; case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMECRITICAL: case PBT_APMRESUMESUSPEND: MetroApp::PostSleepWakeNotification(false); break; } break; } default: { if (aWParam == WM_USER_TSF_TEXTCHANGE) { nsTextStore::OnTextChangeMsg(); } break; } } if (processDefault) { return CallWindowProc(mMetroWndProc, aWnd, aMsg, aWParam, aLParam); } return processResult; } static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { WCHAR className[56]; if (GetClassNameW(hwnd, className, sizeof(className)/sizeof(WCHAR)) && !wcscmp(L"Windows.UI.Core.CoreWindow", className)) { DWORD processID = 0; GetWindowThreadProcessId(hwnd, &processID); if (processID && processID == GetCurrentProcessId()) { *((HWND*)lParam) = hwnd; return FALSE; } } return TRUE; } void MetroWidget::FindMetroWindow() { LogFunction(); if (mWnd) return; EnumWindows(EnumWindowsProc, (LPARAM)&mWnd); NS_ASSERTION(mWnd, "Couldn't find our metro CoreWindow, this is bad."); // subclass it SetSubclass(); return; } void MetroWidget::SetSubclass() { if (!mWnd) { NS_NOTREACHED("SetSubclass called without a valid hwnd."); return; } WNDPROC wndProc = reinterpret_cast( GetWindowLongPtr(mWnd, GWLP_WNDPROC)); if (wndProc != StaticWindowProcedure) { if (!SetPropW(mWnd, kMetroSubclassThisProp, this)) { NS_NOTREACHED("SetProp failed, can't continue."); return; } mMetroWndProc = reinterpret_cast( SetWindowLongPtr(mWnd, GWLP_WNDPROC, reinterpret_cast(StaticWindowProcedure))); NS_ASSERTION(mMetroWndProc != StaticWindowProcedure, "WTF?"); } } void MetroWidget::RemoveSubclass() { if (!mWnd) return; WNDPROC wndProc = reinterpret_cast( GetWindowLongPtr(mWnd, GWLP_WNDPROC)); if (wndProc == StaticWindowProcedure) { NS_ASSERTION(mMetroWndProc, "Should have old proc here."); SetWindowLongPtr(mWnd, GWLP_WNDPROC, reinterpret_cast(mMetroWndProc)); mMetroWndProc = NULL; } RemovePropW(mWnd, kMetroSubclassThisProp); } bool MetroWidget::ShouldUseOffMainThreadCompositing() { // Either we're not initialized yet, or this is the toolkit widget if (!mView) { return false; } // toolkit or test widgets can't use omtc, they don't have ICoreWindow. return (CompositorParent::CompositorLoop() && mWindowType == eWindowType_toplevel); } bool MetroWidget::ShouldUseMainThreadD3D10Manager() { // Either we're not initialized yet, or this is the toolkit widget if (!mView) { return false; } return (!CompositorParent::CompositorLoop() && mWindowType == eWindowType_toplevel); } bool MetroWidget::ShouldUseBasicManager() { // toolkit or test widgets fall back on empty shadow layers return (mWindowType != eWindowType_toplevel); } LayerManager* MetroWidget::GetLayerManager(PLayersChild* aShadowManager, LayersBackend aBackendHint, LayerManagerPersistence aPersistence, bool* aAllowRetaining) { bool retaining = true; // If we initialized earlier than the view, recreate the layer manager now if (mLayerManager && mTempBasicLayerInUse && ShouldUseOffMainThreadCompositing()) { mLayerManager = nullptr; mTempBasicLayerInUse = false; retaining = false; } // If the backend device has changed, create a new manager (pulled from nswindow) if (mLayerManager) { if (mLayerManager->GetBackendType() == LAYERS_D3D10) { LayerManagerD3D10 *layerManagerD3D10 = static_cast(mLayerManager.get()); if (layerManagerD3D10->device() != gfxWindowsPlatform::GetPlatform()->GetD3D10Device()) { MOZ_ASSERT(!mLayerManager->IsInTransaction()); mLayerManager->Destroy(); mLayerManager = nullptr; retaining = false; } } } // Create a layer manager: try to use an async compositor first, if enabled. // Otherwise fall back on the main thread d3d manager. if (!mLayerManager) { if (ShouldUseOffMainThreadCompositing()) { NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s"); CreateCompositor(); } else if (ShouldUseMainThreadD3D10Manager()) { nsRefPtr layerManager = new mozilla::layers::LayerManagerD3D10(this); if (layerManager->Initialize(true)) { mLayerManager = layerManager; } } else if (ShouldUseBasicManager()) { mLayerManager = CreateBasicLayerManager(); } // Either we're not ready to initialize yet due to a missing view pointer, // or something has gone wrong. if (!mLayerManager) { if (!mView) { NS_WARNING("Using temporary basic layer manager."); mLayerManager = new BasicShadowLayerManager(this); mTempBasicLayerInUse = true; } else { NS_RUNTIMEABORT("Couldn't create layer manager"); } } } if (aAllowRetaining) { *aAllowRetaining = retaining; } return mLayerManager; } NS_IMETHODIMP MetroWidget::Invalidate(bool aEraseBackground, bool aUpdateNCArea, bool aIncludeChildren) { nsIntRect rect; if (mView) { mView->GetBounds(rect); } Invalidate(rect); return NS_OK; } NS_IMETHODIMP MetroWidget::Invalidate(const nsIntRect & aRect) { if (mWnd) { RECT rect; rect.left = aRect.x; rect.top = aRect.y; rect.right = aRect.x + aRect.width; rect.bottom = aRect.y + aRect.height; InvalidateRect(mWnd, &rect, FALSE); } return NS_OK; } nsTransparencyMode MetroWidget::GetTransparencyMode() { return mTransparencyMode; } void MetroWidget::SetTransparencyMode(nsTransparencyMode aMode) { mTransparencyMode = aMode; } nsIWidgetListener* MetroWidget::GetPaintListener() { if (mOnDestroyCalled) return nullptr; return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; } void MetroWidget::Paint(const nsIntRegion& aInvalidRegion) { nsIWidgetListener* listener = GetPaintListener(); if (!listener) return; listener->WillPaintWindow(this); // Refresh since calls like WillPaintWindow can destroy the widget listener = GetPaintListener(); if (!listener) return; listener->PaintWindow(this, aInvalidRegion, 0); listener = GetPaintListener(); if (!listener) return; listener->DidPaintWindow(); } void MetroWidget::UserActivity() { // Check if we have the idle service, if not we try to get it. if (!mIdleService) { mIdleService = do_GetService("@mozilla.org/widget/idleservice;1"); } // Check that we now have the idle service. if (mIdleService) { mIdleService->ResetIdleTimeOut(0); } } void MetroWidget::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint) { if (!aPoint) { event.refPoint.x = event.refPoint.y = 0; } else { // convert CSS pixels to device pixels for event.refPoint double scale = GetDefaultScale(); event.refPoint.x = int32_t(NS_round(aPoint->x * scale)); event.refPoint.y = int32_t(NS_round(aPoint->y * scale)); } event.time = ::GetMessageTime(); } bool MetroWidget::DispatchWindowEvent(nsGUIEvent* aEvent) { nsEventStatus aStatus; if (!aEvent || NS_FAILED(DispatchEvent(aEvent, aStatus))) return false; return true; } NS_IMETHODIMP MetroWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus) { if (NS_IS_INPUT_EVENT(event)) { UserActivity(); } aStatus = nsEventStatus_eIgnore; // Top level windows can have a view attached which requires events be sent // to the underlying base window and the view. Added when we combined the // base chrome window with the main content child for nc client area (title // bar) rendering. if (mAttachedWidgetListener) { aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents); } else if (mWidgetListener) { aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents); } // the window can be destroyed during processing of seemingly innocuous events like, say, // mousedowns due to the magic of scripting. mousedowns will return nsEventStatus_eIgnore, // which causes problems with the deleted window. therefore: if (mOnDestroyCalled) aStatus = nsEventStatus_eConsumeNoDefault; return NS_OK; } #ifdef ACCESSIBILITY mozilla::a11y::Accessible* MetroWidget::GetRootAccessible() { // We want the ability to forcibly disable a11y on windows, because // some non-a11y-related components attempt to bring it up. See bug // 538530 for details; we have a pref here that allows it to be disabled // for performance and testing resons. // // This pref is checked only once, and the browser needs a restart to // pick up any changes. static int accForceDisable = -1; if (accForceDisable == -1) { const char* kPrefName = "accessibility.win32.force_disabled"; if (Preferences::GetBool(kPrefName, false)) { accForceDisable = 1; } else { accForceDisable = 0; } } // If the pref was true, return null here, disabling a11y. if (accForceDisable) return nullptr; return GetAccessible(); } #endif double MetroWidget::GetDefaultScaleInternal() { // Return the resolution scale factor reported by the metro environment. // XXX TODO: also consider the desktop resolution setting, as IE appears to do? ComPtr dispProps; if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), dispProps.GetAddressOf()))) { ResolutionScale scale; if (SUCCEEDED(dispProps->get_ResolutionScale(&scale))) { return (double)scale / 100.0; } } return 1.0; } float MetroWidget::GetDPI() { LogFunction(); if (!mView) { return 96.0; } return mView->GetDPI(); } void MetroWidget::ChangedDPI() { if (mWidgetListener) { nsIPresShell* presShell = mWidgetListener->GetPresShell(); if (presShell) { presShell->BackingScaleFactorChanged(); } } } NS_IMETHODIMP MetroWidget::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) { return NS_OK; } void MetroWidget::SizeModeChanged() { if (mWidgetListener) { mWidgetListener->SizeModeChanged(nsSizeMode_Normal); } } void MetroWidget::Activated(bool aActiveated) { if (mWidgetListener) { aActiveated ? mWidgetListener->WindowActivated() : mWidgetListener->WindowDeactivated(); } } NS_IMETHODIMP MetroWidget::Move(double aX, double aY) { if (mWidgetListener) { mWidgetListener->WindowMoved(this, aX, aY); } return NS_OK; } NS_IMETHODIMP MetroWidget::Resize(double aWidth, double aHeight, bool aRepaint) { return NS_OK; } NS_IMETHODIMP MetroWidget::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) { if (mAttachedWidgetListener) { mAttachedWidgetListener->WindowResized(this, aWidth, aHeight); } if (mWidgetListener) { mWidgetListener->WindowResized(this, aWidth, aHeight); } return NS_OK; } NS_IMETHODIMP MetroWidget::SetFocus(bool aRaise) { return NS_OK; } nsresult MetroWidget::ConfigureChildren(const nsTArray& aConfigurations) { return NS_OK; } void* MetroWidget::GetNativeData(uint32_t aDataType) { switch(aDataType) { case NS_NATIVE_WINDOW: return mWnd; case NS_NATIVE_ICOREWINDOW: if (mView) { return reinterpret_cast(mView->GetCoreWindow()); } break; case NS_NATIVE_TSF_THREAD_MGR: case NS_NATIVE_TSF_CATEGORY_MGR: case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: return nsTextStore::GetNativeData(aDataType); } return nullptr; } void MetroWidget::FreeNativeData(void * data, uint32_t aDataType) { } NS_IMETHODIMP MetroWidget::SetTitle(const nsAString& aTitle) { return NS_OK; } nsIntPoint MetroWidget::WidgetToScreenOffset() { return nsIntPoint(0,0); } NS_IMETHODIMP MetroWidget::CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) { return NS_OK; } NS_IMETHODIMP_(void) MetroWidget::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { mInputContext = aContext; nsTextStore::SetInputContext(mInputContext); bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED || mInputContext.mIMEState.mEnabled == IMEState::PLUGIN); if (enable && mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) { bool open = (mInputContext.mIMEState.mOpen == IMEState::OPEN); nsTextStore::SetIMEOpenState(open); } } NS_IMETHODIMP_(nsIWidget::InputContext) MetroWidget::GetInputContext() { return mInputContext; } NS_IMETHODIMP MetroWidget::NotifyIME(NotificationToIME aNotification) { switch (aNotification) { case REQUEST_TO_COMMIT_COMPOSITION: nsTextStore::CommitComposition(false); return NS_OK; case REQUEST_TO_CANCEL_COMPOSITION: nsTextStore::CommitComposition(true); return NS_OK; case NOTIFY_IME_OF_FOCUS: return nsTextStore::OnFocusChange(true, this, mInputContext.mIMEState.mEnabled); case NOTIFY_IME_OF_BLUR: return nsTextStore::OnFocusChange(false, this, mInputContext.mIMEState.mEnabled); case NOTIFY_IME_OF_SELECTION_CHANGE: return nsTextStore::OnSelectionChange(); default: return NS_ERROR_NOT_IMPLEMENTED; } } NS_IMETHODIMP MetroWidget::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) { NS_ENSURE_ARG_POINTER(aLEDState); *aLEDState = (::GetKeyState(aKeyCode) & 1) != 0; return NS_OK; } NS_IMETHODIMP MetroWidget::NotifyIMEOfTextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd) { return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); } nsIMEUpdatePreference MetroWidget::GetIMEUpdatePreference() { return nsTextStore::GetIMEUpdatePreference(); } NS_IMETHODIMP MetroWidget::ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; } void MetroWidget::SuppressBlurEvents(bool aSuppress) { } bool MetroWidget::BlurEventsSuppressed() { return false; } void MetroWidget::PickerOpen() { } void MetroWidget::PickerClosed() { } bool MetroWidget::HasPendingInputEvent() { if (HIWORD(GetQueueStatus(QS_INPUT))) return true; return false; }