Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemeralViolette committed Oct 12, 2023
0 parents commit 7628052
Show file tree
Hide file tree
Showing 3 changed files with 592 additions and 0 deletions.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Firefox Native Controls

Simple source code patches that re-enable native controls on Firefox. Requires modifying Firefox installation files, but you don't need to use a custom build of Firefox altogether.

It is still recommended that you use ESR and disable automatic updates. Unfortunately, updating is just a sacrifice you have to make to theming. However, I may write a custom updater with this mod in consideration, eventually.

The only potential caveat: Widevine support. I don't know if this is controlled by `xul.dll` or not, but there is a file beside it called `xul.dll.sig` in official Firefox builds that has information regarding a Widevine certificate (and is otherwise seemingly completely unused). If you are based, this will not matter to you.

At the moment, it is required that you still manually disable non-native controls in order to use this.

## Change documentation

- [Scrollbars](docs/scrollbars.md)

## Installing prebuilt versions

In the releases section on the right, there are pre-compiled versions of `xul.dll` for Firefox ESR.

Check your Firefox version and download the right version for the version that you have, and then replace `xul.dll` in the installation path with the file that you downloaded.

If you don't trust the versions I built myself, then you can mix the provided patches with the Firefox source code (see the following section). The prebuilt versions are just provided for convenience, because the Firefox source code is pretty big and takes a while to compile.

Step-by-step:

1. Download the release on the right side of the page. If your version is unsupported, then you will have to build the changes from source.
2. Replace `C:\Program Files\Mozilla Firefox\xul.dll` with the downloaded `xul.dll` file.
3. Make sure that `widget.non-native-theme.enabled` is false in `about:config`.
4. Close all Firefox processes in Task Manager and restart the browser.

## Building from source

[Clone Firefox for yourself](https://firefox-source-docs.mozilla.org/setup/index.html), and then mix in the patches as needed. Note that when you clone, it will put in the `mozilla-central` branch. You probably don't want this as these correspond to the latest Nightly builds, which will mean that the produced `xul.dll` binary will likely be incompatible with the current build environment.

After cloning, navigate to `.hg/hgrc` in the source directory and change the default path to the branch of your preferred build. For example, I changed it to `https://hg.mozilla.org/releases/mozilla-esr115` in order to access specific tags for the ESR 115 release. You can usually then find specific tags relating to subversions here: https://hg.mozilla.org/releases/mozilla-esr115/tags ("tags" tab in the Mercurial web viewer).

Since Firefox uses Mercurial for version control, it may be a little unfamiliar to you. You "update", rather than "checkout", different branches. So after you make those changes, I think you just do something like `hg pull -u -r FIREFOX_115_3_1esr_RELEASE` to switch to the specific branch. `pull` makes it get the specific version from remote, and `-u` means to update.

Run `./mach build` to build Firefox from source and then `./mach run` to test it. Here is a [Windhawk](//windhawk.net) mod that I use to quickly test classic theme for Firefox only (where I make a copy of `firefox.exe` named `firefoxa.exe`):

```cpp
// ==WindhawkMod==
// @id firefoxa-classic-theme-test
// @name [Testing] firefox classic theme
// @description The best mod ever that does great things
// @version 0.1
// @author ephemeralViolette
// @include firefoxa.exe
// @compilerOptions -luxtheme
// ==/WindhawkMod==

#include <uxtheme.h>

BOOL Wh_ModInit()
{
SetThemeAppProperties(0);
return TRUE;
}
```

Also, currently there is a bug you need to be aware of. After disabling `widget.non-native-theme.enabled`, you must restart Firefox completely, or else the scrollbars will simply not render. I do not know why this happens exactly (maybe the native theme isn't loaded upon disabling the property? I haven't looked at other controls).

After building, the `xul.dll` file can be found in somewhere like `obj-x86_64-pc-windows-msvc/dist/bin` in the source code root, which looks just like `C:\Program Files\Mozilla Firefox`.

### Distribution method

Also because Firefox uses Mercurial rather than Git, I found it would be more trouble than it's worth to attempt to post a modified codebase onto GitHub. I initially thought to fork [mozilla/gecko-dev](//github.com/mozilla/gecko-dev), but the commit identifiers for this repository do not at all align with their Mercurial revision identifiers, so it is less than worthless. Also, I found I couldn't even find certain tags which were useful to access in the Mercurial version.

As a result, I just `hg export` the patches I make, which makes them pretty easy to bring back into the codebase later.

## Will you do other controls?

Probably. My main focus with the initial release was restoring native scrollbars. I didn't see other controls as important, since their native appearances could be better replicated with CSS.
92 changes: 92 additions & 0 deletions docs/scrollbars.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# How I ported native scrollbars to modern Firefox

"Native" scrollbars (really, they were never native, just themed to look native) were disabled in Firefox 97 and residual code was removed in Firefox 98. This means that the last version of Firefox that they were accessible in was Firefox 96, which was released a pretty long time ago by now.

While it would be ideal to theme the scrollbars natively (i.e. with CSS), there are a few issues with this plan that make it somewhat unviable:

- Installation would require a userChrome.js script (i.e. an autoconfig browser chrome script loader) in order to modify the scrollbar CSS at all. This is because the scrollbars exist in no-mans-land, inaccessible by even the browser chrome inspector and userChrome.css.
- CSS properties on the scrollbar elements, being CSS, are very limited. I could not figure out a way to even adequately replicate the drawing of the native Windows scrollbars from XP to 7, so I gave up rather fast. Since the scrollbars are XUL custom elements, you can't even use pseudoelements on them for glyphs.
- Sites would randomly override the custom CSS if they used any custom scrollbar styles, which I also found very undesirable. I could not figure out how to fix that.

It took me about 4 days straight to figure my way around the Firefox codebase and solve some pretty weird, pretty obvious bugs with this. I am currently maining my `xul.dll` build with native scrollbars and I think it works better than I expected.

My original plan was to do this using Windhawk, which would have been a nicer solution, if only I weren't completely misguided in the beginning, and if Windhawk could actually hook Firefox's symbols. Even though Mozilla has a symbol server for all release builds of Firefox, the symbols for `xul.dll` are so large (~1.19 GB) that Windhawk seemingly fails to load it. I assume it infinitely looped or whatever.

Now, I think a source code modification is the cleanest and best approach to patching this, in any case. Even if it's only a few functions that needed to be patched realistically, it would have still gotten pretty unmanagable to be hooking everything from foreign code needlessly.

## Don't get lost

ScrollbarDrawing (`widget/`) and its related classes are completely pointless to the implementation of native-styled scrollbars. They are
only responsible for drawing routines of the non-native ones seen in regular use.

Scrollbar drawing proper is done by `layout/generic/nsGfxScrollFrame.cpp`, with backing from `layout/xul/nsScrollbarFrame.cpp`. Themes
are reported by the application theme, which is `nsNativeThemeWin` when non-native controls are disabled (presumedly).

The primary file you will be editing is `widget/win/nsNativeThemeWin.cpp`.

## Important change history:

1. Sometime before the scrollbar changes, rendering code was restructured, such that
nsBasicNativeTheme, previously platform-specific, became `Theme` (`widgets/Theme.cpp`).
This doesn't really matter except in tracking change history, which may be quite useful
to do. (commit: https://github.com/mozilla/gecko-dev/commit/0468798e53b23f65d6538d985c0d3fef08cd5816)

2. As of ESR 115, scrollbar sizing code was greatly simplified. Previously, there was a `ScrollbarSizes`
struct which was used internally. This allowed for scrollbars to have independent horizontal and
vertical sizes, albeit this was never used. To account for this change, anything extending from
`nsITheme` (like `nsNativeThemeWin`) must change:

```
virtual ScrollbarSizes GetScrollbarSizes(nsPresContext *, StyleScrollbarWidth, Overlay);
```

to:

```
virtual LayoutDeviceIntCoord GetScrollbarSize(const nsPresContext *, StyleScrollbarWidth, Overlay);
```

Do note that the `const` before the `nsPresContext` argument is also very important.

(commit: https://github.com/mozilla/gecko-dev/commit/604d8268b2e83340b8fce766bed3a0302c65cac4)

**This was important to me in an early iteration, however, I figured out you don't need this function in the first place.** I simply use `GetMinimumWidgetSize` and `ClassicGetMinimumWidgetSize` since the initial public release of this.

3. Similarly, XUL layout was deprecated for the scrollbars by ESR 115. You will need to modify how
scrollbar size calculation works in order for it to appear at all (in debug builds, there will be an assertion
fail). I originally did this by modifying `ScrollbarMinSize` in `layout/xul/nsScrollbarFrame.cpp`, but later changed it to a simpler approach by modifying
`GetMinimumWidgetSize` in `widget/win/nsNativeThemeWin.cpp`, which meant modifying one fewer file. (commit: https://github.com/mozilla/gecko-dev/commit/38b10eafda0c7d5959deab3b7f212eddaba5cc57)

4. At some point, `EventState` was merged into `ElementState`. You can just replace all "`EventState`" with
"`ElementState`" and "`NS_EVENT_STATE_`" with "`ElementState::`", if this is applicable.

5. At some point (I don't know when), `nsNativeThemeWin::GetWidgetBorder` broke with scrollbar thumbs. This may need to be fixed, which can be done by redirecting the call to `nsNativeThemeWin::ClassicGetWidgetBorder`.

In the provided diff file, I basically already made all of these necessary changes as of ESR 115.

## General instructions

Here are the general steps for restoring native scrollbars. This is the approach I used to restore them to ESR 115,
and these instructions may be very useful for applying the patch to previous or later versions of Firefox.


1. Generally undo these commits:
- ["Always draw scrollbars using the non-native theme on Windows."](https://github.com/mozilla/gecko-dev/commit/36215bf43067b04058cc397a12ef78b4f864d2a6)
- ["Remove dead windows scrollbar drawing code."](https://github.com/mozilla/gecko-dev/commit/98c3acf4c210d194f0e48f9d6e87813a002c390b)

Note that there have been a few changes in types and code organisation since then, so you will need to make
some modifications to the code. One such example includes changing `GetScrollbarSizes` to the simpler `GetScrollbarSize`
(described above).

2. Fix up layout code if necessary (important change history #2 and #3).

3. In nsNativeThemeWin, make `GetWidgetBorder` call `ClassicGetWidgetBorder` for thumbs:
- `StyleAppearance::ScrollbarthumbVertical`
- `StyleAppearance::ScrollbarthumbHorizontal`

This is necessary in order for the horizontal thumbs to appear at all with themes for some versions of Firefox.

## Files I changed

- `widget/windows/nsNativeThemeWin.cpp` (most things)
- `widget/windows/nsUXThemeData.cpp` & `.h` (old UxTheme theme classes)
Loading

0 comments on commit 7628052

Please sign in to comment.