diff -r 000000000000 -r 6474c204b198 widget/windows/winrt/MetroContracts.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widget/windows/winrt/MetroContracts.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,573 @@ +/* -*- 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 "FrameworkView.h" +#include "MetroUtils.h" +#include "nsICommandLineRunner.h" +#include "nsNetUtil.h" +#include "nsIDOMChromeWindow.h" +#include "nsIURI.h" +#include "nsPrintfCString.h" +#include "mozilla/Services.h" +#include +#include +#include +#include +#include +#include +#include "MetroUIUtils.h" +#include "nsIStringBundle.h" + +using namespace mozilla; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +// Play to contract +using namespace ABI::Windows::Media::PlayTo; + +// Activation contracts +using namespace ABI::Windows::ApplicationModel::Activation; +using namespace ABI::Windows::ApplicationModel::DataTransfer; +using namespace ABI::Windows::ApplicationModel::Search; + +// Settings contract +using namespace ABI::Windows::UI::ApplicationSettings; +using namespace ABI::Windows::UI::Popups; + +// Print contract +using namespace ABI::Windows::Graphics::Printing; + +namespace mozilla { +namespace widget { +namespace winrt { + +extern nsTArray* sSettingsArray; + +void +FrameworkView::SearchActivated(ComPtr& aArgs, bool aStartup) +{ + if (!aArgs) + return; + + HString data; + AssertHRESULT(aArgs->get_QueryText(data.GetAddressOf())); + if (WindowsIsStringEmpty(data.Get())) + return; + + unsigned int length; + WinUtils::LogW(L"SearchActivated text=%s", data.GetRawBuffer(&length)); + if (aStartup) { + WindowsDuplicateString(data.Get(), &sActivationURI); + } else { + PerformURILoadOrSearch(data); + } +} + +void +FrameworkView::FileActivated(ComPtr& aArgs, bool aStartup) +{ + if (!aArgs) + return; + + ComPtr> list; + AssertHRESULT(aArgs->get_Files(list.GetAddressOf())); + ComPtr item; + AssertHRESULT(list->GetAt(0, item.GetAddressOf())); + HString filePath; + AssertHRESULT(item->get_Path(filePath.GetAddressOf())); + + if (aStartup) { + WindowsDuplicateString(filePath.Get(), &sActivationURI); + } else { + PerformURILoad(filePath); + } +} + +void +FrameworkView::LaunchActivated(ComPtr& aArgs, bool aStartup) +{ + if (!aArgs) + return; + HString data; + AssertHRESULT(aArgs->get_Arguments(data.GetAddressOf())); + if (WindowsIsStringEmpty(data.Get())) + return; + + // If we're being launched from a secondary tile then we have a 2nd command line param of -url + // and a third of the secondary tile. We want it in sActivationURI so that browser.js will + // load it in without showing the start UI. + int argc; + unsigned int length; + LPWSTR* argv = CommandLineToArgvW(data.GetRawBuffer(&length), &argc); + if (aStartup && argc == 2 && !wcsicmp(argv[0], L"-url")) { + WindowsCreateString(argv[1], wcslen(argv[1]), &sActivationURI); + } else { + // Some other command line or this is not a startup. + // If it is startup we process it later when XPCOM is initialilzed. + mActivationCommandLine = data.GetRawBuffer(&length); + if (!aStartup) { + ProcessLaunchArguments(); + } + } +} + +void +FrameworkView::ProcessLaunchArguments() +{ + if (!mActivationCommandLine.Length()) + return; + + int argc; + LPWSTR* argv = CommandLineToArgvW(mActivationCommandLine.BeginReading(), &argc); + nsCOMPtr cmdLine = + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + if (!cmdLine) { + NS_WARNING("Unable to instantiate command line runner."); + return; + } + + LPSTR *argvUTF8 = new LPSTR[argc]; + for (int i = 0; i < argc; ++i) { + NS_ConvertUTF16toUTF8 arg(argv[i]); + argvUTF8[i] = new char[arg.Length() + 1]; + strcpy(argvUTF8[i], const_cast(arg.BeginReading())); + WinUtils::LogW(L"Launch arg[%d]: '%s'", i, argv[i]); + } + + nsresult rv = cmdLine->Init(argc, + argvUTF8, + nullptr, + nsICommandLine::STATE_REMOTE_EXPLICIT); + if (NS_SUCCEEDED(rv)) { + cmdLine->Run(); + } else { + NS_WARNING("cmdLine->Init failed."); + } + + for (int i = 0; i < argc; ++i) { + delete[] argvUTF8[i]; + } + delete[] argvUTF8; +} + +void +FrameworkView::ProcessActivationArgs(IActivatedEventArgs* aArgs, bool aStartup) +{ + ActivationKind kind; + if (!aArgs || FAILED(aArgs->get_Kind(&kind))) + return; + ComPtr args(aArgs); + if (kind == ActivationKind::ActivationKind_Protocol) { + WinUtils::Log("Activation argument kind: Protocol"); + ComPtr protoArgs; + AssertHRESULT(args.As(&protoArgs)); + ComPtr uri; + AssertHRESULT(protoArgs->get_Uri(uri.GetAddressOf())); + if (!uri) + return; + + HString data; + AssertHRESULT(uri->get_AbsoluteUri(data.GetAddressOf())); + if (WindowsIsStringEmpty(data.Get())) + return; + + if (aStartup) { + WindowsDuplicateString(data.Get(), &sActivationURI); + } else { + PerformURILoad(data); + } + } else if (kind == ActivationKind::ActivationKind_Search) { + WinUtils::Log("Activation argument kind: Search"); + ComPtr searchArgs; + args.As(&searchArgs); + SearchActivated(searchArgs, aStartup); + } else if (kind == ActivationKind::ActivationKind_File) { + WinUtils::Log("Activation argument kind: File"); + ComPtr fileArgs; + args.As(&fileArgs); + FileActivated(fileArgs, aStartup); + } else if (kind == ActivationKind::ActivationKind_Launch) { + WinUtils::Log("Activation argument kind: Launch"); + ComPtr launchArgs; + args.As(&launchArgs); + LaunchActivated(launchArgs, aStartup); + } +} + +void +FrameworkView::SetupContracts() +{ + LogFunction(); + HRESULT hr; + + // Add support for the share charm to indicate that we share data to other apps + ComPtr transStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager).Get(), + transStatics.GetAddressOf()); + AssertHRESULT(hr); + ComPtr trans; + AssertHRESULT(transStatics->GetForCurrentView(trans.GetAddressOf())); + trans->add_DataRequested(Callback<__FITypedEventHandler_2_Windows__CApplicationModel__CDataTransfer__CDataTransferManager_Windows__CApplicationModel__CDataTransfer__CDataRequestedEventArgs_t>( + this, &FrameworkView::OnDataShareRequested).Get(), &mDataTransferRequested); + + // Add support for the search charm to indicate that you can search using our app. + ComPtr searchStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_Search_SearchPane).Get(), + searchStatics.GetAddressOf()); + AssertHRESULT(hr); + ComPtr searchPane; + AssertHRESULT(searchStatics->GetForCurrentView(searchPane.GetAddressOf())); + searchPane->add_QuerySubmitted(Callback<__FITypedEventHandler_2_Windows__CApplicationModel__CSearch__CSearchPane_Windows__CApplicationModel__CSearch__CSearchPaneQuerySubmittedEventArgs_t>( + this, &FrameworkView::OnSearchQuerySubmitted).Get(), &mSearchQuerySubmitted); + + // Add support for the devices play to charm + ComPtr playToStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Media_PlayTo_PlayToManager).Get(), + playToStatics.GetAddressOf()); + AssertHRESULT(hr); + ComPtr playTo; + AssertHRESULT(playToStatics->GetForCurrentView(playTo.GetAddressOf())); + playTo->add_SourceRequested(Callback<__FITypedEventHandler_2_Windows__CMedia__CPlayTo__CPlayToManager_Windows__CMedia__CPlayTo__CPlayToSourceRequestedEventArgs_t>( + this, &FrameworkView::OnPlayToSourceRequested).Get(), &mPlayToRequested); + + // Add support for the settings charm + ComPtr settingsPaneStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_ApplicationSettings_SettingsPane).Get(), + settingsPaneStatics.GetAddressOf()); + AssertHRESULT(hr); + ComPtr settingsPane; + AssertHRESULT(settingsPaneStatics->GetForCurrentView(settingsPane.GetAddressOf())); + settingsPane->add_CommandsRequested(Callback<__FITypedEventHandler_2_Windows__CUI__CApplicationSettings__CSettingsPane_Windows__CUI__CApplicationSettings__CSettingsPaneCommandsRequestedEventArgs_t>( + this, &FrameworkView::OnSettingsCommandsRequested).Get(), &mSettingsPane); + + // Add support for the settings print charm + ComPtr printStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Printing_PrintManager).Get(), + printStatics.GetAddressOf()); + AssertHRESULT(hr); + ComPtr printManager; + AssertHRESULT(printStatics->GetForCurrentView(printManager.GetAddressOf())); + printManager->add_PrintTaskRequested(Callback<__FITypedEventHandler_2_Windows__CGraphics__CPrinting__CPrintManager_Windows__CGraphics__CPrinting__CPrintTaskRequestedEventArgs_t>( + this, &FrameworkView::OnPrintTaskRequested).Get(), &mPrintManager); +} + +void +FrameworkView::PerformURILoad(HString& aURI) +{ + LogFunction(); + + unsigned int length; + WinUtils::LogW(L"PerformURILoad uri=%s", aURI.GetRawBuffer(&length)); + + nsCOMPtr cmdLine = + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + if (!cmdLine) { + NS_WARNING("Unable to instantiate command line runner."); + return; + } + + nsAutoCString utf8data(NS_ConvertUTF16toUTF8(aURI.GetRawBuffer(&length))); + + // NB: The first argument gets stripped by nsICommandLineRunner::Init, + // so it doesn't matter what we pass as the first argument, but we + // have to pass something. + const char *argv[] = { "", // This argument gets stripped + "-url", + utf8data.BeginReading() }; + nsresult rv = cmdLine->Init(ArrayLength(argv), + const_cast(argv), nullptr, + nsICommandLine::STATE_REMOTE_EXPLICIT); + if (NS_FAILED(rv)) { + NS_WARNING("cmdLine->Init failed."); + return; + } + cmdLine->Run(); +} + +void +FrameworkView::PerformSearch(HString& aQuery) +{ + LogFunction(); + + nsCOMPtr cmdLine = + (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); + if (!cmdLine) { + NS_WARNING("Unable to instantiate command line runner."); + return; + } + + nsAutoCString parameter; + parameter.AppendLiteral("\""); + unsigned int length; + parameter.Append(NS_ConvertUTF16toUTF8(aQuery.GetRawBuffer(&length))); + parameter.AppendLiteral("\""); + + // NB: The first argument gets stripped by nsICommandLineRunner::Init, + // so it doesn't matter what we pass as the first argument, but we + // have to pass something. + const char *argv[] = { "", // This argument gets stripped + "-search", + parameter.BeginReading() }; + nsresult rv = cmdLine->Init(ArrayLength(argv), + const_cast(argv), nullptr, + nsICommandLine::STATE_REMOTE_EXPLICIT); + if (NS_FAILED(rv)) { + NS_WARNING("cmdLine->Init failed."); + return; + } + cmdLine->Run(); +} + +void +FrameworkView::PerformURILoadOrSearch(HString& aString) +{ + LogFunction(); + + if (WindowsIsStringEmpty(aString.Get())) { + WinUtils::Log("Emptry string passed to PerformURILoadOrSearch"); + return; + } + + // If we have a URI then devert to load the URI directly + ComPtr uri; + MetroUtils::CreateUri(aString.Get(), uri); + if (uri) { + PerformURILoad(aString); + } else { + PerformSearch(aString); + } +} + +HRESULT +FrameworkView::OnDataShareRequested(IDataTransferManager* aDTM, + IDataRequestedEventArgs* aArg) +{ + // Only share pages that contain a title and a URI + nsCOMPtr metroUIUtils = do_CreateInstance("@mozilla.org/metro-ui-utils;1"); + if (!metroUIUtils) + return E_FAIL; + + nsString url, title; + nsresult rv = metroUIUtils->GetCurrentPageURI(url); + nsresult rv2 = metroUIUtils->GetCurrentPageTitle(title); + if (NS_FAILED(rv) || NS_FAILED(rv2)) { + return E_FAIL; + } + + // Get the package to share + HRESULT hr; + ComPtr request; + AssertRetHRESULT(hr = aArg->get_Request(request.GetAddressOf()), hr); + ComPtr dataPackage; + AssertRetHRESULT(hr = request->get_Data(dataPackage.GetAddressOf()), hr); + ComPtr props; + AssertRetHRESULT(hr = dataPackage->get_Properties(props.GetAddressOf()), hr); + + // Only add a URI to the package when there is no selected content. + // This is because most programs treat URIs as highest priority to generate + // their own preview, but we only want the selected content to show up. + bool hasSelectedContent = false; + metroUIUtils->GetHasSelectedContent(&hasSelectedContent); + if (!hasSelectedContent) { + ComPtr uri; + AssertRetHRESULT(hr = MetroUtils::CreateUri(HStringReference(url.BeginReading()).Get(), uri), hr); + + // If there is no selection, then we don't support sharing for sites that + // are not HTTP, HTTPS, FTP, and FILE. + HString schemeHString; + uri->get_SchemeName(schemeHString.GetAddressOf()); + unsigned int length; + LPCWSTR scheme = schemeHString.GetRawBuffer(&length); + if (!scheme || wcscmp(scheme, L"http") && wcscmp(scheme, L"https") && + wcscmp(scheme, L"ftp") && wcscmp(scheme, L"file")) { + return S_OK; + } + + AssertRetHRESULT(hr = dataPackage->SetUri(uri.Get()), hr); + } + + // Add whatever content metroUIUtils wants us to for the text sharing + nsString shareText; + if (NS_SUCCEEDED(metroUIUtils->GetShareText(shareText)) && shareText.Length()) { + AssertRetHRESULT(hr = dataPackage->SetText(HStringReference(shareText.BeginReading()).Get()) ,hr); + } + + // Add whatever content metroUIUtils wants us to for the HTML sharing + nsString shareHTML; + if (NS_SUCCEEDED(metroUIUtils->GetShareHTML(shareHTML)) && shareHTML.Length()) { + // The sharing format needs some special headers, so pass it through Windows + ComPtr htmlFormatHelper; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_HtmlFormatHelper).Get(), + htmlFormatHelper.GetAddressOf()); + AssertRetHRESULT(hr, hr); + HSTRING fixedHTML; + htmlFormatHelper->CreateHtmlFormat(HStringReference(shareHTML.BeginReading()).Get(), &fixedHTML); + + // And now add the fixed HTML to the data package + AssertRetHRESULT(hr = dataPackage->SetHtmlFormat(fixedHTML), hr); + } + + // Obtain the brand name + nsCOMPtr bundleService = + do_GetService(NS_STRINGBUNDLE_CONTRACTID); + NS_ENSURE_TRUE(bundleService, E_FAIL); + nsCOMPtr brandBundle; + nsString brandName; + bundleService->CreateBundle("chrome://branding/locale/brand.properties", + getter_AddRefs(brandBundle)); + NS_ENSURE_TRUE(brandBundle, E_FAIL); + if(brandBundle) { + brandBundle->GetStringFromName(MOZ_UTF16("brandFullName"), + getter_Copies(brandName)); + } + + // Set these properties at the end. Otherwise users can get a + // "There was a problem with the data package" error when there + // is simply nothing to share. + props->put_ApplicationName(HStringReference(brandName.BeginReading()).Get()); + if (title.Length()) { + props->put_Title(HStringReference(title.BeginReading()).Get()); + } else { + props->put_Title(HStringReference(brandName.BeginReading()).Get()); + } + props->put_Description(HStringReference(url.BeginReading()).Get()); + + return S_OK; +} + +HRESULT +FrameworkView::OnSearchQuerySubmitted(ISearchPane* aPane, + ISearchPaneQuerySubmittedEventArgs* aArgs) +{ + LogFunction(); + HString aQuery; + aArgs->get_QueryText(aQuery.GetAddressOf()); + PerformURILoadOrSearch(aQuery); + return S_OK; +} + +HRESULT +FrameworkView::OnSettingsCommandInvoked(IUICommand* aCommand) +{ + LogFunction(); + HRESULT hr; + uint32_t id; + ComPtr prop; + AssertRetHRESULT(hr = aCommand->get_Id((IInspectable**)prop.GetAddressOf()), hr); + AssertRetHRESULT(hr = prop->GetUInt32(&id), hr); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + NS_ConvertASCIItoUTF16 idStr(nsPrintfCString("%lu", id)); + obs->NotifyObservers(nullptr, "metro-settings-entry-selected", idStr.BeginReading()); + } + + return S_OK; +} + +void +FrameworkView::AddSetting(ISettingsPaneCommandsRequestedEventArgs* aArgs, + uint32_t aId, HString& aSettingName) +{ + HRESULT hr; + + ComPtr request; + AssertHRESULT(aArgs->get_Request(request.GetAddressOf())); + + // ApplicationCommands - vector that holds SettingsCommand to be invoked + ComPtr> list; + AssertHRESULT(request->get_ApplicationCommands(list.GetAddressOf())); + + ComPtr command; + ComPtr factory; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_ApplicationSettings_SettingsCommand).Get(), + factory.GetAddressOf()); + AssertHRESULT(hr); + + // Create the IInspectable string property that identifies this command + ComPtr prop; + ComPtr propStatics; + hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), + propStatics.GetAddressOf()); + AssertHRESULT(hr); + hr = propStatics->CreateUInt32(aId, prop.GetAddressOf()); + AssertHRESULT(hr); + + // Create the command + hr = factory->CreateSettingsCommand(prop.Get(), aSettingName.Get(), + Callback( + this, &FrameworkView::OnSettingsCommandInvoked).Get(), command.GetAddressOf()); + AssertHRESULT(hr); + + // Add it to the list + hr = list->Append(command.Get()); + AssertHRESULT(hr); +} + +HRESULT +FrameworkView::OnSettingsCommandsRequested(ISettingsPane* aPane, + ISettingsPaneCommandsRequestedEventArgs* aArgs) +{ + if (!sSettingsArray) + return E_FAIL; + if (!sSettingsArray->Length()) + return S_OK; + for (uint32_t i = 0; i < sSettingsArray->Length(); i++) { + HString label; + label.Set(sSettingsArray->ElementAt(i).BeginReading()); + AddSetting(aArgs, i, label); + } + return S_OK; +} + +HRESULT +FrameworkView::OnPlayToSourceRequested(IPlayToManager* aPlayToManager, + IPlayToSourceRequestedEventArgs* aArgs) +{ + // TODO: Implement PlayTo, find the element on the page and then do something similar to this: + // PlayToReceiver::Dispatcher.Helper.BeginInvoke( + // mMediaElement = ref new Windows::UI::Xaml::Controls::MediaElement(); + // mMediaElement->Source = ref new Uri("http://www.youtube.com/watch?v=2U0NFgoNI7s"); + // aArgs->SourceRequest->SetSource(mMediaElement->PlayToSource); + return S_OK; +} + +HRESULT +FrameworkView::OnPrintTaskSourceRequested(IPrintTaskSourceRequestedArgs* aArgs) +{ + return S_OK; +} + +HRESULT +FrameworkView::OnPrintTaskRequested(IPrintManager* aPrintManager, + IPrintTaskRequestedEventArgs* aArgs) +{ + return S_OK; +} + +void +FrameworkView::CreatePrintControl(IPrintDocumentPackageTarget* docPackageTarget, + D2D1_PRINT_CONTROL_PROPERTIES* printControlProperties) +{ +} + +HRESULT +FrameworkView::ClosePrintControl() +{ + return S_OK; +} + +// Print out one page, with the given print ticket. +// This sample has only one page and we ignore pageNumber below. +void FrameworkView::PrintPage(uint32_t pageNumber, + D2D1_RECT_F imageableArea, + D2D1_SIZE_F pageSize, + IStream* pagePrintTicketStream) +{ +} + +} } }