mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge last PGO-green changeset from mozilla-inbound to mozilla-central
This commit is contained in:
commit
9ef7070e0d
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
60
build/mobile/robocop/Actions.java.in
Normal file
60
build/mobile/robocop/Actions.java.in
Normal 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);
|
||||
}
|
19
build/mobile/robocop/AndroidManifest.xml.in
Normal file
19
build/mobile/robocop/AndroidManifest.xml.in
Normal 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>
|
78
build/mobile/robocop/Driver.java.in
Normal file
78
build/mobile/robocop/Driver.java.in
Normal 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);
|
||||
}
|
49
build/mobile/robocop/Element.java.in
Normal file
49
build/mobile/robocop/Element.java.in
Normal 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();
|
||||
}
|
197
build/mobile/robocop/FennecNativeActions.java.in
Normal file
197
build/mobile/robocop/FennecNativeActions.java.in
Normal 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);
|
||||
}
|
||||
}
|
425
build/mobile/robocop/FennecNativeDriver.java.in
Normal file
425
build/mobile/robocop/FennecNativeDriver.java.in
Normal 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?");
|
||||
}
|
||||
}
|
149
build/mobile/robocop/FennecNativeElement.java.in
Normal file
149
build/mobile/robocop/FennecNativeElement.java.in
Normal 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;
|
||||
}
|
||||
}
|
136
build/mobile/robocop/Makefile.in
Normal file
136
build/mobile/robocop/Makefile.in
Normal 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 -)
|
||||
|
59
build/mobile/robocop/RoboCopException.java.in
Normal file
59
build/mobile/robocop/RoboCopException.java.in
Normal 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);
|
||||
}
|
||||
}
|
60
build/mobile/robocop/parse_ids.py
Normal file
60
build/mobile/robocop/parse_ids.py
Normal 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()
|
||||
|
5
build/mobile/robocop/res/values/strings.xml
Normal file
5
build/mobile/robocop/res/values/strings.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Roboexample</string>
|
||||
|
||||
</resources>
|
@ -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");
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -1,4 +0,0 @@
|
||||
#filter substitution
|
||||
|
||||
android-res.jar:
|
||||
res/values-@AB_CD@/android_strings.dtd (%android_strings.dtd)
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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. */
|
||||
|
@ -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
@ -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 {
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
@ -60,7 +60,7 @@ f9 = (function() {
|
||||
for each(let w in []) {}
|
||||
}
|
||||
})
|
||||
trap(f9, 22, undefined);
|
||||
trap(f9, 23, undefined);
|
||||
for (b in f9())
|
||||
(function() {})()
|
||||
|
||||
|
@ -4,7 +4,7 @@ function f(){
|
||||
this.zzz.zzz;
|
||||
for(let d in []);
|
||||
}
|
||||
trap(f, 18, '')
|
||||
trap(f, 16, '')
|
||||
try {
|
||||
f()
|
||||
} catch(e) {
|
||||
|
@ -11,5 +11,5 @@ f = (function() {
|
||||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
trap(f, 52, undefined);
|
||||
trap(f, 40, undefined);
|
||||
f()
|
||||
|
6
js/src/jit-test/tests/basic/testBug692274-1.js
Normal file
6
js/src/jit-test/tests/basic/testBug692274-1.js
Normal file
@ -0,0 +1,6 @@
|
||||
// |jit-test| debug; error: TypeError
|
||||
function f() {
|
||||
""(this.z)
|
||||
}
|
||||
trap(f, 0, '')
|
||||
f()
|
7
js/src/jit-test/tests/basic/testBug692274-2.js
Normal file
7
js/src/jit-test/tests/basic/testBug692274-2.js
Normal 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) {}
|
16
js/src/jit-test/tests/basic/testBug692274-3.js
Normal file
16
js/src/jit-test/tests/basic/testBug692274-3.js
Normal 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;
|
||||
}
|
4
js/src/jit-test/tests/basic/testBug692274-4.js
Normal file
4
js/src/jit-test/tests/basic/testBug692274-4.js
Normal file
@ -0,0 +1,4 @@
|
||||
// |jit-test| error: TypeError
|
||||
var obj = {};
|
||||
let ([] = print) 3;
|
||||
let ( i = "a" ) new i [ obj[i] ];
|
12
js/src/jit-test/tests/basic/testBug703857.js
Normal file
12
js/src/jit-test/tests/basic/testBug703857.js
Normal 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();
|
9
js/src/jit-test/tests/basic/testBug709633.js
Normal file
9
js/src/jit-test/tests/basic/testBug709633.js
Normal 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 + '';
|
||||
}
|
342
js/src/jit-test/tests/basic/testLet.js
Normal file
342
js/src/jit-test/tests/basic/testLet.js
Normal 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'});
|
@ -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;
|
@ -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");
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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. */
|
||||
|
@ -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 = ®s.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 = ®s.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, ®s.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() == ®s.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 = ®s.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 = ®s.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
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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: ");
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ®s = 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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
37
layout/reftests/border-image/border-image-nofill-1-ref.html
Normal file
37
layout/reftests/border-image/border-image-nofill-1-ref.html
Normal 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>
|
20
layout/reftests/border-image/border-image-nofill-1.html
Normal file
20
layout/reftests/border-image/border-image-nofill-1.html
Normal 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>
|
15
layout/reftests/border-image/border-image-outset-1-ref.html
Normal file
15
layout/reftests/border-image/border-image-outset-1-ref.html
Normal 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>
|
21
layout/reftests/border-image/border-image-outset-1a.html
Normal file
21
layout/reftests/border-image/border-image-outset-1a.html
Normal 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>
|
22
layout/reftests/border-image/border-image-outset-1b.html
Normal file
22
layout/reftests/border-image/border-image-outset-1b.html
Normal 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>
|
21
layout/reftests/border-image/border-image-outset-1c.html
Normal file
21
layout/reftests/border-image/border-image-outset-1c.html
Normal 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>
|
@ -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>
|
30
layout/reftests/border-image/border-image-outset-move-1.html
Normal file
30
layout/reftests/border-image/border-image-outset-move-1.html
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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
Loading…
Reference in New Issue
Block a user