Merge last PGO-green changeset from mozilla-inbound to mozilla-central

This commit is contained in:
Ed Morley 2011-12-23 23:54:24 +00:00
commit 9ef7070e0d
217 changed files with 5945 additions and 3163 deletions

View File

@ -35,7 +35,9 @@
}
.tail-up {
-moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 / 26px 56px 22px 18px round stretch;
border-width: 26px 56px 22px 18px;
border-style: solid;
-moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 fill round stretch;
}
/* tail-down uses the old styling; it doesn't look as good as the new styling,
@ -44,7 +46,9 @@
installed Test Pilot from AMO, they should get the new styling, similar
to .tail-up! */
.tail-down {
-moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 / 26px 50px 22px 18px repeat;
border-width: 26px 50px 22px 18px;
border-style: solid;
-moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 fill repeat;
color: white;
}

View File

@ -1239,8 +1239,9 @@ toolbar[iconsize="small"] #feed-button {
background-clip: padding-box;
padding-left: 4px;
border-radius: 2.5px 0 0 2.5px;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0;
-moz-margin-end: -8px;
border-width: 0 8px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill; -moz-margin-end: -8px;
margin-top: -1px;
margin-bottom: -1px;
}
@ -1498,7 +1499,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
color: inherit;
margin: 0;
padding: 0;
-moz-border-image: url(tabbrowser/tab.png) 4 5 3 6 / 4px 5px 3px 6px repeat stretch;
border-width: 4px 5px 3px 6px;
border-style: solid;
-moz-border-image: url(tabbrowser/tab.png) 4 5 3 6 fill repeat stretch;
border-radius: 10px 8px 0 0;
min-height: 25px; /* reserve space for the sometimes hidden close button */
}
@ -1670,12 +1673,16 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0;
border-width: 0 2px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
}
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr),
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) {
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 0 0 2px;
border-width: 0 0 0 2px;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
}
#TabsToolbar .toolbarbutton-1 > .toolbarbutton-icon,

View File

@ -1856,30 +1856,30 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
@TABSONTOP_TAB_STACK@ > .tab-content,
@TABSONTOP_NEWTAB_BUTTON@ > .toolbarbutton-icon {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-normal-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-normal-active.png) 0 11 fill repeat stretch;
}
@TABSONTOP_TAB@:hover > .tab-stack > .tab-content:not([selected="true"]),
@TABSONTOP_NEWTAB_BUTTON@:hover > .toolbarbutton-icon {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-hover-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-hover-active.png) 0 11 fill repeat stretch;
}
@TABSONTOP_TAB_STACK@ > .tab-content[selected="true"] {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-selected-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-selected-active.png) 0 11 fill repeat stretch;
}
@TABSONBOTTOM_TAB_STACK@ > .tab-content,
@TABSONBOTTOM_NEWTAB_BUTTON@ > .toolbarbutton-icon {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-normal-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-normal-active.png) 0 11 fill repeat stretch;
}
@TABSONBOTTOM_TAB@:hover > .tab-stack > .tab-content:not([selected="true"]),
@TABSONBOTTOM_NEWTAB_BUTTON@:hover > .toolbarbutton-icon {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-hover-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-hover-active.png) 0 11 fill repeat stretch;
}
@TABSONBOTTOM_TAB_STACK@ > .tab-content[selected="true"] {
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-selected-active.png) 0 11 repeat stretch;
-moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-selected-active.png) 0 11 fill repeat stretch;
}
/* preloading hack */
@ -2090,12 +2090,16 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0;
border-width: 0 2px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
}
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr),
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) {
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 0 0 2px;
border-width: 0 0 0 2px;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
}
/**
@ -2310,7 +2314,9 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
background-clip: padding-box;
padding-left: 3px;
border-radius: 2px 0 0 2px;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0;
border-width: 0 8px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill;
-moz-margin-end: -8px;
}

View File

@ -1819,7 +1819,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
background-repeat: no-repeat;
margin: 0;
padding: 2px 0 4px;
-moz-border-image: url(tabbrowser/tab.png) 4 3 0 / 4px 3px 0 repeat stretch;
border-width: 4px 3px 0;
border-style: solid;
-moz-border-image: url(tabbrowser/tab.png) 4 3 0 fill repeat stretch;
border-radius: 0;
}
@ -2014,7 +2016,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]),
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]) {
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0;
border-width: 0 2px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
}
.tabs-newtab-button > .toolbarbutton-icon {
@ -2312,7 +2316,9 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
background-clip: padding-box;
padding-left: 3px;
border-radius: 2.5px 0 0 2.5px;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0;
border-width: 0 8px 0 0;
border-style: solid;
-moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill;
-moz-margin-end: -8px;
}

View File

@ -0,0 +1,60 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
import java.util.List;
public interface Actions {
public enum SpecialKey {
DOWN, UP, LEFT, RIGHT, ENTER
}
/**
* Waits for a gecko event to be sent from the Gecko instance.
*
* @param geckoEvent The geckoEvent JSONObject's type
*/
void waitForGeckoEvent(String geckoEvent);
// Send the string kewsToSend to the application
void sendKeys(String keysToSend);
//Send any of the above keys to the element
void sendSpecialKey(SpecialKey button);
void drag(int startingX, int endingX, int startingY, int endingY);
}

View File

@ -0,0 +1,19 @@
#filter substitution
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mozilla.roboexample.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="@ANDROID_PACKAGE_NAME@" />
<application
android:label="@string/app_name" >
<uses-library android:name="android.test.runner" />
</application>
</manifest>

View File

@ -0,0 +1,78 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
import java.util.List;
public interface Driver {
/**
* Find the first Element using the given method.
*
* @param name The name of the element
* @return The first matching element on the current context
* @throws RoboCopException If no matching elements are found
*/
Element findElement(String name);
/**
* Sets up scroll handling so that data is received from the extension.
*/
void setupScrollHandling();
int getPageHeight();
int getScrollHeight();
int getHeight();
int getGeckoTop();
int getGeckoLeft();
int getGeckoWidth();
int getGeckoHeight();
void startFrameRecording();
int stopFrameRecording();
void dumpLog(String message);
void setLogFile(String filename);
void ok(boolean condition, String name, String diag);
void is(Object a, Object b, String name);
void isnot(Object a, Object b, String name);
void todo(boolean condition, String name, String diag);
void todo_is(Object a, Object b, String name);
void todo_isnot(Object a, Object b, String name);
void info(String name, String message);
}

View File

@ -0,0 +1,49 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
public interface Element {
//Click on the element
void click();
//Returns true if the element is currently displayed
boolean isDisplayed();
//Returns the text currently displayed on the element.
String getText();
}

View File

@ -0,0 +1,197 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;
import java.lang.Class;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.Long;
import android.app.Activity;
import android.app.Instrumentation;
import android.util.Log;
import android.view.View;
import android.view.KeyEvent;
import java.util.concurrent.SynchronousQueue;
import org.json.*;
import com.jayway.android.robotium.solo.Solo;
public class FennecNativeActions implements Actions {
// Map of IDs to element names.
private Solo solo;
private Instrumentation instr;
// Objects for reflexive access of fennec classes.
private ClassLoader classLoader;
private Class gel;
private Class ge;
private Class gas;
private Method registerGEL;
private Method unregisterGEL;
private Method sendGE;
// If waiting for an event.
private SynchronousQueue waitqueue = new SynchronousQueue<Boolean>();
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
this.solo = robocop;
this.instr = instrumentation;
// Set up reflexive access of java classes and methods.
try {
classLoader = activity.getClassLoader();
gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
Class [] parameters = new Class[2];
parameters[0] = String.class;
parameters[1] = gel;
registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
parameters = new Class[1];
parameters[0] = ge;
sendGE = gas.getMethod("sendEventToGecko", parameters);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
class wakeInvocationHandler implements InvocationHandler {
public wakeInvocationHandler(){};
public Object invoke(Object proxy, Method method, Object[] args) {
String methodName = method.getName();
//Depending on the method, return a completely different type.
if(methodName.equals("toString")) {
return "wakeInvocationHandler";
}
if(methodName.equals("equals")) {
return this == args[0];
}
if(methodName.equals("clone")) {
return this;
}
if(methodName.equals("hashCode")) {
return 314;
}
Log.i("Robocop", "Waking up on "+methodName);
waitqueue.offer(new Boolean(true));
return null;
}
}
public void waitForGeckoEvent(String geckoEvent) {
Log.i("Robocop", "waiting for "+geckoEvent);
try {
Class [] interfaces = new Class[1];
interfaces[0] = gel;
Object[] finalParams = new Object[2];
finalParams[0] = geckoEvent;
wakeInvocationHandler wIH = new wakeInvocationHandler();
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
finalParams[1] = proxy;
registerGEL.invoke(null, finalParams);
waitqueue.take();
unregisterGEL.invoke(null, finalParams);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("Robocop", "wait ends for: "+geckoEvent);
}
public void sendSpecialKey(SpecialKey button) {
switch( button) {
case DOWN:
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
break;
case UP:
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
break;
case LEFT:
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
break;
case RIGHT:
instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
break;
case ENTER:
instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
break;
default:
break;
}
}
@Override
public void sendKeys(String input) {
instr.sendStringSync(input);
}
public void drag(int startingX, int endingX, int startingY, int endingY) {
solo.drag(startingX, endingX, startingY, endingY, 10);
}
}

View File

@ -0,0 +1,425 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;
import java.lang.Class;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.Long;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import org.json.*;
import com.jayway.android.robotium.solo.Solo;
public class FennecNativeDriver implements Driver {
// Map of IDs to element names.
private HashMap locators = null;
private Activity activity;
private Solo solo;
private String logFile;
// Objects for reflexive access of fennec classes.
private ClassLoader classLoader;
private Class gel;
private Class ge;
private Class gas;
private Method registerGEL;
private Method unregisterGEL;
private Method sendGE;
private Method _startFrameRecording;
private Method _stopFrameRecording;
private LinkedList<testInfo> testList = new LinkedList<testInfo>();
public FennecNativeDriver(Activity activity, Solo robocop){
this.activity = activity;
this.solo = robocop;
// Set up table of fennec_ids.
locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
// Set up reflexive access of java classes and methods.
try {
classLoader = activity.getClassLoader();
gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
Class [] parameters = new Class[2];
parameters[0] = String.class;
parameters[1] = gel;
registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
parameters = new Class[1];
parameters[0] = ge;
sendGE = gas.getMethod("sendEventToGecko", parameters);
Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
_startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
_stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
//Information on the location of the Gecko Frame.
private boolean geckoInfo = false;
private int geckoTop = 100;
private int geckoLeft = 0;
private int geckoHeight= 700;
private int geckoWidth = 1024;
private void getGeckoInfo() {
View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
if (geckoLayout != null) {
geckoTop = geckoLayout.getTop();
geckoLeft = geckoLayout.getLeft();
geckoWidth = geckoLayout.getWidth();
geckoHeight = geckoLayout.getHeight();
geckoInfo = true;
}
}
public int getGeckoTop() {
if(!geckoInfo) {
getGeckoInfo();
}
return geckoTop;
}
public int getGeckoLeft() {
if(!geckoInfo) {
getGeckoInfo();
}
return geckoLeft;
}
public int getGeckoHeight() {
if(!geckoInfo) {
getGeckoInfo();
}
return geckoHeight;
}
public int getGeckoWidth() {
if(!geckoInfo) {
getGeckoInfo();
}
return geckoWidth;
}
@Override
public Element findElement(String name) {
if (name == null)
throw new IllegalArgumentException("Can not findElements when passed a null");
if (locators.containsKey(name)){
return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo);
}
throw new RoboCopException("Element does not exist in the list");
}
public void startFrameRecording() {
try {
Object [] params = null;
_startFrameRecording.invoke(null, params);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public int stopFrameRecording() {
Class [] parameters = new Class[1];
parameters[0] = null;
List frames;
try {
Object [] params = null;
frames = (List)_stopFrameRecording.invoke(null, params);
Object [] framearray = frames.toArray();
Long last = new Long(0);
Long threshold = new Long(17);
int numDelays = 0;
for (int i=0; i < framearray.length; i++) {
Long val = (Long)framearray[i];
if ((val - last) > threshold) {
numDelays++;
}
last = val;
}
return numDelays;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
class scrollHandler implements InvocationHandler {
public scrollHandler(){};
public Object invoke(Object proxy, Method method, Object[] args) {
try{
//Disect the JSON object into the appropriate variables
JSONObject jo = ((JSONObject)args[1]);
scrollHeight = jo.getInt("y");
height = jo.getInt("cheight");
//We don't want a height of 0. That means it's a bad response.
if( height > 0) {
pageHeight = jo.getInt("height");
}
} catch( Throwable e) {
Log.i("Robocop", "WARNING: ScrollReceived, but read wrong!");
}
return null;
}
}
public int getScrollHeight() {
return scrollHeight;
}
public int getPageHeight() {
return pageHeight;
}
public int getHeight() {
return height;
}
public int height=0;
public int scrollHeight=0;
public int pageHeight=10;
public void setupScrollHandling() {
//Setup scrollHandler to catch "robocop:scroll" events.
try {
Class [] interfaces = new Class[1];
interfaces[0] = gel;
Object[] finalParams = new Object[2];
finalParams[0] = "robocop:scroll";
finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler());
registerGEL.invoke(null, finalParams);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//Takes a filename, loads the file,
// and returns a string version of the entire file.
public static String getFile(String filename)
{
File file = new File(filename);
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
} catch(IOException e) {
e.printStackTrace();
}
return text.toString();
}
// Write information to a logfile and logcat
public void dumpLog(String message)
{
File file = new File(logFile);
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(logFile, true));
bw.write(message);
bw.newLine();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.flush();
bw.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
Log.i("Robocop", message);
}
// Set the filename used for dumpLog.
public void setLogFile(String filename)
{
logFile = filename;
}
// Takes a string of "key=value" pairs split by \n and creates a hash table.
public static HashMap convertTextToTable(String data)
{
HashMap retVal = new HashMap();
String[] lines = data.split("\n");
for (int i = 0; i < lines.length; i++) {
String[] parts = lines[i].split("=");
retVal.put(parts[0].trim(), parts[1].trim());
}
return retVal;
}
class testInfo {
public boolean result;
public String name;
public String diag;
public boolean todo;
public testInfo(boolean r, String n, String d, boolean t) {
result = r;
name = n;
diag = d;
todo = t;
}
}
private void _logResult(testInfo test, String passString, String failString)
{
boolean isError = true;
String resultString = failString;
if(test.result || test.todo){
isError = false;
}
if(test.result)
{
resultString = passString;
}
String diag= test.name;
if(test.diag!=null) diag+= " - " + test.diag;
String message = resultString + " | " + "ROBOCOP" + " | " + diag;
if(isError) {
dumpLog(message);
}
else {
dumpLog(message);
}
}
public void ok(boolean condition, String name, String diag) {
testInfo test = new testInfo(condition, name, diag, false);
_logResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
testList.add(test);
}
public void is(Object a, Object b, String name) {
boolean pass = a.equals(b);
String diag = "got " + a.toString() + ", expected " + b.toString();
if(pass) {
diag = a.toString() + " should equal " + b.toString();
}
ok(pass, name, diag);
}
public void isnot(Object a, Object b, String name) {
boolean pass = !a.equals(b);
String diag = "didn't expect " + a.toString() + ", but got it";
if(pass) {
diag = a.toString() + " should not equal " + b.toString();
}
ok(pass, name, diag);
}
public void todo(boolean condition, String name, String diag) {
testInfo test = new testInfo(condition, name, diag, true);
_logResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
testList.add(test);
}
public void todo_is(Object a, Object b, String name) {
boolean pass = a.equals(b);
String diag = "got " + a.toString() + ", expected " + b.toString();
if(pass) {
diag = a.toString() + " should equal " + b.toString();
}
todo(pass, name, diag);
}
public void todo_isnot(Object a, Object b, String name) {
boolean pass = !a.equals(b);
String diag = "didn't expect " + a.toString() + ", but got it";
if(pass) {
diag = a.toString() + " should not equal " + b.toString();
}
todo(pass, name, diag);
}
public void info(String name, String message) {
testInfo test = new testInfo(true, name, message, false);
_logResult(test, "TEST-INFO", "INFO FAILED?");
}
}

View File

@ -0,0 +1,149 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
import java.util.List;
import android.app.Activity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextSwitcher;
import android.app.Instrumentation;
import android.util.Log;
import com.jayway.android.robotium.solo.Solo;
import java.util.concurrent.SynchronousQueue;
public class FennecNativeElement implements Element {
private Integer id;
private Activity currentActivity;
private Solo robocop;
public FennecNativeElement(Integer id, Activity activity, Solo solo){
this.id = id;
robocop = solo;
currentActivity = activity;
}
public Integer getId() {
return id;
}
@Override
public void click() {
final SynchronousQueue syncQueue = new SynchronousQueue();
currentActivity = robocop.getCurrentActivity();
currentActivity.runOnUiThread(
new Runnable() {
public void run() {
View view = (View)currentActivity.findViewById(id);
if(view != null) {
view.performClick();
} else {
throw new RoboCopException("click: unable to find view "+id);
}
syncQueue.offer(new Object());
}
});
try {
syncQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Object text;
private Activity elementActivity;
@Override
public String getText() {
elementActivity = robocop.getCurrentActivity();
final SynchronousQueue syncQueue = new SynchronousQueue();
elementActivity.runOnUiThread(
new Runnable() {
public void run() {
View v = elementActivity.findViewById(id);
if(v instanceof EditText) {
EditText et = (EditText)v;
text = et.getEditableText();
}else if(v instanceof TextSwitcher) {
TextSwitcher ts = (TextSwitcher)v;
ts.getNextView();
text = ((TextView)ts.getCurrentView()).getText();
}else if(v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup)v;
for(int i = 0; i < vg.getChildCount(); i++) {
if(vg.getChildAt(i) instanceof TextView) {
text = ((TextView)vg.getChildAt(i)).getText();
}
} //end of for
} else if(v instanceof TextView) {
text = ((TextView)v).getText();
} else if(v == null) {
throw new RoboCopException("getText: unable to find view "+id);
} else {
throw new RoboCopException("getText: unhandled type for view "+id);
}
syncQueue.offer(new Object());
} // end of run() method definition
} // end of anonymous Runnable object instantiation
);
try {
//Wait for the UiThread code to finish running
syncQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(text == null) {
throw new RoboCopException("getText: Text is null for view "+id);
}
return text.toString();
}
@Override
public boolean isDisplayed() {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,136 @@
# ***** 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 the Android sutagent for testing.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Clint Talbert <ctalbert@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
TESTPATH = $(topsrcdir)/mobile/android/base/tests
include $(DEPTH)/config/autoconf.mk
MODULE = robocop
ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.0.jar
JAVAFILES = \
R.java \
_JAVA_HARNESS = \
Driver.java \
Element.java \
Actions.java \
FennecNativeElement.java \
RoboCopException.java \
FennecNativeDriver.java \
FennecNativeActions.java \
_JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
_ROBOCOP_TOOLS = \
$(TESTPATH)/robocop.ini \
parse_ids.py \
$(NULL)
GARBAGE += \
AndroidManifest.xml \
_JAVA_TESTS \
_JAVA_HARNESS \
classes.dex \
robocop.apk \
robocop.ap_ \
robocop-unsigned-unaligned.apk \
robocop-unaligned.apk \
$(NULL)
DEFINES += \
-DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
$(NULL)
GARBAGE_DIRS += res
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH)
include $(topsrcdir)/config/rules.mk
# Override rules.mk java flags with the android specific ones
include $(topsrcdir)/config/android-common.mk
$(_JAVA_HARNESS): % : %.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
AndroidManifest.xml: % : %.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
$(_JAVA_TESTS): % : $(TESTPATH)/%.in
$(NSINSTALL) -D $(DEPTH)/mobile/android/base/tests
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $(DEPTH)/mobile/android/base/tests/$@
$(_ROBOCOP_TOOLS):
cp $(TESTPATH)/robocop.ini robocop.ini
cp $(srcdir)/parse_ids.txt parse_ids.txt
tools:: robocop.apk
classes.dex: robocop.ap_
classes.dex: $(_ROBOCOP_TOOLS)
classes.dex: $(_JAVA_HARNESS)
classes.dex: $(_JAVA_TESTS)
classes.dex: $(TEST_FILES)
$(NSINSTALL) -D classes
$(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(addprefix $(DEPTH)/mobile/android/base/tests/,$(_JAVA_TESTS))
$(DX) --dex --output=$@ classes $(ROBOTIUM_PATH)
robocop.ap_: AndroidManifest.xml
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -I . -S res -F $@ -J ./
robocop-unsigned-unaligned.apk: robocop.ap_ classes.dex
$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex
robocop-unaligned.apk: robocop-unsigned-unaligned.apk
cp robocop-unsigned-unaligned.apk $@
jarsigner -keystore ~/.android/debug.keystore -storepass android -keypass android $@ androiddebugkey
robocop.apk: robocop-unaligned.apk
$(ZIPALIGN) -f -v 4 robocop-unaligned.apk $@
cp $(TESTPATH)/robocop.ini robocop.ini
cp $(srcdir)/parse_ids.py parse_ids.py
export::
$(NSINSTALL) -D res
@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/robocop/res && tar -xf -)

View File

@ -0,0 +1,59 @@
#filter substitution
/* ***** 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 Firefox Mobile Test Framework.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Trevor Fairey <tnfairey@gmail.com>
* David Burns <dburns@mozilla.com>
* Joel Maher <joel.maher@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
package @ANDROID_PACKAGE_NAME@;
public class RoboCopException extends RuntimeException {
public RoboCopException(){
super();
}
public RoboCopException(String message){
super(message);
}
public RoboCopException(Throwable cause){
super(cause);
}
public RoboCopException(String message, Throwable cause){
super(message, cause);
}
}

View File

@ -0,0 +1,60 @@
import re
import os
import sys
import optparse
def getFile(filename):
fHandle = open(filename, 'r')
data = fHandle.read()
fHandle.close()
return data
def findIDs(data):
start_function = False
reID = re.compile('.*public static final class id {.*')
reEnd = re.compile('.*}.*')
idlist = []
for line in data.split('\n'):
if reEnd.match(line):
start_function = False
if start_function:
id_value = line.split(' ')[-1]
idlist.append(id_value.split(';')[0].split('='))
if reID.match(line):
start_function = True
return idlist
def printIDs(outputFile, idlist):
fOutput = open(outputFile, 'w')
for item in idlist:
fOutput.write("%s=%s\n" % (item[0], item[1]))
fOutput.close()
def main(args=sys.argv[1:]):
parser = optparse.OptionParser()
parser.add_option('-o', '--output', dest='outputFile', default='',
help="output file with the id=value pairs")
parser.add_option('-i', '--input', dest='inputFile', default='',
help="filename of the input R.java file")
options, args = parser.parse_args(args)
if options.inputFile == '':
print "Error: please provide input file: -i <filename>"
sys.exit(1)
if options.outputFile == '':
print "Error: please provide output file: -o <filename>"
sys.exit(1)
data = getFile(os.path.abspath(options.inputFile));
idlist = findIDs(data)
printIDs(os.path.abspath(options.outputFile), idlist)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Roboexample</string>
</resources>

View File

@ -4968,18 +4968,16 @@ nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin)
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = scheme + NS_LITERAL_CSTRING("://") + host;
// If needed, append the port
PRInt32 port;
PRInt32 port = -1;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(':');
aOrigin.AppendInt(port);
}
}
if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
port = -1;
nsCString hostPort;
rv = NS_GenerateHostPort(host, port, hostPort);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
}
else {
aOrigin.AssignLiteral("null");
@ -5028,18 +5026,17 @@ nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin)
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = NS_ConvertUTF8toUTF16(scheme + NS_LITERAL_CSTRING("://") + host);
// If needed, append the port
PRInt32 port;
PRInt32 port = -1;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(':');
aOrigin.AppendInt(port);
}
}
if (port != -1 && port == NS_GetDefaultPort(scheme.get()))
port = -1;
nsCString hostPort;
rv = NS_GenerateHostPort(host, port, hostPort);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = NS_ConvertUTF8toUTF16(
scheme + NS_LITERAL_CSTRING("://") + hostPort);
}
else {
aOrigin.AssignLiteral("null");

View File

@ -51,7 +51,7 @@
* http://www.w3.org/TR/DOM-Level-2-Style
*/
[builtinclass, scriptable, uuid(0a6fc4c6-a62a-4f52-9ab6-3d398b958843)]
[builtinclass, scriptable, uuid(b477527a-a6f4-4f86-a16b-563e602e930a)]
interface nsIDOMCSS2Properties : nsISupports
{
attribute DOMString background;
@ -767,4 +767,19 @@ interface nsIDOMCSS2Properties : nsISupports
attribute DOMString MozTextSizeAdjust;
// raises(DOMException) on setting
attribute DOMString MozBorderImageSource;
// raises(DOMException) on setting
attribute DOMString MozBorderImageSlice;
// raises(DOMException) on setting
attribute DOMString MozBorderImageWidth;
// raises(DOMException) on setting
attribute DOMString MozBorderImageOutset;
// raises(DOMException) on setting
attribute DOMString MozBorderImageRepeat;
// raises(DOMException) on setting
};

View File

@ -133,15 +133,8 @@ RES_LAYOUT = \
RES_VALUES = res/values/colors.xml res/values/themes.xml
AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/)
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd
DEFAULT_STRINGSPATH = locales/en-US/android_strings.dtd
LOCALIZED_BRANDPATH = $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd
LOCALIZED_STRINGSPATH = $(DEPTH)/dist/bin/chrome/android-res/res/values-$(AB_CD)/android_strings.dtd
ifdef MOZ_CRASHREPORTER
PROCESSEDJAVAFILES += CrashReporter.java
MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/crash_reporter.png
@ -154,10 +147,6 @@ MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTOR
include $(topsrcdir)/config/rules.mk
ifneq ($(AB_CD),en-US)
LOCALIZED_STRINGS_XML = res/values-$(AB_rCD)/strings.xml
endif
# Override the Java settings with some specific android settings
include $(topsrcdir)/config/android-common.mk
@ -200,23 +189,5 @@ R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable
gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
res/values/strings.xml: $(DEFAULT_BRANDPATH) $(DEFAULT_STRINGSPATH)
mkdir -p res/values
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(DEFAULT_BRANDPATH)" \
-DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \
$(srcdir)/strings.xml.in \
> $@
res/values-$(AB_rCD)/strings.xml: $(LOCALIZED_BRANDPATH) $(LOCALIZED_STRINGSPATH)
mkdir -p res/values-$(AB_rCD)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(call core_abspath,$(LOCALIZED_BRANDPATH))" \
-DSTRINGSPATH="$(call core_abspath,$(LOCALIZED_STRINGSPATH))" \
$(srcdir)/strings.xml.in \
> $@
chrome:: $(LOCALIZED_STRINGS_XML)
libs:: classes.dex
$(INSTALL) classes.dex $(FINAL_TARGET)

View File

@ -43,6 +43,33 @@ relativesrcdir = embedding/android/locales
include $(DEPTH)/config/autoconf.mk
# special case some locale codes, he and id
# http://code.google.com/p/android/issues/detail?id=3639
AB_rCD = $(if $(filter he, $(AB_CD)),iw,$(if $(filter id, $(AB_CD)),in,$(subst -,-r,$(AB_CD))))
STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd))
ifeq (,$(XPI_NAME))
BRANDPATH = $(call core_abspath,$(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
else
BRANDPATH = $(call core_abspath,$(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
endif
DEFINES += -DAB_CD=$(AB_CD)
libs realchrome:: ../res/values/strings.xml ;
chrome-%:: AB_CD=$*
chrome-%::
@$(MAKE) ../res/values-$(AB_rCD)/strings.xml AB_CD=$*
%/strings.xml: FORCE
$(NSINSTALL) -D $*
# we don't have branding yet, but we need it. Call it explicitly
@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales realchrome
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(BRANDPATH)" \
-DSTRINGSPATH="$(STRINGSPATH)" \
$(srcdir)/../strings.xml.in \
> $@
include $(topsrcdir)/config/rules.mk

View File

@ -1,4 +0,0 @@
#filter substitution
android-res.jar:
res/values-@AB_CD@/android_strings.dtd (%android_strings.dtd)

View File

@ -78,7 +78,7 @@ DEFINES += -DDISABLE_FILE_FACE -DDISABLE_TRACING -DDISABLE_SEGCACHE
# provide a custom header that overrides malloc() and friends,
# to ensure safe OOM handling
DEFINES += -DGR_CUSTOM_HEADER="\"MozGrMalloc.h\""
DEFINES += -DGR2_CUSTOM_HEADER="\"MozGrMalloc.h\""
# Filter out -pedantic so that direct_machine.cpp can use computed goto
CXXFLAGS := $(filter-out -pedantic,$(CXXFLAGS))

View File

@ -1,6 +1,6 @@
This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
Current revision: r74
Current revision: r77
Applied local patches:
ots-fix-vc10.patch - workaround for VS10 STL wrappers (bug 602558)

View File

@ -57,7 +57,7 @@ bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
}
if (index->count == 0) {
// An empty INDEX.
index->offset_to_next = table->offset() + sizeof(index->count);
index->offset_to_next = table->offset();
return true;
}

View File

@ -34,12 +34,6 @@ template<typename T> T Round4(T value) {
return (value + 3) & ~3;
}
uint32_t Tag(const char *tag_str) {
uint32_t ret;
std::memcpy(&ret, tag_str, 4);
return ret;
}
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
@ -83,6 +77,10 @@ struct Arena {
std::vector<uint8_t*> hunks_;
};
// Use a macro instead of a function because gcc 4.4.3 creates static
// initializers in that case. Note this macro assumes a little-endian system.
#define TAG(a, b, c, d) (a | (b << 8) | (c << 16) | (d << 24))
const struct {
uint32_t tag;
bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
@ -91,80 +89,80 @@ const struct {
void (*free)(ots::OpenTypeFile *file);
bool required;
} table_parsers[] = {
{ Tag("maxp"), ots::ots_maxp_parse, ots::ots_maxp_serialise,
{ TAG('m', 'a', 'x', 'p'), ots::ots_maxp_parse, ots::ots_maxp_serialise,
ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
{ Tag("head"), ots::ots_head_parse, ots::ots_head_serialise,
{ TAG('h', 'e', 'a', 'd'), ots::ots_head_parse, ots::ots_head_serialise,
ots::ots_head_should_serialise, ots::ots_head_free, true },
{ Tag("OS/2"), ots::ots_os2_parse, ots::ots_os2_serialise,
{ TAG('O', 'S', '/', '2'), ots::ots_os2_parse, ots::ots_os2_serialise,
ots::ots_os2_should_serialise, ots::ots_os2_free, true },
{ Tag("cmap"), ots::ots_cmap_parse, ots::ots_cmap_serialise,
{ TAG('c', 'm', 'a', 'p'), ots::ots_cmap_parse, ots::ots_cmap_serialise,
ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
{ Tag("hhea"), ots::ots_hhea_parse, ots::ots_hhea_serialise,
{ TAG('h', 'h', 'e', 'a'), ots::ots_hhea_parse, ots::ots_hhea_serialise,
ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
{ Tag("hmtx"), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
{ TAG('h', 'm', 't', 'x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
{ Tag("name"), ots::ots_name_parse, ots::ots_name_serialise,
{ TAG('n', 'a', 'm', 'e'), ots::ots_name_parse, ots::ots_name_serialise,
ots::ots_name_should_serialise, ots::ots_name_free, true },
{ Tag("post"), ots::ots_post_parse, ots::ots_post_serialise,
{ TAG('p', 'o', 's', 't'), ots::ots_post_parse, ots::ots_post_serialise,
ots::ots_post_should_serialise, ots::ots_post_free, true },
{ Tag("loca"), ots::ots_loca_parse, ots::ots_loca_serialise,
{ TAG('l', 'o', 'c', 'a'), ots::ots_loca_parse, ots::ots_loca_serialise,
ots::ots_loca_should_serialise, ots::ots_loca_free, false },
{ Tag("glyf"), ots::ots_glyf_parse, ots::ots_glyf_serialise,
{ TAG('g', 'l', 'y', 'f'), ots::ots_glyf_parse, ots::ots_glyf_serialise,
ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
{ Tag("CFF "), ots::ots_cff_parse, ots::ots_cff_serialise,
{ TAG('C', 'F', 'F', ' '), ots::ots_cff_parse, ots::ots_cff_serialise,
ots::ots_cff_should_serialise, ots::ots_cff_free, false },
{ Tag("VDMX"), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
{ TAG('V', 'D', 'M', 'X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
{ Tag("hdmx"), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
{ TAG('h', 'd', 'm', 'x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
{ Tag("gasp"), ots::ots_gasp_parse, ots::ots_gasp_serialise,
{ TAG('g', 'a', 's', 'p'), ots::ots_gasp_parse, ots::ots_gasp_serialise,
ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
{ Tag("cvt "), ots::ots_cvt_parse, ots::ots_cvt_serialise,
{ TAG('c', 'v', 't', ' '), ots::ots_cvt_parse, ots::ots_cvt_serialise,
ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
{ Tag("fpgm"), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
{ TAG('f', 'p', 'g', 'm'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
{ Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
{ TAG('p', 'r', 'e', 'p'), ots::ots_prep_parse, ots::ots_prep_serialise,
ots::ots_prep_should_serialise, ots::ots_prep_free, false },
{ Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
{ TAG('L', 'T', 'S', 'H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
{ Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
{ TAG('V', 'O', 'R', 'G'), ots::ots_vorg_parse, ots::ots_vorg_serialise,
ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
{ Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise,
{ TAG('k', 'e', 'r', 'n'), ots::ots_kern_parse, ots::ots_kern_serialise,
ots::ots_kern_should_serialise, ots::ots_kern_free, false },
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
// because they could refer GDEF table.
{ Tag("GDEF"), ots::ots_gdef_parse, ots::ots_gdef_serialise,
{ TAG('G', 'D', 'E', 'F'), ots::ots_gdef_parse, ots::ots_gdef_serialise,
ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
{ Tag("GPOS"), ots::ots_gpos_parse, ots::ots_gpos_serialise,
{ TAG('G', 'P', 'O', 'S'), ots::ots_gpos_parse, ots::ots_gpos_serialise,
ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
{ Tag("GSUB"), ots::ots_gsub_parse, ots::ots_gsub_serialise,
{ TAG('G', 'S', 'U', 'B'), ots::ots_gsub_parse, ots::ots_gsub_serialise,
ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
{ Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise,
{ TAG('v', 'h', 'e', 'a'), ots::ots_vhea_parse, ots::ots_vhea_serialise,
ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
{ Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
{ TAG('v', 'm', 't', 'x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
// SILGraphite layout tables - not actually parsed, just copied
{ Tag("Silf"), ots::ots_silf_parse, ots::ots_silf_serialise,
{ TAG('S', 'i', 'l', 'f'), ots::ots_silf_parse, ots::ots_silf_serialise,
ots::ots_silf_should_serialise, ots::ots_silf_free, false },
{ Tag("Sill"), ots::ots_sill_parse, ots::ots_sill_serialise,
{ TAG('S', 'i', 'l', 'l'), ots::ots_sill_parse, ots::ots_sill_serialise,
ots::ots_sill_should_serialise, ots::ots_sill_free, false },
{ Tag("Gloc"), ots::ots_gloc_parse, ots::ots_gloc_serialise,
{ TAG('G', 'l', 'o', 'c'), ots::ots_gloc_parse, ots::ots_gloc_serialise,
ots::ots_gloc_should_serialise, ots::ots_gloc_free, false },
{ Tag("Glat"), ots::ots_glat_parse, ots::ots_glat_serialise,
{ TAG('G', 'l', 'a', 't'), ots::ots_glat_parse, ots::ots_glat_serialise,
ots::ots_glat_should_serialise, ots::ots_glat_free, false },
{ Tag("Feat"), ots::ots_feat_parse, ots::ots_feat_serialise,
{ TAG('F', 'e', 'a', 't'), ots::ots_feat_parse, ots::ots_feat_serialise,
ots::ots_feat_should_serialise, ots::ots_feat_free, false },
// TODO(bashi): Support mort, base, and jstf tables.
{ 0, NULL, NULL, NULL, NULL, false },
};
bool IsValidVersionTag(uint32_t tag) {
return tag == Tag("\x00\x01\x00\x00") ||
return tag == TAG('\x00', '\x01', '\x00', '\x00') ||
// OpenType fonts with CFF data have 'OTTO' tag.
tag == Tag("OTTO") ||
tag == TAG('O', 'T', 'T', 'O') ||
// Older Mac fonts might have 'true' or 'typ1' tag.
tag == Tag("true") ||
tag == Tag("typ1");
tag == TAG('t', 'r', 'u', 'e') ||
tag == TAG('t', 'y', 'p', '1');
}
bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
@ -262,7 +260,7 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
return OTS_FAILURE();
}
if (woff_tag != Tag("wOFF")) {
if (woff_tag != TAG('w', 'O', 'F', 'F')) {
return OTS_FAILURE();
}
@ -458,7 +456,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
if (header->cff) {
// font with PostScript glyph
if (header->version != Tag("OTTO")) {
if (header->version != TAG('O', 'T', 'T', 'O')) {
return OTS_FAILURE();
}
if (header->glyf || header->loca) {
@ -522,7 +520,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
out.offset = output->Tell();
output->ResetChecksum();
if (table_parsers[i].tag == Tag("head")) {
if (table_parsers[i].tag == TAG('h', 'e', 'a', 'd')) {
head_table_offset = out.offset;
}
if (!table_parsers[i].serialise(output, header)) {

View File

@ -380,6 +380,23 @@ class Vector : private AllocPolicy
return *(end() - 1);
}
class Range {
friend class Vector;
T *cur, *end;
Range(T *cur, T *end) : cur(cur), end(end) {}
public:
Range() {}
bool empty() const { return cur == end; }
size_t remain() const { return end - cur; }
T &front() const { return *cur; }
void popFront() { JS_ASSERT(!empty()); ++cur; }
T popCopyFront() { JS_ASSERT(!empty()); return *cur++; }
};
Range all() {
return Range(begin(), end());
}
/* mutators */
/* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */

View File

@ -49,7 +49,7 @@ namespace js {
inline
TreeContext::TreeContext(Parser *prs)
: flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0),
topStmt(NULL), topScopeStmt(NULL), blockChainBox(NULL), blockNode(NULL),
topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL),
decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), scopeChain_(NULL),
lexdeps(prs->context), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
innermostWith(NULL), bindings(prs->context), sharpSlotBase(-1)

File diff suppressed because it is too large Load Diff

View File

@ -138,7 +138,7 @@ struct StmtInfo {
ptrdiff_t continues; /* offset of last continue in loop */
union {
JSAtom *label; /* name of LABEL */
ObjectBox *blockBox; /* block scope object */
JSObject *blockObj; /* block scope object */
};
StmtInfo *down; /* info for enclosing statement */
StmtInfo *downScope; /* next enclosing lexical scope */
@ -299,7 +299,7 @@ struct TreeContext { /* tree context for semantic checks */
at non-zero depth in current paren tree */
StmtInfo *topStmt; /* top of statement info stack */
StmtInfo *topScopeStmt; /* top lexical scope statement */
ObjectBox *blockChainBox; /* compile time block scope chain (NB: one
JSObject *blockChain; /* compile time block scope chain (NB: one
deeper than the topScopeStmt/downScope
chain when in head of let block/expr) */
ParseNode *blockNode; /* parse node for a block with let declarations
@ -376,10 +376,6 @@ struct TreeContext { /* tree context for semantic checks */
uintN blockid() { return topStmt ? topStmt->blockid : bodyid; }
JSObject *blockChain() {
return blockChainBox ? blockChainBox->object : NULL;
}
/*
* True if we are at the topmost level of a entire script or function body.
* For example, while parsing this code we would encounter f1 and f2 at
@ -796,12 +792,6 @@ Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1);
ptrdiff_t
Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2);
/*
* Emit five bytecodes, an opcode with two 16-bit immediates.
*/
ptrdiff_t
Emit5(JSContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t op1, uint16_t op2);
/*
* Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
*/
@ -843,7 +833,7 @@ PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top);
* (if generating code), PopStatementBCE.
*/
void
PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top);
PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top);
/*
* Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it
@ -952,9 +942,10 @@ enum SrcNoteType {
from before loop, else JSOP_NOP at top of
do-while loop */
SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break;
also used on JSOP_ENDINIT if extra comma
at end of array literal: [1,2,,];
JSOP_DUP continuing destructuring pattern */
JSOP_ENDINIT needs extra comma at end of
array literal: [1,2,,];
JSOP_DUP continuing destructuring pattern;
JSOP_POP at end of for-in */
SRC_DECL = 6, /* type of a declaration (var, const, let*) */
SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment
operation, with SRC_DECL_* offset operand */
@ -962,6 +953,8 @@ enum SrcNoteType {
next POP, or from CONDSWITCH to first CASE
opcode, etc. -- always a forward delta */
SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */
SRC_DESTRUCTLET = 7, /* JSOP_DUP starting a destructuring let
operation, with offset to JSOP_ENTERLET0 */
SRC_ASSIGNOP = 8, /* += or another assign-op follows */
SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */
SRC_BRACE = 10, /* mandatory brace, for scope or to avoid
@ -1040,6 +1033,8 @@ enum SrcNoteType {
#define SN_3BYTE_OFFSET_FLAG 0x80
#define SN_3BYTE_OFFSET_MASK 0x7f
#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16) - 1)
#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \
: js_SrcNoteLength(sn))
#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn))
@ -1112,6 +1107,28 @@ BytecodeEmitter::countFinalSourceNotes()
return cnt;
}
/*
* To avoid offending js_SrcNoteSpec[SRC_DECL].arity, pack the two data needed
* to decompile let into one ptrdiff_t:
* offset: offset to the LEAVEBLOCK(EXPR) op (not including ENTER/LEAVE)
* groupAssign: whether this was an optimized group assign ([x,y] = [a,b])
*/
inline ptrdiff_t PackLetData(size_t offset, bool groupAssign)
{
JS_ASSERT(offset <= (size_t(-1) >> 1));
return ptrdiff_t(offset << 1) | ptrdiff_t(groupAssign);
}
inline size_t LetDataToOffset(ptrdiff_t w)
{
return size_t(w) >> 1;
}
inline bool LetDataToGroupAssign(ptrdiff_t w)
{
return size_t(w) & 1;
}
} /* namespace js */
struct JSSrcNoteSpec {

View File

@ -845,6 +845,11 @@ struct ParseNode {
/* Return true if this node appears in a Directive Prologue. */
bool isDirectivePrologueMember() const { return pn_prologue; }
#ifdef JS_HAS_DESTRUCTURING
/* Return true if this represents a hole in an array literal. */
bool isArrayHole() const { return isKind(PNK_COMMA) && isArity(PN_NULLARY); }
#endif
#ifdef JS_HAS_GENERATOR_EXPRS
/*
* True if this node is a desugared generator expression.
@ -1293,8 +1298,6 @@ struct ObjectBox {
ObjectBox *traceLink;
ObjectBox *emitLink;
JSObject *object;
ObjectBox *parent;
uintN index;
bool isFunctionBox;
};

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,8 @@ typedef struct BindData BindData;
namespace js {
enum FunctionSyntaxKind { Expression, Statement };
enum LetContext { LetExpresion, LetStatement };
enum VarContext { HoistVars, DontHoistVars };
struct Parser : private AutoGCRooter
{
@ -196,7 +198,7 @@ struct Parser : private AutoGCRooter
ParseNode *letStatement();
#endif
ParseNode *expressionStatement();
ParseNode *variables(ParseNodeKind kind, bool inLetHead);
ParseNode *variables(ParseNodeKind kind, JSObject *blockObj = NULL, VarContext varContext = HoistVars);
ParseNode *expr();
ParseNode *assignExpr();
ParseNode *condExpr1();
@ -240,7 +242,7 @@ struct Parser : private AutoGCRooter
ParseNode *generatorExpr(ParseNode *kid);
JSBool argumentList(ParseNode *listNode);
ParseNode *bracketedExpr();
ParseNode *letBlock(JSBool statement);
ParseNode *letBlock(LetContext letContext);
ParseNode *returnOrYield(bool useAssignExpr);
ParseNode *destructuringExpr(BindData *data, TokenKind tt);

View File

@ -60,7 +60,7 @@ f9 = (function() {
for each(let w in []) {}
}
})
trap(f9, 22, undefined);
trap(f9, 23, undefined);
for (b in f9())
(function() {})()

View File

@ -4,7 +4,7 @@ function f(){
this.zzz.zzz;
for(let d in []);
}
trap(f, 18, '')
trap(f, 16, '')
try {
f()
} catch(e) {

View File

@ -11,5 +11,5 @@ f = (function() {
} catch (e) {}
}
})
trap(f, 52, undefined);
trap(f, 40, undefined);
f()

View File

@ -0,0 +1,6 @@
// |jit-test| debug; error: TypeError
function f() {
""(this.z)
}
trap(f, 0, '')
f()

View File

@ -0,0 +1,7 @@
function f() {
var ss = [new f("abc"), new String("foobar"), new String("quux")];
for (let a6 = this ;; ) {}
}
try {
f();
} catch (e) {}

View File

@ -0,0 +1,16 @@
var x = -false;
switch(x) {
case 11:
let y = 42;
}
switch(x) {
case 11:
let y = 42;
let z = 'ponies';
}
switch(x) {
case 11:
let y = 42;
let z = 'ponies';
let a = false;
}

View File

@ -0,0 +1,4 @@
// |jit-test| error: TypeError
var obj = {};
let ([] = print) 3;
let ( i = "a" ) new i [ obj[i] ];

View File

@ -0,0 +1,12 @@
Function.prototype.X = 42;
function ownProperties() {
var props = {};
var r = function () {};
for (var a in r) {
let (a = function() { for (var r=0;r<6;++r) ++a; }) {
a();
}
props[a] = true;
}
}
ownProperties();

View File

@ -0,0 +1,9 @@
test();
function test() {
var f;
f = function() { (let(x) {y: z}) }
let (f = function() {
for (var t=0;t<6;++t) ++f;
}) { f(); } // { }
actual = f + '';
}

View File

@ -0,0 +1,342 @@
function test(str, arg, result)
{
arg = arg || 'ponies';
result = result || 'ponies';
var fun = new Function('x', str);
var got = fun.toSource().replace(/\n/g,'');
var expect = '(function anonymous(x) {' + str + '})';
if (got !== expect) {
print("GOT: " + got);
print("EXPECT: " + expect);
assertEq(got, expect);
}
Reflect.parse(got);
var got = fun(arg);
var expect = result;
if (got !== expect) {
print("GOT:" + got);
print("EXPECT: " + expect);
assertEq(got, expect);
}
}
function isError(str)
{
var caught = false;
try {
new Function(str);
} catch(e) {
assertEq(String(e).indexOf('TypeError') == 0 || String(e).indexOf('SyntaxError') == 0, true);
caught = true;
}
assertEq(caught, true);
}
// let expr
test('return let (y) x;');
test('return let (x) "" + x;', 'unicorns', 'undefined');
test('return let (y = x) (y++, "" + y);', 'unicorns', 'NaN');
test('return let (y = 1) (y = x, y);');
test('return let ([] = x) x;');
test('return let (x = {a: x}) x.a;');
test('return let ({a: x} = {a: x}) x;');
test('return let ([x] = {0: x}) x;');
test('return let ({0: x} = [x]) x;');
test('return let ({0: []} = []) x;');
test('return let ([, ] = x) x;');
test('return let ([, , , , ] = x) x;');
test('return let ([[]] = x) x;');
test('return let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) x;');
test('return let ([[], []] = x) x;');
test('return let ([[[[]]], [], , [], [[]]] = x) x;');
test('return let ({x: []} = x) x;');
test('return let ({x: [], y: {x: []}} = x) "ponies";', {y:{}});
test('return let ({x: []} = x, [{x: []}] = x) "ponies";');
test('return let (x = x) x;');
test('return let (x = eval("x")) x;');
test('return let (x = (let (x = x + 1) x) + 1) x;', 1, 3);
test('return let (x = (let (x = eval("x") + 1) eval("x")) + 1) eval("x");', 1, 3);
test('return let (x = x + 1, y = x) y;');
test('return let (x = x + 1, [] = x, [[, , ]] = x, y = x) y;');
test('return let ([{a: x}] = x, [, {b: y}] = x) let (x = x + 1, y = y + 2) x + y;', [{a:"p"},{b:"p"}], "p1p2");
test('return let ([] = []) x;');
test('return let ([] = [x]) x;');
test('return let ([x] = [x]) x;');
test('return let ([[a, [b, c]]] = [[x, []]]) a;');
test('return let ([x, y] = [x, x + 1]) x + y;', 1, 3);
test('return let ([x, y, z] = [x, x + 1, x + 2]) x + y + z;', 1, 6);
test('return let ([[x]] = [[x]]) x;');
test('return let ([x, y] = [x, x + 1]) x;');
test('return let ([x, [y, z]] = [x, x + 1]) x;');
test('return let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) x;',{x:['ponies']});
test('return let (x = (3, x)) x;');
test('return let (x = x + "s") x;', 'ponie');
test('return let ([x] = (3, [x])) x;');
test('return let ([] = [[]] = {}) x;');
test('return let (y = x) function () {return eval("y");}();');
test('return eval("let (y = x) y");');
test('return let (y = x) (eval("var y = 2"), y);', 'ponies', 2);
test('"use strict";return let (y = x) (eval("var y = 2"), y);');
test('this.y = x;return let (y = 1) this.eval("y");');
test('try {let (x = x) eval("throw x");} catch (e) {return e;}');
test('try {return let (x = eval("throw x")) x;} catch (e) {return e;}');
isError('let (x = 1, x = 2) x');
isError('let ([x, y] = a, {a:x} = b) x');
isError('let ([x, y, x] = a) x');
isError('let ([x, [y, [x]]] = a) x');
isError('let (x = function() { return x}) x()return x;');
isError('(let (x = function() { return x}) x())return x;');
// let block
test('let (y) {return x;}');
test('let (y = x) {y++;return "" + y;}', 'unicorns', 'NaN');
test('let (y = 1) {y = x;return y;}');
test('let (x) {return "" + x;}', 'unicorns', 'undefined');
test('let ([] = x) {return x;}');
test('let (x) {}return x;');
test('let (x = {a: x}) {return x.a;}');
test('let ({a: x} = {a: x}) {return x;}');
test('let ([x] = {0: x}) {return x;}');
test('let ({0: x} = [x]) {return x;}');
test('let ({0: []} = []) {return x;}');
test('let ([, ] = x) {return x;}');
test('let ([, , , , ] = x) {return x;}');
test('let ([[]] = x) {return x;}');
test('let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) {return x;}');
test('let ([[], []] = x) {return x;}');
test('let ([[[[]]], [], , [], [[]]] = x) {return x;}');
test('let ({x: []} = x) {return x;}');
test('let ({x: [], y: {x: []}} = x) {return "ponies";}', {y:{}});
test('let ({x: []} = x, [{x: []}] = x) {return "ponies";}');
test('let (x = x) {return x;}');
test('let (x = eval("x")) {return x;}');
test('let (x = (let (x = x + 1) x) + 1) {return x;}', 1, 3);
test('let (x = (let (x = eval("x") + 1) eval("x")) + 1) {return eval("x");}', 1, 3);
test('let (x = x + 1, y = x) {return y;}');
test('let (x = x + 1, [] = x, [[, , ]] = x, y = x) {return y;}');
test('let ([{a: x}] = x, [, {b: y}] = x) {let (x = x + 1, y = y + 2) {return x + y;}}', [{a:"p"},{b:"p"}], "p1p2");
test('let ([] = []) {return x;}');
test('let ([] = [x]) {return x;}');
test('let ([x] = [x]) {return x;}');
test('let ([[a, [b, c]]] = [[x, []]]) {return a;}');
test('let ([x, y] = [x, x + 1]) {return x + y;}', 1, 3);
test('let ([x, y, z] = [x, x + 1, x + 2]) {return x + y + z;}', 1, 6);
test('let ([[x]] = [[x]]) {return x;}');
test('let ([x, y] = [x, x + 1]) {return x;}');
test('let ([x, [y, z]] = [x, x + 1]) {return x;}');
test('let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) {return x;}',{x:['ponies']});
test('let (y = x[1]) {let (x = x[0]) {try {let (y = "unicorns") {throw y;}} catch (e) {return x + y;}}}', ['pon','ies']);
test('let (x = x) {try {let (x = "unicorns") eval("throw x");} catch (e) {return x;}}');
test('let ([] = [[]] = {}) {return x;}');
test('let (y = x) {return function () {return eval("y");}();}');
test('return eval("let (y = x) {y;}");');
test('let (y = x) {eval("var y = 2");return y;}', 'ponies', 2);
test('"use strict";let (y = x) {eval("var y = 2");return y;}');
test('this.y = x;let (y = 1) {return this.eval("y");}');
isError('let (x = 1, x = 2) {x}');
isError('let ([x, y] = a, {a:x} = b) {x}');
isError('let ([x, y, x] = a) {x}');
isError('let ([x, [y, [x]]] = a) {x}');
// var declarations
test('var y;return x;');
test('var y = x;return x;');
test('var [] = x;return x;');
test('var [, ] = x;return x;');
test('var [, , , , ] = x;return x;');
test('var [[]] = x;return x;');
test('var [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;');
test('var [[], []] = x;return x;');
test('var [[[[]]], [], , [], [[]]] = x;return x;');
test('var {x: []} = x;return x;');
test('var {x: [], y: {x: []}} = x;return "ponies";', {y:{}});
test('var {x: []} = x, [{x: []}] = x;return "ponies";');
test('var x = x;return x;');
test('var y = y;return "" + y;', 'unicorns', 'undefined');
test('var x = eval("x");return x;');
test('var x = (let (x = x + 1) x) + 1;return x;', 1, 3);
test('var x = (let (x = eval("x") + 1) eval("x")) + 1;return eval("x");', 1, 3);
test('var X = x + 1, y = x;return y;');
test('var X = x + 1, [] = X, [[, , ]] = X, y = x;return y;');
test('var [{a: X}] = x, [, {b: y}] = x;var X = X + 1, y = y + 2;return X + y;', [{a:"p"},{b:"p"}], "p1p2");
test('var [x] = [x];return x;');
test('var [[a, [b, c]]] = [[x, []]];return a;');
test('var [y] = [x];return y;');
test('var [x, y] = [x, x + 1];return x + y;', 1, 3);
test('var [x, y, z] = [x, x + 1, x + 2];return x + y + z;', 1, 6);
test('var [[x]] = [[x]];return x;');
test('var [x, y] = [x, x + 1];return x;');
test('var [x, [y, z]] = [x, x + 1];return x;');
test('var [{x: [x]}, {y1: y, z1: z}] = [x, x + 1];return x;',{x:['ponies']});
test('var [] = [[]] = {};return x;');
test('if (x) {var y = x;return x;}');
test('if (x) {y = x;var y = y;return y;}');
test('if (x) {var z = y;var [y] = x;z += y;}return z;', ['-'], 'undefined-');
// let declaration in context
test('if (x) {let y;return x;}');
test('if (x) {let x;return "" + x;}', 'unicorns', 'undefined');
test('if (x) {let y = x;return x;}');
test('if (x) {y = x;let y = y;return y;}');
test('if (x) {var z = y;let [y] = x;z += y;}return z;', ['-'], 'undefined-');
test('if (x) {let y = x;return x;}');
test('if (x) {let [] = x;return x;}');
test('if (x) {let [, ] = x;return x;}');
test('if (x) {let [, , , , ] = x;return x;}');
test('if (x) {let [[]] = x;return x;}');
test('if (x) {let [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;}');
test('if (x) {let [[], []] = x;return x;}');
test('if (x) {let [[[[]]], [], , [], [[]]] = x;return x;}');
test('if (x) {let {x: []} = x;return x;}');
test('if (x) {let {x: [], y: {x: []}} = x;return "ponies";}', {y:{}});
test('if (x) {let {x: []} = x, [{x: []}] = x;return "ponies";}');
test('if (x) {let x = x;return "" + x;}', 'unicorns', 'undefined');
test('if (x) {let y = y;return "" + y;}', 'unicorns', 'undefined');
test('if (x) {let x = eval("x");return "" + x;}', 'unicorns', 'undefined');
test('if (x) {let y = (let (x = x + 1) x) + 1;return y;}', 1, 3);
test('if (x) {let y = (let (x = eval("x") + 1) eval("x")) + 1;return eval("y");}', 1, 3);
test('if (x) {let X = x + 1, y = x;return y;}');
test('if (x) {let X = x + 1, [] = X, [[, , ]] = X, y = x;return y;}');
test('if (x) {let [{a: X}] = x, [, {b: Y}] = x;var XX = X + 1, YY = Y + 2;return XX + YY;}', [{a:"p"},{b:"p"}], "p1p2");
test('if (x) {let [[a, [b, c]]] = [[x, []]];return a;}');
test('if (x) {let [X] = [x];return X;}');
test('if (x) {let [y] = [x];return y;}');
test('if (x) {let [X, y] = [x, x + 1];return X + y;}', 1, 3);
test('if (x) {let [X, y, z] = [x, x + 1, x + 2];return X + y + z;}', 1, 6);
test('if (x) {let [[X]] = [[x]];return X;}');
test('if (x) {let [X, y] = [x, x + 1];return X;}');
test('if (x) {let [X, [y, z]] = [x, x + 1];return X;}');
test('if (x) {let [{x: [X]}, {y1: y, z1: z}] = [x, x + 1];return X;}',{x:['ponies']});
test('if (x) {let y = x;try {let x = 1;throw 2;} catch (e) {return y;}}');
test('if (x) {let [] = [[]] = {};return x;}');
test('let (y, [] = x) {}try {let a = b(), b;} catch (e) {return x;}');
test('try {let x = 1;throw 2;} catch (e) {return x;}');
test('let (y = x) {let x;return y;}');
test('let (y = x) {let x = y;return x;}');
test('let ([y, z] = x) {let a = x, b = y;return a;}');
test('let ([y, z] = x, a = x, [] = x) {let b = x, c = y;return a;}');
test('function f() {return unicorns;}try {let (x = 1) {let a, b;f();}} catch (e) {return x;}');
test('function f() {return unicorns;}try {let (x = 1) {let a, b;}f();} catch (e) {return x;}');
test('x.foo;{let y = x;return y;}');
test('x.foo;if (x) {x.bar;let y = x;return y;}');
test('if (x) {let y = x;return function () {return eval("y");}();}');
test('return eval("let y = x; y");');
test('if (x) {let y = x;eval("var y = 2");return y;}', 'ponies', 2);
test('"use strict";if (x) {let y = x;eval("var y = 2");return y;}');
test('"use strict";if (x) {let y = x;eval("let y = 2");return y;}');
test('"use strict";if (x) {let y = 1;return eval("let y = x;y;");}');
test('this.y = x;if (x) {let y = 1;return this.eval("y");}');
isError('if (x) {let (x = 1, x = 2) {x}}');
isError('if (x) {let ([x, y] = a, {a:x} = b) {x}}');
isError('if (x) {let ([x, y, x] = a) {x}}');
isError('if (x) {let ([x, [y, [x]]] = a) {x}}');
isError('let ([x, y] = x) {let x;}');
// for(;;)
test('for (;;) {return x;}');
test('for (let y = 1;;) {return x;}');
test('for (let y = 1;; ++y) {return x;}');
test('for (let y = 1; ++y;) {return x;}');
test('for (let (x = 1) x; x != 1; ++x) {return x;}');
test('for (let [, {a: [], b: []}] = x, [] = x; x;) {return x;}');
test('for (let x = 1, [y, z] = x, a = x; z < 4; ++z) {return x + y;}', [2,3], 3);
test('for (let (x = 1, [{a: b, c: d}] = [{a: 1, c: 2}]) x; x != 1; ++x) {return x;}');
test('for (let [[a, [b, c]]] = [[x, []]];;) {return a;}');
test('var sum = 0;for (let y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6);
test('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;', 1, 6);
test('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;', 1, 1);
test('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;', 1, 6);
test('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
test('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
test('for (var y = 1;;) {return x;}');
test('for (var y = 1;; ++y) {return x;}');
test('for (var y = 1; ++y;) {return x;}');
test('for (var [, {a: [], b: []}] = x, [] = x; x;) {return x;}');
test('for (var X = 1, [y, z] = x, a = x; z < 4; ++z) {return X + y;}', [2,3], 3);
test('var sum = 0;for (var y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6);
test('var sum = 0;for (var X = x, y = 10; X < 4; ++X) {sum += X;}return sum;', 1, 6);
test('var sum = 0;for (var X = x; X < 4; ++X) {sum += X;}return x;', 1, 1);
test('var sum = 0;for (var X = eval("x"); X < 4; ++X) {sum += X;}return sum;', 1, 6);
test('var sum = 0;for (var X = x; eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
test('var sum = 0;for (var X = eval("x"); eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
test('try {for (let x = eval("throw x");;) {}} catch (e) {return e;}');
test('try {for (let x = x + "s"; eval("throw x");) {}} catch (e) {return e;}', 'ponie');
test('for (let y = x;;) {let x;return y;}');
test('for (let y = x;;) {let y;return x;}');
test('for (let y;;) {let y;return x;}');
test('for (let a = x;;) {let c = x, d = x;return c;}');
test('for (let [a, b] = x;;) {let c = x, d = x;return c;}');
test('for (let [] = [[]] = {};;) {return x;}');
isError('for (let x = 1, x = 2;;) {}');
isError('for (let [x, y] = a, {a:x} = b;;) {}');
isError('for (let [x, y, x] = a;;) {}');
isError('for (let [x, [y, [x]]] = a;;) {}');
// for(in)
test('for (let i in x) {return x;}');
test('for (let i in x) {let y;return x;}');
test('for each (let [a, b] in x) {let y;return x;}');
test('for (let i in x) {let (i = x) {return i;}}');
test('for (let i in x) {let i = x;return i;}');
test('for each (let [x, y] in x) {return x + y;}', [['ponies', '']]);
test('for each (let [{0: x, 1: y}, z] in x) {return x + y + z;}', [[['po','nies'], '']]);
test('var s = "";for (let a in x) {for (let b in x) {s += a + b;}}return s;', [1,2], '00011011');
test('var res = "";for (let i in x) {res += x[i];}return res;');
test('var res = "";for (var i in x) {res += x[i];}return res;');
test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}');
test('for (let x in eval("x")) {return x;}', {ponies:true});
test('for (let x in x) {return eval("x");}', {ponies:true});
test('for (let x in eval("x")) {return eval("x");}', {ponies:true});
test('for ((let (x = {y: true}) x).y in eval("x")) {return eval("x");}');
test('for (let i in x) {break;}return x;');
test('for (let i in x) {break;}return eval("x");');
test('for (let x in x) {break;}return x;');
test('for (let x in x) {break;}return eval("x");');
test('a:for (let i in x) {for (let j in x) {break a;}}return x;');
test('a:for (let i in x) {for (let j in x) {break a;}}return eval("x");');
test('var j;for (let i in x) {j = i;break;}return j;', {ponies:true});
test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}');
test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies']);
isError('for (let [x, x] in o) {}');
isError('for (let [x, y, x] in o) {}');
isError('for (let [x, [y, [x]]] in o) {}');
// genexps
test('return (i for (i in x)).next();', {ponies:true});
test('return (eval("i") for (i in x)).next();', {ponies:true});
test('return (eval("i") for (i in eval("x"))).next();', {ponies:true});
test('try {return (eval("throw i") for (i in x)).next();} catch (e) {return e;}', {ponies:true});
// array comprehension
test('return [i for (i in x)][0];', {ponies:true});
test('return [eval("i") for (i in x)][0];', {ponies:true});
test('return [eval("i") for (i in eval("x"))][0];', {ponies:true});
test('try {return [eval("throw i") for (i in x)][0];} catch (e) {return e;}', {ponies:true});
// don't forget about switch craziness
test('var y = 3;switch (function () {return eval("y");}()) {case 3:let y;return x;default:;}');
test('switch (x) {case 3:let y;return 3;case 4:let z;return 4;default:return x;}');
test('switch (x) {case 3:let x;break;default:if (x === undefined) {return "ponies";}}');
test('switch (x) {case 3:default:let y;let (y = x) {return y;}}');
isError('switch (x) {case 3:let y;return 3;case 4:let y;return 4;default:;}');
// test weird cases where the decompiler changes tokens
function testWeird(str, printedAs, arg, result)
{
var fun = new Function('x', str);
// this is lame and doesn't normalize whitespace so if an assert fails
// here, see if its just whitespace and fix the caller
assertEq(fun.toSource(), '(function anonymous(x) {' + printedAs + '})');
test(printedAs, arg, result);
}
testWeird('let y = x;return x;', 'var y = x;return x;');
testWeird('let y = 1, y = x;return y;', 'var y = 1, y = x;return y;');
testWeird('return let ({x:x, y:y} = x) x + y', 'return let ({x, y} = x) x + y;', {x:'pon', y:'ies'});
testWeird('let ({x:x, y:y} = x) {return x + y;}', 'let ({x, y} = x) {return x + y;}', {x:'pon', y:'ies'});

View File

@ -0,0 +1,4 @@
// |jit-test| error: 3
var str = (function (x) {return (i for (i in x));}).toSource().replace('\n', '');
assertEq(str, "(function (x) {return (i for (i in x));})");
throw 3;

View File

@ -6,5 +6,5 @@ function caller(code, obj) {
eval(code); // Make the compiler give up on binding analysis.
return x;
}
trap(caller, 14, "var x = 'success'; nop()");
trap(caller, 12, "var x = 'success'; nop()");
assertEq(caller("var y = 'ignominy'", this), "success");

View File

@ -3,7 +3,7 @@ setDebug(true);
x = "notset";
function main() {
/* The JSOP_STOP in main. */
a = { valueOf: function () { trap(main, 36, "success()"); } };
a = { valueOf: function () { trap(main, 34, "success()"); } };
a + "";
x = "failure";
}

View File

@ -3,7 +3,7 @@ setDebug(true);
x = "notset";
function main() {
/* The JSOP_STOP in main. */
a = { valueOf: function () { trap(main, 57, "success()"); } };
a = { valueOf: function () { trap(main, 55, "success()"); } };
b = "";
eval();
a + b;

View File

@ -5,7 +5,7 @@ x = "notset";
function myparent(nested) {
if (nested) {
/* noop call in myparent */
trap(myparent, 50, "success()");
trap(myparent, 49, "success()");
} else {
myparent(true);
x = "failure";

View File

@ -14,7 +14,7 @@ function myparent(nested) {
}
}
/* JSOP_CALL to doNothing in myparent with nested = false. */
trap(myparent, 35, "myparent(true)");
trap(myparent, 34, "myparent(true)");
function success() {
x = "success";

View File

@ -119,11 +119,11 @@ ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc)
JSAtom *atom;
if (JSOp(*pc) == JSOP_DEFFUN) {
JSFunction *fun = script->getFunction(js_GetIndexFromBytecode(cx, script, pc, 0));
JSFunction *fun = script->getFunction(js_GetIndexFromBytecode(script, pc, 0));
atom = fun->atom;
} else {
JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM);
atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0));
atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0));
}
uintN index;
@ -389,6 +389,8 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
isInlineable = canTrackVars = false;
break;
case JSOP_ENTERLET0:
case JSOP_ENTERLET1:
case JSOP_ENTERBLOCK:
case JSOP_LEAVEBLOCK:
addsScopeObjects_ = true;

View File

@ -203,9 +203,6 @@ GetDefCount(JSScript *script, unsigned offset)
JS_ASSERT(offset < script->length);
jsbytecode *pc = script->code + offset;
if (js_CodeSpec[*pc].ndefs == -1)
return js_GetEnterBlockStackDefs(NULL, script, pc);
/*
* Add an extra pushed value for OR/AND opcodes, so that they are included
* in the pushed array of stack values for type inference.
@ -227,7 +224,7 @@ GetDefCount(JSScript *script, unsigned offset)
*/
return (pc[1] + 1);
default:
return js_CodeSpec[*pc].ndefs;
return StackDefs(script, pc);
}
}
@ -240,7 +237,7 @@ GetUseCount(JSScript *script, unsigned offset)
if (JSOp(*pc) == JSOP_PICK)
return (pc[1] + 1);
if (js_CodeSpec[*pc].nuses == -1)
return js_GetVariableStackUses(JSOp(*pc), pc);
return StackUses(script, pc);
return js_CodeSpec[*pc].nuses;
}

View File

@ -2281,7 +2281,7 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
}
JSFunction *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
{
/*
* Flat closures cannot yet be partial, that is, all upvars must be copied,

View File

@ -48,7 +48,6 @@
#include "jsatom.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsopcode.h"
#include "gc/Barrier.h"
@ -322,7 +321,7 @@ extern JSFunction * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
extern JSFunction *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
extern JSFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,

View File

@ -2032,21 +2032,21 @@ TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, const AllocationSite
static inline jsid
GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
{
unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset);
unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset);
return MakeTypeId(cx, ATOM_TO_JSID(script->getAtom(index)));
}
static inline JSObject *
GetScriptObject(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
{
unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset);
unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset);
return script->getObject(index);
}
static inline const Value &
GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc)
{
unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, 0);
unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, 0);
return script->getConst(index);
}
@ -3405,8 +3405,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_INDEXBASE3:
case JSOP_RESETBASE:
case JSOP_RESETBASE0:
case JSOP_BLOCKCHAIN:
case JSOP_NULLBLOCKCHAIN:
case JSOP_POPV:
case JSOP_DEBUGGER:
case JSOP_SETCALL:
@ -3958,6 +3956,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
case JSOP_ENTERWITH:
case JSOP_ENTERBLOCK:
case JSOP_ENTERLET0:
/*
* Scope lookups can occur on the values being pushed here. We don't track
* the value or its properties, and just monitor all name opcodes in the
@ -3965,6 +3964,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
*/
break;
case JSOP_ENTERLET1:
/*
* JSOP_ENTERLET1 enters a let block with an unrelated value on top of
* the stack (such as the condition to a switch) whose constraints must
* be propagated. The other values are ignored for the same reason as
* JSOP_ENTERLET0.
*/
poppedTypes(pc, 0)->addSubset(cx, &pushed[defCount - 1]);
break;
case JSOP_ITER: {
/*
* Use a per-script type set to unify the possible target types of all
@ -4023,6 +4032,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
break;
case JSOP_LEAVEFORLETIN:
break;
case JSOP_CASE:
case JSOP_CASEX:
poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
@ -4556,7 +4568,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
* integer properties and bail out. We can't mark the aggregate
* JSID_VOID type property as being in a definite slot.
*/
unsigned index = js_GetIndexFromBytecode(cx, script, pc, 0);
unsigned index = js_GetIndexFromBytecode(script, pc, 0);
jsid id = ATOM_TO_JSID(script->getAtom(index));
if (MakeTypeId(cx, id) != id)
return false;
@ -5423,6 +5435,8 @@ IgnorePushed(const jsbytecode *pc, unsigned index)
/* Storage for 'with' and 'let' blocks not monitored. */
case JSOP_ENTERWITH:
case JSOP_ENTERBLOCK:
case JSOP_ENTERLET0:
case JSOP_ENTERLET1:
return true;
/* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */

View File

@ -83,7 +83,6 @@
#include "jsinferinlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsopcodeinlines.h"
#include "jsprobes.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
@ -107,124 +106,6 @@ using namespace js;
using namespace js::gc;
using namespace js::types;
JSObject *
js::GetScopeChain(JSContext *cx)
{
/*
* Note: we don't need to expand inline frames here, because frames are
* only inlined when the caller and callee share the same scope chain.
*/
StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
if (!fp) {
/*
* There is no code active on this context. In place of an actual
* scope chain, use the context's global object, which is set in
* js_InitFunctionAndObjectClasses, and which represents the default
* scope chain for the embedding. See also js_FindClassObject.
*
* For embeddings that use the inner and outer object hooks, the inner
* object represents the ultimate global object, with the outer object
* acting as a stand-in.
*/
JSObject *obj = cx->globalObject;
if (!obj) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
return NULL;
}
OBJ_TO_INNER_OBJECT(cx, obj);
return obj;
}
return GetScopeChain(cx, fp);
}
/*
* This computes the blockChain by iterating through the bytecode
* of the current script until it reaches the PC. Each time it sees
* an ENTERBLOCK or LEAVEBLOCK instruction, it records the new
* blockChain. A faster variant of this function that doesn't
* require bytecode scanning appears below.
*/
JSObject *
js::GetBlockChain(JSContext *cx, StackFrame *fp)
{
if (!fp->isScriptFrame())
return NULL;
jsbytecode *target = fp->pcQuadratic(cx->stack);
JSScript *script = fp->script();
jsbytecode *start = script->code;
/*
* If the debugger asks for the scope chain at a pc where we are about to
* fix it up, advance target past the fixup. See bug 672804.
*/
JSOp op = JSOp(*target);
while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 ||
op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 ||
op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN)
{
target += js_CodeSpec[op].length;
op = JSOp(*target);
}
JS_ASSERT(target >= start && target < start + script->length);
JSObject *blockChain = NULL;
uintN indexBase = 0;
ptrdiff_t oplen;
for (jsbytecode *pc = start; pc < target; pc += oplen) {
JSOp op = JSOp(*pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
oplen = cs->length;
if (oplen < 0)
oplen = js_GetVariableBytecodeLength(pc);
if (op == JSOP_INDEXBASE)
indexBase = GET_INDEXBASE(pc);
else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3)
indexBase = (op - JSOP_INDEXBASE1 + 1) << 16;
else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0)
indexBase = 0;
else if (op == JSOP_ENTERBLOCK)
blockChain = script->getObject(indexBase + GET_INDEX(pc));
else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR)
blockChain = blockChain->getStaticBlockScopeChain();
else if (op == JSOP_BLOCKCHAIN)
blockChain = script->getObject(indexBase + GET_INDEX(pc));
else if (op == JSOP_NULLBLOCKCHAIN)
blockChain = NULL;
}
return blockChain;
}
/*
* This function computes the current blockChain, but only in
* the special case where a BLOCKCHAIN or NULLBLOCKCHAIN
* instruction appears immediately after the current PC.
* We ensure this happens for a few important ops like DEFFUN.
* |oplen| is the length of opcode at the current PC.
*/
JSObject *
js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
{
/* Assume that we're in a script frame. */
jsbytecode *pc = fp->pcQuadratic(cx->stack);
JS_ASSERT(JSOp(*pc) == op);
pc += oplen;
op = JSOp(*pc);
/* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
if (op == JSOP_NULLBLOCKCHAIN)
return NULL;
if (op == JSOP_BLOCKCHAIN)
return fp->script()->getObject(GET_INDEX(pc));
return GetBlockChain(cx, fp);
}
/*
* We can't determine in advance which local variables can live on the stack and
* be freed when their dynamic scope ends, and which will be closed over and
@ -255,10 +136,10 @@ js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
* This lazy cloning is implemented in GetScopeChain, which is also used in
* some other cases --- entering 'with' blocks, for example.
*/
static JSObject *
GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
JSObject *
js::GetScopeChain(JSContext *cx, StackFrame *fp)
{
JSObject *sharedBlock = blockChain;
JSObject *sharedBlock = fp->maybeBlockChain();
if (!sharedBlock) {
/*
@ -341,7 +222,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
JSObject *newChild = innermostNewChild;
for (;;) {
JS_ASSERT(newChild->getProto() == sharedBlock);
sharedBlock = sharedBlock->getStaticBlockScopeChain();
sharedBlock = sharedBlock->staticBlockScopeChain();
/* Sometimes limitBlock will be NULL, so check that first. */
if (sharedBlock == limitBlock || !sharedBlock)
@ -375,15 +256,34 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
}
JSObject *
js::GetScopeChain(JSContext *cx, StackFrame *fp)
js::GetScopeChain(JSContext *cx)
{
return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp));
}
/*
* Note: we don't need to expand inline frames here, because frames are
* only inlined when the caller and callee share the same scope chain.
*/
StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
if (!fp) {
/*
* There is no code active on this context. In place of an actual
* scope chain, use the context's global object, which is set in
* js_InitFunctionAndObjectClasses, and which represents the default
* scope chain for the embedding. See also js_FindClassObject.
*
* For embeddings that use the inner and outer object hooks, the inner
* object represents the ultimate global object, with the outer object
* acting as a stand-in.
*/
JSObject *obj = cx->globalObject;
if (!obj) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
return NULL;
}
JSObject *
js::GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
{
return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen));
OBJ_TO_INNER_OBJECT(cx, obj);
return obj;
}
return GetScopeChain(cx, fp);
}
/* Some objects (e.g., With) delegate 'this' to another object. */
@ -1162,7 +1062,7 @@ js::ValueToId(JSContext *cx, const Value &v, jsid *idp)
* of the with block with sp + stackIndex.
*/
static bool
EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
EnterWith(JSContext *cx, jsint stackIndex)
{
StackFrame *fp = cx->fp();
Value *sp = cx->regs().sp;
@ -1179,7 +1079,7 @@ EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
sp[-1].setObject(*obj);
}
JSObject *parent = GetScopeChainFast(cx, fp, op, oplen);
JSObject *parent = GetScopeChain(cx, fp);
if (!parent)
return JS_FALSE;
@ -1228,6 +1128,15 @@ js::UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp);
StackFrame *fp = cx->fp();
JSObject *obj = fp->maybeBlockChain();
while (obj) {
JS_ASSERT(obj->isStaticBlock());
if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
break;
obj = obj->staticBlockScopeChain();
}
fp->setBlockChain(obj);
for (;;) {
JSObject &scopeChain = fp->scopeChain();
if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth))
@ -1948,14 +1857,12 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
/* No-ops for ease of decompilation. */
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_UNUSED0)
ADD_EMPTY_CASE(JSOP_UNUSED1)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
#if JS_HAS_XML_SUPPORT
ADD_EMPTY_CASE(JSOP_STARTXML)
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
#endif
ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
ADD_EMPTY_CASE(JSOP_LOOPHEAD)
END_EMPTY_CASES
@ -2001,9 +1908,6 @@ check_backedge:
BEGIN_CASE(JSOP_LINENO)
END_CASE(JSOP_LINENO)
BEGIN_CASE(JSOP_BLOCKCHAIN)
END_CASE(JSOP_BLOCKCHAIN)
BEGIN_CASE(JSOP_UNDEFINED)
PUSH_UNDEFINED();
END_CASE(JSOP_UNDEFINED)
@ -2017,7 +1921,7 @@ BEGIN_CASE(JSOP_POPN)
regs.sp -= GET_UINT16(regs.pc);
#ifdef DEBUG
JS_ASSERT(regs.fp()->base() <= regs.sp);
JSObject *obj = GetBlockChain(cx, regs.fp());
JSObject *obj = regs.fp()->maybeBlockChain();
JS_ASSERT_IF(obj,
OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
<= (size_t) (regs.sp - regs.fp()->base()));
@ -2040,7 +1944,7 @@ BEGIN_CASE(JSOP_POPV)
END_CASE(JSOP_POPV)
BEGIN_CASE(JSOP_ENTERWITH)
if (!EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH))
if (!EnterWith(cx, -1))
goto error;
/*
@ -2078,6 +1982,7 @@ BEGIN_CASE(JSOP_STOP)
if (entryFrame != regs.fp())
inline_return:
{
JS_ASSERT(!regs.fp()->hasBlockChain());
JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
if (cx->compartment->debugMode())
@ -4064,7 +3969,7 @@ BEGIN_CASE(JSOP_DEFFUN)
} else {
JS_ASSERT(!fun->isFlatClosure());
obj2 = GetScopeChainFast(cx, regs.fp(), JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
obj2 = GetScopeChain(cx, regs.fp());
if (!obj2)
goto error;
}
@ -4163,7 +4068,7 @@ BEGIN_CASE(JSOP_DEFFUN_FC)
JSFunction *fun;
LOAD_FUNCTION(0);
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH);
JSObject *obj = js_NewFlatClosure(cx, fun);
if (!obj)
goto error;
@ -4207,8 +4112,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
if (fun->isNullClosure()) {
parent = &regs.fp()->scopeChain();
} else {
parent = GetScopeChainFast(cx, regs.fp(), JSOP_DEFLOCALFUN,
JSOP_DEFLOCALFUN_LENGTH);
parent = GetScopeChain(cx, regs.fp());
if (!parent)
goto error;
}
@ -4228,7 +4132,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
JSFunction *fun;
LOAD_FUNCTION(SLOTNO_LEN);
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
JSObject *obj = js_NewFlatClosure(cx, fun);
if (!obj)
goto error;
@ -4251,7 +4155,7 @@ BEGIN_CASE(JSOP_LAMBDA)
parent = &regs.fp()->scopeChain();
if (fun->joinable()) {
jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH;
JSOp op2 = JSOp(*pc2);
/*
@ -4266,7 +4170,7 @@ BEGIN_CASE(JSOP_LAMBDA)
JSObject *obj2 = &lref.toObject();
JS_ASSERT(obj2->isObject());
#endif
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
break;
}
@ -4277,7 +4181,7 @@ BEGIN_CASE(JSOP_LAMBDA)
#endif
const Value &lref = regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
break;
}
} else if (op2 == JSOP_CALL) {
@ -4315,7 +4219,7 @@ BEGIN_CASE(JSOP_LAMBDA)
}
}
} else {
parent = GetScopeChainFast(cx, regs.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
parent = GetScopeChain(cx, regs.fp());
if (!parent)
goto error;
}
@ -4337,7 +4241,7 @@ BEGIN_CASE(JSOP_LAMBDA_FC)
JSFunction *fun;
LOAD_FUNCTION(0);
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
JSObject *obj = js_NewFlatClosure(cx, fun);
if (!obj)
goto error;
JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
@ -5079,7 +4983,7 @@ BEGIN_CASE(JSOP_ENDFILTER)
* temporaries.
*/
JS_ASSERT(IsXML(regs.sp[-1]));
if (!EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH))
if (!EnterWith(cx, -2))
goto error;
regs.sp--;
len = GET_JUMP_OFFSET(regs.pc);
@ -5202,18 +5106,32 @@ END_CASE(JSOP_GETFUNNS)
#endif /* JS_HAS_XML_SUPPORT */
BEGIN_CASE(JSOP_ENTERBLOCK)
BEGIN_CASE(JSOP_ENTERLET0)
BEGIN_CASE(JSOP_ENTERLET1)
{
JSObject *obj;
LOAD_OBJECT(0, obj);
JS_ASSERT(obj->isStaticBlock());
JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(regs.sp < vp);
JS_ASSERT(vp <= regs.fp()->slots() + script->nslots);
SetValueRangeToUndefined(regs.sp, vp);
regs.sp = vp;
JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain());
if (op == JSOP_ENTERBLOCK) {
JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(regs.sp < vp);
JS_ASSERT(vp <= regs.fp()->slots() + script->nslots);
SetValueRangeToUndefined(regs.sp, vp);
regs.sp = vp;
} else if (op == JSOP_ENTERLET0) {
JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
== regs.sp);
} else if (op == JSOP_ENTERLET1) {
JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
== regs.sp - 1);
}
#ifdef DEBUG
JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain());
/*
* The young end of fp->scopeChain may omit blocks if we haven't closed
* over them, but if there are any closure blocks on fp->scopeChain, they'd
@ -5225,7 +5143,8 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
while (obj2->isWith())
obj2 = obj2->internalScopeChain();
if (obj2->isBlock() &&
obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) {
obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp()))
{
JSObject *youngestProto = obj2->getProto();
JS_ASSERT(youngestProto->isStaticBlock());
JSObject *parent = obj;
@ -5233,41 +5152,47 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
JS_ASSERT(parent);
}
#endif
regs.fp()->setBlockChain(obj);
}
END_CASE(JSOP_ENTERBLOCK)
BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
BEGIN_CASE(JSOP_LEAVEBLOCK)
BEGIN_CASE(JSOP_LEAVEFORLETIN)
BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
{
JSObject *blockChain;
LOAD_OBJECT(UINT16_LEN, blockChain);
#ifdef DEBUG
JS_ASSERT(blockChain->isStaticBlock());
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
JS_ASSERT(regs.fp()->blockChain().isStaticBlock());
DebugOnly<uintN> blockDepth = OBJ_BLOCK_DEPTH(cx, &regs.fp()->blockChain());
JS_ASSERT(blockDepth <= StackDepth(script));
#endif
/*
* If we're about to leave the dynamic scope of a block that has been
* cloned onto fp->scopeChain, clear its private data, move its locals from
* the stack into the clone, and pop it off the chain.
*/
JSObject &obj = regs.fp()->scopeChain();
if (obj.getProto() == blockChain) {
if (obj.getProto() == &regs.fp()->blockChain()) {
JS_ASSERT(obj.isClonedBlock());
if (!js_PutBlockObject(cx, JS_TRUE))
goto error;
}
/* Move the result of the expression to the new topmost stack slot. */
Value *vp = NULL; /* silence GCC warnings */
if (op == JSOP_LEAVEBLOCKEXPR)
vp = &regs.sp[-1];
regs.sp -= GET_UINT16(regs.pc);
if (op == JSOP_LEAVEBLOCKEXPR) {
regs.fp()->setBlockChain(regs.fp()->blockChain().staticBlockScopeChain());
if (op == JSOP_LEAVEBLOCK) {
/* Pop the block's slots. */
regs.sp -= GET_UINT16(regs.pc);
JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp);
} else if (op == JSOP_LEAVEBLOCKEXPR) {
/* Pop the block's slots maintaining the topmost expr. */
Value *vp = &regs.sp[-1];
regs.sp -= GET_UINT16(regs.pc);
JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp - 1);
regs.sp[-1] = *vp;
} else {
JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp);
/* Another op will pop; nothing to do here. */
len = JSOP_LEAVEFORLETIN_LENGTH;
DO_NEXT_OP(len);
}
}
END_CASE(JSOP_LEAVEBLOCK)
@ -5478,6 +5403,8 @@ END_CASE(JSOP_ARRAYPUSH)
switch (tn->kind) {
case JSTRY_CATCH:
JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
#if JS_HAS_GENERATORS
/* Catch cannot intercept the closing of a generator. */
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
@ -5565,9 +5492,10 @@ END_CASE(JSOP_ARRAYPUSH)
* frame pc.
*/
JS_ASSERT(entryFrame == regs.fp());
JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(),
!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
if (!regs.fp()->isGeneratorFrame()) {
JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
JS_ASSERT(!regs.fp()->hasBlockChain());
}
#ifdef JS_METHODJIT
/*

View File

@ -51,15 +51,6 @@
namespace js {
extern JSObject *
GetBlockChain(JSContext *cx, StackFrame *fp);
extern JSObject *
GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen);
extern JSObject *
GetScopeChain(JSContext *cx);
/*
* Refresh and return fp->scopeChain. It may be stale if block scopes are
* active but not yet reflected by objects in the scope chain. If a block
@ -67,11 +58,12 @@ GetScopeChain(JSContext *cx);
* dynamically scoped construct, then compile-time block scope at fp->blocks
* must reflect at runtime.
*/
extern JSObject *
GetScopeChain(JSContext *cx, StackFrame *fp);
extern JSObject *
GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen);
GetScopeChain(JSContext *cx);
extern JSObject *
GetScopeChain(JSContext *cx, StackFrame *fp);
/*
* ScriptPrologue/ScriptEpilogue must be called in pairs. ScriptPrologue

View File

@ -1318,12 +1318,14 @@ DirectEval(JSContext *cx, const CallArgs &args)
AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
JSObject *scopeChain =
GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
JSObject *scopeChain = GetScopeChain(cx, caller);
if (!scopeChain)
return false;
return scopeChain &&
WarnOnTooManyArgs(cx, args) &&
EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
if (!WarnOnTooManyArgs(cx, args))
return false;
return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
}
bool
@ -3676,20 +3678,28 @@ block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *v
}
const Shape *
JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index)
JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared)
{
JS_ASSERT(isStaticBlock());
*redeclared = false;
/* Inline JSObject::addProperty in order to trap the redefinition case. */
Shape **spp = nativeSearch(cx, id, true);
if (SHAPE_FETCH(spp)) {
*redeclared = true;
return NULL;
}
/*
* Use JSPROP_ENUMERATE to aid the disassembler, and don't convert this
* object to dictionary mode so that we can clone the block's shape later.
* Don't convert this object to dictionary mode so that we can clone the
* block's shape later.
*/
uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
const Shape *shape = addProperty(cx, id,
block_getProperty, block_setProperty,
slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
Shape::HAS_SHORTID, index,
/* allowDictionary = */ false);
const Shape *shape = addPropertyInternal(cx, id, block_getProperty, block_setProperty,
slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
Shape::HAS_SHORTID, index, spp,
/* allowDictionary = */ false);
if (!shape)
return NULL;
return shape;
@ -4134,7 +4144,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_ENCODE) {
obj = *objp;
parent = obj->getStaticBlockScopeChain();
parent = obj->staticBlockScopeChain();
parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
? FindObjectIndex(xdr->script->objects(), parent)
: NO_PARENT_INDEX;
@ -4189,8 +4199,11 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
if (!js_XDRAtom(xdr, &atom))
return false;
if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i))
bool redeclared;
if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i, &redeclared)) {
JS_ASSERT(!redeclared);
return false;
}
}
} else {
AutoShapeVector shapes(cx);
@ -7458,6 +7471,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
fprintf(stderr, "\n");
}
MaybeDumpObject("argsobj", fp->maybeArgsObj());
MaybeDumpObject("blockChain", fp->maybeBlockChain());
if (!fp->isDummyFrame()) {
MaybeDumpValue("this", fp->thisValue());
fprintf(stderr, " rval: ");

View File

@ -908,7 +908,7 @@ struct JSObject : js::gc::Cell
* on scope chains but mirror their structure, and can have a NULL
* scope chain.
*/
inline JSObject *getStaticBlockScopeChain() const;
inline JSObject *staticBlockScopeChain() const;
inline void setStaticBlockScopeChain(JSObject *obj);
/* Common fixed slot for the scope chain of internal scope objects. */
@ -1341,7 +1341,7 @@ struct JSObject : js::gc::Cell
bool swap(JSContext *cx, JSObject *other);
const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);
const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared);
inline bool isArguments() const;
inline bool isArrayBuffer() const;

View File

@ -324,7 +324,7 @@ JSObject::scopeChain() const
}
inline JSObject *
JSObject::getStaticBlockScopeChain() const
JSObject::staticBlockScopeChain() const
{
JS_ASSERT(isStaticBlock());
return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();

File diff suppressed because it is too large Load Diff

View File

@ -332,8 +332,7 @@ js_puts(JSPrinter *jp, const char *s);
* lexical environments.
*/
uintN
js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
ptrdiff_t pcoff);
js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff);
/*
* A slower version of GET_ATOM when the caller does not want to maintain
@ -342,72 +341,46 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
#define GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom) \
JS_BEGIN_MACRO \
JS_ASSERT(*(pc) != JSOP_DOUBLE); \
uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
(atom) = (script)->getAtom(index_); \
JS_END_MACRO
#define GET_DOUBLE_FROM_BYTECODE(script, pc, pcoff, dbl) \
JS_BEGIN_MACRO \
uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
JS_ASSERT(index_ < (script)->consts()->length); \
(dbl) = (script)->getConst(index_).toDouble(); \
JS_END_MACRO
#define GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj) \
JS_BEGIN_MACRO \
uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
obj = (script)->getObject(index_); \
JS_END_MACRO
#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \
JS_BEGIN_MACRO \
uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
fun = (script)->getFunction(index_); \
JS_END_MACRO
#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \
JS_BEGIN_MACRO \
uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \
uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \
obj = (script)->getRegExp(index_); \
JS_END_MACRO
/*
* Find the number of stack slots used by a variadic opcode such as JSOP_CALL
* (for such ops, JSCodeSpec.nuses is -1).
*/
#ifdef __cplusplus
namespace js {
extern uintN
js_GetVariableStackUses(JSOp op, jsbytecode *pc);
StackUses(JSScript *script, jsbytecode *pc);
/*
* Find the number of stack slots defined by JSOP_ENTERBLOCK (for this op,
* JSCodeSpec.ndefs is -1).
*/
extern uintN
js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc);
StackDefs(JSScript *script, jsbytecode *pc);
#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */
static JS_INLINE uintN
js_GetStackUses(const JSCodeSpec *cs, JSOp op, jsbytecode *pc)
{
JS_ASSERT(cs == &js_CodeSpec[op]);
if (cs->nuses >= 0)
return cs->nuses;
return js_GetVariableStackUses(op, pc);
}
static JS_INLINE uintN
js_GetStackDefs(JSContext *cx, const JSCodeSpec *cs, JSOp op, JSScript *script,
jsbytecode *pc)
{
JS_ASSERT(cs == &js_CodeSpec[op]);
if (cs->ndefs >= 0)
return cs->ndefs;
/* Only JSOP_ENTERBLOCK has a variable number of stack defs. */
JS_ASSERT(op == JSOP_ENTERBLOCK);
return js_GetEnterBlockStackDefs(cx, script, pc);
}
#endif
} /* namespace js */
#endif /* __cplusplus */
/*
* Decompilers, for script, function, and expression pretty-printing.

View File

@ -232,7 +232,7 @@ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE)
/* Call a function as a constructor; operand is argc. */
OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
OPDEF(JSOP_UNUSED1, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED0, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Fast get/set ops for function arguments and local variables. */
OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME)
@ -271,7 +271,8 @@ OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|
OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_UNUSED0, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */
OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, 0, JOF_BYTE)
/* The argument is the offset to the next statement and is used by IonMonkey. */
OPDEF(JSOP_LABEL, 106,"label", NULL, 3, 0, 0, 0, JOF_JUMP)
@ -440,15 +441,11 @@ OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|J
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3)
/*
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_BLOCKCHAIN, 185,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,186,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/* Enter a let block/expr whose slots are at the top of the stack. */
OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 3, -1, -1, 0, JOF_OBJECT)
/* Enter a let block/expr whose slots are 1 below the top of the stack. */
OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 3, -1, -1, 0, JOF_OBJECT)
/*
* Opcode to hold 24-bit immediate integer operands.
@ -496,7 +493,7 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J
* Block-local scope support.
*/
OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16)
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16)
/* Jump to target if top of stack value isn't callable. */
OPDEF(JSOP_IFCANTCALLTOP, 200,"ifcantcalltop",NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING)
@ -525,7 +522,7 @@ OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|J
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
* which must be moved down when the block pops.
*/
OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16)
OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16)
\
/*
* Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after

View File

@ -57,20 +57,6 @@ class BytecodeRange {
jsbytecode *pc, *end;
};
/*
* Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is
* useful only when checking for optimization opportunities.
*/
JS_ALWAYS_INLINE jsbytecode *
AdvanceOverBlockchainOp(jsbytecode *pc)
{
if (*pc == JSOP_NULLBLOCKCHAIN)
return pc + JSOP_NULLBLOCKCHAIN_LENGTH;
if (*pc == JSOP_BLOCKCHAIN)
return pc + JSOP_BLOCKCHAIN_LENGTH;
return pc;
}
class SrcNoteLineScanner
{
/* offset of the current JSOp in the bytecode */

View File

@ -1623,7 +1623,7 @@ class ASTSerializer
bool declaration(ParseNode *pn, Value *dst);
bool variableDeclaration(ParseNode *pn, bool let, Value *dst);
bool variableDeclarator(ParseNode *pn, VarDeclKind *pkind, Value *dst);
bool letHead(ParseNode *pn, NodeVector &dtors);
bool let(ParseNode *pn, bool expr, Value *dst);
bool optStatement(ParseNode *pn, Value *dst) {
if (!pn) {
@ -1963,14 +1963,21 @@ ASTSerializer::variableDeclarator(ParseNode *pn, VarDeclKind *pkind, Value *dst)
}
bool
ASTSerializer::letHead(ParseNode *pn, NodeVector &dtors)
ASTSerializer::let(ParseNode *pn, bool expr, Value *dst)
{
if (!dtors.reserve(pn->pn_count))
ParseNode *letHead = pn->pn_left;
LOCAL_ASSERT(letHead->isArity(PN_LIST));
ParseNode *letBody = pn->pn_right;
LOCAL_ASSERT(letBody->isKind(PNK_LEXICALSCOPE));
NodeVector dtors(cx);
if (!dtors.reserve(letHead->pn_count))
return false;
VarDeclKind kind = VARDECL_LET_HEAD;
for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
for (ParseNode *next = letHead->pn_head; next; next = next->pn_next) {
Value child;
/*
* Unlike in |variableDeclaration|, this does not update |kind|; since let-heads do
@ -1981,7 +1988,12 @@ ASTSerializer::letHead(ParseNode *pn, NodeVector &dtors)
dtors.infallibleAppend(child);
}
return true;
Value v;
return expr
? expression(letBody->pn_expr, &v) &&
builder.letExpression(dtors, v, &pn->pn_pos, dst)
: statement(letBody->pn_expr, &v) &&
builder.letStatement(dtors, v, &pn->pn_pos, dst);
}
bool
@ -2078,8 +2090,6 @@ ASTSerializer::forInit(ParseNode *pn, Value *dst)
return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
? variableDeclaration(pn, false, dst)
: pn->isKind(PNK_LET)
? variableDeclaration(pn, true, dst)
: expression(pn, dst);
}
@ -2091,9 +2101,13 @@ ASTSerializer::statement(ParseNode *pn, Value *dst)
case PNK_FUNCTION:
case PNK_VAR:
case PNK_CONST:
case PNK_LET:
return declaration(pn, dst);
case PNK_LET:
return pn->isArity(PN_BINARY)
? let(pn, false, dst)
: declaration(pn, dst);
case PNK_NAME:
LOCAL_ASSERT(pn->isUsed());
return statement(pn->pn_lexdef, dst);
@ -2108,15 +2122,6 @@ ASTSerializer::statement(ParseNode *pn, Value *dst)
case PNK_LEXICALSCOPE:
pn = pn->pn_expr;
if (pn->isKind(PNK_LET)) {
NodeVector dtors(cx);
Value stmt;
return letHead(pn->pn_left, dtors) &&
statement(pn->pn_right, &stmt) &&
builder.letStatement(dtors, stmt, &pn->pn_pos, dst);
}
if (!pn->isKind(PNK_STATEMENTLIST))
return statement(pn, dst);
/* FALL THROUGH */
@ -2176,9 +2181,9 @@ ASTSerializer::statement(ParseNode *pn, Value *dst)
return (!head->pn_kid1
? pattern(head->pn_kid2, NULL, &var)
: variableDeclaration(head->pn_kid1,
head->pn_kid1->isKind(PNK_LET),
&var)) &&
: head->pn_kid1->isKind(PNK_LEXICALSCOPE)
? variableDeclaration(head->pn_kid1->pn_expr, true, &var)
: variableDeclaration(head->pn_kid1, false, &var)) &&
expression(head->pn_kid3, &expr) &&
builder.forInStatement(var, expr, stmt, isForEach, &pn->pn_pos, dst);
}
@ -2633,17 +2638,8 @@ ASTSerializer::expression(ParseNode *pn, Value *dst)
return comprehension(pn->pn_head->pn_expr, dst);
case PNK_LEXICALSCOPE:
{
pn = pn->pn_expr;
NodeVector dtors(cx);
Value expr;
return letHead(pn->pn_left, dtors) &&
expression(pn->pn_right, &expr) &&
builder.letExpression(dtors, expr, &pn->pn_pos, dst);
}
case PNK_LET:
return let(pn, true, dst);
#ifdef JS_HAS_XML_SUPPORT
case PNK_XMLUNARY:

View File

@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id);
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 99)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 100)
/*
* Library-private functions.

View File

@ -2470,12 +2470,6 @@ mjit::Compiler::generateMethod()
frame.popn(2);
END_CASE(JSOP_ENUMELEM)
BEGIN_CASE(JSOP_BLOCKCHAIN)
END_CASE(JSOP_BLOCKCHAIN)
BEGIN_CASE(JSOP_NULLBLOCKCHAIN)
END_CASE(JSOP_NULLBLOCKCHAIN)
BEGIN_CASE(JSOP_CONDSWITCH)
/* No-op for the decompiler. */
END_CASE(JSOP_CONDSWITCH)
@ -2544,7 +2538,7 @@ mjit::Compiler::generateMethod()
jsbytecode *pc2 = NULL;
if (fun->joinable()) {
pc2 = AdvanceOverBlockchainOp(PC + JSOP_LAMBDA_LENGTH);
pc2 = PC + JSOP_LAMBDA_LENGTH;
JSOp next = JSOp(*pc2);
if (next == JSOP_INITMETHOD) {
@ -2568,9 +2562,6 @@ mjit::Compiler::generateMethod()
prepareStubCall(Uses(uses));
masm.move(ImmPtr(fun), Registers::ArgReg1);
if (stub != stubs::Lambda)
masm.storePtr(ImmPtr(pc2), FrameAddress(offsetof(VMFrame, scratch)));
INLINE_STUBCALL(stub, REJOIN_PUSH_OBJECT);
frame.takeReg(Registers::ReturnReg);
@ -2687,6 +2678,8 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_GETXPROP)
BEGIN_CASE(JSOP_ENTERBLOCK)
BEGIN_CASE(JSOP_ENTERLET0)
BEGIN_CASE(JSOP_ENTERLET1)
enterBlock(script->getObject(fullAtomIndex(PC)));
END_CASE(JSOP_ENTERBLOCK);
@ -7143,9 +7136,9 @@ mjit::Compiler::enterBlock(JSObject *obj)
/* For now, don't bother doing anything for this opcode. */
frame.syncAndForgetEverything();
masm.move(ImmPtr(obj), Registers::ArgReg1);
uint32_t n = js_GetEnterBlockStackDefs(cx, script, PC);
INLINE_STUBCALL(stubs::EnterBlock, REJOIN_NONE);
frame.enterBlock(n);
if (*PC == JSOP_ENTERBLOCK)
frame.enterBlock(StackDefs(script, PC));
}
void
@ -7155,10 +7148,8 @@ mjit::Compiler::leaveBlock()
* Note: After bug 535912, we can pass the block obj directly, inline
* PutBlockObject, and do away with the muckiness in PutBlockObject.
*/
uint32_t n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC);
JSObject *obj = script->getObject(fullAtomIndex(PC + UINT16_LEN));
uint32_t n = StackUses(script, PC);
prepareStubCall(Uses(n));
masm.move(ImmPtr(obj), Registers::ArgReg1);
INLINE_STUBCALL(stubs::LeaveBlock, REJOIN_NONE);
frame.leaveBlock(n);
}

View File

@ -177,6 +177,7 @@ InlineReturn(VMFrame &f)
{
JS_ASSERT(f.fp() != f.entryfp);
JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0));
JS_ASSERT(!f.fp()->hasBlockChain());
f.cx->stack.popInlineFrame(f.regs);
DebugOnly<JSOp> op = JSOp(*f.regs.pc);
@ -638,6 +639,7 @@ js_InternalThrow(VMFrame &f)
cx->clearPendingException();
cx->regs().sp++;
cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
cx->regs().fp()->setBlockChain(obj);
}
*f.oldregs = f.regs;

View File

@ -1869,7 +1869,7 @@ LoopState::analyzeLoopBody(unsigned frame)
case JSOP_SETPROP:
case JSOP_SETMETHOD: {
JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0));
JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0));
jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
TypeSet *objTypes = analysis->poppedTypes(pc, 1);
@ -2185,7 +2185,7 @@ LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pcon
}
case JSOP_GETPROP: {
JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0));
JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0));
jsid id = ATOM_TO_JSID(atom);
CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
FrameEntry *tmp = invariantProperty(objcv, id);

View File

@ -673,7 +673,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun)
} else {
JS_ASSERT(!fun->isFlatClosure());
obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
obj2 = GetScopeChain(cx, fp);
if (!obj2)
THROW();
}
@ -1333,8 +1333,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
if (fun->isNullClosure()) {
parent = &f.fp()->scopeChain();
} else {
parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
JSOP_DEFLOCALFUN_LENGTH);
parent = GetScopeChain(f.cx, f.fp());
if (!parent)
THROWV(NULL);
}
@ -1350,7 +1349,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
JSObject * JS_FASTCALL
stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
{
JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
JSObject *obj = js_NewFlatClosure(f.cx, fun);
if (!obj)
THROWV(NULL);
return obj;
@ -1376,8 +1375,8 @@ stubs::RegExp(VMFrame &f, JSObject *regex)
JSObject * JS_FASTCALL
stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun)
{
DebugOnly<jsbytecode*> nextpc = (jsbytecode *) f.scratch;
JS_ASSERT(fun->joinable());
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
return fun;
}
@ -1386,9 +1385,9 @@ JSObject * JS_FASTCALL
stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
DebugOnly<jsbytecode*> nextpc = (jsbytecode *) f.scratch;
const Value &lref = f.regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
return fun;
}
@ -1399,7 +1398,6 @@ JSObject * JS_FASTCALL
stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
{
JS_ASSERT(fun->joinable());
jsbytecode *nextpc = (jsbytecode *) f.scratch;
/*
* Array.prototype.sort and String.prototype.replace are optimized as if
@ -1407,7 +1405,7 @@ stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
* object fun, therefore we don't need to clone that compiler-created
* function object for identity/mutation reasons.
*/
int iargc = GET_ARGC(nextpc);
int iargc = GET_ARGC(f.regs.pc + JSOP_LAMBDA_LENGTH);
/*
* Note that we have not yet pushed fun as the final argument, so
@ -1444,7 +1442,7 @@ stubs::Lambda(VMFrame &f, JSFunction *fun)
if (fun->isNullClosure()) {
parent = &f.fp()->scopeChain();
} else {
parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
parent = GetScopeChain(f.cx, f.fp());
if (!parent)
THROWV(NULL);
}
@ -1763,7 +1761,7 @@ stubs::Throw(VMFrame &f)
JSObject * JS_FASTCALL
stubs::FlatLambda(VMFrame &f, JSFunction *fun)
{
JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
JSObject *obj = js_NewFlatClosure(f.cx, fun);
if (!obj)
THROWV(NULL);
return obj;
@ -1819,21 +1817,23 @@ void JS_FASTCALL
stubs::EnterBlock(VMFrame &f, JSObject *obj)
{
FrameRegs &regs = f.regs;
#ifdef DEBUG
StackFrame *fp = f.fp();
#endif
JS_ASSERT(!f.regs.inlined());
JS_ASSERT(obj->isStaticBlock());
JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(regs.sp < vp);
JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
SetValueRangeToUndefined(regs.sp, vp);
regs.sp = vp;
if (*regs.pc == JSOP_ENTERBLOCK) {
JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(regs.sp < vp);
JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
SetValueRangeToUndefined(regs.sp, vp);
regs.sp = vp;
}
#ifdef DEBUG
JSContext *cx = f.cx;
JS_ASSERT(fp->maybeBlockChain() == obj->staticBlockScopeChain());
/*
* The young end of fp->scopeChain() may omit blocks if we haven't closed
@ -1854,17 +1854,19 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj)
JS_ASSERT(parent);
}
#endif
fp->setBlockChain(obj);
}
void JS_FASTCALL
stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
stubs::LeaveBlock(VMFrame &f)
{
JSContext *cx = f.cx;
StackFrame *fp = f.fp();
#ifdef DEBUG
JS_ASSERT(blockChain->isStaticBlock());
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
JS_ASSERT(fp->blockChain().isBlock());
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, &fp->blockChain());
JS_ASSERT(blockDepth <= StackDepth(fp->script()));
#endif
@ -1873,12 +1875,15 @@ stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
* cloned onto fp->scopeChain(), clear its private data, move its locals from
* the stack into the clone, and pop it off the chain.
*/
JSObject *obj = &fp->scopeChain();
if (obj->getProto() == blockChain) {
JS_ASSERT(obj->isBlock());
JSObject &obj = fp->scopeChain();
JSObject &blockChain = fp->blockChain();
if (obj.getProto() == &blockChain) {
JS_ASSERT(obj.isBlock());
if (!js_PutBlockObject(cx, JS_TRUE))
THROW();
}
fp->setBlockChain(blockChain.staticBlockScopeChain());
}
void * JS_FASTCALL

View File

@ -154,7 +154,7 @@ JSObject * JS_FASTCALL LambdaJoinableForNull(VMFrame &f, JSFunction *fun);
JSObject * JS_FASTCALL FlatLambda(VMFrame &f, JSFunction *fun);
void JS_FASTCALL Arguments(VMFrame &f);
void JS_FASTCALL EnterBlock(VMFrame &f, JSObject *obj);
void JS_FASTCALL LeaveBlock(VMFrame &f, JSObject *blockChain);
void JS_FASTCALL LeaveBlock(VMFrame &f);
JSBool JS_FASTCALL LessThan(VMFrame &f);
JSBool JS_FASTCALL LessEqual(VMFrame &f);

View File

@ -49,11 +49,11 @@ expect = '99999';
jit(true);
for (let j = 0; j < 5; ++j) {
var e;
e = 9;
print(actual += '' + e);
e = 47;
if (e & 0) {
var e;
let e;
}
}

View File

@ -575,7 +575,7 @@ function testVarPatternCombinations(makePattSrc, makePattPatt) {
assertStmt("for (var " + pattSrcs[i].join(",") + "; foo; bar);",
forStmt(varDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt));
assertStmt("for (let " + pattSrcs[i].join(",") + "; foo; bar);",
forStmt(letDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt));
letStmt(pattPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt)));
assertStmt("for (const " + pattSrcs[i].join(",") + "; foo; bar);",
forStmt(constDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt));
}

View File

@ -6,7 +6,7 @@ function f() {
let (a = let (x = 1) x) {}
}
trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)');
trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)');
f();
reportCompare(0, 0, 'ok');

View File

@ -5,7 +5,7 @@ var e = [], x = {b: []};
function f() {
let (a = [[] for (x in e)], {b: []} = x) {}
}
trap(f, 4, '');
trap(f, 3, '');
f();
reportCompare(0, 0, 'ok');

View File

@ -154,12 +154,14 @@ StackFrame::initCallFrame(JSContext *cx, JSFunction &callee,
JS_ASSERT(script == callee.toFunction()->script());
/* Initialize stack frame members. */
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | flagsArg;
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
exec.fun = &callee;
args.nactual = nactual;
scopeChain_ = callee.toFunction()->environment();
ncode_ = NULL;
initPrev(cx);
blockChain_= NULL;
JS_ASSERT(!hasBlockChain());
JS_ASSERT(!hasHookData());
JS_ASSERT(annotation() == NULL);
JS_ASSERT(!hasCallObj());

View File

@ -78,7 +78,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs
* script in the context of another frame and the frame type is determined
* by the context.
*/
flags_ = type | HAS_SCOPECHAIN | HAS_PREVPC;
flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC;
if (!(flags_ & GLOBAL))
flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
@ -102,6 +102,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs
prev_ = prev;
prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
prevInline_ = regs ? regs->inlined() : NULL;
blockChain_ = NULL;
#ifdef DEBUG
ncode_ = (void *)0xbad;

View File

@ -345,10 +345,11 @@ class StackFrame
HAS_RVAL = 0x20000, /* frame has rval_ set */
HAS_SCOPECHAIN = 0x40000, /* frame has scopeChain_ set */
HAS_PREVPC = 0x80000, /* frame has prevpc_ and prevInline_ set */
HAS_BLOCKCHAIN = 0x100000, /* frame has blockChain_ set */
/* Method JIT state */
DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */
LOWERED_CALL_APPLY = 0x200000 /* Pushed by a lowered call/apply */
DOWN_FRAMES_EXPANDED = 0x400000, /* inlining in down frames has been expanded */
LOWERED_CALL_APPLY = 0x800000 /* Pushed by a lowered call/apply */
};
private:
@ -368,6 +369,7 @@ class StackFrame
/* Lazily initialized */
Value rval_; /* return value of the frame */
JSObject *blockChain_; /* innermost let block */
jsbytecode *prevpc_; /* pc of previous frame*/
JSInlinedSite *prevInline_; /* inlined site in previous frame */
void *hookData_; /* closure returned by call hook */
@ -840,6 +842,26 @@ class StackFrame
inline void setScopeChainNoCallObj(JSObject &obj);
inline void setScopeChainWithOwnCallObj(CallObject &obj);
/* Block chain */
bool hasBlockChain() const {
return (flags_ & HAS_BLOCKCHAIN) && blockChain_;
}
JSObject *maybeBlockChain() {
return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : NULL;
}
JSObject &blockChain() const {
JS_ASSERT(hasBlockChain());
return *blockChain_;
}
void setBlockChain(JSObject *obj) {
flags_ |= HAS_BLOCKCHAIN;
blockChain_ = obj;
}
/*
* Prologue for function frames: make a call object for heavyweight
* functions, and maintain type nesting invariants.

View File

@ -55,7 +55,7 @@ class GeneratorFrameGuard;
enum InitialFrameFlags {
INITIAL_NONE = 0,
INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
INITIAL_LOWERED = 0x200000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
};
enum ExecuteType {

View File

@ -696,10 +696,17 @@ class YarrGenerator : private MacroAssembler {
#endif
if (m_pattern.m_ignoreCase) {
#if WTF_CPU_BIG_ENDIAN
if (isASCIIAlpha(ch))
mask |= 32 << 16;
if (isASCIIAlpha(ch2))
mask |= 32;
#else
if (isASCIIAlpha(ch))
mask |= 32;
if (isASCIIAlpha(ch2))
mask |= 32 << 16;
#endif
}
BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar));

View File

@ -2713,7 +2713,7 @@ nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
static void
DrawBorderImage(nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
nsRenderingContext& aRenderingContext,
nsIFrame* aForFrame,
const nsRect& aBorderArea,
const nsStyleBorder& aStyleBorder,
@ -2735,9 +2735,7 @@ DrawBorderImage(nsPresContext* aPresContext,
imgIRequest *req = aStyleBorder.GetBorderImage();
// Get the actual image, and determine where the split points are.
// Note that mBorderImageSplit is in image pixels, not necessarily
// CSS pixels.
// Get the actual image.
nsCOMPtr<imgIContainer> imgContainer;
req->GetImage(getter_AddRefs(imgContainer));
@ -2753,13 +2751,21 @@ DrawBorderImage(nsPresContext* aPresContext,
nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.height);
}
// Convert percentages and clamp values to the image size.
nsIntMargin split;
// Determine the border image area, which by default corresponds to the
// border box but can be modified by 'border-image-outset'.
nsRect borderImgArea(aBorderArea);
borderImgArea.Inflate(aStyleBorder.GetImageOutset());
// Compute the used values of 'border-image-slice' and 'border-image-width';
// we do them together because the latter can depend on the former.
nsIntMargin slice;
nsMargin border;
NS_FOR_CSS_SIDES(s) {
nsStyleCoord coord = aStyleBorder.mBorderImageSplit.Get(s);
PRInt32 imgDimension = ((s == NS_SIDE_TOP || s == NS_SIDE_BOTTOM)
? imageSize.height
: imageSize.width);
nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s);
PRInt32 imgDimension = NS_SIDE_IS_VERTICAL(s)
? imageSize.width : imageSize.height;
nscoord borderDimension = NS_SIDE_IS_VERTICAL(s)
? borderImgArea.width : borderImgArea.height;
double value;
switch (coord.GetUnit()) {
case eStyleUnit_Percent:
@ -2769,8 +2775,7 @@ DrawBorderImage(nsPresContext* aPresContext,
value = coord.GetFactorValue();
break;
default:
NS_ASSERTION(coord.GetUnit() == eStyleUnit_Null,
"unexpected CSS unit for image split");
NS_NOTREACHED("unexpected CSS unit for image slice");
value = 0;
break;
}
@ -2778,74 +2783,123 @@ DrawBorderImage(nsPresContext* aPresContext,
value = 0;
if (value > imgDimension)
value = imgDimension;
split.Side(s) = NS_lround(value);
slice.Side(s) = NS_lround(value);
nsMargin borderWidths(aStyleBorder.GetActualBorder());
coord = aStyleBorder.mBorderImageWidth.Get(s);
switch (coord.GetUnit()) {
case eStyleUnit_Coord: // absolute dimension
value = coord.GetCoordValue();
break;
case eStyleUnit_Percent:
value = coord.GetPercentValue() * borderDimension;
break;
case eStyleUnit_Factor:
value = coord.GetFactorValue() * borderWidths.Side(s);
break;
case eStyleUnit_Auto: // same as the slice value, in CSS pixels
value = nsPresContext::CSSPixelsToAppUnits(slice.Side(s));
break;
default:
NS_NOTREACHED("unexpected CSS unit for border image area division");
value = 0;
break;
}
border.Side(s) = NS_lround(value);
}
nsMargin border(aStyleBorder.GetActualBorder());
// "If two opposite border-image-width offsets are large enough that they
// overlap, their used values are proportionately reduced until they no
// longer overlap."
double scaleX = border.left + border.right > borderImgArea.width
? borderImgArea.width / double(border.left + border.right)
: 1.0;
double scaleY = border.top + border.bottom > borderImgArea.height
? borderImgArea.height / double(border.top + border.bottom)
: 1.0;
double scale = NS_MIN(scaleX, scaleY);
if (scale < 1.0) {
border.left *= scale;
border.right *= scale;
border.top *= scale;
border.bottom *= scale;
NS_ASSERTION(border.left + border.right <= borderImgArea.width &&
border.top + border.bottom <= borderImgArea.height,
"rounding error in width reduction???");
}
// These helper tables recharacterize the 'split' and 'border' margins
// These helper tables recharacterize the 'slice' and 'width' margins
// in a more convenient form: they are the x/y/width/height coords
// required for various bands of the border, and they have been transformed
// to be relative to the image (for 'split') or the page (for 'border').
// to be relative to the innerRect (for 'slice') or the page (for 'border').
enum {
LEFT, MIDDLE, RIGHT,
TOP = LEFT, BOTTOM = RIGHT
};
const nscoord borderX[3] = {
aBorderArea.x + 0,
aBorderArea.x + border.left,
aBorderArea.x + aBorderArea.width - border.right,
borderImgArea.x + 0,
borderImgArea.x + border.left,
borderImgArea.x + borderImgArea.width - border.right,
};
const nscoord borderY[3] = {
aBorderArea.y + 0,
aBorderArea.y + border.top,
aBorderArea.y + aBorderArea.height - border.bottom,
borderImgArea.y + 0,
borderImgArea.y + border.top,
borderImgArea.y + borderImgArea.height - border.bottom,
};
const nscoord borderWidth[3] = {
border.left,
aBorderArea.width - border.left - border.right,
borderImgArea.width - border.left - border.right,
border.right,
};
const nscoord borderHeight[3] = {
border.top,
aBorderArea.height - border.top - border.bottom,
borderImgArea.height - border.top - border.bottom,
border.bottom,
};
const PRInt32 splitX[3] = {
const PRInt32 sliceX[3] = {
0,
split.left,
imageSize.width - split.right,
slice.left,
imageSize.width - slice.right,
};
const PRInt32 splitY[3] = {
const PRInt32 sliceY[3] = {
0,
split.top,
imageSize.height - split.bottom,
slice.top,
imageSize.height - slice.bottom,
};
const PRInt32 splitWidth[3] = {
split.left,
imageSize.width - split.left - split.right,
split.right,
const PRInt32 sliceWidth[3] = {
slice.left,
PR_MAX(imageSize.width - slice.left - slice.right, 0),
slice.right,
};
const PRInt32 splitHeight[3] = {
split.top,
imageSize.height - split.top - split.bottom,
split.bottom,
const PRInt32 sliceHeight[3] = {
slice.top,
PR_MAX(imageSize.height - slice.top - slice.bottom, 0),
slice.bottom,
};
// In all the 'factor' calculations below, 'border' measurements are
// in app units but 'split' measurements are in image/CSS pixels, so
// in app units but 'slice' measurements are in image/CSS pixels, so
// the factor corresponding to no additional scaling is
// CSSPixelsToAppUnits(1), not simply 1.
for (int i = LEFT; i <= RIGHT; i++) {
for (int j = TOP; j <= BOTTOM; j++) {
nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
nsIntRect subArea(splitX[i], splitY[j], splitWidth[i], splitHeight[j]);
nsIntRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
PRUint8 fillStyleH, fillStyleV;
nsSize unitSize;
if (i == MIDDLE && j == MIDDLE) {
// Discard the middle portion unless set to fill.
if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL ==
aStyleBorder.mBorderImageFill) {
continue;
}
NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL ==
aStyleBorder.mBorderImageFill,
"Unexpected border image fill");
// css-background:
// The middle image's width is scaled by the same factor as the
// top image unless that factor is zero or infinity, in which
@ -2857,57 +2911,57 @@ DrawBorderImage(nsPresContext* aPresContext,
// that, the height is not scaled.
gfxFloat hFactor, vFactor;
if (0 < border.left && 0 < split.left)
vFactor = gfxFloat(border.left)/split.left;
else if (0 < border.right && 0 < split.right)
vFactor = gfxFloat(border.right)/split.right;
if (0 < border.left && 0 < slice.left)
vFactor = gfxFloat(border.left)/slice.left;
else if (0 < border.right && 0 < slice.right)
vFactor = gfxFloat(border.right)/slice.right;
else
vFactor = nsPresContext::CSSPixelsToAppUnits(1);
if (0 < border.top && 0 < split.top)
hFactor = gfxFloat(border.top)/split.top;
else if (0 < border.bottom && 0 < split.bottom)
hFactor = gfxFloat(border.bottom)/split.bottom;
if (0 < border.top && 0 < slice.top)
hFactor = gfxFloat(border.top)/slice.top;
else if (0 < border.bottom && 0 < slice.bottom)
hFactor = gfxFloat(border.bottom)/slice.bottom;
else
hFactor = nsPresContext::CSSPixelsToAppUnits(1);
unitSize.width = splitWidth[i]*hFactor;
unitSize.height = splitHeight[j]*vFactor;
fillStyleH = aStyleBorder.mBorderImageHFill;
fillStyleV = aStyleBorder.mBorderImageVFill;
unitSize.width = sliceWidth[i]*hFactor;
unitSize.height = sliceHeight[j]*vFactor;
fillStyleH = aStyleBorder.mBorderImageRepeatH;
fillStyleV = aStyleBorder.mBorderImageRepeatV;
} else if (i == MIDDLE) { // top, bottom
// Sides are always stretched to the thickness of their border,
// and stretched proportionately on the other axis.
gfxFloat factor;
if (0 < borderHeight[j] && 0 < splitHeight[j])
factor = gfxFloat(borderHeight[j])/splitHeight[j];
if (0 < borderHeight[j] && 0 < sliceHeight[j])
factor = gfxFloat(borderHeight[j])/sliceHeight[j];
else
factor = nsPresContext::CSSPixelsToAppUnits(1);
unitSize.width = splitWidth[i]*factor;
unitSize.width = sliceWidth[i]*factor;
unitSize.height = borderHeight[j];
fillStyleH = aStyleBorder.mBorderImageHFill;
fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH;
fillStyleH = aStyleBorder.mBorderImageRepeatH;
fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
} else if (j == MIDDLE) { // left, right
gfxFloat factor;
if (0 < borderWidth[i] && 0 < splitWidth[i])
factor = gfxFloat(borderWidth[i])/splitWidth[i];
if (0 < borderWidth[i] && 0 < sliceWidth[i])
factor = gfxFloat(borderWidth[i])/sliceWidth[i];
else
factor = nsPresContext::CSSPixelsToAppUnits(1);
unitSize.width = borderWidth[i];
unitSize.height = splitHeight[j]*factor;
fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH;
fillStyleV = aStyleBorder.mBorderImageVFill;
unitSize.height = sliceHeight[j]*factor;
fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
fillStyleV = aStyleBorder.mBorderImageRepeatV;
} else {
// Corners are always stretched to fit the corner.
unitSize.width = borderWidth[i];
unitSize.height = borderHeight[j];
fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH;
fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH;
fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
}
DrawBorderImageComponent(aRenderingContext, aForFrame,
@ -2920,7 +2974,7 @@ DrawBorderImage(nsPresContext* aPresContext,
}
static void
DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
nsIFrame* aForFrame,
imgIContainer* aImage,
const nsRect& aDirtyRect,
@ -2956,8 +3010,8 @@ DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
// If we have no tiling in either direction, we can skip the intermediate
// scaling step.
if ((aHFill == NS_STYLE_BORDER_IMAGE_STRETCH &&
aVFill == NS_STYLE_BORDER_IMAGE_STRETCH) ||
if ((aHFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH &&
aVFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) ||
(aUnitSize.width == aFill.width &&
aUnitSize.height == aFill.height)) {
nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
@ -2969,39 +3023,35 @@ DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
// Compute the scale and position of the master copy of the image.
nsRect tile;
switch (aHFill) {
case NS_STYLE_BORDER_IMAGE_STRETCH:
case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
tile.x = aFill.x;
tile.width = aFill.width;
break;
case NS_STYLE_BORDER_IMAGE_REPEAT:
case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
tile.width = aUnitSize.width;
break;
case NS_STYLE_BORDER_IMAGE_ROUND:
case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
tile.x = aFill.x;
tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width);
break;
default:
NS_NOTREACHED("unrecognized border-image fill style");
}
switch (aVFill) {
case NS_STYLE_BORDER_IMAGE_STRETCH:
case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
tile.y = aFill.y;
tile.height = aFill.height;
break;
case NS_STYLE_BORDER_IMAGE_REPEAT:
case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
tile.height = aUnitSize.height;
break;
case NS_STYLE_BORDER_IMAGE_ROUND:
case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
tile.y = aFill.y;
tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
break;
default:
NS_NOTREACHED("unrecognized border-image fill style");
}

View File

@ -1451,8 +1451,9 @@ nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
nsRect
nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder)
{
return SnapBounds(mSnappingEnabled, mFrame->PresContext(),
nsRect(ToReferenceFrame(), mFrame->GetSize()));
nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
borderBounds.Inflate(mFrame->GetStyleBorder()->GetImageOutset());
return SnapBounds(mSnappingEnabled, mFrame->PresContext(), borderBounds);
}
// Given a region, compute a conservative approximation to it as a list

View File

@ -179,9 +179,6 @@ NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest,
}
// Take requested actions
if (mActions & ACTION_REFLOW_ON_DECODE) {
DoReflow();
}
if (mActions & ACTION_REDRAW_ON_DECODE) {
DoRedraw(nsnull);
}
@ -209,9 +206,6 @@ NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
}
// Take requested actions
if (mActions & ACTION_REFLOW_ON_LOAD) {
DoReflow();
}
if (mActions & ACTION_REDRAW_ON_LOAD) {
DoRedraw(nsnull);
}
@ -238,13 +232,6 @@ NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer,
return NS_OK;
}
void
nsImageLoader::DoReflow()
{
nsIPresShell *shell = mFrame->PresContext()->GetPresShell();
shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
}
void
nsImageLoader::DoRedraw(const nsRect* aDamageRect)
{

View File

@ -69,10 +69,8 @@ public:
* from the network.
*/
enum {
ACTION_REFLOW_ON_DECODE = 0x01,
ACTION_REDRAW_ON_DECODE = 0x02,
ACTION_REFLOW_ON_LOAD = 0x04,
ACTION_REDRAW_ON_LOAD = 0x08
ACTION_REDRAW_ON_DECODE = 0x01,
ACTION_REDRAW_ON_LOAD = 0x02,
};
static already_AddRefed<nsImageLoader>
Create(nsIFrame *aFrame, imgIRequest *aRequest,
@ -102,7 +100,6 @@ public:
private:
nsresult Load(imgIRequest *aImage);
void DoReflow();
/* if aDamageRect is nsnull, the whole frame is redrawn. */
void DoRedraw(const nsRect* aDamageRect);

View File

@ -1382,8 +1382,6 @@ nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
}
PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
if (aStyleBorder->ImageBorderDiffers())
actions |= nsImageLoader::ACTION_REFLOW_ON_LOAD;
nsRefPtr<nsImageLoader> loader =
nsImageLoader::Create(aFrame, borderImage, actions, nsnull);
SetImageLoaders(aFrame, BORDER_IMAGE, loader);

View File

@ -328,9 +328,12 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
#define NS_STYLE_BORDER_STYLE_AUTO 10 // for outline-style only
// See nsStyleBorder mBorderImage
#define NS_STYLE_BORDER_IMAGE_STRETCH 0
#define NS_STYLE_BORDER_IMAGE_REPEAT 1
#define NS_STYLE_BORDER_IMAGE_ROUND 2
#define NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH 0
#define NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT 1
#define NS_STYLE_BORDER_IMAGE_REPEAT_ROUND 2
#define NS_STYLE_BORDER_IMAGE_SLICE_NOFILL 0
#define NS_STYLE_BORDER_IMAGE_SLICE_FILL 1
// See nsStyleDisplay
#define NS_STYLE_CLEAR_NONE 0

View File

@ -89,7 +89,6 @@ _TEST_FILES = \
test_bug404209.xhtml \
test_bug416896.html \
test_bug423523.html \
test_bug445810.html \
test_bug449781.html \
test_bug450930.xhtml \
test_bug458898.html \

View File

@ -1,138 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=445810
-->
<head>
<title>Test for Bug 445810</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/image/test/mochitest/imgutils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445810">Mozilla Bug 445810</a>
<div><p id="display"></p></div>
<div style="display: none;"><img id="currimg"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 445810 **/
// Once the border image is loaded, it isn't necessarily decoded. We need to
// force a decode, but only have a good way of doing that for image elements,
// not border images. However, we can take advantage of the image cache (which
// shares images of the same url) and assign the same url to both.
var currImageElem = document.getElementById("currimg");
function new_image_url()
{
var width = 10;
var height = 10;
var canvas = document.createElement("canvas");
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
var cx = canvas.getContext("2d");
function random_color() {
function random_component() {
return Math.floor(Math.random() * 256);
}
return "rgb(" + random_component() + "," +
random_component() + "," +
random_component() + ")";
}
for (var x = 0; x < width; ++x) {
for (var y = 0; y < height; ++y) {
cx.fillStyle = random_color();
cx.fillRect(x, y, 1, 1);
}
}
return canvas.toDataURL();
}
SimpleTest.waitForExplicitFinish();
var p = document.getElementById("display");
var div = p.parentNode;
var divcs = getComputedStyle(div, "");
div.style.width = "-moz-min-content";
p.style.width = "5px";
p.style.height = "5px";
is(divcs.width, "5px", "correct width without a border");
is(divcs.height, "5px", "correct height without a border");
p.style.border = "3px solid";
is(divcs.width, "11px", // 3 (border-left) + 5 (width) + 3 (border-right)
"correct width without a border image");
is(divcs.height, "11px", // 3 (border-top) + 5 (height) + 3 (border-bottom)
"correct height without a border image");
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "11px", "border image not loaded yet");
is(divcs.height, "11px", "border image not loaded yet");
currImageElem.onload = function() { setTimeout(step2, 0); }
function step2() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
p.style.borderStyle = "none";
is(divcs.width, "9px", "border image still shows with border-style:none");
is(divcs.height, "19px", "border image still shows with border-style:none");
p.style.MozBorderImage = "";
is(divcs.width, "5px", "correct width without a border");
is(divcs.height, "5px", "correct height without a border");
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
currImageElem.onload = function() { setTimeout(step3, 0); }
}
function step3() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
currImageElem.onload = function() { setTimeout(step4, 0); }
}
function step4() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "11px", "border image loading caused reflow");
is(divcs.height, "11px", "border image loading caused reflow");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -4748,7 +4748,30 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame, bool* aAnyOutlineOrEffects,
*aAnyOutlineOrEffects = true;
}
}
// border-image-outset.
// We need to include border-image-outset because it can cause the
// border image to be drawn beyond the border box.
// (1) It's important we not check whether there's a border-image
// since the style hint for a change in border image doesn't cause
// reflow, and that's probably more important than optimizing the
// overflow areas for the silly case of border-image-outset without
// border-image
// (2) It's important that we not check whether the border-image
// is actually loaded, since that would require us to reflow when
// the image loads.
const nsStyleBorder* styleBorder = aFrame->GetStyleBorder();
nsMargin outsetMargin = styleBorder->GetImageOutset();
if (outsetMargin != nsMargin(0, 0, 0, 0)) {
nsRect outsetRect(nsPoint(0, 0), aNewSize);
outsetRect.Inflate(outsetMargin);
r.UnionRect(r, outsetRect);
*aAnyOutlineOrEffects = true;
}
// Note that we don't remove the outlineInnerRect if a frame loses outline
// style. That would require an extra property lookup for every frame,
// or a new frame state bit to track whether a property had been stored,

View File

@ -5,7 +5,9 @@
div {
width: 48px;
height: 48px;
-moz-border-image: url(3x3green-1DD813.png) 0 / 0;
border-width: 0;
border-style: solid;
-moz-border-image: url(3x3green-1DD813.png) 0 fill;
}
</style>
</head><body>

View File

@ -6,7 +6,9 @@ table {
width: 48px;
height: 48px;
border-collapse: separate;
-moz-border-image: url(3x3green-1DD813.png) 0 / 0;
border-width: 0;
border-style: solid;
-moz-border-image: url(3x3green-1DD813.png) 0 fill;
}
</style>
</head><body>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-image-slice without fill reference</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
table { margin: 0; padding: 0; border-spacing: 0; empty-cells: show; }
td { padding: 0; }
</style>
</head>
<body>
<table>
<col style="width: 7px">
<col style="width: 100px">
<col style="width: 7px">
<tr style="height: 7px">
<td style="background: #87f0b4"></td>
<td style="background: #4a298e"></td>
<td style="background: #c98bb7"></td>
</tr>
<tr style="height: 5px">
<td style="background: #90a213"></td>
<td></td>
<td style="background: #90c157"></td>
</tr>
<tr style="height: 7px">
<td style="background: #9d57c1"></td>
<td style="background: #3a8e20"></td>
<td style="background: #0e6f6c"></td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>test of -moz-border-image-slice without fill</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
border-width: 7px;
border-style: solid;
-moz-border-image: url('3x3multicolor.png') 1 1 1 1;
}
</style>
</head>
<body>
<div style="width: 100px; height: 5px"></div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-image-outset: 1em reference</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
</head>
<body>
<div style="border: solid #1DD813 1em; margin: 1em;">
<div style="padding: 1em;">
border.png<br />second longer longer longer longer longer longer line<br />third longer longer longer longer longer longer line
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-outset-width: 1em</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
margin: 2em;
border-width: 1em;
border-style: solid;
-moz-border-image: url('3x3green-1DD813.png') 1 1 1 1 / / 1em;
}
</style>
</head>
<body>
<div>
border.png<br />second longer longer longer longer longer longer line<br />third longer longer longer longer longer longer line
</div>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-outset-width: 1em</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
margin: 2em;
border-width: 1em;
border-style: solid;
-moz-border-image: url('3x3green-1DD813.png') 1 1 1 1;
-moz-border-image-outset: 1em;
}
</style>
</head>
<body>
<div>
border.png<br />second longer longer longer longer longer longer line<br />third longer longer longer longer longer longer line
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-outset-width: 1em</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
margin: 2em;
border-width: 1em;
border-style: solid;
-moz-border-image: 1 1 1 1 / / 1em url('3x3green-1DD813.png');
}
</style>
</head>
<body>
<div>
border.png<br />second longer longer longer longer longer longer line<br />third longer longer longer longer longer longer line
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-image-outset move reference</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
</head>
<body>
<div style="width: 100px; border: solid #1DD813 10px; margin: 10px;">
<div style="padding: 10px;">
Hello World!
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html class="reftest-wait" lang="en-US">
<head>
<title>-moz-border-outset move</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
margin: 20px;
border-width: 10px;
border-style: solid;
-moz-border-image: url('3x3green-1DD813.png') 1 1 1 1;
-moz-border-image-outset: 10px;
}
</style>
</head>
<body>
<div id="container" style="width: 80px; margin-left: 60px;">
Hello World!
</div>
<script type="text/javascript">
// The purpose of this test is to move the border and ensure that the
// overflow area caused by border-image-outset is redrawn.
setTimeout(function() {
document.getElementById("container").style.marginLeft = "20px";
document.documentElement.className = "";
}, 100);
</script>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-image-outset resize reference</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
</head>
<body>
<div style="width: 100px; border: solid #1DD813 10px; margin: 10px;">
<div style="padding: 10px;">
Hello World!
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html class="reftest-wait" lang="en-US">
<head>
<title>-moz-border-outset resize</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
margin: 20px;
border-width: 10px;
border-style: solid;
-moz-border-image: url('3x3green-1DD813.png') 1 1 1 1;
-moz-border-image-outset: 10px;
}
</style>
</head>
<body>
<div id="container" style="width: 180px;">
Hello World!
</div>
<script type="text/javascript">
// The purpose of this test is to resize the border and ensure that the
// overflow area caused by border-image-outset is redrawn.
setTimeout(function() {
document.getElementById("container").style.width = "80px";
document.documentElement.className = "";
}, 100);
</script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>-moz-border-image-width: auto with border-bottom: none reference</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
table { margin: 0; padding: 0; border-spacing: 0; empty-cells: show; }
td { padding: 0; }
</style>
</head>
<body>
<table>
<col style="width: 1px">
<col style="width: 112px"> <!-- 100px + 7px + 7px - 1px - 1px -->
<col style="width: 1px">
<tr style="height: 1px">
<td style="background: #87f0b4"></td>
<td style="background: #4a298e"></td>
<td style="background: #c98bb7"></td>
</tr>
<tr style="height: 10px"> <!-- 5px + 7px - 1px - 1px -->
<td style="background: #90a213"></td>
<td></td>
<td style="background: #90c157"></td>
</tr>
<tr style="height: 1px">
<td style="background: #9d57c1"></td>
<td style="background: #3a8e20"></td>
<td style="background: #0e6f6c"></td>
</tr>
</table>
</body>
</html>
<!DOCTYPE html>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>test of -moz-border-image-width: auto with border-bottom: none</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css">
div {
border-width: 7px;
border-style: solid;
border-bottom: none;
-moz-border-image: url('3x3multicolor.png') 1 1 1 1 / auto;
}
</style>
</head>
<body>
<div style="width: 100px; height: 5px"></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More