bool DRW_Block_Record::parseDwg(DRW::Version version, dwgBuffer *buf, duint32 bs){
    dwgBuffer sBuff = *buf;
    dwgBuffer *sBuf = buf;
    if (version > DRW::AC1018) {//2007+
        sBuf = &sBuff; //separate buffer for strings
    }
    bool ret = DRW_TableEntry::parseDwg(version, buf, sBuf, bs);
    DRW_DBG("\n***************************** parsing block record ******************************************\n");
    if (!ret)
        return ret;
    duint32 insertCount = 0;//only 2000+
    duint32 objectCount = 0; //only 2004+

    name = sBuf->getVariableText(version, false);
    DRW_DBG("block record name: "); DRW_DBG(name.c_str()); DRW_DBG("\n");

    flags |= buf->getBit()<< 6;//referenced external reference, block code 70, bit 7 (64)
    if (version > DRW::AC1018) {//2007+
    } else {//2004- //RLZ: verify in 2004, 2010 &2013
        dint16 xrefindex = buf->getBitShort();
        DRW_DBG(" xrefindex: "); DRW_DBG(xrefindex); DRW_DBG("\n");
    }
    flags |= buf->getBit() << 4;//is refx dependent, block code 70, bit 5 (16)
    flags |= buf->getBit(); //if is anonymous block (*U) block code 70, bit 1 (1)
    flags |= buf->getBit() << 1; //if block contains attdefs, block code 70, bit 2 (2)
    bool blockIsXref = buf->getBit(); //if is a Xref, block code 70, bit 3 (4)
    bool xrefOverlaid = buf->getBit(); //if is a overlaid Xref, block code 70, bit 4 (8)
    flags |= blockIsXref << 2; //if is a Xref, block code 70, bit 3 (4)
    flags |= xrefOverlaid << 3; //if is a overlaid Xref, block code 70, bit 4 (8)
    if (version > DRW::AC1014) {//2000+
        flags |= buf->getBit() << 5; //if is a loaded Xref, block code 70, bit 6 (32)
    }
    DRW_DBG("flags: "); DRW_DBG(flags); DRW_DBG(", ");
    if (version > DRW::AC1015) {//2004+ fails in 2007
        objectCount = buf->getBitLong(); //Number of objects owned by this block
        entMap.reserve(objectCount);
    }
    basePoint.x = buf->getBitDouble();
    basePoint.y = buf->getBitDouble();
    basePoint.z = buf->getBitDouble();
    DRW_DBG("insertion point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n");
    UTF8STRING path = sBuf->getVariableText(version, false);
    DRW_DBG("XRef path name: "); DRW_DBG(path.c_str()); DRW_DBG("\n");

    if (version > DRW::AC1014) {//2000+
        insertCount = 0;
        while (duint8 i = buf->getRawChar8() != 0)
            insertCount +=i;
        UTF8STRING bkdesc = sBuf->getVariableText(version, false);
        DRW_DBG("Block description: "); DRW_DBG(bkdesc.c_str()); DRW_DBG("\n");

        duint32 prevData = buf->getBitLong();
        for (unsigned int j= 0; j < prevData; ++j)
            buf->getRawChar8();
    }
    if (version > DRW::AC1018) {//2007+
        duint16 insUnits = buf->getBitShort();
        bool canExplode = buf->getBit(); //if block can be exploded
        duint8 bkScaling = buf->getRawChar8();

        DRW_UNUSED(insUnits);
        DRW_UNUSED(canExplode);
        DRW_UNUSED(bkScaling);
    }

    if (version > DRW::AC1018) {//2007+ skip string area
        buf->setPosition(objSize >> 3);
        buf->setBitPos(objSize & 7);
    }

    dwgHandle blockControlH = buf->getHandle();
    DRW_DBG("block control Handle: "); DRW_DBGHL(blockControlH.code, blockControlH.size, blockControlH.ref); DRW_DBG("\n");
    parentHandle = blockControlH.ref;

    for (int i=0; i<numReactors; i++){
        dwgHandle reactorH = buf->getHandle();
        DRW_DBG(" reactor Handle #"); DRW_DBG(i); DRW_DBG(": "); DRW_DBGHL(reactorH.code, reactorH.size, reactorH.ref); DRW_DBG("\n");
    }
    if (xDictFlag !=1) {//R14+ //seems present in 2000
        dwgHandle XDicObjH = buf->getHandle();
        DRW_DBG(" XDicObj control Handle: "); DRW_DBGHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref); DRW_DBG("\n");
    }
    if (version != DRW::AC1021) {//2007+ XDicObjH or NullH not present
    }
    dwgHandle NullH = buf->getHandle();
    DRW_DBG(" NullH control Handle: "); DRW_DBGHL(NullH.code, NullH.size, NullH.ref); DRW_DBG("\n");
    dwgHandle blockH = buf->getOffsetHandle(handle);
    DRW_DBG(" blockH Handle: "); DRW_DBGHL(blockH.code, blockH.size, blockH.ref); DRW_DBG("\n");
    block = blockH.ref;

    if (version > DRW::AC1015) {//2004+
        for (unsigned int i=0; i< objectCount; i++){
            dwgHandle entityH = buf->getHandle();
            DRW_DBG(" entityH Handle #"); DRW_DBG(i); DRW_DBG(": "); DRW_DBGHL(entityH.code, entityH.size, entityH.ref); DRW_DBG("\n");
            entMap.push_back(entityH.ref);
        }
    } else {//2000-
        if(!blockIsXref && !xrefOverlaid){
            dwgHandle firstH = buf->getHandle();
            DRW_DBG(" firstH entity Handle: "); DRW_DBGHL(firstH.code, firstH.size, firstH.ref); DRW_DBG("\n");
            firstEH = firstH.ref;
            dwgHandle lastH = buf->getHandle();
            DRW_DBG(" lastH entity Handle: "); DRW_DBGHL(lastH.code, lastH.size, lastH.ref); DRW_DBG("\n");
            lastEH = lastH.ref;
        }
    }
    DRW_DBG("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes()); DRW_DBG("\n");
    dwgHandle endBlockH = buf->getOffsetHandle(handle);
    DRW_DBG(" endBlockH Handle: "); DRW_DBGHL(endBlockH.code, endBlockH.size, endBlockH.ref); DRW_DBG("\n");
    endBlock = endBlockH.ref;

    if (version > DRW::AC1014) {//2000+
        for (unsigned int i=0; i< insertCount; i++){
            dwgHandle insertsH = buf->getHandle();
            DRW_DBG(" insertsH Handle #"); DRW_DBG(i); DRW_DBG(": "); DRW_DBGHL(insertsH.code, insertsH.size, insertsH.ref); DRW_DBG("\n");
        }
        DRW_DBG("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes()); DRW_DBG("\n");
        dwgHandle layoutH = buf->getHandle();
        DRW_DBG(" layoutH Handle: "); DRW_DBGHL(layoutH.code, layoutH.size, layoutH.ref); DRW_DBG("\n");
    }
    DRW_DBG("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes()); DRW_DBG("\n\n");
//    RS crc;   //RS */
    return buf->isGood();
}


void DRW_Vport::parseCode(int code, dxfReader *reader){
    switch (code) {
    case 10:
        lowerLeft.x = reader->getDouble();
        break;
    case 20:
        lowerLeft.y = reader->getDouble();
        break;
    case 11:
        UpperRight.x = reader->getDouble();
        break;
    case 21:
        UpperRight.y = reader->getDouble();
        break;
    case 12:
        center.x = reader->getDouble();
        break;
    case 22:
        center.y = reader->getDouble();
        break;
    case 13:
        snapBase.x = reader->getDouble();
        break;
    case 23:
        snapBase.y = reader->getDouble();
        break;
    case 14:
        snapSpacing.x = reader->getDouble();
        break;
    case 24:
        snapSpacing.y = reader->getDouble();
        break;
    case 15:
        gridSpacing.x = reader->getDouble();
        break;
    case 25:
        gridSpacing.y = reader->getDouble();
        break;
    case 16:
        viewDir.x = reader->getDouble();
        break;
    case 26:
        viewDir.y = reader->getDouble();
        break;
    case 36:
        viewDir.z = reader->getDouble();
        break;
    case 17:
        viewTarget.x = reader->getDouble();
        break;
    case 27:
        viewTarget.y = reader->getDouble();
        break;
    case 37:
        viewTarget.z = reader->getDouble();
        break;
    case 40:
        height = reader->getDouble();
        break;
    case 41:
        ratio = reader->getDouble();
        break;
    case 42:
        lensHeight = reader->getDouble();
        break;
    case 43:
        frontClip = reader->getDouble();
        break;
    case 44:
        backClip = reader->getDouble();
        break;
    case 50:
        snapAngle = reader->getDouble();
        break;
    case 51:
        twistAngle = reader->getDouble();
        break;
    case 71:
        viewMode = reader->getInt32();
        break;
    case 72:
        circleZoom = reader->getInt32();
        break;
    case 73:
        fastZoom = reader->getInt32();
        break;
    case 74:
        ucsIcon = reader->getInt32();
        break;
    case 75:
        snap = reader->getInt32();
        break;
    case 76:
        grid = reader->getInt32();
        break;
    case 77:
        snapStyle = reader->getInt32();
        break;
    case 78:
        snapIsopair = reader->getInt32();
        break;
    default:
        DRW_TableEntry::parseCode(code, reader);
        break;
    }
}


    void reset(){
        tType = DRW::IMAGEDEF;
        imgVersion = 0;
        DRW_TableEntry::reset();
    }


static void addrange(struct cstate *g, Rune a, Rune b)
{
	if (a > b)
		die(g, "invalid character class range");
	if (g->yycc->end + 2 >= g->yycc->spans + nelem(g->yycc->spans))
		die(g, "too many character class ranges");
	*g->yycc->end++ = a;
	*g->yycc->end++ = b;
}


static void dumpprog(Reprog *prog)
{
	Reinst *inst;
	int i;
	for (i = 0, inst = prog->start; inst < prog->end; ++i, ++inst) {
		printf("% 5d: ", i);
		switch (inst->opcode) {
		case I_END: puts("end"); break;
		case I_JUMP: printf("jump %d\n", (int)(inst->x - prog->start)); break;
		case I_SPLIT: printf("split %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
		case I_PLA: printf("pla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
		case I_NLA: printf("nla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
		case I_ANY: puts("any"); break;
		case I_ANYNL: puts("anynl"); break;
		case I_CHAR: printf(inst->c >= 32 && inst->c < 127 ? "char '%c'\n" : "char U+%04X\n", inst->c); break;
		case I_CCLASS: printf("cclass"); dumpcclass(inst->cc); break;
		case I_NCCLASS: printf("ncclass"); dumpcclass(inst->cc); break;
		case I_REF: printf("ref %d\n", inst->n); break;
		case I_BOL: puts("bol"); break;
		case I_EOL: puts("eol"); break;
		case I_WORD: puts("word"); break;
		case I_NWORD: puts("nword"); break;
		case I_LPAR: printf("lpar %d\n", inst->n); break;
		case I_RPAR: printf("rpar %d\n", inst->n); break;
		}
	}
}


static int lex(struct cstate *g)
{
	int quoted = nextrune(g);
	if (quoted) {
		switch (g->yychar) {
		case 'b': return L_WORD;
		case 'B': return L_NWORD;
		case 'd': newcclass(g); addranges_d(g); return L_CCLASS;
		case 's': newcclass(g); addranges_s(g); return L_CCLASS;
		case 'w': newcclass(g); addranges_w(g); return L_CCLASS;
		case 'D': newcclass(g); addranges_d(g); return L_NCCLASS;
		case 'S': newcclass(g); addranges_s(g); return L_NCCLASS;
		case 'W': newcclass(g); addranges_w(g); return L_NCCLASS;
		case '0': g->yychar = 0; return L_CHAR;
		}
		if (g->yychar >= '0' && g->yychar <= '9') {
			g->yychar -= '0';
			if (*g->source >= '0' && *g->source <= '9')
				g->yychar = g->yychar * 10 + *g->source++ - '0';
			return L_REF;
		}
		return L_CHAR;
	}

	switch (g->yychar) {
	case EOF:
	case '$': case ')': case '*': case '+':
	case '.': case '?': case '^': case '|':
		return g->yychar;
	}

	if (g->yychar == '{')
		return lexcount(g);
	if (g->yychar == '[')
		return lexclass(g);
	if (g->yychar == '(') {
		if (g->source[0] == '?') {
			if (g->source[1] == ':') {
				g->source += 2;
				return L_NC;
			}
			if (g->source[1] == '=') {
				g->source += 2;
				return L_PLA;
			}
			if (g->source[1] == '!') {
				g->source += 2;
				return L_NLA;
			}
		}
		return '(';
	}

	return L_CHAR;
}


static void pexpi(int d, int p, js_Ast *exp)
{
	int tp, paren;

	if (!exp) return;

	tp = prec(exp->type);
	paren = 0;
	if (tp < p) {
		pc('(');
		paren = 1;
	}
	p = tp;

	switch (exp->type) {
	case AST_IDENTIFIER: ps(exp->string); break;
	case EXP_IDENTIFIER: ps(exp->string); break;
	case EXP_NUMBER: printf("%.9g", exp->number); break;
	case EXP_STRING: pstr(exp->string); break;
	case EXP_REGEXP: pregexp(exp->string, exp->number); break;

	case EXP_UNDEF: break;
	case EXP_NULL: ps("null"); break;
	case EXP_TRUE: ps("true"); break;
	case EXP_FALSE: ps("false"); break;
	case EXP_THIS: ps("this"); break;

	case EXP_OBJECT: pobject(d, exp->a); break;
	case EXP_ARRAY: parray(d, exp->a); break;

	case EXP_DELETE: puna(d, p, exp, "delete ", ""); break;
	case EXP_VOID: puna(d, p, exp, "void ", ""); break;
	case EXP_TYPEOF: puna(d, p, exp, "typeof ", ""); break;
	case EXP_PREINC: puna(d, p, exp, "++", ""); break;
	case EXP_PREDEC: puna(d, p, exp, "--", ""); break;
	case EXP_POSTINC: puna(d, p, exp, "", "++"); break;
	case EXP_POSTDEC: puna(d, p, exp, "", "--"); break;
	case EXP_POS: puna(d, p, exp, "+", ""); break;
	case EXP_NEG: puna(d, p, exp, "-", ""); break;
	case EXP_BITNOT: puna(d, p, exp, "~", ""); break;
	case EXP_LOGNOT: puna(d, p, exp, "!", ""); break;

	case EXP_LOGOR: pbin(d, p, exp, "||"); break;
	case EXP_LOGAND: pbin(d, p, exp, "&&"); break;
	case EXP_BITOR: pbin(d, p, exp, "|"); break;
	case EXP_BITXOR: pbin(d, p, exp, "^"); break;
	case EXP_BITAND: pbin(d, p, exp, "&"); break;
	case EXP_EQ: pbin(d, p, exp, "=="); break;
	case EXP_NE: pbin(d, p, exp, "!="); break;
	case EXP_STRICTEQ: pbin(d, p, exp, "==="); break;
	case EXP_STRICTNE: pbin(d, p, exp, "!=="); break;
	case EXP_LT: pbin(d, p, exp, "<"); break;
	case EXP_GT: pbin(d, p, exp, ">"); break;
	case EXP_LE: pbin(d, p, exp, "<="); break;
	case EXP_GE: pbin(d, p, exp, ">="); break;
	case EXP_IN: pbin(d, p, exp, "in"); break;
	case EXP_SHL: pbin(d, p, exp, "<<"); break;
	case EXP_SHR: pbin(d, p, exp, ">>"); break;
	case EXP_USHR: pbin(d, p, exp, ">>>"); break;
	case EXP_ADD: pbin(d, p, exp, "+"); break;
	case EXP_SUB: pbin(d, p, exp, "-"); break;
	case EXP_MUL: pbin(d, p, exp, "*"); break;
	case EXP_DIV: pbin(d, p, exp, "/"); break;
	case EXP_MOD: pbin(d, p, exp, "%"); break;
	case EXP_ASS: pbin(d, p, exp, "="); break;
	case EXP_ASS_MUL: pbin(d, p, exp, "*="); break;
	case EXP_ASS_DIV: pbin(d, p, exp, "/="); break;
	case EXP_ASS_MOD: pbin(d, p, exp, "%="); break;
	case EXP_ASS_ADD: pbin(d, p, exp, "+="); break;
	case EXP_ASS_SUB: pbin(d, p, exp, "-="); break;
	case EXP_ASS_SHL: pbin(d, p, exp, "<<="); break;
	case EXP_ASS_SHR: pbin(d, p, exp, ">>="); break;
	case EXP_ASS_USHR: pbin(d, p, exp, ">>>="); break;
	case EXP_ASS_BITAND: pbin(d, p, exp, "&="); break;
	case EXP_ASS_BITXOR: pbin(d, p, exp, "^="); break;
	case EXP_ASS_BITOR: pbin(d, p, exp, "|="); break;

	case EXP_INSTANCEOF:
		pexpi(d, p, exp->a);
		ps(" instanceof ");
		pexpi(d, p, exp->b);
		break;

	case EXP_COMMA:
		pexpi(d, p, exp->a);
		pc(','); sp();
		pexpi(d, p, exp->b);
		break;

	case EXP_COND:
		pexpi(d, p, exp->a);
		sp(); pc('?'); sp();
		pexpi(d, p, exp->b);
		sp(); pc(':'); sp();
		pexpi(d, p, exp->c);
		break;

	case EXP_INDEX:
		pexpi(d, p, exp->a);
		pc('[');
		pexpi(d, 0, exp->b);
		pc(']');
		break;

	case EXP_MEMBER:
		pexpi(d, p, exp->a);
		pc('.');
		pexpi(d, 0, exp->b);
		break;

	case EXP_CALL:
		pexpi(d, p, exp->a);
		pc('(');
		pargs(d, exp->b);
		pc(')');
		break;

	case EXP_NEW:
		ps("new ");
		pexpi(d, p, exp->a);
		pc('(');
		pargs(d, exp->b);
		pc(')');
		break;

	case EXP_FUN:
		if (p == 0) pc('(');
		ps("function ");
		pexpi(d, 0, exp->a);
		pc('(');
		pargs(d, exp->b);
		pc(')'); sp(); pc('{'); nl();
		pstmlist(d, exp->c);
		in(d); pc('}');
		if (p == 0) pc(')');
		break;

	default:
		ps("<UNKNOWN>");
		break;
	}

	if (paren) pc(')');
}


static void comma(void)
{
	putchar(',');
	sp();
}


static void pargs(int d, js_Ast *list)
{
	while (list) {
		assert(list->type == AST_LIST);
		pexpi(d, COMMA, list->a);
		list = list->b;
		if (list)
			comma();
	}
}


static void pbin(int d, int p, js_Ast *exp, const char *op)
{
	pexpi(d, p, exp->a);
	sp();
	ps(op);
	sp();
	pexpi(d, p, exp->b);
}


static void sp(void)
{
	if (minify < 1)
		putchar(' ');
}


        std::unordered_map<std::string, std::string> get_dict(const std::string& name) const
        {
            std::unordered_map<std::string, std::string> ret;

            int count = 0;
            while (1)
            {
                if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++))
                    ret.insert(*element);
                else
                    break;
            }
            return ret;
        }


        std::vector<char*> pop_list(const std::string& name, bool use_brackets = true)
        {
            std::vector<char*> ret = get_list(name, use_brackets);
            if (!ret.empty())
            {
                for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
                {
                    std::string str_item(key_value_pairs_[i]);
                    if ((use_brackets ? (str_item.substr(0, name.size() + 3) == name + "[]=") : (str_item.substr(0, name.size() + 1) == name + '=')))
                    {
                        key_value_pairs_.erase(key_value_pairs_.begin() + i--);
                    }
                }
            }
            return ret;
        }


uint32_t __div64_32(uint64_t *n, uint32_t base)
{
    uint64_t rem = *n;
    uint64_t b = base;
    uint64_t res, d = 1;
    uint32_t high = rem >> 32;

    /* Reduce the thing a bit first */
    res = 0;
    if (high >= base) {
        high /= base;
        res = (uint64_t) high << 32;
        rem -= (uint64_t) (high*base) << 32;
    }

    while ((int64_t)b > 0 && b < rem) {
        b = b+b;
        d = d+d;
    }

    do {
        if (rem >= b) {
            rem -= b;
            res += d;
        }
        b >>= 1;
        d >>= 1;
    } while (d);

    *n = res;
    return rem;
}


packet_stats(const tcpreplay_stats_t *stats)
{
    struct timeval diff;
    COUNTER diff_us;
    COUNTER bytes_sec = 0;
    u_int32_t bytes_sec_10ths = 0;
    COUNTER mb_sec = 0;
    u_int32_t mb_sec_100ths = 0;
    u_int32_t mb_sec_1000ths = 0;
    COUNTER pkts_sec = 0;
    u_int32_t pkts_sec_100ths = 0;

    timersub(&stats->end_time, &stats->start_time, &diff);
    diff_us = TIMEVAL_TO_MICROSEC(&diff);

    if (diff_us && stats->pkts_sent && stats->bytes_sent) {
        COUNTER bytes_sec_X10;
        COUNTER pkts_sec_X100;
        COUNTER mb_sec_X1000;
        COUNTER mb_sec_X100;

        if (stats->bytes_sent > 1000 * 1000 * 1000 && diff_us > 1000 * 1000) {
            bytes_sec_X10 = (stats->bytes_sent * 10 * 1000) / (diff_us / 1000);
            pkts_sec_X100 = (stats->pkts_sent * 100 * 1000) / (diff_us / 1000);
         } else {
            bytes_sec_X10 = (stats->bytes_sent * 10 * 1000 * 1000) / diff_us;
            pkts_sec_X100 = (stats->pkts_sent * 100 * 1000 * 1000) / diff_us;
         }

        bytes_sec = bytes_sec_X10 / 10;
        bytes_sec_10ths = bytes_sec_X10 % 10;

        mb_sec_X1000 = (bytes_sec * 8) / 1000;
        mb_sec_X100 = mb_sec_X1000 / 10;
        mb_sec = mb_sec_X1000 / 1000;
        mb_sec_100ths = mb_sec_X100 % 100;
        mb_sec_1000ths = mb_sec_X1000 % 1000;

        pkts_sec = pkts_sec_X100 / 100;
        pkts_sec_100ths = pkts_sec_X100 % 100;
    }

    if (diff_us >= 1000 * 1000)
        printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n",
                stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_usec / (10 * 1000)));
    else
        printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n",
                stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_usec);

    if (mb_sec >= 1)
        printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n",
               bytes_sec, bytes_sec_10ths, mb_sec, mb_sec_100ths, pkts_sec, pkts_sec_100ths);
    else
        printf("Rated: %llu.%1u Bps, %llu.%03u Mbps, %llu.%02u pps\n",
               bytes_sec, bytes_sec_10ths, mb_sec, mb_sec_1000ths, pkts_sec, pkts_sec_100ths);
    fflush(NULL);

    if (stats->failed)
        printf("Failed write attempts: " COUNTER_SPEC "\n",
                stats->failed);
}


_our_safe_free(void *ptr, const char *funcname, const int line, const char *file)
{
    assert(funcname);
    assert(line);
    assert(file);

    if (ptr == NULL)
        return;

    free(ptr);
}


ephy_string_get_host_name (const char *url)
{
  g_autoptr (GUri) uri = NULL;

  if (url == NULL ||
      g_str_has_prefix (url, "file://") ||
      g_str_has_prefix (url, "about:") ||
      g_str_has_prefix (url, "ephy-about:"))
    return NULL;

  uri = g_uri_parse (url, G_URI_FLAGS_NONE, NULL);
  /* If uri is NULL it's very possible that we just got
   * something without a scheme, let's try to prepend
   * 'http://' */
  if (uri == NULL) {
    char *effective_url = g_strconcat ("http://", url, NULL);
    uri = g_uri_parse (effective_url, G_URI_FLAGS_NONE, NULL);
    g_free (effective_url);
  }

  if (uri == NULL)
    return NULL;

  return g_strdup (g_uri_get_host (uri));
}


ephy_string_find_and_replace (const char *haystack,
                              const char *to_find,
                              const char *to_repl)
{
  GString *str;

  g_assert (haystack);
  g_assert (to_find);
  g_assert (to_repl);

  str = g_string_new (haystack);
  g_string_replace (str, to_find, to_repl, 0);
  return g_string_free (str, FALSE);
}


ephy_string_blank_chr (char *source)
{
  char *p;

  if (source == NULL)
    return NULL;

  p = source;
  while (*p != '\0') {
    if ((guchar) * p < 0x20)
      *p = ' ';

    p++;
  }

  return source;
}


read_Header(struct archive_read *a, struct _7z_header_info *h,
    int check_header_id)
{
	struct _7zip *zip = (struct _7zip *)a->format->data;
	const unsigned char *p;
	struct _7z_folder *folders;
	struct _7z_stream_info *si = &(zip->si);
	struct _7zip_entry *entries;
	uint32_t folderIndex, indexInFolder;
	unsigned i;
	int eindex, empty_streams, sindex;

	if (check_header_id) {
		/*
		 * Read Header.
		 */
		if ((p = header_bytes(a, 1)) == NULL)
			return (-1);
		if (*p != kHeader)
			return (-1);
	}

	/*
	 * Read ArchiveProperties.
	 */
	if ((p = header_bytes(a, 1)) == NULL)
		return (-1);
	if (*p == kArchiveProperties) {
		for (;;) {
			uint64_t size;
			if ((p = header_bytes(a, 1)) == NULL)
				return (-1);
			if (*p == 0)
				break;
			if (parse_7zip_uint64(a, &size) < 0)
				return (-1);
		}
		if ((p = header_bytes(a, 1)) == NULL)
			return (-1);
	}

	/*
	 * Read MainStreamsInfo.
	 */
	if (*p == kMainStreamsInfo) {
		if (read_StreamsInfo(a, &(zip->si)) < 0)
			return (-1);
		if ((p = header_bytes(a, 1)) == NULL)
			return (-1);
	}
	if (*p == kEnd)
		return (0);

	/*
	 * Read FilesInfo.
	 */
	if (*p != kFilesInfo)
		return (-1);

	if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
		return (-1);
	if (UMAX_ENTRY < zip->numFiles)
		return (-1);

	zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
	if (zip->entries == NULL)
		return (-1);
	entries = zip->entries;

	empty_streams = 0;
	for (;;) {
		int type;
		uint64_t size;
		size_t ll;

		if ((p = header_bytes(a, 1)) == NULL)
			return (-1);
		type = *p;
		if (type == kEnd)
			break;

		if (parse_7zip_uint64(a, &size) < 0)
			return (-1);
		if (zip->header_bytes_remaining < size)
			return (-1);
		ll = (size_t)size;

		switch (type) {
		case kEmptyStream:
			if (h->emptyStreamBools != NULL)
				return (-1);
			h->emptyStreamBools = calloc((size_t)zip->numFiles,
			    sizeof(*h->emptyStreamBools));
			if (h->emptyStreamBools == NULL)
				return (-1);
			if (read_Bools(
			    a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
				return (-1);
			empty_streams = 0;
			for (i = 0; i < zip->numFiles; i++) {
				if (h->emptyStreamBools[i])
					empty_streams++;
			}
			break;
		case kEmptyFile:
			if (empty_streams <= 0) {
				/* Unexcepted sequence. Skip this. */
				if (header_bytes(a, ll) == NULL)
					return (-1);
				break;
			}
			if (h->emptyFileBools != NULL)
				return (-1);
			h->emptyFileBools = calloc(empty_streams,
			    sizeof(*h->emptyFileBools));
			if (h->emptyFileBools == NULL)
				return (-1);
			if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
				return (-1);
			break;
		case kAnti:
			if (empty_streams <= 0) {
				/* Unexcepted sequence. Skip this. */
				if (header_bytes(a, ll) == NULL)
					return (-1);
				break;
			}
			if (h->antiBools != NULL)
				return (-1);
			h->antiBools = calloc(empty_streams,
			    sizeof(*h->antiBools));
			if (h->antiBools == NULL)
				return (-1);
			if (read_Bools(a, h->antiBools, empty_streams) < 0)
				return (-1);
			break;
		case kCTime:
		case kATime:
		case kMTime:
			if (read_Times(a, h, type) < 0)
				return (-1);
			break;
		case kName:
		{
			unsigned char *np;
			size_t nl, nb;

			/* Skip one byte. */
			if ((p = header_bytes(a, 1)) == NULL)
				return (-1);
			ll--;

			if ((ll & 1) || ll < zip->numFiles * 4)
				return (-1);

			if (zip->entry_names != NULL)
				return (-1);
			zip->entry_names = malloc(ll);
			if (zip->entry_names == NULL)
				return (-1);
			np = zip->entry_names;
			nb = ll;
			/*
			 * Copy whole file names.
			 * NOTE: This loop prevents from expanding
			 * the uncompressed buffer in order not to
			 * use extra memory resource.
			 */
			while (nb) {
				size_t b;
				if (nb > UBUFF_SIZE)
					b = UBUFF_SIZE;
				else
					b = nb;
				if ((p = header_bytes(a, b)) == NULL)
					return (-1);
				memcpy(np, p, b);
				np += b;
				nb -= b;
			}
			np = zip->entry_names;
			nl = ll;

			for (i = 0; i < zip->numFiles; i++) {
				entries[i].utf16name = np;
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
				entries[i].wname = (wchar_t *)np;
#endif

				/* Find a terminator. */
				while (nl >= 2 && (np[0] || np[1])) {
					np += 2;
					nl -= 2;
				}
				if (nl < 2)
					return (-1);/* Terminator not found */
				entries[i].name_len = np - entries[i].utf16name;
				np += 2;
				nl -= 2;
			}
			break;
		}
		case kAttributes:
		{
			int allAreDefined;

			if ((p = header_bytes(a, 2)) == NULL)
				return (-1);
			allAreDefined = *p;
			if (h->attrBools != NULL)
				return (-1);
			h->attrBools = calloc((size_t)zip->numFiles,
			    sizeof(*h->attrBools));
			if (h->attrBools == NULL)
				return (-1);
			if (allAreDefined)
				memset(h->attrBools, 1, (size_t)zip->numFiles);
			else {
				if (read_Bools(a, h->attrBools,
				      (size_t)zip->numFiles) < 0)
					return (-1);
			}
			for (i = 0; i < zip->numFiles; i++) {
				if (h->attrBools[i]) {
					if ((p = header_bytes(a, 4)) == NULL)
						return (-1);
					entries[i].attr = archive_le32dec(p);
				}
			}
			break;
		}
		case kDummy:
			if (ll == 0)
				break;
			__LA_FALLTHROUGH;
		default:
			if (header_bytes(a, ll) == NULL)
				return (-1);
			break;
		}
	}

	/*
	 * Set up entry's attributes.
	 */
	folders = si->ci.folders;
	eindex = sindex = 0;
	folderIndex = indexInFolder = 0;
	for (i = 0; i < zip->numFiles; i++) {
		if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)
			entries[i].flg |= HAS_STREAM;
		/* The high 16 bits of attributes is a posix file mode. */
		entries[i].mode = entries[i].attr >> 16;
		if (entries[i].flg & HAS_STREAM) {
			if ((size_t)sindex >= si->ss.unpack_streams)
				return (-1);
			if (entries[i].mode == 0)
				entries[i].mode = AE_IFREG | 0666;
			if (si->ss.digestsDefined[sindex])
				entries[i].flg |= CRC32_IS_SET;
			entries[i].ssIndex = sindex;
			sindex++;
		} else {
			int dir;
			if (h->emptyFileBools == NULL)
				dir = 1;
			else {
				if (h->emptyFileBools[eindex])
					dir = 0;
				else
					dir = 1;
				eindex++;
			}
			if (entries[i].mode == 0) {
				if (dir)
					entries[i].mode = AE_IFDIR | 0777;
				else
					entries[i].mode = AE_IFREG | 0666;
			} else if (dir &&
			    (entries[i].mode & AE_IFMT) != AE_IFDIR) {
				entries[i].mode &= ~AE_IFMT;
				entries[i].mode |= AE_IFDIR;
			}
			if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&
			    entries[i].name_len >= 2 &&
			    (entries[i].utf16name[entries[i].name_len-2] != '/' ||
			     entries[i].utf16name[entries[i].name_len-1] != 0)) {
				entries[i].utf16name[entries[i].name_len] = '/';
				entries[i].utf16name[entries[i].name_len+1] = 0;
				entries[i].name_len += 2;
			}
			entries[i].ssIndex = -1;
		}
		if (entries[i].attr & 0x01)
			entries[i].mode &= ~0222;/* Read only. */

		if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
			/*
			 * The entry is an empty file or a directory file,
			 * those both have no contents.
			 */
			entries[i].folderIndex = -1;
			continue;
		}
		if (indexInFolder == 0) {
			for (;;) {
				if (folderIndex >= si->ci.numFolders)
					return (-1);
				if (folders[folderIndex].numUnpackStreams)
					break;
				folderIndex++;
			}
		}
		entries[i].folderIndex = folderIndex;
		if ((entries[i].flg & HAS_STREAM) == 0)
			continue;
		indexInFolder++;
		if (indexInFolder >= folders[folderIndex].numUnpackStreams) {
			folderIndex++;
			indexInFolder = 0;
		}
	}

	return (0);
}


read_stream(struct archive_read *a, const void **buff, size_t size,
    size_t minimum)
{
	struct _7zip *zip = (struct _7zip *)a->format->data;
	uint64_t skip_bytes = 0;
	ssize_t r;

	if (zip->uncompressed_buffer_bytes_remaining == 0) {
		if (zip->pack_stream_inbytes_remaining > 0) {
			r = extract_pack_stream(a, 0);
			if (r < 0)
				return (r);
			return (get_uncompressed_data(a, buff, size, minimum));
		} else if (zip->folder_outbytes_remaining > 0) {
			/* Extract a remaining pack stream. */
			r = extract_pack_stream(a, 0);
			if (r < 0)
				return (r);
			return (get_uncompressed_data(a, buff, size, minimum));
		}
	} else
		return (get_uncompressed_data(a, buff, size, minimum));

	/*
	 * Current pack stream has been consumed.
	 */
	if (zip->pack_stream_remaining == 0) {
		if (zip->header_is_being_read) {
			/* Invalid sequence. This might happen when
			 * reading a malformed archive. */
			archive_set_error(&(a->archive),
			    ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");
			return (ARCHIVE_FATAL);
		}

		/*
		 * All current folder's pack streams have been
		 * consumed. Switch to next folder.
		 */
		if (zip->folder_index == 0 &&
		    (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
		     || zip->folder_index != zip->entry->folderIndex)) {
			zip->folder_index = zip->entry->folderIndex;
			skip_bytes =
			    zip->si.ci.folders[zip->folder_index].skipped_bytes;
		}

		if (zip->folder_index >= zip->si.ci.numFolders) {
			/*
			 * We have consumed all folders and its pack streams.
			 */
			*buff = NULL;
			return (0);
		}
		r = setup_decode_folder(a,
			&(zip->si.ci.folders[zip->folder_index]), 0);
		if (r != ARCHIVE_OK)
			return (ARCHIVE_FATAL);

		zip->folder_index++;
	}

	/*
	 * Switch to next pack stream.
	 */
	r = seek_pack(a);
	if (r < 0)
		return (r);

	/* Extract a new pack stream. */
	r = extract_pack_stream(a, 0);
	if (r < 0)
		return (r);

	/*
	 * Skip the bytes we already has skipped in skip_stream().
	 */
	while (skip_bytes) {
		ssize_t skipped;

		if (zip->uncompressed_buffer_bytes_remaining == 0) {
			if (zip->pack_stream_inbytes_remaining > 0) {
				r = extract_pack_stream(a, 0);
				if (r < 0)
					return (r);
			} else if (zip->folder_outbytes_remaining > 0) {
				/* Extract a remaining pack stream. */
				r = extract_pack_stream(a, 0);
				if (r < 0)
					return (r);
			} else {
				archive_set_error(&a->archive,
				    ARCHIVE_ERRNO_FILE_FORMAT,
				    "Truncated 7-Zip file body");
				return (ARCHIVE_FATAL);
			}
		}
		skipped = get_uncompressed_data(
			a, buff, (size_t)skip_bytes, 0);
		if (skipped < 0)
			return (skipped);
		skip_bytes -= skipped;
		if (zip->pack_stream_bytes_unconsumed)
			read_consume(a);
	}

	return (get_uncompressed_data(a, buff, size, minimum));
}


u32 parse_sdp_ext(char *arg_val, u32 param)
{
	char *id;
	sdp_lines = gf_realloc(sdp_lines, sizeof(SDPLine) * (nb_sdp_ex + 1));
	if (!sdp_lines) return 2;
	id = strchr(arg_val, ':');
	if (id) {
		id[0] = 0;
		if (parse_track_id(&sdp_lines[nb_sdp_ex].track_id, arg_val, GF_FALSE)) {
			id[0] = ':';
			sdp_lines[nb_sdp_ex].line = id + 1;
		}
		else {
			id[0] = ':';
			sdp_lines[nb_sdp_ex].line = arg_val;
		}
	}
	else {
		sdp_lines[nb_sdp_ex].line = arg_val;
		sdp_lines[nb_sdp_ex].track_id.type = 0;
		sdp_lines[nb_sdp_ex].track_id.ID_or_num = 0;
	}
	open_edit = GF_TRUE;
	nb_sdp_ex++;
	return GF_FALSE;
}


void PrintImportUsage()
{
	u32 i;

	gf_sys_format_help(helpout, help_flags, "# Importing Options\n"
		"# File importing\n"
		"Syntax is [-add]() / [-cat]() `URL[#FRAGMENT][:opt1...:optN=val]`\n"
		"This process will create the destination file if not existing, and add the track(s) to it. If you wish to always create a new destination file, add [-new](MP4B_GEN).\n"
		"The supported input media types depend on your installation, check [filters documentation](Filters) for more info.\n"
		"  \n"
		"To select a desired media track from a source, a fragment identifier '#' can be specified, before any other options. The following syntax is used:\n"
		"- `#video`: adds the first video track found in source\n"
		"- `#audio`: adds the first audio track found in source\n"
		"- `#auxv`: adds the first auxiliary video track found in source\n"
		"- `#pict`: adds the first picture track found in source\n"
		"- `#trackID=ID` or `#ID`: adds the specified track. For IsoMedia files, ID is the track ID. For other media files, ID is the value indicated by `MP4Box -info inputFile`\n"
		"- `#pid=ID`: number of desired PID for MPEG-2 TS sources\n"
		"- `#prog_id=ID`: number of desired program for MPEG-2 TS sources\n"
		"- `#program=NAME`: name of desired program for MPEG-2 TS sources\n"
		"  \n"
		"By default all imports are performed sequentially, and final interleaving is done at the end; this however requires a temporary file holding original ISOBMF file (if any) and added files before creating the final output. Since this can become quite large, it is possible to add media to a new file without temporary storage, using [-flat](MP4B_GEN) option, but this disables media interleaving.\n"
		"  \n"
		"If you wish to create an interleaved new file with no temporary storage, use the [-newfs](MP4B_GEN) option. The interleaving might not be as precise as when using [-new]() since it is dependent on multiplexer input scheduling (each execution might lead to a slightly different result). Additionally in this mode: \n"
		" - Some multiplexing options (marked with `X` below) will be activated for all inputs (e.g. it is not possible to import one AVC track with `xps_inband` and another without).\n"
		" - Some multiplexing options (marked as `D` below) cannot be used as they require temporary storage for file edition.\n"
		" - Usage of [-cat]() is possible, but concatenated sources will not be interleaved in the output. If you wish to perform more complex cat/add operations without temp file, use a [playlist](flist).\n"
		"  \n"
		"Source URL can be any URL supported by GPAC, not limited to local files.\n"
		"  \n"
		"Note: When importing SRT or SUB files, MP4Box will choose default layout options to make the subtitle appear at the bottom of the video. You SHOULD NOT import such files before any video track is added to the destination file, otherwise the results will likely not be useful (default SRT/SUB importing uses default serif font, fontSize 18 and display size 400x60). For more details, check [TTXT doc](Subtitling-with-GPAC).\n"
		"  \n"
		"When importing several tracks/sources in one pass, all options will be applied if relevant to each source. These options are set for all imported streams. If you need to specify these options per stream, set per-file options using the syntax `-add stream[:opt1:...:optN]`.\n"
		"  \n"
		"The import file name may be set to empty or `self`, indicating that the import options should be applied to the destination file track(s).\n"
		"EX -add self:moovts=-1:noedit src.mp4\n"
		"This will apply `moovts` and `noedit` option to all tracks in src.mp4\n"
		"EX -add self#2:moovts=-1:noedit src.mp4\n"
		"This will apply `moovts` and `noedit` option to track with `ID=2` in src.mp4\n"
		"Only per-file options marked with a `S` are possible in this mode.\n"
		"  \n"
		"When importing an ISOBMFF/QT file, only options marked as `C` or `S` can be used.\n"
		"  \n"
		"Allowed per-file options:\n\n"
	);

	i=0;
	while (m4b_imp_fileopt_args[i].name) {
		GF_GPACArg *arg = (GF_GPACArg *) &m4b_imp_fileopt_args[i];
		i++;
		gf_sys_print_arg(helpout, help_flags | GF_PRINTARG_NO_DASH, arg, "mp4box-import");
	}

	gf_sys_format_help(helpout, help_flags, "\n"
		"Note: `sopt`, `dopt` and `@f` must be placed after all other options.\n"
		"# Global import options\n"
	);

	i=0;
	while (m4b_imp_args[i].name) {
		GF_GPACArg *arg = (GF_GPACArg *) &m4b_imp_args[i];
		i++;
		gf_sys_print_arg(helpout, help_flags, arg, "mp4box-import");
	}
}


u32 parse_dump_ts(char *arg_val, u32 opt)
{
	dump_timestamps = 1;
	if (arg_val) {
		if (isdigit(arg_val[0])) {
			program_number = parse_u32(arg_val, "Program");
		} else {
			return 3;
		}
	}
	return 0;


static void PrintHelp(char *arg_name, Bool search_desc, Bool no_match)
{
	GF_FilterSession *fs;
	Bool res;

	fs = gf_fs_new_defaults(0);

	if (arg_name[0]=='-')
		arg_name++;

	if (search_desc) {
		char *_arg_name = gf_strdup(arg_name);
		strlwr(_arg_name);
		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Possible options mentioning `%s`:\n", arg_name));
		PrintHelpArg(_arg_name, SEARCH_DESC, fs);
		gf_free(_arg_name);
	} else {
		res = no_match ? GF_FALSE : PrintHelpArg(arg_name, SEARCH_ARG_EXACT, fs);
		if (!res) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Option -%s unknown, please check usage.\n", arg_name));
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Possible options are:\n"));

			PrintHelpArg(arg_name, SEARCH_ARG_CLOSE, fs);
		}
	}
	if (fs)
		gf_fs_del(fs);
}


u32 parse_comp_box(char *arg_val, u32 opt)
{
	compress_top_boxes = arg_val;
	size_top_box = opt;
	return 0;


}
u32 parse_dsap(char *arg_val, u32 opt)
{
	parse_track_id(&dump_saps_track, arg_val, GF_FALSE);
	dump_saps_mode = opt;
	return 0;


static GF_Err do_meta_act()
{
	u32 i;
	for (i=0; i<nb_meta_act; i++) {
		GF_Err e = GF_OK;
		u32 tk = 0;
#ifndef GPAC_DISABLE_ISOM_WRITE
		Bool self_ref;
#endif
		MetaAction *meta = &metas[i];
		u32 tk_id = get_track_id(file, &meta->track_id);

		if (tk_id) tk = gf_isom_get_track_by_id(file, tk_id);

		switch (meta->act_type) {
#ifndef GPAC_DISABLE_ISOM_WRITE
		case META_ACTION_SET_TYPE:
			/*note: we don't handle file brand modification, this is an author stuff and cannot be guessed from meta type*/
			e = gf_isom_set_meta_type(file, meta->root_meta, tk, meta->meta_4cc);
			gf_isom_modify_alternate_brand(file, GF_ISOM_BRAND_ISO2, GF_TRUE);
			do_save = GF_TRUE;
			break;
		case META_ACTION_ADD_ITEM:

			if (meta->replace) {
				if (!gf_isom_get_meta_item_by_id(file, meta->root_meta, tk, meta->item_id)) {
					M4_LOG(GF_LOG_WARNING, ("No item with ID %d, ignoring replace\n", meta->item_id));
					meta->item_id = 0;
				} else {
					e = gf_isom_remove_meta_item(file, meta->root_meta, tk, meta->item_id, GF_TRUE, meta->keep_props);
					if (e) break;
				}
			}

			self_ref = !stricmp(meta->szPath, "NULL") || !stricmp(meta->szPath, "this") || !stricmp(meta->szPath, "self");
			e = gf_isom_add_meta_item(file, meta->root_meta, tk, self_ref, self_ref ? NULL : meta->szPath,
			                          meta->szName,
			                          meta->item_id,
									  meta->item_type,
			                          meta->mime_type,
			                          meta->enc_type,
			                          meta->use_dref ? meta->szPath : NULL,  NULL,
			                          meta->image_props);
			if (meta->item_refs && gf_list_count(meta->item_refs)) {
				u32 ref_i;
				for (ref_i = 0; ref_i < gf_list_count(meta->item_refs); ref_i++) {
					MetaRef	*ref_entry = gf_list_get(meta->item_refs, ref_i);
					e = gf_isom_meta_add_item_ref(file, meta->root_meta, tk, meta->item_id, ref_entry->ref_item_id, ref_entry->ref_type, NULL);
				}
			}
			do_save = GF_TRUE;
			break;
		case META_ACTION_ADD_IMAGE_ITEM:
		{
			u32 old_tk_count = gf_isom_get_track_count(file);
			u32 src_tk_id = 1;
			GF_Fraction _frac = {0,0};
			GF_ISOFile *fsrc = file;
			self_ref = GF_FALSE;

			tk = 0;
			if (meta->image_props && meta->image_props->auto_grid) {
				e = GF_OK;
				self_ref = GF_TRUE;
			} else if (!meta->szPath || (meta->image_props && meta->image_props->sample_num && meta->image_props->use_reference)) {
				e = GF_OK;
				self_ref = GF_TRUE;
				src_tk_id = tk_id;
			} else if (meta->szPath) {
				if (meta->image_props && gf_isom_probe_file(meta->szPath) && !meta->image_props->tile_mode) {
					meta->image_props->src_file = gf_isom_open(meta->szPath, GF_ISOM_OPEN_READ, NULL);
					e = gf_isom_last_error(meta->image_props->src_file);
					fsrc = meta->image_props->src_file;
					if (meta->image_props->item_ref_id)
						src_tk_id = 0;
				} else {
					gf_isom_disable_brand_rewrite(file, GF_TRUE);
					e = import_file(file, meta->szPath, 0, _frac, 0, NULL, NULL, NULL, 0);
					gf_isom_disable_brand_rewrite(file, GF_FALSE);
				}
			} else {
				M4_LOG(GF_LOG_ERROR, ("Missing file name to import\n"));
				e = GF_BAD_PARAM;
			}
			if (e == GF_OK) {
				u32 meta_type = gf_isom_get_meta_type(file, meta->root_meta, tk);
				if (!meta_type) {
					e = gf_isom_set_meta_type(file, meta->root_meta, tk, GF_META_ITEM_TYPE_PICT);
				} else {
					if (meta_type != GF_META_ITEM_TYPE_PICT) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Warning: file already has a root 'meta' box of type %s\n", gf_4cc_to_str(meta_type)));
						e = GF_BAD_PARAM;
					}
				}
				if (e == GF_OK) {
					if (!meta->item_id) {
						e = gf_isom_meta_get_next_item_id(file, meta->root_meta, tk, &meta->item_id);
					}
					if (e == GF_OK) {
						if (!src_tk_id && (!meta->image_props || !meta->image_props->item_ref_id) ) {
							u32 j;
							for (j=0; j<gf_isom_get_track_count(fsrc); j++) {
								if (gf_isom_is_video_handler_type (gf_isom_get_media_type(fsrc, j+1))) {
									src_tk_id = gf_isom_get_track_id(fsrc, j+1);
									break;
								}
							}

							if (!src_tk_id) {
								M4_LOG(GF_LOG_ERROR, ("No video track in file, cannot add image from track\n"));
								e = GF_BAD_PARAM;
							}
						}

						if (e == GF_OK && meta->replace) {
							if (!gf_isom_get_meta_item_by_id(file, meta->root_meta, tk, meta->item_id)) {
								M4_LOG(GF_LOG_WARNING, ("No item with ID %d, ignoring replace\n", meta->item_id));
								meta->item_id = 0;
							} else {
								e = gf_isom_remove_meta_item(file, meta->root_meta, tk, meta->item_id, GF_TRUE, meta->keep_props);
							}
						}
						if (e==GF_OK)
							e = gf_isom_iff_create_image_item_from_track(file, meta->root_meta, tk, src_tk_id, meta->szName, meta->item_id, meta->image_props, NULL);

						if (e == GF_OK && meta->primary) {
							e = gf_isom_set_meta_primary_item(file, meta->root_meta, tk, meta->item_id);
						}
						if (e == GF_OK && meta->item_refs && gf_list_count(meta->item_refs)) {
							u32 ref_i;
							for (ref_i = 0; ref_i < gf_list_count(meta->item_refs); ref_i++) {
								MetaRef	*ref_entry = gf_list_get(meta->item_refs, ref_i);
								e = gf_isom_meta_add_item_ref(file, meta->root_meta, tk, meta->item_id, ref_entry->ref_item_id, ref_entry->ref_type, NULL);
							}
						}
						if (e == GF_OK && meta->group_type) {
							e = gf_isom_meta_add_item_group(file, meta->root_meta, tk, meta->item_id, meta->group_id, meta->group_type);
						}
					}
				}
			}
			if (meta->image_props && meta->image_props->src_file) {
				gf_isom_delete(meta->image_props->src_file);
				meta->image_props->src_file = NULL;
			} else if (!self_ref) {
				gf_isom_remove_track(file, old_tk_count+1);
				if (do_flat) {
					M4_LOG(GF_LOG_ERROR, ("Warning: -flat storage cannot be used when using -add-image on external file\n"));
					e = GF_NOT_SUPPORTED;
				}
			}
			do_save = GF_TRUE;
		}
			break;
		case META_ACTION_ADD_IMAGE_DERIVED:
		{
			u32 meta_type = gf_isom_get_meta_type(file, meta->root_meta, tk);
			e = GF_OK;
			if (!meta_type) {
				e = gf_isom_set_meta_type(file, meta->root_meta, tk, GF_META_ITEM_TYPE_PICT);
			} else {
				if (meta_type != GF_META_ITEM_TYPE_PICT) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Warning: file already has a root 'meta' box of type %s\n", gf_4cc_to_str(meta_type)));
					e = GF_BAD_PARAM;
				}
			}
			if (e) break;
			if (!meta->item_id) {
				e = gf_isom_meta_get_next_item_id(file, meta->root_meta, tk, &meta->item_id);
			}
			if (e == GF_OK && meta->item_type) {
				if (meta->item_type == GF_4CC('g','r','i','d')) {
					e = gf_isom_iff_create_image_grid_item(file, meta->root_meta, tk,
							meta->szName && strlen(meta->szName) ? meta->szName : NULL,
							meta->item_id,
							meta->image_props);
				} else if (meta->item_type == GF_4CC('i','o','v','l')) {
					e = gf_isom_iff_create_image_overlay_item(file, meta->root_meta, tk,
							meta->szName && strlen(meta->szName) ? meta->szName : NULL,
							meta->item_id,
							meta->image_props);
				} else if (meta->item_type == GF_4CC('i','d','e','n')) {
					e = gf_isom_iff_create_image_identity_item(file, meta->root_meta, tk,
							meta->szName && strlen(meta->szName) ? meta->szName : NULL,
							meta->item_id,
							meta->image_props);
				} else {
					e = GF_NOT_SUPPORTED;
				}
				if (e) break;
				if (meta->primary) {
					e = gf_isom_set_meta_primary_item(file, meta->root_meta, tk, meta->item_id);
					if (e) break;
				}
				if (meta->item_refs && gf_list_count(meta->item_refs)) {
					u32 ref_i;
					for (ref_i = 0; ref_i < gf_list_count(meta->item_refs); ref_i++) {
						MetaRef	*ref_entry = gf_list_get(meta->item_refs, ref_i);
						e = gf_isom_meta_add_item_ref(file, meta->root_meta, tk, meta->item_id, ref_entry->ref_item_id, ref_entry->ref_type, NULL);
						if (e) break;
					}
				}
			}
			do_save = GF_TRUE;
		}
			break;
		case META_ACTION_REM_ITEM:
			e = gf_isom_remove_meta_item(file, meta->root_meta, tk, meta->item_id, GF_FALSE, NULL);
			do_save = GF_TRUE;
			break;
		case META_ACTION_SET_PRIMARY_ITEM:
			e = gf_isom_set_meta_primary_item(file, meta->root_meta, tk, meta->item_id);
			do_save = GF_TRUE;
			break;
		case META_ACTION_SET_XML:
		case META_ACTION_SET_BINARY_XML:
			e = gf_isom_set_meta_xml(file, meta->root_meta, tk, meta->szPath, NULL, 0, (meta->act_type==META_ACTION_SET_BINARY_XML) ? 1 : 0);
			do_save = GF_TRUE;
			break;
		case META_ACTION_REM_XML:
			if (gf_isom_get_meta_item_count(file, meta->root_meta, tk)) {
				e = gf_isom_remove_meta_xml(file, meta->root_meta, tk);
				do_save = GF_TRUE;
			} else {
				M4_LOG(GF_LOG_WARNING, ("No meta box in input file\n"));
				e = GF_OK;
			}
			break;
		case META_ACTION_DUMP_ITEM:
			if (gf_isom_get_meta_item_count(file, meta->root_meta, tk)) {
				e = gf_isom_extract_meta_item(file, meta->root_meta, tk, meta->item_id, meta->szPath && strlen(meta->szPath) ? meta->szPath : NULL);
			} else {
				M4_LOG(GF_LOG_WARNING, ("No meta box in input file\n"));
				e = GF_OK;
			}
			break;
#endif // GPAC_DISABLE_ISOM_WRITE

		case META_ACTION_DUMP_XML:
			if (gf_isom_has_meta_xml(file, meta->root_meta, tk)) {
				e = gf_isom_extract_meta_xml(file, meta->root_meta, tk, meta->szPath, NULL);
			} else {
				M4_LOG(GF_LOG_WARNING, ("No meta box in input file\n"));
				e = GF_OK;
			}
			break;
		default:
			break;
		}
		if (meta->item_refs) {
			while (gf_list_count(meta->item_refs)) {
				gf_free(gf_list_pop_back(meta->item_refs));
			}
			gf_list_del(meta->item_refs);
			meta->item_refs = NULL;
		}
		// meta->image_props is freed in mp4box_cleanup
		if (e) return e;
	}
	return GF_OK;


static GF_Err do_tsel_act()
{
	u32 i;
	GF_Err e;
	for (i=0; i<nb_tsel_acts; i++) {
		u32 refID, trackID = get_track_id(file, &tsel_acts[i].target_track);
		if (!trackID) {
			M4_LOG(GF_LOG_ERROR, ("Track not found\n"));
			e = GF_BAD_PARAM;
			return e;
		}

		switch (tsel_acts[i].act_type) {
		case TSEL_ACTION_SET_PARAM:
			refID = get_track_id(file, &tsel_acts[i].reference_track);
			e = gf_isom_set_track_switch_parameter(file,
			                                       gf_isom_get_track_by_id(file, trackID),
			                                       refID,
			                                       tsel_acts[i].is_switchGroup ? 1 : 0,
			                                       &tsel_acts[i].switchGroupID,
			                                       tsel_acts[i].criteria, tsel_acts[i].nb_criteria);
			if (e == GF_BAD_PARAM) {
				u32 alternateGroupID, nb_groups;
				gf_isom_get_track_switch_group_count(file, gf_isom_get_track_by_id(file, trackID), &alternateGroupID, &nb_groups);
				if (alternateGroupID) {
					M4_LOG(GF_LOG_ERROR, ("Error - for adding more tracks to group, using: -group-add -refTrack=ID1:[criteria:]trackID=ID2\n"));
				} else {
					M4_LOG(GF_LOG_ERROR, ("Error - for creating a new grouping information, using -group-add -trackID=ID1:[criteria:]trackID=ID2\n"));
				}
			}
			if (e) return e;
			do_save = GF_TRUE;
			break;
		case TSEL_ACTION_REMOVE_TSEL:
			e = gf_isom_reset_track_switch_parameter(file, gf_isom_get_track_by_id(file, trackID), 0);
			if (e) return e;
			do_save = GF_TRUE;
			break;
		case TSEL_ACTION_REMOVE_ALL_TSEL_IN_GROUP:
			e = gf_isom_reset_track_switch_parameter(file, gf_isom_get_track_by_id(file, trackID), 1);
			if (e) return e;
			do_save = GF_TRUE;
			break;
		default:
			break;
		}
	}
	return GF_OK;


}
u32 parse_cryp(char *arg_val, u32 opt)
{
	open_edit = GF_TRUE;
	if (!opt) {
		crypt = 1;
		drm_file = arg_val;
		open_edit = GF_TRUE;
		return 0;
	}
	crypt = 2;
	if (arg_val && get_file_type_by_ext(arg_val) != 1) {
		drm_file = arg_val;
		return 0;
	}
	return 3;


}
u32 parse_base_url(char *arg_val, u32 opt)
{
	mpd_base_urls = gf_realloc(mpd_base_urls, (nb_mpd_base_urls + 1)*sizeof(char**));
	if (!mpd_base_urls) return 2;
	mpd_base_urls[nb_mpd_base_urls] = arg_val;
	nb_mpd_base_urls++;
	return 0;


static GF_Err do_track_act()
{
	u32 j;
	for (j=0; j<nb_track_act; j++) {
		u32 i;
		GF_Err e = GF_OK;
		TrackAction *tka = &tracks[j];
		u32 trackID = get_track_id(file, &tka->target_track);
		u32 track = trackID ? gf_isom_get_track_by_id(file, trackID) : 0;

		u32 newTrackID = get_track_id(file, &tka->newTrackID);

		timescale = gf_isom_get_timescale(file);
		switch (tka->act_type) {
		case TRACK_ACTION_REM_TRACK:
			e = gf_isom_remove_track(file, track);
			if (e) {
				M4_LOG(GF_LOG_ERROR, ("Error Removing track ID %d: %s\n", trackID, gf_error_to_string(e)));
			} else {
				M4_LOG(GF_LOG_INFO, ("Removing track ID %d\n", trackID));
			}
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_LANGUAGE:
			for (i=0; i<gf_isom_get_track_count(file); i++) {
				if (track && (track != i+1)) continue;
				e = gf_isom_set_media_language(file, i+1, tka->lang);
				if (e) return e;
				do_save = GF_TRUE;
			}
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_KIND:
			for (i=0; i<gf_isom_get_track_count(file); i++) {
				if (track && (track != i+1)) continue;
				e = gf_isom_add_track_kind(file, i+1, tka->kind_scheme, tka->kind_value);
				if (e) return e;
				do_save = GF_TRUE;
			}
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_REM_KIND:
			for (i=0; i<gf_isom_get_track_count(file); i++) {
				if (track && (track != i+1)) continue;
				e = gf_isom_remove_track_kind(file, i+1, tka->kind_scheme, tka->kind_value);
				if (e) return e;
				do_save = GF_TRUE;
			}
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_DELAY:
			if (tka->delay.num && tka->delay.den) {
				u64 tk_dur;

				e = gf_isom_remove_edits(file, track);
				if (e) return e;
				tk_dur = gf_isom_get_track_duration(file, track);
				if (gf_isom_get_edits_count(file, track))
					do_save = GF_TRUE;
				if (tka->delay.num>0) {
					//cast to u64, delay_ms * timescale can be quite big before / 1000
					e = gf_isom_append_edit(file, track, ((u64) tka->delay.num) * timescale / tka->delay.den, 0, GF_ISOM_EDIT_EMPTY);
					if (e) return e;
					e = gf_isom_append_edit(file, track, tk_dur, 0, GF_ISOM_EDIT_NORMAL);
					if (e) return e;
					do_save = GF_TRUE;
				} else {
					//cast to u64, delay_ms * timescale can be quite big before / 1000
					u64 to_skip = ((u64) -tka->delay.num) * timescale / tka->delay.den;
					if (to_skip<tk_dur) {
						//cast to u64, delay_ms * timescale can be quite big before / 1000
						u64 media_time = ((u64) -tka->delay.num) * gf_isom_get_media_timescale(file, track) / tka->delay.den;
						e = gf_isom_append_edit(file, track, tk_dur-to_skip, media_time, GF_ISOM_EDIT_NORMAL);
						if (e) return e;
						do_save = GF_TRUE;
					} else {
						M4_LOG(GF_LOG_WARNING, ("Warning: request negative delay longer than track duration - ignoring\n"));
					}
				}
			} else if (gf_isom_get_edits_count(file, track)) {
				e = gf_isom_remove_edits(file, track);
				if (e) return e;
				do_save = GF_TRUE;
			}
			break;
		case TRACK_ACTION_SET_KMS_URI:
			for (i=0; i<gf_isom_get_track_count(file); i++) {
				if (track && (track != i+1)) continue;
				if (!gf_isom_is_media_encrypted(file, i+1, 1)) continue;
				if (!gf_isom_is_ismacryp_media(file, i+1, 1)) continue;
				e = gf_isom_change_ismacryp_protection(file, i+1, 1, NULL, (char *) tka->kms);
				if (e) return e;
				do_save = GF_TRUE;
			}
			break;
		case TRACK_ACTION_SET_ID:
			if (!trackID && (gf_isom_get_track_count(file) == 1)) {
				M4_LOG(GF_LOG_WARNING, ("Warning: track id is not specified, but file has only one track - assume that you want to change id for this track\n"));
				track = 1;
			}
			if (track) {
				u32 newTrack;
				newTrack = gf_isom_get_track_by_id(file, newTrackID);
				if (newTrack != 0) {
					M4_LOG(GF_LOG_WARNING, ("Cannot set track id with value %d because a track already exists - ignoring", tka->newTrackID));
				} else {
					e = gf_isom_set_track_id(file, track, newTrackID);
					if (e) return e;
					do_save = GF_TRUE;
				}
			} else {
				M4_LOG(GF_LOG_WARNING, ("Error: Cannot change id for track %d because it does not exist - ignoring", trackID));
			}
			break;
		case TRACK_ACTION_SWAP_ID:
			if (track) {
				u32 tk1, tk2;
				tk1 = gf_isom_get_track_by_id(file, trackID);
				tk2 = gf_isom_get_track_by_id(file, newTrackID);
				if (!tk1 || !tk2) {
					M4_LOG(GF_LOG_WARNING, ("Error: Cannot swap track IDs because not existing - ignoring"));
				} else {
					e = gf_isom_set_track_id(file, tk2, 0);
					if (e) return e;
					e = gf_isom_set_track_id(file, tk1, newTrackID);
					if (e) return e;
					e = gf_isom_set_track_id(file, tk2, trackID);
					if (e) return e;
					do_save = GF_TRUE;
				}
			} else {
				M4_LOG(GF_LOG_WARNING, ("Error: Cannot change id for track %d because it does not exist - ignoring", trackID));
			}
			break;
		case TRACK_ACTION_SET_PAR:
			e = gf_media_change_par(file, track, tka->par_num, tka->par_den, tka->force_par, tka->rewrite_bs);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_CLAP:
			e = gf_isom_set_clean_aperture(file, track, 1, tka->clap_wnum, tka->clap_wden, tka->clap_hnum, tka->clap_hden, tka->clap_honum, tka->clap_hoden, tka->clap_vonum, tka->clap_voden);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_MX:
			e = gf_isom_set_track_matrix(file, track, tka->mx);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_HANDLER_NAME:
			e = gf_isom_set_handler_name(file, track, tka->hdl_name);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_ENABLE:
			if (!gf_isom_is_track_enabled(file, track)) {
				e = gf_isom_set_track_enabled(file, track, GF_TRUE);
				do_save = GF_TRUE;
			}
			break;
		case TRACK_ACTION_DISABLE:
			if (gf_isom_is_track_enabled(file, track)) {
				e = gf_isom_set_track_enabled(file, track, GF_FALSE);
				do_save = GF_TRUE;
			}
			break;
		case TRACK_ACTION_REFERENCE:
			e = gf_isom_set_track_reference(file, track, GF_4CC(tka->lang[0], tka->lang[1], tka->lang[2], tka->lang[3]), newTrackID);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_REM_NON_RAP:
			e = gf_media_remove_non_rap(file, track, GF_FALSE);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_REM_NON_REFS:
			e = gf_media_remove_non_rap(file, track, GF_TRUE);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_UDTA:
			e = set_file_udta(file, track, tka->udta_type, tka->string ? tka->string : tka->src_name , tka->sample_num ? GF_TRUE : GF_FALSE, tka->string ? GF_TRUE : GF_FALSE);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_EDITS:
			e = apply_edits(file, track, tka->string);
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_TIME:
			if (!trackID) {
				e = gf_isom_set_creation_time(file, tka->time, tka->time);
				if (e) return e;
				for (i=0; i<gf_isom_get_track_count(file); i++) {
					e = gf_isom_set_track_creation_time(file, i+1, tka->time, tka->time);
					if (e) return e;
				}
			} else {
				e = gf_isom_set_track_creation_time(file, track, tka->time, tka->time);
			}
			do_save = GF_TRUE;
			break;
		case TRACK_ACTION_SET_MEDIA_TIME:
			for (i=0; i<gf_isom_get_track_count(file); i++) {
				if (track && (track != i+1)) continue;
				e = gf_isom_set_media_creation_time(file, i+1, tka->time, tka->time);
				if (e) return e;
			}
			do_save = GF_TRUE;
			break;
		default:
			break;
		}
		if (e) return e;
	}
	return GF_OK;


u32 parse_senc_param(char *arg_val, u32 opt)
{
#ifndef GPAC_DISABLE_SCENE_ENCODER
	switch (opt) {
	case 0: //-sync
		smenc_opts.flags |= GF_SM_ENCODE_RAP_INBAND;
		smenc_opts.rap_freq = parse_u32(arg_val, "sync");
		break;
	case 1: //-shadow
		smenc_opts.flags &= ~GF_SM_ENCODE_RAP_INBAND;
		smenc_opts.flags |= GF_SM_ENCODE_RAP_SHADOW;
		smenc_opts.rap_freq = parse_u32(arg_val, "shadow");
		break;
	case 2: //-carousel
		smenc_opts.flags &= ~(GF_SM_ENCODE_RAP_INBAND | GF_SM_ENCODE_RAP_SHADOW);
		smenc_opts.rap_freq = parse_u32(arg_val, "carousel");
		break;
	case 3: //-auto-quant
		smenc_opts.resolution = parse_s32(arg_val, "auto-quant");
		smenc_opts.auto_quant = 1;
		break;
	case 4: //-global-quant
		smenc_opts.resolution = parse_s32(arg_val, "global-quant");
		smenc_opts.auto_quant = 2;
		break;
	case 5: //-ctx-in or -inctx
		chunk_mode = GF_TRUE;
		input_ctx = arg_val;
	}
#endif
	return 0;


static GF_Err do_remux_file()
{
	GF_MediaExporter mdump;
	memset(&mdump, 0, sizeof(GF_MediaExporter));
	mdump.in_name = inName;
	mdump.out_name = mux_name;
	mdump.flags = GF_EXPORT_REMUX;
	mdump.print_stats_graph = fs_dump_flags;
	return gf_media_export(&mdump);


static Bool create_new_track_action(char *arg_val, u32 act_type, u32 dump_type)
{
	TrackAction *tka;
	char *param = arg_val;
	tracks = (TrackAction *)gf_realloc(tracks, sizeof(TrackAction) * (nb_track_act+1));
	if (!tracks) return GF_FALSE;

	tka = & tracks[nb_track_act];
	nb_track_act++;

	memset(tka, 0, sizeof(TrackAction) );
	tka->act_type = act_type;
	tka->dump_type = dump_type;
	if (act_type != TRACK_ACTION_RAW_EXTRACT) {
		open_edit = GF_TRUE;
		do_save = GF_TRUE;
	}

	if ((act_type==TRACK_ACTION_SET_ID) || (act_type==TRACK_ACTION_SWAP_ID)) {
		char *sep = strchr(param, ':');
		if (!sep) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for -set-track-id - expecting \"id1:id2\" got \"%s\"\n", param));
			return GF_FALSE;
		}
		sep[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		sep[0] = ':';
		if (act_type==TRACK_ACTION_SWAP_ID)
			parse_track_id(&tka->newTrackID, sep+1, GF_FALSE);
		else
			tka->newTrackID.ID_or_num = parse_u32(sep+1, "ID");
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_PAR) {
		char *ext;
		ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track par - expecting tkID=none or tkID=PAR_NUM:PAR_DEN got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';

		if (!stricmp(ext+1, "none"))
			tka->par_num = tka->par_den = 0;
		else if (!stricmp(ext+1, "auto")) {
			tka->par_num = tka->par_den = -1;
			tka->force_par = 1;
		}
		else if (!stricmp(ext+1, "force")) {
			tka->par_num = tka->par_den = 1;
			tka->force_par = 1;
		}
		else {
			if (ext[1]=='w') {
				tka->rewrite_bs = 1;
				ext++;
			}
			if (sscanf(ext+1, "%d:%d", &tka->par_num, &tka->par_den) != 2) {
				M4_LOG(GF_LOG_ERROR, ("Bad format for track par - expecting tkID=PAR_NUM:PAR_DEN got %s\n", param));
				return GF_FALSE;
			}
		}
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_CLAP) {
		char *ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track clap - expecting tkID=none or tkID=Wn,Wd,Hn,Hd,HOn,HOd,VOn,VOd got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';
		if (stricmp(ext + 1, "none")) {
			if (sscanf(ext + 1, "%d,%d,%d,%d,%d,%d,%d,%d", &tka->clap_wnum, &tka->clap_wden, &tka->clap_hnum, &tka->clap_hden, &tka->clap_honum, &tka->clap_hoden, &tka->clap_vonum, &tka->clap_voden) != 8) {

				M4_LOG(GF_LOG_ERROR, ("Bad format for track clap - expecting tkID=none or tkID=Wn,Wd,Hn,Hd,HOn,HOd,VOn,VOd got %s\n", param));
				return GF_FALSE;
			}
		}
		return GF_TRUE;
	}

	if (act_type==TRACK_ACTION_SET_MX) {
		char *ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track matrix - expecting ID=none or ID=M1:M2:M3:M4:M5:M6:M7:M8:M9 got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';
		if (!stricmp(ext + 1, "none")) {
			memset(tka->mx, 0, sizeof(s32)*9);
			tka->mx[0] = tka->mx[4] = tka->mx[8] = 1;
		} else {
			s32 res;
			if (strstr(ext+1, "0x")) {
				res = sscanf(ext + 1, "0x%d:0x%d:0x%d:0x%d:0x%d:0x%d:0x%d:0x%d:0x%d", &tka->mx[0], &tka->mx[1], &tka->mx[2], &tka->mx[3], &tka->mx[4], &tka->mx[5], &tka->mx[6], &tka->mx[7], &tka->mx[8]);
			} else {
				res = sscanf(ext + 1, "%d:%d:%d:%d:%d:%d:%d:%d:%d", &tka->mx[0], &tka->mx[1], &tka->mx[2], &tka->mx[3], &tka->mx[4], &tka->mx[5], &tka->mx[6], &tka->mx[7], &tka->mx[8]);
			}
			if (res != 9) {
				M4_LOG(GF_LOG_ERROR, ("Bad format for track matrix - expecting ID=none or ID=M1:M2:M3:M4:M5:M6:M7:M8:M9 got %s\n", param));
				return GF_FALSE;
			}
		}
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_EDITS) {
		char *ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track edits - expecting ID=EDITS got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';
		tka->string = gf_strdup(ext+1);
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_LANGUAGE) {
		char *ext = strchr(param, '=');
		if (!strnicmp(param, "all=", 4)) {
			strncpy(tka->lang, param + 4, LANG_SIZE-1);
		}
		else if (!ext) {
			strncpy(tka->lang, param, LANG_SIZE-1);
		} else {
			strncpy(tka->lang, ext + 1, LANG_SIZE-1);
			ext[0] = 0;
			parse_track_id(&tka->target_track, param, GF_FALSE);
			ext[0] = '=';
		}
		return GF_TRUE;
	}
	if ((act_type==TRACK_ACTION_SET_KIND) || (act_type==TRACK_ACTION_REM_KIND)) {
		char *ext;
		char *scheme_start = NULL;

		//extract trackID
		if (!strnicmp(param, "all=", 4)) {
			scheme_start = param + 4;
		} else {
			ext = strchr(param, '=');
			if (ext) {
				ext[0] = 0;
				parse_track_id(&tka->target_track, param, GF_FALSE);
				if (tka->target_track.ID_or_num) {
					scheme_start = ext + 1;
				} else {
					scheme_start = param;
				}
				ext[0] = '=';
			} else {
				scheme_start = param;
			}
		}

		//extract scheme and value - if not, remove kind
		if (!scheme_start || !scheme_start[0]) {
			M4_LOG(GF_LOG_ERROR, ("Missing kind scheme - expecting ID=schemeURI=value got %s\n", param));
			return GF_FALSE;
		} else {
			ext = strchr(scheme_start, '=');
			if (!ext) {
				tka->kind_scheme = gf_strdup(scheme_start);
			} else {
				ext[0] = 0;
				tka->kind_scheme = gf_strdup(scheme_start);
				ext[0] = '=';
				tka->kind_value = gf_strdup(ext + 1);
			}
		}
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_DELAY) {
		char *ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track delay - expecting tkID=DLAY got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';
		if (sscanf(ext+1, "%d/%u", &tka->delay.num, &tka->delay.den) != 2) {
			tka->delay.num = parse_s32(ext + 1, "delay");
			tka->delay.den = 1000;
		}
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_REFERENCE) {
		char *ext = strchr(param, '=');
		if (!ext) ext = strchr(param, ':');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track reference - expecting tkID:XXXX:refID got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';

		char *ext2 = strchr(ext, ':');
		if (!ext2) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track reference - expecting tkID:XXXX:refID got %s\n", param));
			return GF_FALSE;
		}
		ext2[0] = 0;
		strncpy(tka->lang, ext+1, LANG_SIZE-1);
		ext2[0] = ':';
		parse_track_id(&tka->newTrackID, ext2 + 1, GF_FALSE);
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_HANDLER_NAME) {
		char *ext = strchr(param, '=');
		if (!ext) {
			M4_LOG(GF_LOG_ERROR, ("Bad format for track name - expecting tkID=name got %s\n", param));
			return GF_FALSE;
		}
		ext[0] = 0;
		parse_track_id(&tka->target_track, param, GF_FALSE);
		ext[0] = '=';
		tka->hdl_name = ext + 1;
		return GF_TRUE;
	}
	if (act_type==TRACK_ACTION_SET_KMS_URI) {
		char *ext = strchr(param, '=');

		if (!strnicmp(param, "all=", 4)) {
			tka->kms = param + 4;
		} else if (!ext) {
			tka->kms = param;
		} else {
			tka->kms = ext + 1;
			ext[0] = 0;
			parse_track_id(&tka->target_track, param, GF_FALSE);
			ext[0] = '=';
		}
		return GF_TRUE;
	}
	if ((act_type==TRACK_ACTION_SET_TIME) || (act_type==TRACK_ACTION_SET_MEDIA_TIME)) {
		struct tm time;
		char *ext = strchr(arg_val, '=');
		if (ext) {
			ext[0] = 0;
			parse_track_id(&tka->target_track, arg_val, GF_FALSE);
			ext[0] = '=';
			arg_val = ext+1;
		}
		memset(&time, 0, sizeof(struct tm));
		sscanf(arg_val, "%d/%d/%d-%d:%d:%d", &time.tm_mday, &time.tm_mon, &time.tm_year, &time.tm_hour, &time.tm_min, &time.tm_sec);
		time.tm_isdst = 0;
		time.tm_year -= 1900;
		time.tm_mon -= 1;
		tka->time = 2082758400;
		tka->time += mktime(&time);
		return GF_TRUE;
	}

	while (param) {
		param = gf_url_colon_suffix(param, '=');
		if (param) {
			*param = 0;
			param++;
#ifndef GPAC_DISABLE_MEDIA_EXPORT
			if (!strncmp("vttnomerge", param, 10)) {
				tka->dump_type |= GF_EXPORT_WEBVTT_NOMERGE;
			} else if (!strncmp("layer", param, 5)) {
				tka->dump_type |= GF_EXPORT_SVC_LAYER;
			} else if (!strncmp("full", param, 4)) {
				tka->dump_type |= GF_EXPORT_NHML_FULL;
			} else if (!strncmp("embedded", param, 8)) {
				tka->dump_type |= GF_EXPORT_WEBVTT_META_EMBEDDED;
			} else if (!strncmp("output=", param, 7)) {
				tka->out_name = gf_strdup(param+7);
			} else if (!strncmp("src=", param, 4)) {
				tka->src_name = gf_strdup(param+4);
			} else if (!strncmp("str=", param, 4)) {
				tka->string = gf_strdup(param+4);
			} else if (!strncmp("box=", param, 4)) {
				tka->src_name = gf_strdup(param+4);
				tka->sample_num = 1;
			} else if (!strncmp("type=", param, 4)) {
				tka->udta_type = GF_4CC(param[5], param[6], param[7], param[8]);
			} else if (tka->dump_type == GF_EXPORT_RAW_SAMPLES) {
				tka->sample_num = parse_u32(param, "Sample number");
			}
#endif
		}
	}
	if (arg_val) {
		parse_track_id(&tka->target_track, arg_val, GF_TRUE);
	}
	return GF_TRUE;


static GF_Err do_export_tracks_non_isobmf()
{
	u32 i;

	GF_MediaExporter mdump;
	char szFile[GF_MAX_PATH+24];
	for (i=0; i<nb_track_act; i++) {
		GF_Err e;
		TrackAction *tka = &tracks[i];
		if (tka->act_type != TRACK_ACTION_RAW_EXTRACT) continue;
		memset(&mdump, 0, sizeof(mdump));
		mdump.in_name = inName;
		mdump.flags = tka->dump_type;
		mdump.trackID = tka->target_track.ID_or_num;
		if (tka->target_track.type>1)
			mdump.track_type = tka->target_track.type-1;
		mdump.sample_num = tka->sample_num;

		if (dump_std) {
			mdump.out_name = "std";
		}
		else if (outName) {
			mdump.out_name = outName;
			mdump.flags |= GF_EXPORT_MERGE;
		} else if (nb_track_act>1) {
			sprintf(szFile, "%s_track%d", outfile, mdump.trackID);
			mdump.out_name = szFile;
		} else {
			mdump.out_name = outfile;
		}
		mdump.print_stats_graph = fs_dump_flags;
		e = gf_media_export(&mdump);
		if (e) return e;
	}
	return GF_OK;


bool ShouldDisablePopups() const { return disable_popups_; }


content::RenderFrame* GetRenderFrame(v8::Local<v8::Object> value) {
  v8::Local<v8::Context> context = value->CreationContext();
  if (context.IsEmpty())
    return nullptr;
  blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context);
  if (!frame)
    return nullptr;
  return content::RenderFrame::FromWebFrame(frame);
}


bool ElectronBrowserClient::PreSpawnChild(sandbox::TargetPolicy* policy,
                                          sandbox::mojom::Sandbox sandbox_type,
                                          ChildSpawnFlags flags) {
  // Allow crashpad to communicate via named pipe.
  sandbox::ResultCode result = policy->AddRule(
      sandbox::TargetPolicy::SUBSYS_FILES,
      sandbox::TargetPolicy::FILES_ALLOW_ANY, L"\\??\\pipe\\crashpad_*");
  if (result != sandbox::SBOX_ALL_OK)
    return false;
  return true;
}


void WebContents::OnEnterFullscreenModeForTab(
    content::RenderFrameHost* requesting_frame,
    const blink::mojom::FullscreenOptions& options,
    bool allowed) {
  if (!allowed || !owner_window_)
    return;

  auto* source = content::WebContents::FromRenderFrameHost(requesting_frame);
  if (IsFullscreenForTabOrPending(source)) {
    DCHECK_EQ(fullscreen_frame_, source->GetFocusedFrame());
    return;
  }

  SetHtmlApiFullscreen(true);

  if (native_fullscreen_) {
    // Explicitly trigger a view resize, as the size is not actually changing if
    // the browser is fullscreened, too.
    source->GetRenderViewHost()->GetWidget()->SynchronizeVisualProperties();
  }
}


std::string ElectronBrowserClient::GetApplicationLocale() {
  if (BrowserThread::CurrentlyOn(BrowserThread::IO))
    return g_io_thread_application_locale.Get();
  return *g_application_locale;
}


void ElectronBrowserClient::
    RegisterAssociatedInterfaceBindersForRenderFrameHost(
        content::RenderFrameHost&
            render_frame_host,  // NOLINT(runtime/references)
        blink::AssociatedInterfaceRegistry&
            associated_registry) {  // NOLINT(runtime/references)
  auto* contents =
      content::WebContents::FromRenderFrameHost(&render_frame_host);
  if (contents) {
    auto* prefs = WebContentsPreferences::From(contents);
    if (render_frame_host.GetFrameTreeNodeId() ==
            contents->GetMainFrame()->GetFrameTreeNodeId() ||
        (prefs && prefs->AllowsNodeIntegrationInSubFrames())) {
      associated_registry.AddInterface(base::BindRepeating(
          [](content::RenderFrameHost* render_frame_host,
             mojo::PendingAssociatedReceiver<electron::mojom::ElectronApiIPC>
                 receiver) {
            ElectronApiIPCHandlerImpl::Create(render_frame_host,
                                              std::move(receiver));
          },
          &render_frame_host));
    }
  }

  associated_registry.AddInterface(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         mojo::PendingAssociatedReceiver<
             electron::mojom::ElectronWebContentsUtility> receiver) {
        ElectronWebContentsUtilityHandlerImpl::Create(render_frame_host,
                                                      std::move(receiver));
      },
      &render_frame_host));

  associated_registry.AddInterface(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         mojo::PendingAssociatedReceiver<mojom::ElectronAutofillDriver>
             receiver) {
        AutofillDriverFactory::BindAutofillDriver(std::move(receiver),
                                                  render_frame_host);
      },
      &render_frame_host));
#if BUILDFLAG(ENABLE_PRINTING)
  associated_registry.AddInterface(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost>
             receiver) {
        PrintViewManagerElectron::BindPrintManagerHost(std::move(receiver),
                                                       render_frame_host);
      },
      &render_frame_host));
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
  associated_registry.AddInterface(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         mojo::PendingAssociatedReceiver<extensions::mojom::LocalFrameHost>
             receiver) {
        extensions::ExtensionWebContentsObserver::BindLocalFrameHost(
            std::move(receiver), render_frame_host);
      },
      &render_frame_host));
#endif
#if BUILDFLAG(ENABLE_PDF_VIEWER)
  associated_registry.AddInterface(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         mojo::PendingAssociatedReceiver<pdf::mojom::PdfService> receiver) {
        pdf::PDFWebContentsHelper::BindPdfService(std::move(receiver),
                                                  render_frame_host);
      },
      &render_frame_host));
#endif
}


  void set_fullscreen_frame(content::RenderFrameHost* rfh) {
    fullscreen_frame_ = rfh;
  }


bool is_html_fullscreen() const { return html_fullscreen_; }


  SpellCheckerHolder(content::RenderFrame* render_frame,
                     std::unique_ptr<SpellCheckClient> spell_check_client)
      : content::RenderFrameObserver(render_frame),
        spell_check_client_(std::move(spell_check_client)) {
    DCHECK(!FromRenderFrame(render_frame));
    instances_.insert(this);
  }


  void OnDestruct() final {
    // Since we delete this in WillReleaseScriptContext, this method is unlikely
    // to be called, but override anyway since I'm not sure if there are some
    // corner cases.
    //
    // Note that while there are two "delete this", it is totally fine as the
    // observer unsubscribes automatically in destructor and the other one won't
    // be called.
    //
    // Also note that we should not call UnsetAndDestroy here, as the render
    // frame is going to be destroyed.
    delete this;
  }


void ElectronBrowserClient::RenderProcessHostDestroyed(
    content::RenderProcessHost* host) {
  int process_id = host->GetID();
  pending_processes_.erase(process_id);
  renderer_is_subframe_.erase(process_id);
  host->RemoveObserver(this);
}


absl::optional<base::TimeDelta> GetCursorBlinkInterval() {
#if BUILDFLAG(IS_MAC)
  absl::optional<base::TimeDelta> system_value(
      ui::TextInsertionCaretBlinkPeriodFromDefaults());
  if (system_value)
    return *system_value;
#elif BUILDFLAG(IS_LINUX)
  if (auto* linux_ui = views::LinuxUI::instance())
    return linux_ui->GetCursorBlinkInterval();
#elif BUILDFLAG(IS_WIN)
  const auto system_msec = ::GetCaretBlinkTime();
  if (system_msec != 0) {
    return (system_msec == INFINITE) ? base::TimeDelta()
                                     : base::Milliseconds(system_msec);
  }
#endif
  return absl::nullopt;
}


void ElectronBrowserClient::SiteInstanceDeleting(
    content::SiteInstance* site_instance) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
  // Don't do anything if we're shutting down.
  if (content::BrowserMainRunner::ExitedMainMessageLoop())
    return;

  auto* browser_context =
      static_cast<ElectronBrowserContext*>(site_instance->GetBrowserContext());
  if (!browser_context->IsOffTheRecord()) {
    // If this isn't an extension renderer there's nothing to do.
    extensions::ExtensionRegistry* registry =
        extensions::ExtensionRegistry::Get(browser_context);
    const extensions::Extension* extension =
        registry->enabled_extensions().GetExtensionOrAppByURL(
            site_instance->GetSiteURL());
    if (!extension)
      return;

    extensions::ProcessMap::Get(browser_context)
        ->Remove(extension->id(), site_instance->GetProcess()->GetID(),
                 site_instance->GetId());
  }
#endif
}


bool WebContents::CanGoBack() const {
  return web_contents()->GetController().CanGoBack();
}


void OnOpenExternal(const GURL& escaped_url, bool allowed) {
  if (allowed) {
    platform_util::OpenExternal(
        escaped_url, platform_util::OpenExternalOptions(), base::DoNothing());
  }
}


void WebContents::AddNewContents(
    content::WebContents* source,
    std::unique_ptr<content::WebContents> new_contents,
    const GURL& target_url,
    WindowOpenDisposition disposition,
    const gfx::Rect& initial_rect,
    bool user_gesture,
    bool* was_blocked) {
  auto* tracker = ChildWebContentsTracker::FromWebContents(new_contents.get());
  DCHECK(tracker);

  v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();

  v8::Locker locker(isolate);
  v8::HandleScope handle_scope(isolate);
  auto api_web_contents =
      CreateAndTake(isolate, std::move(new_contents), Type::kBrowserWindow);

  // We call RenderFrameCreated here as at this point the empty "about:blank"
  // render frame has already been created.  If the window never navigates again
  // RenderFrameCreated won't be called and certain prefs like
  // "kBackgroundColor" will not be applied.
  auto* frame = api_web_contents->MainFrame();
  if (frame) {
    api_web_contents->HandleNewRenderFrame(frame);
  }

  if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
           initial_rect.x(), initial_rect.y(), initial_rect.width(),
           initial_rect.height(), tracker->url, tracker->frame_name,
           tracker->referrer, tracker->raw_features, tracker->body)) {
    api_web_contents->Destroy();
  }
}


void Initialize(v8::Local<v8::Object> exports,
                v8::Local<v8::Value> unused,
                v8::Local<v8::Context> context,
                void* priv) {
  v8::Isolate* isolate = context->GetIsolate();
  gin_helper::Dictionary dict(isolate, exports);
  dict.Set("WebContents", WebContents::GetConstructor(context));
  dict.SetMethod("fromId", &WebContentsFromID);
  dict.SetMethod("fromDevToolsTargetId", &WebContentsFromDevToolsTargetID);
  dict.SetMethod("getAllWebContents", &GetAllWebContentsAsV8);
}


void Initialize(v8::Local<v8::Object> exports,
                v8::Local<v8::Value> unused,
                v8::Local<v8::Context> context,
                void* priv) {
  using namespace electron::api;  // NOLINT(build/namespaces)

  v8::Isolate* isolate = context->GetIsolate();
  gin_helper::Dictionary dict(isolate, exports);
  dict.Set("mainFrame", WebFrameRenderer::Create(
                            isolate, electron::GetRenderFrame(exports)));
}


ElectronBrowserClient::GetNetworkContextsParentDirectory() {
  base::FilePath user_data_dir;
  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  DCHECK(!user_data_dir.empty());

  return {user_data_dir};
}


bool ShouldDisableDialogs() const { return disable_dialogs_; }


void ElectronRenderFrameObserver::DidInstallConditionalFeatures(
    v8::Handle<v8::Context> context,
    int world_id) {
  // When a child window is created with window.open, its WebPreferences will
  // be copied from its parent, and Chromium will intialize JS context in it
  // immediately.
  // Normally the WebPreferences is overriden in browser before navigation,
  // but this behavior bypasses the browser side navigation and the child
  // window will get wrong WebPreferences in the initialization.
  // This will end up initializing Node.js in the child window with wrong
  // WebPreferences, leads to problem that child window having node integration
  // while "nodeIntegration=no" is passed.
  // We work around this issue by delaying the child window's initialization of
  // Node.js if this is the initial empty document, and only do it when the
  // acutal page has started to load.
  auto* web_frame =
      static_cast<blink::WebLocalFrameImpl*>(render_frame_->GetWebFrame());
  if (web_frame->Opener() && web_frame->IsOnInitialEmptyDocument()) {
    // FIXME(zcbenz): Chromium does not do any browser side navigation for
    // window.open('about:blank'), so there is no way to override WebPreferences
    // of it. We should not delay Node.js initialization as there will be no
    // further loadings.
    // Please check http://crbug.com/1215096 for updates which may help remove
    // this hack.
    GURL url = web_frame->GetDocument().Url();
    if (!url.IsAboutBlank()) {
      has_delayed_node_initialization_ = true;
      return;
    }
  }
  has_delayed_node_initialization_ = false;

  auto* isolate = context->GetIsolate();
  v8::MicrotasksScope microtasks_scope(
      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);

  if (ShouldNotifyClient(world_id))
    renderer_client_->DidCreateScriptContext(context, render_frame_);

  auto prefs = render_frame_->GetBlinkPreferences();
  bool use_context_isolation = prefs.context_isolation;
  // This logic matches the EXPLAINED logic in electron_renderer_client.cc
  // to avoid explaining it twice go check that implementation in
  // DidCreateScriptContext();
  bool is_main_world = IsMainWorld(world_id);
  bool is_main_frame = render_frame_->IsMainFrame();
  bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;

  bool should_create_isolated_context =
      use_context_isolation && is_main_world &&
      (is_main_frame || allow_node_in_sub_frames);

  if (should_create_isolated_context) {
    CreateIsolatedWorldContext();
    if (!renderer_client_->IsWebViewFrame(context, render_frame_))
      renderer_client_->SetupMainWorldOverrides(context, render_frame_);
  }
}


int WebContents::GetHistoryLength() const {
  return web_contents()->GetController().GetEntryCount();
}


void ElectronWebContentsUtilityHandlerImpl::WebContentsDestroyed() {
  delete this;
}


  v8::Local<v8::Value> GetFrameForSelector(v8::Isolate* isolate,
                                           const std::string& selector) {
    content::RenderFrame* render_frame;
    if (!MaybeGetRenderFrame(isolate, "getFrameForSelector", &render_frame))
      return v8::Null(isolate);

    blink::WebElement element =
        render_frame->GetWebFrame()->GetDocument().QuerySelector(
            blink::WebString::FromUTF8(selector));
    if (element.IsNull())  // not found
      return v8::Null(isolate);

    blink::WebFrame* frame = blink::WebFrame::FromFrameOwnerElement(element);
    return CreateWebFrameRenderer(isolate, frame);
  }


bool ElectronBrowserClient::ShouldUseProcessPerSite(
    content::BrowserContext* browser_context,
    const GURL& effective_url) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
  const extensions::Extension* extension =
      GetEnabledExtensionFromEffectiveURL(browser_context, effective_url);
  return extension != nullptr;
#else
  return content::ContentBrowserClient::ShouldUseProcessPerSite(browser_context,
                                                                effective_url);
#endif
}


scoped_refptr<base::RefCountedMemory> NetResourceProvider(int key) {
  if (key == IDR_DIR_HEADER_HTML) {
    return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
        IDR_DIR_HEADER_HTML);
  }
  return nullptr;
}


  void SetIgnoreMenuShortcuts(bool ignore_menu_shortcuts) {
    ignore_menu_shortcuts_ = ignore_menu_shortcuts;
  }


  std::u16string InsertCSS(v8::Isolate* isolate,
                           const std::string& css,
                           gin::Arguments* args) {
    blink::WebDocument::CSSOrigin css_origin =
        blink::WebDocument::CSSOrigin::kAuthorOrigin;

    gin_helper::Dictionary options;
    if (args->GetNext(&options))
      options.Get("cssOrigin", &css_origin);

    content::RenderFrame* render_frame;
    if (!MaybeGetRenderFrame(isolate, "insertCSS", &render_frame))
      return std::u16string();

    blink::WebFrame* web_frame = render_frame->GetWebFrame();
    if (web_frame->IsWebLocalFrame()) {
      return web_frame->ToWebLocalFrame()
          ->GetDocument()
          .InsertStyleSheet(blink::WebString::FromUTF8(css), nullptr,
                            css_origin)
          .Utf16();
    }
    return std::u16string();
  }


void ElectronRenderFrameObserver::DraggableRegionsChanged() {
  blink::WebVector<blink::WebDraggableRegion> webregions =
      render_frame_->GetWebFrame()->GetDocument().DraggableRegions();
  std::vector<mojom::DraggableRegionPtr> regions;
  for (auto& webregion : webregions) {
    auto region = mojom::DraggableRegion::New();
    render_frame_->ConvertViewportToWindow(&webregion.bounds);
    region->bounds = webregion.bounds;
    region->draggable = webregion.draggable;
    regions.push_back(std::move(region));
  }

  mojo::AssociatedRemote<mojom::ElectronWebContentsUtility>
      web_contents_utility_remote;
  render_frame_->GetRemoteAssociatedInterfaces()->GetInterface(
      &web_contents_utility_remote);
  web_contents_utility_remote->UpdateDraggableRegions(std::move(regions));
}


gin::Handle<WebContents> WebContents::CreateAndTake(
    v8::Isolate* isolate,
    std::unique_ptr<content::WebContents> web_contents,
    Type type) {
  gin::Handle<WebContents> handle = gin::CreateHandle(
      isolate, new WebContents(isolate, std::move(web_contents), type));
  v8::TryCatch try_catch(isolate);
  gin_helper::CallMethod(isolate, handle.get(), "_init");
  if (try_catch.HasCaught()) {
    node::errors::TriggerUncaughtException(isolate, try_catch);
  }
  return handle;
}


  blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) {
    blink::WebCacheResourceTypeStats stats;
    blink::WebCache::GetResourceTypeStats(&stats);
    return stats;
  }


void WebContents::Focus() {
  // Focusing on WebContents does not automatically focus the window on macOS
  // and Linux, do it manually to match the behavior on Windows.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  if (owner_window())
    owner_window()->Focus(true);
#endif
  web_contents()->Focus();
}


static void ext4_mb_new_preallocation(struct ext4_allocation_context *ac)
{
	if (ac->ac_flags & EXT4_MB_HINT_GROUP_ALLOC)
		ext4_mb_new_group_pa(ac);
	else
		ext4_mb_new_inode_pa(ac);
}


int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
			struct ext4_map_blocks *map,
			int flags)
{
	struct ext4_allocation_request ar;
	int err = -EIO;
	ext4_lblk_t offsets[4];
	Indirect chain[4];
	Indirect *partial;
	int indirect_blks;
	int blocks_to_boundary = 0;
	int depth;
	int count = 0;
	ext4_fsblk_t first_block = 0;

	trace_ext4_ind_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
	J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
	J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
	depth = ext4_block_to_path(inode, map->m_lblk, offsets,
				   &blocks_to_boundary);

	if (depth == 0)
		goto out;

	partial = ext4_get_branch(inode, depth, offsets, chain, &err);

	/* Simplest case - block found, no allocation needed */
	if (!partial) {
		first_block = le32_to_cpu(chain[depth - 1].key);
		count++;
		/*map more blocks*/
		while (count < map->m_len && count <= blocks_to_boundary) {
			ext4_fsblk_t blk;

			blk = le32_to_cpu(*(chain[depth-1].p + count));

			if (blk == first_block + count)
				count++;
			else
				break;
		}
		goto got_it;
	}

	/* Next simple case - plain lookup failed */
	if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
		unsigned epb = inode->i_sb->s_blocksize / sizeof(u32);
		int i;

		/*
		 * Count number blocks in a subtree under 'partial'. At each
		 * level we count number of complete empty subtrees beyond
		 * current offset and then descend into the subtree only
		 * partially beyond current offset.
		 */
		count = 0;
		for (i = partial - chain + 1; i < depth; i++)
			count = count * epb + (epb - offsets[i] - 1);
		count++;
		/* Fill in size of a hole we found */
		map->m_pblk = 0;
		map->m_len = min_t(unsigned int, map->m_len, count);
		goto cleanup;
	}

	/* Failed read of indirect block */
	if (err == -EIO)
		goto cleanup;

	/*
	 * Okay, we need to do block allocation.
	*/
	if (ext4_has_feature_bigalloc(inode->i_sb)) {
		EXT4_ERROR_INODE(inode, "Can't allocate blocks for "
				 "non-extent mapped inodes with bigalloc");
		return -EFSCORRUPTED;
	}

	/* Set up for the direct block allocation */
	memset(&ar, 0, sizeof(ar));
	ar.inode = inode;
	ar.logical = map->m_lblk;
	if (S_ISREG(inode->i_mode))
		ar.flags = EXT4_MB_HINT_DATA;
	if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
		ar.flags |= EXT4_MB_DELALLOC_RESERVED;
	if (flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
		ar.flags |= EXT4_MB_USE_RESERVED;

	ar.goal = ext4_find_goal(inode, map->m_lblk, partial);

	/* the number of blocks need to allocate for [d,t]indirect blocks */
	indirect_blks = (chain + depth) - partial - 1;

	/*
	 * Next look up the indirect map to count the totoal number of
	 * direct blocks to allocate for this branch.
	 */
	ar.len = ext4_blks_to_allocate(partial, indirect_blks,
				       map->m_len, blocks_to_boundary);

	/*
	 * Block out ext4_truncate while we alter the tree
	 */
	err = ext4_alloc_branch(handle, &ar, indirect_blks,
				offsets + (partial - chain), partial);

	/*
	 * The ext4_splice_branch call will free and forget any buffers
	 * on the new chain if there is a failure, but that risks using
	 * up transaction credits, especially for bitmaps where the
	 * credits cannot be returned.  Can we handle this somehow?  We
	 * may need to return -EAGAIN upwards in the worst case.  --sct
	 */
	if (!err)
		err = ext4_splice_branch(handle, &ar, partial, indirect_blks);
	if (err)
		goto cleanup;

	map->m_flags |= EXT4_MAP_NEW;

	ext4_update_inode_fsync_trans(handle, inode, 1);
	count = ar.len;
got_it:
	map->m_flags |= EXT4_MAP_MAPPED;
	map->m_pblk = le32_to_cpu(chain[depth-1].key);
	map->m_len = count;
	if (count > blocks_to_boundary)
		map->m_flags |= EXT4_MAP_BOUNDARY;
	err = count;
	/* Clean up and exit */
	partial = chain + depth - 1;	/* the whole chain */
cleanup:
	while (partial > chain) {
		BUFFER_TRACE(partial->bh, "call brelse");
		brelse(partial->bh);
		partial--;
	}
out:
	trace_ext4_ind_map_blocks_exit(inode, flags, map, err);
	return err;
}


ext4_mb_discard_group_preallocations(struct super_block *sb,
					ext4_group_t group, int needed)
{
	struct ext4_group_info *grp = ext4_get_group_info(sb, group);
	struct buffer_head *bitmap_bh = NULL;
	struct ext4_prealloc_space *pa, *tmp;
	struct list_head list;
	struct ext4_buddy e4b;
	int err;
	int busy = 0;
	int free = 0;

	mb_debug(sb, "discard preallocation for group %u\n", group);
	if (list_empty(&grp->bb_prealloc_list))
		goto out_dbg;

	bitmap_bh = ext4_read_block_bitmap(sb, group);
	if (IS_ERR(bitmap_bh)) {
		err = PTR_ERR(bitmap_bh);
		ext4_error_err(sb, -err,
			       "Error %d reading block bitmap for %u",
			       err, group);
		goto out_dbg;
	}

	err = ext4_mb_load_buddy(sb, group, &e4b);
	if (err) {
		ext4_warning(sb, "Error %d loading buddy information for %u",
			     err, group);
		put_bh(bitmap_bh);
		goto out_dbg;
	}

	if (needed == 0)
		needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1;

	INIT_LIST_HEAD(&list);
repeat:
	ext4_lock_group(sb, group);
	this_cpu_inc(discard_pa_seq);
	list_for_each_entry_safe(pa, tmp,
				&grp->bb_prealloc_list, pa_group_list) {
		spin_lock(&pa->pa_lock);
		if (atomic_read(&pa->pa_count)) {
			spin_unlock(&pa->pa_lock);
			busy = 1;
			continue;
		}
		if (pa->pa_deleted) {
			spin_unlock(&pa->pa_lock);
			continue;
		}

		/* seems this one can be freed ... */
		pa->pa_deleted = 1;

		/* we can trust pa_free ... */
		free += pa->pa_free;

		spin_unlock(&pa->pa_lock);

		list_del(&pa->pa_group_list);
		list_add(&pa->u.pa_tmp_list, &list);
	}

	/* if we still need more blocks and some PAs were used, try again */
	if (free < needed && busy) {
		busy = 0;
		ext4_unlock_group(sb, group);
		cond_resched();
		goto repeat;
	}

	/* found anything to free? */
	if (list_empty(&list)) {
		BUG_ON(free != 0);
		mb_debug(sb, "Someone else may have freed PA for this group %u\n",
			 group);
		goto out;
	}

	/* now free all selected PAs */
	list_for_each_entry_safe(pa, tmp, &list, u.pa_tmp_list) {

		/* remove from object (inode or locality group) */
		spin_lock(pa->pa_obj_lock);
		list_del_rcu(&pa->pa_inode_list);
		spin_unlock(pa->pa_obj_lock);

		if (pa->pa_type == MB_GROUP_PA)
			ext4_mb_release_group_pa(&e4b, pa);
		else
			ext4_mb_release_inode_pa(&e4b, bitmap_bh, pa);

		list_del(&pa->u.pa_tmp_list);
		call_rcu(&(pa)->u.pa_rcu, ext4_mb_pa_callback);
	}

out:
	ext4_unlock_group(sb, group);
	ext4_mb_unload_buddy(&e4b);
	put_bh(bitmap_bh);
out_dbg:
	mb_debug(sb, "discarded (%d) blocks preallocated for group %u bb_free (%d)\n",
		 free, group, grp->bb_free);
	return free;
}


long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
	struct inode *inode = file_inode(file);
	loff_t new_size = 0;
	unsigned int max_blocks;
	int ret = 0;
	int flags;
	ext4_lblk_t lblk;
	unsigned int blkbits = inode->i_blkbits;

	/*
	 * Encrypted inodes can't handle collapse range or insert
	 * range since we would need to re-encrypt blocks with a
	 * different IV or XTS tweak (which are based on the logical
	 * block number).
	 */
	if (IS_ENCRYPTED(inode) &&
	    (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE)))
		return -EOPNOTSUPP;

	/* Return error if mode is not supported */
	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
		     FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
		     FALLOC_FL_INSERT_RANGE))
		return -EOPNOTSUPP;

	if (mode & FALLOC_FL_PUNCH_HOLE)
		return ext4_punch_hole(inode, offset, len);

	ret = ext4_convert_inline_data(inode);
	if (ret)
		return ret;

	if (mode & FALLOC_FL_COLLAPSE_RANGE)
		return ext4_collapse_range(inode, offset, len);

	if (mode & FALLOC_FL_INSERT_RANGE)
		return ext4_insert_range(inode, offset, len);

	if (mode & FALLOC_FL_ZERO_RANGE)
		return ext4_zero_range(file, offset, len, mode);

	trace_ext4_fallocate_enter(inode, offset, len, mode);
	lblk = offset >> blkbits;

	max_blocks = EXT4_MAX_BLOCKS(len, offset, blkbits);
	flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;

	inode_lock(inode);

	/*
	 * We only support preallocation for extent-based files only
	 */
	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
		ret = -EOPNOTSUPP;
		goto out;
	}

	if (!(mode & FALLOC_FL_KEEP_SIZE) &&
	    (offset + len > inode->i_size ||
	     offset + len > EXT4_I(inode)->i_disksize)) {
		new_size = offset + len;
		ret = inode_newsize_ok(inode, new_size);
		if (ret)
			goto out;
	}

	/* Wait all existing dio workers, newcomers will block on i_mutex */
	inode_dio_wait(inode);

	ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size, flags);
	if (ret)
		goto out;

	if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
		ret = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal,
						EXT4_I(inode)->i_sync_tid);
	}
out:
	inode_unlock(inode);
	trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
	return ret;
}


void ext4_ind_truncate(handle_t *handle, struct inode *inode)
{
	struct ext4_inode_info *ei = EXT4_I(inode);
	__le32 *i_data = ei->i_data;
	int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
	ext4_lblk_t offsets[4];
	Indirect chain[4];
	Indirect *partial;
	__le32 nr = 0;
	int n = 0;
	ext4_lblk_t last_block, max_block;
	unsigned blocksize = inode->i_sb->s_blocksize;

	last_block = (inode->i_size + blocksize-1)
					>> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
	max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
					>> EXT4_BLOCK_SIZE_BITS(inode->i_sb);

	if (last_block != max_block) {
		n = ext4_block_to_path(inode, last_block, offsets, NULL);
		if (n == 0)
			return;
	}

	ext4_es_remove_extent(inode, last_block, EXT_MAX_BLOCKS - last_block);

	/*
	 * The orphan list entry will now protect us from any crash which
	 * occurs before the truncate completes, so it is now safe to propagate
	 * the new, shorter inode size (held for now in i_size) into the
	 * on-disk inode. We do this via i_disksize, which is the value which
	 * ext4 *really* writes onto the disk inode.
	 */
	ei->i_disksize = inode->i_size;

	if (last_block == max_block) {
		/*
		 * It is unnecessary to free any data blocks if last_block is
		 * equal to the indirect block limit.
		 */
		return;
	} else if (n == 1) {		/* direct blocks */
		ext4_free_data(handle, inode, NULL, i_data+offsets[0],
			       i_data + EXT4_NDIR_BLOCKS);
		goto do_indirects;
	}

	partial = ext4_find_shared(inode, n, offsets, chain, &nr);
	/* Kill the top of shared branch (not detached) */
	if (nr) {
		if (partial == chain) {
			/* Shared branch grows from the inode */
			ext4_free_branches(handle, inode, NULL,
					   &nr, &nr+1, (chain+n-1) - partial);
			*partial->p = 0;
			/*
			 * We mark the inode dirty prior to restart,
			 * and prior to stop.  No need for it here.
			 */
		} else {
			/* Shared branch grows from an indirect block */
			BUFFER_TRACE(partial->bh, "get_write_access");
			ext4_free_branches(handle, inode, partial->bh,
					partial->p,
					partial->p+1, (chain+n-1) - partial);
		}
	}
	/* Clear the ends of indirect blocks on the shared branch */
	while (partial > chain) {
		ext4_free_branches(handle, inode, partial->bh, partial->p + 1,
				   (__le32*)partial->bh->b_data+addr_per_block,
				   (chain+n-1) - partial);
		BUFFER_TRACE(partial->bh, "call brelse");
		brelse(partial->bh);
		partial--;
	}
do_indirects:
	/* Kill the remaining (whole) subtrees */
	switch (offsets[0]) {
	default:
		nr = i_data[EXT4_IND_BLOCK];
		if (nr) {
			ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
			i_data[EXT4_IND_BLOCK] = 0;
		}
		/* fall through */
	case EXT4_IND_BLOCK:
		nr = i_data[EXT4_DIND_BLOCK];
		if (nr) {
			ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
			i_data[EXT4_DIND_BLOCK] = 0;
		}
		/* fall through */
	case EXT4_DIND_BLOCK:
		nr = i_data[EXT4_TIND_BLOCK];
		if (nr) {
			ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
			i_data[EXT4_TIND_BLOCK] = 0;
		}
		/* fall through */
	case EXT4_TIND_BLOCK:
		;
	}
}


static int __ext4_ext_dirty(const char *where, unsigned int line,
			    handle_t *handle, struct inode *inode,
			    struct ext4_ext_path *path)
{
	int err;

	WARN_ON(!rwsem_is_locked(&EXT4_I(inode)->i_data_sem));
	if (path->p_bh) {
		ext4_extent_block_csum_set(inode, ext_block_hdr(path->p_bh));
		/* path points to block */
		err = __ext4_handle_dirty_metadata(where, line, handle,
						   inode, path->p_bh);
	} else {
		/* path points to leaf/index in inode body */
		err = ext4_mark_inode_dirty(handle, inode);
	}
	return err;
}