diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 03:35:06 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 03:35:06 -0500 |
commit | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (patch) | |
tree | 6efefe6a09feb09d965932b24e10436b9ac8189c /application/palemoon/components/shell | |
parent | e72ef92b5bdc43cd2584198e2e54e951b70299e8 (diff) | |
download | UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.gz UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.lz UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.tar.xz UXP-49ee0794b5d912db1f95dce6eb52d781dc210db5.zip |
Add Pale Moon
Diffstat (limited to 'application/palemoon/components/shell')
17 files changed, 2990 insertions, 0 deletions
diff --git a/application/palemoon/components/shell/Makefile.in b/application/palemoon/components/shell/Makefile.in new file mode 100644 index 000000000..df084b709 --- /dev/null +++ b/application/palemoon/components/shell/Makefile.in @@ -0,0 +1,11 @@ +# +# 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 $(topsrcdir)/config/rules.mk + +CXXFLAGS += $(TK_CFLAGS) + +clobber:: + rm -f $(DIST)/lib/$(LIBRARY_NAME).lib diff --git a/application/palemoon/components/shell/content/setDesktopBackground.js b/application/palemoon/components/shell/content/setDesktopBackground.js new file mode 100644 index 000000000..e90a32d03 --- /dev/null +++ b/application/palemoon/components/shell/content/setDesktopBackground.js @@ -0,0 +1,213 @@ +# 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/. + +var Ci = Components.interfaces; + +var gSetBackground = { +#ifndef XP_MACOSX + _position : "", + _backgroundColor : 0, +#else + _position : "STRETCH", +#endif + _screenWidth : 0, + _screenHeight : 0, + _image : null, + _canvas : null, + + get _shell() + { + return Components.classes["@mozilla.org/browser/shell-service;1"] + .getService(Ci.nsIShellService); + }, + + load: function () + { + this._canvas = document.getElementById("screen"); + this._screenWidth = screen.width; + this._screenHeight = screen.height; +#ifdef XP_MACOSX + document.documentElement.getButton("accept").hidden = true; +#endif + if (this._screenWidth / this._screenHeight >= 1.6) + document.getElementById("monitor").setAttribute("aspectratio", "16:10"); + +#ifdef XP_WIN + // hide fill + fit options if <win7 since don't work + var version = Components.classes["@mozilla.org/system-info;1"] + .getService(Ci.nsIPropertyBag2) + .getProperty("version"); + var isWindows7OrHigher = (parseFloat(version) >= 6.1); + if (!isWindows7OrHigher) { + document.getElementById("fillPosition").hidden = true; + document.getElementById("fitPosition").hidden = true; + } +#endif + + // make sure that the correct dimensions will be used + setTimeout(function(self) { + self.init(window.arguments[0]); + }, 0, this); + }, + + init: function (aImage) + { + this._image = aImage; + + // set the size of the coordinate space + this._canvas.width = this._canvas.clientWidth; + this._canvas.height = this._canvas.clientHeight; + + var ctx = this._canvas.getContext("2d"); + ctx.scale(this._canvas.clientWidth / this._screenWidth, this._canvas.clientHeight / this._screenHeight); + +#ifndef XP_MACOSX + this._initColor(); +#else + // Make sure to reset the button state in case the user has already + // set an image as their desktop background. + var setDesktopBackground = document.getElementById("setDesktopBackground"); + setDesktopBackground.hidden = false; + var bundle = document.getElementById("backgroundBundle"); + setDesktopBackground.label = bundle.getString("DesktopBackgroundSet"); + setDesktopBackground.disabled = false; + + document.getElementById("showDesktopPreferences").hidden = true; +#endif + this.updatePosition(); + }, + +#ifndef XP_MACOSX + _initColor: function () + { + var color = this._shell.desktopBackgroundColor; + + const rMask = 4294901760; + const gMask = 65280; + const bMask = 255; + var r = (color & rMask) >> 16; + var g = (color & gMask) >> 8; + var b = (color & bMask); + this.updateColor(this._rgbToHex(r, g, b)); + + var colorpicker = document.getElementById("desktopColor"); + colorpicker.color = this._backgroundColor; + }, + + updateColor: function (aColor) + { + this._backgroundColor = aColor; + this._canvas.style.backgroundColor = aColor; + }, + + // Converts a color string in the format "#RRGGBB" to an integer. + _hexStringToLong: function (aString) + { + return parseInt(aString.substring(1,3), 16) << 16 | + parseInt(aString.substring(3,5), 16) << 8 | + parseInt(aString.substring(5,7), 16); + }, + + _rgbToHex: function (aR, aG, aB) + { + return "#" + [aR, aG, aB].map(function(aInt) aInt.toString(16).replace(/^(.)$/, "0$1")) + .join("").toUpperCase(); + }, +#else + observe: function (aSubject, aTopic, aData) + { + if (aTopic == "shell:desktop-background-changed") { + document.getElementById("setDesktopBackground").hidden = true; + document.getElementById("showDesktopPreferences").hidden = false; + + Components.classes["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .removeObserver(this, "shell:desktop-background-changed"); + } + }, + + showDesktopPrefs: function() + { + this._shell.openApplication(Ci.nsIMacShellService.APPLICATION_DESKTOP); + }, +#endif + + setDesktopBackground: function () + { +#ifndef XP_MACOSX + document.persist("menuPosition", "value"); + this._shell.desktopBackgroundColor = this._hexStringToLong(this._backgroundColor); +#else + Components.classes["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .addObserver(this, "shell:desktop-background-changed", false); + + var bundle = document.getElementById("backgroundBundle"); + var setDesktopBackground = document.getElementById("setDesktopBackground"); + setDesktopBackground.disabled = true; + setDesktopBackground.label = bundle.getString("DesktopBackgroundDownloading"); +#endif + this._shell.setDesktopBackground(this._image, + Ci.nsIShellService["BACKGROUND_" + this._position]); + }, + + updatePosition: function () + { + var ctx = this._canvas.getContext("2d"); + ctx.clearRect(0, 0, this._screenWidth, this._screenHeight); + +#ifndef XP_MACOSX + this._position = document.getElementById("menuPosition").value; +#endif + + switch (this._position) { + case "TILE": + ctx.save(); + ctx.fillStyle = ctx.createPattern(this._image, "repeat"); + ctx.fillRect(0, 0, this._screenWidth, this._screenHeight); + ctx.restore(); + break; + case "STRETCH": + ctx.drawImage(this._image, 0, 0, this._screenWidth, this._screenHeight); + break; + case "CENTER": + var x = (this._screenWidth - this._image.naturalWidth) / 2; + var y = (this._screenHeight - this._image.naturalHeight) / 2; + ctx.drawImage(this._image, x, y); + break; + case "FILL": + //Try maxing width first, overflow height + var widthRatio = this._screenWidth / this._image.naturalWidth; + var width = this._image.naturalWidth * widthRatio; + var height = this._image.naturalHeight * widthRatio; + if (height < this._screenHeight) { + //height less than screen, max height and overflow width + var heightRatio = this._screenHeight / this._image.naturalHeight; + width = this._image.naturalWidth * heightRatio; + height = this._image.naturalHeight * heightRatio; + } + var x = (this._screenWidth - width) / 2; + var y = (this._screenHeight - height) / 2; + ctx.drawImage(this._image, x, y, width, height); + break; + case "FIT": + //Try maxing width first, top and bottom borders + var widthRatio = this._screenWidth / this._image.naturalWidth; + var width = this._image.naturalWidth * widthRatio; + var height = this._image.naturalHeight * widthRatio; + var x = 0; + var y = (this._screenHeight - height) / 2; + if (height > this._screenHeight) { + //height overflow, maximise height, side borders + var heightRatio = this._screenHeight / this._image.naturalHeight; + width = this._image.naturalWidth * heightRatio; + height = this._image.naturalHeight * heightRatio; + x = (this._screenWidth - width) / 2; + y = 0; + } + ctx.drawImage(this._image, x, y, width, height); + break; + } + } +}; diff --git a/application/palemoon/components/shell/content/setDesktopBackground.xul b/application/palemoon/components/shell/content/setDesktopBackground.xul new file mode 100644 index 000000000..d7d4079e3 --- /dev/null +++ b/application/palemoon/components/shell/content/setDesktopBackground.xul @@ -0,0 +1,84 @@ +<?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> + +# 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/. + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/setDesktopBackground.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://browser/locale/setDesktopBackground.dtd"> + +#ifdef XP_MACOSX +<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?> +#endif + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + windowtype="Shell:SetDesktopBackground" +#ifndef XP_MACOSX + buttons="accept,cancel" +#else + buttons="accept" +#endif + buttonlabelaccept="&setDesktopBackground.title;" + onload="gSetBackground.load();" + ondialogaccept="gSetBackground.setDesktopBackground();" + title="&setDesktopBackground.title;" + style="width: 30em;"> + + <stringbundle id="backgroundBundle" + src="chrome://browser/locale/shellservice.properties"/> + <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> + <script type="application/javascript" src="chrome://browser/content/setDesktopBackground.js"/> + <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/> + +#ifndef XP_MACOSX + <hbox align="center"> + <label value="&position.label;"/> + <menulist id="menuPosition" + label="&position.label;" + oncommand="gSetBackground.updatePosition();"> + <menupopup> + <menuitem label="¢er.label;" value="CENTER"/> + <menuitem label="&tile.label;" value="TILE"/> + <menuitem label="&stretch.label;" value="STRETCH"/> + <menuitem label="&fill.label;" value="FILL" id="fillPosition"/> + <menuitem label="&fit.label;" value="FIT" id="fitPosition"/> + </menupopup> + </menulist> + <spacer flex="1"/> + <label value="&color.label;"/> + <colorpicker id="desktopColor" + type="button" + onchange="gSetBackground.updateColor(this.color);"/> + </hbox> +#endif + <groupbox align="center"> + <caption label="&preview.label;"/> + <stack> + <!-- if width and height are not present, they default to 300x150 and stretch the stack --> + <html:canvas id="screen" width="1" height="1"/> + <image id="monitor"/> + </stack> + </groupbox> + +#ifdef XP_MACOSX + <separator/> + + <hbox align="right"> + <button id="setDesktopBackground" + label="&setDesktopBackground.title;" + oncommand="gSetBackground.setDesktopBackground();"/> + <button id="showDesktopPreferences" + label="&openDesktopPrefs.label;" + oncommand="gSetBackground.showDesktopPrefs();" + hidden="true"/> + </hbox> +#endif + +#ifdef XP_MACOSX +#include ../../../base/content/browserMountPoints.inc +#endif + +</dialog> diff --git a/application/palemoon/components/shell/jar.mn b/application/palemoon/components/shell/jar.mn new file mode 100644 index 000000000..eed15c397 --- /dev/null +++ b/application/palemoon/components/shell/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +browser.jar: +* content/browser/setDesktopBackground.xul (content/setDesktopBackground.xul) +* content/browser/setDesktopBackground.js (content/setDesktopBackground.js) diff --git a/application/palemoon/components/shell/moz.build b/application/palemoon/components/shell/moz.build new file mode 100644 index 000000000..38965c5d7 --- /dev/null +++ b/application/palemoon/components/shell/moz.build @@ -0,0 +1,48 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn'] + +XPIDL_SOURCES += [ + 'nsIShellService.idl', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + XPIDL_SOURCES += [ + 'nsIWindowsShellService.idl', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + XPIDL_SOURCES += [ + 'nsIMacShellService.idl', + ] + +XPIDL_MODULE = 'shellservice' + +if CONFIG['OS_ARCH'] == 'WINNT': + SOURCES += [ + 'nsWindowsShellService.cpp', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += [ + 'nsMacShellService.cpp', + ] +elif CONFIG['MOZ_WIDGET_GTK']: + SOURCES += [ + 'nsGNOMEShellService.cpp', + ] + +if SOURCES: + FINAL_LIBRARY = 'browsercomps' + +EXTRA_COMPONENTS += [ + 'nsSetDefaultBrowser.js', + 'nsSetDefaultBrowser.manifest', +] + +for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'): + DEFINES[var] = '"%s"' % CONFIG[var] + +CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/application/palemoon/components/shell/nsGNOMEShellService.cpp b/application/palemoon/components/shell/nsGNOMEShellService.cpp new file mode 100644 index 000000000..14510d111 --- /dev/null +++ b/application/palemoon/components/shell/nsGNOMEShellService.cpp @@ -0,0 +1,664 @@ +/* -*- Mode: C++; tab-width: 2; 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 "mozilla/ArrayUtils.h" + +#include "nsCOMPtr.h" +#include "nsGNOMEShellService.h" +#include "nsShellService.h" +#include "nsIServiceManager.h" +#include "nsIFile.h" +#include "nsIProperties.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIPrefService.h" +#include "prenv.h" +#include "nsStringAPI.h" +#include "nsIGConfService.h" +#include "nsIGIOService.h" +#include "nsIGSettingsService.h" +#include "nsIStringBundle.h" +#include "nsIOutputStream.h" +#include "nsIProcess.h" +#include "nsNetUtil.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsIImageLoadingContent.h" +#include "imgIRequest.h" +#include "imgIContainer.h" +#include "prprf.h" +#if defined(MOZ_WIDGET_GTK) +#include "nsIImageToPixbuf.h" +#endif +#include "nsXULAppAPI.h" + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <limits.h> +#include <stdlib.h> + +using namespace mozilla; + +struct ProtocolAssociation +{ + const char *name; + bool essential; +}; + +struct MimeTypeAssociation +{ + const char *mimeType; + const char *extensions; +}; + +static const ProtocolAssociation appProtocols[] = { + { "http", true }, + { "https", true }, + { "ftp", false }, + { "chrome", false } +}; + +static const MimeTypeAssociation appTypes[] = { + { "text/html", "htm html shtml" }, + { "application/xhtml+xml", "xhtml xht" } +}; + +// GConf registry key constants +#define DG_BACKGROUND "/desktop/gnome/background" + +static const char kDesktopImageKey[] = DG_BACKGROUND "/picture_filename"; +static const char kDesktopOptionsKey[] = DG_BACKGROUND "/picture_options"; +static const char kDesktopDrawBGKey[] = DG_BACKGROUND "/draw_background"; +static const char kDesktopColorKey[] = DG_BACKGROUND "/primary_color"; + +static const char kDesktopBGSchema[] = "org.gnome.desktop.background"; +static const char kDesktopImageGSKey[] = "picture-uri"; +static const char kDesktopOptionGSKey[] = "picture-options"; +static const char kDesktopDrawBGGSKey[] = "draw-background"; +static const char kDesktopColorGSKey[] = "primary-color"; + +nsresult +nsGNOMEShellService::Init() +{ + nsresult rv; + + // GConf, GSettings or GIO _must_ be available, or we do not allow + // CreateInstance to succeed. + + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + nsCOMPtr<nsIGIOService> giovfs = + do_GetService(NS_GIOSERVICE_CONTRACTID); + nsCOMPtr<nsIGSettingsService> gsettings = + do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + + if (!gconf && !giovfs && !gsettings) + return NS_ERROR_NOT_AVAILABLE; + + // Check G_BROKEN_FILENAMES. If it's set, then filenames in glib use + // the locale encoding. If it's not set, they use UTF-8. + mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nullptr; + + if (GetAppPathFromLauncher()) + return NS_OK; + + nsCOMPtr<nsIProperties> dirSvc + (do_GetService("@mozilla.org/file/directory_service;1")); + NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE); + + nsCOMPtr<nsIFile> appPath; + rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), + getter_AddRefs(appPath)); + NS_ENSURE_SUCCESS(rv, rv); + + return appPath->GetNativePath(mAppPath); +} + +NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIShellService) + +bool +nsGNOMEShellService::GetAppPathFromLauncher() +{ + gchar *tmp; + + const char *launcher = PR_GetEnv("MOZ_APP_LAUNCHER"); + if (!launcher) + return false; + + if (g_path_is_absolute(launcher)) { + mAppPath = launcher; + tmp = g_path_get_basename(launcher); + gchar *fullpath = g_find_program_in_path(tmp); + if (fullpath && mAppPath.Equals(fullpath)) + mAppIsInPath = true; + g_free(fullpath); + } else { + tmp = g_find_program_in_path(launcher); + if (!tmp) + return false; + mAppPath = tmp; + mAppIsInPath = true; + } + + g_free(tmp); + return true; +} + +bool +nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const +{ + + gchar *commandPath; + if (mUseLocaleFilenames) { + gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, nullptr, nullptr, nullptr); + if (!nativePath) { + NS_ERROR("Error converting path to filesystem encoding"); + return false; + } + + commandPath = g_find_program_in_path(nativePath); + g_free(nativePath); + } else { + commandPath = g_find_program_in_path(aKeyValue); + } + + if (!commandPath) + return false; + + bool matches = mAppPath.Equals(commandPath); + g_free(commandPath); + return matches; +} + +bool +nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const +{ + gint argc; + gchar **argv; + nsAutoCString command(handler); + + // The string will be something of the form: [/path/to/]browser "%s" + // We want to remove all of the parameters and get just the binary name. + + if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) { + command.Assign(argv[0]); + g_strfreev(argv); + } + + if (!KeyMatchesAppName(command.get())) + return false; // the handler is set to another app + + return true; +} + +NS_IMETHODIMP +nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck, + bool aForAllTypes, + bool* aIsDefaultBrowser) +{ + *aIsDefaultBrowser = false; + if (aStartupCheck) + mCheckedThisSession = true; + + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); + + bool enabled; + nsAutoCString handler; + nsCOMPtr<nsIGIOMimeApp> gioApp; + + for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { + if (!appProtocols[i].essential) + continue; + + if (gconf) { + handler.Truncate(); + gconf->GetAppForProtocol(nsDependentCString(appProtocols[i].name), + &enabled, handler); + + if (!CheckHandlerMatchesAppName(handler) || !enabled) + return NS_OK; // the handler is disabled or set to another app + } + + if (giovfs) { + handler.Truncate(); + giovfs->GetAppForURIScheme(nsDependentCString(appProtocols[i].name), + getter_AddRefs(gioApp)); + if (!gioApp) + return NS_OK; + + gioApp->GetCommand(handler); + + if (!CheckHandlerMatchesAppName(handler)) + return NS_OK; // the handler is set to another app + } + } + + *aIsDefaultBrowser = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes, + bool aForAllUsers) +{ +#ifdef DEBUG + if (aForAllUsers) + NS_WARNING("Setting the default browser for all users is not yet supported"); +#endif + + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); + if (gconf) { + nsAutoCString appKeyValue; + if (mAppIsInPath) { + // mAppPath is in the users path, so use only the basename as the launcher + gchar *tmp = g_path_get_basename(mAppPath.get()); + appKeyValue = tmp; + g_free(tmp); + } else { + appKeyValue = mAppPath; + } + + appKeyValue.AppendLiteral(" %s"); + + for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { + if (appProtocols[i].essential || aClaimAllTypes) { + gconf->SetAppForProtocol(nsDependentCString(appProtocols[i].name), + appKeyValue); + } + } + } + + if (giovfs) { + nsresult rv; + nsCOMPtr<nsIStringBundleService> bundleService = + do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIStringBundle> brandBundle; + rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString brandShortName; + brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"), + getter_Copies(brandShortName)); + + // use brandShortName as the application id. + NS_ConvertUTF16toUTF8 id(brandShortName); + nsCOMPtr<nsIGIOMimeApp> appInfo; + rv = giovfs->CreateAppFromCommand(mAppPath, + id, + getter_AddRefs(appInfo)); + NS_ENSURE_SUCCESS(rv, rv); + + // set handler for the protocols + for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) { + if (appProtocols[i].essential || aClaimAllTypes) { + appInfo->SetAsDefaultForURIScheme(nsDependentCString(appProtocols[i].name)); + } + } + + // set handler for .html and xhtml files and MIME types: + if (aClaimAllTypes) { + // Add mime types for html, xhtml extension and set app to just created appinfo. + for (unsigned int i = 0; i < ArrayLength(appTypes); ++i) { + appInfo->SetAsDefaultForMimeType(nsDependentCString(appTypes[i].mimeType)); + appInfo->SetAsDefaultForFileExtensions(nsDependentCString(appTypes[i].extensions)); + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGNOMEShellService::GetShouldCheckDefaultBrowser(bool* aResult) +{ + // If we've already checked, the browser has been started and this is a + // new window open, and we don't want to check again. + if (mCheckedThisSession) { + *aResult = false; + return NS_OK; + } + + nsCOMPtr<nsIPrefBranch> prefs; + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (pserve) + pserve->GetBranch("", getter_AddRefs(prefs)); + + if (prefs) + prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult); + + return NS_OK; +} + +NS_IMETHODIMP +nsGNOMEShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck) +{ + nsCOMPtr<nsIPrefBranch> prefs; + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (pserve) + pserve->GetBranch("", getter_AddRefs(prefs)); + + if (prefs) + prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck); + + return NS_OK; +} + +NS_IMETHODIMP +nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult) +{ + // setting desktop background is currently only supported + // for Gnome or desktops using the same GSettings and GConf keys + const char* gnomeSession = getenv("GNOME_DESKTOP_SESSION_ID"); + if (gnomeSession) { + *aResult = true; + } else { + *aResult = false; + } + + return NS_OK; +} + +static nsresult +WriteImage(const nsCString& aPath, imgIContainer* aImage) +{ +#if !defined(MOZ_WIDGET_GTK) + return NS_ERROR_NOT_AVAILABLE; +#else + nsCOMPtr<nsIImageToPixbuf> imgToPixbuf = + do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1"); + if (!imgToPixbuf) + return NS_ERROR_NOT_AVAILABLE; + + GdkPixbuf* pixbuf = imgToPixbuf->ConvertImageToPixbuf(aImage); + if (!pixbuf) + return NS_ERROR_NOT_AVAILABLE; + + gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr); + + g_object_unref(pixbuf); + return res ? NS_OK : NS_ERROR_FAILURE; +#endif +} + +NS_IMETHODIMP +nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement, + int32_t aPosition) +{ + nsresult rv; + nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, &rv); + if (!imageContent) return rv; + + // get the image container + nsCOMPtr<imgIRequest> request; + rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(request)); + if (!request) return rv; + nsCOMPtr<imgIContainer> container; + rv = request->GetImage(getter_AddRefs(container)); + if (!container) return rv; + + // Set desktop wallpaper filling style + nsAutoCString options; + if (aPosition == BACKGROUND_TILE) + options.Assign("wallpaper"); + else if (aPosition == BACKGROUND_STRETCH) + options.Assign("stretched"); + else if (aPosition == BACKGROUND_FILL) + options.Assign("zoom"); + else if (aPosition == BACKGROUND_FIT) + options.Assign("scaled"); + else + options.Assign("centered"); + + // Write the background file to the home directory. + nsAutoCString filePath(PR_GetEnv("HOME")); + + // get the product brand name from localized strings + nsString brandName; + nsCID bundleCID = NS_STRINGBUNDLESERVICE_CID; + nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(bundleCID)); + if (bundleService) { + nsCOMPtr<nsIStringBundle> brandBundle; + rv = bundleService->CreateBundle(BRAND_PROPERTIES, + getter_AddRefs(brandBundle)); + if (NS_SUCCEEDED(rv) && brandBundle) { + rv = brandBundle->GetStringFromName(MOZ_UTF16("brandShortName"), + getter_Copies(brandName)); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + // build the file name + filePath.Append('/'); + filePath.Append(NS_ConvertUTF16toUTF8(brandName)); + filePath.Append("_wallpaper.png"); + + // write the image to a file in the home dir + rv = WriteImage(filePath, container); + NS_ENSURE_SUCCESS(rv, rv); + + // Try GSettings first. If we don't have GSettings or the right schema, fall back + // to using GConf instead. Note that if GSettings works ok, the changes get + // mirrored to GConf by the gsettings->gconf bridge in gnome-settings-daemon + nsCOMPtr<nsIGSettingsService> gsettings = + do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + if (gsettings) { + nsCOMPtr<nsIGSettingsCollection> background_settings; + gsettings->GetCollectionForSchema( + NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); + if (background_settings) { + gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr); + if (!file_uri) + return NS_ERROR_FAILURE; + + background_settings->SetString(NS_LITERAL_CSTRING(kDesktopOptionGSKey), + options); + + background_settings->SetString(NS_LITERAL_CSTRING(kDesktopImageGSKey), + nsDependentCString(file_uri)); + g_free(file_uri); + background_settings->SetBoolean(NS_LITERAL_CSTRING(kDesktopDrawBGGSKey), + true); + return rv; + } + } + + // if the file was written successfully, set it as the system wallpaper + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + + if (gconf) { + gconf->SetString(NS_LITERAL_CSTRING(kDesktopOptionsKey), options); + + // Set the image to an empty string first to force a refresh + // (since we could be writing a new image on top of an existing + // Firefox_wallpaper.png and nautilus doesn't monitor the file for changes) + gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), + EmptyCString()); + + gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), filePath); + gconf->SetBool(NS_LITERAL_CSTRING(kDesktopDrawBGKey), true); + } + + return rv; +} + +#define COLOR_16_TO_8_BIT(_c) ((_c) >> 8) +#define COLOR_8_TO_16_BIT(_c) ((_c) << 8 | (_c)) + +NS_IMETHODIMP +nsGNOMEShellService::GetDesktopBackgroundColor(uint32_t *aColor) +{ + nsCOMPtr<nsIGSettingsService> gsettings = + do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + nsCOMPtr<nsIGSettingsCollection> background_settings; + nsAutoCString background; + + if (gsettings) { + gsettings->GetCollectionForSchema( + NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); + if (background_settings) { + background_settings->GetString(NS_LITERAL_CSTRING(kDesktopColorGSKey), + background); + } + } + + if (!background_settings) { + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + if (gconf) + gconf->GetString(NS_LITERAL_CSTRING(kDesktopColorKey), background); + } + + if (background.IsEmpty()) { + *aColor = 0; + return NS_OK; + } + + GdkColor color; + gboolean success = gdk_color_parse(background.get(), &color); + + NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); + + *aColor = COLOR_16_TO_8_BIT(color.red) << 16 | + COLOR_16_TO_8_BIT(color.green) << 8 | + COLOR_16_TO_8_BIT(color.blue); + return NS_OK; +} + +static void +ColorToCString(uint32_t aColor, nsCString& aResult) +{ + // The #rrrrggggbbbb format is used to match gdk_color_to_string() + char *buf = aResult.BeginWriting(13); + if (!buf) + return; + + uint16_t red = COLOR_8_TO_16_BIT((aColor >> 16) & 0xff); + uint16_t green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff); + uint16_t blue = COLOR_8_TO_16_BIT(aColor & 0xff); + + PR_snprintf(buf, 14, "#%04x%04x%04x", red, green, blue); +} + +NS_IMETHODIMP +nsGNOMEShellService::SetDesktopBackgroundColor(uint32_t aColor) +{ + NS_ASSERTION(aColor <= 0xffffff, "aColor has extra bits"); + nsAutoCString colorString; + ColorToCString(aColor, colorString); + + nsCOMPtr<nsIGSettingsService> gsettings = + do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + if (gsettings) { + nsCOMPtr<nsIGSettingsCollection> background_settings; + gsettings->GetCollectionForSchema( + NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); + if (background_settings) { + background_settings->SetString(NS_LITERAL_CSTRING(kDesktopColorGSKey), + colorString); + return NS_OK; + } + } + + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + + if (gconf) { + gconf->SetString(NS_LITERAL_CSTRING(kDesktopColorKey), colorString); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGNOMEShellService::OpenApplication(int32_t aApplication) +{ + nsAutoCString scheme; + if (aApplication == APPLICATION_MAIL) + scheme.Assign("mailto"); + else if (aApplication == APPLICATION_NEWS) + scheme.Assign("news"); + else + return NS_ERROR_NOT_AVAILABLE; + + nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); + if (giovfs) { + nsCOMPtr<nsIGIOMimeApp> gioApp; + giovfs->GetAppForURIScheme(scheme, getter_AddRefs(gioApp)); + if (gioApp) + return gioApp->Launch(EmptyCString()); + } + + nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + if (!gconf) + return NS_ERROR_FAILURE; + + bool enabled; + nsAutoCString appCommand; + gconf->GetAppForProtocol(scheme, &enabled, appCommand); + + if (!enabled) + return NS_ERROR_FAILURE; + + // XXX we don't currently handle launching a terminal window. + // If the handler requires a terminal, bail. + bool requiresTerminal; + gconf->HandlerRequiresTerminal(scheme, &requiresTerminal); + if (requiresTerminal) + return NS_ERROR_FAILURE; + + // Perform shell argument expansion + int argc; + char **argv; + if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr)) + return NS_ERROR_FAILURE; + + char **newArgv = new char*[argc + 1]; + int newArgc = 0; + + // Run through the list of arguments. Copy all of them to the new + // argv except for %s, which we skip. + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], "%s") != 0) + newArgv[newArgc++] = argv[i]; + } + + newArgv[newArgc] = nullptr; + + gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH, + nullptr, nullptr, nullptr, nullptr); + + g_strfreev(argv); + delete[] newArgv; + + return err ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGNOMEShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) +{ + nsresult rv; + nsCOMPtr<nsIProcess> process = + do_CreateInstance("@mozilla.org/process/util;1", &rv); + if (NS_FAILED(rv)) + return rv; + + rv = process->Init(aApplication); + if (NS_FAILED(rv)) + return rv; + + const nsCString spec(aURI); + const char* specStr = spec.get(); + return process->Run(false, &specStr, 1); +} + +NS_IMETHODIMP +nsGNOMEShellService::GetDefaultFeedReader(nsIFile** _retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/application/palemoon/components/shell/nsGNOMEShellService.h b/application/palemoon/components/shell/nsGNOMEShellService.h new file mode 100644 index 000000000..36a4a0c42 --- /dev/null +++ b/application/palemoon/components/shell/nsGNOMEShellService.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef nsgnomeshellservice_h____ +#define nsgnomeshellservice_h____ + +#include "nsIShellService.h" +#include "nsStringAPI.h" +#include "mozilla/Attributes.h" + +class nsGNOMEShellService final : public nsIShellService +{ +public: + nsGNOMEShellService() : mCheckedThisSession(false), mAppIsInPath(false) { } + + NS_DECL_ISUPPORTS + NS_DECL_NSISHELLSERVICE + + nsresult Init() NS_HIDDEN; + +private: + ~nsGNOMEShellService() {} + + NS_HIDDEN_(bool) KeyMatchesAppName(const char *aKeyValue) const; + NS_HIDDEN_(bool) CheckHandlerMatchesAppName(const nsACString& handler) const; + + NS_HIDDEN_(bool) GetAppPathFromLauncher(); + bool mCheckedThisSession; + bool mUseLocaleFilenames; + nsCString mAppPath; + bool mAppIsInPath; +}; + +#endif // nsgnomeshellservice_h____ diff --git a/application/palemoon/components/shell/nsIMacShellService.idl b/application/palemoon/components/shell/nsIMacShellService.idl new file mode 100644 index 000000000..3c2f42315 --- /dev/null +++ b/application/palemoon/components/shell/nsIMacShellService.idl @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 2; 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 "nsIShellService.idl" + +[scriptable, uuid(7f8ca08e-1df4-4735-86e9-50dedb48e5e8)] +interface nsIMacShellService : nsIShellService +{ + const long APPLICATION_KEYCHAIN_ACCESS = 2; + const long APPLICATION_NETWORK = 3; + const long APPLICATION_DESKTOP = 4; +}; + diff --git a/application/palemoon/components/shell/nsIShellService.idl b/application/palemoon/components/shell/nsIShellService.idl new file mode 100644 index 000000000..4825741b3 --- /dev/null +++ b/application/palemoon/components/shell/nsIShellService.idl @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; 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 "nsISupports.idl" + +interface nsIDOMElement; +interface nsIFile; + +[scriptable, uuid(99d2e9f1-3c86-40f7-81fd-3060c18489f0)] +interface nsIShellService : nsISupports +{ + /** + * Determines whether or not Firefox is the "Default Browser." + * This is simply whether or not Firefox is registered to handle + * http links. + * + * @param aStartupCheck true if this is the check being performed + * by the first browser window at startup, + * false otherwise. + * @param aForAllTypes true if the check should be made for HTTP and HTML. + * false if the check should be made for HTTP only. + * This parameter may be ignored on some platforms. + */ + boolean isDefaultBrowser(in boolean aStartupCheck, + [optional] in boolean aForAllTypes); + + /** + * Registers Firefox as the "Default Browser." + * + * @param aClaimAllTypes Register Firefox as the handler for + * additional protocols (ftp, chrome etc) + * and web documents (.html, .xhtml etc). + * @param aForAllUsers Whether or not Firefox should attempt + * to become the default browser for all + * users on a multi-user system. + */ + void setDefaultBrowser(in boolean aClaimAllTypes, in boolean aForAllUsers); + + /** + * Used to determine whether or not to show a "Set Default Browser" + * query dialog. This attribute is true if the application is starting + * up and "browser.shell.checkDefaultBrowser" is true, otherwise it + * is false. + */ + attribute boolean shouldCheckDefaultBrowser; + + /** + * Used to determine whether or not to offer "Set as desktop background" + * functionality. Even if shell service is available it is not + * guaranteed that it is able to set the background for every desktop + * which is especially true for Linux with its many different desktop + * environments. + */ + readonly attribute boolean canSetDesktopBackground; + + /** + * Flags for positioning/sizing of the Desktop Background image. + */ + const long BACKGROUND_TILE = 1; + const long BACKGROUND_STRETCH = 2; + const long BACKGROUND_CENTER = 3; + const long BACKGROUND_FILL = 4; + const long BACKGROUND_FIT = 5; + + /** + * Sets the desktop background image using either the HTML <IMG> + * element supplied or the background image of the element supplied. + * + * @param aImageElement Either a HTML <IMG> element or an element with + * a background image from which to source the + * background image. + * @param aPosition How to place the image on the desktop + */ + void setDesktopBackground(in nsIDOMElement aElement, in long aPosition); + + /** + * Constants identifying applications that can be opened with + * openApplication. + */ + const long APPLICATION_MAIL = 0; + const long APPLICATION_NEWS = 1; + + /** + * Opens the application specified. If more than one application of the + * given type is available on the system, the default or "preferred" + * application is used. + */ + void openApplication(in long aApplication); + + /** + * The desktop background color, visible when no background image is + * used, or if the background image is centered and does not fill the + * entire screen. A rgb value, where (r << 16 | g << 8 | b) + */ + attribute unsigned long desktopBackgroundColor; + + /** + * Opens an application with a specific URI to load. + * @param application + * The application file (or bundle directory, on OS X) + * @param uri + * The uri to be loaded by the application + */ + void openApplicationWithURI(in nsIFile aApplication, in ACString aURI); + + /** + * The default system handler for web feeds + */ + readonly attribute nsIFile defaultFeedReader; +}; diff --git a/application/palemoon/components/shell/nsIWindowsShellService.idl b/application/palemoon/components/shell/nsIWindowsShellService.idl new file mode 100644 index 000000000..913eb4292 --- /dev/null +++ b/application/palemoon/components/shell/nsIWindowsShellService.idl @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 2; 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 "nsIShellService.idl" + +[scriptable, uuid(89b0a761-d9a0-4c39-ab83-d81566459a31)] +interface nsIWindowsShellService : nsIShellService +{ + /** + * Provides the shell service an opportunity to do some Win7+ shortcut + * maintenance needed on initial startup of the browser. + */ + void shortcutMaintenance(); +}; + diff --git a/application/palemoon/components/shell/nsMacShellService.cpp b/application/palemoon/components/shell/nsMacShellService.cpp new file mode 100644 index 000000000..914b9ae65 --- /dev/null +++ b/application/palemoon/components/shell/nsMacShellService.cpp @@ -0,0 +1,470 @@ +/* -*- Mode: C++; tab-width: 2; 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 "nsDirectoryServiceDefs.h" +#include "nsIDOMElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsIImageLoadingContent.h" +#include "nsIDocument.h" +#include "nsIContent.h" +#include "nsILocalFileMac.h" +#include "nsIObserverService.h" +#include "nsIPrefService.h" +#include "nsIServiceManager.h" +#include "nsIStringBundle.h" +#include "nsIURL.h" +#include "nsIWebBrowserPersist.h" +#include "nsMacShellService.h" +#include "nsNetUtil.h" +#include "nsShellService.h" +#include "nsStringAPI.h" +#include "nsIDocShell.h" +#include "nsILoadContext.h" + + +#include <CoreFoundation/CoreFoundation.h> +#include <ApplicationServices/ApplicationServices.h> + +#define NETWORK_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/Network.prefPane") +#define DESKTOP_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/DesktopScreenEffectsPref.prefPane") + +#define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari" + +NS_IMPL_ISUPPORTS(nsMacShellService, nsIMacShellService, nsIShellService, nsIWebProgressListener) + +NS_IMETHODIMP +nsMacShellService::IsDefaultBrowser(bool aStartupCheck, + bool aForAllTypes, + bool* aIsDefaultBrowser) +{ + *aIsDefaultBrowser = false; + + CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); + if (!firefoxID) { + // CFBundleGetIdentifier is expected to return nullptr only if the specified + // bundle doesn't have a bundle identifier in its plist. In this case, that + // means a failure, since our bundle does have an identifier. + return NS_ERROR_FAILURE; + } + + // Get the default http handler's bundle ID (or nullptr if it has not been explicitly set) + CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http")); + if (defaultBrowserID) { + *aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo; + ::CFRelease(defaultBrowserID); + } + + // If this is the first browser window, maintain internal state that we've + // checked this session (so that subsequent window opens don't show the + // default browser dialog). + if (aStartupCheck) + mCheckedThisSession = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) +{ + // Note: We don't support aForAllUsers on Mac OS X. + + CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); + if (!firefoxID) { + return NS_ERROR_FAILURE; + } + + if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), firefoxID) != noErr) { + return NS_ERROR_FAILURE; + } + if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), firefoxID) != noErr) { + return NS_ERROR_FAILURE; + } + + if (aClaimAllTypes) { + if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) { + return NS_ERROR_FAILURE; + } + if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, firefoxID) != noErr) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult) +{ + // If we've already checked, the browser has been started and this is a + // new window open, and we don't want to check again. + if (mCheckedThisSession) { + *aResult = false; + return NS_OK; + } + + nsCOMPtr<nsIPrefBranch> prefs; + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (pserve) + pserve->GetBranch("", getter_AddRefs(prefs)); + + prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult); + + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck) +{ + nsCOMPtr<nsIPrefBranch> prefs; + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (pserve) + pserve->GetBranch("", getter_AddRefs(prefs)); + + prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck); + + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::GetCanSetDesktopBackground(bool* aResult) +{ + *aResult = true; + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement, + int32_t aPosition) +{ + // Note: We don't support aPosition on OS X. + + // Get the image URI: + nsresult rv; + nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, + &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIURI> imageURI; + rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI)); + NS_ENSURE_SUCCESS(rv, rv); + + // We need the referer URI for nsIWebBrowserPersist::saveURI + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsIURI *docURI = content->OwnerDoc()->GetDocumentURI(); + if (!docURI) + return NS_ERROR_FAILURE; + + // Get the desired image file name + nsCOMPtr<nsIURL> imageURL(do_QueryInterface(imageURI)); + if (!imageURL) { + // XXXmano (bug 300293): Non-URL images (e.g. the data: protocol) are not + // yet supported. What filename should we take here? + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsAutoCString fileName; + imageURL->GetFileName(fileName); + nsCOMPtr<nsIProperties> fileLocator + (do_GetService("@mozilla.org/file/directory_service;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + // Get the current user's "Pictures" folder (That's ~/Pictures): + fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(mBackgroundFile)); + if (!mBackgroundFile) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoString fileNameUnicode; + CopyUTF8toUTF16(fileName, fileNameUnicode); + + // and add the imgage file name itself: + mBackgroundFile->Append(fileNameUnicode); + + // Download the image; the desktop background will be set in OnStateChange() + nsCOMPtr<nsIWebBrowserPersist> wbp + (do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION | + nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES | + nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; + + wbp->SetPersistFlags(flags); + wbp->SetProgressListener(this); + + nsCOMPtr<nsILoadContext> loadContext; + nsCOMPtr<nsISupports> container = content->OwnerDoc()->GetContainer(); + nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container); + if (docShell) { + loadContext = do_QueryInterface(docShell); + } + + return wbp->SaveURI(imageURI, nullptr, docURI, content->OwnerDoc()->GetReferrerPolicy(), + nullptr, nullptr, mBackgroundFile, loadContext); +} + +NS_IMETHODIMP +nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) +{ + if (aStateFlags & STATE_STOP) { + nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1")); + if (os) + os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr); + + bool exists = false; + mBackgroundFile->Exists(&exists); + if (!exists) + return NS_OK; + + nsAutoCString nativePath; + mBackgroundFile->GetNativePath(nativePath); + + AEDesc tAEDesc = { typeNull, nil }; + OSErr err = noErr; + AliasHandle aliasHandle = nil; + FSRef pictureRef; + OSStatus status; + + // Convert the path into a FSRef + status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, nullptr); + if (status == noErr) { + err = ::FSNewAlias(nil, &pictureRef, &aliasHandle); + if (err == noErr && aliasHandle == nil) + err = paramErr; + + if (err == noErr) { + // We need the descriptor (based on the picture file reference) + // for the 'Set Desktop Picture' apple event. + char handleState = ::HGetState((Handle)aliasHandle); + ::HLock((Handle)aliasHandle); + err = ::AECreateDesc(typeAlias, *aliasHandle, + GetHandleSize((Handle)aliasHandle), &tAEDesc); + // unlock the alias handler + ::HSetState((Handle)aliasHandle, handleState); + ::DisposeHandle((Handle)aliasHandle); + } + if (err == noErr) { + AppleEvent tAppleEvent; + OSType sig = 'MACS'; + AEBuildError tAEBuildError; + // Create a 'Set Desktop Pictue' Apple Event + err = ::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature, + &sig, sizeof(OSType), kAutoGenerateReturnID, + kAnyTransactionID, &tAppleEvent, &tAEBuildError, + "'----':'obj '{want:type (prop),form:prop" \ + ",seld:type('dpic'),from:'null'()},data:(@)", + &tAEDesc); + if (err == noErr) { + AppleEvent reply = { typeNull, nil }; + // Sent the event we built, the reply event isn't necessary + err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority, + kNoTimeOut, nil, nil); + ::AEDisposeDesc(&tAppleEvent); + } + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::OpenApplication(int32_t aApplication) +{ + nsresult rv = NS_OK; + CFURLRef appURL = nil; + OSStatus err = noErr; + + switch (aApplication) { + case nsIShellService::APPLICATION_MAIL: + { + CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, + CFSTR("mailto:"), nullptr); + err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); + ::CFRelease(tempURL); + } + break; + case nsIShellService::APPLICATION_NEWS: + { + CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, + CFSTR("news:"), nullptr); + err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); + ::CFRelease(tempURL); + } + break; + case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS: + err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll, nullptr, + &appURL); + break; + case nsIMacShellService::APPLICATION_NETWORK: + { + nsCOMPtr<nsIFile> lf; + rv = NS_NewNativeLocalFile(NETWORK_PREFPANE, true, getter_AddRefs(lf)); + NS_ENSURE_SUCCESS(rv, rv); + bool exists; + lf->Exists(&exists); + if (!exists) + return NS_ERROR_FILE_NOT_FOUND; + return lf->Launch(); + } + break; + case nsIMacShellService::APPLICATION_DESKTOP: + { + nsCOMPtr<nsIFile> lf; + rv = NS_NewNativeLocalFile(DESKTOP_PREFPANE, true, getter_AddRefs(lf)); + NS_ENSURE_SUCCESS(rv, rv); + bool exists; + lf->Exists(&exists); + if (!exists) + return NS_ERROR_FILE_NOT_FOUND; + return lf->Launch(); + } + break; + } + + if (appURL && err == noErr) { + err = ::LSOpenCFURLRef(appURL, nullptr); + rv = err != noErr ? NS_ERROR_FAILURE : NS_OK; + + ::CFRelease(appURL); + } + + return rv; +} + +NS_IMETHODIMP +nsMacShellService::GetDesktopBackgroundColor(uint32_t *aColor) +{ + // This method and |SetDesktopBackgroundColor| has no meaning on Mac OS X. + // The mac desktop preferences UI uses pictures for the few solid colors it + // supports. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor) +{ + // This method and |GetDesktopBackgroundColor| has no meaning on Mac OS X. + // The mac desktop preferences UI uses pictures for the few solid colors it + // supports. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) +{ + nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(aApplication)); + CFURLRef appURL; + nsresult rv = lfm->GetCFURL(&appURL); + if (NS_FAILED(rv)) + return rv; + + const nsCString spec(aURI); + const UInt8* uriString = (const UInt8*)spec.get(); + CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(), + kCFStringEncodingUTF8, nullptr); + if (!uri) + return NS_ERROR_OUT_OF_MEMORY; + + CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr); + if (!uris) { + ::CFRelease(uri); + return NS_ERROR_OUT_OF_MEMORY; + } + + LSLaunchURLSpec launchSpec; + launchSpec.appURL = appURL; + launchSpec.itemURLs = uris; + launchSpec.passThruParams = nullptr; + launchSpec.launchFlags = kLSLaunchDefaults; + launchSpec.asyncRefCon = nullptr; + + OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr); + + ::CFRelease(uris); + ::CFRelease(uri); + + return err != noErr ? NS_ERROR_FAILURE : NS_OK; +} + +NS_IMETHODIMP +nsMacShellService::GetDefaultFeedReader(nsIFile** _retval) +{ + nsresult rv = NS_ERROR_FAILURE; + *_retval = nullptr; + + CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("feed")); + if (!defaultHandlerID) { + defaultHandlerID = ::CFStringCreateWithCString(kCFAllocatorDefault, + SAFARI_BUNDLE_IDENTIFIER, + kCFStringEncodingASCII); + } + + CFURLRef defaultHandlerURL = nullptr; + OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator, + defaultHandlerID, + nullptr, // inName + nullptr, // outAppRef + &defaultHandlerURL); + + if (status == noErr && defaultHandlerURL) { + nsCOMPtr<nsILocalFileMac> defaultReader = + do_CreateInstance("@mozilla.org/file/local;1", &rv); + if (NS_SUCCEEDED(rv)) { + rv = defaultReader->InitWithCFURL(defaultHandlerURL); + if (NS_SUCCEEDED(rv)) { + NS_ADDREF(*_retval = defaultReader); + rv = NS_OK; + } + } + + ::CFRelease(defaultHandlerURL); + } + + ::CFRelease(defaultHandlerID); + + return rv; +} diff --git a/application/palemoon/components/shell/nsMacShellService.h b/application/palemoon/components/shell/nsMacShellService.h new file mode 100644 index 000000000..7ca1c71ac --- /dev/null +++ b/application/palemoon/components/shell/nsMacShellService.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef nsmacshellservice_h____ +#define nsmacshellservice_h____ + +#include "nsIMacShellService.h" +#include "nsIWebProgressListener.h" +#include "nsIFile.h" +#include "nsCOMPtr.h" + +class nsMacShellService : public nsIMacShellService, + public nsIWebProgressListener +{ +public: + nsMacShellService() : mCheckedThisSession(false) {}; + + NS_DECL_ISUPPORTS + NS_DECL_NSISHELLSERVICE + NS_DECL_NSIMACSHELLSERVICE + NS_DECL_NSIWEBPROGRESSLISTENER + +protected: + virtual ~nsMacShellService() {}; + +private: + nsCOMPtr<nsIFile> mBackgroundFile; + + bool mCheckedThisSession; +}; + +#endif // nsmacshellservice_h____ diff --git a/application/palemoon/components/shell/nsSetDefaultBrowser.js b/application/palemoon/components/shell/nsSetDefaultBrowser.js new file mode 100644 index 000000000..d1d1e72cb --- /dev/null +++ b/application/palemoon/components/shell/nsSetDefaultBrowser.js @@ -0,0 +1,31 @@ +/* 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/. */ + +/* + * -setDefaultBrowser commandline handler + * Makes the current executable the "default browser". + */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function nsSetDefaultBrowser() {} + +nsSetDefaultBrowser.prototype = { + handle: function nsSetDefault_handle(aCmdline) { + if (aCmdline.handleFlag("setDefaultBrowser", false)) { + var shell = Cc["@mozilla.org/browser/shell-service;1"]. + getService(Ci.nsIShellService); + shell.setDefaultBrowser(true, true); + } + }, + + helpInfo: " -setDefaultBrowser Set this app as the default browser.\n", + + classID: Components.ID("{F57899D0-4E2C-4ac6-9E29-50C736103B0C}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSetDefaultBrowser]); diff --git a/application/palemoon/components/shell/nsSetDefaultBrowser.manifest b/application/palemoon/components/shell/nsSetDefaultBrowser.manifest new file mode 100644 index 000000000..bf3c0f04f --- /dev/null +++ b/application/palemoon/components/shell/nsSetDefaultBrowser.manifest @@ -0,0 +1,3 @@ +component {F57899D0-4E2C-4ac6-9E29-50C736103B0C} nsSetDefaultBrowser.js +contract @mozilla.org/browser/default-browser-clh;1 {F57899D0-4E2C-4ac6-9E29-50C736103B0C} +category command-line-handler m-setdefaultbrowser @mozilla.org/browser/default-browser-clh;1 diff --git a/application/palemoon/components/shell/nsShellService.h b/application/palemoon/components/shell/nsShellService.h new file mode 100644 index 000000000..d5fade37b --- /dev/null +++ b/application/palemoon/components/shell/nsShellService.h @@ -0,0 +1,10 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#define PREF_CHECKDEFAULTBROWSER "browser.shell.checkDefaultBrowser" + +#define SHELLSERVICE_PROPERTIES "chrome://browser/locale/shellservice.properties" +#define BRAND_PROPERTIES "chrome://branding/locale/brand.properties" + diff --git a/application/palemoon/components/shell/nsWindowsShellService.cpp b/application/palemoon/components/shell/nsWindowsShellService.cpp new file mode 100644 index 000000000..813ec4fe1 --- /dev/null +++ b/application/palemoon/components/shell/nsWindowsShellService.cpp @@ -0,0 +1,1198 @@ +/* -*- Mode: C++; tab-width: 2; 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 "imgIContainer.h" +#include "imgIRequest.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsIDOMElement.h" +#include "nsIDOMHTMLImageElement.h" +#include "nsIImageLoadingContent.h" +#include "nsIPrefService.h" +#include "nsIPrefLocalizedString.h" +#include "nsIServiceManager.h" +#include "nsIStringBundle.h" +#include "nsNetUtil.h" +#include "nsShellService.h" +#include "nsWindowsShellService.h" +#include "nsIProcess.h" +#include "nsICategoryManager.h" +#include "nsBrowserCompsCID.h" +#include "nsDirectoryServiceUtils.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIWindowsRegKey.h" +#include "nsUnicharUtils.h" +#include "nsIWinTaskbar.h" +#include "nsISupportsPrimitives.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "mozilla/WindowsVersion.h" + +#include "windows.h" +#include "shellapi.h" + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0600 +#define INITGUID +#undef NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WIN8 +// Needed for access to IApplicationActivationManager +#include <shlobj.h> + +#include <mbstring.h> +#include <shlwapi.h> + +#ifndef MAX_BUF +#define MAX_BUF 4096 +#endif + +#define REG_SUCCEEDED(val) \ + (val == ERROR_SUCCESS) + +#define REG_FAILED(val) \ + (val != ERROR_SUCCESS) + +#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" + +using mozilla::IsWin8OrLater; +using namespace mozilla; +using namespace mozilla::gfx; + +NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIWindowsShellService, nsIShellService) + +static nsresult +OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey) +{ + const nsString &flatName = PromiseFlatString(aKeyName); + + DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey); + switch (res) { + case ERROR_SUCCESS: + break; + case ERROR_ACCESS_DENIED: + return NS_ERROR_FILE_ACCESS_DENIED; + case ERROR_FILE_NOT_FOUND: + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Default Browser Registry Settings +// +// The setting of these values are made by an external binary since writing +// these values may require elevation. +// +// - File Extension Mappings +// ----------------------- +// The following file extensions: +// .htm .html .shtml .xht .xhtml +// are mapped like so: +// +// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ PaleMoonHTML +// +// as aliases to the class: +// +// HKCU\SOFTWARE\Classes\PaleMoonHTML\ +// DefaultIcon (default) REG_SZ <apppath>,1 +// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" +// shell\open\ddeexec (default) REG_SZ <empty string> +// +// - Windows Vista and above Protocol Handler +// +// HKCU\SOFTWARE\Classes\PaleMoonURL\ (default) REG_SZ <appname> URL +// EditFlags REG_DWORD 2 +// FriendlyTypeName REG_SZ <appname> URL +// DefaultIcon (default) REG_SZ <apppath>,1 +// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" +// shell\open\ddeexec (default) REG_SZ <empty string> +// +// - Protocol Mappings +// ----------------- +// The following protocols: +// HTTP, HTTPS, FTP +// are mapped like so: +// +// HKCU\SOFTWARE\Classes\<protocol>\ +// DefaultIcon (default) REG_SZ <apppath>,1 +// shell\open\command (default) REG_SZ <apppath> -osint -url "%1" +// shell\open\ddeexec (default) REG_SZ <empty string> +// +// - Windows Start Menu (XP SP1 and newer) +// ------------------------------------------------- +// The following keys are set to make PaleMoon appear in the Start Menu as the +// browser: +// +// HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\ +// (default) REG_SZ <appname> +// DefaultIcon (default) REG_SZ <apppath>,0 +// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts +// InstallInfo IconsVisible REG_DWORD 1 +// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal +// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts +// shell\open\command (default) REG_SZ <apppath> +// shell\properties (default) REG_SZ <appname> &Options +// shell\properties\command (default) REG_SZ <apppath> -preferences +// shell\safemode (default) REG_SZ <appname> &Safe Mode +// shell\safemode\command (default) REG_SZ <apppath> -safe-mode +// + +// The values checked are all default values so the value name is not needed. +typedef struct { + const char* keyName; + const char* valueData; + const char* oldValueData; +} SETTING; + +#define APP_REG_NAME L"Pale Moon" +#define VAL_FILE_ICON "%APPPATH%,1" +#define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\"" +#define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\"" +#define DI "\\DefaultIcon" +#define SOC "\\shell\\open\\command" +#define SOD "\\shell\\open\\ddeexec" +// Used for updating the FTP protocol handler's shell open command under HKCU. +#define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command" + +#define MAKE_KEY_NAME1(PREFIX, MID) \ + PREFIX MID + +// The DefaultIcon registry key value should never be used when checking if +// PaleMoon is the default browser for file handlers since other applications +// (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon +// Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for +// more info. The FTP protocol is not checked so advanced users can set the FTP +// handler to another application and still have PaleMoon check if it is the +// default HTTP and HTTPS handler. +// *** Do not add additional checks here unless you skip them when aForAllTypes +// is false below***. +static SETTING gSettings[] = { + // File Handler Class + // ***keep this as the first entry because when aForAllTypes is not set below + // it will skip over this check.*** + { MAKE_KEY_NAME1("PaleMoonHTML", SOC), VAL_OPEN, OLD_VAL_OPEN }, + + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1("PaleMoonURL", SOC), VAL_OPEN, OLD_VAL_OPEN }, + + // Protocol Handlers + { MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON }, + { MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN }, + { MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON }, + { MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN } +}; + +// The settings to disable DDE are separate from the default browser settings +// since they are only checked when PaleMoon is the default browser and if they +// are incorrect they are fixed without notifying the user. +static SETTING gDDESettings[] = { + // File Handler Class + { MAKE_KEY_NAME1("Software\\Classes\\PaleMoonHTML", SOD) }, + + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1("Software\\Classes\\PaleMoonURL", SOD) }, + + // Protocol Handlers + { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) }, + { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) }, + { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) } +}; + +nsresult +GetHelperPath(nsAutoString& aPath) +{ + nsresult rv; + nsCOMPtr<nsIProperties> directoryService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> appHelper; + rv = directoryService->Get(XRE_EXECUTABLE_FILE, + NS_GET_IID(nsIFile), + getter_AddRefs(appHelper)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->GetPath(aPath); + + aPath.Insert(L'"', 0); + aPath.Append(L'"'); + return rv; +} + +nsresult +LaunchHelper(nsAutoString& aPath) +{ + STARTUPINFOW si = {sizeof(si), 0}; + PROCESS_INFORMATION pi = {0}; + + if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, + 0, nullptr, nullptr, &si, &pi)) { + return NS_ERROR_FAILURE; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsShellService::ShortcutMaintenance() +{ + nsresult rv; + + // XXX App ids were updated to a constant install path hash, + // XXX this code can be removed after a few upgrade cycles. + + // Launch helper.exe so it can update the application user model ids on + // shortcuts in the user's taskbar and start menu. This keeps older pinned + // shortcuts grouped correctly after major updates. Note, we also do this + // through the upgrade installer script, however, this is the only place we + // have a chance to trap links created by users who do control the install/ + // update process of the browser. + + nsCOMPtr<nsIWinTaskbar> taskbarInfo = + do_GetService(NS_TASKBAR_CONTRACTID); + if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails. + return NS_OK; + + // Avoid if this isn't Win7+ + bool isSupported = false; + taskbarInfo->GetAvailable(&isSupported); + if (!isSupported) + return NS_OK; + + nsAutoString appId; + if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId))) + return NS_ERROR_UNEXPECTED; + + NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid"); + nsCOMPtr<nsIPrefService> prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (!prefs) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIPrefBranch> prefBranch; + prefs->GetBranch(nullptr, getter_AddRefs(prefBranch)); + if (!prefBranch) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsISupportsString> prefString; + rv = prefBranch->GetComplexValue(prefName.get(), + NS_GET_IID(nsISupportsString), + getter_AddRefs(prefString)); + if (NS_SUCCEEDED(rv)) { + nsAutoString version; + prefString->GetData(version); + if (!version.IsEmpty() && version.Equals(appId)) { + // We're all good, get out of here. + return NS_OK; + } + } + // Update the version in prefs + prefString = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + prefString->SetData(appId); + rv = prefBranch->SetComplexValue(prefName.get(), + NS_GET_IID(nsISupportsString), + prefString); + if (NS_FAILED(rv)) { + NS_WARNING("Couldn't set last user model id!"); + return NS_ERROR_UNEXPECTED; + } + + nsAutoString appHelperPath; + if (NS_FAILED(GetHelperPath(appHelperPath))) + return NS_ERROR_UNEXPECTED; + + appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds"); + + return LaunchHelper(appHelperPath); +} + +static bool +IsAARDefaultHTTP(IApplicationAssociationRegistration* pAAR, + bool* aIsDefaultBrowser) +{ + // Make sure the Prog ID matches what we have + LPWSTR registeredApp; + HRESULT hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE, + ®isteredApp); + if (SUCCEEDED(hr)) { + LPCWSTR firefoxHTTPProgID = L"PaleMoonURL"; + *aIsDefaultBrowser = !wcsicmp(registeredApp, firefoxHTTPProgID); + CoTaskMemFree(registeredApp); + } else { + *aIsDefaultBrowser = false; + } + return SUCCEEDED(hr); +} + +static bool +IsAARDefaultHTML(IApplicationAssociationRegistration* pAAR, + bool* aIsDefaultBrowser) +{ + LPWSTR registeredApp; + HRESULT hr = pAAR->QueryCurrentDefault(L".html", AT_FILEEXTENSION, AL_EFFECTIVE, + ®isteredApp); + if (SUCCEEDED(hr)) { + LPCWSTR firefoxHTMLProgID = L"PaleMoonHTML"; + *aIsDefaultBrowser = !wcsicmp(registeredApp, firefoxHTMLProgID); + CoTaskMemFree(registeredApp); + } else { + *aIsDefaultBrowser = false; + } + return SUCCEEDED(hr); +} + +/* + * Query's the AAR for the default status. + * This only checks for PaleMoonURL and if aCheckAllTypes is set, then + * it also checks for PaleMoonHTML. Note that those ProgIDs are shared + * by all PaleMoon browsers. +*/ +bool +nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes, + bool* aIsDefaultBrowser) +{ + IApplicationAssociationRegistration* pAAR; + HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, + nullptr, + CLSCTX_INPROC, + IID_IApplicationAssociationRegistration, + (void**)&pAAR); + + if (SUCCEEDED(hr)) { + if (aCheckAllTypes) { + BOOL res; + hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, + APP_REG_NAME, + &res); + *aIsDefaultBrowser = res; + + // If we have all defaults, let's make sure that our ProgID + // is explicitly returned as well. Needed only for Windows 8. + if (*aIsDefaultBrowser && IsWin8OrLater()) { + IsAARDefaultHTTP(pAAR, aIsDefaultBrowser); + if (*aIsDefaultBrowser) { + IsAARDefaultHTML(pAAR, aIsDefaultBrowser); + } + } + } else { + IsAARDefaultHTTP(pAAR, aIsDefaultBrowser); + } + + pAAR->Release(); + return true; + } + return false; +} + +NS_IMETHODIMP +nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, + bool aForAllTypes, + bool* aIsDefaultBrowser) +{ + // If this is the first browser window, maintain internal state that we've + // checked this session (so that subsequent window opens don't show the + // default browser dialog). + if (aStartupCheck) + mCheckedThisSession = true; + + // Assume we're the default unless one of the several checks below tell us + // otherwise. + *aIsDefaultBrowser = true; + + wchar_t exePath[MAX_BUF]; + if (!::GetModuleFileNameW(0, exePath, MAX_BUF)) + return NS_ERROR_FAILURE; + + // Convert the path to a long path since GetModuleFileNameW returns the path + // that was used to launch PaleMoon which is not necessarily a long path. + if (!::GetLongPathNameW(exePath, exePath, MAX_BUF)) + return NS_ERROR_FAILURE; + + nsAutoString appLongPath(exePath); + + HKEY theKey; + DWORD res; + nsresult rv; + wchar_t currValue[MAX_BUF]; + + SETTING* settings = gSettings; + if (!aForAllTypes && IsWin8OrLater()) { + // Skip over the file handler check + settings++; + } + + SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING); + + for (; settings < end; ++settings) { + NS_ConvertUTF8toUTF16 keyName(settings->keyName); + NS_ConvertUTF8toUTF16 valueData(settings->valueData); + int32_t offset = valueData.Find("%APPPATH%"); + valueData.Replace(offset, 9, appLongPath); + + rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey); + if (NS_FAILED(rv)) { + *aIsDefaultBrowser = false; + return NS_OK; + } + + ::ZeroMemory(currValue, sizeof(currValue)); + DWORD len = sizeof currValue; + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, + (LPBYTE)currValue, &len); + // Close the key that was opened. + ::RegCloseKey(theKey); + if (REG_FAILED(res) || + _wcsicmp(valueData.get(), currValue)) { + // Key wasn't set or was set to something other than our registry entry. + NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData); + offset = oldValueData.Find("%APPPATH%"); + oldValueData.Replace(offset, 9, appLongPath); + // The current registry value doesn't match the current or the old format. + if (_wcsicmp(oldValueData.get(), currValue)) { + *aIsDefaultBrowser = false; + return NS_OK; + } + + res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, PromiseFlatString(keyName).get(), + 0, KEY_SET_VALUE, &theKey); + if (REG_FAILED(res)) { + // If updating the open command fails try to update it using the helper + // application when setting PaleMoon as the default browser. + *aIsDefaultBrowser = false; + return NS_OK; + } + + const nsString &flatValue = PromiseFlatString(valueData); + res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, + (const BYTE *) flatValue.get(), + (flatValue.Length() + 1) * sizeof(char16_t)); + // Close the key that was created. + ::RegCloseKey(theKey); + if (REG_FAILED(res)) { + // If updating the open command fails try to update it using the helper + // application when setting PaleMoon as the default browser. + *aIsDefaultBrowser = false; + return NS_OK; + } + } + } + + // Only check if PaleMoon is the default browser on Vista and above if the + // previous checks show that PaleMoon is the default browser. + if (*aIsDefaultBrowser) { + IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser); + } + + // To handle the case where DDE isn't disabled due for a user because there + // account didn't perform a PaleMoon update this will check if PaleMoon is the + // default browser and if dde is disabled for each handler + // and if it isn't disable it. When PaleMoon is not the default browser the + // helper application will disable dde for each handler. + if (*aIsDefaultBrowser && aForAllTypes) { + // Check ftp settings + + end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING); + + for (settings = gDDESettings; settings < end; ++settings) { + NS_ConvertUTF8toUTF16 keyName(settings->keyName); + + rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey); + if (NS_FAILED(rv)) { + ::RegCloseKey(theKey); + // If disabling DDE fails try to disable it using the helper + // application when setting PaleMoon as the default browser. + *aIsDefaultBrowser = false; + return NS_OK; + } + + ::ZeroMemory(currValue, sizeof(currValue)); + DWORD len = sizeof currValue; + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, + (LPBYTE)currValue, &len); + // Close the key that was opened. + ::RegCloseKey(theKey); + if (REG_FAILED(res) || char16_t('\0') != *currValue) { + // Key wasn't set or was set to something other than our registry entry. + // Delete the key along with all of its childrean and then recreate it. + const nsString &flatName = PromiseFlatString(keyName); + ::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get()); + res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, + nullptr, &theKey, nullptr); + if (REG_FAILED(res)) { + // If disabling DDE fails try to disable it using the helper + // application when setting PaleMoon as the default browser. + *aIsDefaultBrowser = false; + return NS_OK; + } + + res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"", + sizeof(char16_t)); + // Close the key that was created. + ::RegCloseKey(theKey); + if (REG_FAILED(res)) { + // If disabling DDE fails try to disable it using the helper + // application when setting PaleMoon as the default browser. + *aIsDefaultBrowser = false; + return NS_OK; + } + } + } + + // Update the FTP protocol handler's shell open command if it is the old + // format. + res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS, + &theKey); + // Don't update the FTP protocol handler's shell open command when opening + // its registry key fails under HKCU since it most likely doesn't exist. + if (NS_FAILED(rv)) { + return NS_OK; + } + + NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN); + int32_t offset = oldValueOpen.Find("%APPPATH%"); + oldValueOpen.Replace(offset, 9, appLongPath); + + ::ZeroMemory(currValue, sizeof(currValue)); + DWORD len = sizeof currValue; + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue, + &len); + + // Don't update the FTP protocol handler's shell open command when the + // current registry value doesn't exist or matches the old format. + if (REG_FAILED(res) || + _wcsicmp(oldValueOpen.get(), currValue)) { + ::RegCloseKey(theKey); + return NS_OK; + } + + NS_ConvertUTF8toUTF16 valueData(VAL_OPEN); + valueData.Replace(offset, 9, appLongPath); + const nsString &flatValue = PromiseFlatString(valueData); + res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, + (const BYTE *) flatValue.get(), + (flatValue.Length() + 1) * sizeof(char16_t)); + // Close the key that was created. + ::RegCloseKey(theKey); + // If updating the FTP protocol handlers shell open command fails try to + // update it using the helper application when setting PaleMoon as the + // default browser. + if (REG_FAILED(res)) { + *aIsDefaultBrowser = false; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsShellService::GetCanSetDesktopBackground(bool* aResult) +{ + *aResult = true; + return NS_OK; +} + +static nsresult +DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo) +{ + // shell32.dll is in the knownDLLs list so will always be loaded from the + // system32 directory. + static const wchar_t kSehllLibraryName[] = L"shell32.dll"; + HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName); + if (!shellDLL) { + return NS_ERROR_FAILURE; + } + + decltype(SHOpenWithDialog)* SHOpenWithDialogFn = + (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog"); + + if (!SHOpenWithDialogFn) { + return NS_ERROR_FAILURE; + } + + nsresult rv = + SUCCEEDED(SHOpenWithDialogFn(hwndParent, poainfo)) ? NS_OK : + NS_ERROR_FAILURE; + FreeLibrary(shellDLL); + return rv; +} + +nsresult +nsWindowsShellService::LaunchControlPanelDefaultPrograms() +{ + // Build the path control.exe path safely + WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' }; + if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) { + return NS_ERROR_FAILURE; + } + LPCWSTR controlEXE = L"control.exe"; + if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) { + return NS_ERROR_FAILURE; + } + if (!PathAppendW(controlEXEPath, controlEXE)) { + return NS_ERROR_FAILURE; + } + + WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram"; + STARTUPINFOW si = {sizeof(si), 0}; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWDEFAULT; + PROCESS_INFORMATION pi = {0}; + if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE, + 0, nullptr, nullptr, &si, &pi)) { + return NS_ERROR_FAILURE; + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return NS_OK; +} + +nsresult +nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() +{ + IApplicationActivationManager* pActivator; + HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager, + nullptr, + CLSCTX_INPROC, + IID_IApplicationActivationManager, + (void**)&pActivator); + + if (SUCCEEDED(hr)) { + DWORD pid; + hr = pActivator->ActivateApplication( + L"windows.immersivecontrolpanel_cw5n1h2txyewy" + L"!microsoft.windows.immersivecontrolpanel", + L"page=SettingsPageAppsDefaults", AO_NONE, &pid); + pActivator->Release(); + return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult +nsWindowsShellService::LaunchHTTPHandlerPane() +{ + OPENASINFO info; + info.pcszFile = L"http"; + info.pcszClass = nullptr; + info.oaifInFlags = OAIF_FORCE_REGISTRATION | + OAIF_URL_PROTOCOL | + OAIF_REGISTER_EXT; + return DynSHOpenWithDialog(nullptr, &info); +} + +NS_IMETHODIMP +nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) +{ + nsAutoString appHelperPath; + if (NS_FAILED(GetHelperPath(appHelperPath))) + return NS_ERROR_FAILURE; + + if (aForAllUsers) { + appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal"); + } else { + appHelperPath.AppendLiteral(" /SetAsDefaultAppUser"); + } + + nsresult rv = LaunchHelper(appHelperPath); + if (NS_SUCCEEDED(rv) && IsWin8OrLater()) { + if (aClaimAllTypes) { + rv = LaunchControlPanelDefaultPrograms(); + // The above call should never really fail, but just in case + // fall back to showing the HTTP association screen only. + if (NS_FAILED(rv)) { + rv = LaunchHTTPHandlerPane(); + } + } else { + // Windows 10 blocks attempts to load the HTTP Handler + // association dialog, so the modern Settings dialog + // is opened with the Default Apps view loaded. + if (IsWin10OrLater()) { + rv = LaunchModernSettingsDialogDefaultApps(); + } else { + rv = LaunchHTTPHandlerPane(); + } + + // The above call should never really fail, but just in case + // fall back to showing control panel for all defaults + if (NS_FAILED(rv)) { + rv = LaunchControlPanelDefaultPrograms(); + } + } + } + + return rv; +} + +NS_IMETHODIMP +nsWindowsShellService::GetShouldCheckDefaultBrowser(bool* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + // If we've already checked, the browser has been started and this is a + // new window open, and we don't want to check again. + if (mCheckedThisSession) { + *aResult = false; + return NS_OK; + } + + nsCOMPtr<nsIPrefBranch> prefs; + nsresult rv; + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = pserve->GetBranch("", getter_AddRefs(prefs)); + NS_ENSURE_SUCCESS(rv, rv); + + return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult); +} + +NS_IMETHODIMP +nsWindowsShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck) +{ + nsCOMPtr<nsIPrefBranch> prefs; + nsresult rv; + + nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = pserve->GetBranch("", getter_AddRefs(prefs)); + NS_ENSURE_SUCCESS(rv, rv); + + return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck); +} + +static nsresult +WriteBitmap(nsIFile* aFile, imgIContainer* aImage) +{ + nsresult rv; + + RefPtr<SourceSurface> surface = + aImage->GetFrame(imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE); + NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); + + // For either of the following formats we want to set the biBitCount member + // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap + // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored + // for the BI_RGB value we use for the biCompression member. + MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 || + surface->GetFormat() == SurfaceFormat::B8G8R8X8); + + RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); + + int32_t width = dataSurface->GetSize().width; + int32_t height = dataSurface->GetSize().height; + int32_t bytesPerPixel = 4 * sizeof(uint8_t); + uint32_t bytesPerRow = bytesPerPixel * width; + + // initialize these bitmap structs which we will later + // serialize directly to the head of the bitmap file + BITMAPINFOHEADER bmi; + bmi.biSize = sizeof(BITMAPINFOHEADER); + bmi.biWidth = width; + bmi.biHeight = height; + bmi.biPlanes = 1; + bmi.biBitCount = (WORD)bytesPerPixel*8; + bmi.biCompression = BI_RGB; + bmi.biSizeImage = bytesPerRow * height; + bmi.biXPelsPerMeter = 0; + bmi.biYPelsPerMeter = 0; + bmi.biClrUsed = 0; + bmi.biClrImportant = 0; + + BITMAPFILEHEADER bf; + bf.bfType = 0x4D42; // 'BM' + bf.bfReserved1 = 0; + bf.bfReserved2 = 0; + bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + bf.bfSize = bf.bfOffBits + bmi.biSizeImage; + + // get a file output stream + nsCOMPtr<nsIOutputStream> stream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile); + NS_ENSURE_SUCCESS(rv, rv); + + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return NS_ERROR_FAILURE; + } + + // write the bitmap headers and rgb pixel data to the file + rv = NS_ERROR_FAILURE; + if (stream) { + uint32_t written; + stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written); + if (written == sizeof(BITMAPFILEHEADER)) { + stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written); + if (written == sizeof(BITMAPINFOHEADER)) { + // write out the image data backwards because the desktop won't + // show bitmaps with negative heights for top-to-bottom + uint32_t i = map.mStride * height; + do { + i -= map.mStride; + stream->Write(((const char*)map.mData) + i, bytesPerRow, &written); + if (written == bytesPerRow) { + rv = NS_OK; + } else { + rv = NS_ERROR_FAILURE; + break; + } + } while (i != 0); + } + } + + stream->Close(); + } + + dataSurface->Unmap(); + + return rv; +} + +NS_IMETHODIMP +nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement, + int32_t aPosition) +{ + nsresult rv; + + nsCOMPtr<imgIContainer> container; + nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement)); + if (!imgElement) { + // XXX write background loading stuff! + return NS_ERROR_NOT_AVAILABLE; + } + else { + nsCOMPtr<nsIImageLoadingContent> imageContent = + do_QueryInterface(aElement, &rv); + if (!imageContent) + return rv; + + // get the image container + nsCOMPtr<imgIRequest> request; + rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(request)); + if (!request) + return rv; + rv = request->GetImage(getter_AddRefs(container)); + if (!container) + return NS_ERROR_FAILURE; + } + + // get the file name from localized strings + nsCOMPtr<nsIStringBundleService> + bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIStringBundle> shellBundle; + rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES, + getter_AddRefs(shellBundle)); + NS_ENSURE_SUCCESS(rv, rv); + + // e.g. "Desktop Background.bmp" + nsString fileLeafName; + rv = shellBundle->GetStringFromName + (MOZ_UTF16("desktopBackgroundLeafNameWin"), + getter_Copies(fileLeafName)); + NS_ENSURE_SUCCESS(rv, rv); + + // get the profile root directory + nsCOMPtr<nsIFile> file; + rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR, + getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + // eventually, the path is "%APPDATA%\Mozilla\PaleMoon\Desktop Background.bmp" + rv = file->Append(fileLeafName); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString path; + rv = file->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + // write the bitmap to a file in the profile directory + rv = WriteBitmap(file, container); + + // if the file was written successfully, set it as the system wallpaper + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIWindowsRegKey> regKey = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Control Panel\\Desktop"), + nsIWindowsRegKey::ACCESS_SET_VALUE); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString tile; + nsAutoString style; + switch (aPosition) { + case BACKGROUND_TILE: + style.AssignLiteral("0"); + tile.AssignLiteral("1"); + break; + case BACKGROUND_CENTER: + style.AssignLiteral("0"); + tile.AssignLiteral("0"); + break; + case BACKGROUND_STRETCH: + style.AssignLiteral("2"); + tile.AssignLiteral("0"); + break; + case BACKGROUND_FILL: + style.AssignLiteral("10"); + tile.AssignLiteral("0"); + break; + case BACKGROUND_FIT: + style.AssignLiteral("6"); + tile.AssignLiteral("0"); + break; + } + + rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile); + NS_ENSURE_SUCCESS(rv, rv); + rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style); + NS_ENSURE_SUCCESS(rv, rv); + rv = regKey->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(), + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + } + return rv; +} + +NS_IMETHODIMP +nsWindowsShellService::OpenApplication(int32_t aApplication) +{ + nsAutoString application; + switch (aApplication) { + case nsIShellService::APPLICATION_MAIL: + application.AssignLiteral("Mail"); + break; + case nsIShellService::APPLICATION_NEWS: + application.AssignLiteral("News"); + break; + } + + // The Default Client section of the Windows Registry looks like this: + // + // Clients\aClient\ + // e.g. aClient = "Mail"... + // \Mail\(default) = Client Subkey Name + // \Client Subkey Name + // \Client Subkey Name\shell\open\command\ + // \Client Subkey Name\shell\open\command\(default) = path to exe + // + + // Find the default application for this class. + HKEY theKey; + nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey); + if (NS_FAILED(rv)) + return rv; + + wchar_t buf[MAX_BUF]; + DWORD type, len = sizeof buf; + DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, + &type, (LPBYTE)&buf, &len); + + if (REG_FAILED(res) || !*buf) + return NS_OK; + + // Close the key we opened. + ::RegCloseKey(theKey); + + // Find the "open" command + application.AppendLiteral("\\"); + application.Append(buf); + application.AppendLiteral("\\shell\\open\\command"); + + rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey); + if (NS_FAILED(rv)) + return rv; + + ::ZeroMemory(buf, sizeof(buf)); + len = sizeof buf; + res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, + &type, (LPBYTE)&buf, &len); + if (REG_FAILED(res) || !*buf) + return NS_ERROR_FAILURE; + + // Close the key we opened. + ::RegCloseKey(theKey); + + // Look for any embedded environment variables and substitute their + // values, as |::CreateProcessW| is unable to do this. + nsAutoString path(buf); + int32_t end = path.Length(); + int32_t cursor = 0, temp = 0; + ::ZeroMemory(buf, sizeof(buf)); + do { + cursor = path.FindChar('%', cursor); + if (cursor < 0) + break; + + temp = path.FindChar('%', cursor + 1); + ++cursor; + + ::ZeroMemory(&buf, sizeof(buf)); + + ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(), + buf, sizeof(buf)); + + // "+ 2" is to subtract the extra characters used to delimit the environment + // variable ('%'). + path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf)); + + ++cursor; + } + while (cursor < end); + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ::ZeroMemory(&si, sizeof(STARTUPINFOW)); + ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, + &si, &pi); + if (!success) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) +{ + uint32_t color = ::GetSysColor(COLOR_DESKTOP); + *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) +{ + int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP }; + BYTE r = (aColor >> 16); + BYTE g = (aColor << 16) >> 24; + BYTE b = (aColor << 24) >> 24; + COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) }; + + ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors); + + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> regKey = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_LITERAL_STRING("Control Panel\\Colors"), + nsIWindowsRegKey::ACCESS_SET_VALUE); + NS_ENSURE_SUCCESS(rv, rv); + + wchar_t rgb[12]; + _snwprintf(rgb, 12, L"%u %u %u", r, g, b); + + rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"), + nsDependentString(rgb)); + NS_ENSURE_SUCCESS(rv, rv); + + return regKey->Close(); +} + +nsWindowsShellService::nsWindowsShellService() : + mCheckedThisSession(false) +{ +} + +nsWindowsShellService::~nsWindowsShellService() +{ +} + +NS_IMETHODIMP +nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication, + const nsACString& aURI) +{ + nsresult rv; + nsCOMPtr<nsIProcess> process = + do_CreateInstance("@mozilla.org/process/util;1", &rv); + if (NS_FAILED(rv)) + return rv; + + rv = process->Init(aApplication); + if (NS_FAILED(rv)) + return rv; + + const nsCString spec(aURI); + const char* specStr = spec.get(); + return process->Run(false, &specStr, 1); +} + +NS_IMETHODIMP +nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval) +{ + *_retval = nullptr; + + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> regKey = + do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, + NS_LITERAL_STRING("feed\\shell\\open\\command"), + nsIWindowsRegKey::ACCESS_READ); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString path; + rv = regKey->ReadStringValue(EmptyString(), path); + NS_ENSURE_SUCCESS(rv, rv); + if (path.IsEmpty()) + return NS_ERROR_FAILURE; + + if (path.First() == '"') { + // Everything inside the quotes + path = Substring(path, 1, path.FindChar('"', 1) - 1); + } + else { + // Everything up to the first space + path = Substring(path, 0, path.FindChar(' ')); + } + + nsCOMPtr<nsIFile> defaultReader = + do_CreateInstance("@mozilla.org/file/local;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = defaultReader->InitWithPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = defaultReader->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) + return NS_ERROR_FAILURE; + + NS_ADDREF(*_retval = defaultReader); + return NS_OK; +} diff --git a/application/palemoon/components/shell/nsWindowsShellService.h b/application/palemoon/components/shell/nsWindowsShellService.h new file mode 100644 index 000000000..f856ffd35 --- /dev/null +++ b/application/palemoon/components/shell/nsWindowsShellService.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef nswindowsshellservice_h____ +#define nswindowsshellservice_h____ + +#include "nscore.h" +#include "nsStringAPI.h" +#include "nsIWindowsShellService.h" +#include "nsITimer.h" + +#include <windows.h> +#include <ole2.h> + +class nsWindowsShellService : public nsIWindowsShellService +{ +public: + nsWindowsShellService(); + virtual ~nsWindowsShellService(); + + NS_DECL_ISUPPORTS + NS_DECL_NSISHELLSERVICE + NS_DECL_NSIWINDOWSSHELLSERVICE + +protected: + bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser); + nsresult LaunchControlPanelDefaultPrograms(); + nsresult LaunchModernSettingsDialogDefaultApps(); + nsresult LaunchHTTPHandlerPane(); + +private: + bool mCheckedThisSession; +}; + +#endif // nswindowsshellservice_h____ |