Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package perl-Mojolicious for
openSUSE:Factory checked in at 2026-05-13 17:21:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
and /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1966 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Mojolicious"
Wed May 13 17:21:28 2026 rev:183 rq:1352905 version:9.450.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes
2025-10-10 17:13:28.651666682 +0200
+++
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1966/perl-Mojolicious.changes
2026-05-13 17:23:19.221201375 +0200
@@ -1,0 +2,18 @@
+Wed May 6 07:47:09 UTC 2026 - Tina Müller <[email protected]>
+
+- updated to 9.450.0 (9.45)
+ see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+ 9.45 2026-05-05
+ - Fixed portability issue in WebSocket tests.
+
+ 9.44 2026-05-05
+ - Fixed various spec compliance issues in Mojo::DOM.
+ - Fixed permessage-deflate support in Mojo::Transaction::WebSocket to be
more interoperable with non-spec compliant
+ implementations.
+
+ 9.43 2026-05-04
+ - Fixed punycode roundtrip bug in Mojo::Util.
+ - Fixed Windows compatibility issues of Mojo::File::list_tree.
+
+-------------------------------------------------------------------
Old:
----
Mojolicious-9.42.tar.gz
New:
----
Mojolicious-9.45.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.w1XIkd/_old 2026-05-13 17:23:20.093237570 +0200
+++ /var/tmp/diff_new_pack.w1XIkd/_new 2026-05-13 17:23:20.097237737 +0200
@@ -1,7 +1,7 @@
#
# spec file for package perl-Mojolicious
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,10 +18,10 @@
%define cpan_name Mojolicious
Name: perl-Mojolicious
-Version: 9.420.0
+Version: 9.450.0
Release: 0
-# 9.42 -> normalize -> 9.420.0
-%define cpan_version 9.42
+# 9.45 -> normalize -> 9.450.0
+%define cpan_version 9.45
License: Artistic-2.0
Summary: Real-time web framework
URL: https://metacpan.org/release/%{cpan_name}
++++++ Mojolicious-9.42.tar.gz -> Mojolicious-9.45.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/Changes new/Mojolicious-9.45/Changes
--- old/Mojolicious-9.42/Changes 2025-10-01 16:40:36.000000000 +0200
+++ new/Mojolicious-9.45/Changes 2026-05-05 20:30:05.000000000 +0200
@@ -1,4 +1,16 @@
+9.45 2026-05-05
+ - Fixed portability issue in WebSocket tests.
+
+9.44 2026-05-05
+ - Fixed various spec compliance issues in Mojo::DOM.
+ - Fixed permessage-deflate support in Mojo::Transaction::WebSocket to be
more interoperable with non-spec compliant
+ implementations.
+
+9.43 2026-05-04
+ - Fixed punycode roundtrip bug in Mojo::Util.
+ - Fixed Windows compatibility issues of Mojo::File::list_tree.
+
9.42 2025-10-01
- Un-deprecated the spurt method in Mojo::File, it is now an alternative to
spew.
- Removed experimental status from top-level await support in Mojo::Promise.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/META.json
new/Mojolicious-9.45/META.json
--- old/Mojolicious-9.42/META.json 2025-10-01 16:46:10.000000000 +0200
+++ new/Mojolicious-9.45/META.json 2026-05-05 20:33:03.000000000 +0200
@@ -52,7 +52,7 @@
},
"homepage" : "https://mojolicious.org",
"license" : [
- "http://www.opensource.org/licenses/artistic-license-2.0"
+ "https://opensource.org/license/artistic-2-0"
],
"repository" : {
"type" : "git",
@@ -64,6 +64,6 @@
"web" : "https://web.libera.chat/#mojo"
}
},
- "version" : "9.42",
+ "version" : "9.45",
"x_serialization_backend" : "JSON::PP version 4.16"
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/META.yml
new/Mojolicious-9.45/META.yml
--- old/Mojolicious-9.42/META.yml 2025-10-01 16:46:10.000000000 +0200
+++ new/Mojolicious-9.45/META.yml 2026-05-05 20:33:03.000000000 +0200
@@ -33,7 +33,7 @@
web: https://web.libera.chat/#mojo
bugtracker: https://github.com/mojolicious/mojo/issues
homepage: https://mojolicious.org
- license: http://www.opensource.org/licenses/artistic-license-2.0
+ license: https://opensource.org/license/artistic-2-0
repository: https://github.com/mojolicious/mojo.git
-version: '9.42'
+version: '9.45'
x_serialization_backend: 'CPAN::Meta::YAML version 0.020'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/Makefile.PL
new/Mojolicious-9.45/Makefile.PL
--- old/Mojolicious-9.42/Makefile.PL 2022-06-14 13:31:51.000000000 +0200
+++ new/Mojolicious-9.45/Makefile.PL 2026-02-12 12:07:38.000000000 +0100
@@ -22,7 +22,7 @@
resources => {
bugtracker => {web => 'https://github.com/mojolicious/mojo/issues'},
homepage => 'https://mojolicious.org',
- license =>
['http://www.opensource.org/licenses/artistic-license-2.0'],
+ license => ['https://opensource.org/license/artistic-2-0'],
repository => {
type => 'git',
url => 'https://github.com/mojolicious/mojo.git',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojo/DOM/CSS.pm
new/Mojolicious-9.45/lib/Mojo/DOM/CSS.pm
--- old/Mojolicious-9.42/lib/Mojo/DOM/CSS.pm 2023-03-08 19:43:29.000000000
+0100
+++ new/Mojolicious-9.45/lib/Mojo/DOM/CSS.pm 2026-05-05 18:08:48.000000000
+0200
@@ -22,7 +22,8 @@
sub matches {
my $tree = shift->tree;
- return $tree->[0] ne 'tag' ? undef : _match(_compile(@_), $tree, $tree,
_root($tree));
+ return undef if $tree->[0] ne 'tag';
+ return _match(_compile(@_), $tree, $tree, _root($tree));
}
sub select { _select(0, shift->tree, _compile(@_)) }
@@ -30,16 +31,18 @@
sub _absolutize { [map { _is_scoped($_) ? $_ : [[['pc', 'scope']], ' ', @$_] }
@{shift()}] }
-sub _ancestor {
- my ($selectors, $current, $tree, $scope, $one, $pos) = @_;
+sub _all_tags {
+ my $tree = shift;
- while ($current ne $scope && $current->[0] ne 'root' && ($current =
$current->[3])) {
- return 1 if _combinator($selectors, $current, $tree, $scope, $pos);
- return undef if $current eq $scope;
- last if $one;
+ my @tags;
+ my @queue = @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree];
+ while (my $current = shift @queue) {
+ next unless $current->[0] eq 'tag';
+ push @tags, $current;
+ unshift @queue, @$current[4 .. $#$current];
}
- return undef;
+ return \@tags;
}
sub _attr {
@@ -55,29 +58,6 @@
return undef;
}
-sub _combinator {
- my ($selectors, $current, $tree, $scope, $pos) = @_;
-
- # Selector
- return undef unless my $c = $selectors->[$pos];
- if (ref $c) {
- return undef unless _selector($c, $current, $tree, $scope);
- return 1 unless $c = $selectors->[++$pos];
- }
-
- # ">" (parent only)
- return _ancestor($selectors, $current, $tree, $scope, 1, ++$pos) if $c eq
'>';
-
- # "~" (preceding siblings)
- return _sibling($selectors, $current, $tree, $scope, 0, ++$pos) if $c eq '~';
-
- # "+" (immediately preceding siblings)
- return _sibling($selectors, $current, $tree, $scope, 1, ++$pos) if $c eq '+';
-
- # " " (ancestor)
- return _ancestor($selectors, $current, $tree, $scope, 0, ++$pos);
-}
-
sub _compile {
my ($css, %ns) = (trim('' . shift), @_);
@@ -90,13 +70,19 @@
if ($css =~ /\G\s*,\s*/gc) { push @$group, [] }
# Combinator
- elsif ($css =~ /\G\s*([ >+~])\s*/gc) {
+ elsif ($css =~ /\G\s*([>+~])\s*/gc) {
push @$last, ['pc', 'scope'] unless @$last;
push @$selectors, $1;
}
+ # Descendant combinator
+ elsif ($css =~ /\G\s+/gc) {
+ push @$last, ['pc', 'scope'] unless @$last;
+ push @$selectors, ' ';
+ }
+
# Class or ID
- elsif ($css =~ /\G([.#])((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) {
+ elsif ($css =~ /\G([.#])((?:$ESCAPE_RE\s?|[^,.#:[\s>~+])+)/gco) {
my ($name, $op) = $1 eq '.' ? ('class', '~') : ('id', '');
push @$last, ['attr', _name($name), _value($op, $2)];
}
@@ -124,7 +110,7 @@
}
# Tag
- elsif ($css =~ /\G((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) {
+ elsif ($css =~ /\G((?:$ESCAPE_RE\s|\\[\s\S]|[^,.#:[\s>~+])+)/gco) {
my $alias = (my $name = $1) =~ s/^([^|]*)\|// && $1 ne '*' ? $1
: undef;
my $ns = length $alias ? $ns{$alias}
// return [['invalid']] : $alias;
push @$last, ['tag', $name eq '*' ? undef : _name($name),
_unescape($ns)];
@@ -154,6 +140,42 @@
return [$1 eq '-' ? -1 : !length $1 ? 1 : $1, join('', split(' ', $2 // 0))];
}
+sub _evaluate {
+ my ($group, $tree, $scope, $pool) = @_;
+
+ my (@results, %seen);
+ push @results, grep { !$seen{$_}++ } _evaluate_one($_, $tree, $scope, $pool)
for @$group;
+
+ return \@results;
+}
+
+sub _evaluate_one {
+ my ($selector, $tree, $scope, $pool) = @_;
+
+ # Match the leftmost compound, then propagate forward through combinators
+ my @parts = @$selector;
+ my $compound = shift @parts;
+ return () unless ref $compound;
+ my @candidates = grep { _selector($compound, $_, $tree, $scope) } @$pool;
+
+ while (@parts) {
+ my $combinator = shift @parts;
+ my $next = shift @parts;
+ return () unless ref $next;
+
+ my (%seen, @new);
+ for my $node (@candidates) {
+ for my $cand (_step_forward($combinator, $node)) {
+ next if $seen{$cand}++;
+ push @new, $cand if _selector($next, $cand, $tree, $scope);
+ }
+ }
+ @candidates = @new;
+ }
+
+ return @candidates;
+}
+
sub _is_scoped {
my $selector = shift;
@@ -171,10 +193,38 @@
sub _match {
my ($group, $current, $tree, $scope) = @_;
- _combinator([reverse @$_], $current, $tree, $scope, 0) and return 1 for
@$group;
+ _match_one($_, $current, $tree, $scope) and return 1 for @$group;
return undef;
}
+sub _match_one {
+ my ($selector, $current, $tree, $scope) = @_;
+
+ # Match the rightmost compound, then propagate backward through combinators
+ my @parts = reverse @$selector;
+ my $compound = shift @parts;
+ return undef unless ref $compound && _selector($compound, $current, $tree,
$scope);
+
+ my @candidates = ($current);
+ while (@parts) {
+ my $combinator = shift @parts;
+ my $next = shift @parts;
+ return undef unless ref $next;
+
+ my (%seen, @new);
+ for my $node (@candidates) {
+ for my $cand (_step_back($combinator, $node, $scope)) {
+ next if $seen{$cand}++;
+ push @new, $cand if _selector($next, $cand, $tree, $scope);
+ }
+ }
+ return undef unless @new;
+ @candidates = @new;
+ }
+
+ return 1;
+}
+
sub _name {qr/(?:^|:)\Q@{[_unescape(shift)]}\E$/}
sub _namespace {
@@ -267,14 +317,15 @@
my $tree = $scope;
($group, $tree) = (_absolutize($group), _root($scope)) if grep {
_is_scoped($_) } @$group;
- my @results;
- my @queue = @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree];
- while (my $current = shift @queue) {
- next unless $current->[0] eq 'tag';
+ # Pool includes $tree so ":scope" can match it, but results exclude it
+ my $tags = _all_tags($tree);
+ my %match = map { $_ => 1 } @{_evaluate($group, $tree, $scope, [$tree,
@$tags])};
- unshift @queue, @$current[4 .. $#$current];
- next unless _match($group, $current, $tree, $scope);
- $one ? return $current : push @results, $current;
+ my @results;
+ for my $node (@$tags) {
+ next unless $match{$node};
+ return $node if $one;
+ push @results, $node;
}
return $one ? undef : \@results;
@@ -307,31 +358,54 @@
return 1;
}
-sub _sibling {
- my ($selectors, $current, $tree, $scope, $immediate, $pos) = @_;
-
- my $found;
- for my $sibling (@{_siblings($current)}) {
- return $found if $sibling eq $current;
+sub _siblings {
+ my ($current, $type) = @_;
+ my $parent = $current->[3];
+ my @siblings = grep { $_->[0] eq 'tag' } @$parent[($parent->[0] eq 'root' ?
1 : 4) .. $#$parent];
+ @siblings = grep { $type eq $_->[1] } @siblings if defined $type;
+ return \@siblings;
+}
- # "+" (immediately preceding sibling)
- if ($immediate) { $found = _combinator($selectors, $sibling, $tree,
$scope, $pos) }
+sub _step_back {
+ my ($combinator, $node, $scope) = @_;
- # "~" (preceding sibling)
- else { return 1 if _combinator($selectors, $sibling, $tree, $scope, $pos) }
+ # " " (ancestors) and ">" (parent only)
+ if ($combinator eq ' ' || $combinator eq '>') {
+ my @ancestors;
+ while ($node ne $scope && $node->[0] ne 'root' && ($node = $node->[3])) {
+ push @ancestors, $node;
+ last if $combinator eq '>' || $node eq $scope;
+ }
+ return @ancestors;
}
- return undef;
+ # "~" (preceding siblings) and "+" (immediately preceding)
+ return () if $node->[0] eq 'root';
+ my @prev;
+ for my $sib (@{_siblings($node)}) {
+ last if $sib eq $node;
+ push @prev, $sib;
+ }
+ return $combinator eq '+' ? ($prev[-1] || ()) : @prev;
}
-sub _siblings {
- my ($current, $type) = @_;
+sub _step_forward {
+ my ($combinator, $node) = @_;
- my $parent = $current->[3];
- my @siblings = grep { $_->[0] eq 'tag' } @$parent[($parent->[0] eq 'root' ?
1 : 4) .. $#$parent];
- @siblings = grep { $type eq $_->[1] } @siblings if defined $type;
+ # " " (descendants)
+ return @{_all_tags($node)} if $combinator eq ' ';
- return \@siblings;
+ # ">" (children only)
+ return grep { $_->[0] eq 'tag' } @$node[($node->[0] eq 'root' ? 1 : 4) ..
$#$node] if $combinator eq '>';
+
+ # "~" (following siblings) and "+" (immediately following)
+ return () if $node->[0] eq 'root';
+ my (@next, $found);
+ for my $sib (@{_siblings($node)}) {
+ push @next, $sib if $found;
+ $found = 1 if $sib eq $node;
+ }
+ return $combinator eq '+' ? ($next[0] || ()) : @next;
}
sub _unescape {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojo/DOM/HTML.pm
new/Mojolicious-9.45/lib/Mojo/DOM/HTML.pm
--- old/Mojolicious-9.42/lib/Mojo/DOM/HTML.pm 2023-03-08 19:43:29.000000000
+0100
+++ new/Mojolicious-9.45/lib/Mojo/DOM/HTML.pm 2026-05-05 17:48:04.000000000
+0200
@@ -29,14 +29,14 @@
(?:\s+\[.+?\])?
# Int Subset
\s*)
|
- --(.*?)--\s*
# Comment
+ --(.*?)(?:--!?|(?<=<!--)-?(?=>))
# Comment
|
\[CDATA\[(.*?)\]\]
# CDATA
)
|
\?(.*?)\?
# Processing Instruction
|
- \s*((?:\/\s*)?[^<>\s\/0-9.\-][^<>\s\/]*\s*(?:(?:$ATTR_RE){0,32766})*+)
# Tag
+ ((?:\/\s*)?[^<>\s\/0-9.\-][^<>\s\/]*\s*(?:(?:$ATTR_RE){0,32766})*+)
# Tag
)>
|
(<)
# Runaway "<"
@@ -144,6 +144,14 @@
# Raw text elements
next if $xml || !$RAW{$start} && !$RCDATA{$start};
+
+ if ($start eq 'script') {
+ my ($script, $found_end) = _script_content(\$html);
+ _node($current, 'raw', $script) if length $script;
+ _end($start, 0, \$current) if $found_end;
+ next;
+ }
+
next unless $html =~ m!\G(.*?)</\Q$start\E(?:\s+|\s*>)!gcsi;
_node($current, 'raw', $RCDATA{$start} ? html_unescape $1 : $1);
_end($start, 0, \$current);
@@ -257,6 +265,38 @@
return '';
}
+sub _script_content {
+ my $html = shift;
+ my $start = pos $$html;
+
+ my $state = 0;
+
+ while (1) {
+ if ($state == 0) { $$html =~ /\G[^<]*/gcs }
+ else { $$html =~ /\G[^<\-]*/gcs }
+
+ my $p = pos $$html;
+ return (substr($$html, $start), 0) if $p >= length $$html;
+
+ if ($state == 0) {
+ if ($$html =~ m!\G</script(?:\s+|\s*>)!gcsi) { return (substr($$html,
$start, $p - $start), 1) }
+ elsif ($$html =~ /\G<!--/gcs) { $state = 1 }
+ else { pos($$html) = $p + 1 }
+ }
+ elsif ($state == 1) {
+ if ($$html =~ m!\G</script(?:\s+|\s*>)!gcsi) { return (substr($$html,
$start, $p - $start), 1) }
+ elsif ($$html =~ /\G-->/gcs) { $state = 0 }
+ elsif ($$html =~ m!\G<script(?=[\s/>])!gcsi) { $state = 2 }
+ else { pos($$html) = $p + 1 }
+ }
+ else {
+ if ($$html =~ m!\G</script(?:\s+|\s*>)!gcsi) { $state = 1 }
+ elsif ($$html =~ /\G-->/gcs) { $state = 0 }
+ else { pos($$html) = $p + 1 }
+ }
+ }
+}
+
sub _start {
my ($start, $attrs, $xml, $current) = @_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojo/File.pm
new/Mojolicious-9.45/lib/Mojo/File.pm
--- old/Mojolicious-9.42/lib/Mojo/File.pm 2025-10-01 16:30:21.000000000
+0200
+++ new/Mojolicious-9.45/lib/Mojo/File.pm 2026-05-04 17:29:38.000000000
+0200
@@ -7,7 +7,6 @@
use Exporter qw(import);
use File::Basename ();
use File::Copy qw(copy move);
-use File::Find qw(find);
use File::Path ();
use File::Spec::Functions qw(abs2rel canonpath catfile file_name_is_absolute
rel2abs splitdir);
use File::stat ();
@@ -65,25 +64,24 @@
sub list_tree {
my ($self, $options) = (shift, shift // {});
+ return Mojo::Collection->new unless -d $$self;
- # This may break in the future, but is worth it for performance
- local $File::Find::skip_pattern = qr/^\./ unless $options->{hidden};
-
- # The File::Find documentation lies, this is needed for CIFS
- local $File::Find::dont_use_nlink = 1 if $options->{dont_use_nlink};
-
- my %all;
- my $wanted = sub {
- if ($options->{max_depth}) {
- (my $rel = $File::Find::name) =~ s!^\Q$$self\E/?!!;
- $File::Find::prune = 1 if splitdir($rel) >= $options->{max_depth};
+ my (@results, $walk);
+ $walk = sub {
+ my ($path, $depth) = @_;
+ opendir my $dh, $path or return;
+ my @names = sort grep { $_ ne '.' && $_ ne '..' } readdir $dh;
+ @names = grep { !/^\./ } @names unless $options->{hidden};
+ for my $name (@names) {
+ my $child = $self->new($path, $name);
+ my $is_dir = -d $$child;
+ push @results, $child if $options->{dir} || !$is_dir;
+ $walk->($$child, $depth + 1) if $is_dir && (!$options->{max_depth} ||
$depth + 1 < $options->{max_depth});
}
- $all{$File::Find::name}++ if $options->{dir} || !-d $File::Find::name;
};
- find {wanted => $wanted, no_chdir => 1}, $$self if -d $$self;
- delete $all{$$self};
+ $walk->($$self, 0);
- return Mojo::Collection->new(map { $self->new(canonpath $_) } sort keys
%all);
+ return Mojo::Collection->new(@results);
}
sub lstat { File::stat::lstat(${shift()}) }
@@ -395,12 +393,6 @@
Include directories.
-=item dont_use_nlink
-
- dont_use_nlink => 1
-
-Force L<File::Find> to always stat directories.
-
=item hidden
hidden => 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojo/Transaction/WebSocket.pm
new/Mojolicious-9.45/lib/Mojo/Transaction/WebSocket.pm
--- old/Mojolicious-9.42/lib/Mojo/Transaction/WebSocket.pm 2023-03-08
19:43:30.000000000 +0100
+++ new/Mojolicious-9.45/lib/Mojo/Transaction/WebSocket.pm 2026-05-05
17:48:04.000000000 +0200
@@ -29,6 +29,7 @@
||= Compress::Raw::Zlib::Deflate->new(AppendOutput => 1, MemLevel => 8,
WindowBits => -15);
$deflate->deflate($frame->[5], my $out);
$deflate->flush($out, Z_SYNC_FLUSH);
+ $deflate->deflateReset;
@$frame[1, 5] = (1, substr($out, 0, length($out) - 4));
return $frame;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojo/Util.pm
new/Mojolicious-9.45/lib/Mojo/Util.pm
--- old/Mojolicious-9.42/lib/Mojo/Util.pm 2025-10-01 16:38:12.000000000
+0200
+++ new/Mojolicious-9.45/lib/Mojo/Util.pm 2026-02-12 12:07:38.000000000
+0100
@@ -303,7 +303,7 @@
my $h = my $basic = length $output;
$output .= "\x2d" if $basic > 0;
- for my $m (sort grep { $_ >= PC_INITIAL_N } @input) {
+ for my $m (sort { $a <=> $b } grep { $_ >= PC_INITIAL_N } @input) {
next if $m < $n;
$delta += ($m - $n) * ($h + 1);
$n = $m;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/lib/Mojolicious.pm
new/Mojolicious-9.45/lib/Mojolicious.pm
--- old/Mojolicious-9.42/lib/Mojolicious.pm 2025-10-01 16:41:31.000000000
+0200
+++ new/Mojolicious-9.45/lib/Mojolicious.pm 2026-05-05 20:09:38.000000000
+0200
@@ -57,7 +57,7 @@
has validator => sub { Mojolicious::Validator->new };
our $CODENAME = 'Waffle';
-our $VERSION = '9.42';
+our $VERSION = '9.45';
sub BUILD_DYNAMIC {
my ($class, $method, $dyn_methods) = @_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/dom.t
new/Mojolicious-9.45/t/mojo/dom.t
--- old/Mojolicious-9.42/t/mojo/dom.t 2023-03-08 19:43:33.000000000 +0100
+++ new/Mojolicious-9.45/t/mojo/dom.t 2026-05-05 18:10:15.000000000 +0200
@@ -327,6 +327,20 @@
is $dom->at('script')->text, "alert('lalala');", 'right script content';
};
+subtest 'Script tag with HTML comment containing nested script tags' => sub {
+ my $dom = Mojo::DOM->new(q{<script> console.log("<!--"); </script>});
+ is $dom->at('script')->text, ' console.log("<!--"); ', 'right script
content';
+
+ $dom = Mojo::DOM->new(q{<script> console.log("<!-- <script> -->");
</script>});
+ is $dom->at('script')->text, ' console.log("<!-- <script> -->"); ', 'right
script content';
+
+ $dom = Mojo::DOM->new(q{<script> console.log("<!-- <script> </script>");
</script>});
+ is $dom->at('script')->text, ' console.log("<!-- <script> </script>"); ',
'right script content';
+
+ $dom = Mojo::DOM->new(q{<script> console.log("<!-- <script> </script> -->");
</script>});
+ is $dom->at('script')->text, ' console.log("<!-- <script> </script> -->");
', 'right script content';
+};
+
subtest 'HTML5 (unquoted values)' => sub {
my $dom = Mojo::DOM->new('<div id = test foo ="bar" class=tset bar=/baz/
value baz=//>works</div>');
is $dom->at('#test')->text, 'works', 'right text';
@@ -1274,6 +1288,25 @@
is $dom->at('#♥ ~ *:nth-last-child(2)')->text, 'F', 'right text';
};
+subtest 'Whitespace as descendant combinator' => sub {
+ my $dom = Mojo::DOM->new("<ul> <li>Ax1</li> </ul>");
+ is $dom->at("ul li")->text, 'Ax1', 'space combinator';
+ is $dom->at("ul\tli")->text, 'Ax1', 'tab combinator';
+ is $dom->at("ul \tli")->text, 'Ax1', 'space + tab combinator';
+ is $dom->at("ul\t li")->text, 'Ax1', 'tab + space combinator';
+ is $dom->at("ul\t\tli")->text, 'Ax1', 'multiple tab combinator';
+ is $dom->at("ul\nli")->text, 'Ax1', 'newline combinator';
+ is $dom->at("ul\rli")->text, 'Ax1', 'carriage return combinator';
+ is $dom->at("ul\fli")->text, 'Ax1', 'form feed combinator';
+
+ my $multi = Mojo::DOM->new("<ul><li>A</li><li>B</li></ul>");
+ is_deeply $multi->find("ul\tli")->map('text')->to_array, ['A', 'B'], 'find
with tab combinator';
+
+ my $child = Mojo::DOM->new("<ul><li>Ax1</li></ul>");
+ is $child->at("ul\t>\tli")->text, 'Ax1', 'tabs around child combinator';
+ is $child->at("ul\n>\nli")->text, 'Ax1', 'newlines around child combinator';
+};
+
subtest 'Scoped selectors' => sub {
my $dom = Mojo::DOM->new(<<EOF);
<p>Zero</p>
@@ -1775,14 +1808,12 @@
alert('<123>');
}
</script>
- < sCriPt two="23" >if (b > c) { alert('&<ohoh>') }</scRiPt >
<body>Foo!</body>
EOF
is $dom->find('html > body')->[0]->text, 'Foo!',
'right text';
is $dom->find('html > head > style')->[0]->text, "#style { foo:
style('<test>'); }", 'right text';
is $dom->find('html > head > script')->[0]->text, "\n if (a < b) {\n
alert('<123>');\n }\n ",
'right text';
- is $dom->find('html > head > script')->[1]->text, "if (b > c) {
alert('&<ohoh>') }", 'right text';
};
subtest 'More real world JavaScript' => sub {
@@ -1819,12 +1850,11 @@
<body>Bar</body>
</html>
EOF
- is $dom->at('title')->text, 'Foo',
'right text';
- is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js',
'right attribute';
- is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js',
'right attribute';
- is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js',
'right attribute';
- is $dom->find('html > head > script')->[2]->text, "\n ", 'no
text';
- is $dom->at('html > body')->text, 'Bar',
'right text';
+ is $dom->at('title')->text, 'Foo',
'right text';
+ is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js',
'right attribute';
+ is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js',
'right attribute';
+ is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js',
'right attribute';
+ is $dom->find('html > head > script')->[2]->text, "\n </head>\n
<body>Bar</body>\n</html>\n", 'raw content to EOF';
};
subtest 'Inline DTD' => sub {
@@ -2567,10 +2597,9 @@
subtest 'Extra whitespace' => sub {
my $dom = Mojo::DOM->new('< span>a< /span><b >b</b><span >c</ span>');
- is $dom->at('span')->text, 'a',
'right text';
- is $dom->at('span + b')->text, 'b',
'right text';
- is $dom->at('b + span')->text, 'c',
'right text';
- is "$dom", '<span>a</span><b>b</b><span>c</span>',
'right result';
+ is $dom->at('b')->text, 'b',
'right text';
+ is $dom->at('b + span')->text, 'c',
'right text';
+ is "$dom", '< span>a<
/span><b>b</b><span>c</span>', 'right result';
};
subtest 'Selectors with leading and trailing whitespace' => sub {
@@ -2592,8 +2621,8 @@
subtest 'Not self-closing' => sub {
my $dom = Mojo::DOM->new('<div />< div ><pre />test</div >123');
- is $dom->at('div > div > pre')->text, 'test',
'right text';
- is "$dom",
'<div><div><pre>test</pre></div>123</div>', 'right result';
+ is $dom->at('div > pre')->text, 'test',
'right text';
+ is "$dom", '<div>< div
><pre>test</pre></div>123', 'right result';
$dom = Mojo::DOM->new('<p /><svg><circle /><circle /></svg>');
is $dom->find('p > svg > circle')->size, 2,
'two circles';
is "$dom",
'<p><svg><circle></circle><circle></circle></svg></p>', 'right result';
@@ -2640,13 +2669,26 @@
my $dom = Mojo::DOM->new(<<EOF);
<!-- HTML5 -->
<!-- bad idea -- HTML5 -->
-<!-- HTML4 -- >
-<!-- bad idea -- HTML4 -- >
EOF
is $dom->tree->[1][1], ' HTML5 ', 'right comment';
is $dom->tree->[3][1], ' bad idea -- HTML5 ', 'right comment';
- is $dom->tree->[5][1], ' HTML4 ', 'right comment';
- is $dom->tree->[7][1], ' bad idea -- HTML4 ', 'right comment';
+};
+
+subtest 'Comment is not terminated by "-- >"' => sub {
+ my $dom = Mojo::DOM->new('<!-- a > -- > b <blink>c</blink> -->');
+ is $dom->at('blink'), undef, 'blink element is
inside comment';
+ is $dom->tree->[1][1], ' a > -- > b <blink>c</blink> ', 'right comment';
+};
+
+subtest 'Abrupt and bang-terminated comments' => sub {
+ my $dom = Mojo::DOM->new("<!DOCTYPE html>\n<!--> <p>OK</p> <!-- -->");
+ is $dom->at('p')->text, 'OK', 'abrupt empty comment closure';
+
+ $dom = Mojo::DOM->new("<!DOCTYPE html>\n<!---> <p>OK</p> <!-- -->");
+ is $dom->at('p')->text, 'OK', 'dash-terminated empty comment';
+
+ $dom = Mojo::DOM->new("<!DOCTYPE html>\n<!-- --!> <p>OK</p> <!-- -->");
+ is $dom->at('p')->text, 'OK', 'bang-terminated comment';
};
subtest 'Huge number of attributes' => sub {
@@ -3023,6 +3065,19 @@
is $dom->at('.test')->text, 'works', 'right text';
};
+subtest '"<" followed by space is not a tag opener' => sub {
+ my $dom = Mojo::DOM->new('if a < script then="<!--"> </script>
<p>FAIL</p>-->');
+ is_deeply $dom->find('script, p')->map('to_string')->to_array, [], 'fragment
contains no tags, just a comment';
+
+ $dom = Mojo::DOM->new('a < b');
+ is "$dom", 'a < b', 'right result';
+ is_deeply $dom->find('*')->to_array, [], 'no elements';
+
+ $dom = Mojo::DOM->new('a < b <p>ok</p>');
+ is_deeply $dom->find('*')->map('tag')->to_array, ['p'], 'only one element';
+ is $dom->at('p')->text, 'ok', 'right text';
+};
+
subtest 'XML name characters' => sub {
my $dom = Mojo::DOM->new->xml(1)->parse('<Foo><1a>foo</1a></Foo>');
is $dom->at('Foo')->text, '<1a>foo</1a>', 'right
text';
@@ -3092,6 +3147,20 @@
like $dom->at('div')->text, qr/^\s+$/s, 'right
text';
};
+subtest 'Descendant combinator chain with non-matching selector' => sub {
+ my $dom = Mojo::DOM->new('<div> ' x 100 . '</div> ' x 100);
+ is_deeply $dom->find('#gobbledygook * * * *')->map(sub { $_->to_string
})->to_array, [],
+ 'non-existent elements have no descendants';
+ is $dom->at('#gobbledygook * * * *'), undef, 'no match';
+
+ is_deeply $dom->find('* * * * #gobbledygook')->map(sub { $_->to_string
})->to_array, [],
+ 'non-existent elements have no ancestors';
+ is $dom->at('* * * * #gobbledygook'), undef, 'no match';
+
+ my $exists = Mojo::DOM->new('<div
id="x"><a><b><c><d>ok</d></c></b></a></div>');
+ is $exists->at('#x * * * *')->text, 'ok', 'matching id still resolves
descendants';
+};
+
subtest 'Unknown CSS selector' => sub {
my $dom =
Mojo::DOM->new('<html><head></head><body><div><div>x</div></div></body></html>');
eval { $dom->at('div[') };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/ioloop_tls.t
new/Mojolicious-9.45/t/mojo/ioloop_tls.t
--- old/Mojolicious-9.42/t/mojo/ioloop_tls.t 2023-03-08 19:43:33.000000000
+0100
+++ new/Mojolicious-9.45/t/mojo/ioloop_tls.t 2026-02-12 12:07:38.000000000
+0100
@@ -335,7 +335,7 @@
is $client, 'connected', 'right result';
ok !$client_err, 'no error';
my $expect = $version eq 'TLSv1_3' ? 'TLS_AES_256_GCM_SHA384' : 'AES256-SHA';
-is $cipher, $expect, "$expect has been negotiatied";
+is $cipher, $expect, "$expect has been negotiated";
# Ignore missing client certificate
($server, $client, $client_err) = ();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/tls.t
new/Mojolicious-9.45/t/mojo/tls.t
--- old/Mojolicious-9.42/t/mojo/tls.t 2023-03-08 19:43:35.000000000 +0100
+++ new/Mojolicious-9.45/t/mojo/tls.t 2026-02-12 12:07:38.000000000 +0100
@@ -58,8 +58,8 @@
is ref $client_result, 'IO::Socket::SSL', 'right class';
is ref $server_result, 'IO::Socket::SSL', 'right class';
my $expect = $server_result->get_sslversion eq 'TLSv1_3' ?
'TLS_AES_256_GCM_SHA384' : 'AES256-SHA';
- is $client_result->get_cipher, $expect, "$expect has been negotiatied";
- is $server_result->get_cipher, $expect, "$expect has been negotiatied";
+ is $client_result->get_cipher, $expect, "$expect has been negotiated";
+ is $server_result->get_cipher, $expect, "$expect has been negotiated";
};
done_testing;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/user_agent_tls.t
new/Mojolicious-9.45/t/mojo/user_agent_tls.t
--- old/Mojolicious-9.42/t/mojo/user_agent_tls.t 2023-03-08
19:43:36.000000000 +0100
+++ new/Mojolicious-9.45/t/mojo/user_agent_tls.t 2026-02-12
12:07:38.000000000 +0100
@@ -94,8 +94,8 @@
$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
$tx = $ua->get("https://127.0.0.1:$port");
ok !$tx->error, 'no error';
- is $ua->ioloop->stream($tx->connection)->handle->get_cipher,
'AES256-SHA', 'AES256-SHA has been negotiatied';
- is $ua->ioloop->stream($tx->connection)->handle->get_sslversion, 'TLSv1',
'TLSv1 has been negotiatied';
+ is $ua->ioloop->stream($tx->connection)->handle->get_cipher,
'AES256-SHA', 'AES256-SHA has been negotiated';
+ is $ua->ioloop->stream($tx->connection)->handle->get_sslversion, 'TLSv1',
'TLSv1 has been negotiated';
};
subtest 'Client side TLS options' => sub {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/util.t
new/Mojolicious-9.45/t/mojo/util.t
--- old/Mojolicious-9.42/t/mojo/util.t 2024-11-22 12:13:40.000000000 +0100
+++ new/Mojolicious-9.45/t/mojo/util.t 2026-02-12 12:07:38.000000000 +0100
@@ -289,6 +289,11 @@
is punycode_decode('bcher-kva'), 'bücher', 'right punycode decoded result';
};
+subtest 'punycode_decode/punycode_encode roundtrip' => sub {
+ is punycode_decode(punycode_encode('☃⭐️ ')), '☃⭐️ ', 'right punycode
roundtrip result';
+ is punycode_decode(punycode_encode('🐳')), '🐳', 'right punycode
roundtrip result';
+};
+
subtest 'RFC 3492' => sub {
my @tests = (
'(A) Arabic (Egyptian):',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-9.42/t/mojo/websocket_frames.t
new/Mojolicious-9.45/t/mojo/websocket_frames.t
--- old/Mojolicious-9.42/t/mojo/websocket_frames.t 2025-07-03
14:03:09.000000000 +0200
+++ new/Mojolicious-9.45/t/mojo/websocket_frames.t 2026-05-05
20:29:16.000000000 +0200
@@ -1,6 +1,7 @@
use Mojo::Base -strict;
use Test::More;
+use Compress::Raw::Zlib;
use Mojo::Transaction::WebSocket;
use Mojo::WebSocket qw(WS_BINARY WS_CLOSE WS_CONTINUATION WS_PING WS_PONG
WS_TEXT), qw(build_frame parse_frame);
@@ -268,9 +269,6 @@
is $frame->[3], 0, 'rsv3 flag is not set';
is $frame->[4], WS_BINARY, 'binary frame';
ok $frame->[5], 'has payload';
- my $payload = $compressed->build_message({binary => 'just works'})->[5];
- isnt $frame->[5], $payload, 'different payload';
- ok length $frame->[5] > length $payload, 'payload is smaller';
my $uncompressed = Mojo::Transaction::WebSocket->new;
my $frame2 = $uncompressed->build_message({binary => 'just works'});
is $frame2->[0], 1, 'fin flag is set';
@@ -297,4 +295,34 @@
is $text, 'just works', 'decoded correctly';
};
+subtest 'Compressed messages are independently decodable per message' => sub {
+ my $tx = Mojo::Transaction::WebSocket->new({compressed => 1});
+ my @payloads = map {qq[{"action":"heartbeat","client_ts":$_}]} 1 .. 3;
+
+ for my $i (0 .. $#payloads) {
+ my $frame = $tx->build_message({text => $payloads[$i]});
+
+ my $inflate = Compress::Raw::Zlib::Inflate->new(WindowBits => -15,
LimitOutput => 1, Bufsize => 65536);
+ my $data = $frame->[5] . "\x00\x00\xff\xff";
+ my $status = $inflate->inflate($data, my $out);
+
+ cmp_ok $status, '==', Z_OK, "message @{[$i + 1]} inflates without error";
+ is length $data, 0, "message @{[$i + 1]} fully consumed";
+ is $out, $payloads[$i], "message @{[$i + 1]} round-trips";
+ }
+};
+
+subtest 'Compressed messages round-trip between two transactions' => sub {
+ my $sender = Mojo::Transaction::WebSocket->new({compressed => 1});
+ my $peer = Mojo::Transaction::WebSocket->new({compressed => 1});
+
+ my @got;
+ $peer->on(message => sub { push @got, pop });
+
+ my @payloads = map {qq[{"action":"heartbeat","client_ts":$_}]} 1 .. 3;
+ $peer->parse_message($sender->build_message({text => $_})) for @payloads;
+
+ is_deeply \@got, \@payloads, 'all messages decoded';
+};
+
done_testing();
++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.w1XIkd/_old 2026-05-13 17:23:20.781266129 +0200
+++ /var/tmp/diff_new_pack.w1XIkd/_new 2026-05-13 17:23:20.789266461 +0200
@@ -1,6 +1,6 @@
-mtime: 1760040066
-commit: 8c438d306d27aa758414e2190e55169dc3152bb54022f13e5c20ff2add1fd14f
-url: https://src.opensuse.org/perl/perl-Mojolicious.git
-revision: 8c438d306d27aa758414e2190e55169dc3152bb54022f13e5c20ff2add1fd14f
+mtime: 1778053630
+commit: 9cad6b445729b6947032c8fc69fe825d33858ae62150882fb10b468f6cc1e9ad
+url: https://src.opensuse.org/perl/perl-Mojolicious
+revision: 9cad6b445729b6947032c8fc69fe825d33858ae62150882fb10b468f6cc1e9ad
projectscmsync: https://src.opensuse.org/perl/_ObsPrj
++++++ build.specials.obscpio ++++++
++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore 1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore 2026-05-06 09:47:10.000000000 +0200
@@ -0,0 +1 @@
+.osc