ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Mozilla-Plugin/Plugin.pm
Revision: 1.10
Committed: Mon Mar 5 12:05:56 2001 UTC (23 years, 4 months ago) by root
Branch: MAIN
Changes since 1.9: +133 -53 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Mozilla::Plugin - embed perl into mozilla/netscape.
4
5 =head1 SYNOPSIS
6
7 use Mozilla::Plugin;
8
9 =head1 DESCRIPTION
10
11 sorry...
12
13 =over 4
14
15 =cut
16
17 package Mozilla::Plugin;
18
19 use base Exporter;
20 use Storable;
21 #use XSLoader;
22
23 $VERSION = 0.01;
24 @EXPORT = qw();
25
26 #XSLoader::load __PACKAGE__, $VERSION;
27
28 my $interface; # Mozilla::Plugin::xxx (design weakness)
29
30 sub NP_VERSION_MAJOR (){ 0 }
31 sub NP_VERSION_MINOR (){ 9 }
32 sub NP_EMBED (){ 1 }
33 sub NP_FULL (){ 2 }
34 sub NP_NORMAL (){ 1 }
35 sub NP_SEEK (){ 2 }
36 sub NP_ASFILE (){ 3 }
37 sub NP_ASFILEONLY (){ 4 }
38 sub NP_MAXREADY (){ 2147483647 }
39 sub NPERR_NO_ERROR (){ 0 }
40 sub NPERR_GENERIC_ERROR (){ 1 }
41 sub NPERR_INVALID_INSTANCE_ERROR (){ 2 }
42 sub NPERR_INVALID_FUNCTABLE_ERROR (){ 3 }
43 sub NPERR_MODULE_LOAD_FAILED_ERROR (){ 4 }
44 sub NPERR_OUT_OF_MEMORY_ERROR (){ 5 }
45 sub NPERR_INVALID_PLUGIN_ERROR (){ 6 }
46 sub NPERR_INVALID_PLUGIN_DIR_ERROR (){ 7 }
47 sub NPERR_INCOMPATIBLE_VERSION_ERROR (){ 8 }
48 sub NPERR_INVALID_PARAM (){ 9 }
49 sub NPERR_INVALID_URL (){ 10 }
50 sub NPERR_FILE_NOT_FOUND (){ 11 }
51 sub NPERR_NO_DATA (){ 12 }
52 sub NPERR_STREAM_NOT_SEEKABLE (){ 13 }
53 sub NPVERS_HAS_STREAMOUTPUT (){ 8 }
54 sub NPVERS_HAS_NOTIFICATION (){ 9 }
55 sub NPVERS_HAS_LIVECONNECT (){ 9 }
56 sub NPVERS_WIN16_HAS_LIVECONNECT (){ 10 }
57
58 my $data;
59
60 sub BIAS() { -2147483647 } # ugliest hack ever seen
61
62 sub snd_cmd {
63 $data = pack "NN", 0, ord($_[-1]);
64 }
65
66 sub snd_ptr {
67 $data .= $_[-1];
68 }
69
70 sub snd_u32 {
71 $data .= pack "N", $_[-1];
72 }
73
74 sub snd_i32 {
75 $data .= pack "N", $_[-1] - BIAS;
76 }
77
78 sub snd_blk {
79 $data .= pack "NA*", length($_[-1]), $_[-1];
80 }
81
82 sub snd_snd {
83 substr ($data, 0, 4) = pack "N", length $data;
84 length ($data) == syswrite FD, $data;
85 }
86
87 sub snd_dyn {
88 length ($_[-1]) == syswrite FD, $_[-1];
89 }
90
91 sub rcv_cmd {
92 shift if ref $_[0];
93 my $cmd = shift;
94 my ($buf, $rcmd, $len);
95
96 do {
97 4 == sysread FD, $buf, 4 or exit;
98 $len = unpack "N", $buf;
99 $len -= 4;
100 $len == sysread FD, $buf, $len or die;
101
102 $data = substr $buf, 4;
103
104 $rcmd = substr $buf, 3, 1;
105 if ($rcmd =~ /^[a-z]$/) {
106 die "rcv_cmd: sync error, expected '$cmd' but got '$rcmd'\n" if $cmd && $rcmd ne $cmd;
107 return $rcmd;
108 } else {
109 &_handle_event($rcmd);
110 }
111 } while ($cmd);
112 }
113
114 sub rcv_ptr {
115 my $ptr = substr $data, 0, 8;
116 $data = substr $data, 8;
117 $ptr;
118 }
119
120 sub rcv_u32 {
121 my $u32 = unpack "N", $data;
122 $data = substr $data, 4;
123 $u32;
124 }
125
126 sub rcv_i32 {
127 my $i32 = BIAS + unpack "N", $data;
128 $data = substr $data, 4;
129 $i32;
130 }
131
132 sub rcv_blk {
133 my $len = unpack "N", $data;
134 my $str = substr $data, 4, $len;
135 $data = substr $data, 4 + $len;
136 $str;
137 }
138
139 sub rcv_dyn {
140 my $buf;
141 $_[-1] == sysread FD, $buf, $_[-1] or die "rcv_dyn: $!";
142 $buf;
143 }
144
145 sub _handle_event($) {
146 my $cmd = shift;
147
148 if ($cmd eq "I") {
149 rcv_u32 == 1 or die "protocol version mismatch\n";
150 ($IFACE, $OSNAME, $OSVERS) = (rcv_blk, rcv_blk, rcv_blk);
151
152 } elsif ($cmd eq "+") { # New
153 my ($objid, $type, $mode, $save, $argc)
154 = (rcv_ptr, rcv_blk, rcv_u32, rcv_blk, rcv_u32);
155 my %args;
156 while ($argc--) {
157 my ($argn, $argv) = (rcv_blk, rcv_blk);
158 $args{$argn} = $argv;
159 }
160
161 $_OBJ{$objid} = $interface->new(
162 instance => $objid,
163 type => $type,
164 mode => $mode,
165 save => $save,
166 args => \%args,
167 );
168
169 } elsif ($cmd eq "-") { # Destroy
170 my $objid = rcv_ptr;
171 my $save = (delete $_OBJ{$objid})->save;
172 snd_cmd "-";
173 snd_u32 length $save;
174 snd_snd and snd_dyn $save;
175
176 } elsif ($cmd eq "X") { # SetWindow
177 my $objid = rcv_ptr;
178 my %args = (
179 id => rcv_ptr,
180 x => rcv_i32,
181 y => rcv_i32,
182 w => rcv_i32,
183 h => rcv_i32,
184 );
185 if ($IFACE eq "UNIX") {
186 $args{id} = unpack "xxxxN", $args{id};
187 $args{ws_type} = rcv_i32;
188 $args{ws_depth} = rcv_u32;
189 }
190
191 snd_cmd 'X';
192 snd_u32 $_OBJ{$objid}->set_window(\%args);
193 snd_snd;
194
195 } elsif ($cmd eq "N") { # NewStream
196 my $obj = $_OBJ{+rcv_ptr};
197 my %args = (
198 instance => $obj,
199 mimetype => rcv_blk,
200 id => rcv_ptr,
201 url => rcv_blk,
202 end => rcv_u32,
203 lastmodified => rcv_u32,
204 notifydata => rcv_u32,
205 seekable => rcv_u32,
206 push_stream => 1,
207 );
208 my $str = $_OBJ{$args{id}} = new Mozilla::Stream::Receive \%args;
209
210 my ($stype, $err) = $obj->push_stream($str);
211
212 snd_cmd 'N';
213 snd_u32 $err;
214 snd_u32 $stype || NP_NORMAL;
215 snd_snd;
216
217 } elsif ($cmd eq "/") { # StreamAsFile
218 my $obj = $_OBJ{+rcv_ptr};
219 my $str = $_OBJ{+rcv_ptr};
220 my $path = rcv_blk;
221
222 $obj->stream_as_file($obj, $stream, $path);
223
224 } elsif ($cmd eq "R") { # WriteReady
225 my $obj = $_OBJ{+rcv_ptr};
226 my $str = $_OBJ{+rcv_ptr};
227
228 snd_cmd 'R';
229 snd_u32 $obj->write_ready($obj, $str);
230 snd_snd;
231
232 } elsif ($cmd eq "W") { # Write
233 my $obj = $_OBJ{+rcv_ptr};
234 my $str = $_OBJ{+rcv_ptr};
235 my $ofs = rcv_i32;
236 my $len = rcv_i32;
237 my $dta = rcv_dyn $len;
238
239 snd_cmd 'W';
240 snd_i32 $obj->write($str, $ofs, $len, $dta);
241 snd_snd;
242
243 } elsif ($cmd eq "D") { # DestroyStream
244 my $obj = $_OBJ{+rcv_ptr};
245 my $str = delete $_OBJ{+rcv_ptr};
246 my $why = rcv_u32;
247
248 $obj->destroy_stream($obj, $str, $why);
249
250 } else {
251 die "unknown command '$cmd' received";
252 }
253 }
254
255 =item my $fh = server_fh
256
257 The design of this module is event-based. When the plug-in starts (there
258 is always just one interpreter) it spawns one perl interpreter which
259 will immediately go into a even loop. If you want to use your own event
260 loop (e.g. using the Gtk or Event modules) you need to register a file
261 input handler on the filehandle returned by C<server_fh> that calls
262 C<server_event> whenever there is input pending on C<server_fh>. This will
263 ensure proper operation of the plug-in.
264
265 =item server_event
266
267 Call this function whenever there is data available on the C<server_fh>. This function
268 might not return.
269
270 Due to this design (flaw?), sharing of different toolkits using this
271 plug-in is difficult at best. Spawning a new perl interpreter for every
272 plug-in is also not very viable, so in the future one might be able to
273 specify a group on the embed statement (i.e. EMBED GROUP="gtk+").
274
275 =cut
276
277 sub server_fh { \*FD }
278
279 sub server_event { rcv_cmd }
280
281 sub mainloop {
282 server_event while 1;
283 }
284
285 sub init {
286 $interface = shift;
287
288 open FD, "+<&=$_[0]"; binmode FD;
289
290 $interface->mainloop;
291 }
292
293 sub new {
294 my $class = shift;
295 my $self = bless { @_ }, $class;
296 $self->{save} = $self->{save} ne ""
297 ? Storable::thaw $self->{save}
298 : {};
299 $self;
300 }
301
302 sub set_window {
303 my $self = shift;
304 my $new = shift;
305
306 if ($self->{wininfo}) {
307 if ($self->{wininfo}{id} ne $new->{id}) {
308 $self->window_delete($self->{wininfo});
309 } elsif ($self->{wininfo}{w} != $new->{w}
310 or $self->{wininfo}{h} != $new->{h}) {
311 $self->window_resize($new->{id}, $new->{w}, $new->{h});
312 }
313 $self->{wininfo} = $new;
314 }
315
316 unless ($self->{wininfo}) {
317 $self->{window} = $self->window_new($new) and $self->{wininfo} = $new;
318 }
319
320 ();
321 }
322
323 sub window_new { () }
324 sub window_resize {}
325
326 sub window_delete {
327 my $self = shift;
328 delete $self->{wininfo};
329 delete $self->{window};
330 }
331
332 sub save {
333 my $self = shift;
334 $self->set_window(undef);
335 $self->destroy;
336 Storable::nfreeze $self->{save};
337 }
338
339 sub destroy {}
340 sub DESTROY {}
341 sub write_ready { 0xfffff }
342 sub push_stream {}
343 sub stream_as_file {}
344 sub destroy_stream {}
345
346 sub write {
347 my ($self) = shift;
348 shift->write(@_);
349 }
350
351 =item ($error, $stream) = new_stream $mimetype, $target
352
353 Requests the creation of a new data stream produced by the plug-in and
354 consumed by the browser.
355
356 =cut
357
358 sub new_stream {
359 my ($self, $type, $target) = @_;
360 my ($error, $stream);
361
362 snd_cmd 'n';
363 snd_blk $mimetype;
364 snd_blk $target;
365 if (snd_snd) {
366 rcv_cmd 'n';
367 $error = rcv_u32;
368 unless ($error) {
369 $stream = new Mozilla::Stream::Send (
370 instance => $self,
371 mimetype => $type,
372 id => rcv_ptr,
373 url => $target,
374 );
375 }
376 }
377 ($error, $stream);
378 }
379
380 =item $error = $obj->get_url $url, $target
381
382 =cut
383
384 sub get_url {
385 my ($self, $url, $target) = @_;
386 }
387
388 sub Mozilla::Stream::new {
389 bless $_[1], $_[0];
390 }
391
392 sub Mozilla::Stream::destroy {
393 my $str = shift;
394 my $error = shift;
395 unless ($str->{destroyed}) {
396 $str->{destroyed}++;
397
398 my $obj = $str->{instance};
399 snd_cmd 'd';
400 snd_ptr $obj->{id};
401 snd_ptr $str->{id};
402 snd_u32 $error || NPERR_NO_ERROR;
403 snd_snd;
404 }
405 }
406
407 *Mozilla::Stream::DESTROY = \&Mozilla::Stream::destroy;
408
409 BEGIN {
410 @Mozilla::Stream::Receive::ISA = Mozilla::Stream;
411 }
412
413 sub Mozilla::Stream::Receive::write {
414 # ...
415 }
416
417 BEGIN {
418 @Mozilla::Stream::Send::ISA = Mozilla::Stream;
419 }
420
421 =item $bytes = $stream->write($data)
422
423 =cut
424
425 sub Mozilla::Stream::Send::write {
426 my $str = shift;
427 my $obj = $self->{instance};
428
429 snd_cmd 'w';
430 snd_ptr $obj->{id};
431 snd_ptr $str->{id};
432 snd_u32 length $_[0];
433 if (snd_snd) {
434 if (snd_dyn $_[0]) {
435 rcv_cmd 'w';
436 return rcv_i32 ();
437 }
438 }
439 return 0;
440 }
441
442 1;
443
444 =back
445
446 =head1 BUGS
447
448 =head1 SEE ALSO
449
450 L<PApp>.
451
452 =head1 AUTHOR
453
454 Marc Lehmann <pcg@goof.com>
455 http://www.goof.com/pcg/marc/
456
457 =cut
458