diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 1d95a595703..e9241cc40a7 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -380,7 +380,6 @@ @BINPATH@/components/nsPrompter.js #ifdef MOZ_SERVICES_SYNC @BINPATH@/components/SyncComponents.manifest -@BINPATH@/components/FormNotifier.js @BINPATH@/components/Weave.js @BINPATH@/components/WeaveCrypto.manifest @BINPATH@/components/WeaveCrypto.js diff --git a/config/cvsco-fast-update.pl b/config/cvsco-fast-update.pl deleted file mode 100755 index db3e4831ce9..00000000000 --- a/config/cvsco-fast-update.pl +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env perl -# -# cvsco-fast-update.pl cvs co ... -# -# This command parses a "cvs co ..." command and converts it to -# fast-update.pl commands -# -use Getopt::Long; - -my $filename = ".fast-update"; -my $start_time = time(); - -my $branch; -my @modules; -my @dirs; -my $dirlocal = 0; - -print "$0: (".join(')(',@ARGV).")\n"; -while (scalar(@ARGV)) { - my $val = shift(@ARGV); - if ( ($val eq '-A') || ($val eq 'co') || ($val eq 'cvs') - || ($val eq '-P') || ($val eq '-q')) { - #print "ignore $val\n"; - next; - } - elsif (($val eq '-d') || ($val eq '-q') || ($val eq '-z')) { - my $tmp = shift @ARGV; - #print "ignore $val $tmp\n"; - next; - } - elsif ($val eq '-r') { - $branch = shift @ARGV; - #print "branch = $branch\n"; - next; - } - elsif ($val eq '-l') { - $dirlocal = 1; - #print "dirlocal = $dirlocal\n"; - next; - } - elsif ($val =~ /^-/) { - print "*** unknown switch: $val\n"; - exit 1; - } - - if ($val =~ /\//) { - push @dirs, $val; - #print "dir = $val\n"; - } - else { - push @modules, $val; - #print "module = $val\n"; - } -} - -#print "dir = (".join(')(', @dirs)."), " -# . "module = (".join(')(', @modules)."), " -# . "branch = ($branch)\n"; - -if (!$branch) { - $branch = 'HEAD'; -} - -my $status = 0; -foreach my $mod (@modules) { - my $cmd = "config/fast-update.pl -r $branch -m $mod" . ($dirlocal ? " -l" : ""); - #print "system \"$cmd\"\n"; - $status |= system $cmd; -} -my $dirlist = join(' -d ', @dirs); -my $cmd = "config/fast-update.pl -r $branch -d $dirlist -m all" . ($dirlocal ? " -l" : ""); -#print "system \"$cmd\"\n"; -$status |= system $cmd; - -exit $status; - - - diff --git a/config/cvsco.pl b/config/cvsco.pl deleted file mode 100755 index f15e0b31996..00000000000 --- a/config/cvsco.pl +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env perl -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1999 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Stephen Lamm -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -sub usage { - warn "usage: cvsco.pl \n" - ." is the entire cvs co command\n" - ." (e.g. cvs -q -z3 co SeaMonkeyAll).\n"; -} - -usage(), die "Error: Not enough arguments\n" if $#ARGV < 0; -usage(), die "Error: Wrong cwd. Must chdir to mozilla/..\n" - unless -r 'mozilla/config/cvsco.pl'; - -$co_command = join ' ', @ARGV; - -$logfile = 'cvslog.txt'; -$old_logfile = 'cvslog-old.txt'; - -sub dblprint { - print LOG @_; - print STDERR @_; -} - -if (-r $logfile) { - rename $logfile, $old_logfile; - print "rename $logfile, $old_logfile\n"; -} - -open LOG, ">$logfile"; -open CVSCO, "$co_command|"; - -dblprint "\ncheckout start: ".scalar(localtime)."\n"; -dblprint "$co_command | tee cvslog.txt\n"; - -while () { - dblprint $_; - push @conflicts, $_ if /^C /; -} - -if (@conflicts) { - print "Error: cvs conflicts during checkout:\n"; - die join('', @conflicts); -} - -close(CVSCO) or die "cvs error.\n"; -dblprint 'checkout finish: '.scalar(localtime)."\n"; - diff --git a/config/fast-update.pl b/config/fast-update.pl deleted file mode 100755 index d8ff81b50f1..00000000000 --- a/config/fast-update.pl +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env perl -# -# fast-update.pl [-h hours] [-m module] [-r branch] -# -# This command, fast-update.pl, does a (fast) cvs update of the current -# directory. It is fast because the cvs up command is only run on those -# directories / sub-directories where changes have occurred since the -# last fast-update. -# -# The last update time is stored in a ".fast-update" file in the current -# directory. Thus one can choose to only fast-update a branch of the tree -# and then fast-update the whole tree later. -# -# The first time this command is run in a directory the last cvs update -# time is assumed to be the timestamp of the CVS/Entries file. -# -use Getopt::Long; - -my $filename = ".fast-update"; -my $start_time = time(); - -my $branch; -my $module="SeaMonkeyAll"; -my $maxdirs=5; -my $rootdir = ""; -my $hours = 0; -my @dirs = (); -my $dirlocal = 0; - -&GetOptions('d=s@' => \@dirs, 'h=s' => \$hours, 'm=s' => \$module, 'r=s' => \$branch, 'l' => \$dirlocal); - -#print "dirs = (@dirs), hours = ($hours), module = ($module), branch = ($branch), dirlocal = ($dirlocal)\n"; -if (scalar(@dirs) > 0) { - # put .fast-update in the first directory listed - $filename = "$dirs[0]/$filename"; - $filename =~ s#mozilla/*##; -} - -if (!$hours) { - $hours = get_hours_since_last_update(); -} -if (!$hours) { - $hours = 24; -} - - -# pull out the current directory -# if there is no such file, this will all just fail, which is ok -open REPOSITORY, "; -$rootdir =~ tr/\r\n//d; # Remove newlines -close REPOSITORY; - -# try to guess the current branch by looking at all the -# files in CVS/Entries -if (!$branch) { - my $foundbranch =0; - - open ENTRIES, ") { - chop; - @entry = split(/\//); - my ($type, $file, $ver, $date, $unknown, $tag) = @entry; - - # the tag usually starts with "T" - $thisbranch = substr($tag, 1); - - # look for more than one branch - if ($type eq "") { - - if ($foundbranch and ($lastbranch ne $thisbranch)) { - die "Multiple branches in this directory, cannot determine branch\n"; - } - $foundbranch = 1; - $lastbranch = $thisbranch; - } - - } - - $branch = $lastbranch if ($foundbranch); - - close ENTRIES; -} - -# check for a static Tag -# (at least that is what I think this does) -# (bonsai does not report changes when the Tag starts with 'N') -# (I do not really understand all this) -if ($branch) { - open TAG, "; - if ($line =~ /^N/) { - print "static tag, ignore branch\n"; - $branch = ''; - } - close TAG; -} - - -my $url = "http://bonsai.mozilla.org/cvsquery.cgi?module=${module}&branch=${branch}&branchtype=match&sortby=File&date=hours&hours=${hours}&cvsroot=%2Fcvsroot"; - -my $dir_string = ""; -if (scalar(@dirs) > 0) { - $dir_string = join(' ', @dirs); - my $esc_dir = escape($dir_string); - $url .= "&dir=$esc_dir"; -} -if ($dirlocal) { - $url .= "&dirtype=local"; -} - -print "Contacting bonsai for updates to ${module} "; -print "on the ${branch} branch " if ($branch); -print "in the last ${hours} hours "; -print "within the $rootdir directory..\n" if ($rootdir); -print "\n" unless ($rootdir); -#print "url = $url\n"; - -# first try wget, then try lynx, then try curl - -# this is my lame way of checking if a command succeeded AND getting -# output from it. I'd love a better way. -alecf@netscape.com -my $have_checkins = 0; -open CHECKINS,"wget --quiet --output-document=- \"$url\"|" or - die "Error opening wget: $!\n"; - -$header = and $have_checkins=1; - -if (!$have_checkins) { - - open CHECKINS, "lynx -source '$url'|" or die "Error opening lynx: $!\n"; - - $header = and $have_checkins = 1; -} - -if (!$have_checkins) { - - open CHECKINS, "curl -s '$url'|" or die "Error opening curl $!\n"; - - $header = and $have_checkins = 1; -} - -$have_checkins || die "Couldn't get checkins\n"; - -open REALOUT, ">.fast-update.bonsai.html" || die "argh $!\n"; -print "Processing checkins..."; -while () { - print REALOUT $_; - - if (/js_file_menu\((.*),\s*\'(.*)\'\s*,\s*(.*),\s*(.*),\s*(.*),\s*(.*)\)/) { - my ($repos, $dir, $file, $rev, $branch, $event) = - ($1, $2, $3, $4, $5, $6); - $dir =~ s/\/Attic$//; - push @dirlist, $dir; - } -} - -print "done.\n"; -close REALOUT; -unlink '.fast-update.bonsai.html'; - -my $lastdir = ""; -my @uniquedirs; - -foreach $dir (sort @dirlist) { - next if ($lastdir eq $dir); - - my $strippeddir = ""; - $lastdir = $dir; - - # now strip out $rootdir - if ($rootdir) { - - # only deal with directories that start with $rootdir - if (substr($dir, 0, (length $rootdir)) eq $rootdir) { - - if ($dir eq $rootdir) { - $strippeddir = "."; - } else { - $strippeddir = substr($dir,(length $rootdir) + 1 ); - } - - } - } else { - $strippeddir = $dir; - } - - if ($strippeddir) { - push @uniquedirs, $strippeddir; - } -} - -my $status = 0; -if (scalar(@uniquedirs)) { - print "Updating tree... (" . scalar(@uniquedirs) . " directories)\n"; - my $i=0; - my $dirlist = ""; - foreach $dir (sort @uniquedirs) { - if (!-d $dir) { - cvs_up_parent($dir); - } - $dirlist .= "\"$dir\" "; - $i++; - if ($i == 5) { - $status |= spawn("cvs -z3 -q -f up -l -d $dirlist\n"); - $dirlist = ""; - $i=0; - } - } - if ($i) { - $status |= spawn("cvs -z3 -q -f up -l -d $dirlist\n"); - } -} -else { - print "No directories to update.\n"; -} - -close CHECKINS; -if ($status == 0) { - set_last_update_time($filename, $start_time); - print "successfully updated "; -} -else { - print "error while updating "; -} -if ($module ne "all") { - print "$module/"; -} -if (scalar(@dirs) > 0) { - print $dir_string; -} -print "\n"; - -exit $status; - -sub cvs_up_parent { - my ($dir) = @_; - my $pdir = $dir; - $pdir =~ s|/*[^/]*/*$||; - #$pdir =~ s|/$||; - #$pdir =~ s|[^/]*$||; - #$pdir =~ s|/$||; - if (!$pdir) { - $pdir = '.'; - } - if (!-d $pdir) { - cvs_up_parent($pdir); - } - $status |= system "cvs -z3 -q -f up -d -l $pdir\n"; -} - -sub get_hours_since_last_update { - # get the last time this command was run - my $last_time = get_last_update_time($filename); - if (!defined($last_time)) { - # - # This must be the first use of fast-update.pl so use the timestamp - # of a file that: - # 1) is managed by cvs - # 2) the user should not be tampering with - # 3) that gets updated fairly frequently. - # - $last_time = (stat "CVS/Entries")[9]; - if (defined($last_time)) { - $last_time -= 3600*24; # for safety go back a bit - print "use fallback time of ".localtime($last_time)."\n"; - } - } - if(!defined($last_time)) { - print "last_time not defined\n"; - } - - # figure the hours (rounded up) since the last fast-update - my $hours = int(($start_time - $last_time + 3600)/3600); - print "last updated $hours hour(s) ago at ".localtime($last_time)."\n"; - return $hours; -} - -# returns time of last update if known -sub get_last_update_time { - my ($filename) = @_; - if (!-r $filename) { - return undef; - } - open FILE, "<$filename"; - my $line = ; - if (!defined(line)) { - return undef; - } -# print "line = $line"; - $line =~ /^(\d+):/; - return $1; -} - -sub set_last_update_time { - my ($filename, $time) = @_; - my $time_str = localtime($time); - open FILE, ">$filename"; - print FILE "$time: last fast-update.pl at ".localtime($time)."\n"; -} - -# URL-encode data -sub escape { - my ($toencode) = @_; - $toencode=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; - return $toencode; -} - -sub spawn { - my ($procname) = @_; - return system "$procname"; -} diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in index 0cb86e4e4e2..62df94b6468 100644 --- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -113,7 +113,6 @@ _TEST_FILES = \ test_load_candidates.html \ test_load_source.html \ test_media_selection.html \ - test_mixed_principals.html \ test_mozLoadFrom.html \ test_networkState.html \ test_new_audio.html \ @@ -147,6 +146,8 @@ _TEST_FILES = \ # test_videoDocumentTitle.html # Bug 493692: # test_preload_suspend.html +# Bug 567954 and Bug 574586: +# test_mixed_principals.html # Disabled since we don't play Wave files standalone, for now # test_audioDocumentTitle.html diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index cf5e523b1bb..b567dae9830 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -116,8 +116,10 @@ class GeckoAppShell if (!f.exists()) f.mkdirs(); GeckoAppShell.putenv("TMPDIR=" + f.getPath()); - - + + f = Environment.getDownloadCacheDirectory(); + GeckoAppShell.putenv("EXTERNAL_STORAGE" + f.getPath()); + // NSPR System.loadLibrary("nspr4"); System.loadLibrary("plc4"); diff --git a/gfx/cairo/cairo/src/cairo-os2-private.h b/gfx/cairo/cairo/src/cairo-os2-private.h index e71a6dc9431..6c8e8f48b05 100644 --- a/gfx/cairo/cairo/src/cairo-os2-private.h +++ b/gfx/cairo/cairo/src/cairo-os2-private.h @@ -61,6 +61,7 @@ typedef struct _cairo_os2_surface /* General flags: */ cairo_bool_t blit_as_changes; + cairo_bool_t use_24bpp; } cairo_os2_surface_t; #endif /* CAIRO_OS2_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-os2-surface.c b/gfx/cairo/cairo/src/cairo-os2-surface.c index 3fda0b4f66b..379cb25592b 100644 --- a/gfx/cairo/cairo/src/cairo-os2-surface.c +++ b/gfx/cairo/cairo/src/cairo-os2-surface.c @@ -33,6 +33,7 @@ * * Contributor(s): * Peter Weilbacher + * Rich Walsh */ #include "cairoint.h" @@ -66,7 +67,7 @@ /* Initialization counter: */ static int cairo_os2_initialization_count = 0; -static void inline +static inline void DisableFPUException (void) { unsigned short usCW; @@ -169,43 +170,35 @@ cairo_os2_fini (void) */ void *_buffer_alloc (size_t a, size_t b, const unsigned int size) { - /* check length like in the _cairo_malloc_abc macro, but we can leave - * away the unsigned casts as our arguments are unsigned already - */ - size_t nbytes = b && - a >= INT32_MAX / b ? 0 : size && - a*b >= INT32_MAX / size ? 0 : a * b * size; - void *buffer = NULL; -#ifdef OS2_USE_PLATFORM_ALLOC - APIRET rc = NO_ERROR; + size_t nbytes; + void *buffer = NULL; - rc = DosAllocMem ((PPVOID)&buffer, - nbytes, -#ifdef OS2_HIGH_MEMORY /* only if compiled with high-memory support, */ - OBJ_ANY | /* we can allocate anywhere! */ -#endif - PAG_READ | PAG_WRITE | PAG_COMMIT); - if (rc != NO_ERROR) { - /* should there for some reason be another error, let's return - * a null surface and free the buffer again, because that's - * how a malloc failure would look like - */ - if (rc != ERROR_NOT_ENOUGH_MEMORY && buffer) { - DosFreeMem (buffer); - } + if (!a || !b || !size || + a >= INT32_MAX / b || a*b >= INT32_MAX / size) { return NULL; } -#else - buffer = malloc (nbytes); + nbytes = a * b * size; + +#ifdef OS2_USE_PLATFORM_ALLOC + /* Using OBJ_ANY on a machine that isn't configured for hi-mem + * will cause ERROR_INVALID_PARAMETER. If this occurs, or this + * build doesn't have hi-mem enabled, fall back to using lo-mem. + */ +#ifdef OS2_HIGH_MEMORY + if (!DosAllocMem (&buffer, nbytes, + OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) + return buffer; +#endif + if (DosAllocMem (&buffer, nbytes, + PAG_READ | PAG_WRITE | PAG_COMMIT)) + return NULL; +#else + /* Clear the malloc'd buffer the way DosAllocMem() does. */ + buffer = malloc (nbytes); + if (buffer) { + memset (buffer, 0, nbytes); + } #endif - - /* This does not seem to be needed, malloc'd space is usually - * already zero'd out! - */ - /* - * memset (buffer, 0x00, nbytes); - */ - return buffer; } @@ -303,46 +296,38 @@ _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, PRECTL prcl_begin_paint_rect) { POINTL aptlPoints[4]; - LONG lOldYInversion, rc = GPI_OK; + LONG lOldYInversion; + LONG rc = GPI_OK; - /* Enable Y Inversion for the HPS, so the - * GpiDrawBits will work with upside-top image, not with upside-down image! + /* Check the limits (may not be necessary) */ + if (prcl_begin_paint_rect->xLeft < 0) + prcl_begin_paint_rect->xLeft = 0; + if (prcl_begin_paint_rect->yBottom < 0) + prcl_begin_paint_rect->yBottom = 0; + if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) + prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; + if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) + prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; + + /* Exit if the rectangle is empty */ + if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || + prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) + return; + + /* Set the Target & Source coordinates */ + *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; + *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; + + /* Make the Target coordinates non-inclusive */ + aptlPoints[1].x -= 1; + aptlPoints[1].y -= 1; + + /* Enable Y Inversion for the HPS, so GpiDrawBits will + * work with upside-top image, not with upside-down image! */ lOldYInversion = GpiQueryYInversion (hps_begin_paint); GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); - /* Target coordinates (Noninclusive) */ - aptlPoints[0].x = prcl_begin_paint_rect->xLeft; - aptlPoints[0].y = prcl_begin_paint_rect->yBottom; - - aptlPoints[1].x = prcl_begin_paint_rect->xRight-1; - aptlPoints[1].y = prcl_begin_paint_rect->yTop-1; - - /* Source coordinates (Inclusive) */ - aptlPoints[2].x = prcl_begin_paint_rect->xLeft; - aptlPoints[2].y = prcl_begin_paint_rect->yBottom; - - aptlPoints[3].x = prcl_begin_paint_rect->xRight; - aptlPoints[3].y = (prcl_begin_paint_rect->yTop); - - /* Some extra checking for limits - * (Dunno if really needed, but had some crashes sometimes without it, - * while developing the code...) - */ - { - int i; - for (i = 0; i < 4; i++) { - if (aptlPoints[i].x < 0) - aptlPoints[i].x = 0; - if (aptlPoints[i].y < 0) - aptlPoints[i].y = 0; - if (aptlPoints[i].x > (LONG) surface->bitmap_info.cx) - aptlPoints[i].x = (LONG) surface->bitmap_info.cx; - if (aptlPoints[i].y > (LONG) surface->bitmap_info.cy) - aptlPoints[i].y = (LONG) surface->bitmap_info.cy; - } - } - /* Debug code to draw rectangle limits */ #if 0 { @@ -364,63 +349,81 @@ _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, } } #endif - rc = GpiDrawBits (hps_begin_paint, - surface->pixels, - &(surface->bitmap_info), - 4, - aptlPoints, - ROP_SRCCOPY, - BBO_IGNORE); + if (!surface->use_24bpp) { + rc = GpiDrawBits (hps_begin_paint, + surface->pixels, + &(surface->bitmap_info), + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = TRUE; + } - if (rc != GPI_OK) { - /* if GpiDrawBits () failed then this is most likely because the + if (surface->use_24bpp) { + /* If GpiDrawBits () failed then this is most likely because the * display driver could not handle a 32bit bitmap. So we need to * - create a buffer that only contains 3 bytes per pixel * - change the bitmap info header to contain 24bit * - pass the new buffer to GpiDrawBits () again * - clean up the new buffer */ - BITMAPINFOHEADER2 bmpheader; - unsigned char *pchPixBuf, *pchPixSource; - void *pBufStart; - ULONG ulPixels; + BITMAPINFO2 bmpinfo; + unsigned char *pchPixBuf; + unsigned char *pchTarget; + ULONG *pulSource; + ULONG ulX; + ULONG ulY; + ULONG ulPad; - /* allocate temporary pixel buffer */ - pchPixBuf = (unsigned char *) _buffer_alloc (surface->bitmap_info.cy, - surface->bitmap_info.cx, - 3); - pchPixSource = surface->pixels; /* start at beginning of pixel buffer */ - pBufStart = pchPixBuf; /* remember beginning of the new pixel buffer */ + /* Set up the bitmap header, but this time for 24bit depth. */ + bmpinfo = surface->bitmap_info; + bmpinfo.cBitCount = 24; - /* copy the first three bytes for each pixel but skip over the fourth */ - for (ulPixels = 0; ulPixels < surface->bitmap_info.cx * surface->bitmap_info.cy; ulPixels++) - { - /* copy BGR from source buffer */ - *pchPixBuf++ = *pchPixSource++; - *pchPixBuf++ = *pchPixSource++; - *pchPixBuf++ = *pchPixSource++; - pchPixSource++; /* jump over alpha channel in source buffer */ + /* The start of each row has to be DWORD aligned. Calculate the + * of number aligned bytes per row, the total size of the bitmap, + * and the number of padding bytes at the end of each row. + */ + ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; + bmpinfo.cbImage = ulX * bmpinfo.cy; + ulPad = ulX - bmpinfo.cx * 3; + + /* Allocate temporary pixel buffer. If the rows don't need + * padding, it has to be 1 byte larger than the size of the + * bitmap or else the high-order byte from the last source + * row will end up in unallocated memory. + */ + pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, + bmpinfo.cbImage + (ulPad ? 0 : 1)); + + if (pchPixBuf) { + /* Copy 4 bytes from the source but advance the target ptr only + * 3 bytes, so the high-order alpha byte will be overwritten by + * the next copy. At the end of each row, skip over the padding. + */ + pchTarget = pchPixBuf; + pulSource = (ULONG*)surface->pixels; + for (ulY = bmpinfo.cy; ulY; ulY--) { + for (ulX = bmpinfo.cx; ulX; ulX--) { + *((ULONG*)pchTarget) = *pulSource++; + pchTarget += 3; + } + pchTarget += ulPad; + } + + rc = GpiDrawBits (hps_begin_paint, + pchPixBuf, + &bmpinfo, + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = FALSE; + + _buffer_free (pchPixBuf); } - - /* jump back to start of the buffer for display and cleanup */ - pchPixBuf = pBufStart; - - /* set up the bitmap header, but this time with 24bit depth only */ - memset (&bmpheader, 0, sizeof (bmpheader)); - bmpheader.cbFix = sizeof (BITMAPINFOHEADER2); - bmpheader.cx = surface->bitmap_info.cx; - bmpheader.cy = surface->bitmap_info.cy; - bmpheader.cPlanes = surface->bitmap_info.cPlanes; - bmpheader.cBitCount = 24; - rc = GpiDrawBits (hps_begin_paint, - pchPixBuf, - (PBITMAPINFO2)&bmpheader, - 4, - aptlPoints, - ROP_SRCCOPY, - BBO_IGNORE); - - _buffer_free (pchPixBuf); } /* Restore Y inversion */ @@ -434,7 +437,6 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, { HPS hps; HDC hdc; - HAB hab; SIZEL sizlTemp; HBITMAP hbmpTemp; BITMAPINFO2 bmi2Temp; @@ -452,19 +454,10 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, * -- Blit dirty pixels from screen to HBITMAP * - Copy HBITMAP lines (pixels) into our buffer * - Free resources - * - * These steps will require an Anchor Block (HAB). However, - * WinQUeryAnchorBlock () documentation says that HAB is not - * used in current OS/2 implementations, OS/2 deduces all information - * it needs from the TID. Anyway, we'd be in trouble if we'd have to - * get a HAB where we only know a HPS... - * So, we'll simply use a fake HAB. */ - hab = (HAB) 1; /* OS/2 doesn't really use HAB... */ - /* Create a memory device context */ - hdc = DevOpenDC (hab, OD_MEMORY,"*",0L, NULL, NULLHANDLE); + hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE); if (!hdc) { return; } @@ -472,7 +465,7 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, /* Create a memory PS */ sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft; sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom; - hps = GpiCreatePS (hab, + hps = GpiCreatePS (0, hdc, &sizlTemp, PU_PELS | GPIT_NORMAL | GPIA_ASSOC); @@ -537,7 +530,7 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, GpiQueryBitmapBits (hps, sizlTemp.cy - y - 1, /* lScanStart */ 1, /* lScans */ - pchTemp, + (PBYTE)pchTemp, &bmi2Temp); /* Go for next line */ @@ -722,6 +715,14 @@ _cairo_os2_surface_get_extents (void *abstract_surface, { cairo_os2_surface_t *local_os2_surface; + local_os2_surface = (cairo_os2_surface_t *) abstract_surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + rectangle->x = 0; rectangle->y = 0; rectangle->width = local_os2_surface->bitmap_info.cx; @@ -737,14 +738,15 @@ _cairo_os2_surface_get_extents (void *abstract_surface, * @height: the height of the surface * * Create a Cairo surface which is bound to a given presentation space (HPS). - * The surface will be created to have the given size. - * By default every change to the surface will be made visible immediately by - * blitting it into the window. This can be changed with + * The caller retains ownership of the HPS and must dispose of it after the + * the surface has been destroyed. The surface will be created to have the + * given size. By default every change to the surface will be made visible + * immediately by blitting it into the window. This can be changed with * cairo_os2_surface_set_manual_window_refresh(). - * Note that the surface will contain garbage when created, so the pixels have - * to be initialized by hand first. You can use the Cairo functions to fill it - * with black, or use cairo_surface_mark_dirty() to fill the surface with pixels - * from the window/HPS. + * Note that the surface will contain garbage when created, so the pixels + * have to be initialized by hand first. You can use the Cairo functions to + * fill it with black, or use cairo_surface_mark_dirty() to fill the surface + * with pixels from the window/HPS. * * Return value: the newly created surface * @@ -755,72 +757,48 @@ cairo_os2_surface_create (HPS hps_client_window, int width, int height) { - cairo_os2_surface_t *local_os2_surface; + cairo_os2_surface_t *local_os2_surface = 0; cairo_status_t status; int rc; /* Check the size of the window */ - if ((width <= 0) || - (height <= 0)) - { - /* Invalid window size! */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if ((width <= 0) || (height <= 0)) { + status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); + goto error_exit; } + /* Allocate an OS/2 surface structure. */ local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); if (!local_os2_surface) { - /* Not enough memory! */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; } - /* Initialize the OS/2 specific parts of the surface! */ + memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); - /* Create mutex semaphore */ - rc = DosCreateMutexSem (NULL, - &(local_os2_surface->hmtx_use_private_fields), - 0, - FALSE); - if (rc != NO_ERROR) { - /* Could not create mutex semaphore! */ - free (local_os2_surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + /* Allocate resources: mutex & event semaphores and the pixel buffer */ + if (DosCreateMutexSem (NULL, + &(local_os2_surface->hmtx_use_private_fields), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; } - /* Save PS handle */ - local_os2_surface->hps_client_window = hps_client_window; - - /* Defaults */ - local_os2_surface->hwnd_client_window = NULLHANDLE; - local_os2_surface->blit_as_changes = TRUE; - local_os2_surface->pixel_array_lend_count = 0; - rc = DosCreateEventSem (NULL, - &(local_os2_surface->hev_pixel_array_came_back), - 0, - FALSE); - - if (rc != NO_ERROR) { - /* Could not create event semaphore! */ - DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); - free (local_os2_surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (DosCreateEventSem (NULL, + &(local_os2_surface->hev_pixel_array_came_back), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; } - /* Prepare BITMAPINFO2 structure for our buffer */ - memset (&(local_os2_surface->bitmap_info), 0, sizeof (local_os2_surface->bitmap_info)); - local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); - local_os2_surface->bitmap_info.cx = width; - local_os2_surface->bitmap_info.cy = height; - local_os2_surface->bitmap_info.cPlanes = 1; - local_os2_surface->bitmap_info.cBitCount = 32; - - /* Allocate memory for pixels */ local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); - if (!(local_os2_surface->pixels)) { - /* Not enough memory for the pixels! */ - DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); - DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); - free (local_os2_surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (!local_os2_surface->pixels) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; } /* Create image surface from pixel array */ @@ -830,22 +808,91 @@ cairo_os2_surface_create (HPS hps_client_window, width, /* Width */ height, /* Height */ width * 4); /* Rowstride */ - status = local_os2_surface->image_surface->base.status; - if (status) { - /* Could not create image surface! */ - _buffer_free (local_os2_surface->pixels); - DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); - DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); - free (local_os2_surface); - return _cairo_surface_create_in_error (status); - } + if (status) + goto error_exit; + + /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. + * Note: hps_client_window may be null if this was called by + * cairo_os2_surface_create_for_window(). + */ + local_os2_surface->hps_client_window = hps_client_window; + local_os2_surface->blit_as_changes = TRUE; + + /* Prepare BITMAPINFO2 structure for our buffer */ + local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); + local_os2_surface->bitmap_info.cx = width; + local_os2_surface->bitmap_info.cy = height; + local_os2_surface->bitmap_info.cPlanes = 1; + local_os2_surface->bitmap_info.cBitCount = 32; /* Initialize base surface */ _cairo_surface_init (&local_os2_surface->base, &cairo_os2_surface_backend, _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); + /* Successful exit */ + return (cairo_surface_t *)local_os2_surface; + + error_exit: + + /* This point will only be reached if an error occured */ + + if (local_os2_surface) { + if (local_os2_surface->pixels) + _buffer_free (local_os2_surface->pixels); + if (local_os2_surface->hev_pixel_array_came_back) + DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); + if (local_os2_surface->hmtx_use_private_fields) + DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); + free (local_os2_surface); + } + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_os2_surface_create_for_window: + * @hwnd_client_window: the window handle to bind the surface to + * @width: the width of the surface + * @height: the height of the surface + * + * Create a Cairo surface which is bound to a given window; the caller retains + * ownership of the window. This is a convenience function for use with + * windows that will only be updated when cairo_os2_surface_refresh_window() + * is called (usually in response to a WM_PAINT message). It avoids the need + * to create a persistent HPS for every window and assumes that one will be + * supplied by the caller when a cairo function needs one. If it isn't, an + * HPS will be created on-the-fly and released before the function which needs + * it returns. + * + * Return value: the newly created surface + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + + /* A window handle must be provided. */ + if (!hwnd_client_window) { + return _cairo_surface_create_in_error ( + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + /* Create the surface. */ + local_os2_surface = (cairo_os2_surface_t *) + cairo_os2_surface_create (0, width, height); + + /* If successful, save the hwnd & turn off automatic repainting. */ + if (!local_os2_surface->image_surface->base.status) { + local_os2_surface->hwnd_client_window = hwnd_client_window; + local_os2_surface->blit_as_changes = FALSE; + } + return (cairo_surface_t *)local_os2_surface; } @@ -1014,6 +1061,7 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface, { cairo_os2_surface_t *local_os2_surface; RECTL rclTemp; + HPS hpsTemp = 0; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || @@ -1023,9 +1071,19 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface, return; } - /* Manage defaults (NULLs) */ - if (!hps_begin_paint) + /* If an HPS wasn't provided, see if we can get one. */ + if (!hps_begin_paint) { hps_begin_paint = local_os2_surface->hps_client_window; + if (!hps_begin_paint) { + if (local_os2_surface->hwnd_client_window) { + hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window); + hps_begin_paint = hpsTemp; + } + /* No HPS & no way to get one, so exit */ + if (!hps_begin_paint) + return; + } + } if (prcl_begin_paint_rect == NULL) { /* Update the whole window! */ @@ -1046,6 +1104,8 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface, != NO_ERROR) { /* Could not get mutex! */ + if (hpsTemp) + WinReleasePS(hpsTemp); return; } @@ -1073,6 +1133,9 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface, } DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + if (hpsTemp) + WinReleasePS(hpsTemp); } static cairo_status_t @@ -1109,10 +1172,10 @@ _cairo_os2_surface_finish (void *abstract_surface) * @surface: the cairo surface to associate with the window handle * @hwnd_client_window: the window handle of the client window * - * Sets window handle for surface. If Cairo wants to blit into the window - * because it is set to blit as the surface changes (see - * cairo_os2_surface_set_manual_window_refresh()), then there are two ways it - * can choose: + * Sets window handle for surface; the caller retains ownership of the window. + * If Cairo wants to blit into the window because it is set to blit as the + * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then + * there are two ways it can choose: * If it knows the HWND of the surface, then it invalidates that area, so the * application will get a WM_PAINT message and it can call * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself @@ -1214,6 +1277,74 @@ cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface) return !(local_os2_surface->blit_as_changes); } +/** + * cairo_os2_surface_get_hps: + * @surface: the cairo surface to be querued + * @hps: HPS currently associated with the surface (if any) + * + * This API retrieves the HPS associated with the surface. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * %CAIRO_STATUS_NULL_POINTER if the hps argument is null + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + if (!hps) + { + return _cairo_error (CAIRO_STATUS_NULL_POINTER); + } + *hps = local_os2_surface->hps_client_window; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_set_hps: + * @surface: the cairo surface to associate with the HPS + * @hps: new HPS to be associated with the surface (the HPS may be null) + * + * This API replaces the HPS associated with the surface with a new one. + * The caller retains ownership of the HPS and must dispose of it after + * the surface has been destroyed or it has been replaced by another + * call to this function. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + } + local_os2_surface->hps_client_window = hps; + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_os2_surface_mark_dirty_rectangle (void *surface, int x, @@ -1331,5 +1462,11 @@ static const cairo_surface_backend_t cairo_os2_surface_backend = { NULL, /* stroke */ NULL, /* fill */ NULL, /* show_glyphs */ - NULL /* snapshot */ + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* has_show_text_glyphs */ + NULL /* show_text_glyphs */ }; diff --git a/gfx/cairo/cairo/src/cairo-os2.h b/gfx/cairo/cairo/src/cairo-os2.h index 0d18674b2ae..cbb342b09ec 100644 --- a/gfx/cairo/cairo/src/cairo-os2.h +++ b/gfx/cairo/cairo/src/cairo-os2.h @@ -33,6 +33,7 @@ * * Contributor(s): * Peter Weilbacher + * Rich Walsh */ #ifndef _CAIRO_OS2_H_ @@ -65,6 +66,11 @@ cairo_os2_surface_create (HPS hps_client_window, int width, int height); +cairo_public cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height); + cairo_public void cairo_os2_surface_set_hwnd (cairo_surface_t *surface, HWND hwnd_client_window); @@ -87,6 +93,14 @@ cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, cairo_public cairo_bool_t cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface); +cairo_public cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps); + +cairo_public cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps); + #else /* CAIRO_HAS_OS2_SURFACE */ # error Cairo was not compiled with support for the OS/2 backend #endif /* CAIRO_HAS_OS2_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-rename.h b/gfx/cairo/cairo/src/cairo-rename.h index 2013f0839e2..e3cddf99244 100644 --- a/gfx/cairo/cairo/src/cairo-rename.h +++ b/gfx/cairo/cairo/src/cairo-rename.h @@ -114,11 +114,14 @@ #define cairo_os2_fini _moz_cairo_os2_fini #define cairo_os2_init _moz_cairo_os2_init #define cairo_os2_surface_create _moz_cairo_os2_surface_create +#define cairo_os2_surface_create_for_window _moz_cairo_os2_surface_create_for_window #define cairo_os2_surface_get_manual_window_refresh _moz_cairo_os2_surface_get_manual_window_refresh #define cairo_os2_surface_refresh_window _moz_cairo_os2_surface_refresh_window #define cairo_os2_surface_set_hwnd _moz_cairo_os2_surface_set_hwnd #define cairo_os2_surface_set_manual_window_refresh _moz_cairo_os2_surface_set_manual_window_refresh #define cairo_os2_surface_set_size _moz_cairo_os2_surface_set_size +#define cairo_os2_surface_get_hps _moz_cairo_os2_surface_get_hps +#define cairo_os2_surface_set_hps _moz_cairo_os2_surface_set_hps #define cairo_paint _moz_cairo_paint #define cairo_paint_with_alpha _moz_cairo_paint_with_alpha #define cairo_path_destroy _moz_cairo_path_destroy diff --git a/gfx/thebes/gfxOS2Surface.cpp b/gfx/thebes/gfxOS2Surface.cpp index 59d28c759a5..93c82b5f173 100644 --- a/gfx/thebes/gfxOS2Surface.cpp +++ b/gfx/thebes/gfxOS2Surface.cpp @@ -45,7 +45,7 @@ gfxOS2Surface::gfxOS2Surface(const gfxIntSize& aSize, gfxASurface::gfxImageFormat aImageFormat) - : mHasWnd(PR_FALSE), mSize(aSize) + : mWnd(0), mSize(aSize) { #ifdef DEBUG_thebes_2 printf("gfxOS2Surface[%#x]::gfxOS2Surface(Size=%dx%d, %d)\n", (unsigned int)this, @@ -97,40 +97,35 @@ gfxOS2Surface::gfxOS2Surface(const gfxIntSize& aSize, } gfxOS2Surface::gfxOS2Surface(HWND aWnd) - : mHasWnd(PR_TRUE), mDC(nsnull), mBitmap(nsnull) + : mWnd(aWnd), mDC(nsnull), mPS(nsnull), mBitmap(nsnull) { #ifdef DEBUG_thebes_2 printf("gfxOS2Surface[%#x]::gfxOS2Surface(HWND=%#x)\n", (unsigned int)this, (unsigned int)aWnd); #endif - mPS = WinGetPS(aWnd); - RECTL rectl; WinQueryWindowRect(aWnd, &rectl); mSize.width = rectl.xRight - rectl.xLeft; mSize.height = rectl.yTop - rectl.yBottom; if (mSize.width == 0) mSize.width = 1; // fake a minimal surface area to let if (mSize.height == 0) mSize.height = 1; // cairo_os2_surface_create() return something - cairo_surface_t *surf = cairo_os2_surface_create(mPS, mSize.width, mSize.height); + + // This variation on cairo_os2_surface_create() avoids creating a + // persistent HPS that may never be used. It also enables manual + // refresh so nsWindow::OnPaint() controls when the screen is updated. + cairo_surface_t *surf = + cairo_os2_surface_create_for_window(mWnd, mSize.width, mSize.height); #ifdef DEBUG_thebes_2 printf(" type(%#x)=%d (ID=%#x, h/w=%d/%d)\n", (unsigned int)surf, cairo_surface_get_type(surf), (unsigned int)mPS, mSize.width, mSize.height); #endif - // Normally, OS/2 cairo surfaces have to be forced to redraw completely - // by calling cairo_surface_mark_dirty(surf), but Mozilla paints them in - // full, so that is not necessary here. - - // record the window handle in the cairo surface, so that refresh works - cairo_os2_surface_set_hwnd(surf, aWnd); - // manual refresh is done from nsWindow::OnPaint - cairo_os2_surface_set_manual_window_refresh(surf, 1); Init(surf); } gfxOS2Surface::gfxOS2Surface(HDC aDC, const gfxIntSize& aSize) - : mHasWnd(PR_FALSE), mDC(aDC), mBitmap(nsnull), mSize(aSize) + : mWnd(0), mDC(aDC), mBitmap(nsnull), mSize(aSize) { #ifdef DEBUG_thebes_2 printf("gfxOS2Surface[%#x]::gfxOS2Surface(HDC=%#x, Size=%dx%d)\n", (unsigned int)this, @@ -180,7 +175,7 @@ gfxOS2Surface::~gfxOS2Surface() // release it again with WinReleasePS. Memory or printer surfaces on the // other hand were created on device contexts with the GPI functions, so // use those to clean up stuff. - if (mHasWnd) { + if (mWnd) { if (mPS) { WinReleasePS(mPS); } @@ -206,7 +201,7 @@ void gfxOS2Surface::Refresh(RECTL *aRect, HPS aPS) aRect->xLeft, aRect->xRight, aRect->yBottom, aRect->yTop, (unsigned int)aPS, (unsigned int)mPS); #endif - cairo_os2_surface_refresh_window(CairoSurface(), aPS, aRect); + cairo_os2_surface_refresh_window(CairoSurface(), (aPS ? aPS : mPS), aRect); } int gfxOS2Surface::Resize(const gfxIntSize& aSize) @@ -219,3 +214,22 @@ int gfxOS2Surface::Resize(const gfxIntSize& aSize) // hardcode mutex timeout to 50ms for now return cairo_os2_surface_set_size(CairoSurface(), mSize.width, mSize.height, 50); } + +HPS gfxOS2Surface::GetPS() +{ + // Creating an HPS on-the-fly should never be needed because GetPS() + // is only called for printing surfaces & mPS should only be null for + // window surfaces. It would be a bug if Cairo had an HPS but Thebes + // didn't, but we'll check anyway to avoid leakage. As a last resort, + // if this is a window surface we'll create one & hang on to it. + if (!mPS) { + cairo_os2_surface_get_hps(CairoSurface(), &mPS); + if (!mPS && mWnd) { + mPS = WinGetPS(mWnd); + cairo_os2_surface_set_hps(CairoSurface(), mPS); + } + } + + return mPS; +} + diff --git a/gfx/thebes/gfxOS2Surface.h b/gfx/thebes/gfxOS2Surface.h index d191b3d84ed..6ceb79bfb30 100644 --- a/gfx/thebes/gfxOS2Surface.h +++ b/gfx/thebes/gfxOS2Surface.h @@ -66,11 +66,11 @@ public: // Reset the cairo surface to the given size. int Resize(const gfxIntSize& aSize); - HPS GetPS() { return mPS; } + HPS GetPS(); gfxIntSize GetSize() { return mSize; } private: - PRBool mHasWnd; // indicates if created through the HWND constructor + HWND mWnd; // non-null if created through the HWND constructor HDC mDC; // memory device context HPS mPS; // presentation space connected to window or memory device HBITMAP mBitmap; // bitmap for initialization of memory surface diff --git a/netwerk/cache/nsCacheRequest.h b/netwerk/cache/nsCacheRequest.h index 19b869625fc..9ccdad7747e 100644 --- a/netwerk/cache/nsCacheRequest.h +++ b/netwerk/cache/nsCacheRequest.h @@ -54,6 +54,7 @@ class nsCacheRequest : public PRCList private: friend class nsCacheService; friend class nsCacheEntry; + friend class nsProcessRequestEvent; nsCacheRequest( nsCString * key, nsICacheListener * listener, diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index 3dba6a066f0..d9cdd31f29e 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -614,6 +614,44 @@ nsCacheProfilePrefObserver::MemoryCacheCapacity() return capacity; } + +/****************************************************************************** + * nsProcessRequestEvent + *****************************************************************************/ + +class nsProcessRequestEvent : public nsRunnable { +public: + nsProcessRequestEvent(nsCacheRequest *aRequest) + { + mRequest = aRequest; + } + + NS_IMETHOD Run() + { + nsresult rv; + + NS_ASSERTION(mRequest->mListener, + "Sync OpenCacheEntry() posted to background thread!"); + + nsCacheServiceAutoLock lock; + rv = nsCacheService::gService->ProcessRequest(mRequest, + PR_FALSE, + nsnull); + + // Don't delete the request if it was queued + if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) + delete mRequest; + + return NS_OK; + } + +protected: + virtual ~nsProcessRequestEvent() {} + +private: + nsCacheRequest *mRequest; +}; + /****************************************************************************** * nsCacheService *****************************************************************************/ @@ -681,8 +719,13 @@ nsCacheService::Init() CACHE_LOG_INIT(); + nsresult rv = NS_NewThread(getter_AddRefs(mCacheIOThread)); + if (NS_FAILED(rv)) { + NS_WARNING("Can't create cache IO thread"); + } + // initialize hashtable for active cache entries - nsresult rv = mActiveEntries.Init(); + rv = mActiveEntries.Init(); if (NS_FAILED(rv)) return rv; // create profile/preference observer @@ -703,6 +746,9 @@ nsCacheService::Init() void nsCacheService::Shutdown() { + nsCOMPtr cacheIOThread; + + { nsCacheServiceAutoLock lock; NS_ASSERTION(mInitialized, "can't shutdown nsCacheService unless it has been initialized."); @@ -734,7 +780,13 @@ nsCacheService::Shutdown() #if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING) LogCacheStatistics(); #endif + + mCacheIOThread.swap(cacheIOThread); } + } // lock + + if (cacheIOThread) + cacheIOThread->Shutdown(); } @@ -947,6 +999,17 @@ NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy) return EvictEntriesForClient(nsnull, storagePolicy); } +NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget) +{ + nsCacheServiceAutoLock lock; + + if (!mCacheIOThread) + return NS_ERROR_NOT_AVAILABLE; + + NS_ADDREF(*aCacheIOTarget = mCacheIOThread); + return NS_OK; +} + /** * Internal Methods */ @@ -1235,11 +1298,28 @@ nsCacheService::OpenCacheEntry(nsCacheSession * session, CACHE_LOG_DEBUG(("Created request %p\n", request)); - rv = gService->ProcessRequest(request, PR_TRUE, result); + // Process the request on the background thread if we are on the main thread + // and the the request is asynchronous + if (NS_IsMainThread() && listener && gService->mCacheIOThread) { + nsCOMPtr ev = + new nsProcessRequestEvent(request); + if (ev) { + rv = gService->mCacheIOThread->Dispatch(ev, NS_DISPATCH_NORMAL); + } else { + rv = NS_ERROR_OUT_OF_MEMORY; + } - // delete requests that have completed - if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) - delete request; + // delete request if we didn't post the event + if (NS_FAILED(rv)) + delete request; + } + else { + rv = gService->ProcessRequest(request, PR_TRUE, result); + + // delete requests that have completed + if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))) + delete request; + } return rv; } @@ -1779,7 +1859,7 @@ nsCacheService::ReleaseObject_Locked(nsISupports * obj, NS_ASSERTION(gService->mLockedThread == PR_GetCurrentThread(), "oops"); PRBool isCur; - if (!target || NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur) { + if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) { gService->mDoomedObjects.AppendElement(obj); } else { NS_ProxyRelease(target, obj); diff --git a/netwerk/cache/nsCacheService.h b/netwerk/cache/nsCacheService.h index d808f705565..8ca2e5dfe3a 100644 --- a/netwerk/cache/nsCacheService.h +++ b/netwerk/cache/nsCacheService.h @@ -166,6 +166,7 @@ public: private: friend class nsCacheServiceAutoLock; friend class nsOfflineCacheDevice; + friend class nsProcessRequestEvent; /** * Internal Methods @@ -245,6 +246,8 @@ private: PRThread * mLockedThread; // The thread holding mLock #endif + nsCOMPtr mCacheIOThread; + nsTArray mDoomedObjects; PRBool mInitialized; diff --git a/netwerk/cache/nsDiskCacheDeviceSQL.cpp b/netwerk/cache/nsDiskCacheDeviceSQL.cpp index e5a04763d57..4aa9e28fe4a 100644 --- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp +++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp @@ -54,6 +54,7 @@ #include "nsArrayUtils.h" #include "nsIArray.h" #include "nsIVariant.h" +#include "nsThreadUtils.h" #include "mozIStorageService.h" #include "mozIStorageStatement.h" @@ -171,7 +172,7 @@ DCacheHash(const char * key) * nsOfflineCacheEvictionFunction */ -NS_IMPL_ISUPPORTS1(nsOfflineCacheEvictionFunction, mozIStorageFunction) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsOfflineCacheEvictionFunction, mozIStorageFunction) // helper function for directly exposing the same data file binding // path algorithm used in nsOfflineCacheBinding::Create @@ -768,11 +769,37 @@ nsApplicationCache::GetUsage(PRUint32 *usage) return mDevice->GetUsage(mClientID, usage); } +/****************************************************************************** + * nsCloseDBEvent + *****************************************************************************/ + +class nsCloseDBEvent : public nsRunnable { +public: + nsCloseDBEvent(mozIStorageConnection *aDB) + { + mDB = aDB; + } + + NS_IMETHOD Run() + { + mDB->Close(); + return NS_OK; + } + +protected: + virtual ~nsCloseDBEvent() {} + +private: + nsCOMPtr mDB; +}; + + + /****************************************************************************** * nsOfflineCacheDevice */ -NS_IMPL_ISUPPORTS1(nsOfflineCacheDevice, nsIApplicationCacheService) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsOfflineCacheDevice, nsIApplicationCacheService) nsOfflineCacheDevice::nsOfflineCacheDevice() : mDB(nsnull) @@ -986,6 +1013,8 @@ nsOfflineCacheDevice::Init() rv = ss->OpenDatabase(indexFile, getter_AddRefs(mDB)); NS_ENSURE_SUCCESS(rv, rv); + mInitThread = do_GetCurrentThread(); + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;")); // XXX ... other initialization steps @@ -1201,6 +1230,7 @@ nsOfflineCacheDevice::Shutdown() if (mCaches.IsInitialized()) mCaches.EnumerateRead(ShutdownApplicationCache, this); + { EvictionObserver evictionObserver(mDB, mEvictionFunction); // Delete all rows whose clientID is not an active clientID. @@ -1227,9 +1257,51 @@ nsOfflineCacheDevice::Shutdown() if (NS_FAILED(rv)) NS_WARNING("Failed to clean up namespaces."); - mDB = 0; mEvictionFunction = 0; + mStatement_CacheSize = nsnull; + mStatement_ApplicationCacheSize = nsnull; + mStatement_EntryCount = nsnull; + mStatement_UpdateEntry = nsnull; + mStatement_UpdateEntrySize = nsnull; + mStatement_UpdateEntryFlags = nsnull; + mStatement_DeleteEntry = nsnull; + mStatement_FindEntry = nsnull; + mStatement_BindEntry = nsnull; + mStatement_ClearDomain = nsnull; + mStatement_MarkEntry = nsnull; + mStatement_UnmarkEntry = nsnull; + mStatement_GetTypes = nsnull; + mStatement_FindNamespaceEntry = nsnull; + mStatement_InsertNamespaceEntry = nsnull; + mStatement_CleanupUnmarked = nsnull; + mStatement_GatherEntries = nsnull; + mStatement_ActivateClient = nsnull; + mStatement_DeactivateGroup = nsnull; + mStatement_FindClient = nsnull; + mStatement_FindClientByNamespace = nsnull; + mStatement_EnumerateGroups = nsnull; + } + + // Close Database on the correct thread + PRBool isOnCurrentThread = PR_TRUE; + if (mInitThread) + mInitThread->IsOnCurrentThread(&isOnCurrentThread); + + if (!isOnCurrentThread) { + nsCOMPtr ev = new nsCloseDBEvent(mDB); + + if (ev) { + mInitThread->Dispatch(ev, NS_DISPATCH_NORMAL); + } + } + else { + mDB->Close(); + } + + mDB = nsnull; + mInitThread = nsnull; + return NS_OK; } diff --git a/netwerk/cache/nsDiskCacheDeviceSQL.h b/netwerk/cache/nsDiskCacheDeviceSQL.h index 5be4e358c28..a47eeebf30b 100644 --- a/netwerk/cache/nsDiskCacheDeviceSQL.h +++ b/netwerk/cache/nsDiskCacheDeviceSQL.h @@ -275,6 +275,8 @@ private: nsInterfaceHashtable mCaches; nsClassHashtable mActiveCachesByGroup; nsCStringHashSet mActiveCaches; + + nsCOMPtr mInitThread; }; #endif // nsOfflineCacheDevice_h__ diff --git a/netwerk/cache/nsICacheService.idl b/netwerk/cache/nsICacheService.idl index 0bc46b6230b..baa18d809cc 100644 --- a/netwerk/cache/nsICacheService.idl +++ b/netwerk/cache/nsICacheService.idl @@ -47,8 +47,9 @@ interface nsISimpleEnumerator; interface nsICacheListener; interface nsICacheSession; interface nsICacheVisitor; +interface nsIEventTarget; -[scriptable, uuid(de114eb4-29fc-4959-b2f7-2d03eb9bc771)] +[scriptable, uuid(14dbe1e9-f3bc-45af-92f4-2c574fcd4e39)] interface nsICacheService : nsISupports { /** @@ -87,6 +88,11 @@ interface nsICacheService : nsISupports * everything. */ void evictEntries(in nsCacheStoragePolicy storagePolicy); + + /** + * Event target which is used for I/O operations + */ + readonly attribute nsIEventTarget cacheIOTarget; }; %{C++ diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 95f99b14aa6..7da0ce357b2 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -115,6 +115,8 @@ nsHttpChannel::nsHttpChannel() , mCacheAccess(0) , mPostID(0) , mRequestTime(0) + , mOnCacheEntryAvailableCallback(nsnull) + , mAsyncCacheOpen(PR_FALSE) , mPendingAsyncCallOnResume(nsnull) , mSuspendCount(0) , mApplyConversion(PR_TRUE) @@ -198,8 +200,6 @@ nsHttpChannel::Connect(PRBool firstTime) // true when called from AsyncOpen if (firstTime) { - PRBool delayed = PR_FALSE; - // are we offline? PRBool offline = gIOService->IsOffline(); if (offline) @@ -214,7 +214,7 @@ nsHttpChannel::Connect(PRBool firstTime) } // open a cache entry for this channel... - rv = OpenCacheEntry(offline, &delayed); + rv = OpenCacheEntry(); if (NS_FAILED(rv)) { LOG(("OpenCacheEntry failed [rv=%x]\n", rv)); @@ -238,7 +238,7 @@ nsHttpChannel::Connect(PRBool firstTime) if (NS_FAILED(rv)) return rv; } - if (NS_SUCCEEDED(rv) && delayed) + if (NS_SUCCEEDED(rv) && mAsyncCacheOpen) return NS_OK; } @@ -271,6 +271,14 @@ nsHttpChannel::Connect(PRBool firstTime) return NS_ERROR_DOCUMENT_NOT_CACHED; } } + else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { + // If we have a fallback URI (and we're not already + // falling back), process the fallback asynchronously. + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { + return AsyncCall(&nsHttpChannel::HandleAsyncFallback); + } + return NS_ERROR_DOCUMENT_NOT_CACHED; + } // check to see if authorization headers should be included mAuthProvider->AddAuthorizationHeaders(); @@ -1680,11 +1688,11 @@ IsSubRangeRequest(nsHttpRequestHead &aRequestHead) } nsresult -nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) +nsHttpChannel::OpenCacheEntry() { nsresult rv; - *delayed = PR_FALSE; + mAsyncCacheOpen = PR_FALSE; mLoadedFromApplicationCache = PR_FALSE; LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this)); @@ -1720,23 +1728,10 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) GenerateCacheKey(mPostID, cacheKey); - // Get a cache session with appropriate storage policy - nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(); - // Set the desired cache access mode accordingly... nsCacheAccessMode accessRequested; - if (offline || (mLoadFlags & INHIBIT_CACHING)) { - // If we have been asked to bypass the cache and not write to the - // cache, then don't use the cache at all. Unless we're actually - // offline, which takes precedence over BYPASS_LOCAL_CACHE. - if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) - return NS_ERROR_NOT_AVAILABLE; - accessRequested = nsICache::ACCESS_READ; - } - else if (BYPASS_LOCAL_CACHE(mLoadFlags)) - accessRequested = nsICache::ACCESS_WRITE; // replace cache entry - else - accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing + rv = DetermineCacheAccess(&accessRequested); + if NS_FAILED(rv) return rv; if (!mApplicationCache && mInheritApplicationCache) { // Pick up an application cache from the notification @@ -1765,10 +1760,6 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) nsCOMPtr session; - // Will be set to true if we've found the right session, but need - // to open the cache entry asynchronously. - PRBool waitingForValidation = PR_FALSE; - // If we have an application cache, we check it first. if (mApplicationCache) { nsCAutoString appCacheClientID; @@ -1784,107 +1775,217 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) getter_AddRefs(session)); NS_ENSURE_SUCCESS(rv, rv); - // we'll try to synchronously open the cache entry... however, - // it may be in use and not yet validated, in which case we'll - // try asynchronously opening the cache entry. - // - // We open with ACCESS_READ only, because we don't want to - // overwrite the offline cache entry non-atomically. - // ACCESS_READ will prevent us from writing to the offline - // cache as a normal cache entry. - rv = session->OpenCacheEntry(cacheKey, - nsICache::ACCESS_READ, PR_FALSE, - getter_AddRefs(mCacheEntry)); - if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { - accessRequested = nsICache::ACCESS_READ; - waitingForValidation = PR_TRUE; - rv = NS_OK; + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { + // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY + rv = session->OpenCacheEntry(cacheKey, + nsICache::ACCESS_READ, PR_FALSE, + getter_AddRefs(mCacheEntry)); + if (NS_SUCCEEDED(rv)) { + mCacheEntry->GetAccessGranted(&mCacheAccess); + LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", + this, mCacheAccess)); + mLoadedFromApplicationCache = PR_TRUE; + return NS_OK; + } else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { + LOG(("bypassing local cache since it is busy\n")); + // Don't try to load normal cache entry + return NS_ERROR_NOT_AVAILABLE; + } + } else { + mOnCacheEntryAvailableCallback = + &nsHttpChannel::OnOfflineCacheEntryAvailable; + // We open with ACCESS_READ only, because we don't want to + // overwrite the offline cache entry non-atomically. + // ACCESS_READ will prevent us from writing to the offline + // cache as a normal cache entry. + rv = session->AsyncOpenCacheEntry(cacheKey, + nsICache::ACCESS_READ, + this); + + if (NS_SUCCEEDED(rv)) { + mAsyncCacheOpen = PR_TRUE; + return NS_OK; + } } - if (NS_FAILED(rv) && !mCacheForOfflineUse && !mFallbackChannel) { - // Check for namespace match. - nsCOMPtr namespaceEntry; - rv = mApplicationCache->GetMatchingNamespace - (cacheKey, getter_AddRefs(namespaceEntry)); + // sync or async opening failed + return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, + rv, PR_TRUE); + } + + return OpenNormalCacheEntry(PR_TRUE); +} + +nsresult +nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aEntryStatus, + PRBool aIsSync) +{ + nsresult rv; + + if (NS_SUCCEEDED(aEntryStatus)) { + // We successfully opened an offline cache session and the entry, + // so indicate we will load from the offline cache. + mLoadedFromApplicationCache = PR_TRUE; + mCacheEntry = aEntry; + mCacheAccess = aAccess; + } + + if (mCanceled && NS_FAILED(mStatus)) { + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); + return mStatus; + } + + if (NS_SUCCEEDED(aEntryStatus)) + // Called from OnCacheEntryAvailable, advance to the next state + return Connect(PR_FALSE); + + if (!mCacheForOfflineUse && !mFallbackChannel) { + nsCAutoString cacheKey; + GenerateCacheKey(mPostID, cacheKey); + + // Check for namespace match. + nsCOMPtr namespaceEntry; + rv = mApplicationCache->GetMatchingNamespace + (cacheKey, getter_AddRefs(namespaceEntry)); + if (NS_FAILED(rv) && !aIsSync) + return Connect(PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 namespaceType = 0; + if (!namespaceEntry || + NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) || + (namespaceType & + (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK | + nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC | + nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) { + // When loading from an application cache, only items + // on the whitelist or matching a + // fallback/opportunistic namespace should hit the + // network... + mLoadFlags |= LOAD_ONLY_FROM_CACHE; + + // ... and if there were an application cache entry, + // we would have found it earlier. + return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(PR_FALSE); + } + + if (namespaceType & + nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) { + rv = namespaceEntry->GetData(mFallbackKey); + if (NS_FAILED(rv) && !aIsSync) + return Connect(PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 namespaceType = 0; - if (!namespaceEntry || - NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) || - (namespaceType & - (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK | - nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC | - nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) { - // When loading from an application cache, only items - // on the whitelist or matching a - // fallback/opportunistic namespace should hit the - // network... - mLoadFlags |= LOAD_ONLY_FROM_CACHE; - - // ... and if there were an application cache entry, - // we would have found it earlier. - return NS_ERROR_CACHE_KEY_NOT_FOUND; - } - - if (namespaceType & - nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) { - rv = namespaceEntry->GetData(mFallbackKey); - NS_ENSURE_SUCCESS(rv, rv); - } - - if ((namespaceType & - nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) && - mLoadFlags & LOAD_DOCUMENT_URI) { - // Document loads for items in an opportunistic namespace - // should be placed in the offline cache. - nsCString clientID; - mApplicationCache->GetClientID(clientID); - - mCacheForOfflineUse = !clientID.IsEmpty(); - SetOfflineCacheClientID(clientID); - mCachingOpportunistically = PR_TRUE; - } } - else if (NS_SUCCEEDED(rv)) { - // We successfully opened an offline cache session and the entry, - // now indiciate we load from the offline cache. - mLoadedFromApplicationCache = PR_TRUE; + + if ((namespaceType & + nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) && + mLoadFlags & LOAD_DOCUMENT_URI) { + // Document loads for items in an opportunistic namespace + // should be placed in the offline cache. + nsCString clientID; + mApplicationCache->GetClientID(clientID); + + mCacheForOfflineUse = !clientID.IsEmpty(); + SetOfflineCacheClientID(clientID); + mCachingOpportunistically = PR_TRUE; } } - if (!mCacheEntry && !waitingForValidation) { - rv = gHttpHandler->GetCacheSession(storagePolicy, - getter_AddRefs(session)); - if (NS_FAILED(rv)) return rv; + return OpenNormalCacheEntry(aIsSync); +} + +nsresult +nsHttpChannel::OpenNormalCacheEntry(PRBool aIsSync) +{ + NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry"); + + nsresult rv; + + nsCAutoString cacheKey; + GenerateCacheKey(mPostID, cacheKey); + + nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(); + + nsCOMPtr session; + rv = gHttpHandler->GetCacheSession(storagePolicy, + getter_AddRefs(session)); + if (NS_FAILED(rv)) return rv; + + nsCacheAccessMode accessRequested; + rv = DetermineCacheAccess(&accessRequested); + if NS_FAILED(rv) return rv; + + if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { + if (!aIsSync) { + // Unexpected state: we were called from OnCacheEntryAvailable(), + // so LOAD_BYPASS_LOCAL_CACHE_IF_BUSY shouldn't be set. Unless + // somebody altered mLoadFlags between OpenCacheEntry() and + // OnCacheEntryAvailable()... + NS_WARNING( + "OpenNormalCacheEntry() called from OnCacheEntryAvailable() " + "when LOAD_BYPASS_LOCAL_CACHE_IF_BUSY was specified"); + } + + // must use synchronous open for LOAD_BYPASS_LOCAL_CACHE_IF_BUSY rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE, getter_AddRefs(mCacheEntry)); - if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { - waitingForValidation = PR_TRUE; - rv = NS_OK; + if (NS_SUCCEEDED(rv)) { + mCacheEntry->GetAccessGranted(&mCacheAccess); + LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", + this, mCacheAccess)); + } + else if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { + LOG(("bypassing local cache since it is busy\n")); + rv = NS_ERROR_NOT_AVAILABLE; + } + } + else { + mOnCacheEntryAvailableCallback = + &nsHttpChannel::OnNormalCacheEntryAvailable; + rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this); + if (NS_SUCCEEDED(rv)) { + mAsyncCacheOpen = PR_TRUE; + return NS_OK; } - if (NS_FAILED(rv)) return rv; } - if (waitingForValidation) { - // access to the cache entry has been denied (because the - // cache entry is probably in use by another channel). - if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { - LOG(("bypassing local cache since it is busy\n")); - return NS_ERROR_NOT_AVAILABLE; - } - rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this); - if (NS_FAILED(rv)) return rv; - // we'll have to wait for the cache entry - *delayed = PR_TRUE; - } - else if (NS_SUCCEEDED(rv)) { - mCacheEntry->GetAccessGranted(&mCacheAccess); - LOG(("nsHttpChannel::OpenCacheEntry [this=%p grantedAccess=%d]", this, mCacheAccess)); - } + if (!aIsSync) + // Called from OnCacheEntryAvailable, advance to the next state + rv = Connect(PR_FALSE); + return rv; } +nsresult +nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aEntryStatus, + PRBool aIsSync) +{ + NS_ASSERTION(!aIsSync, "aIsSync should be false"); + + if (NS_SUCCEEDED(aEntryStatus)) { + mCacheEntry = aEntry; + mCacheAccess = aAccess; + } + + if (mCanceled && NS_FAILED(mStatus)) { + LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); + return mStatus; + } + + if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus)) + // if this channel is only allowed to pull from the cache, then + // we must fail if we were unable to open a cache entry. + return NS_ERROR_DOCUMENT_NOT_CACHED; + + // advance to the next state... + return Connect(PR_FALSE); +} + nsresult nsHttpChannel::OpenOfflineCacheEntryForWriting() @@ -2711,20 +2812,26 @@ nsHttpChannel::InstallCacheListener(PRUint32 offset) do_CreateInstance(kStreamListenerTeeCID, &rv); if (NS_FAILED(rv)) return rv; + nsCOMPtr serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr cacheIOTarget; + serv->GetCacheIOTarget(getter_AddRefs(cacheIOTarget)); + nsCacheStoragePolicy policy; rv = mCacheEntry->GetStoragePolicy(&policy); - if (!gHttpHandler->mCacheWriteThread || - NS_FAILED(rv) || - policy == nsICache::STORE_ON_DISK_AS_FILE) { - LOG(("nsHttpChannel::InstallCacheListener sync tee %p\n", tee.get())); + if (NS_FAILED(rv) || policy == nsICache::STORE_ON_DISK_AS_FILE || + !cacheIOTarget) { + LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x policy=%d " + "cacheIOTarget=%p", tee.get(), rv, policy, cacheIOTarget.get())); rv = tee->Init(mListener, out, nsnull); } else { - LOG(("nsHttpChannel::InstallCacheListener async tee %p\n", - tee.get())); - rv = tee->InitAsync(mListener, gHttpHandler->mCacheWriteThread, out, nsnull); + LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get())); + rv = tee->InitAsync(mListener, cacheIOTarget, out, nsnull); } - + if (NS_FAILED(rv)) return rv; mListener = tee; return NS_OK; @@ -4077,6 +4184,8 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, nsCacheAccessMode access, nsresult status) { + nsresult rv; + LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p " "access=%x status=%x]\n", this, entry, access, status)); @@ -4085,28 +4194,24 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, if (!mIsPending) return NS_OK; - // otherwise, we have to handle this event. - if (NS_SUCCEEDED(status)) { - mCacheEntry = entry; - mCacheAccess = access; - } + nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback; + mOnCacheEntryAvailableCallback = nsnull; - nsresult rv; + NS_ASSERTION(callback, + "nsHttpChannel::OnCacheEntryAvailable called without callback"); + rv = ((*this).*callback)(entry, access, status, PR_FALSE); - if (mCanceled && NS_FAILED(mStatus)) { - LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus)); - rv = mStatus; - } - else if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(status)) - // if this channel is only allowed to pull from the cache, then - // we must fail if we were unable to open a cache entry. - rv = NS_ERROR_DOCUMENT_NOT_CACHED; - else - // advance to the next state... - rv = Connect(PR_FALSE); - - // a failure from Connect means that we have to abort the channel. if (NS_FAILED(rv)) { + LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv)); + if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { + // If we have a fallback URI (and we're not already + // falling back), process the fallback asynchronously. + if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { + rv = AsyncCall(&nsHttpChannel::HandleAsyncFallback); + if (NS_SUCCEEDED(rv)) + return rv; + } + } CloseCacheEntry(PR_TRUE); AsyncAbort(rv); } @@ -4573,6 +4678,27 @@ nsHttpChannel::DetermineStoragePolicy() return policy; } +nsresult +nsHttpChannel::DetermineCacheAccess(nsCacheAccessMode *_retval) +{ + PRBool offline = gIOService->IsOffline(); + + if (offline || (mLoadFlags & INHIBIT_CACHING)) { + // If we have been asked to bypass the cache and not write to the + // cache, then don't use the cache at all. Unless we're actually + // offline, which takes precedence over BYPASS_LOCAL_CACHE. + if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) + return NS_ERROR_NOT_AVAILABLE; + *_retval = nsICache::ACCESS_READ; + } + else if (BYPASS_LOCAL_CACHE(mLoadFlags)) + *_retval = nsICache::ACCESS_WRITE; // replace cache entry + else + *_retval = nsICache::ACCESS_READ_WRITE; // normal browsing + + return NS_OK; +} + void nsHttpChannel::AsyncOnExamineCachedResponse() { diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 088bebf2443..ac125b1771b 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -216,7 +216,16 @@ private: nsresult ResolveProxy(); // cache specific methods - nsresult OpenCacheEntry(PRBool offline, PRBool *delayed); + nsresult OpenCacheEntry(); + nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aResult, + PRBool aSync); + nsresult OpenNormalCacheEntry(PRBool aSync); + nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry, + nsCacheAccessMode aAccess, + nsresult aResult, + PRBool aSync); nsresult OpenOfflineCacheEntryForWriting(); nsresult GenerateCacheKey(PRUint32 postID, nsACString &key); nsresult UpdateExpirationTime(); @@ -234,6 +243,7 @@ private: nsresult InstallOfflineCacheListener(); void MaybeInvalidateCacheEntryForSubsequentGet(); nsCacheStoragePolicy DetermineStoragePolicy(); + nsresult DetermineCacheAccess(nsCacheAccessMode *_retval); void AsyncOnExamineCachedResponse(); // Handle the bogus Content-Encoding Apache sometimes sends @@ -264,6 +274,11 @@ private: PRUint32 mPostID; PRUint32 mRequestTime; + typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)( + nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, PRBool); + nsOnCacheEntryAvailableCallback mOnCacheEntryAvailableCallback; + PRBool mAsyncCacheOpen; + nsCOMPtr mOfflineCacheEntry; nsCacheAccessMode mOfflineCacheAccess; nsCString mOfflineCacheClientID; diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 638e8a8f20a..2274a97aa3d 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -294,14 +294,6 @@ nsHttpHandler::Init() rv = InitConnectionMgr(); if (NS_FAILED(rv)) return rv; - rv = NS_NewThread(getter_AddRefs(mCacheWriteThread)); - if (NS_FAILED(rv)) { - mCacheWriteThread = nsnull; - LOG(("Failed creating cache-write thread - writes will be synchronous")); - } else { - LOG(("Created cache-write thread = %p", mCacheWriteThread.get())); - } - nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); if (appInfo) @@ -321,7 +313,6 @@ nsHttpHandler::Init() mObserverService->AddObserver(this, "profile-change-net-restore", PR_TRUE); mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE); mObserverService->AddObserver(this, "net:clear-active-logins", PR_TRUE); - mObserverService->AddObserver(this, "xpcom-shutdown-threads", PR_TRUE); } StartPruneDeadConnectionsTimer(); @@ -1780,18 +1771,6 @@ nsHttpHandler::Observe(nsISupports *subject, else if (strcmp(topic, "net:clear-active-logins") == 0) { mAuthCache.ClearAll(); } - else if (strcmp(topic, "xpcom-shutdown-threads") == 0) { - // Shutdown the cache write thread. This must be done after shutting down - // the cache service, because the (memory) cache entries' storage streams - // get released on the thread on which they were first written to, which - // is this thread. - if (mCacheWriteThread) { - LOG((" shutting down cache-write thread...\n")); - mCacheWriteThread->Shutdown(); - LOG((" cache-write thread shutdown complete\n")); - mCacheWriteThread = nsnull; - } - } return NS_OK; } diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index f56c1db2cc7..aec75a59bb7 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -210,8 +210,6 @@ public: static nsresult GenerateHostPort(const nsCString& host, PRInt32 port, nsCString& hostLine); - // The thread used to implement async cache-writes - nsCOMPtr mCacheWriteThread; private: // diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index a7cc259639a..7cf20d7b867 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -116,12 +116,6 @@ libs:: $(_MOD_PYWEBSOCKET_FILES) libs:: $(_HANDSHAKE_FILES) $(INSTALL) $(foreach f,$^,"$f") $(_DEST_DIR)/pywebsocket/mod_pywebsocket/handshake -runtests.py: runtests.py.in - $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ - $(DEFINES) $(ACDEFINES) $^ > $@ - -GARBAGE += runtests.py - libs:: $(_SERV_FILES) $(INSTALL) $^ $(_DEST_DIR) diff --git a/testing/mochitest/runtests.py.in b/testing/mochitest/runtests.py similarity index 100% rename from testing/mochitest/runtests.py.in rename to testing/mochitest/runtests.py diff --git a/toolkit/content/aboutAbout.xhtml b/toolkit/content/aboutAbout.xhtml index 7faefa4a5f6..0197be05048 100644 --- a/toolkit/content/aboutAbout.xhtml +++ b/toolkit/content/aboutAbout.xhtml @@ -2,6 +2,8 @@ %aboutAboutDTD; + +%globalDTD; ]> # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -92,7 +94,7 @@ ]]> - +

&aboutAbout.title;

&aboutAbout.note;

    diff --git a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp index 01a8386201b..4e43fcb75ea 100644 --- a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp +++ b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp @@ -42,11 +42,7 @@ #include "nsStringEnumerator.h" #include "nsNetUtil.h" -NS_IMPL_ISUPPORTS2(nsMIMEInfoAndroid, nsIMIMEInfo, nsIHandlerInfo) - -nsMIMEInfoAndroid::~nsMIMEInfoAndroid() -{ -} +NS_IMPL_ISUPPORTS1(nsMIMEInfoAndroid, nsIMIMEInfo) NS_IMETHODIMP nsMIMEInfoAndroid::LaunchDefaultWithFile(nsIFile* aFile) @@ -67,24 +63,29 @@ nsMIMEInfoAndroid::LoadUriInternal(nsIURI * aURI) return NS_ERROR_FAILURE; } -already_AddRefed -nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType) -{ - mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge(); - if (!bridge) { - // we don't have access to the bridge, so just assume we can handle - // the protocol for now and let the system deal with it - return new nsMIMEInfoAndroid(aMimeType); - } +PRBool +nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType, + nsMIMEInfoAndroid** aMimeInfo) +{ + nsRefPtr info = new nsMIMEInfoAndroid(aMimeType); + mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge(); + // we don't have access to the bridge, so just assume we can handle + // the mime type for now and let the system deal with it + if (!bridge){ + info.forget(aMimeInfo); + return PR_TRUE; + } nsStringArray stringArray; bridge->GetHandlersForMimeType(nsCAutoString(aMimeType).get(), &stringArray); nsString empty = EmptyString(); PRInt32 len = stringArray.Count(); - if (len == 0) - return nsnull; - nsCOMPtr info = new nsMIMEInfoAndroid(aMimeType); + if (len == 0) { + info.forget(aMimeInfo); + return PR_FALSE; + } + for (jsize i = 0; i < len; i+=4) { nsAndroidHandlerApp* app = new nsAndroidHandlerApp(*stringArray[i], empty, *stringArray[i + 2], @@ -93,19 +94,20 @@ nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType) if (stringArray[i + 1] > 0) info->mPrefApp = app; } - nsCOMPtr ret = do_QueryInterface(info); - return ret.forget(); + + info.forget(aMimeInfo); + return PR_TRUE; } - -already_AddRefed -nsMIMEInfoAndroid::GetMimeInfoForFileExt(const nsACString& aFileExt) + +PRBool +nsMIMEInfoAndroid::GetMimeInfoForFileExt(const nsACString& aFileExt, + nsMIMEInfoAndroid **aMimeInfo) { - if (!mozilla::AndroidBridge::Bridge()) - return nsnull; nsCString mimeType; - mozilla::AndroidBridge::Bridge()->GetMimeTypeFromExtension(nsCString(aFileExt), mimeType); - - return GetMimeInfoForMimeType(mimeType); + if (mozilla::AndroidBridge::Bridge()) + mozilla::AndroidBridge::Bridge()-> + GetMimeTypeFromExtension(aFileExt, mimeType); + return GetMimeInfoForMimeType(mimeType, aMimeInfo); } nsresult @@ -116,11 +118,13 @@ nsMIMEInfoAndroid::GetMimeInfoForProtocol(const nsACString &aScheme, const nsString &empty = EmptyString(); const nsCString &emptyC = EmptyCString(); mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge(); + nsMIMEInfoAndroid *mimeinfo = new nsMIMEInfoAndroid(emptyC); + NS_ADDREF(*info = mimeinfo); + *found = PR_TRUE; + if (!bridge) { // we don't have access to the bridge, so just assume we can handle // the protocol for now and let the system deal with it - *found = PR_TRUE; - *info = new nsMIMEInfoAndroid(emptyC); return NS_OK; } @@ -130,21 +134,20 @@ nsMIMEInfoAndroid::GetMimeInfoForProtocol(const nsACString &aScheme, PRInt32 len = stringArray.Count(); if (len == 0) { + // Code that calls this requires an object regardless if the OS has + // something for us, so we return the empty object. *found = PR_FALSE; return NS_OK; } - *found = PR_TRUE; - nsMIMEInfoAndroid *mimeinfo = new nsMIMEInfoAndroid(emptyC); + for (jsize i = 0; i < len; i+=4) { nsAndroidHandlerApp* app = new nsAndroidHandlerApp(*stringArray[i], empty, *stringArray[i + 2], *stringArray[i + 3], emptyC); mimeinfo->mHandlerApps->AppendElement(app, PR_FALSE); - if (!stringArray[i + 1]->IsEmpty()) { + if (!stringArray[i + 1]->IsEmpty()) mimeinfo->mPrefApp = app; - } } - *info = mimeinfo; return NS_OK; } @@ -382,10 +385,9 @@ nsMIMEInfoAndroid::LaunchWithFile(nsIFile *aFile) nsMIMEInfoAndroid::nsMIMEInfoAndroid(const nsACString& aMIMEType) : mMimeType(aMIMEType), mAlwaysAsk(PR_TRUE), - mPrefAction(nsIMIMEInfo::useHelperApp), - mSystemChooser(this) + mPrefAction(nsIMIMEInfo::useHelperApp) { - mPrefApp = &mSystemChooser; + mPrefApp = new nsMIMEInfoAndroid::SystemChooser(this); nsresult rv; mHandlerApps = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); mHandlerApps->AppendElement(mPrefApp, PR_FALSE); diff --git a/uriloader/exthandler/android/nsMIMEInfoAndroid.h b/uriloader/exthandler/android/nsMIMEInfoAndroid.h index ce40a7c4403..5c3792e29cd 100644 --- a/uriloader/exthandler/android/nsMIMEInfoAndroid.h +++ b/uriloader/exthandler/android/nsMIMEInfoAndroid.h @@ -44,18 +44,21 @@ class nsMIMEInfoAndroid : public nsIMIMEInfo { public: - static already_AddRefed GetMimeInfoForMimeType(const nsACString& aMimeType); - static already_AddRefed GetMimeInfoForFileExt(const nsACString& aFileExt); - static nsresult GetMimeInfoForProtocol(const nsACString &aScheme, - PRBool *found, - nsIHandlerInfo **info); + static PRBool + GetMimeInfoForMimeType(const nsACString& aMimeType, + nsMIMEInfoAndroid** aMimeInfo); + static PRBool + GetMimeInfoForFileExt(const nsACString& aFileExt, + nsMIMEInfoAndroid** aMimeInfo); + static nsresult + GetMimeInfoForProtocol(const nsACString &aScheme, PRBool *found, + nsIHandlerInfo **info); NS_DECL_ISUPPORTS NS_DECL_NSIMIMEINFO NS_DECL_NSIHANDLERINFO -private: + nsMIMEInfoAndroid(const nsACString& aMIMEType); - virtual ~nsMIMEInfoAndroid(); protected: virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile); @@ -78,7 +81,6 @@ protected: nsMIMEInfoAndroid* mOuter; }; - SystemChooser mSystemChooser; }; #endif /* nsMIMEInfoAndroid_h */ diff --git a/uriloader/exthandler/android/nsOSHelperAppService.cpp b/uriloader/exthandler/android/nsOSHelperAppService.cpp index e17497fa4d0..1fa117dd477 100644 --- a/uriloader/exthandler/android/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/android/nsOSHelperAppService.cpp @@ -52,19 +52,23 @@ nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, PRBool* aFound) { - // XXX Bug 579388 - need to remote this - if (!mozilla::AndroidBridge::Bridge()) - return nsnull; - + nsRefPtr mimeInfo; *aFound = PR_FALSE; - already_AddRefed mimeInfo = - nsMIMEInfoAndroid::GetMimeInfoForMimeType(aMIMEType); - if (!mimeInfo.get()) - mimeInfo = nsMIMEInfoAndroid::GetMimeInfoForFileExt(aFileExt); + if (!aMIMEType.IsEmpty()) + *aFound = + nsMIMEInfoAndroid::GetMimeInfoForMimeType(aMIMEType, + getter_AddRefs(mimeInfo)); + if (!*aFound) + *aFound = + nsMIMEInfoAndroid::GetMimeInfoForFileExt(aFileExt, + getter_AddRefs(mimeInfo)); - *aFound = !!mimeInfo.get(); - - return mimeInfo; + // Code that calls this requires an object regardless if the OS has + // something for us, so we return the empty object. + if (!*aFound) + mimeInfo = new nsMIMEInfoAndroid(aMIMEType); + + return mimeInfo.forget(); } nsresult diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 7f57e17392d..14fb02d4cce 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -456,6 +456,22 @@ static nsresult GetDownloadDirectory(nsIFile **_directory) getter_AddRefs(dir)); NS_ENSURE_SUCCESS(rv, rv); } +#elif defined(ANDROID) + char* sdcard = getenv("EXTERNAL_STORAGE"); + nsresult rv; + if (sdcard) { + nsCOMPtr ldir; + rv = NS_NewNativeLocalFile(nsDependentCString(sdcard), + PR_TRUE, getter_AddRefs(ldir)); + NS_ENSURE_SUCCESS(rv, rv); + dir = ldir; + + } + else { + rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dir)); + NS_ENSURE_SUCCESS(rv, rv); + } + #else // On all other platforms, we default to the systems temporary directory. nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dir)); diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 02fe3664318..cbec8a590cf 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -343,7 +343,7 @@ AndroidBridge::OpenUriExternal(const nsACString& aUriSpec, const nsACString& aMi } void -AndroidBridge::GetMimeTypeFromExtension(const nsCString& aFileExt, nsCString& aMimeType) { +AndroidBridge::GetMimeTypeFromExtension(const nsACString& aFileExt, nsCString& aMimeType) { AutoLocalJNIFrame jniFrame; NS_ConvertUTF8toUTF16 wFileExt(aFileExt); jstring jstrExt = mJNIEnv->NewString(wFileExt.get(), wFileExt.Length()); diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index 5c3a7ddab86..fdd298f489b 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -122,7 +122,7 @@ public: const nsAString& aPackageName = EmptyString(), const nsAString& aClassName = EmptyString()); - void GetMimeTypeFromExtension(const nsCString& aFileExt, nsCString& aMimeType); + void GetMimeTypeFromExtension(const nsACString& aFileExt, nsCString& aMimeType); void MoveTaskToBack(); diff --git a/widget/src/android/AndroidJavaWrappers.cpp b/widget/src/android/AndroidJavaWrappers.cpp index b1d83a9a941..559c16cbc42 100644 --- a/widget/src/android/AndroidJavaWrappers.cpp +++ b/widget/src/android/AndroidJavaWrappers.cpp @@ -448,8 +448,9 @@ nsJNIString::nsJNIString(jstring jstr) return; } const jchar* jCharPtr = JNI()->GetStringChars(jstr, false); + int len = JNI()->GetStringLength(jstr); nsresult rv; - Assign(jCharPtr); + Assign(jCharPtr, len); JNI()->ReleaseStringChars(jstr, jCharPtr); } diff --git a/widget/src/build/nsWinWidgetFactory.cpp b/widget/src/build/nsWinWidgetFactory.cpp index 660b561d049..a1dd5e2c674 100644 --- a/widget/src/build/nsWinWidgetFactory.cpp +++ b/widget/src/build/nsWinWidgetFactory.cpp @@ -79,6 +79,8 @@ #include "nsPrintSession.h" #endif +using namespace mozilla::widget; + NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(ChildWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker) @@ -91,7 +93,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7 -using namespace mozilla::widget; NS_GENERIC_FACTORY_CONSTRUCTOR(WinTaskbar) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListBuilder) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListItem) diff --git a/widget/src/os2/nsWindow.cpp b/widget/src/os2/nsWindow.cpp index 491e5de4c06..64d7190e396 100644 --- a/widget/src/os2/nsWindow.cpp +++ b/widget/src/os2/nsWindow.cpp @@ -222,6 +222,7 @@ nsWindow::nsWindow() : nsBaseWidget() mOnDestroyCalled = PR_FALSE; mIsDestroying = PR_FALSE; mInSetFocus = PR_FALSE; + mNoPaint = PR_FALSE; mDragHps = 0; mDragStatus = 0; mClipWnd = 0; @@ -399,6 +400,15 @@ NS_METHOD nsWindow::Create(nsIWidget* aParent, if (aInitData) { mWindowType = aInitData->mWindowType; mBorderStyle = aInitData->mBorderStyle; + + // Suppress creation of a Thebes surface for windows that will never + // be painted because they're always covered by another window. + if (mWindowType == eWindowType_toplevel || + mWindowType == eWindowType_invisible || + (mWindowType == eWindowType_child && + aInitData->mContentType == eContentTypeContent)) { + mNoPaint = PR_TRUE; + } } // For toplevel windows, create an instance of our helper class, @@ -701,6 +711,19 @@ gfxASurface* nsWindow::GetThebesSurface() return mThebesSurface; } +//----------------------------------------------------------------------------- +// Internal-only method that suppresses creation of a Thebes surface +// for windows that aren't supposed to be visible. If one was created +// by an external call to GetThebesSurface(), it will be returned. + +gfxASurface* nsWindow::ConfirmThebesSurface() +{ + if (!mThebesSurface && !mNoPaint && mWnd) { + mThebesSurface = new gfxOS2Surface(mWnd); + } + return mThebesSurface; +} + //----------------------------------------------------------------------------- // Return some native data according to aDataType. @@ -1970,12 +1993,10 @@ PRBool nsWindow::OnReposition(PSWP pSwp) mBounds.width = pSwp->cx; mBounds.height = pSwp->cy; - // Resize the thebes surface to the new size. The first time we do - // a resize, we may need to create a thebes surface for the window. - if (!mThebesSurface) { - mThebesSurface = new gfxOS2Surface(mWnd); + // If the window is supposed to have a thebes surface, resize it. + if (ConfirmThebesSurface()) { + mThebesSurface->Resize(gfxIntSize(mBounds.width, mBounds.height)); } - mThebesSurface->Resize(gfxIntSize(mBounds.width, mBounds.height)); result = DispatchResizeEvent(mBounds.width, mBounds.height); } @@ -2026,8 +2047,16 @@ do { WinQueryUpdateRegion(mWnd, hrgn); WinBeginPaint(mWnd, hPS, &rcl); - // Exit if the update rect is empty or mThebesSurface is null. - if (WinIsRectEmpty(0, &rcl) || !GetThebesSurface()) { + // Exit if the update rect is empty. + if (WinIsRectEmpty(0, &rcl)) { + break; + } + + // Exit if a thebes surface can not/should not be created, + // but first fill the area with the default background color + // to erase any visual artifacts. + if (!ConfirmThebesSurface()) { + WinDrawBorder(hPS, &rcl, 0, 0, 0, 0, DB_INTERIOR | DB_AREAATTRS); break; } @@ -2044,6 +2073,15 @@ do { nsRefPtr thebesContext = new gfxContext(mThebesSurface); thebesContext->SetFlag(gfxContext::FLAG_DESTINED_FOR_SCREEN); + // Intersect the update region with the paint rectangle to clip areas + // that aren't visible (e.g. offscreen or covered by another window). + HRGN hrgnPaint; + hrgnPaint = GpiCreateRegion(hPS, 1, &rcl); + if (hrgnPaint) { + GpiCombineRegion(hPS, hrgn, hrgn, hrgnPaint, CRGN_AND); + GpiDestroyRegion(hPS, hrgnPaint); + } + // See how many rects comprise the update region. If there are 8 // or fewer, update them individually. If there are more or the call // failed, update the bounding rectangle returned by WinBeginPaint(). diff --git a/widget/src/os2/nsWindow.h b/widget/src/os2/nsWindow.h index bb5e3aab23c..b533a0e9552 100644 --- a/widget/src/os2/nsWindow.h +++ b/widget/src/os2/nsWindow.h @@ -228,6 +228,7 @@ protected: HWND aParentWnd, const nsIntRect& aRect, nsWidgetInitData* aInitData); + gfxASurface* ConfirmThebesSurface(); HWND GetMainWindow(); static nsWindow* GetNSWindowPtr(HWND aWnd); static PRBool SetNSWindowPtr(HWND aWnd, nsWindow* aPtr); @@ -286,6 +287,7 @@ protected: PRInt32 mWindowState; // current nsWindowState_* value PRBool mIsDestroying; // in destructor PRBool mInSetFocus; // prevent recursive calls + PRBool mNoPaint; // true if window is never visible HPS mDragHps; // retrieved by DrgGetPS() during a drag PRUint32 mDragStatus; // set when object is being dragged over HWND mClipWnd; // used to clip plugin windows diff --git a/widget/src/windows/nsUXThemeData.cpp b/widget/src/windows/nsUXThemeData.cpp index 52113a8c6ed..ca5e42e317c 100644 --- a/widget/src/windows/nsUXThemeData.cpp +++ b/widget/src/windows/nsUXThemeData.cpp @@ -193,12 +193,14 @@ nsUXThemeData::GetThemeDLL() { return sThemeDLL; } +#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN HMODULE nsUXThemeData::GetDwmDLL() { if (!sDwmDLL && sIsVistaOrLater) sDwmDLL = ::LoadLibraryW(kDwmLibraryName); return sDwmDLL; } +#endif const wchar_t *nsUXThemeData::GetClassName(nsUXThemeClass cls) { switch(cls) { diff --git a/widget/src/windows/nsUXThemeData.h b/widget/src/windows/nsUXThemeData.h index 0db117ea073..9f422e796c1 100644 --- a/widget/src/windows/nsUXThemeData.h +++ b/widget/src/windows/nsUXThemeData.h @@ -121,7 +121,9 @@ public: static void Invalidate(); static HANDLE GetTheme(nsUXThemeClass cls); static HMODULE GetThemeDLL(); +#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN static HMODULE GetDwmDLL(); +#endif // nsWindow calls this to update desktop settings info static void InitTitlebarInfo();