michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsWinMetroUtils.h" michael@0: #include "MetroUtils.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "FrameworkView.h" michael@0: #include "MetroApp.h" michael@0: #include "ToastNotificationHandler.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: #include "nsIWindowsRegKey.h" michael@0: #include "mozilla/widget/MetroD3DCheckHelper.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: using namespace ABI::Windows::Foundation; michael@0: using namespace ABI::Windows::UI::StartScreen; michael@0: using namespace ABI::Windows::UI::ViewManagement; michael@0: using namespace Microsoft::WRL; michael@0: using namespace Microsoft::WRL::Wrappers; michael@0: using namespace mozilla::widget::winrt; michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: namespace winrt { michael@0: extern ComPtr sMetroApp; michael@0: extern nsTArray* sSettingsArray; michael@0: } } } michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: bool nsWinMetroUtils::sUpdatePending = false; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsWinMetroUtils, nsIWinMetroUtils) michael@0: michael@0: nsWinMetroUtils::nsWinMetroUtils() michael@0: { michael@0: } michael@0: michael@0: nsWinMetroUtils::~nsWinMetroUtils() michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * Pins a new tile to the Windows 8 start screen. michael@0: * michael@0: * @param aTileID An ID which can later be used to remove the tile michael@0: * @param aShortName A short name for the tile michael@0: * @param aDiplayName The name that will be displayed on the tile michael@0: * @param aActivationArgs The arguments to pass to the browser upon michael@0: * activation of the tile michael@0: * @param aTileImage An image for the normal tile view michael@0: * @param aSmallTileImage An image for the small tile view michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::PinTileAsync(const nsAString &aTileID, michael@0: const nsAString &aShortName, michael@0: const nsAString &aDisplayName, michael@0: const nsAString &aActivationArgs, michael@0: const nsAString &aTileImage, michael@0: const nsAString &aSmallTileImage) michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: NS_WARNING("PinTileAsync can't be called on the desktop."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: HRESULT hr; michael@0: michael@0: HString logoStr, smallLogoStr, displayName, shortName; michael@0: michael@0: logoStr.Set(aTileImage.BeginReading()); michael@0: smallLogoStr.Set(aSmallTileImage.BeginReading()); michael@0: displayName.Set(aDisplayName.BeginReading()); michael@0: shortName.Set(aShortName.BeginReading()); michael@0: michael@0: ComPtr logo, smallLogo; michael@0: AssertRetHRESULT(MetroUtils::CreateUri(logoStr, logo), NS_ERROR_FAILURE); michael@0: AssertRetHRESULT(MetroUtils::CreateUri(smallLogoStr, smallLogo), NS_ERROR_FAILURE); michael@0: michael@0: HString tileActivationArgumentsStr, tileIdStr; michael@0: tileActivationArgumentsStr.Set(aActivationArgs.BeginReading()); michael@0: tileIdStr.Set(aTileID.BeginReading()); michael@0: michael@0: ComPtr tileFactory; michael@0: ComPtr secondaryTile; michael@0: hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_StartScreen_SecondaryTile).Get(), michael@0: tileFactory.GetAddressOf()); michael@0: AssertRetHRESULT(hr, NS_ERROR_FAILURE); michael@0: hr = tileFactory->CreateWithId(tileIdStr.Get(), secondaryTile.GetAddressOf()); michael@0: AssertRetHRESULT(hr, NS_ERROR_FAILURE); michael@0: michael@0: secondaryTile->put_Logo(logo.Get()); michael@0: secondaryTile->put_SmallLogo(smallLogo.Get()); michael@0: secondaryTile->put_DisplayName(displayName.Get()); michael@0: secondaryTile->put_ShortName(shortName.Get()); michael@0: secondaryTile->put_Arguments(tileActivationArgumentsStr.Get()); michael@0: secondaryTile->put_TileOptions(TileOptions::TileOptions_ShowNameOnLogo); michael@0: michael@0: // The tile is created and we can now attempt to pin the tile. michael@0: ComPtr> callback(Callback>( michael@0: sMetroApp.Get(), &MetroApp::OnAsyncTileCreated)); michael@0: ComPtr> operation; michael@0: AssertRetHRESULT(secondaryTile->RequestCreateAsync(operation.GetAddressOf()), NS_ERROR_FAILURE); michael@0: operation->put_Completed(callback.Get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Unpins a tile from the Windows 8 start screen. michael@0: * michael@0: * @param aTileID An existing ID which was previously pinned michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::UnpinTileAsync(const nsAString &aTileID) michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: NS_WARNING("UnpinTileAsync can't be called on the desktop."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: HRESULT hr; michael@0: HString tileIdStr; michael@0: tileIdStr.Set(aTileID.BeginReading()); michael@0: michael@0: ComPtr tileFactory; michael@0: ComPtr secondaryTile; michael@0: hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_StartScreen_SecondaryTile).Get(), michael@0: tileFactory.GetAddressOf()); michael@0: AssertRetHRESULT(hr, NS_ERROR_FAILURE); michael@0: hr = tileFactory->CreateWithId(tileIdStr.Get(), secondaryTile.GetAddressOf()); michael@0: AssertRetHRESULT(hr, NS_ERROR_FAILURE); michael@0: michael@0: // Attempt to unpin the tile michael@0: ComPtr> callback(Callback>( michael@0: sMetroApp.Get(), &MetroApp::OnAsyncTileCreated)); michael@0: ComPtr> operation; michael@0: AssertRetHRESULT(secondaryTile->RequestDeleteAsync(operation.GetAddressOf()), NS_ERROR_FAILURE); michael@0: operation->put_Completed(callback.Get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Determines if a tile is pinned to the Windows 8 start screen. michael@0: * michael@0: * @param aTileID An ID which may have been pinned with pinTileAsync michael@0: * @param aIsPinned Out parameter for determining if the tile is pinned or not michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::IsTilePinned(const nsAString &aTileID, bool *aIsPinned) michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: NS_WARNING("IsTilePinned can't be called on the desktop."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: NS_ENSURE_ARG_POINTER(aIsPinned); michael@0: michael@0: HRESULT hr; michael@0: HString tileIdStr; michael@0: tileIdStr.Set(aTileID.BeginReading()); michael@0: michael@0: ComPtr tileStatics; michael@0: hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_StartScreen_SecondaryTile).Get(), michael@0: tileStatics.GetAddressOf()); michael@0: AssertRetHRESULT(hr, NS_ERROR_FAILURE); michael@0: boolean result = false; michael@0: tileStatics->Exists(tileIdStr.Get(), &result); michael@0: *aIsPinned = result; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Launches the specified application with the specified arguments and michael@0: * switches to Desktop mode if in metro mode. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::LaunchInDesktop(const nsAString &aPath, const nsAString &aArguments) michael@0: { michael@0: SHELLEXECUTEINFOW sinfo; michael@0: memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW)); michael@0: sinfo.cbSize = sizeof(SHELLEXECUTEINFOW); michael@0: // Per the Metro style enabled desktop browser, for some reason, michael@0: // SEE_MASK_FLAG_LOG_USAGE is needed to change from immersive mode michael@0: // to desktop. michael@0: sinfo.fMask = SEE_MASK_FLAG_LOG_USAGE; michael@0: sinfo.hwnd = nullptr; michael@0: sinfo.lpFile = aPath.BeginReading(); michael@0: sinfo.lpParameters = aArguments.BeginReading(); michael@0: sinfo.lpVerb = L"open"; michael@0: sinfo.nShow = SW_SHOWNORMAL; michael@0: michael@0: if (!ShellExecuteEx(&sinfo)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::ShowNativeToast(const nsAString &aTitle, michael@0: const nsAString &aMessage, const nsAString &anImage, michael@0: const nsAString &aCookie, const nsAString& aAppId) michael@0: { michael@0: ToastNotificationHandler* notification_handler = michael@0: new ToastNotificationHandler; michael@0: michael@0: HSTRING title = HStringReference(aTitle.BeginReading()).Get(); michael@0: HSTRING msg = HStringReference(aMessage.BeginReading()).Get(); michael@0: michael@0: bool ret; michael@0: if (anImage.Length() > 0) { michael@0: HSTRING imagePath = HStringReference(anImage.BeginReading()).Get(); michael@0: ret = notification_handler->DisplayNotification(title, msg, imagePath, michael@0: aCookie, michael@0: aAppId); michael@0: } else { michael@0: ret = notification_handler->DisplayTextNotification(title, msg, aCookie, michael@0: aAppId); michael@0: } michael@0: michael@0: if (!ret) { michael@0: delete notification_handler; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::ShowSettingsFlyout() michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: NS_WARNING("Settings flyout can't be shown on the desktop."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: HRESULT hr = MetroUtils::ShowSettingsFlyout(); michael@0: return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetImmersive(bool *aImersive) michael@0: { michael@0: *aImersive = michael@0: XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetActivationURI(nsAString &aActivationURI) michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: FrameworkView::GetActivationURI(aActivationURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetPreviousExecutionState(int32_t *out) michael@0: { michael@0: if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: *out = FrameworkView::GetPreviousExecutionState(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetKeyboardVisible(bool *aImersive) michael@0: { michael@0: *aImersive = FrameworkView::IsKeyboardVisible(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetKeyboardX(uint32_t *aX) michael@0: { michael@0: *aX = static_cast(floor(FrameworkView::KeyboardVisibleRect().X)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetKeyboardY(uint32_t *aY) michael@0: { michael@0: *aY = static_cast(floor(FrameworkView::KeyboardVisibleRect().Y)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetKeyboardWidth(uint32_t *aWidth) michael@0: { michael@0: *aWidth = static_cast(ceil(FrameworkView::KeyboardVisibleRect().Width)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetKeyboardHeight(uint32_t *aHeight) michael@0: { michael@0: *aHeight = static_cast(ceil(FrameworkView::KeyboardVisibleRect().Height)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::AddSettingsPanelEntry(const nsAString &aLabel, uint32_t *aId) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aId); michael@0: if (!sSettingsArray) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *aId = sSettingsArray->Length(); michael@0: sSettingsArray->AppendElement(nsString(aLabel)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::SwapMouseButton(bool aValue, bool *aOriginalValue) michael@0: { michael@0: *aOriginalValue = ::SwapMouseButton(aValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetUpdatePending(bool *aUpdatePending) michael@0: { michael@0: *aUpdatePending = sUpdatePending; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::SetUpdatePending(bool aUpdatePending) michael@0: { michael@0: sUpdatePending = aUpdatePending; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetForeground(bool* aForeground) michael@0: { michael@0: *aForeground = (::GetActiveWindow() == ::GetForegroundWindow()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWinMetroUtils::GetSupported(bool *aSupported) michael@0: { michael@0: *aSupported = false; michael@0: if (!IsWin8OrLater()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if last_used_feature_level_idx is set, we've previously created a michael@0: // d3d device that's compatible. See gfxEindowsPlatform for details. michael@0: if (Preferences::GetInt("gfx.direct3d.last_used_feature_level_idx", -1) != -1) { michael@0: *aSupported = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if last_used_feature_level_idx isn't set, gfx hasn't attempted to create michael@0: // a device yet. This could be a case where d2d is pref'd off or blacklisted michael@0: // on desktop, or we tried to create a device and failed. This could also be michael@0: // a first run case where we haven't created an accelerated top level window michael@0: // yet. michael@0: michael@0: NS_NAMED_LITERAL_STRING(metroRegValueName, "MetroD3DAvailable"); michael@0: NS_NAMED_LITERAL_STRING(metroRegValuePath, "Software\\Mozilla\\Firefox"); michael@0: michael@0: // Check to see if the ceh launched us, it also does this check and caches michael@0: // a flag in the registry. michael@0: nsresult rv; michael@0: uint32_t value = 0; michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, michael@0: metroRegValuePath, michael@0: nsIWindowsRegKey::ACCESS_ALL); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = regKey->ReadIntValue(metroRegValueName, &value); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *aSupported = (bool)value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If all else fails, do the check here. This call is costly but michael@0: // we shouldn't hit this except in rare situations where the michael@0: // ceh never launched the browser that's running. michael@0: value = D3DFeatureLevelCheck(); michael@0: regKey->WriteIntValue(metroRegValueName, value); michael@0: *aSupported = (bool)value; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // widget michael@0: } // mozilla