Repository navigation
Commit 497ca3d
Maintenance: bug fixes, type hints, and documentation corrections (#1836)
* [Qt] Fix confirmation dialogs only rendering one button
QMessageBox.question() was called with a single button flag as both
the buttons and defaultButton arguments, so QMessageBox silently
dropped the button that wasn't part of `buttons`. As a result,
create_confirmation_dialog() could never return True (no Ok button
was ever shown) and the close-confirmation dialog could never
prevent a window from closing (no No button was ever shown).
* [Core] Fix load_url() leaking a new HTTP server on every call
load_url() assigned the newly started server to self.server instead
of self._server, so self._server was never updated and stayed None.
Since the reuse check reads self._server, every subsequent call to
load_url() with a local/app URL spun up a brand-new BottleServer
(new thread + listening socket) and orphaned the previous one
instead of reusing it.
* [Core] Fix custom server class being silently discarded
Window.__init__ stored the user-supplied server class in self._server
and then immediately overwrote it with None two lines later (that
slot is meant to hold the running server instance, not the class).
_initialize() then always passed self._server (always None) to
http.start_server(), so a custom server= class passed to Window()/
create_window() was never actually used. The class is now kept in
its own self._server_class attribute.
* [Core] Fix mutable default arguments and bad server_args default
menu=[], server_args={}/dict and localization={} defaults on Window,
Menu, start() and create_window() were shared mutable objects: two
windows created without an explicit menu= shared and mutated the
same list, and start()'s server_args default was observably mutated
in place (keyfile/certfile), corrupting a later start() call made
without server_args. Window._initialize() also defaulted server_args
to the dict class itself rather than an empty dict, raising TypeError
on **dict when no server_args were supplied anywhere. All of these
now use None sentinels normalized to a fresh list/dict per call.
* [Core] Fix dead server-reuse check in create_window()
create_window()'s dynamic-window path checked `server.is_running`,
but `server` is a class (type[http.ServerType]), not an instance, so
this read the unbound `is_running` property object -- always truthy.
Combined with operator precedence, `is_app(url) or is_local_url(url)
and not server.is_running` reduced to just `is_app(url)`, so windows
created for local URLs from a background thread after start() never
reused an already-running server. Now checks the actual global_server
instance for whether a server is already running.
* [DOM] Escape strings interpolated into generated JS
dom.py, element.py and classlist.py built JS source by interpolating
selectors, ids, text content, class names and event names directly
into quoted JS string literals without escaping. Any value containing
a quote character (a normal CSS selector like input[type="text"], or
ordinary text like "Don't") broke the generated script; a value drawn
from untrusted page content could inject arbitrary JS. propsdict.py
already used webview.util.escape_string for this; the other DOM
files now do too.
* [DOM] Fix backwards escaping for attribute JSON payload
PropsDict.__init__'s Attribute branch escaped individual key/value
strings before running them through json.dumps, then embedded the
result raw in a single-quoted JS literal. This left the JSON text's
own quotes unescaped for the JS-string context (breaking/injecting
into JSON.parse('...')) and double-escaped backslashes (corrupting
round-tripped values). Now matches the correct pattern already used
by the Style branch and __set_attribute/__set_style: escape the
whole json.dumps() output once, after serialization.
* [Core] Escape initial state JSON injected into state.js
load_js_files() embedded json.dumps(window.state) directly into
state.js's `var initialState = '%(state)s'` template. json.dumps
does not escape single quotes, so any window.state value containing
an apostrophe (e.g. a name like "O'Brien") broke pywebview's own
injected startup script, preventing pywebview from initializing in
the page. Now escaped the same way propsdict.py escapes its JSON
payloads before embedding them in a single-quoted JS literal.
* [DOM] Fix event handler cleanup on Element.remove()
The JS injected by remove() referenced the undefined identifier
handler_id instead of the handlerId closure parameter, throwing a
ReferenceError for every removed element that had at least one
registered event handler. Since this failing script ran before
self._exists was set to False, and remove() is wrapped by @_exists
(which swallows JavascriptException), the Python object could keep
believing the removed node still existed, and pywebview._eventHandlers
entries for the removed element were never cleaned up.
* [DOM] Fix off() removing the whole element registry entry
off() unconditionally deleted the element from
window.dom._elements, regardless of whether other event handlers
were still registered on it. This broke still-attached listeners for
other events (pywebviewEventHandler looks the element up via
dom._elements.get() and silently no-ops if missing) and raised an
uncaught KeyError on a second off() call for a different callback on
the same element, since the registry entry was already gone. Now
only removed once no handlers remain tracked for the element.
* [DOM] Key event handler ids by (event, callback), not callback alone
_event_handler_ids was keyed by the Python callback only. Registering
the same callable for two different events on the same element (e.g.
on('click', cb) then on('mouseover', cb)) made the second on() call
silently overwrite the tracked handler_id for the first, so a
subsequent off(event, callback) could remove/detach the wrong
handler_id and leave a phantom listener attached in the page.
* [DOM] Fix DOMEvent.__sub__ raising on an unregistered handler
__sub__ (event.event - handler) called list.remove() unguarded,
raising an uncaught ValueError if the handler was never registered.
__isub__ (event.event -= handler) already guarded this with an
`if item in self._items` check and a warning log; __sub__ now does
the same for consistency.
* [Qt] Fix request_sent header values being replaced with their keys
RequestInterceptor.interceptRequest() built the headers dict with
k.data().decode('utf-8') for both the key and the value expression
(reusing k instead of v), so every header delivered to the
request_sent event had its value replaced with its own key name
(e.g. {'User-Agent': 'User-Agent'}) instead of the real header value.
* [Qt] Fix evaluate_js() hanging forever on the PySide TypeError fallback
on_evaluate_js()'s except TypeError branch (hit on some PySide2/
PySide6 versions where runJavaScript() rejects a Python callback)
fired the script with no result callback at all, so return_result()
was never invoked and evaluate_js()'s result_semaphore.acquire()
blocked forever. Now explicitly releases the wait with no result
instead of silently dropping it.
* [GTK] Fix dialog flags using bitwise AND instead of OR
Gtk.DialogFlags.MODAL and DESTROY_WITH_PARENT are disjoint bit flags,
so ANDing them together always produced 0. Every close-confirmation
dialog, create_confirmation_dialog(), and message_box() dialog was
therefore created non-modal (the user could interact with the main
window while it was open) and without DESTROY_WITH_PARENT (left
orphaned if the parent window was destroyed while it was showing).
* [EdgeChromium] Fix crash on 3-digit hex background_color
DefaultBackgroundColor parsing assumed a 6-hex-digit color string.
A valid CSS shorthand like background_color='#FFF' made the [4:6]
slice empty, so int('', 16) raised ValueError and crashed window
creation. GTK and WinForms already handle 3-digit hex correctly;
EdgeChromium now expands it to 6 digits first, same as GTK's
_hex_to_rgba().
* [Winforms] Fix crash when .NET Framework registry key is missing
_is_chromium() left net_key unbound if winreg.OpenKey() raised (e.g.
the NDP\\v4\\Full key is absent on minimal/Server Core installs). The
broad except swallowed that, but the finally block unconditionally
called winreg.CloseKey(net_key), raising UnboundLocalError. Since
_is_chromium() runs unconditionally at module import, this crashed
the whole Winforms backend on import. net_key is now initialized to
None and only closed if it was actually opened.
* [CEF] Fix evaluate_js() deadlock on invalid JSON and ignored parse_json
JSBridge.return_result() called json.loads(result) with no error
handling; if the page returned something that wasn't valid JSON, the
exception propagated out of the CEF-invoked callback before
eval_events[uid].set() ran, so Browser.evaluate_js()'s unconditional
.wait() blocked forever. return_result() also always attempted to
JSON-decode the result regardless of the parse_json argument passed
to evaluate_js(), unlike every other backend. parse_json is now
tracked per call and return_result() falls back to the raw result
(instead of raising) on a decode failure, matching gtk.py/mshtml.py/
edgechromium.py.
* [GTK] Fix evaluate_js() hanging forever if the window closes mid-call
self.js_results was initialized but never written to; close_window()
looped over it to release any blocked evaluate_js() callers, but it
was always empty, since the real evaluate_js() blocked on a purely
local result_semaphore untracked anywhere. A call to evaluate_js()
from a background thread would hang forever if the window/webview
was torn down before the JS callback fired. evaluate_js() now
registers its semaphore in self.js_results for the duration of the
call so close_window() can actually release it. Also iterate over a
copy of self.js_results.values() in close_window(), since a woken
evaluate_js() call removes its own entry concurrently while the
close loop is still iterating.
* [Winforms/EdgeChromium/MSHTML] Fix evaluate_js() hanging forever on close
EdgeChrome.evaluate_js() runs ExecuteScriptAsync().ContinueWith(...)
and blocks on a purely local semaphore untracked anywhere; the
instance's js_result_semaphore (which winforms.py's on_close/
destroy_window released on window close) was never the object the
call actually waited on. If the window closed before the async
script completed, the calling thread hung forever. MSHTML had the
same dead-semaphore pattern, though its evaluate_js is synchronous
via Control.Invoke so the practical risk there was lower.
Both classes now register their per-call semaphore in a js_results
dict (mirroring the same fix already applied to gtk.py), and
winforms.py's on_close releases every pending entry instead of a
single disconnected semaphore. destroy_window()'s separate release
call is removed: i.Close() synchronously raises FormClosed (wired to
on_close) before Invoke() returns, so that second release was
already redundant (a double-release bug in its own right).
* [CEF] Fix evaluate_js() hanging forever if the window closes mid-call
close_window() closed the browser and removed the instance without
touching eval_events, so a background thread blocked in
evaluate_js()'s unbounded event.wait() (no timeout) would hang
forever if the window was closed before the JS callback fired.
close_window() now sets every pending event first. evaluate_js()
also now uses .get()/.pop(..., None) when reading the JSON result
back, since a call unblocked this way never gets a real result from
JSBridge.return_result().
* [Cocoa] Fix get_cookies() deadlock for windows created without a URL
self.url was only ever assigned when window.real_url was set; a
window created with html= (or no url/html at all) never got the
attribute, so get_cookies()'s async handler raised AttributeError on
`domain not in self.url` before releasing cookie_semaphore -- since
the exception occurred inside a WebKit-invoked callback, it was
swallowed rather than propagated, and the calling thread's
cookie_semaphore.acquire() blocked forever. self.url is now always
initialized to None, and the domain filter is skipped when there is
no current URL to filter against instead of crashing.
* [Cocoa] Fix create_file_dialog(SAVE) returning a bare string
The documented contract (and the Qt/GTK/Winforms backends) return a
tuple of selected files, or None if cancelled. Cocoa's SAVE branch
set self._file_name to save_dlg.filename() directly -- a bare str.
Code written against the documented tuple contract (e.g.
window.create_file_dialog(FileDialog.SAVE)[0]) silently got just the
first character of the path on macOS instead of the full path.
* [Cocoa] Fix <input type=file> dialog resolving to the wrong window
webView_runOpenPanelWithParameters_initiatedByFrame_completionHandler_
hardcoded list(BrowserView.instances.values())[0] -- "the first
window ever created" -- instead of resolving the instance that
actually triggered the callback, unlike every other WKUIDelegate
method in this file. In a multi-window app, opening a file picker
from any window other than the first used the wrong window's
localization context, and raised IndexError if the first-created
window had since closed.
* [Cocoa] Remove script message handlers on window close to avoid a leak
addScriptMessageHandler_name_ was called for 'browserDelegate' and
'jsBridge' at window creation, but windowWillClose_ never called the
matching removeScriptMessageHandlerForName_. WKUserContentController
holds a strong reference to registered handlers, which is a
documented WKWebView leak pattern that could keep JSBridge/
BrowserDelegate (and transitively the window/webview) alive after a
window was closed.
* [Cocoa] Fix DownloadDelegate leaking on every download
download.setDelegate_() was given a DownloadDelegate with an explicit
.retain() (needed since setDelegate_ doesn't retain), but nothing
ever released it. Since this delegate only implements the single
"decide destination" callback, it's now released right after that
callback completes, balancing the retain.
* [GTK] Fix minimized/maximized events dropped on combined state changes
on_window_state_change() tested changed_mask == ICONIFIED /
== MAXIMIZED as mutually exclusive branches. If a single
window-state-event reported both bits changed simultaneously, neither
equality matched and both the minimized/restored and maximized/
restored events were silently dropped for that transition. Now tests
each bit independently with & instead of chaining as if/elif on ==.
* [GTK] Fix dropped file paths not being percent-decoded
on_drag_data() only stripped the 'file://' prefix from dropped-file
URIs, leaving percent-encoded sequences intact (spaces as %20,
unicode escaped, etc). Any dropped file with a space or non-ASCII
character in its name produced a mangled path in _dnd_state['paths'],
breaking os.path.basename and any later file open. Now uses
GLib.filename_from_uri(), the same API family already used for the
reverse conversion in on_download_decide_destination(), with a
fallback to the old behavior if the URI can't be parsed.
* [Winforms] Fix file dialog default directory using drive-relative path
create_file_dialog() defaulted to os.environ['HOMEPATH'] alone, which
is drive-relative on Windows (e.g. \Users\name, no drive letter).
Without HOMEDRIVE, this could point at the wrong or a nonexistent
location if the process's current drive differs from the user's home
drive. Now prefers USERPROFILE (already an absolute path), falling
back to HOMEDRIVE+HOMEPATH.
* [Core] Fix Event.set() crashing when a handler returns an unhashable value
execute() collected handler return values in a set() via .add(value).
A handler returning an unhashable value (a list or dict) raised
TypeError there, which was caught by the enclosing except and logged
as if the handler call itself had failed, even though it succeeded --
losing the return value. return_values is now a plain list, which
only requires the values to exist, not be hashable; the only
consumer (checking for a False return) works identically on a list.
* [Core] Fix Event.__sub__/__isub__ raising on an unregistered handler
window.events.<event> -= handler (or `- handler`) called
list.remove() unguarded, raising an uncaught ValueError if the
handler was never registered on that event. Both now guard with an
`in` check and log a warning instead, matching the fix already
applied to webview.dom.event.DOMEvent for the same pattern.
* [Core] Fix GUI backend probing not catching all expected import failures
import_gtk/import_android already catch (ImportError, ValueError)
since gi.require_version() raises ValueError when a binding isn't
available, but import_qt/import_cocoa only caught ImportError. On
Windows, a pythonnet/CLR load failure can raise RuntimeError rather
than ImportError; import_winforms only caught ImportError too. In
all three cases a real load failure would propagate uncaught out of
initialize() instead of being logged and falling through to try the
next backend.
* [MSHTML] Fix debug-mode console forwarding script
The script passed a stray trailing quote before the closing brace
(`}',`), producing a JS syntax error, and passed the script as a
bare string to InvokeScript('eval', ...) instead of the (script,)
tuple form InvokeScript's object[] args expects -- the pattern
already used correctly elsewhere in this file (evaluate_js). Both
bugs silently broke window.console.log/error forwarding to the
Python-side debug console in the legacy MSHTML backend's debug mode.
* [CEF] Fix HTML content truncated at a literal '#' in data: URIs
load_html() and create_browser() built data:text/html,{html} URIs
with no percent-encoding. A literal '#' anywhere in the loaded HTML
or an injected script was parsed as the URL fragment delimiter,
truncating the document at that point -- a failure mode unique to
CEF, since the other backends load HTML through APIs that don't
URL-encode the content. HTML is now percent-encoded via
urllib.parse.quote() before being embedded in the data: URI.
* [Qt] Fix closeEvent() risking reentrant execution and a KeyError
closeEvent() called self.close() on itself right after event.accept()
and deleting the window from BrowserView.instances. self.close()
synchronously re-dispatches a QCloseEvent (re-entering closeEvent) if
the widget hasn't been hidden yet, which it hasn't at that point --
event.accept() only marks the event as accepted, Qt hides the widget
after closeEvent returns. A reentrant call would re-fire
events.closing (potentially showing a second confirmation dialog)
and then hit del BrowserView.instances[self.uid] on an already-
removed key, raising KeyError inside a Qt event callback. The call
was redundant: event.accept() already tells Qt to proceed with the
close.
* [Core] Fix Response aliasing the caller's headers dict
Request.__init__ copies the headers dict it's given; Response did
not, so a caller-owned dict could be mutated later and silently
change an already-constructed Response's headers (or vice versa).
Response now copies headers the same way Request does.
* [Core] Fix incorrect Python version comparison in create_cookie()
`sys.version_info.major >= 3 and sys.version_info.minor >= 8` is not
equivalent to Python >= 3.8 (e.g. it would be False on a hypothetical
Python 4.0 with minor < 8). No practical impact under currently
supported Python versions, but the tuple comparison is the correct
way to express this.
* [Core] Fix KeyError on a duplicate/late pywebviewAsyncCallback
js_bridge_call() accessed window._callbacks[value_id] with [],
raising an uncaught KeyError if a duplicate or late JS callback
message arrived for a value_id whose callback had already been
consumed and removed. Now checks membership first and logs a
warning instead of raising.
* [Core] Fix load_html()'s default base_uri being frozen at import time
base_uri: str = base_uri() evaluated the default once when window.py
was first imported, freezing it to whatever get_app_root() returned
at that moment rather than recomputing it per call. Now uses a None
sentinel and computes it fresh inside the function body when the
caller doesn't pass one.
* [Core] Remove misleading NameError handling in _api_call decorator
The wrapper already raises WebViewException with a specific message
for both failure cases it's meant to handle (event.wait() timeout,
gui not initialized) before calling the wrapped function. Nothing in
that path naturally raises NameError, so the except NameError clause
was dead for its intended purpose, and would instead catch a genuine
NameError bug in wrapped platform code and mischaracterize it as
"create a window first", masking the real error.
* [Core] Fix width/height/x/y/title bypassing the standard API error handling
These accessed self.gui.get_size()/get_position()/set_title()
directly after a raw self.events.shown/loaded.wait(15) call, without
checking the wait's return value or whether self.gui was None. If
the wait timed out or gui wasn't initialized yet, they raised a raw
AttributeError ('NoneType' object has no attribute ...) instead of
the WebViewException every other API method raises via the
_shown_call/_loaded_call decorators. Now use those decorators like
the rest of the class.
* [Docs] Fix shadow default documented as False
webview/window.py and webview/__init__.py both default shadow=True.
The API docs showed shadow=False in the create_window() signature
sample and stated "Default is False" in the parameter description.
* [Docs] Fix response_received documenting a nonexistent 'status' property
webview.models.Response only has status_code (constructed with
status_code everywhere it's built across the gtk/edgechromium/cocoa
backends). Code written against the doc's `response.status` raised
AttributeError.
* [Docs] Fix broken example links to nonexistent example pages
The doc site generates /examples/<name>.html 1:1 from
examples/<name>.py filenames. Several links referenced files that
don't exist under that name: open_url -> simple_browser,
show_hide -> hide_window, css_load -> load_css, html_load ->
load_html, minimize -> window_state.
* [Docs] Fix broken internal cross-reference links
- faq.md linked to /guide/renderer, which doesn't exist; the actual
content is at /guide/web_engine.html.
- pywebview6.md linked to /guide/api.html for the API reference; the
API docs live at /api.html, not under /guide/.
- api/README.md's "Manipulation mode" links (4 occurrences) pointed
to the anchor #manipulation-mode, but the actual heading is
"webview.dom.ManipulationMode", which slugifies to
#webviewdommanipulationmode.
* [Docs] Fix syntax error in DOM guide example
create_element('<h1>Warning</h1>' parent=..., ...) was missing the
comma between the html argument and the parent kwarg, raising
SyntaxError if copy-pasted as-is.
* [Docs] Fix stale "icon supported only on GTK/QT" claims
webview/platforms/cocoa.py and winforms.py both consume
_state['icon'] at runtime too (cocoa.py's NSImage/dock icon and
winforms.py's window Icon), contradicting the start()/create_window
docstring, the FAQ, and examples/icon.py, all of which claimed only
GTK and QT support setting the icon via webview.start(icon=...).
This also directly contradicted docs/api/README.md's create_window
icon docs, which already correctly listed .ico/Windows and
.icns/macOS support.
* [Docs] Fix stale SECURITY.md supported-versions table
The project is currently at 6.2.1; SECURITY.md still listed only 3.x
as supported, which would tell a real vulnerability reporter their
current install isn't covered. Replaced the hardcoded version table
with a policy that references the changelog instead, so it doesn't
go stale on every major release.
* [Docs] Fix webview.settings sample and document DEFAULT_HTTP_PORT
The settings code sample showed 'pywebview-drag-region' (no leading
dot) for DRAG_REGION_SELECTOR, contradicting both the actual default
('.pywebview-drag-region', a CSS class selector) and this doc's own
prose two lines below. DEFAULT_HTTP_PORT and WEBVIEW2_RUNTIME_PATH
were also missing from the code sample, and DEFAULT_HTTP_PORT had no
description at all despite being a real settings key.
* [Docs] Fix FileDialog.OPEN.SAVE typo
FileDialog.OPEN, .FOLDER and .SAVE are sibling enum members, not
nested attributes; the doc referenced the nonexistent
webview.FileDialog.OPEN.SAVE.
* [Docs] Fix stale Python 2/3 compatibility claim in module docstring
pyproject.toml requires >=3.8 and there is no Python 2 support in
the current codebase. Also added Android, which pywebview supports
but the docstring didn't mention.
* [Docs] Fix stale Python 3.7 references
requires-python = ">=3.8" (and ruff's target-version = "py38") but
pyproject.toml's classifiers still listed 3.7, and the contributing
guide stated "Target Python version: 3.7+". Both now say 3.8+,
matching the actual minimum.
* Marshal Form.TopMost to the GUI thread in the WinForms backend (#1831)
set_on_top assigned Form.TopMost directly on the calling thread. When called
from a non-GUI thread (e.g. a js_api handler, which pywebview runs on a worker
thread), this is an unmarshaled cross-thread operation on a WinForms control,
and the message pump can deadlock permanently while the system is doing window
activation work — the window goes to Not Responding with no exception raised.
Every other window operation in this backend already marshals via Invoke
(show, hide, toggle_fullscreen, maximize, minimize, restore); set_on_top was
the exception. This applies the same pattern, with the InvokeRequired guard
used by toggle_fullscreen so calls already on the GUI thread stay direct.
* WinForms: Shift focus to menu bar when active (#1835)
* WinForms: Shift focus to menu bar when active
This allows keyboard navigation of the menu bar.
Note: Alt+F style &File menu mnemonics are not supported with this
change. They would require more complex forwarding of keystrokes.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Troy Deck <troy@visitorlabs.com>
Co-authored-by: Roman <roman@flowrl.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* [Cocoa] fix window coordinates on multi-monitor setups (#1828)
move() added the cached screen's origin to coordinates that are already absolute, so a
frameless window dragged on a screen with a non-zero origin jumped off-screen: the drag
region JS sends ev.screenX/ev.screenY, which are absolute already.
move() and get_position now treat x/y as absolute and flip y against the primary screen
instead of the window's screen, so both agree on any monitor arrangement. initial_x and
initial_y stay relative to the target screen, as on the qt backend.
Fixes #1820
Co-authored-by: Victor Purcallas Marchesi <6121411+VictorPurMar@users.noreply.github.com>
* Add type hints for functions and parameters across multiple modules
* Add lock acquisition to various test functions for thread safety
* Add caching for WebView2 Runtime in Windows CI setup
* FIx CI
* Add future annotations import to multiple modules
* [QT] Skip response test
* Add token validation to JS bridge calls and update related interfaces
* Refactor test mode handling to use a dedicated utility function
* Fix typos in documentation and improve token validation in JS bridge calls
* Remove test for missing token validation in JS bridge calls
* Use json.dumps for selector and id in DOM methods to ensure proper string formatting
---------
Co-authored-by: fjmarson <fjmarson@gmail.com>
Co-authored-by: Troy Deck <troy.deque@gmail.com>
Co-authored-by: Troy Deck <troy@visitorlabs.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Victor Purcallas Marchesi <vpurcallas@gmail.com>
Co-authored-by: Victor Purcallas Marchesi <6121411+VictorPurMar@users.noreply.github.com>1 parent 8b02c10 commit 497ca3d
45 files changed
Lines changed: 470 additions & 258 deletions
File tree
- .github/workflows
- docs
- api
- blog
- contributing
- guide
- examples
- interop/android/lib/src/main/java/com/pywebview
- tests
- webview
- dom
- js
- lib
- platforms
- android
- jinterface
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
155 | 162 | | |
156 | 163 | | |
157 | 164 | | |
| |||
162 | 169 | | |
163 | 170 | | |
164 | 171 | | |
165 | | - | |
| 172 | + | |
166 | 173 | | |
167 | 174 | | |
168 | 175 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
| 5 | + | |
| 6 | + | |
11 | 7 | | |
12 | 8 | | |
13 | 9 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
109 | | - | |
| 109 | + | |
110 | 110 | | |
| 111 | + | |
111 | 112 | | |
112 | 113 | | |
113 | | - | |
114 | 114 | | |
115 | | - | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
116 | 118 | | |
117 | 119 | | |
118 | 120 | | |
| |||
122 | 124 | | |
123 | 125 | | |
124 | 126 | | |
| 127 | + | |
125 | 128 | | |
126 | 129 | | |
127 | 130 | | |
| |||
211 | 214 | | |
212 | 215 | | |
213 | 216 | | |
214 | | - | |
| 217 | + | |
215 | 218 | | |
216 | 219 | | |
217 | 220 | | |
| |||
235 | 238 | | |
236 | 239 | | |
237 | 240 | | |
238 | | - | |
| 241 | + | |
239 | 242 | | |
240 | 243 | | |
241 | 244 | | |
| |||
291 | 294 | | |
292 | 295 | | |
293 | 296 | | |
294 | | - | |
| 297 | + | |
295 | 298 | | |
296 | 299 | | |
297 | 300 | | |
| |||
625 | 628 | | |
626 | 629 | | |
627 | 630 | | |
628 | | - | |
| 631 | + | |
629 | 632 | | |
630 | 633 | | |
631 | 634 | | |
| |||
702 | 705 | | |
703 | 706 | | |
704 | 707 | | |
705 | | - | |
| 708 | + | |
706 | 709 | | |
707 | 710 | | |
708 | 711 | | |
| |||
713 | 716 | | |
714 | 717 | | |
715 | 718 | | |
716 | | - | |
| 719 | + | |
717 | 720 | | |
718 | 721 | | |
719 | 722 | | |
| |||
724 | 727 | | |
725 | 728 | | |
726 | 729 | | |
727 | | - | |
| 730 | + | |
728 | 731 | | |
729 | 732 | | |
730 | 733 | | |
| |||
801 | 804 | | |
802 | 805 | | |
803 | 806 | | |
804 | | - | |
| 807 | + | |
805 | 808 | | |
806 | 809 | | |
807 | 810 | | |
| |||
811 | 814 | | |
812 | 815 | | |
813 | 816 | | |
814 | | - | |
| 817 | + | |
815 | 818 | | |
816 | 819 | | |
817 | 820 | | |
| |||
842 | 845 | | |
843 | 846 | | |
844 | 847 | | |
845 | | - | |
| 848 | + | |
846 | 849 | | |
847 | 850 | | |
848 | 851 | | |
| |||
868 | 871 | | |
869 | 872 | | |
870 | 873 | | |
871 | | - | |
| 874 | + | |
872 | 875 | | |
873 | 876 | | |
874 | 877 | | |
| |||
968 | 971 | | |
969 | 972 | | |
970 | 973 | | |
971 | | - | |
| 974 | + | |
972 | 975 | | |
973 | 976 | | |
974 | 977 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
0 commit comments