Skip to content

Instantly share code, notes, and snippets.

@Xliff
Last active January 28, 2019 20:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Xliff/afed9c58e738dec8e144d76645520c8f to your computer and use it in GitHub Desktop.
Save Xliff/afed9c58e738dec8e144d76645520c8f to your computer and use it in GitHub Desktop.
Webkit on Perl6 (Is there a letter earlier than ALPHA?)

Yes, Petunia! You CAN code a browser in Perl6.

UPDATED: You can also use JavaScript, and get the results back!

@Xliff
Copy link
Author

Xliff commented Jan 24, 2019

Here is the full code if you can see it from the screenshot.

use v6.c;

use GTK::Application;
use WebkitGTK::WebView;

my $a = GTK::Application.new(
  title => 'org.genex.p6-browser', width => 640, height => 480
);

$a.activate.tap({
  my $wv = WebkitGTK::WebView.new;
  $wv.load_uri("http://slashdot.org/");
  $a.window.add($wv);
  $a.window.title = 'Perl6 Browser';
  $a.window.show_all;
});

$a.run;

@Xliff
Copy link
Author

Xliff commented Jan 25, 2019

Second script is a LOT longer:

use v6.c;

use GTK::Compat::Types;
use WebkitGTK::Raw::Types;

use GTK::Application;
use WebkitGTK::WebView;
use WebkitGTK::JavaScript::Context;;

my $a = GTK::Application.new(
  title => 'org.genex.p6-browser', width => 640, height => 480
);

sub print_story_title($o, $r, $ud, $wv) {
  my $js_result = $wv.run_javascript_finish($r);
  without $js_result {
    say "Error running javascript: { $ERROR.message }" with $ERROR;
    return;
  }

  my $val = $js_result.get_js_value;
  if $val.defined && $val.is_string {
    my $s = $val.to_string;
    my $e = WebkitGTK::JavaScript::Context.new(
      $val.get_context
    ).get_exception;
    with $e {
      say "Error running javascript: { $e.get_message }";
    } else {
      say "First story title is: { $s }";
    }
  } else {
    say 'Error running javascript: unexpected return value';
  }
}

sub get_story_title($wv) {
  $wv.run_javascript(
    '$("header span.story-title a")[0].innerText;',
    -> *@a {
      CATCH { default { .message.say } }
      print_story_title(|@a, $wv)
    },
  );
}

$a.activate.tap({
  my $wv = WebkitGTK::WebView.new;

  # A lot more work when integrating with JavaScript.
  $wv.load-changed.tap(-> *@a {
    get_story_title($wv) if @a[1] == WEBKIT_LOAD_FINISHED;
  });

  $wv.load_uri("http://slashdot.org/");
  $a.window.add($wv);
  $a.window.title = 'Perl6 Browser';
  $a.window.show_all;
});

$a.run;

@Xliff
Copy link
Author

Xliff commented Jan 27, 2019

Here is the latest code for the simple UI version:

use v6.c;

use GTK::Application;
use GTK::Builder;
use WebkitGTK::Raw::Types;
use WebkitGTK::HitTestResult;
use WebkitGTK::WebView;

my $a = GTK::Application.new( title => 'org.genex.p6-browser' );
my $b = GTK::Builder.new( pod => $=pod );
my $wv = WebkitGTK::WebView.new;

my (%cids, $tc, $last_s);
sub update_statusbar($s, $wv, $htr, $m, $ud) {
  my $hit = WebkitGTK::HitTestResult.new($htr);

  if $hit.context_is_link {
    with $tc { $tc.cancel; $tc = Nil; }
    my $k = $hit.get_link_uri;
    $k = $k.substr(0, 63) ~ '';
    %cids{$k} //= $last_s = $s.get_context_id($k);
    $s.push($last_s, $k);
  } else {
    $s.remove_all($last_s) with $last_s;
    $tc = $*SCHEDULER.cue({
      $s.remove_all($_) for %cids.values;
      %cids = (); $tc = Nil;
    }, in => 0.2);
  }
}

sub load {
  $wv.load_uri( $b<URIEntry>.text );
}

sub stop {
  $wv.stop_loading if $wv.is_loading;
}

sub fwd {
  $wv.go_forward if $wv.can_go_forward;
  $b<Fwd>.sensitive = $wv.can_go_forward;
  $b<Back>.sensitive = $wv.can_go_back;
};

sub back {
  $wv.go_back if $wv.can_go_back;
  $b<Back>.sensitive = $wv.can_go_back;
  $b<Fwd>.sensitive = $wv.can_go_forward;
}

sub handle_loading($s, *@a) {
  given @a[1] {
    when WEBKIT_LOAD_STARTED    {
      my $uri = $wv.get_uri;
      my $text = "Loading { $uri }……";
      $last_s = %cids{$uri} = $s.get_context_id($text);
      $s.push($last_s, $text);
    }
    when WEBKIT_LOAD_REDIRECTED { }
    when WEBKIT_LOAD_COMMITTED  { }
    when WEBKIT_LOAD_FINISHED   {
      $s.pop($last_s);
      $last_s = Nil;
      %cids{$_}:delete for %cids.keys;
    }
  }
}

$a.activate.tap({
  my $s  = GTK::Statusbar.new;

  $b<Back>.sensitive = $b<Fwd>.sensitive = False;
  $s.margins = 0;
  $wv.mouse-target-changed.tap(-> *@a { update_statusbar($s, |@a) });
  $wv.load-changed.tap(        -> *@a {   handle_loading($s, |@a) });

  $b<MainWin>.destroy-signal.tap({ $a.exit });

  $b<URIEntry>.activate.tap({ load() });
  $b<Back>.clicked.tap(     { back() });
  $b<Fwd>.clicked.tap(      { fwd()  });
  $b<Stop>.clicked.tap(     { stop() });

  $b<MainWin>.set_size_request(640, 480);
  $b<MainWinBox>.pack_start($wv, True, True);
  $b<MainWinBox>.pack_end($s, False);
  $b<MainWin>.title = 'Perl6 Browser';
  $b<MainWin>.show_all;
});

$a.run;

=begin css
statusbar { font-size: 10px; }
=end css

=begin ui
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="MainWin">
    <property name="can_focus">False</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox" id="MainWinBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="margin_bottom">2</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="orientation">vertical</property>
                <child>
                  <object class="GtkEntry" id="URIEntry">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButtonBox">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="layout_style">start</property>
                <child>
                  <object class="GtkButton" id="Stop">
                    <property name="label">gtk-stop</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_stock">True</property>
                    <property name="image_position">top</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="Back">
                    <property name="label">gtk-go-back</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_stock">True</property>
                    <property name="image_position">top</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="Fwd">
                    <property name="label">gtk-go-forward</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_stock">True</property>
                    <property name="image_position">top</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">2</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="Home">
                    <property name="label">gtk-home</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_stock">True</property>
                    <property name="image_position">top</property>
                  </object>
                  <packing>
                    <property name="expand">True</property>
                    <property name="fill">True</property>
                    <property name="position">3</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>
=end ui

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment